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 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 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 106 static URET _device_release(uffs_Device *dev) 107 { 108 return U_SUCC; 109 } 110 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 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 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 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 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 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 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 375 static int dfs_uffs_ioctl(struct dfs_fd * file, int cmd, void* args) 376 { 377 return -ENOSYS; 378 } 379 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 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 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 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 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*/ 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 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 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 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 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