Merge pull request #756 from geekbozu/BLE_FS
BLE FS Using adafruits Simple (not fast) BLE FS Api
This commit is contained in:
commit
bccd77d5c9
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
|
330
src/components/ble/FSService.cpp
Normal file
330
src/components/ble/FSService.cpp
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
191
src/components/ble/FSService.h
Normal file
191
src/components/ble/FSService.h
Normal file
|
@ -0,0 +1,191 @@
|
|||
#pragma once
|
||||
#define min // workaround: nimble's min/max macros conflict with libstdc++
|
||||
#define max
|
||||
#include <host/ble_gap.h>
|
||||
#undef max
|
||||
#undef min
|
||||
|
||||
#include "components/fs/FS.h"
|
||||
|
||||
namespace Pinetime {
|
||||
namespace System {
|
||||
class SystemTask;
|
||||
}
|
||||
namespace Controllers {
|
||||
class Ble;
|
||||
class FSService {
|
||||
public:
|
||||
FSService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::FS& fs);
|
||||
void Init();
|
||||
|
||||
int OnFSServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context);
|
||||
void NotifyFSRaw(uint16_t connectionHandle);
|
||||
|
||||
private:
|
||||
Pinetime::System::SystemTask& systemTask;
|
||||
Pinetime::Controllers::FS& fs;
|
||||
static constexpr uint16_t FSServiceId {0xFEBB};
|
||||
static constexpr uint16_t fsVersionId {0x0100};
|
||||
static constexpr uint16_t fsTransferId {0x0200};
|
||||
uint16_t fsVersion = {0x0004};
|
||||
static constexpr uint16_t maxpathlen = 256;
|
||||
static constexpr ble_uuid16_t fsServiceUuid {
|
||||
.u {.type = BLE_UUID_TYPE_16},
|
||||
.value = {0xFEBB}}; // {0x72, 0x65, 0x66, 0x73, 0x6e, 0x61, 0x72, 0x54, 0x65, 0x6c, 0x69, 0x46, 0xBB, 0xFE, 0xAF, 0xAD}};
|
||||
|
||||
static constexpr ble_uuid128_t fsVersionUuid {
|
||||
.u {.type = BLE_UUID_TYPE_128},
|
||||
.value = {0x72, 0x65, 0x66, 0x73, 0x6e, 0x61, 0x72, 0x54, 0x65, 0x6c, 0x69, 0x46, 0x00, 0x01, 0xAF, 0xAD}};
|
||||
|
||||
static constexpr ble_uuid128_t fsTransferUuid {
|
||||
.u {.type = BLE_UUID_TYPE_128},
|
||||
.value = {0x72, 0x65, 0x66, 0x73, 0x6e, 0x61, 0x72, 0x54, 0x65, 0x6c, 0x69, 0x46, 0x00, 0x02, 0xAF, 0xAD}};
|
||||
|
||||
struct ble_gatt_chr_def characteristicDefinition[3];
|
||||
struct ble_gatt_svc_def serviceDefinition[2];
|
||||
uint16_t versionCharacteristicHandle;
|
||||
uint16_t transferCharacteristicHandle;
|
||||
|
||||
enum class commands : uint8_t {
|
||||
INVALID = 0x00,
|
||||
READ = 0x10,
|
||||
READ_DATA = 0x11,
|
||||
READ_PACING = 0x12,
|
||||
WRITE = 0x20,
|
||||
WRITE_PACING = 0x21,
|
||||
WRITE_DATA = 0x22,
|
||||
DELETE = 0x30,
|
||||
DELETE_STATUS = 0x31,
|
||||
MKDIR = 0x40,
|
||||
MKDIR_STATUS = 0x41,
|
||||
LISTDIR = 0x50,
|
||||
LISTDIR_ENTRY = 0x51,
|
||||
MOVE = 0x60,
|
||||
MOVE_STATUS = 0x61
|
||||
};
|
||||
enum class FSState : uint8_t {
|
||||
IDLE = 0x00,
|
||||
READ = 0x01,
|
||||
WRITE = 0x02,
|
||||
};
|
||||
FSState state;
|
||||
char filepath[maxpathlen]; // TODO ..ugh fixed filepath len
|
||||
int fileSize;
|
||||
using ReadHeader = struct __attribute__((packed)) {
|
||||
commands command;
|
||||
uint8_t padding;
|
||||
uint16_t pathlen;
|
||||
uint32_t chunkoff;
|
||||
uint32_t chunksize;
|
||||
char pathstr[];
|
||||
};
|
||||
|
||||
using ReadResponse = struct __attribute__((packed)) {
|
||||
commands command;
|
||||
uint8_t status;
|
||||
uint16_t padding;
|
||||
uint32_t chunkoff;
|
||||
uint32_t totallen;
|
||||
uint32_t chunklen;
|
||||
uint8_t chunk[];
|
||||
};
|
||||
using ReadPacing = struct __attribute__((packed)) {
|
||||
commands command;
|
||||
uint8_t status;
|
||||
uint16_t padding;
|
||||
uint32_t chunkoff;
|
||||
uint32_t chunksize;
|
||||
};
|
||||
|
||||
using WriteHeader = struct __attribute__((packed)) {
|
||||
commands command;
|
||||
uint8_t padding;
|
||||
uint16_t pathlen;
|
||||
uint32_t offset;
|
||||
uint64_t modTime;
|
||||
uint32_t totalSize;
|
||||
char pathstr[];
|
||||
};
|
||||
|
||||
using WriteResponse = struct __attribute__((packed)) {
|
||||
commands command;
|
||||
uint8_t status;
|
||||
uint16_t padding;
|
||||
uint32_t offset;
|
||||
uint64_t modTime;
|
||||
uint32_t freespace;
|
||||
};
|
||||
|
||||
using WritePacing = struct __attribute__((packed)) {
|
||||
commands command;
|
||||
uint8_t status;
|
||||
uint16_t padding;
|
||||
uint32_t offset;
|
||||
uint32_t dataSize;
|
||||
uint8_t data[];
|
||||
};
|
||||
using ListDirHeader = struct __attribute__((packed)) {
|
||||
commands command;
|
||||
uint8_t padding;
|
||||
uint16_t pathlen;
|
||||
char pathstr[];
|
||||
};
|
||||
|
||||
using ListDirResponse = struct __attribute__((packed)) {
|
||||
commands command;
|
||||
uint8_t status;
|
||||
uint16_t path_length;
|
||||
uint32_t entry;
|
||||
uint32_t totalentries;
|
||||
uint32_t flags;
|
||||
uint64_t modification_time;
|
||||
uint32_t file_size;
|
||||
char path[];
|
||||
};
|
||||
|
||||
using MKDirHeader = struct __attribute__((packed)) {
|
||||
commands command;
|
||||
uint8_t padding;
|
||||
uint16_t pathlen;
|
||||
uint32_t padding2;
|
||||
uint64_t time;
|
||||
char pathstr[];
|
||||
};
|
||||
|
||||
using MKDirResponse = struct __attribute__((packed)) {
|
||||
commands command;
|
||||
uint8_t status;
|
||||
uint32_t padding1;
|
||||
uint16_t padding2;
|
||||
uint64_t modification_time;
|
||||
};
|
||||
|
||||
using DelHeader = struct __attribute__((packed)) {
|
||||
commands command;
|
||||
uint8_t padding;
|
||||
uint16_t pathlen;
|
||||
char pathstr[];
|
||||
};
|
||||
|
||||
using DelResponse = struct __attribute__((packed)) {
|
||||
commands command;
|
||||
uint8_t status;
|
||||
};
|
||||
using MoveHeader = struct __attribute__((packed)) {
|
||||
commands command;
|
||||
uint8_t padding;
|
||||
uint16_t OldPathLength;
|
||||
uint16_t NewPathLength;
|
||||
char pathstr[];
|
||||
};
|
||||
|
||||
using MoveResponse = struct __attribute__((packed)) {
|
||||
commands command;
|
||||
uint8_t status;
|
||||
};
|
||||
|
||||
int FSCommandHandler(uint16_t connectionHandle, os_mbuf* om);
|
||||
void prepareReadDataResp(ReadHeader* header, ReadResponse* resp);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
|
|||
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
|
||||
Controllers::HeartRateController& heartRateController,
|
||||
Controllers::MotionController& motionController,
|
||||
Pinetime::Controllers::FS& fs)
|
||||
Controllers::FS& fs)
|
||||
: systemTask {systemTask},
|
||||
bleController {bleController},
|
||||
dateTimeController {dateTimeController},
|
||||
|
@ -50,6 +50,7 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
|
|||
immediateAlertService {systemTask, notificationManager},
|
||||
heartRateService {systemTask, heartRateController},
|
||||
motionService {systemTask, motionController},
|
||||
fsService {systemTask, fs},
|
||||
serviceDiscovery({¤tTimeClient, &alertNotificationClient}) {
|
||||
}
|
||||
|
||||
|
@ -97,6 +98,7 @@ void NimbleController::Init() {
|
|||
immediateAlertService.Init();
|
||||
heartRateService.Init();
|
||||
motionService.Init();
|
||||
fsService.Init();
|
||||
|
||||
int rc;
|
||||
rc = ble_hs_util_ensure_addr(0);
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "components/ble/MotionService.h"
|
||||
#include "components/ble/weather/WeatherService.h"
|
||||
#include "components/fs/FS.h"
|
||||
#include "components/ble/FSService.h"
|
||||
|
||||
namespace Pinetime {
|
||||
namespace Drivers {
|
||||
|
@ -110,6 +111,7 @@ namespace Pinetime {
|
|||
HeartRateService heartRateService;
|
||||
MotionService motionService;
|
||||
ServiceDiscovery serviceDiscovery;
|
||||
FSService fsService;
|
||||
|
||||
uint8_t addrType;
|
||||
uint16_t connectionHandle = BLE_HS_CONN_HANDLE_NONE;
|
||||
|
|
|
@ -5,29 +5,28 @@
|
|||
|
||||
using namespace Pinetime::Controllers;
|
||||
|
||||
FS::FS(Pinetime::Drivers::SpiNorFlash& driver) :
|
||||
flashDriver{ driver },
|
||||
lfsConfig{
|
||||
.context = this,
|
||||
.read = SectorRead,
|
||||
.prog = SectorProg,
|
||||
.erase = SectorErase,
|
||||
.sync = SectorSync,
|
||||
FS::FS(Pinetime::Drivers::SpiNorFlash& driver)
|
||||
: flashDriver {driver},
|
||||
lfsConfig {
|
||||
.context = this,
|
||||
.read = SectorRead,
|
||||
.prog = SectorProg,
|
||||
.erase = SectorErase,
|
||||
.sync = SectorSync,
|
||||
|
||||
.read_size = 16,
|
||||
.prog_size = 8,
|
||||
.block_size = blockSize,
|
||||
.block_count = size / blockSize,
|
||||
.block_cycles = 1000u,
|
||||
.read_size = 16,
|
||||
.prog_size = 8,
|
||||
.block_size = blockSize,
|
||||
.block_count = size / blockSize,
|
||||
.block_cycles = 1000u,
|
||||
|
||||
.cache_size = 16,
|
||||
.lookahead_size = 16,
|
||||
|
||||
.name_max = 50,
|
||||
.attr_max = 50,
|
||||
}
|
||||
{ }
|
||||
.cache_size = 16,
|
||||
.lookahead_size = 16,
|
||||
|
||||
.name_max = 50,
|
||||
.attr_max = 50,
|
||||
} {
|
||||
}
|
||||
|
||||
void FS::Init() {
|
||||
|
||||
|
@ -48,7 +47,6 @@ void FS::Init() {
|
|||
VerifyResource();
|
||||
LVGLFileSystemInit();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void FS::VerifyResource() {
|
||||
|
@ -56,7 +54,7 @@ void FS::VerifyResource() {
|
|||
resourcesValid = true;
|
||||
}
|
||||
|
||||
int FS::FileOpen(lfs_file_t* file_p, const char* fileName, const int flags) {
|
||||
int FS::FileOpen(lfs_file_t* file_p, const char* fileName, const int flags) {
|
||||
return lfs_file_open(&lfs, file_p, fileName, flags);
|
||||
}
|
||||
|
||||
|
@ -80,27 +78,31 @@ int FS::FileDelete(const char* fileName) {
|
|||
return lfs_remove(&lfs, fileName);
|
||||
}
|
||||
|
||||
int FS::DirOpen(const char* path, lfs_dir_t* lfs_dir) {
|
||||
return lfs_dir_open(&lfs, lfs_dir, path);
|
||||
}
|
||||
|
||||
int FS::DirClose(lfs_dir_t* lfs_dir) {
|
||||
return lfs_dir_close(&lfs, lfs_dir);
|
||||
}
|
||||
|
||||
int FS::DirRead(lfs_dir_t* dir, lfs_info* info) {
|
||||
return lfs_dir_read(&lfs, dir, info);
|
||||
}
|
||||
int FS::DirRewind(lfs_dir_t* dir) {
|
||||
return lfs_dir_rewind(&lfs, dir);
|
||||
}
|
||||
int FS::DirCreate(const char* path) {
|
||||
return lfs_mkdir(&lfs, path);
|
||||
}
|
||||
|
||||
// Delete directory and all files inside
|
||||
int FS::DirDelete(const char* path) {
|
||||
|
||||
lfs_dir_t lfs_dir;
|
||||
lfs_info entryInfo;
|
||||
|
||||
int err;
|
||||
err = lfs_dir_open(&lfs, &lfs_dir, path);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
while (lfs_dir_read(&lfs, &lfs_dir, &entryInfo)) {
|
||||
lfs_remove(&lfs, entryInfo.name);
|
||||
}
|
||||
lfs_dir_close(&lfs, &lfs_dir);
|
||||
return LFS_ERR_OK;
|
||||
int FS::Rename(const char* oldPath, const char* newPath){
|
||||
return lfs_rename(&lfs,oldPath,newPath);
|
||||
}
|
||||
int FS::Stat(const char* path, lfs_info* info) {
|
||||
return lfs_stat(&lfs, path, info);
|
||||
}
|
||||
lfs_ssize_t FS::GetFSSize() {
|
||||
return lfs_fs_size(&lfs);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -141,17 +143,17 @@ int FS::SectorRead(const struct lfs_config* c, lfs_block_t block, lfs_off_t off,
|
|||
|
||||
namespace {
|
||||
lv_fs_res_t lvglOpen(lv_fs_drv_t* drv, void* file_p, const char* path, lv_fs_mode_t mode) {
|
||||
|
||||
lfs_file_t* file = static_cast<lfs_file_t*>(file_p);
|
||||
FS* filesys = static_cast<FS*>(drv->user_data);
|
||||
filesys->FileOpen(file, path, LFS_O_RDONLY);
|
||||
|
||||
if (file->type == 0) {
|
||||
return LV_FS_RES_FS_ERR;
|
||||
}
|
||||
else {
|
||||
return LV_FS_RES_OK;
|
||||
int res = filesys->FileOpen(file, path, LFS_O_RDONLY);
|
||||
if (res == 0) {
|
||||
if (file->type == 0) {
|
||||
return LV_FS_RES_FS_ERR;
|
||||
} else {
|
||||
return LV_FS_RES_OK;
|
||||
}
|
||||
}
|
||||
return LV_FS_RES_NOT_EX;
|
||||
}
|
||||
|
||||
lv_fs_res_t lvglClose(lv_fs_drv_t* drv, void* file_p) {
|
||||
|
@ -193,5 +195,4 @@ void FS::LVGLFileSystemInit() {
|
|||
fs_drv.user_data = this;
|
||||
|
||||
lv_fs_drv_register(&fs_drv);
|
||||
|
||||
}
|
|
@ -21,37 +21,49 @@ namespace Pinetime {
|
|||
|
||||
int FileDelete(const char* fileName);
|
||||
|
||||
int DirOpen(const char* path, lfs_dir_t* lfs_dir);
|
||||
int DirClose(lfs_dir_t* lfs_dir);
|
||||
int DirRead(lfs_dir_t* dir, lfs_info* info);
|
||||
int DirRewind(lfs_dir_t* dir);
|
||||
int DirCreate(const char* path);
|
||||
int DirDelete(const char* path);
|
||||
|
||||
|
||||
lfs_ssize_t GetFSSize();
|
||||
int Rename(const char* oldPath, const char* newPath);
|
||||
int Stat(const char* path, lfs_info* info);
|
||||
void VerifyResource();
|
||||
|
||||
private:
|
||||
static size_t getSize() {
|
||||
return size;
|
||||
}
|
||||
static size_t getBlockSize() {
|
||||
return blockSize;
|
||||
}
|
||||
|
||||
private:
|
||||
Pinetime::Drivers::SpiNorFlash& flashDriver;
|
||||
|
||||
/*
|
||||
* External Flash MAP (4 MBytes)
|
||||
*
|
||||
* 0x000000 +---------------------------------------+
|
||||
* | Bootloader Assets |
|
||||
* | 256 KBytes |
|
||||
* | |
|
||||
* 0x040000 +---------------------------------------+
|
||||
* | OTA |
|
||||
* | 464 KBytes |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* 0x0B4000 +---------------------------------------+
|
||||
* | File System |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* 0x400000 +---------------------------------------+
|
||||
*
|
||||
*/
|
||||
* External Flash MAP (4 MBytes)
|
||||
*
|
||||
* 0x000000 +---------------------------------------+
|
||||
* | Bootloader Assets |
|
||||
* | 256 KBytes |
|
||||
* | |
|
||||
* 0x040000 +---------------------------------------+
|
||||
* | OTA |
|
||||
* | 464 KBytes |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* 0x0B4000 +---------------------------------------+
|
||||
* | File System |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* 0x400000 +---------------------------------------+
|
||||
*
|
||||
*/
|
||||
static constexpr size_t startAddress = 0x0B4000;
|
||||
static constexpr size_t size = 0x34C000;
|
||||
static constexpr size_t blockSize = 4096;
|
||||
|
@ -65,7 +77,6 @@ namespace Pinetime {
|
|||
static int SectorErase(const struct lfs_config* c, lfs_block_t block);
|
||||
static int SectorProg(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size);
|
||||
static int SectorRead(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, void* buffer, lfs_size_t size);
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ namespace Pinetime {
|
|||
StopRinging,
|
||||
MeasureBatteryTimerExpired,
|
||||
BatteryPercentageUpdated,
|
||||
StartFileTransfer,
|
||||
StopFileTransfer,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -342,6 +342,19 @@ void SystemTask::Work() {
|
|||
doNotGoToSleep = false;
|
||||
xTimerStart(dimTimer, 0);
|
||||
break;
|
||||
case Messages::StartFileTransfer:
|
||||
NRF_LOG_INFO("[systemtask] FS Started");
|
||||
doNotGoToSleep = true;
|
||||
if (isSleeping && !isWakingUp)
|
||||
GoToRunning();
|
||||
//TODO add intent of fs access icon or something
|
||||
break;
|
||||
case Messages::StopFileTransfer:
|
||||
NRF_LOG_INFO("[systemtask] FS Stopped");
|
||||
doNotGoToSleep = false;
|
||||
xTimerStart(dimTimer, 0);
|
||||
//TODO add intent of fs access icon or something
|
||||
break;
|
||||
case Messages::OnTouchEvent:
|
||||
if (touchHandler.GetNewTouchInfo()) {
|
||||
touchHandler.UpdateLvglTouchPoint();
|
||||
|
|
Loading…
Reference in a new issue