Skip to content

Commit bccd77d

Browse files
authored
Merge pull request #756 from geekbozu/BLE_FS
BLE FS Using adafruits Simple (not fast) BLE FS Api
2 parents 42a5cdb + 1470489 commit bccd77d

9 files changed

Lines changed: 632 additions & 77 deletions

File tree

src/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ list(APPEND SOURCE_FILES
487487
components/ble/NavigationService.cpp
488488
displayapp/fonts/lv_font_navi_80.c
489489
components/ble/BatteryInformationService.cpp
490+
components/ble/FSService.cpp
490491
components/ble/ImmediateAlertService.cpp
491492
components/ble/ServiceDiscovery.cpp
492493
components/ble/HeartRateService.cpp
@@ -557,6 +558,7 @@ list(APPEND RECOVERY_SOURCE_FILES
557558
components/ble/MusicService.cpp
558559
components/ble/weather/WeatherService.cpp
559560
components/ble/BatteryInformationService.cpp
561+
components/ble/FSService.cpp
560562
components/ble/ImmediateAlertService.cpp
561563
components/ble/ServiceDiscovery.cpp
562564
components/ble/NavigationService.cpp
@@ -669,6 +671,7 @@ set(INCLUDE_FILES
669671
components/ble/DfuService.h
670672
components/firmwarevalidator/FirmwareValidator.h
671673
components/ble/BatteryInformationService.h
674+
components/ble/FSService.h
672675
components/ble/ImmediateAlertService.h
673676
components/ble/ServiceDiscovery.h
674677
components/ble/BleClient.h

src/components/ble/FSService.cpp

Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
#include <nrf_log.h>
2+
#include "FSService.h"
3+
#include "components/ble/BleController.h"
4+
#include "systemtask/SystemTask.h"
5+
6+
using namespace Pinetime::Controllers;
7+
8+
constexpr ble_uuid16_t FSService::fsServiceUuid;
9+
constexpr ble_uuid128_t FSService::fsVersionUuid;
10+
constexpr ble_uuid128_t FSService::fsTransferUuid;
11+
12+
int FSServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
13+
auto* fsService = static_cast<FSService*>(arg);
14+
return fsService->OnFSServiceRequested(conn_handle, attr_handle, ctxt);
15+
}
16+
17+
FSService::FSService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::FS& fs)
18+
: systemTask {systemTask},
19+
fs {fs},
20+
characteristicDefinition {{.uuid = &fsVersionUuid.u,
21+
.access_cb = FSServiceCallback,
22+
.arg = this,
23+
.flags = BLE_GATT_CHR_F_READ,
24+
.val_handle = &versionCharacteristicHandle},
25+
{
26+
.uuid = &fsTransferUuid.u,
27+
.access_cb = FSServiceCallback,
28+
.arg = this,
29+
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
30+
.val_handle = &transferCharacteristicHandle,
31+
},
32+
{0}},
33+
serviceDefinition {
34+
{/* Device Information Service */
35+
.type = BLE_GATT_SVC_TYPE_PRIMARY,
36+
.uuid = &fsServiceUuid.u,
37+
.characteristics = characteristicDefinition},
38+
{0},
39+
} {
40+
}
41+
42+
void FSService::Init() {
43+
int res = 0;
44+
res = ble_gatts_count_cfg(serviceDefinition);
45+
ASSERT(res == 0);
46+
47+
res = ble_gatts_add_svcs(serviceDefinition);
48+
ASSERT(res == 0);
49+
}
50+
51+
int FSService::OnFSServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context) {
52+
if (attributeHandle == versionCharacteristicHandle) {
53+
NRF_LOG_INFO("FS_S : handle = %d", versionCharacteristicHandle);
54+
int res = os_mbuf_append(context->om, &fsVersion, sizeof(fsVersion));
55+
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
56+
}
57+
if (attributeHandle == transferCharacteristicHandle) {
58+
return FSCommandHandler(connectionHandle, context->om);
59+
}
60+
return 0;
61+
}
62+
63+
int FSService::FSCommandHandler(uint16_t connectionHandle, os_mbuf* om) {
64+
auto command = static_cast<commands>(om->om_data[0]);
65+
NRF_LOG_INFO("[FS_S] -> FSCommandHandler Command %d", command);
66+
// Just always make sure we are awake...
67+
systemTask.PushMessage(Pinetime::System::Messages::StartFileTransfer);
68+
vTaskDelay(10);
69+
while (systemTask.IsSleeping()) {
70+
vTaskDelay(100); // 50ms
71+
}
72+
lfs_dir_t dir = {0};
73+
lfs_info info = {0};
74+
lfs_file f = {0};
75+
switch (command) {
76+
case commands::READ: {
77+
NRF_LOG_INFO("[FS_S] -> Read");
78+
auto* header = (ReadHeader*) om->om_data;
79+
uint16_t plen = header->pathlen;
80+
if (plen > maxpathlen) { //> counts for null term
81+
return -1;
82+
}
83+
memcpy(filepath, header->pathstr, plen);
84+
filepath[plen] = 0; // Copy and null teminate string
85+
ReadResponse resp;
86+
os_mbuf* om;
87+
resp.command = commands::READ_DATA;
88+
resp.status = 0x01;
89+
resp.chunkoff = header->chunkoff;
90+
int res = fs.Stat(filepath, &info);
91+
if (res == LFS_ERR_NOENT && info.type != LFS_TYPE_DIR) {
92+
resp.status = (int8_t) res;
93+
resp.chunklen = 0;
94+
resp.totallen = 0;
95+
om = ble_hs_mbuf_from_flat(&resp, sizeof(ReadResponse));
96+
} else {
97+
resp.chunklen = std::min(header->chunksize, info.size); // TODO add mtu somehow
98+
resp.totallen = info.size;
99+
fs.FileOpen(&f, filepath, LFS_O_RDONLY);
100+
fs.FileSeek(&f, header->chunkoff);
101+
uint8_t fileData[resp.chunklen] = {0};
102+
resp.chunklen = fs.FileRead(&f, fileData, resp.chunklen);
103+
om = ble_hs_mbuf_from_flat(&resp, sizeof(ReadResponse));
104+
os_mbuf_append(om, fileData, resp.chunklen);
105+
fs.FileClose(&f);
106+
}
107+
108+
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
109+
break;
110+
}
111+
case commands::READ_PACING: {
112+
NRF_LOG_INFO("[FS_S] -> Readpacing");
113+
auto* header = (ReadHeader*) om->om_data;
114+
ReadResponse resp;
115+
resp.command = commands::READ_DATA;
116+
resp.status = 0x01;
117+
resp.chunkoff = header->chunkoff;
118+
int res = fs.Stat(filepath, &info);
119+
if (res == LFS_ERR_NOENT && info.type != LFS_TYPE_DIR) {
120+
resp.status = (int8_t) res;
121+
resp.chunklen = 0;
122+
resp.totallen = 0;
123+
} else {
124+
resp.chunklen = std::min(header->chunksize, info.size); // TODO add mtu somehow
125+
resp.totallen = info.size;
126+
fs.FileOpen(&f, filepath, LFS_O_RDONLY);
127+
fs.FileSeek(&f, header->chunkoff);
128+
}
129+
os_mbuf* om;
130+
if (resp.chunklen > 0) {
131+
uint8_t fileData[resp.chunklen] = {0};
132+
resp.chunklen = fs.FileRead(&f, fileData, resp.chunklen);
133+
om = ble_hs_mbuf_from_flat(&resp, sizeof(ReadResponse));
134+
os_mbuf_append(om, fileData, resp.chunklen);
135+
} else {
136+
resp.chunklen = 0;
137+
om = ble_hs_mbuf_from_flat(&resp, sizeof(ReadResponse));
138+
}
139+
fs.FileClose(&f);
140+
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
141+
break;
142+
}
143+
case commands::WRITE: {
144+
NRF_LOG_INFO("[FS_S] -> Write");
145+
auto* header = (WriteHeader*) om->om_data;
146+
uint16_t plen = header->pathlen;
147+
if (plen > maxpathlen) { //> counts for null term
148+
return -1; // TODO make this actually return a BLE notif
149+
}
150+
memcpy(filepath, header->pathstr, plen);
151+
filepath[plen] = 0; // Copy and null teminate string
152+
fileSize = header->totalSize;
153+
WriteResponse resp;
154+
resp.command = commands::WRITE_PACING;
155+
resp.offset = header->offset;
156+
resp.modTime = 0;
157+
158+
int res = fs.FileOpen(&f, filepath, LFS_O_RDWR | LFS_O_CREAT);
159+
if (res == 0) {
160+
fs.FileClose(&f);
161+
resp.status = (res == 0) ? 0x01 : (int8_t) res;
162+
}
163+
resp.freespace = std::min(fs.getSize() - (fs.GetFSSize() * fs.getBlockSize()), fileSize - header->offset);
164+
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(WriteResponse));
165+
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
166+
break;
167+
}
168+
case commands::WRITE_DATA: {
169+
NRF_LOG_INFO("[FS_S] -> WriteData");
170+
auto* header = (WritePacing*) om->om_data;
171+
WriteResponse resp;
172+
resp.command = commands::WRITE_PACING;
173+
resp.offset = header->offset;
174+
int res = 0;
175+
176+
if (!(res = fs.FileOpen(&f, filepath, LFS_O_RDWR | LFS_O_CREAT))) {
177+
if ((res = fs.FileSeek(&f, header->offset)) >= 0) {
178+
res = fs.FileWrite(&f, header->data, header->dataSize);
179+
}
180+
fs.FileClose(&f);
181+
}
182+
if (res < 0) {
183+
resp.status = (int8_t) res;
184+
}
185+
resp.freespace = std::min(fs.getSize() - (fs.GetFSSize() * fs.getBlockSize()), fileSize - header->offset);
186+
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(WriteResponse));
187+
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
188+
break;
189+
}
190+
case commands::DELETE: {
191+
NRF_LOG_INFO("[FS_S] -> Delete");
192+
auto* header = (DelHeader*) om->om_data;
193+
uint16_t plen = header->pathlen;
194+
char path[plen + 1] = {0};
195+
memcpy(path, header->pathstr, plen);
196+
path[plen] = 0; // Copy and null teminate string
197+
DelResponse resp {};
198+
resp.command = commands::DELETE_STATUS;
199+
int res = fs.FileDelete(path);
200+
resp.status = (res == 0) ? 0x01 : (int8_t) res;
201+
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(DelResponse));
202+
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
203+
break;
204+
}
205+
case commands::MKDIR: {
206+
NRF_LOG_INFO("[FS_S] -> MKDir");
207+
auto* header = (MKDirHeader*) om->om_data;
208+
uint16_t plen = header->pathlen;
209+
char path[plen + 1] = {0};
210+
memcpy(path, header->pathstr, plen);
211+
path[plen] = 0; // Copy and null teminate string
212+
MKDirResponse resp {};
213+
resp.command = commands::MKDIR_STATUS;
214+
resp.modification_time = 0;
215+
int res = fs.DirCreate(path);
216+
resp.status = (res == 0) ? 0x01 : (int8_t) res;
217+
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(MKDirResponse));
218+
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
219+
break;
220+
}
221+
case commands::LISTDIR: {
222+
NRF_LOG_INFO("[FS_S] -> ListDir");
223+
ListDirHeader* header = (ListDirHeader*) om->om_data;
224+
uint16_t plen = header->pathlen;
225+
char path[plen + 1] = {0};
226+
path[plen] = 0; // Copy and null teminate string
227+
memcpy(path, header->pathstr, plen);
228+
229+
ListDirResponse resp {};
230+
231+
resp.command = commands::LISTDIR_ENTRY;
232+
resp.status = 0x01;
233+
resp.totalentries = 0;
234+
resp.entry = 0;
235+
resp.modification_time = 0;
236+
int res = fs.DirOpen(path, &dir);
237+
if (res != 0) {
238+
resp.status = (int8_t) res;
239+
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(ListDirResponse));
240+
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
241+
break;
242+
};
243+
while (fs.DirRead(&dir, &info)) {
244+
resp.totalentries++;
245+
}
246+
fs.DirRewind(&dir);
247+
while (true) {
248+
res = fs.DirRead(&dir, &info);
249+
if (res <= 0) {
250+
break;
251+
}
252+
switch (info.type) {
253+
case LFS_TYPE_REG: {
254+
resp.flags = 0;
255+
resp.file_size = info.size;
256+
break;
257+
}
258+
case LFS_TYPE_DIR: {
259+
resp.flags = 1;
260+
resp.file_size = 0;
261+
break;
262+
}
263+
}
264+
265+
// strcpy(resp.path, info.name);
266+
resp.path_length = strlen(info.name);
267+
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(ListDirResponse));
268+
os_mbuf_append(om, info.name, resp.path_length);
269+
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
270+
/*
271+
* Todo Figure out how to know when the previous Notify was TX'd
272+
* For now just delay 100ms to make sure that the data went out...
273+
*/
274+
vTaskDelay(100); // Allow stuff to actually go out over the BLE conn
275+
resp.entry++;
276+
}
277+
assert(fs.DirClose(&dir) == 0);
278+
resp.file_size = 0;
279+
resp.path_length = 0;
280+
resp.flags = 0;
281+
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(ListDirResponse));
282+
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
283+
break;
284+
}
285+
case commands::MOVE: {
286+
NRF_LOG_INFO("[FS_S] -> Move");
287+
MoveHeader* header = (MoveHeader*) om->om_data;
288+
uint16_t plen = header->OldPathLength;
289+
// Null Terminate string
290+
header->pathstr[plen] = 0;
291+
char path[header->NewPathLength + 1] = {0};
292+
memcpy(path, &header->pathstr[plen + 1], header->NewPathLength);
293+
path[header->NewPathLength] = 0; // Copy and null teminate string
294+
MoveResponse resp {};
295+
resp.command = commands::MOVE_STATUS;
296+
int8_t res = (int8_t) fs.Rename(header->pathstr, path);
297+
resp.status = (res == 0) ? 1 : res;
298+
auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(MoveResponse));
299+
ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
300+
}
301+
default:
302+
break;
303+
}
304+
NRF_LOG_INFO("[FS_S] -> done ");
305+
systemTask.PushMessage(Pinetime::System::Messages::StopFileTransfer);
306+
return 0;
307+
}
308+
309+
// Loads resp with file data given a valid filepath header and resp
310+
void FSService::prepareReadDataResp(ReadHeader* header, ReadResponse* resp) {
311+
// uint16_t plen = header->pathlen;
312+
resp->command = commands::READ_DATA;
313+
resp->chunkoff = header->chunkoff;
314+
resp->status = 0x01;
315+
struct lfs_info info = {};
316+
int res = fs.Stat(filepath, &info);
317+
if (res == LFS_ERR_NOENT && info.type != LFS_TYPE_DIR) {
318+
resp->status = 0x03;
319+
resp->chunklen = 0;
320+
resp->totallen = 0;
321+
} else {
322+
lfs_file f;
323+
resp->chunklen = std::min(header->chunksize, info.size);
324+
resp->totallen = info.size;
325+
fs.FileOpen(&f, filepath, LFS_O_RDONLY);
326+
fs.FileSeek(&f, header->chunkoff);
327+
resp->chunklen = fs.FileRead(&f, resp->chunk, resp->chunklen);
328+
fs.FileClose(&f);
329+
}
330+
}

0 commit comments

Comments
 (0)