/* vim: set ts=4 sw=4 et fdm=marker: */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include <../h/flash/vxbFlash.h> #include <../h/flash/vxbFlashCommon.h> #include #define LFS_NO_ASSERT 1 #define LFS_THREADSAFE 1 #define LFS_NO_DEBUG 1 #define LFS_NO_WARN 1 #include "lfs/lfs.c" #include "lfs/lfs_util.c" #define LFS_NAME "littlefs" typedef struct __lfs_drv_ctrl lfsDrvCtrl; typedef struct lfs_ios_drv lfsIosDrv; struct __lfs_file_descr { union { lfs_file_t file; lfs_dir_t dir; } u; BOOL isDir; lfsDrvCtrl *ctrl; }; typedef struct __lfs_file_descr LFS_FILE_DESC; struct lfs_ios_drv { DEV_HDR devHdr; lfsDrvCtrl *pDrvCtrl; }; struct __lfs_drv_ctrl { VXB_DEVICE_ID pDev; /* per-driver info */ void *pDrvCtrl; char name[16]; const char *mountPoint; BOOL autoFormat; const char *flashDrvName; int flashUnit; UINT32 flashOffset; UINT32 flashSize; UINT32 pageSize; UINT32 blkNum; lfsIosDrv lfsDrv; FLASH_CHIP_ID mtd; struct lfs_config lfsConfig; SEM_ID lock; lfs_t fsh; }; /* {{{ vxBus methods */ LOCAL void lfsDevInstInit(VXB_DEVICE_ID pDev); LOCAL void lfsDevInstInit2(VXB_DEVICE_ID pDev); LOCAL void lfsDevInstConnect(VXB_DEVICE_ID pDev); LOCAL void lfsDevShow(VXB_DEVICE_ID pDev, int verbose); LOCAL STATUS lfsDevInstUnlink(VXB_DEVICE_ID pDev, void *unused); /* }}} */ /* Publish the methods for the resources controlled with this file */ /* clang-format off */ LOCAL struct drvBusFuncs lfsDevFuncs = { lfsDevInstInit, /* devInstanceInit */ lfsDevInstInit2, /* devInstanceInit2 */ lfsDevInstConnect /* devConnect */ }; LOCAL struct vxbDeviceMethod lfsDevMethods[] = { DEVMETHOD(busDevShow , lfsDevShow), DEVMETHOD(vxbDrvUnlink, lfsDevInstUnlink), { 0, 0 } }; LOCAL struct vxbSpiRegister lfsDevDriver = { { NULL, /* pNext */ VXB_DEVID_DEVICE, /* devID */ VXB_BUSID_PLB, /* busID */ VXBUS_VERSION_5, /* vxbVersion */ LFS_NAME, /* drvName */ &lfsDevFuncs, /* pDrvBusFuncs */ lfsDevMethods, /* pMethods */ NULL, /* devProbe */ NULL, /* pParamDefaults */ }, }; /* clang-format on */ LOCAL int __lfs_driver_node = -1; void lfsDevRegister(void) { (void)vxbDevRegister((struct vxbDevRegInfo *)&lfsDevDriver); } /* {{{ lfsDevFuncs */ LOCAL void lfsDevInstInit(VXB_DEVICE_ID pDev) { lfsDrvCtrl *drv; struct hcfDevice *pHcf; drv = (lfsDrvCtrl *)hwMemAlloc(sizeof(*drv)); if (drv == NULL) { return; } drv->pDrvCtrl = pDev; pDev->pDrvCtrl = drv; drv->mtd = NULL; pHcf = (struct hcfDevice *)hcfDeviceGet(pDev); if (pHcf == NULL) { #ifndef _VXBUS_BASIC_HWMEMLIB hwMemFree((char *)drv); #endif /* _VXBUS_BASIC_HWMEMLIB */ pDev->pDrvCtrl = NULL; return; } if (devResourceGet(pHcf, "flashName", HCF_RES_STRING, (void *)&drv->flashDrvName) != OK) { goto err; } if (devResourceGet(pHcf, "flashUnit", HCF_RES_INT, (void *)&drv->flashUnit) != OK) { drv->flashUnit = 0; } if (devResourceGet(pHcf, "flashOffset", HCF_RES_INT, (void *)&drv->flashOffset) != OK) { drv->flashOffset = 0; } if (devResourceGet(pHcf, "flashSize", HCF_RES_INT, (void *)&drv->flashSize) != OK) { drv->flashSize = 0; } if (devResourceGet(pHcf, "pageSize", HCF_RES_INT, (void *)&drv->pageSize) != OK) { drv->pageSize = 256; } if (devResourceGet(pHcf, "autoFormat", HCF_RES_INT, (void *)&drv->autoFormat) != OK) { drv->autoFormat = 0; } if (devResourceGet(pHcf, "mountPoint", HCF_RES_STRING, (void *)&drv->mountPoint) != OK) { snprintf(drv->name, sizeof drv->name, "/lfs%d", pDev->unitNumber); drv->mountPoint = drv->name; } vxbNextUnitGet(pDev); return; err: #ifndef _VXBUS_BASIC_HWMEMLIB hwMemFree((char *)drv); #endif /* _VXBUS_BASIC_HWMEMLIB */ pDev->pDrvCtrl = NULL; } /* {{{ lfs handler */ LOCAL LFS_FILE_DESC *lfsFuncOpen(lfsIosDrv *dev, const char *name, int flags, int mode) { int err; int is_dir = 0; LFS_FILE_DESC *ptr = NULL; char namep[_PARM_NAME_MAX + 1] = { 0 }; int oflags = 0, openFlag; if (!name) { goto end; } if (S_ISDIR(mode)) { is_dir = 1; } if (name[0] == '\0') { namep[0] = '/'; namep[1] = '.'; namep[2] = '\0'; is_dir = 1; } else { strncpy(namep, name, _PARM_NAME_MAX + 1); namep[_PARM_NAME_MAX - 1] = EOS; } if (namep[strlen(namep) - 1] == '/') { namep[strlen(namep) - 1] = '\0'; is_dir = 1; } ptr = calloc(1, sizeof *ptr); if (!ptr) { goto end; } ptr->ctrl = dev->pDrvCtrl; if (is_dir) { if ((flags & O_CREAT) == O_CREAT) { lfs_mkdir(&ptr->ctrl->fsh, namep); } err = lfs_dir_open(&ptr->ctrl->fsh, &ptr->u.dir, namep); if (err != LFS_ERR_OK) { goto end; } ptr->isDir = 1; return ptr; } openFlag = flags & (O_RDONLY | O_WRONLY | O_RDWR); switch (openFlag) { case O_RDONLY: oflags = LFS_O_RDONLY; break; case O_WRONLY: oflags = LFS_O_WRONLY; break; case O_RDWR: oflags = LFS_O_RDWR; break; default: errnoSet(EINVAL); goto end; break; } if (flags & O_CREAT) { oflags |= LFS_O_CREAT; } if (lfs_file_open(&ptr->ctrl->fsh, &ptr->u.file, namep, oflags) == LFS_ERR_OK) { ptr->isDir = 0; return ptr; } else { /* try dir */ if (lfs_dir_open(&ptr->ctrl->fsh, &ptr->u.dir, namep) == LFS_ERR_OK) { ptr->isDir = 1; return ptr; } } end: if (ptr) { free(ptr); } return (LFS_FILE_DESC *)(ERROR); } LOCAL int lfsFuncClose(LFS_FILE_DESC *pfd) { lfsDrvCtrl *pDrvCtrl = pfd->ctrl; if (pfd->isDir) { lfs_dir_close(&pDrvCtrl->fsh, &pfd->u.dir); } else { lfs_file_close(&pDrvCtrl->fsh, &pfd->u.file); } free(pfd); return OK; } LOCAL int lfsFuncIoctl(LFS_FILE_DESC *pfd, int cmd, _Vx_ioctl_arg_t arg) { struct stat *stbuf; int ret = OK; const char *name; DIR *dirp; size_t offset; struct lfs_info info; lfsDrvCtrl *pDrvCtrl = pfd->ctrl; errnoSet(OK); switch (cmd) { case FIOFSTATGET: stbuf = (struct stat *)(arg); stbuf->st_dev = (u_long)&pDrvCtrl->lfsDrv.devHdr; stbuf->st_nlink = 1; stbuf->st_attrib = 0; stbuf->st_mode = S_IRWXU | S_IRWXG | S_IRWXO; if (pfd->isDir) { stbuf->st_size = pDrvCtrl->pageSize; stbuf->st_blocks = 1; stbuf->st_mode |= S_IFDIR; } else { stbuf->st_mode |= S_IFREG; stbuf->st_size = lfs_file_size(&pDrvCtrl->fsh, &pfd->u.file); } break; case FIOREADDIR: if (!pfd->isDir) { errnoSet(EIO); ret = ERROR; break; } dirp = (DIR *)(arg); if (!lfs_dir_read(&pDrvCtrl->fsh, &pfd->u.dir, &info)) { /* return is not TRUE */ dirp->dd_eof = TRUE; ret = 0; break; } dirp->dd_eof = FALSE; dirp->dd_dirent.d_ino = __lfs_driver_node; strncpy(dirp->dd_dirent.d_name, info.name, _PARM_NAME_MAX + 1); dirp->dd_dirent.d_name[_PARM_NAME_MAX - 1] = EOS; break; case FIOSEEK: if (pfd->isDir) { errnoSet(EINVAL); ret = ERROR; break; } offset = (size_t)arg; lfs_file_seek(&pDrvCtrl->fsh, &pfd->u.file, offset, LFS_SEEK_SET); break; case FIOUNLINK: name = (const char *)arg; ret = lfs_remove(&pDrvCtrl->fsh, name); if (ret != LFS_ERR_OK) { } ret = OK; break; default: printf("lfs unsupport ioctl: %d\n", cmd); errnoSet(ENOTSUP); ret = ERROR; break; } return (ret); } LOCAL ssize_t lfsFuncRead(LFS_FILE_DESC *pfd, char *buffer, size_t maxbytes) { lfsDrvCtrl *pDrvCtrl = pfd->ctrl; if (pfd->isDir) { printf("read dir\r\n"); return 0; } return lfs_file_read(&pDrvCtrl->fsh, &pfd->u.file, buffer, maxbytes); } LOCAL ssize_t lfsFuncWrite(LFS_FILE_DESC *pfd, char *buffer, size_t nbytes) { lfsDrvCtrl *pDrvCtrl = pfd->ctrl; if (pfd->isDir) { printf("write dir\r\n"); return 0; } return lfs_file_write(&pDrvCtrl->fsh, &pfd->u.file, buffer, nbytes); } LOCAL STATUS lfsFuncRemove(lfsIosDrv *dev, char *pPath) { lfsDrvCtrl *pDrvCtrl = dev->pDrvCtrl; lfs_remove(&pDrvCtrl->fsh, pPath); return OK; } /* }}} */ /* {{{ lfs flash operations */ LOCAL int lfsFlashRead(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) { UINT32 pos; lfsDrvCtrl *pDrvCtrl = cfg->context; UINT8 *buffers[2]; buffers[0] = buffer; buffers[1] = NULL; pos = pDrvCtrl->flashOffset + block * cfg->block_size + off; if (pDrvCtrl->mtd->flashOPs.read(pDrvCtrl->mtd, pos, 1, size, buffers, NULL) == OK) { return LFS_ERR_OK; } return LFS_ERR_IO; } LOCAL int lfsFlashProg(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) { STATUS r; UINT32 pos; lfsDrvCtrl *pDrvCtrl = cfg->context; UINT8 *buffers[2]; buffers[0] = (void *)buffer; buffers[1] = NULL; pos = pDrvCtrl->flashOffset + block * cfg->block_size + off; r = pDrvCtrl->mtd->flashOPs.write(pDrvCtrl->mtd, pos, 1, size, buffers, NULL); return r == OK ? LFS_ERR_OK : LFS_ERR_IO; } LOCAL int lfsFlashErase(const struct lfs_config *cfg, lfs_block_t block) { UINT32 pos; lfsDrvCtrl *pDrvCtrl = cfg->context; pos = pDrvCtrl->flashOffset + block * cfg->block_size; if (pDrvCtrl->mtd->flashOPs.blkErase(pDrvCtrl->mtd, pos, 1) == OK) { return LFS_ERR_OK; } return LFS_ERR_IO; } LOCAL int lfsFlashSync(const struct lfs_config *c) { return LFS_ERR_OK; } #ifdef LFS_THREADSAFE LOCAL int lfsFlashLock(const struct lfs_config *cfg) { lfsDrvCtrl *pDrvCtrl = cfg->context; semTake(pDrvCtrl->lock, WAIT_FOREVER); return LFS_ERR_OK; } LOCAL int lfsFlashUnlock(const struct lfs_config *cfg) { lfsDrvCtrl *pDrvCtrl = cfg->context; semGive(pDrvCtrl->lock); return LFS_ERR_OK; } #endif LOCAL void lfsCfgInit(lfsDrvCtrl *drv, FLASH_CHIP_ID flash) { int buffer_size = flash->uPageSize > 0 ? flash->uPageSize : drv->pageSize; memset(&drv->lfsConfig, 0, sizeof drv->lfsConfig); drv->lfsConfig.context = drv; drv->lfsConfig.read = lfsFlashRead; drv->lfsConfig.prog = lfsFlashProg; drv->lfsConfig.erase = lfsFlashErase; drv->lfsConfig.sync = lfsFlashSync; drv->lfsConfig.read_size = buffer_size; drv->lfsConfig.prog_size = buffer_size; drv->lfsConfig.block_size = flash->uEraseSize; drv->lfsConfig.block_count = drv->blkNum; drv->lfsConfig.cache_size = buffer_size; drv->lfsConfig.lookahead_size = flash->uEraseSize / 8; drv->lfsConfig.block_cycles = 100; #ifdef LFS_THREADSAFE drv->lfsConfig.lock = lfsFlashLock; drv->lfsConfig.unlock = lfsFlashUnlock; #endif } /* }}} */ LOCAL void lfsDevInstInit2(VXB_DEVICE_ID pDev) { lfsDrvCtrl *pDrvCtrl = (lfsDrvCtrl *)pDev->pDrvCtrl; pDrvCtrl->lock = semBCreate(SEM_Q_FIFO, SEM_FULL); } LOCAL void lfsDevInstConnect(VXB_DEVICE_ID pDev) { lfsDrvCtrl *pDrvCtrl; VXB_DEVICE_ID pFlashDev; FLASH_CHIP_ID (*flashChipInfoGet)(VXB_DEVICE_ID pDev, UINT32 chipId); pDrvCtrl = (lfsDrvCtrl *)pDev->pDrvCtrl; if (__lfs_driver_node < 0) { /* clang-format off */ __lfs_driver_node = iosDrvInstall( (DRV_CREATE_PTR) lfsFuncOpen, (DRV_REMOVE_PTR) lfsFuncRemove, (DRV_OPEN_PTR) lfsFuncOpen, (DRV_CLOSE_PTR) lfsFuncClose, (DRV_READ_PTR) lfsFuncRead, (DRV_WRITE_PTR) lfsFuncWrite, (DRV_IOCTL_PTR) lfsFuncIoctl ); /* clang-format on */ } pFlashDev = vxbInstByNameFind((char *)pDrvCtrl->flashDrvName, pDrvCtrl->flashUnit); if (!pFlashDev) { return; } semTake(pDrvCtrl->lock, WAIT_FOREVER); flashChipInfoGet = (void *)vxbDevMethodGet(pFlashDev, DEVMETHOD_CALL(vxbFlashChipInfoGet)); pDrvCtrl->mtd = flashChipInfoGet(pFlashDev, 0); if (pDrvCtrl->mtd) { if (pDrvCtrl->flashSize <= 0) { pDrvCtrl->flashSize = pDrvCtrl->mtd->uChipSize - pDrvCtrl->flashOffset; } pDrvCtrl->blkNum = pDrvCtrl->flashSize / pDrvCtrl->mtd->uEraseSize; if (__lfs_driver_node > 0) { if (iosDevAdd(&pDrvCtrl->lfsDrv.devHdr, pDrvCtrl->mountPoint, __lfs_driver_node) == OK) { pDrvCtrl->lfsDrv.pDrvCtrl = pDrvCtrl; lfsCfgInit(pDrvCtrl, pDrvCtrl->mtd); } else { pDrvCtrl->mtd = NULL; } } else { pDrvCtrl->mtd = NULL; } } semGive(pDrvCtrl->lock); if (pDrvCtrl->mtd) { int ret; ret = lfs_mount(&pDrvCtrl->fsh, &pDrvCtrl->lfsConfig); if ((ret != LFS_ERR_OK) && pDrvCtrl->autoFormat) { lfs_format(&pDrvCtrl->fsh, &pDrvCtrl->lfsConfig); lfs_mount(&pDrvCtrl->fsh, &pDrvCtrl->lfsConfig); } } } /* }}} */ /* {{{ lfsMethods */ LOCAL void lfsDevShow(VXB_DEVICE_ID pDev, int verbose) { lfsDrvCtrl *pDrvCtrl = (lfsDrvCtrl *)pDev->pDrvCtrl; printf(" %s unit %d on %s @ 0x%08x", pDev->pName, pDev->unitNumber, vxbBusTypeString(pDev->busID), pDev); printf(" with busInfo %p\n", pDev->u.pSubordinateBus); if (verbose) { printf(" mountPoint: %s\n", pDrvCtrl->mountPoint); printf(" flashName: %s\n", pDrvCtrl->flashDrvName); printf(" mtd: %p\n", pDrvCtrl->mtd); printf(" read_size: %d\n", pDrvCtrl->lfsConfig.read_size); printf(" prog_size: %d\n", pDrvCtrl->lfsConfig.prog_size); printf(" block_size: %d\n", pDrvCtrl->lfsConfig.block_size); printf(" flashOffset: 0x%x\n", pDrvCtrl->flashOffset); printf(" partSize: 0x%x\n", pDrvCtrl->flashSize); printf(" blkNum: 0x%x\n", pDrvCtrl->blkNum); if (pDrvCtrl->mtd) { printf(" flashChip: %s\n", pDrvCtrl->mtd->chipName); } } } LOCAL STATUS lfsDevInstUnlink(VXB_DEVICE_ID pDev, void *unused) { lfsDrvCtrl *pDrvCtrl = (lfsDrvCtrl *)pDev->pDrvCtrl; /* Check for vaild parameter */ VXB_ASSERT_NONNULL_V(pDev); #ifndef _VXBUS_BASIC_HWMEMLIB hwMemFree((char *)pDrvCtrl); #endif /* _VXBUS_BASIC_HWMEMLIB */ pDev->pDrvCtrl = NULL; return OK; } /* }}} */ LOCAL lfsDrvCtrl * getLfsLowHandle(int unit) { VXB_DEVICE_ID pDev; lfsDrvCtrl *pDrvCtrl = NULL; pDev = vxbInstByNameFind(LFS_NAME, unit); if (pDev == NULL) { printf("Can't find lfs%d\n", unit); return NULL; } pDrvCtrl = (lfsDrvCtrl *)pDev->pDrvCtrl; if (!pDrvCtrl->mtd) { printf("can't find spiFlash\n"); return NULL; } return pDrvCtrl; } STATUS lfsFormat(int unit) { lfsDrvCtrl *pDrvCtrl = getLfsLowHandle(unit); if (pDrvCtrl == NULL) { return ERROR; } return lfs_format(&pDrvCtrl->fsh, &pDrvCtrl->lfsConfig); } STATUS lfsMount(int unit) { lfsDrvCtrl *pDrvCtrl = getLfsLowHandle(unit); if (pDrvCtrl == NULL) { return ERROR; } return lfs_mount(&pDrvCtrl->fsh, &pDrvCtrl->lfsConfig); } int lfsLowRawWrite(unsigned long offset, const char *buffer, int size) { lfsDrvCtrl *pDrvCtrl = getLfsLowHandle(0); int nblocks, block_size; STATUS ok = OK; if (pDrvCtrl == NULL) { return ERROR; } semTake(pDrvCtrl->lock, WAIT_FOREVER); block_size = pDrvCtrl->mtd->uEraseSize; if (block_size <= 0) { block_size = 0x10000; } nblocks = (size + block_size - 1) / block_size; if (pDrvCtrl->mtd->flashOPs.blkErase) { pDrvCtrl->mtd->flashOPs.blkErase(pDrvCtrl->mtd, offset, nblocks); } if (pDrvCtrl->mtd->flashOPs.write) { ok = pDrvCtrl->mtd->flashOPs.write(pDrvCtrl->mtd, offset, 1, size, (UINT8 **)&buffer, NULL); } semGive(pDrvCtrl->lock); return ok; }