/* * Copyright (c) 2006-2022, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2018-06-23 armink the first version * 2019-08-22 MurphyZhao adapt to none rt-thread case */ #include #ifdef RT_VER_NUM #include #include #include #include /* ========================== block device ======================== */ struct fal_blk_device { struct rt_device parent; struct rt_device_blk_geometry geometry; const struct fal_partition *fal_part; #ifdef AIC_FATFS_ENABLE_WRITE_IN_SPINOR rt_uint32_t length; rt_uint8_t *buf; #endif }; /* RT-Thread device interface */ #if RTTHREAD_VERSION >= 30000 static rt_err_t blk_dev_control(rt_device_t dev, int cmd, void *args) #else static rt_err_t blk_dev_control(rt_device_t dev, rt_uint8_t cmd, void *args) #endif { struct fal_blk_device *part = (struct fal_blk_device*) dev; assert(part != RT_NULL); if (cmd == RT_DEVICE_CTRL_BLK_GETGEOME) { struct rt_device_blk_geometry *geometry; geometry = (struct rt_device_blk_geometry *) args; if (geometry == RT_NULL) { return -RT_ERROR; } memcpy(geometry, &part->geometry, sizeof(struct rt_device_blk_geometry)); } else if (cmd == RT_DEVICE_CTRL_BLK_ERASE) { rt_uint32_t *addrs = (rt_uint32_t *) args, start_addr = addrs[0], end_addr = addrs[1], phy_start_addr; rt_size_t phy_size; log_d("start_addr = %d end_addr = %d.\n", start_addr, end_addr); if (addrs == RT_NULL || start_addr > end_addr) { return -RT_ERROR; } if (end_addr == start_addr) { end_addr++; } phy_start_addr = start_addr * part->geometry.bytes_per_sector; phy_size = (end_addr - start_addr) * part->geometry.bytes_per_sector; if (fal_partition_erase(part->fal_part, phy_start_addr, phy_size) < 0) { return -RT_ERROR; } } return RT_EOK; } static rt_size_t blk_dev_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size) { int ret = 0; struct fal_blk_device *part = (struct fal_blk_device*) dev; assert(part != RT_NULL); ret = fal_partition_read(part->fal_part, pos * part->geometry.block_size, buffer, size * part->geometry.block_size); if (ret != (int)(size * part->geometry.block_size)) { ret = 0; } else { ret = size; } return ret; } static rt_size_t __attribute__((unused)) blk_dev_cal_phy_len(rt_device_t dev, rt_off_t pos, rt_size_t size, rt_size_t cluster_size) { rt_size_t phy_len = 0; rt_uint8_t phy_sector_num = 0; struct fal_blk_device *part = (struct fal_blk_device*) dev; assert(part != RT_NULL); phy_sector_num = roundup(pos % cluster_size + size, cluster_size); phy_len = phy_sector_num * part->geometry.bytes_per_sector; return phy_len; } static rt_size_t blk_dev_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t sectors) { #ifndef AIC_FATFS_ENABLE_WRITE_IN_SPINOR log_e("This config only supports read!\n"); return sectors; #else #define FATFS_CLUSTER_SIZE AIC_USING_FS_IMAGE_TYPE_FATFS_CLUSTER_SIZE struct fal_blk_device *part = (struct fal_blk_device*) dev; rt_size_t total_phy_len, total_copy_len; rt_size_t do_phy_len, do_copy_len; rt_off_t phy_pos, buf_pos; int ret = 0; assert(part != RT_NULL); total_phy_len = blk_dev_cal_phy_len(dev, pos, sectors, FATFS_CLUSTER_SIZE); buf_pos = pos % FATFS_CLUSTER_SIZE * part->geometry.bytes_per_sector; phy_pos = (pos - pos % FATFS_CLUSTER_SIZE) * part->geometry.bytes_per_sector; total_copy_len = sectors * part->geometry.bytes_per_sector; while (total_copy_len != 0) { if (total_phy_len > part->length) { do_phy_len = part->length; do_copy_len = part->length - pos % FATFS_CLUSTER_SIZE * part->geometry.bytes_per_sector; } else { do_phy_len = total_phy_len; do_copy_len = total_copy_len; } memset(part->buf, 0xFF, part->length); ret = fal_partition_read(part->fal_part, phy_pos, part->buf, do_phy_len); if (ret != (int)(do_phy_len)) { log_e("FAL partition read data size failed!\n"); return 0; } rt_memcpy(part->buf + buf_pos, buffer, do_copy_len); ret = fal_partition_erase(part->fal_part, phy_pos, do_phy_len); if (ret != (int)(do_phy_len)) { log_e("FAL partition erase data size failed!\n"); return 0; } ret = fal_partition_write(part->fal_part, phy_pos, part->buf, do_phy_len); if (ret != (int)(do_phy_len)) { log_e("FAL partition write data size failed!\n"); return 0; } phy_pos += do_phy_len; pos += do_copy_len / part->geometry.bytes_per_sector; buf_pos = pos % FATFS_CLUSTER_SIZE * part->geometry.bytes_per_sector; total_copy_len -= do_copy_len; total_phy_len -= do_phy_len; } return sectors; #endif } #ifdef RT_USING_DEVICE_OPS static const struct rt_device_ops blk_dev_ops = { RT_NULL, RT_NULL, RT_NULL, blk_dev_read, blk_dev_write, blk_dev_control }; #endif /** * create RT-Thread block device by specified partition * * @param parition_name partition name * * @return != NULL: created block device * NULL: created failed */ struct rt_device *fal_blk_device_create(const char *parition_name) { struct fal_blk_device *blk_dev; const struct fal_partition *fal_part = fal_partition_find(parition_name); const struct fal_flash_dev *fal_flash = NULL; char str[32] = {0}; if (!fal_part) { log_e("Error: the partition name (%s) is not found.", parition_name); return NULL; } if ((fal_flash = fal_flash_device_find(fal_part->flash_name)) == NULL) { log_e("Error: the flash device name (%s) is not found.", fal_part->flash_name); return NULL; } blk_dev = (struct fal_blk_device*) rt_malloc(sizeof(struct fal_blk_device)); if (blk_dev) { #ifdef AIC_FATFS_ENABLE_WRITE_IN_SPINOR blk_dev->length = AIC_USING_FS_IMAGE_TYPE_FATFS_CLUSTER_SIZE * 512 * 2; blk_dev->buf = (uint8_t *)rt_malloc(blk_dev->length); if (!blk_dev->buf) { log_e("Error: no memory for create SPI NOR block buf"); return NULL; } #endif blk_dev->fal_part = fal_part; /*To solve the problem of space waste when the sector is set to 4096*/ /*bytes_per_sector and block_size must set to 512, the fatfs partition info can be recognized normally*/ blk_dev->geometry.bytes_per_sector = 512; blk_dev->geometry.block_size = blk_dev->geometry.bytes_per_sector; blk_dev->geometry.sector_count = fal_part->len / blk_dev->geometry.bytes_per_sector; /* register device */ blk_dev->parent.type = RT_Device_Class_Block; #ifdef RT_USING_DEVICE_OPS blk_dev->parent.ops = &blk_dev_ops; #else blk_dev->parent.init = NULL; blk_dev->parent.open = NULL; blk_dev->parent.close = NULL; blk_dev->parent.read = blk_dev_read; blk_dev->parent.write = blk_dev_write; blk_dev->parent.control = blk_dev_control; #endif /* no private */ blk_dev->parent.user_data = RT_NULL; rt_sprintf(str, "blk_%s", fal_part->name); log_d("The FAL block device (%s) created successfully", str); rt_device_register(RT_DEVICE(blk_dev), str, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE); } else { log_e("Error: no memory for create FAL block device"); } return RT_DEVICE(blk_dev); } /* ========================== MTD nor device ======================== */ #if defined(RT_USING_MTD_NOR) struct fal_mtd_nor_device { struct rt_mtd_nor_device parent; const struct fal_partition *fal_part; }; static rt_size_t mtd_nor_dev_read(struct rt_mtd_nor_device* device, rt_off_t offset, rt_uint8_t* data, rt_uint32_t length) { int ret = 0; struct fal_mtd_nor_device *part = (struct fal_mtd_nor_device*) device; assert(part != RT_NULL); ret = fal_partition_read(part->fal_part, offset, data, length); if (ret != (int)length) { ret = 0; } else { ret = length; } return ret; } static rt_size_t mtd_nor_dev_write(struct rt_mtd_nor_device* device, rt_off_t offset, const rt_uint8_t* data, rt_uint32_t length) { int ret = 0; struct fal_mtd_nor_device *part; part = (struct fal_mtd_nor_device*) device; assert(part != RT_NULL); ret = fal_partition_write(part->fal_part, offset, data, length); if (ret != (int) length) { ret = 0; } else { ret = length; } return ret; } static rt_err_t mtd_nor_dev_erase(struct rt_mtd_nor_device* device, rt_off_t offset, rt_uint32_t length) { int ret = 0; struct fal_mtd_nor_device *part; part = (struct fal_mtd_nor_device*) device; assert(part != RT_NULL); ret = fal_partition_erase(part->fal_part, offset, length); if ((rt_uint32_t)ret != length || ret < 0) { return -RT_ERROR; } else { return RT_EOK; } } static const struct rt_mtd_nor_driver_ops _ops = { RT_NULL, mtd_nor_dev_read, mtd_nor_dev_write, mtd_nor_dev_erase, }; /** * create RT-Thread MTD NOR device by specified partition * * @param parition_name partition name * * @return != NULL: created MTD NOR device * NULL: created failed */ struct rt_device *fal_mtd_nor_device_create(const char *parition_name) { struct fal_mtd_nor_device *mtd_nor_dev; const struct fal_partition *fal_part = fal_partition_find(parition_name); const struct fal_flash_dev *fal_flash = NULL; if (!fal_part) { log_e("Error: the partition name (%s) is not found.", parition_name); return NULL; } if ((fal_flash = fal_flash_device_find(fal_part->flash_name)) == NULL) { log_e("Error: the flash device name (%s) is not found.", fal_part->flash_name); return NULL; } mtd_nor_dev = (struct fal_mtd_nor_device*) rt_malloc(sizeof(struct fal_mtd_nor_device)); if (mtd_nor_dev) { mtd_nor_dev->fal_part = fal_part; mtd_nor_dev->parent.block_start = 0; mtd_nor_dev->parent.block_end = fal_part->len / fal_flash->blk_size; mtd_nor_dev->parent.block_size = fal_flash->blk_size; /* set ops */ mtd_nor_dev->parent.ops = &_ops; log_d("The FAL MTD NOR device (%s) created successfully", fal_part->name); rt_mtd_nor_register_device(fal_part->name, &mtd_nor_dev->parent); } else { log_e("Error: no memory for create FAL MTD NOR device"); } return RT_DEVICE(&mtd_nor_dev->parent); } #endif /* defined(RT_USING_MTD_NOR) */ /* ========================== char device ======================== */ struct fal_char_device { struct rt_device parent; const struct fal_partition *fal_part; }; /* RT-Thread device interface */ static rt_size_t char_dev_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) { int ret = 0; struct fal_char_device *part = (struct fal_char_device *) dev; assert(part != RT_NULL); if (pos + size > part->fal_part->len) size = part->fal_part->len - pos; ret = fal_partition_read(part->fal_part, pos, buffer, size); if (ret != (int)(size)) ret = 0; return ret; } static rt_size_t char_dev_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) { int ret = 0; struct fal_char_device *part; part = (struct fal_char_device *) dev; assert(part != RT_NULL); if (pos == 0) { fal_partition_erase_all(part->fal_part); } else if (pos + size > part->fal_part->len) { size = part->fal_part->len - pos; } ret = fal_partition_write(part->fal_part, pos, buffer, size); if (ret != (int) size) ret = 0; return ret; } #ifdef RT_USING_DEVICE_OPS static const struct rt_device_ops char_dev_ops = { RT_NULL, RT_NULL, RT_NULL, char_dev_read, char_dev_write, RT_NULL }; #endif #ifdef RT_USING_POSIX #include /* RT-Thread device filesystem interface */ static int char_dev_fopen(struct dfs_fd *fd) { struct fal_char_device *part = (struct fal_char_device *) fd->data; assert(part != RT_NULL); switch (fd->flags & O_ACCMODE) { case O_RDONLY: break; case O_WRONLY: case O_RDWR: /* erase partition when device file open */ fal_partition_erase_all(part->fal_part); break; default: break; } fd->pos = 0; return RT_EOK; } static int char_dev_fread(struct dfs_fd *fd, void *buf, size_t count) { int ret = 0; struct fal_char_device *part = (struct fal_char_device *) fd->data; assert(part != RT_NULL); if (fd->pos + count > part->fal_part->len) count = part->fal_part->len - fd->pos; ret = fal_partition_read(part->fal_part, fd->pos, buf, count); if (ret != (int)(count)) return 0; fd->pos += ret; return ret; } static int char_dev_fwrite(struct dfs_fd *fd, const void *buf, size_t count) { int ret = 0; struct fal_char_device *part = (struct fal_char_device *) fd->data; assert(part != RT_NULL); if (fd->pos + count > part->fal_part->len) count = part->fal_part->len - fd->pos; ret = fal_partition_write(part->fal_part, fd->pos, buf, count); if (ret != (int) count) return 0; fd->pos += ret; return ret; } static const struct dfs_file_ops char_dev_fops = { char_dev_fopen, RT_NULL, RT_NULL, char_dev_fread, char_dev_fwrite, RT_NULL, /* flush */ RT_NULL, /* lseek */ RT_NULL, /* getdents */ RT_NULL, }; #endif /* defined(RT_USING_POSIX) */ /** * create RT-Thread char device by specified partition * * @param parition_name partition name * * @return != NULL: created char device * NULL: created failed */ struct rt_device *fal_char_device_create(const char *parition_name) { struct fal_char_device *char_dev; const struct fal_partition *fal_part = fal_partition_find(parition_name); if (!fal_part) { log_e("Error: the partition name (%s) is not found.", parition_name); return NULL; } if ((fal_flash_device_find(fal_part->flash_name)) == NULL) { log_e("Error: the flash device name (%s) is not found.", fal_part->flash_name); return NULL; } char_dev = (struct fal_char_device *) rt_malloc(sizeof(struct fal_char_device)); if (char_dev) { char_dev->fal_part = fal_part; /* register device */ char_dev->parent.type = RT_Device_Class_Char; #ifdef RT_USING_DEVICE_OPS char_dev->parent.ops = &char_dev_ops; #else char_dev->parent.init = NULL; char_dev->parent.open = NULL; char_dev->parent.close = NULL; char_dev->parent.read = char_dev_read; char_dev->parent.write = char_dev_write; char_dev->parent.control = NULL; /* no private */ char_dev->parent.user_data = NULL; #endif rt_device_register(RT_DEVICE(char_dev), fal_part->name, RT_DEVICE_FLAG_RDWR); log_d("The FAL char device (%s) created successfully", fal_part->name); #ifdef RT_USING_POSIX /* set fops */ char_dev->parent.fops = &char_dev_fops; #endif } else { log_e("Error: no memory for create FAL char device"); } return RT_DEVICE(char_dev); } #endif /* RT_VER_NUM */