28
28
29
29
#include "extmod/vfs.h"
30
30
#include "extmod/vfs_fat.h"
31
+ #include "lib/timeutils/timeutils.h"
31
32
32
33
#include "shared-bindings/_bleio/__init__.h"
33
34
#include "shared-bindings/_bleio/Adapter.h"
41
42
42
43
#include "common-hal/_bleio/__init__.h"
43
44
45
+ #include "supervisor/fatfs_port.h"
44
46
#include "supervisor/shared/autoreload.h"
45
47
#include "supervisor/shared/bluetooth/file_transfer_protocol.h"
46
48
#include "supervisor/shared/tick.h"
@@ -98,7 +100,7 @@ void supervisor_start_bluetooth_file_transfer(void) {
98
100
NULL , // no initial value
99
101
NULL ); // no description
100
102
101
- uint32_t version = 2 ;
103
+ uint32_t version = 3 ;
102
104
mp_buffer_info_t bufinfo ;
103
105
bufinfo .buf = & version ;
104
106
bufinfo .len = sizeof (version );
@@ -131,12 +133,23 @@ void supervisor_start_bluetooth_file_transfer(void) {
131
133
#define ANY_COMMAND 0x00
132
134
#define THIS_COMMAND 0x01
133
135
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
+
134
147
// Used by read and write.
135
148
STATIC FIL active_file ;
136
149
STATIC uint8_t _process_read (const uint8_t * raw_buf , size_t command_len ) {
137
150
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 ) ;
140
153
uint8_t data_buffer [response_size ];
141
154
struct read_data response ;
142
155
response .command = READ_DATA ;
@@ -190,38 +203,38 @@ STATIC uint8_t _process_read(const uint8_t *raw_buf, size_t command_len) {
190
203
return READ_PACING ;
191
204
}
192
205
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
+
201
213
uint32_t total_length = f_size (& active_file );
202
214
// 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 ;
207
219
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 );
209
221
// Write out the chunk contents. We can do this in small pieces because PacketBuffer
210
222
// will assemble them into larger packets of its own.
211
223
size_t chunk_offset = 0 ;
224
+ uint8_t data [20 ];
212
225
while (chunk_offset < chunk_size ) {
213
226
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 );
216
229
if (quantity_read == 0 || result != FR_OK ) {
217
230
// TODO: If we can't read everything, then the file must have been shortened. Maybe we
218
231
// should return 0s to pad it out.
219
232
break ;
220
233
}
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 );
222
235
chunk_offset += quantity_read ;
223
236
}
224
- if ((offset + chunk_size ) >= total_length ) {
237
+ if ((chunk_offset + chunk_size ) >= total_length ) {
225
238
f_close (& active_file );
226
239
return ANY_COMMAND ;
227
240
}
@@ -230,10 +243,11 @@ STATIC uint8_t _process_read_pacing(const uint8_t *command, size_t command_len)
230
243
231
244
// Used by write and write data to know when the write is complete.
232
245
STATIC size_t total_write_length ;
246
+ STATIC uint64_t _truncated_time ;
233
247
234
248
STATIC uint8_t _process_write (const uint8_t * raw_buf , size_t command_len ) {
235
249
struct write_command * command = (struct write_command * )raw_buf ;
236
- size_t header_size = 12 ;
250
+ size_t header_size = sizeof ( struct write_command ) ;
237
251
struct write_pacing response ;
238
252
response .command = WRITE_PACING ;
239
253
response .status = STATUS_OK ;
@@ -262,13 +276,17 @@ STATIC uint8_t _process_write(const uint8_t *raw_buf, size_t command_len) {
262
276
#endif
263
277
264
278
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 );
265
282
FRESULT result = f_open (fs , & active_file , path , FA_WRITE | FA_OPEN_ALWAYS );
266
283
if (result != FR_OK ) {
267
284
response .status = STATUS_ERROR ;
268
285
common_hal_bleio_packet_buffer_write (& _transfer_packet_buffer , (const uint8_t * )& response , sizeof (struct write_pacing ), NULL , 0 );
269
286
#if CIRCUITPY_USB_MSC
270
287
usb_msc_unlock ();
271
288
#endif
289
+ override_fattime (0 );
272
290
return ANY_COMMAND ;
273
291
}
274
292
// Write out the pacing response.
@@ -281,12 +299,14 @@ STATIC uint8_t _process_write(const uint8_t *raw_buf, size_t command_len) {
281
299
f_lseek (& active_file , offset );
282
300
f_truncate (& active_file );
283
301
f_close (& active_file );
302
+ override_fattime (0 );
284
303
#if CIRCUITPY_USB_MSC
285
304
usb_msc_unlock ();
286
305
#endif
287
306
}
288
307
response .offset = offset ;
289
308
response .free_space = chunk_size ;
309
+ response .truncated_time = _truncated_time ;
290
310
common_hal_bleio_packet_buffer_write (& _transfer_packet_buffer , (const uint8_t * )& response , sizeof (struct write_pacing ), NULL , 0 );
291
311
if (chunk_size == 0 ) {
292
312
// 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) {
301
321
302
322
STATIC uint8_t _process_write_data (const uint8_t * raw_buf , size_t command_len ) {
303
323
struct write_data * command = (struct write_data * )raw_buf ;
304
- size_t header_size = 12 ;
324
+ size_t header_size = sizeof ( struct write_data ) ;
305
325
struct write_pacing response ;
306
326
response .command = WRITE_PACING ;
307
327
response .status = STATUS_OK ;
@@ -312,6 +332,7 @@ STATIC uint8_t _process_write_data(const uint8_t *raw_buf, size_t command_len) {
312
332
#if CIRCUITPY_USB_MSC
313
333
usb_msc_unlock ();
314
334
#endif
335
+ override_fattime (0 );
315
336
return ANY_COMMAND ;
316
337
}
317
338
// 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) {
329
350
#if CIRCUITPY_USB_MSC
330
351
usb_msc_unlock ();
331
352
#endif
353
+ override_fattime (0 );
332
354
return ANY_COMMAND ;
333
355
}
334
356
offset += command -> data_size ;
335
357
// Align the next chunk to a sector boundary.
336
358
size_t chunk_size = MIN (total_write_length - offset , 512 );
337
359
response .offset = offset ;
338
360
response .free_space = chunk_size ;
361
+ response .truncated_time = _truncated_time ;
339
362
common_hal_bleio_packet_buffer_write (& _transfer_packet_buffer , (const uint8_t * )& response , sizeof (struct write_pacing ), NULL , 0 );
340
363
if (total_write_length == offset ) {
341
364
f_truncate (& active_file );
342
365
f_close (& active_file );
366
+ override_fattime (0 );
343
367
#if CIRCUITPY_USB_MSC
344
368
usb_msc_unlock ();
345
369
#endif
@@ -387,7 +411,7 @@ STATIC FRESULT _delete_directory_contents(FATFS *fs, const TCHAR *path) {
387
411
388
412
STATIC uint8_t _process_delete (const uint8_t * raw_buf , size_t command_len ) {
389
413
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 ) ;
391
415
struct delete_status response ;
392
416
response .command = DELETE_STATUS ;
393
417
response .status = STATUS_OK ;
@@ -423,7 +447,7 @@ STATIC uint8_t _process_delete(const uint8_t *raw_buf, size_t command_len) {
423
447
424
448
STATIC uint8_t _process_mkdir (const uint8_t * raw_buf , size_t command_len ) {
425
449
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 ) ;
427
451
struct mkdir_status response ;
428
452
response .command = MKDIR_STATUS ;
429
453
response .status = STATUS_OK ;
@@ -438,10 +462,14 @@ STATIC uint8_t _process_mkdir(const uint8_t *raw_buf, size_t command_len) {
438
462
return THIS_COMMAND ;
439
463
}
440
464
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 ;
442
466
// TODO: Check that the final character is a `/`
443
467
path [command -> path_length - 1 ] = '\0' ;
468
+ DWORD fattime ;
469
+ response .truncated_time = truncate_time (command -> modification_time , & fattime );
470
+ override_fattime (fattime );
444
471
FRESULT result = f_mkdir (fs , path );
472
+ override_fattime (0 );
445
473
if (result != FR_OK ) {
446
474
response .status = STATUS_ERROR ;
447
475
}
@@ -452,8 +480,8 @@ STATIC uint8_t _process_mkdir(const uint8_t *raw_buf, size_t command_len) {
452
480
STATIC uint8_t _process_listdir (uint8_t * raw_buf , size_t command_len ) {
453
481
const struct listdir_command * command = (struct listdir_command * )raw_buf ;
454
482
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 );
457
485
// We reuse the command buffer so that we can produce long packets without
458
486
// making the stack large.
459
487
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) {
469
497
}
470
498
471
499
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 ;
473
501
// -1 because fatfs doesn't want a trailing /
474
502
path [command -> path_length - 1 ] = '\0' ;
475
503
// 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) {
502
530
for (size_t i = 0 ; i < total_entries ; i ++ ) {
503
531
res = f_readdir (& dir , & file_info );
504
532
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 ;
505
540
if ((file_info .fattrib & AM_DIR ) != 0 ) {
506
541
entry -> flags = 1 ; // Directory
507
542
entry -> file_size = 0 ;
0 commit comments