10000 v3 BLE file service: Add file modification times · unwiredben/circuitpython@64ff8d9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 64ff8d9

Browse files
committed
v3 BLE file service: Add file modification times
1 parent 9b2d8ad commit 64ff8d9

File tree

4 files changed

+130
-40
lines changed

4 files changed

+130
-40
lines changed

ports/nrf/fatfs_port.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@
3030
#include "shared-bindings/rtc/RTC.h"
3131
#include "shared-bindings/time/__init__.h"
3232

33+
DWORD _time_override = 0;
3334
DWORD get_fattime(void) {
35+
if (_time_override > 0) {
36+
return _time_override;
37+
}
3438
#if CIRCUITPY_RTC
3539
timeutils_struct_time_t tm;
3640
common_hal_rtc_get_time(&tm);
@@ -40,3 +44,7 @@ DWORD get_fattime(void) {
4044
return ((2016 - 1980) << 25) | ((9) << 21) | ((1) << 16) | ((16) << 11) | ((43) << 5) | (35 / 2);
4145
#endif
4246
}
47+
48+
void override_fattime(DWORD time) {
49+
_time_override = time;
50+
}

supervisor/fatfs_port.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#ifndef MICROPY_INCLUDED_SUPERVISOR_FATFS_PORT_H
28+
#define MICROPY_INCLUDED_SUPERVISOR_FATFS_PORT_H
29+
30+
#include "lib/oofatfs/ff.h"
31+
32+
void override_fattime(DWORD time);
33+
34+
#endif // MICROPY_INCLUDED_SUPERVISOR_FATFS_PORT_H

supervisor/shared/bluetooth/file_transfer.c

Lines changed: 63 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include "extmod/vfs.h"
3030
#include "extmod/vfs_fat.h"
31+
#include "lib/timeutils/timeutils.h"
3132

3233
#include "shared-bindings/_bleio/__init__.h"
3334
#include "shared-bindings/_bleio/Adapter.h"
@@ -41,6 +42,7 @@
4142

4243
#include "common-hal/_bleio/__init__.h"
4344

45+
#include "supervisor/fatfs_port.h"
4446
#include "supervisor/shared/autoreload.h"
4547
#include "supervisor/shared/bluetooth/file_transfer_protocol.h"
4648
#include "supervisor/shared/tick.h"
@@ -98,7 +100,7 @@ void supervisor_start_bluetooth_file_transfer(void) {
98100
NULL, // no initial value
99101
NULL); // no description
100102

101-
uint32_t version = 2;
103+
uint32_t version = 3;
102104
mp_buffer_info_t bufinfo;
103105
bufinfo.buf = &version;
104106
bufinfo.len = sizeof(version);
@@ -131,12 +133,23 @@ void supervisor_start_bluetooth_file_transfer(void) {
131133
#define ANY_COMMAND 0x00
132134
#define THIS_COMMAND 0x01
133135

136+
uint64_t truncate_time(uint64_t input_time, DWORD *fattime) {
137+
timeutils_struct_time_t tm;
138+
uint64_t seconds_since_epoch = timeutils_seconds_since_epoch_from_nanoseconds_since_1970(input_time);
139+
timeutils_seconds_since_epoch_to_struct_time(seconds_since_epoch, &tm);
140+
uint64_t truncated_time = timeutils_nanoseconds_since_epoch_to_nanoseconds_since_1970((seconds_since_epoch / 2) * 2 * 1000000000);
141+
142+
*fattime = ((tm.tm_year - 1980) << 25) | (tm.tm_mon << 21) | (tm.tm_mday << 16) |
143+
(tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec >> 1);
144+
return truncated_time;
145+
}
146+
134147
// Used by read and write.
135148
STATIC FIL active_file;
136149
STATIC uint8_t _process_read(const uint8_t *raw_buf, size_t command_len) {
137150
struct read_command *command = (struct read_command *)raw_buf;
138-
size_t header_size = 12;
139-
size_t response_size = 16;
151+
size_t header_size = sizeof(struct read_command);
152+
size_t response_size = sizeof(struct read_data);
140153
uint8_t data_buffer[response_size];
141154
struct read_data response;
142155
response.command = READ_DATA;
@@ -190,38 +203,38 @@ STATIC uint8_t _process_read(const uint8_t *raw_buf, size_t command_len) {
190203
return READ_PACING;
191204
}
192205

193-
STATIC uint8_t _process_read_pacing(const uint8_t *command, size_t command_len) {
194-
size_t response_size = 4 * sizeof(uint32_t);
195-
uint32_t response[response_size / sizeof(uint32_t)];
196-
uint8_t *response_bytes = (uint8_t *)response;
197-
response_bytes[0] = READ_DATA;
198-
response_bytes[1] = STATUS_OK;
199-
uint32_t offset = ((uint32_t *)command)[1];
200-
uint32_t chunk_size = ((uint32_t *)command)[2];
206+
STATIC uint8_t _process_read_pacing(const uint8_t *raw_buf, size_t command_len) {
207+
struct read_pacing *command = (struct read_pacing *)raw_buf;
208+
struct read_data response;
209+
response.command = READ_DATA;
210+
response.status = STATUS_OK;
211+
size_t response_size = sizeof(struct read_data);
212+
201213
uint32_t total_length = f_size(&active_file);
202214
// Write out the response header.
203-
chunk_size = MIN(chunk_size, total_length - offset);
204-
response[1] = offset;
205-
response[2] = total_length;
206-
response[3] = chunk_size;
215+
uint32_t chunk_size = MIN(command->chunk_size, total_length - command->chunk_offset);
216+
response.chunk_offset = command->chunk_offset;
217+
response.total_length = total_length;
218+
response.data_size = chunk_size;
207219
common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, response_size, NULL, 0);
208-
f_lseek(&active_file, offset);
220+
f_lseek(&active_file, command->chunk_offset);
209221
// Write out the chunk contents. We can do this in small pieces because PacketBuffer
210222
// will assemble them into larger packets of its own.
211223
size_t chunk_offset = 0;
224+
uint8_t data[20];
212225
while (chunk_offset < chunk_size) {
213226
size_t quantity_read;
214-
size_t read_size = MIN(chunk_size - chunk_offset, response_size);
215-
FRESULT result = f_read(&active_file, response, read_size, &quantity_read);
227+
size_t read_size = MIN(chunk_size - chunk_offset, sizeof(data));
228+
FRESULT result = f_read(&active_file, &data, read_size, &quantity_read);
216229
if (quantity_read == 0 || result != FR_OK) {
217230
// TODO: If we can't read everything, then the file must have been shortened. Maybe we
218231
// should return 0s to pad it out.
219232
break;
220233
}
221-
common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, quantity_read, NULL, 0);
234+
common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&data, quantity_read, NULL, 0);
222235
chunk_offset += quantity_read;
223236
}
224-
if ((offset + chunk_size) >= total_length) {
237+
if ((chunk_offset + chunk_size) >= total_length) {
225238
f_close(&active_file);
226239
return ANY_COMMAND;
227240
}
@@ -230,10 +243,11 @@ STATIC uint8_t _process_read_pacing(const uint8_t *command, size_t command_len)
230243

231244
// Used by write and write data to know when the write is complete.
232245
STATIC size_t total_write_length;
246+
STATIC uint64_t _truncated_time;
233247

234248
STATIC uint8_t _process_write(const uint8_t *raw_buf, size_t command_len) {
235249
struct write_command *command = (struct write_command *)raw_buf;
236-
size_t header_size = 12;
250+
size_t header_size = sizeof(struct write_command);
237251
struct write_pacing response;
238252
response.command = WRITE_PACING;
239253
response.status = STATUS_OK;
@@ -262,13 +276,17 @@ STATIC uint8_t _process_write(const uint8_t *raw_buf, size_t command_len) {
262276
#endif
263277

264278
FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs;
279+
DWORD fattime;
280+
_truncated_time = truncate_time(command->modification_time, &fattime);
281+
override_fattime(fattime);
265282
FRESULT result = f_open(fs, &active_file, path, FA_WRITE | FA_OPEN_ALWAYS);
266283
if (result != FR_OK) {
267284
response.status = STATUS_ERROR;
268285
common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0);
269286
#if CIRCUITPY_USB_MSC
270287
usb_msc_unlock();
271288
#endif
289+
override_fattime(0);
272290
return ANY_COMMAND;
273291
}
274292
// Write out the pacing response.
@@ -281,12 +299,14 @@ STATIC uint8_t _process_write(const uint8_t *raw_buf, size_t command_len) {
281299
f_lseek(&active_file, offset);
282300
f_truncate(&active_file);
283301
f_close(&active_file);
302+
override_fattime(0);
284303
#if CIRCUITPY_USB_MSC
285304
usb_msc_unlock();
286305
#endif
287306
}
288307
response.offset = offset;
289308
response.free_space = chunk_size;
309+
response.truncated_time = _truncated_time;
290310
common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0);
291311
if (chunk_size == 0) {
292312
// Don't reload until everything is written out of the packet buffer.
@@ -301,7 +321,7 @@ STATIC uint8_t _process_write(const uint8_t *raw_buf, size_t command_len) {
301321

302322
STATIC uint8_t _process_write_data(const uint8_t *raw_buf, size_t command_len) {
303323
struct write_data *command = (struct write_data *)raw_buf;
304-
size_t header_size = 12;
324+
size_t header_size = sizeof(struct write_data);
305325
struct write_pacing response;
306326
response.command = WRITE_PACING;
307327
response.status = STATUS_OK;
@@ -312,6 +332,7 @@ STATIC uint8_t _process_write_data(const uint8_t *raw_buf, size_t command_len) {
312332
#if CIRCUITPY_USB_MSC
313333
usb_msc_unlock();
314334
#endif
335+
override_fattime(0);
315336
return ANY_COMMAND;
316337
}
317338
// We need to receive another packet to have the full path.
@@ -329,17 +350,20 @@ STATIC uint8_t _process_write_data(const uint8_t *raw_buf, size_t command_len) {
329350
#if CIRCUITPY_USB_MSC
330351
usb_msc_unlock();
331352
#endif
353+
override_fattime(0);
332354
return ANY_COMMAND;
333355
}
334356
offset += command->data_size;
335357
// Align the next chunk to a sector boundary.
336358
size_t chunk_size = MIN(total_write_length - offset, 512);
337359
response.offset = offset;
338360
response.free_space = chunk_size;
361+
response.truncated_time = _truncated_time;
339362
common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0);
340363
if (total_write_length == offset) {
341364
f_truncate(&active_file);
342365
f_close(&active_file);
366+
override_fattime(0);
343367
#if CIRCUITPY_USB_MSC
344368
usb_msc_unlock();
345369
#endif
@@ -387,7 +411,7 @@ STATIC FRESULT _delete_directory_contents(FATFS *fs, const TCHAR *path) {
387411

388412
STATIC uint8_t _process_delete(const uint8_t *raw_buf, size_t command_len) {
389413
const struct delete_command *command = (struct delete_command *)raw_buf;
390-
size_t header_size = 4;
414+
size_t header_size = sizeof(struct delete_command);
391415
struct delete_status response;
392416
response.command = DELETE_STATUS;
393417
response.status = STATUS_OK;
@@ -423,7 +447,7 @@ STATIC uint8_t _process_delete(const uint8_t *raw_buf, size_t command_len) {
423447

424448
STATIC uint8_t _process_mkdir(const uint8_t *raw_buf, size_t command_len) {
425449
const struct mkdir_command *command = (struct mkdir_command *)raw_buf;
426-
size_t header_size = 4;
450+
size_t header_size = sizeof(struct mkdir_command);
427451
struct mkdir_status response;
428452
response.command = MKDIR_STATUS;
429453
response.status = STATUS_OK;
@@ -438,10 +462,14 @@ STATIC uint8_t _process_mkdir(const uint8_t *raw_buf, size_t command_len) {
438462
return THIS_COMMAND;
439463
}
440464
FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs;
441-
char *path = (char *)((uint8_t *)command) + header_size;
465+
char *path = (char *)command->path;
442466
// TODO: Check that the final character is a `/`
443467
path[command->path_length - 1] = '\0';
468+
DWORD fattime;
469+
response.truncated_time = truncate_time(command->modification_time, &fattime);
470+
override_fattime(fattime);
444471
FRESULT result = f_mkdir(fs, path);
472+
override_fattime(0);
445473
if (result != FR_OK) {
446474
response.status = STATUS_ERROR;
447475
}
@@ -452,8 +480,8 @@ STATIC uint8_t _process_mkdir(const uint8_t *raw_buf, size_t command_len) {
452480
STATIC uint8_t _process_listdir(uint8_t *raw_buf, size_t command_len) {
453481
const struct listdir_command *command = (struct listdir_command *)raw_buf;
454482
struct listdir_entry *entry = (struct listdir_entry *)raw_buf;
455-
size_t header_size = 4;
456-
size_t response_size = 5 * sizeof(uint32_t);
483+
size_t header_size = sizeof(struct listdir_command);
484+
size_t response_size = sizeof(struct listdir_entry);
457485
// We reuse the command buffer so that we can produce long packets without
458486
// making the stack large.
459487
if (command->path_length > (COMMAND_SIZE - header_size - 1)) { // -1 for the null we'll write
@@ -469,7 +497,7 @@ STATIC uint8_t _process_listdir(uint8_t *raw_buf, size_t command_len) {
469497
}
470498

471499
FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs;
472-
char *path = (char *)command->path;
500+
char *path = (char *)&command->path;
473501
// -1 because fatfs doesn't want a trailing /
474502
path[command->path_length - 1] = '\0';
475503
// mp_printf(&mp_plat_print, "list %s\n", path);
@@ -502,6 +530,13 @@ STATIC uint8_t _process_listdir(uint8_t *raw_buf, size_t command_len) {
502530
for (size_t i = 0; i < total_entries; i++) {
503531
res = f_readdir(&dir, &file_info);
504532
entry->entry_number = i;
533+
uint64_t truncated_time = timeutils_mktime(1980 + (file_info.fdate >> 9),
534+
(file_info.fdate >> 5) & 0xf,
535+
file_info.fdate & 0x1f,
536+
file_info.ftime >> 11,
537+
(file_info.ftime >> 5) & 0x1f,
538+
(file_info.ftime & 0x1f) * 2) * 1000000000ULL;
539+
entry->truncated_time = truncated_time;
505540
if ((file_info.fattrib & AM_DIR) != 0) {
506541
entry->flags = 1; // Directory
507542
entry->file_size = 0;

0 commit comments

Comments
 (0)
0