Skip to content

Commit

Permalink
Merge pull request #756 from geekbozu/BLE_FS
Browse files Browse the repository at this point in the history
BLE FS Using adafruits Simple (not fast) BLE FS  Api
  • Loading branch information
JF002 authored Dec 11, 2021
2 parents 42a5cdb + 1470489 commit bccd77d
Show file tree
Hide file tree
Showing 9 changed files with 632 additions and 77 deletions.
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ list(APPEND SOURCE_FILES
components/ble/NavigationService.cpp
displayapp/fonts/lv_font_navi_80.c
components/ble/BatteryInformationService.cpp
components/ble/FSService.cpp
components/ble/ImmediateAlertService.cpp
components/ble/ServiceDiscovery.cpp
components/ble/HeartRateService.cpp
Expand Down Expand Up @@ -557,6 +558,7 @@ list(APPEND RECOVERY_SOURCE_FILES
components/ble/MusicService.cpp
components/ble/weather/WeatherService.cpp
components/ble/BatteryInformationService.cpp
components/ble/FSService.cpp
components/ble/ImmediateAlertService.cpp
components/ble/ServiceDiscovery.cpp
components/ble/NavigationService.cpp
Expand Down Expand Up @@ -669,6 +671,7 @@ set(INCLUDE_FILES
components/ble/DfuService.h
components/firmwarevalidator/FirmwareValidator.h
components/ble/BatteryInformationService.h
components/ble/FSService.h
components/ble/ImmediateAlertService.h
components/ble/ServiceDiscovery.h
components/ble/BleClient.h
Expand Down
330 changes: 330 additions & 0 deletions src/components/ble/FSService.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,330 @@
#include <nrf_log.h>
#include "FSService.h"
#include "components/ble/BleController.h"
#include "systemtask/SystemTask.h"

using namespace Pinetime::Controllers;

constexpr ble_uuid16_t FSService::fsServiceUuid;
constexpr ble_uuid128_t FSService::fsVersionUuid;
constexpr ble_uuid128_t FSService::fsTransferUuid;

int FSServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
auto* fsService = static_cast<FSService*>(arg);
return fsService->OnFSServiceRequested(conn_handle, attr_handle, ctxt);
}

FSService::FSService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::FS& fs)
: systemTask {systemTask},
fs {fs},
characteristicDefinition {{.uuid = &fsVersionUuid.u,
.access_cb = FSServiceCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_READ,
.val_handle = &versionCharacteristicHandle},
{
.uuid = &fsTransferUuid.u,
.access_cb = FSServiceCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
.val_handle = &transferCharacteristicHandle,
},
{0}},
serviceDefinition {
{/* Device Information Service */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = &fsServiceUuid.u,
.characteristics = characteristicDefinition},
{0},
} {
}

void FSService::Init() {
int res = 0;
res = ble_gatts_count_cfg(serviceDefinition);
ASSERT(res == 0);

res = ble_gatts_add_svcs(serviceDefinition);
ASSERT(res == 0);
}

int FSService::OnFSServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context) {
if (attributeHandle == versionCharacteristicHandle) {
NRF_LOG_INFO("FS_S : handle = %d", versionCharacteristicHandle);
int res = os_mbuf_append(context->om, &fsVersion, sizeof(fsVersion));
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
if (attributeHandle == transferCharacteristicHandle) {
return FSCommandHandler(connectionHandle, context->om);
}
return 0;
}

int FSService::FSCommandHandler(uint16_t connectionHandle, os_mbuf* om) {
auto command = static_cast<commands>(om->om_data[0]);
NRF_LOG_INFO("[FS_S] -> FSCommandHandler Command %d", command);
// Just always make sure we are awake...
systemTask.PushMessage(Pinetime::System::Messages::StartFileTransfer);
vTaskDelay(10);
while (systemTask.IsSleeping()) {
vTaskDelay(100); // 50ms
}
lfs_dir_t dir = {0};
lfs_info info = {0};
lfs_file f = {0};
switch (command) {
case commands::READ: {
NRF_LOG_INFO("[FS_S] -> Read");
auto* header = (ReadHeader*) om->om_data;
uint16_t plen = header->pathlen;
if (plen > maxpathlen) { //> counts for null term
return -1;
}
memcpy(filepath, header->pathstr, plen);
filepath[plen] = 0; // Copy and null teminate string
ReadResponse resp;
os_mbuf* om;
resp.command = commands::READ_DATA;
resp.status = 0x01;
resp.chunkoff = header->chunkoff;
int res = fs.Stat(filepath, &info);
if (res == LFS_ERR_NOENT && info.type != LFS_TYPE_DIR) {
resp.status = (int8_t) res;
resp.chunklen = 0;
resp.totallen = 0;
om = ble_hs_mbuf_from_flat(&resp, sizeof(ReadResponse));
} else {
resp.chunklen = std::min(header->chunksize, info.size); // TODO add mtu somehow
resp.totallen = info.size;
fs.FileOpen(&f, filepath, LFS_O_RDONLY);
fs.FileSeek(&f, header->chunkoff);
uint8_t fileData[resp.chunklen] = {0};
resp.chunklen = fs.FileRead(&f, fileData, resp.chunklen);
om = ble_hs_mbuf_from_flat(&resp, sizeof(ReadResponse));
os_mbuf_append(om, fileData, resp.chunklen);
fs.FileClose(&f);
}

ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
break;
}
case commands::READ_PACING: {
NRF_LOG_INFO("[FS_S] -> Readpacing");
auto* header = (ReadHeader*) om->om_data;
ReadResponse resp;
resp.command = commands::READ_DATA;
resp.status = 0x01;
resp.chunkoff = header->chunkoff;
int res = fs.Stat(filepath, &info);
if (res == LFS_ERR_NOENT && info.type != LFS_TYPE_DIR) {
resp.status = (int8_t) res;
resp.chunklen = 0;
resp.totallen = 0;
} else {
resp.chunklen = std::min(header->chunksize, info.size); // TODO add mtu somehow
resp.totallen = info.size;
fs.FileOpen(&f, filepath, LFS_O_RDONLY);
fs.FileSeek(&f, header->chunkoff);
}
os_mbuf* om;
if (resp.chunklen > 0) {
uint8_t fileData[resp.chunklen] = {0};
resp.chunklen = fs.FileRead(&f, fileData, resp.chunklen);
om = ble_hs_mbuf_from_flat(&resp, sizeof(ReadResponse));
os_mbuf_append(om, fileData, resp.chunklen);
} else {
resp.chunklen = 0;
om = ble_hs_mbuf_from_flat(&resp, sizeof(ReadResponse));
}
fs.FileClose(&f);
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
break;
}
case commands::WRITE: {
NRF_LOG_INFO("[FS_S] -> Write");
auto* header = (WriteHeader*) om->om_data;
uint16_t plen = header->pathlen;
if (plen > maxpathlen) { //> counts for null term
return -1; // TODO make this actually return a BLE notif
}
memcpy(filepath, header->pathstr, plen);
filepath[plen] = 0; // Copy and null teminate string
fileSize = header->totalSize;
WriteResponse resp;
resp.command = commands::WRITE_PACING;
resp.offset = header->offset;
resp.modTime = 0;

int res = fs.FileOpen(&f, filepath, LFS_O_RDWR | LFS_O_CREAT);
if (res == 0) {
fs.FileClose(&f);
resp.status = (res == 0) ? 0x01 : (int8_t) res;
}
resp.freespace = std::min(fs.getSize() - (fs.GetFSSize() * fs.getBlockSize()), fileSize - header->offset);
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(WriteResponse));
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
break;
}
case commands::WRITE_DATA: {
NRF_LOG_INFO("[FS_S] -> WriteData");
auto* header = (WritePacing*) om->om_data;
WriteResponse resp;
resp.command = commands::WRITE_PACING;
resp.offset = header->offset;
int res = 0;

if (!(res = fs.FileOpen(&f, filepath, LFS_O_RDWR | LFS_O_CREAT))) {
if ((res = fs.FileSeek(&f, header->offset)) >= 0) {
res = fs.FileWrite(&f, header->data, header->dataSize);
}
fs.FileClose(&f);
}
if (res < 0) {
resp.status = (int8_t) res;
}
resp.freespace = std::min(fs.getSize() - (fs.GetFSSize() * fs.getBlockSize()), fileSize - header->offset);
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(WriteResponse));
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
break;
}
case commands::DELETE: {
NRF_LOG_INFO("[FS_S] -> Delete");
auto* header = (DelHeader*) om->om_data;
uint16_t plen = header->pathlen;
char path[plen + 1] = {0};
memcpy(path, header->pathstr, plen);
path[plen] = 0; // Copy and null teminate string
DelResponse resp {};
resp.command = commands::DELETE_STATUS;
int res = fs.FileDelete(path);
resp.status = (res == 0) ? 0x01 : (int8_t) res;
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(DelResponse));
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
break;
}
case commands::MKDIR: {
NRF_LOG_INFO("[FS_S] -> MKDir");
auto* header = (MKDirHeader*) om->om_data;
uint16_t plen = header->pathlen;
char path[plen + 1] = {0};
memcpy(path, header->pathstr, plen);
path[plen] = 0; // Copy and null teminate string
MKDirResponse resp {};
resp.command = commands::MKDIR_STATUS;
resp.modification_time = 0;
int res = fs.DirCreate(path);
resp.status = (res == 0) ? 0x01 : (int8_t) res;
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(MKDirResponse));
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
break;
}
case commands::LISTDIR: {
NRF_LOG_INFO("[FS_S] -> ListDir");
ListDirHeader* header = (ListDirHeader*) om->om_data;
uint16_t plen = header->pathlen;
char path[plen + 1] = {0};
path[plen] = 0; // Copy and null teminate string
memcpy(path, header->pathstr, plen);

ListDirResponse resp {};

resp.command = commands::LISTDIR_ENTRY;
resp.status = 0x01;
resp.totalentries = 0;
resp.entry = 0;
resp.modification_time = 0;
int res = fs.DirOpen(path, &dir);
if (res != 0) {
resp.status = (int8_t) res;
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(ListDirResponse));
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
break;
};
while (fs.DirRead(&dir, &info)) {
resp.totalentries++;
}
fs.DirRewind(&dir);
while (true) {
res = fs.DirRead(&dir, &info);
if (res <= 0) {
break;
}
switch (info.type) {
case LFS_TYPE_REG: {
resp.flags = 0;
resp.file_size = info.size;
break;
}
case LFS_TYPE_DIR: {
resp.flags = 1;
resp.file_size = 0;
break;
}
}

// strcpy(resp.path, info.name);
resp.path_length = strlen(info.name);
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(ListDirResponse));
os_mbuf_append(om, info.name, resp.path_length);
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
/*
* Todo Figure out how to know when the previous Notify was TX'd
* For now just delay 100ms to make sure that the data went out...
*/
vTaskDelay(100); // Allow stuff to actually go out over the BLE conn
resp.entry++;
}
assert(fs.DirClose(&dir) == 0);
resp.file_size = 0;
resp.path_length = 0;
resp.flags = 0;
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(ListDirResponse));
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
break;
}
case commands::MOVE: {
NRF_LOG_INFO("[FS_S] -> Move");
MoveHeader* header = (MoveHeader*) om->om_data;
uint16_t plen = header->OldPathLength;
// Null Terminate string
header->pathstr[plen] = 0;
char path[header->NewPathLength + 1] = {0};
memcpy(path, &header->pathstr[plen + 1], header->NewPathLength);
path[header->NewPathLength] = 0; // Copy and null teminate string
MoveResponse resp {};
resp.command = commands::MOVE_STATUS;
int8_t res = (int8_t) fs.Rename(header->pathstr, path);
resp.status = (res == 0) ? 1 : res;
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(MoveResponse));
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
}
default:
break;
}
NRF_LOG_INFO("[FS_S] -> done ");
systemTask.PushMessage(Pinetime::System::Messages::StopFileTransfer);
return 0;
}

// Loads resp with file data given a valid filepath header and resp
void FSService::prepareReadDataResp(ReadHeader* header, ReadResponse* resp) {
// uint16_t plen = header->pathlen;
resp->command = commands::READ_DATA;
resp->chunkoff = header->chunkoff;
resp->status = 0x01;
struct lfs_info info = {};
int res = fs.Stat(filepath, &info);
if (res == LFS_ERR_NOENT && info.type != LFS_TYPE_DIR) {
resp->status = 0x03;
resp->chunklen = 0;
resp->totallen = 0;
} else {
lfs_file f;
resp->chunklen = std::min(header->chunksize, info.size);
resp->totallen = info.size;
fs.FileOpen(&f, filepath, LFS_O_RDONLY);
fs.FileSeek(&f, header->chunkoff);
resp->chunklen = fs.FileRead(&f, resp->chunk, resp->chunklen);
fs.FileClose(&f);
}
}
Loading

0 comments on commit bccd77d

Please sign in to comment.