summaryrefslogtreecommitdiff
path: root/src/components/ble/FSService.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/ble/FSService.cpp')
-rw-r--r--src/components/ble/FSService.cpp330
1 files changed, 330 insertions, 0 deletions
diff --git a/src/components/ble/FSService.cpp b/src/components/ble/FSService.cpp
new file mode 100644
index 00000000..8dc9ed67
--- /dev/null
+++ b/src/components/ble/FSService.cpp
@@ -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);
+ }
+}