1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
|
/*
* Copyright (c) 2024 ~ 2026, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "ff.h"
#include "diskio.h"
#include "usbd_core.h"
#include "usb_vfs.h"
FATFS s_sd_disk;
FIL s_file;
BYTE work[FF_MAX_SS];
const TCHAR driver_num_buf[4] = { DEV_SD + '0', ':', '/', '\0' };
static const char *show_error_string(FRESULT fresult);
static FRESULT sd_mount_fs(void)
{
FRESULT fresult = f_mount(&s_sd_disk, driver_num_buf, 1);
if (fresult == FR_OK) {
printf("SD card has been mounted successfully\n");
} else {
printf("Failed to mount SD card, cause: %s\n", show_error_string(fresult));
}
fresult = f_chdrive(driver_num_buf);
return fresult;
}
#if 0
static FRESULT sd_mkfs(void)
{
printf("Formatting the SD card, depending on the SD card capacity, the formatting process may take a long time\n");
FRESULT fresult = f_mkfs(driver_num_buf, NULL, work, sizeof(work));
if (fresult != FR_OK) {
printf("Making File system failed, cause: %s\n", show_error_string(fresult));
} else {
printf("Making file system is successful\n");
}
return fresult;
}
#endif
static FRESULT sd_write_file(void)
{
FRESULT fresult = f_open(&s_file, "readme.txt", FA_WRITE | FA_CREATE_ALWAYS);
if (fresult != FR_OK) {
printf("Create new file failed, cause: %s\n", show_error_string(fresult));
} else {
printf("Create new file successfully, status=%d\n", fresult);
}
char hello_str[] = "Hello, this is SD card FATFS demo\n";
UINT byte_written;
fresult = f_write(&s_file, hello_str, sizeof(hello_str), &byte_written);
if (fresult != FR_OK) {
printf("Write file failed, cause: %s\n", show_error_string(fresult));
} else {
printf("Write file operation is successfully\n");
}
f_close(&s_file);
return fresult;
}
static const char *show_error_string(FRESULT fresult)
{
const char *result_str;
switch (fresult) {
case FR_OK:
result_str = "succeeded";
break;
case FR_DISK_ERR:
result_str = "A hard error occurred in the low level disk I/O level";
break;
case FR_INT_ERR:
result_str = "Assertion failed";
break;
case FR_NOT_READY:
result_str = "The physical drive cannot work";
break;
case FR_NO_FILE:
result_str = "Could not find the file";
break;
case FR_NO_PATH:
result_str = "Could not find the path";
break;
case FR_INVALID_NAME:
result_str = "Tha path name format is invalid";
break;
case FR_DENIED:
result_str = "Access denied due to prohibited access or directory full";
break;
case FR_EXIST:
result_str = "Access denied due to prohibited access";
break;
case FR_INVALID_OBJECT:
result_str = "The file/directory object is invalid";
break;
case FR_WRITE_PROTECTED:
result_str = "The physical drive is write protected";
break;
case FR_INVALID_DRIVE:
result_str = "The logical driver number is invalid";
break;
case FR_NOT_ENABLED:
result_str = "The volume has no work area";
break;
case FR_NO_FILESYSTEM:
result_str = "There is no valid FAT volume";
break;
case FR_MKFS_ABORTED:
result_str = "THe f_mkfs() aborted due to any problem";
break;
case FR_TIMEOUT:
result_str = "Could not get a grant to access the volume within defined period";
break;
case FR_LOCKED:
result_str = "The operation is rejected according to the file sharing policy";
break;
case FR_NOT_ENOUGH_CORE:
result_str = "LFN working buffer could not be allocated";
break;
case FR_TOO_MANY_OPEN_FILES:
result_str = "Number of open files > FF_FS_LOCK";
break;
case FR_INVALID_PARAMETER:
result_str = "Given parameter is invalid";
break;
default:
result_str = "Unknown error";
break;
}
return result_str;
}
const char *vfs_get_fs_root_path(void)
{
return "/";
}
const char *vfs_get_fs_description(void)
{
return "CherryUSB VFS FATFS";
}
/* -------------------------------------------------------------------------
* helpers
* ---------------------------------------------------------------------- */
/** FatFs result -> VFS_ERR_*, so the host gets a readable reason. */
static int adb_err(FRESULT fr)
{
switch (fr) {
case FR_OK:
return 0;
case FR_NO_FILE:
case FR_NO_PATH:
return VFS_ERR_NOENT;
case FR_EXIST:
return VFS_ERR_EXIST;
case FR_INVALID_NAME:
return VFS_ERR_INVAL;
case FR_INVALID_PARAMETER:
return VFS_ERR_INVAL;
case FR_INVALID_OBJECT:
return VFS_ERR_INVAL;
case FR_WRITE_PROTECTED:
return VFS_ERR_ROFS;
case FR_DENIED:
/* "access denied", which FatFs also uses for a full directory */
return VFS_ERR_ROFS;
case FR_INVALID_DRIVE:
case FR_NOT_ENABLED:
case FR_NO_FILESYSTEM:
case FR_NOT_READY:
return VFS_ERR_NODEV;
case FR_TIMEOUT:
case FR_LOCKED:
return VFS_ERR_AGAIN;
case FR_NOT_ENOUGH_CORE:
case FR_TOO_MANY_OPEN_FILES:
return VFS_ERR_NOMEM;
case FR_DISK_ERR:
case FR_INT_ERR:
default:
return VFS_ERR_IO;
}
}
/**
* days since 1970-01-01, proleptic gregorian (Howard Hinnant's algorithm).
* Kept here rather than pulling in <time.h>, which an RTOS may not have.
*/
static int days_from_civil(int y, unsigned m, unsigned d)
{
int era;
unsigned yoe, doy, doe;
y -= (m <= 2u) ? 1 : 0;
era = (y >= 0 ? y : y - 399) / 400;
yoe = (unsigned)(y - era * 400);
doy = (153u * (m + (m > 2u ? (unsigned)-3 : 9u)) + 2u) / 5u + d - 1u;
doe = yoe * 365u + yoe / 4u - yoe / 100u + doy;
return era * 146097 + (int)doe - 719468;
}
/**
* FatFs date/time -> unix seconds.
*
* adb sync carries the file time in STAT and DENT records, but FatFs keeps a
* packed DOS timestamp, so it has to be converted somewhere. Doing it here
* keeps the protocol layer free of file system specifics.
*/
static uint32_t fat_time_to_unix(uint16_t fdate, uint16_t ftime)
{
unsigned year = 1980u + ((fdate >> 9) & 0x7Fu);
unsigned month = (fdate >> 5) & 0x0Fu;
unsigned day = fdate & 0x1Fu;
unsigned hour = ((unsigned)ftime >> 11) & 0x1Fu;
unsigned minute = ((unsigned)ftime >> 5) & 0x3Fu;
unsigned second = ((unsigned)ftime & 0x1Fu) * 2u;
if ((fdate == 0) || (month == 0) || (day == 0)) {
return 0; /* FatFs stores 0 when there is no valid timestamp */
}
return (uint32_t)(days_from_civil((int)year, month, day) * 86400 +
(int)(hour * 3600u + minute * 60u + second));
}
int vfs_mkdir(const char *path)
{
FRESULT result;
if (!path || !path[0]) {
return VFS_ERR_INVAL;
}
result = f_mkdir(path);
/* adb push of a directory tree re-sends SEND for directories it has already
* created, so "it is already there" has to count as success */
if ((result != FR_OK) && (result != FR_EXIST)) {
return adb_err(result);
}
return 0;
}
int vfs_rmdir(const char *path)
{
FRESULT result = f_rmdir(path);
if (result != FR_OK) {
return adb_err(result);
}
return 0;
}
VFS_DIR *vfs_opendir(const char *name)
{
FRESULT result;
DIR *dir;
if (!name || !name[0]) {
return NULL;
}
dir = usb_osal_malloc(sizeof(DIR));
if (!dir) {
return NULL;
}
result = f_opendir(dir, name);
if (result != FR_OK) {
usb_osal_free(dir);
return NULL;
}
return (VFS_DIR *)dir;
}
int vfs_closedir(VFS_DIR *dir)
{
FRESULT result;
if (!dir) {
return VFS_ERR_INVAL;
}
result = f_closedir((DIR *)dir);
usb_osal_free(dir); /* release it either way */
if (result != FR_OK) {
return adb_err(result);
}
return 0;
}
struct vfs_dirent *vfs_readdir(VFS_DIR *dir)
{
static struct vfs_dirent dirent;
FILINFO fno;
FRESULT result;
if (!dir) {
return NULL;
}
result = f_readdir((DIR *)dir, &fno);
if ((result != FR_OK) || (fno.fname[0] == 0)) {
return NULL; /* end of directory, or a hard error */
}
memset(&dirent, 0, sizeof(dirent));
strncpy(dirent.d_name, fno.fname, sizeof(dirent.d_name) - 1);
dirent.d_name[sizeof(dirent.d_name) - 1] = '\0';
dirent.d_namlen = (uint8_t)strlen(dirent.d_name);
/* d_type is just the type field of a mode shifted down, see vfs_posix.h */
dirent.d_type = (uint8_t)(((fno.fattrib & AM_DIR) ? VFS_S_IFDIR
: VFS_S_IFREG) >>
12);
return &dirent;
}
#undef SS
#if FF_MAX_SS == FF_MIN_SS
#define SS(fs) ((UINT)FF_MAX_SS) /* Fixed sector size */
#else
#define SS(fs) ((fs)->ssize) /* Variable sector size */
#endif
int vfs_stat(const char *path, struct vfs_stat *buf)
{
FILINFO file_info;
FRESULT result;
FATFS *f;
if (!path || !path[0] || !buf) {
return VFS_ERR_INVAL;
}
f = &s_sd_disk;
result = f_stat(path, &file_info);
if (result != FR_OK) {
/* Stay quiet on purpose: adb stats a path precisely to find out whether
* it exists, so "not found" is a normal answer and not an error. The
* service reports it to the host as "mode 0". */
return adb_err(result);
}
memset(buf, 0, sizeof(*buf));
buf->st_mode = VFS_S_IFREG | VFS_S_IRUSR | VFS_S_IRGRP | VFS_S_IROTH |
VFS_S_IWUSR | VFS_S_IWGRP | VFS_S_IWOTH;
if (file_info.fattrib & AM_DIR) {
buf->st_mode &= ~VFS_S_IFREG;
buf->st_mode |= VFS_S_IFDIR | VFS_S_IXUSR | VFS_S_IXGRP | VFS_S_IXOTH;
}
if (file_info.fattrib & AM_RDO) {
buf->st_mode &= ~(VFS_S_IWUSR | VFS_S_IWGRP | VFS_S_IWOTH);
}
buf->st_dev = 0;
buf->st_nlink = 1;
buf->st_size = file_info.fsize;
buf->st_blksize = f->csize * SS(f);
if (file_info.fattrib & AM_ARC) {
buf->st_blocks = file_info.fsize ? ((file_info.fsize - 1) / SS(f) / f->csize + 1) : 0;
buf->st_blocks *= (buf->st_blksize / 512); /* st_blocks is in 512B units */
} else {
buf->st_blocks = f->csize;
}
buf->st_mtime = fat_time_to_unix(file_info.fdate, file_info.ftime);
return 0;
}
int vfs_statfs(const char *path, struct vfs_statfs *buf)
{
FATFS *f;
FRESULT res;
DWORD fre_clust, fre_sect, tot_sect;
if (!path || !path[0] || !buf) {
return VFS_ERR_INVAL;
}
f = &s_sd_disk;
res = f_getfree(path, &fre_clust, &f);
if (res != FR_OK) {
return adb_err(res);
}
tot_sect = (f->n_fatent - 2) * f->csize;
fre_sect = fre_clust * f->csize;
buf->f_blocks = tot_sect;
buf->f_bfree = fre_sect;
#if FF_MAX_SS != FF_MIN_SS
buf->f_bsize = f->ssize;
#else
buf->f_bsize = FF_MIN_SS;
#endif
return 0;
}
int vfs_open(const char *path, uint8_t mode)
{
FRESULT result;
BYTE flags;
if (!path || !path[0]) {
return VFS_ERR_INVAL;
}
if (mode == VFS_O_RDONLY) {
flags = FA_READ | FA_OPEN_EXISTING;
} else if (mode == VFS_O_WRONLY) {
flags = FA_WRITE | FA_CREATE_ALWAYS;
} else if (mode == VFS_O_RDWR) {
flags = FA_READ | FA_WRITE | FA_OPEN_ALWAYS;
} else {
return VFS_ERR_INVAL;
}
result = f_open(&s_file, path, flags);
if (result != FR_OK) {
return adb_err(result);
}
return 0;
}
int vfs_close(int fd)
{
FRESULT result;
(void)fd;
result = f_close(&s_file);
if (result != FR_OK) {
return adb_err(result);
}
return 0;
}
int vfs_read(int fd, void *buf, size_t len)
{
UINT bytes_read;
FRESULT result;
(void)fd;
result = f_read(&s_file, buf, len, &bytes_read);
if (result != FR_OK) {
return adb_err(result);
}
return (int)bytes_read;
}
int vfs_write(int fd, const void *buf, size_t len)
{
UINT bytes_written;
FRESULT result;
(void)fd;
result = f_write(&s_file, buf, len, &bytes_written);
if (result != FR_OK) {
return adb_err(result);
}
return (int)bytes_written;
}
int vfs_unlink(const char *path)
{
FRESULT result = f_unlink(path);
if (result != FR_OK) {
return adb_err(result);
}
return 0;
}
void vfs_mount(void)
{
//sd_mkfs();
sd_mount_fs();
sd_write_file();
}
|