2021-10-16 21:09:26 -04:00
|
|
|
#include <nrf_log.h>
|
|
|
|
#include "FSService.h"
|
2021-10-17 12:04:23 -04:00
|
|
|
#include "components/ble/BleController.h"
|
2021-10-19 21:30:04 -04:00
|
|
|
#include "systemtask/SystemTask.h"
|
2021-10-16 21:09:26 -04:00
|
|
|
|
|
|
|
using namespace Pinetime::Controllers;
|
|
|
|
|
2021-10-18 00:20:26 -04:00
|
|
|
constexpr ble_uuid16_t FSService::fsServiceUuid;
|
2021-10-16 21:09:26 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-10-19 21:30:04 -04:00
|
|
|
FSService::FSService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::FS& fs)
|
|
|
|
: systemTask {systemTask},
|
|
|
|
fs {fs},
|
2021-10-17 12:04:23 -04:00
|
|
|
characteristicDefinition {{.uuid = &fsVersionUuid.u,
|
2021-10-16 21:09:26 -04:00
|
|
|
.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,
|
2021-10-17 12:04:23 -04:00
|
|
|
.val_handle = &transferCharacteristicHandle,
|
2021-10-16 21:09:26 -04:00
|
|
|
},
|
|
|
|
{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;
|
|
|
|
}
|
2021-10-17 12:04:23 -04:00
|
|
|
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]);
|
2021-10-19 21:30:04 -04:00
|
|
|
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
|
|
|
|
}
|
2021-10-21 00:02:50 -04:00
|
|
|
lfs_dir_t dir;
|
|
|
|
lfs_info info;
|
2021-10-17 12:04:23 -04:00
|
|
|
switch (command) {
|
2021-10-18 00:20:26 -04:00
|
|
|
case commands::READ: {
|
2021-10-21 23:34:23 -04:00
|
|
|
lfs_file f;
|
2021-10-18 00:20:26 -04:00
|
|
|
NRF_LOG_INFO("[FS_S] -> Read");
|
|
|
|
auto* header = (ReadHeader*) om->om_data;
|
|
|
|
uint16_t plen = header->pathlen;
|
2021-10-21 23:34:23 -04:00
|
|
|
if (plen > maxpathlen) { //> counts for null term
|
2021-10-18 00:20:26 -04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
memcpy(filepath, header->pathstr, plen);
|
|
|
|
filepath[plen + 1] = 0; // Copy and null teminate string
|
|
|
|
ReadResponse resp;
|
|
|
|
resp.command = commands::READ_DATA;
|
|
|
|
resp.status = 0x01;
|
2021-10-21 23:34:23 -04:00
|
|
|
resp.chunkoff = header->chunkoff;
|
2021-10-18 00:20:26 -04:00
|
|
|
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 {
|
2021-10-21 23:34:23 -04:00
|
|
|
resp.chunklen = std::min(header->chunksize, info.size); // TODO add mtu somehow
|
2021-10-18 00:20:26 -04:00
|
|
|
resp.totallen = info.size;
|
|
|
|
fs.FileOpen(&f, filepath, LFS_O_RDONLY);
|
|
|
|
fs.FileSeek(&f, header->chunkoff);
|
|
|
|
}
|
2021-10-21 23:34:23 -04:00
|
|
|
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));
|
2021-10-18 00:20:26 -04:00
|
|
|
}
|
2021-10-21 23:34:23 -04:00
|
|
|
fs.FileClose(&f);
|
|
|
|
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
|
|
|
|
break;
|
2021-10-18 00:20:26 -04:00
|
|
|
}
|
|
|
|
case commands::READ_PACING: {
|
2021-10-21 23:34:23 -04:00
|
|
|
lfs_file f;
|
|
|
|
NRF_LOG_INFO("[FS_S] -> Readpacing");
|
|
|
|
auto* header = (ReadHeader*) om->om_data;
|
|
|
|
ReadResponse resp;
|
2021-10-18 00:20:26 -04:00
|
|
|
resp.command = commands::READ_DATA;
|
|
|
|
resp.status = 0x01;
|
2021-10-21 23:34:23 -04:00
|
|
|
resp.chunkoff = header->chunkoff;
|
2021-10-18 00:20:26 -04:00
|
|
|
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 {
|
2021-10-21 23:34:23 -04:00
|
|
|
resp.chunklen = std::min(header->chunksize, info.size); // TODO add mtu somehow
|
2021-10-18 00:20:26 -04:00
|
|
|
resp.totallen = info.size;
|
|
|
|
fs.FileOpen(&f, filepath, LFS_O_RDONLY);
|
|
|
|
fs.FileSeek(&f, header->chunkoff);
|
|
|
|
}
|
2021-10-21 23:34:23 -04:00
|
|
|
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));
|
2021-10-18 00:20:26 -04:00
|
|
|
}
|
2021-10-21 23:34:23 -04:00
|
|
|
fs.FileClose(&f);
|
|
|
|
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
|
|
|
|
break;
|
2021-10-18 00:20:26 -04:00
|
|
|
}
|
|
|
|
case commands::WRITE: {
|
|
|
|
if (state != FSState::IDLE) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case commands::WRITE_PACING: {
|
|
|
|
if (state != FSState::WRITE) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2021-10-17 19:07:43 -04:00
|
|
|
case commands::DELETE: {
|
|
|
|
NRF_LOG_INFO("[FS_S] -> Delete");
|
2021-10-18 00:20:26 -04:00
|
|
|
auto* header = (DelHeader*) om->om_data;
|
2021-10-17 19:07:43 -04:00
|
|
|
uint16_t plen = header->pathlen;
|
2021-10-21 00:02:50 -04:00
|
|
|
char path[plen + 1] {0};
|
|
|
|
memcpy(path, header->pathstr, plen);
|
|
|
|
DelResponse resp {};
|
2021-10-17 19:07:43 -04:00
|
|
|
resp.command = commands::DELETE_STATUS;
|
2021-10-21 00:02:50 -04:00
|
|
|
resp.status = fs.FileDelete(path) ? 0x02 : 0x01;
|
2021-10-17 19:07:43 -04:00
|
|
|
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;
|
2021-10-21 00:02:50 -04:00
|
|
|
char path[plen + 1] {0};
|
2021-10-17 19:07:43 -04:00
|
|
|
memcpy(path, header->pathstr, plen);
|
2021-10-21 00:02:50 -04:00
|
|
|
MKDirResponse resp {};
|
2021-10-17 19:07:43 -04:00
|
|
|
resp.command = commands::MKDIR_STATUS;
|
2021-10-21 00:02:50 -04:00
|
|
|
resp.modification_time = 0;
|
|
|
|
resp.status = fs.DirCreate(path) ? 0x02 : 0x01;
|
2021-10-17 19:07:43 -04:00
|
|
|
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(MKDirResponse));
|
|
|
|
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
|
|
|
|
break;
|
2021-10-21 00:02:50 -04:00
|
|
|
}
|
2021-10-17 12:04:23 -04:00
|
|
|
case commands::LISTDIR: {
|
|
|
|
NRF_LOG_INFO("[FS_S] -> ListDir");
|
2021-10-18 00:20:26 -04:00
|
|
|
ListDirHeader* header = (ListDirHeader*) om->om_data;
|
2021-10-17 12:04:23 -04:00
|
|
|
uint16_t plen = header->pathlen;
|
2021-10-21 00:02:50 -04:00
|
|
|
char path[plen + 1] {0};
|
2021-10-17 12:04:23 -04:00
|
|
|
memcpy(path, header->pathstr, plen);
|
2021-10-19 21:30:04 -04:00
|
|
|
|
2021-10-21 00:02:50 -04:00
|
|
|
ListDirResponse resp {};
|
2021-10-17 12:04:23 -04:00
|
|
|
|
2021-10-17 19:07:43 -04:00
|
|
|
resp.command = commands::LISTDIR_ENTRY;
|
2021-10-21 00:02:50 -04:00
|
|
|
resp.status = 1;
|
2021-10-17 12:04:23 -04:00
|
|
|
resp.totalentries = 0;
|
|
|
|
resp.entry = 0;
|
2021-10-21 00:02:50 -04:00
|
|
|
resp.modification_time = 0; // TODO Does LFS actually support TS?
|
|
|
|
if (fs.DirOpen(path, &dir) != 0) {
|
|
|
|
resp.status = 0x02;
|
|
|
|
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(ListDirResponse));
|
|
|
|
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
// Count Total files in directory.
|
2021-10-17 12:04:23 -04:00
|
|
|
while (fs.DirRead(&dir, &info)) {
|
|
|
|
resp.totalentries++;
|
|
|
|
}
|
2021-10-17 16:48:34 -04:00
|
|
|
fs.DirRewind(&dir);
|
2021-10-21 00:02:50 -04:00
|
|
|
while (true) {
|
2021-10-18 00:20:26 -04:00
|
|
|
int res = fs.DirRead(&dir, &info);
|
2021-10-19 21:30:04 -04:00
|
|
|
if (res <= 0) {
|
2021-10-18 00:20:26 -04:00
|
|
|
break;
|
|
|
|
}
|
2021-10-17 19:07:43 -04:00
|
|
|
switch (info.type) {
|
|
|
|
case LFS_TYPE_REG: {
|
2021-10-17 12:04:23 -04:00
|
|
|
resp.flags = 0;
|
|
|
|
resp.file_size = info.size;
|
|
|
|
break;
|
2021-10-17 19:07:43 -04:00
|
|
|
}
|
|
|
|
case LFS_TYPE_DIR: {
|
|
|
|
resp.flags = 1;
|
|
|
|
resp.file_size = 0;
|
|
|
|
break;
|
2021-10-17 12:04:23 -04:00
|
|
|
}
|
|
|
|
}
|
2021-10-21 00:02:50 -04:00
|
|
|
|
2021-10-21 23:34:23 -04:00
|
|
|
// strcpy(resp.path, info.name);
|
2021-10-17 12:04:23 -04:00
|
|
|
resp.path_length = strlen(info.name);
|
2021-10-21 00:02:50 -04:00
|
|
|
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(ListDirResponse));
|
2021-10-21 23:34:23 -04:00
|
|
|
os_mbuf_append(om, info.name, resp.path_length);
|
2021-10-17 19:07:43 -04:00
|
|
|
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
|
2021-10-21 00:02:50 -04:00
|
|
|
/*
|
2021-10-21 23:34:23 -04:00
|
|
|
* 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...
|
|
|
|
*/
|
2021-10-19 21:30:04 -04:00
|
|
|
vTaskDelay(100); // Allow stuff to actually go out over the BLE conn
|
2021-10-17 12:04:23 -04:00
|
|
|
resp.entry++;
|
|
|
|
}
|
2021-10-21 00:02:50 -04:00
|
|
|
assert(fs.DirClose(&dir) == 0);
|
2021-10-17 12:04:23 -04:00
|
|
|
resp.file_size = 0;
|
|
|
|
resp.path_length = 0;
|
|
|
|
resp.flags = 0;
|
2021-10-21 00:02:50 -04:00
|
|
|
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(ListDirResponse));
|
2021-10-17 19:07:43 -04:00
|
|
|
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
|
2021-10-17 18:07:43 -04:00
|
|
|
break;
|
2021-10-17 12:04:23 -04:00
|
|
|
}
|
2021-10-21 00:02:50 -04:00
|
|
|
default:
|
|
|
|
break;
|
2021-10-17 12:04:23 -04:00
|
|
|
}
|
2021-10-21 00:02:50 -04:00
|
|
|
NRF_LOG_INFO("[FS_S] -> done ");
|
2021-10-19 21:30:04 -04:00
|
|
|
systemTask.PushMessage(Pinetime::System::Messages::StopFileTransfer);
|
2021-10-16 21:09:26 -04:00
|
|
|
return 0;
|
|
|
|
}
|
2021-10-21 00:02:50 -04:00
|
|
|
|
2021-10-18 00:20:26 -04:00
|
|
|
// Loads resp with file data given a valid filepath header and resp
|
|
|
|
void FSService::prepareReadDataResp(ReadHeader* header, ReadResponse* resp) {
|
2021-10-21 00:02:50 -04:00
|
|
|
// uint16_t plen = header->pathlen;
|
2021-10-18 00:20:26 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|