You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

665 lines
17 KiB

/* vim: set ts=4 sw=4 et fdm=marker: */
#include <vxWorks.h>
#include <stdio.h>
#include <string.h>
#include <logLib.h>
#include <vxBusLib.h>
#include <semLib.h>
#include <taskLib.h>
#include <sysLib.h>
#include <tickLib.h>
#include <iosLib.h>
#include <errnoLib.h>
#include <dirent.h>
#include <hwif/vxbus/vxBus.h>
#include <hwif/vxbus/hwConf.h>
#include <hwif/vxbus/vxbPlbLib.h>
#include <hwif/util/hwMemLib.h>
#include <hwif/util/vxbParamSys.h>
#include <hwif/vxbus/vxbSpiLib.h>
#include <../h/flash/vxbFlash.h>
#include <../h/flash/vxbFlashCommon.h>
#include <usrLib.h>
#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;
}