1 /*
2 * Copyright (c) 2006-2018, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2011-10-22 prife the first version
9 * 2012-03-28 prife use mtd device interface
10 * 2012-04-05 prife update uffs with official repo and use uffs_UnMount/Mount
11 * 2017-04-12 lizhen9880 fix the f_bsize and f_blocks issue in function dfs_uffs_statfs
12 */
13
14 #include <rtthread.h>
15
16 #include <dfs_fs.h>
17 #include <dfs_file.h>
18 #include <rtdevice.h>
19
20 #include "dfs_uffs.h"
21
22 #include "uffs/uffs_fd.h" /* posix file api is here */
23 #include "uffs/uffs_mtb.h"
24 #include "uffs/uffs_mem.h"
25 #include "uffs/uffs_utils.h"
26
27 /*
28 * RT-Thread DFS Interface for uffs
29 */
30 #define UFFS_DEVICE_MAX 2 /* the max partions on a nand deivce*/
31 #define UFFS_MOUNT_PATH_MAX 128 /* the mount point max length */
32 #define FILE_PATH_MAX 256 /* the longest file path */
33
34 struct _nand_dev
35 {
36 struct rt_mtd_nand_device * dev;
37 struct uffs_StorageAttrSt storage;
38 uffs_Device uffs_dev;
39 uffs_MountTable mount_table;
40 char mount_path[UFFS_MOUNT_PATH_MAX];
41 void * data; /* when uffs use static buf, it will save ptr here */
42 };
43 /* make sure the following struct var had been initilased to 0! */
44 static struct _nand_dev nand_part[UFFS_DEVICE_MAX] = {0};
45
uffs_result_to_dfs(int result)46 static int uffs_result_to_dfs(int result)
47 {
48 int status = -1;
49
50 result = result < 0 ? -result : result;
51 switch (result)
52 {
53 case UENOERR:/** no error */
54 break;
55 case UEACCES:/** Tried to open read-only file for writing, or files sharing mode
56 does not allow specified operations, or given path is directory */
57 status = -EINVAL;
58 break;/* no suitable */
59 case UEEXIST: /** _O_CREAT and _O_EXCL flags specified, but filename already exists */
60 status = -EEXIST;
61 break;
62 case UEINVAL: /** Invalid oflag or pmode argument */
63 status = -EINVAL;
64 break;
65 case UEMFILE: /** No more file handles available(too many open files) */
66 status = -1;
67 break;
68 case UENOENT: /** file or path not found */
69 status = -ENOENT;
70 break;
71 case UETIME: /** can't set file time */
72 status = -1;
73 break;
74 case UEBADF: /** invalid file handle */
75 status = -EBADF;
76 break;
77 case UENOMEM:/** no enough memory */
78 status = -ENOSPC;
79 break;
80 case UEIOERR: /** I/O error from lower level flash operation */
81 status = -EIO;
82 break;
83 case UENOTDIR: /** Not a directory */
84 status = -ENOTDIR;
85 break;
86 case UEISDIR: /** Is a directory */
87 status = -EISDIR;
88 break;
89 case UEUNKNOWN_ERR:
90 default:
91 status = -1;
92 break; /* unknown error! */
93 }
94
95 return status;
96 }
97
_device_init(uffs_Device * dev)98 static URET _device_init(uffs_Device *dev)
99 {
100 dev->attr->_private = NULL; // hook nand_chip data structure to attr->_private
101 dev->ops = (struct uffs_FlashOpsSt *)&nand_ops;
102
103 return U_SUCC;
104 }
105
_device_release(uffs_Device * dev)106 static URET _device_release(uffs_Device *dev)
107 {
108 return U_SUCC;
109 }
110
init_uffs_fs(struct _nand_dev * nand_part)111 static int init_uffs_fs(
112 struct _nand_dev * nand_part)
113 {
114 uffs_MountTable * mtb;
115 struct rt_mtd_nand_device * nand;
116 struct uffs_StorageAttrSt * flash_storage;
117
118 mtb = &nand_part->mount_table;
119 nand = nand_part->dev;
120 flash_storage = &nand_part->storage;
121
122 /* setup nand storage attributes */
123 uffs_setup_storage(flash_storage, nand);
124
125 /* register mount table */
126 if(mtb->dev)
127 {
128 /* set memory allocator for uffs */
129 #if CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR > 0
130 uffs_MemSetupSystemAllocator(&mtb->dev->mem);
131 #endif
132 /* setup device init/release entry */
133 mtb->dev->Init = _device_init;
134 mtb->dev->Release = _device_release;
135 mtb->dev->attr = flash_storage;
136
137 uffs_RegisterMountTable(mtb);
138 }
139 /* mount uffs partion on nand device */
140 return uffs_Mount(nand_part->mount_path) == U_SUCC ? 0 : -1;
141 }
142
dfs_uffs_mount(struct dfs_filesystem * fs,unsigned long rwflag,const void * data)143 static int dfs_uffs_mount(
144 struct dfs_filesystem* fs,
145 unsigned long rwflag,
146 const void* data)
147 {
148 rt_base_t index;
149 uffs_MountTable * mount_part;
150 struct rt_mtd_nand_device * dev;
151
152 RT_ASSERT(rt_strlen(fs->path) < (UFFS_MOUNT_PATH_MAX-1));
153 dev = RT_MTD_NAND_DEVICE(fs->dev_id);
154
155 /*1. find a empty entry in partition table */
156 for (index = 0; index < UFFS_DEVICE_MAX ; index ++)
157 {
158 if (nand_part[index].dev == RT_NULL)
159 break;
160 }
161 if (index == UFFS_DEVICE_MAX)
162 return -ENOENT;
163
164 /*2. fill partition structure */
165 nand_part[index].dev = dev;
166
167 /* make a right mount path for uffs, end with '/' */
168 rt_snprintf(nand_part[index].mount_path, UFFS_MOUNT_PATH_MAX, "%s/", fs->path);
169 if (nand_part[index].mount_path[1] == '/')
170 nand_part[index].mount_path[1] = 0;
171
172 mount_part = &(nand_part[index].mount_table);
173 mount_part->mount = nand_part[index].mount_path;
174 mount_part->dev = &(nand_part[index].uffs_dev);
175 rt_memset(mount_part->dev, 0, sizeof(uffs_Device));//in order to make uffs happy.
176 mount_part->dev->_private = dev; /* save dev_id into uffs */
177 mount_part->start_block = dev->block_start;
178 mount_part->end_block = dev->block_end;
179 /*3. mount uffs */
180 if (init_uffs_fs(&nand_part[index]) < 0)
181 {
182 return uffs_result_to_dfs(uffs_get_error());
183 }
184 return 0;
185 }
186
dfs_uffs_unmount(struct dfs_filesystem * fs)187 static int dfs_uffs_unmount(struct dfs_filesystem* fs)
188 {
189 rt_base_t index;
190 int result;
191
192 /* find the device index and then unmount it */
193 for (index = 0; index < UFFS_DEVICE_MAX; index++)
194 {
195 if (nand_part[index].dev == RT_MTD_NAND_DEVICE(fs->dev_id))
196 {
197 nand_part[index].dev = RT_NULL;
198 result = uffs_UnMount(nand_part[index].mount_path);
199 if (result != U_SUCC)
200 break;
201
202 result = uffs_UnRegisterMountTable(& nand_part[index].mount_table);
203 return (result == U_SUCC) ? RT_EOK : -1;
204 }
205 }
206 return -ENOENT;
207 }
208
dfs_uffs_mkfs(rt_device_t dev_id)209 static int dfs_uffs_mkfs(rt_device_t dev_id)
210 {
211 rt_base_t index;
212 rt_uint32_t block;
213 struct rt_mtd_nand_device * mtd;
214
215 /*1. find the device index */
216 for (index = 0; index < UFFS_DEVICE_MAX; index++)
217 {
218 if (nand_part[index].dev == (struct rt_mtd_nand_device *)dev_id)
219 break;
220 }
221
222 if (index == UFFS_DEVICE_MAX)
223 {
224 /* can't find device driver */
225 return -ENOENT;
226 }
227
228 /*2. then unmount the partition */
229 uffs_Mount(nand_part[index].mount_path);
230 mtd = nand_part[index].dev;
231
232 /*3. erase all blocks on the partition */
233 block = mtd->block_start;
234 for (; block <= mtd->block_end; block++)
235 {
236 rt_mtd_nand_erase_block(mtd, block);
237 if (rt_mtd_nand_check_block(mtd, block) != RT_EOK)
238 {
239 rt_kprintf("found bad block %d\n", block);
240 rt_mtd_nand_mark_badblock(mtd, block);
241 }
242 }
243
244 /*4. remount it */
245 if (init_uffs_fs(&nand_part[index]) < 0)
246 {
247 return uffs_result_to_dfs(uffs_get_error());
248 }
249 return RT_EOK;
250 }
251
dfs_uffs_statfs(struct dfs_filesystem * fs,struct statfs * buf)252 static int dfs_uffs_statfs(struct dfs_filesystem* fs,
253 struct statfs *buf)
254 {
255 rt_base_t index;
256 struct rt_mtd_nand_device * mtd = RT_MTD_NAND_DEVICE(fs->dev_id);
257
258 RT_ASSERT(mtd != RT_NULL);
259
260 /* find the device index */
261 for (index = 0; index < UFFS_DEVICE_MAX; index++)
262 {
263 if (nand_part[index].dev == (void *)mtd)
264 break;
265 }
266 if (index == UFFS_DEVICE_MAX)
267 return -ENOENT;
268
269 buf->f_bsize = mtd->page_size*mtd->pages_per_block;
270 buf->f_blocks = (mtd->block_end - mtd->block_start + 1);
271 buf->f_bfree = uffs_GetDeviceFree(&nand_part[index].uffs_dev)/buf->f_bsize ;
272
273 return 0;
274 }
275
dfs_uffs_open(struct dfs_fd * file)276 static int dfs_uffs_open(struct dfs_fd* file)
277 {
278 int fd;
279 int oflag, mode;
280 char * file_path;
281
282 oflag = file->flags;
283 if (oflag & O_DIRECTORY) /* operations about dir */
284 {
285 uffs_DIR * dir;
286
287 if (oflag & O_CREAT) /* create a dir*/
288 {
289 if (uffs_mkdir(file->path) < 0)
290 return uffs_result_to_dfs(uffs_get_error());
291 }
292 /* open dir */
293 file_path = rt_malloc(FILE_PATH_MAX);
294 if(file_path == RT_NULL)
295 return -ENOMEM;
296
297 if (file->path[0] == '/' && !(file->path[1] == 0))
298 rt_snprintf(file_path, FILE_PATH_MAX, "%s/", file->path);
299 else
300 {
301 file_path[0] = '/';
302 file_path[1] = 0;
303 }
304
305 dir = uffs_opendir(file_path);
306
307 if (dir == RT_NULL)
308 {
309 rt_free(file_path);
310 return uffs_result_to_dfs(uffs_get_error());
311 }
312 /* save this pointer,will used by dfs_uffs_getdents*/
313 file->data = dir;
314 rt_free(file_path);
315 return RT_EOK;
316 }
317 /* regular file operations */
318 /* int uffs_open(const char *name, int oflag, ...); what is this?
319 * uffs_open can open dir!! **/
320 mode = 0;
321 if (oflag & O_RDONLY) mode |= UO_RDONLY;
322 if (oflag & O_WRONLY) mode |= UO_WRONLY;
323 if (oflag & O_RDWR) mode |= UO_RDWR;
324 /* Opens the file, if it is existing. If not, a new file is created. */
325 if (oflag & O_CREAT) mode |= UO_CREATE;
326 /* Creates a new file. If the file is existing, it is truncated and overwritten. */
327 if (oflag & O_TRUNC) mode |= UO_TRUNC;
328 /* Creates a new file. The function fails if the file is already existing. */
329 if (oflag & O_EXCL) mode |= UO_EXCL;
330
331 fd = uffs_open(file->path, mode);
332 if (fd < 0)
333 {
334 return uffs_result_to_dfs(uffs_get_error());
335 }
336
337 /* save this pointer, it will be used when calling read(), write(),
338 * flush(), seek(), and will be free when calling close()*/
339
340 file->data = (void *)fd;
341 file->pos = uffs_seek(fd, 0, USEEK_CUR);
342 file->size = uffs_seek(fd, 0, USEEK_END);
343 uffs_seek(fd, file->pos, USEEK_SET);
344
345 if (oflag & O_APPEND)
346 {
347 file->pos = uffs_seek(fd, 0, USEEK_END);
348 }
349 return 0;
350 }
351
dfs_uffs_close(struct dfs_fd * file)352 static int dfs_uffs_close(struct dfs_fd* file)
353 {
354 int oflag;
355 int fd;
356
357 oflag = file->flags;
358 if (oflag & O_DIRECTORY)
359 {
360 /* operations about dir */
361 if (uffs_closedir((uffs_DIR *)(file->data)) < 0)
362 return uffs_result_to_dfs(uffs_get_error());
363
364 return 0;
365 }
366 /* regular file operations */
367 fd = (int)(file->data);
368
369 if (uffs_close(fd) == 0)
370 return 0;
371
372 return uffs_result_to_dfs(uffs_get_error());
373 }
374
dfs_uffs_ioctl(struct dfs_fd * file,int cmd,void * args)375 static int dfs_uffs_ioctl(struct dfs_fd * file, int cmd, void* args)
376 {
377 return -ENOSYS;
378 }
379
dfs_uffs_read(struct dfs_fd * file,void * buf,size_t len)380 static int dfs_uffs_read(struct dfs_fd * file, void* buf, size_t len)
381 {
382 int fd;
383 int char_read;
384
385 fd = (int)(file->data);
386 char_read = uffs_read(fd, buf, len);
387 if (char_read < 0)
388 return uffs_result_to_dfs(uffs_get_error());
389
390 /* update position */
391 file->pos = uffs_seek(fd, 0, USEEK_CUR);
392 return char_read;
393 }
394
dfs_uffs_write(struct dfs_fd * file,const void * buf,size_t len)395 static int dfs_uffs_write(struct dfs_fd* file,
396 const void* buf,
397 size_t len)
398 {
399 int fd;
400 int char_write;
401
402 fd = (int)(file->data);
403
404 char_write = uffs_write(fd, buf, len);
405 if (char_write < 0)
406 return uffs_result_to_dfs(uffs_get_error());
407
408 /* update position */
409 file->pos = uffs_seek(fd, 0, USEEK_CUR);
410 return char_write;
411 }
412
dfs_uffs_flush(struct dfs_fd * file)413 static int dfs_uffs_flush(struct dfs_fd* file)
414 {
415 int fd;
416 int result;
417
418 fd = (int)(file->data);
419
420 result = uffs_flush(fd);
421 if (result < 0 )
422 return uffs_result_to_dfs(uffs_get_error());
423 return 0;
424 }
425
uffs_seekdir(uffs_DIR * dir,long offset)426 int uffs_seekdir(uffs_DIR *dir, long offset)
427 {
428 int i = 0;
429
430 while(i < offset)
431 {
432 if (uffs_readdir(dir) == RT_NULL)
433 return -1;
434 i++;
435 }
436 return 0;
437 }
438
439
dfs_uffs_seek(struct dfs_fd * file,rt_off_t offset)440 static int dfs_uffs_seek(struct dfs_fd* file,
441 rt_off_t offset)
442 {
443 int result;
444
445 /* set offset as current offset */
446 if (file->type == FT_DIRECTORY)
447 {
448 uffs_rewinddir((uffs_DIR *)(file->data));
449 result = uffs_seekdir((uffs_DIR *)(file->data), offset/sizeof(struct dirent));
450 if (result >= 0)
451 {
452 file->pos = offset;
453 return offset;
454 }
455 }
456 else if (file->type == FT_REGULAR)
457 {
458 result = uffs_seek((int)(file->data), offset, USEEK_SET);
459 if (result >= 0)
460 return offset;
461 }
462
463 return uffs_result_to_dfs(uffs_get_error());
464 }
465
466 /* return the size of struct dirent*/
dfs_uffs_getdents(struct dfs_fd * file,struct dirent * dirp,uint32_t count)467 static int dfs_uffs_getdents(
468 struct dfs_fd* file,
469 struct dirent* dirp,
470 uint32_t count)
471 {
472 rt_uint32_t index;
473 char * file_path;
474 struct dirent* d;
475 uffs_DIR* dir;
476 struct uffs_dirent * uffs_d;
477
478 dir = (uffs_DIR*)(file->data);
479 RT_ASSERT(dir != RT_NULL);
480
481 /* round count, count is always 1 */
482 count = (count / sizeof(struct dirent)) * sizeof(struct dirent);
483 if (count == 0) return -EINVAL;
484
485 /* allocate file name */
486 file_path = rt_malloc(FILE_PATH_MAX);
487 if (file_path == RT_NULL)
488 return -ENOMEM;
489
490 index = 0;
491 /* usually, the while loop should only be looped only once! */
492 while (1)
493 {
494 struct uffs_stat s;
495
496 d = dirp + index;
497
498 uffs_d = uffs_readdir(dir);
499 if (uffs_d == RT_NULL)
500 {
501 rt_free(file_path);
502 return (uffs_result_to_dfs(uffs_get_error()));
503 }
504
505 if (file->path[0] == '/' && !(file->path[1] == 0))
506 rt_snprintf(file_path, FILE_PATH_MAX, "%s/%s", file->path, uffs_d->d_name);
507 else
508 rt_strncpy(file_path, uffs_d->d_name, FILE_PATH_MAX);
509
510 uffs_stat(file_path, &s);
511 switch(s.st_mode & US_IFMT) /* file type mark */
512 {
513 case US_IFREG: /* directory */
514 d->d_type = DT_REG;
515 break;
516 case US_IFDIR: /* regular file */
517 d->d_type = DT_DIR;
518 break;
519 case US_IFLNK: /* symbolic link */
520 case US_IREAD: /* read permission */
521 case US_IWRITE:/* write permission */
522 default:
523 d->d_type = DT_UNKNOWN;
524 break;
525 }
526
527 /* write the rest args of struct dirent* dirp */
528 d->d_namlen = rt_strlen(uffs_d->d_name);
529 d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
530 rt_strncpy(d->d_name, uffs_d->d_name, rt_strlen(uffs_d->d_name) + 1);
531
532 index ++;
533 if (index * sizeof(struct dirent) >= count)
534 break;
535 }
536
537 /* free file name buf */
538 rt_free(file_path);
539
540 if (index == 0)
541 return uffs_result_to_dfs(uffs_get_error());
542
543 file->pos += index * sizeof(struct dirent);
544
545 return index * sizeof(struct dirent);
546 }
547
dfs_uffs_unlink(struct dfs_filesystem * fs,const char * path)548 static int dfs_uffs_unlink(struct dfs_filesystem* fs, const char* path)
549 {
550 int result;
551 struct uffs_stat s;
552
553 /* judge file type, dir is to be delete by uffs_rmdir, others by uffs_remove */
554 if (uffs_lstat(path, &s) < 0)
555 {
556 return uffs_result_to_dfs(uffs_get_error());
557 }
558
559 switch(s.st_mode & US_IFMT)
560 {
561 case US_IFREG:
562 result = uffs_remove(path);
563 break;
564 case US_IFDIR:
565 result = uffs_rmdir(path);
566 break;
567 default:
568 /* unknown file type */
569 return -1;
570 }
571 if (result < 0)
572 return uffs_result_to_dfs(uffs_get_error());
573
574 return 0;
575 }
576
dfs_uffs_rename(struct dfs_filesystem * fs,const char * oldpath,const char * newpath)577 static int dfs_uffs_rename(
578 struct dfs_filesystem* fs,
579 const char* oldpath,
580 const char* newpath)
581 {
582 int result;
583
584 result = uffs_rename(oldpath, newpath);
585 if (result < 0)
586 return uffs_result_to_dfs(uffs_get_error());
587
588 return 0;
589 }
590
dfs_uffs_stat(struct dfs_filesystem * fs,const char * path,struct stat * st)591 static int dfs_uffs_stat(struct dfs_filesystem* fs, const char *path, struct stat *st)
592 {
593 int result;
594 struct uffs_stat s;
595
596 result = uffs_stat(path, &s);
597 if (result < 0)
598 return uffs_result_to_dfs(uffs_get_error());
599
600 /* convert uffs stat to dfs stat structure */
601 /* FIXME, these field may not be the same */
602 st->st_dev = 0;
603 st->st_mode = s.st_mode;
604 st->st_size = s.st_size;
605 st->st_mtime = s.st_mtime;
606
607 return 0;
608 }
609
610 static const struct dfs_file_ops dfs_uffs_fops =
611 {
612 dfs_uffs_open,
613 dfs_uffs_close,
614 dfs_uffs_ioctl,
615 dfs_uffs_read,
616 dfs_uffs_write,
617 dfs_uffs_flush,
618 dfs_uffs_seek,
619 dfs_uffs_getdents,
620 };
621
622 static const struct dfs_filesystem_ops dfs_uffs_ops =
623 {
624 "uffs", /* file system type: uffs */
625 #if RTTHREAD_VERSION >= 10100
626 DFS_FS_FLAG_FULLPATH,
627 #else
628 #error "uffs can only work with rtthread whose version should >= 1.01\n"
629 #endif
630 &dfs_uffs_fops,
631
632 dfs_uffs_mount,
633 dfs_uffs_unmount,
634 dfs_uffs_mkfs,
635 dfs_uffs_statfs,
636
637 dfs_uffs_unlink,
638 dfs_uffs_stat,
639 dfs_uffs_rename,
640 };
641
dfs_uffs_init(void)642 int dfs_uffs_init(void)
643 {
644 /* register uffs file system */
645 dfs_register(&dfs_uffs_ops);
646
647 if (uffs_InitObjectBuf() == U_SUCC)
648 {
649 if (uffs_DirEntryBufInit() == U_SUCC)
650 {
651 uffs_InitGlobalFsLock();
652 return RT_EOK;
653 }
654 }
655 return -RT_ERROR;
656 }
657 INIT_COMPONENT_EXPORT(dfs_uffs_init);
658
659