#include #include #include #include #include #include #include #include #include #include #include #include "pm_flash.h" #include "pm_mtd.h" #define MAX_PARTS 32 /* Flash max partition number*/ #define MAX_MTD_PARTITION (MAX_PARTS) #define TMP_READ_BUF_SIZE 512 #define MTD_INFO_DEV "/proc/mtd" static xmedia_char *skip_space(xmedia_char *line) { xmedia_char *p = line; while (*p == ' ' || *p == '\t') { p++; } return p; } static xmedia_char *get_word(xmedia_char *line, xmedia_char *value) { xmedia_char *p = line; p = skip_space(p); while (*p != '\t' && *p != ' ' && *p != '\n' && *p != 0) { *value++ = *p++; } *value = 0; return p; } static xmedia_u32 file_name_start(xmedia_char *name) { xmedia_u32 size = 0; if (name) { size = strlen(name); if (size == 0) return size; while (size--) { if (name[size] == '/') return size + 1; } } return size; } static xmedia_s32 get_dname_from_pname(struct flash_part_dev *part_dev) { FILE *fd; xmedia_char *buff; xmedia_s32 ret = -1; xmedia_s32 i = 0; xmedia_char *p; xmedia_char devname[FLASH_NAME_LEN]; xmedia_char tmp[32]; buff = (xmedia_char *)malloc(TMP_READ_BUF_SIZE); if (buff == XMEDIA_NULL) return XMEDIA_ERRCODE_NOT_ENOUGH_MEMORY; fd = fopen(MTD_INFO_DEV, "r"); if (fd) { if (NULL == fgets(buff, TMP_READ_BUF_SIZE, fd)) {//skip first line fclose(fd); goto ERR_END; } for (i = 0; i < MAX_MTD_PARTITION; i++) { if (fgets(buff, TMP_READ_BUF_SIZE, fd) == NULL) { break; } p = &buff[0]; p = get_word(p, devname); p = get_word(p, tmp); part_dev->part.size = (xmedia_u32)strtoull(tmp, NULL, 16); p = get_word(p, tmp); part_dev->part.block_size = (xmedia_u32)strtoull(tmp, NULL, 16); p = get_word(p, tmp); if (strncmp(&tmp[1], part_dev->part.partname, strlen(part_dev->part.partname)) == 0){ /* delete ':' */ devname[strlen(devname) - 1] = 0; strncpy(part_dev->part.devname + file_name_start(part_dev->part.devname), devname, FLASH_NAME_LEN); printf("use new devname: %s\n", part_dev->part.devname); ret = XMEDIA_SUCCESS; break; } } fclose(fd); } ERR_END: free(buff); return ret; } /*****************************************************************************/ /* * > 0 skip bad block num. * = 0 no bad block. * < 0 error. */ static xmedia_s32 check_skip_badblock(struct flash_part_dev *part_dev, xmedia_s32 *blockindex, xmedia_s32 blocksize) { xmedia_s32 rel = 0; xmedia_s32 badblock; xmedia_u64 size = part_dev->flash.part_size + 1; loff_t offset = ((loff_t)(*blockindex) * (loff_t)blocksize); if (part_dev->part_fd == -1) { return -1; } do { badblock = ioctl(part_dev->part_fd, MEMGETBADBLOCK, &offset); if (badblock < 0) { PM_USR_TRACE(MODULE_DBG_ERR, "Get nand badblock fail. error(%d)\n", errno); return -1; } if (badblock == 1) { PM_USR_TRACE(MODULE_DBG_INFO, "Skip bad block at address: 0x%llX\n", (xmedia_u64)offset); (*blockindex)++; rel++; } offset = (loff_t)(*blockindex) * (loff_t)blocksize; } while (badblock == 1 && offset < (loff_t)size); if (offset >= (loff_t)size) return -1; return rel; } static xmedia_s32 nand_rw(struct flash_part_dev *part_dev, xmedia_u32 addr, xmedia_u32 size, xmedia_char *buf, xmedia_s32 w) { xmedia_s32 total = 0; xmedia_u32 cur_offset = addr; xmedia_u32 remaining_len = size; xmedia_char *cur_buf = buf; xmedia_u32 page_size = part_dev->flash.page_size; xmedia_u32 page_mask = page_size - 1; xmedia_u32 block_size = part_dev->flash.block_size; if ((addr & page_mask) || (size & page_mask)) { return XMEDIA_ERRCODE_INVALID_PARAM; } /* check addr and size */ if (addr > part_dev->flash.part_size || addr + size > part_dev->flash.part_size) return XMEDIA_ERRCODE_INVALID_PARAM; while (remaining_len && remaining_len <= size) { xmedia_s32 ret = 0; xmedia_s32 block_index, bad_skipped; xmedia_u32 rw_size; block_index = cur_offset / block_size; bad_skipped = check_skip_badblock(part_dev, &block_index, block_size); if (bad_skipped < 0) { return XMEDIA_ERRCODE_BAD_ADDRESS; } else { cur_offset += bad_skipped * block_size; } if (lseek(part_dev->part_fd, cur_offset, SEEK_SET) < 0) { PM_USR_TRACE(MODULE_DBG_ERR, "Fail to seek to 0x%x\n", addr); return XMEDIA_ERRCODE_BAD_ADDRESS; } if (remaining_len > block_size) rw_size = block_size; else rw_size = remaining_len; if (w) ret = write(part_dev->part_fd, cur_buf, rw_size); else ret = read(part_dev->part_fd, cur_buf, rw_size); if (ret < 0) { PM_USR_TRACE(MODULE_DBG_ERR, "Fail to %s at 0x%x\n", w ? "write" : "read", addr); return XMEDIA_ERRCODE_BAD_ADDRESS; } cur_buf += rw_size; cur_offset += rw_size; remaining_len -= rw_size; total += ret; } return total; } static xmedia_s32 nand_read(struct flash_part_dev *part_dev, xmedia_u32 addr, xmedia_u32 size, xmedia_char *buf) { return nand_rw(part_dev, addr, size, buf, 0); } static xmedia_s32 nand_write(struct flash_part_dev *part_dev, xmedia_u32 addr, xmedia_u32 size, xmedia_char *buf) { return nand_rw(part_dev, addr, size, buf, 1); } static xmedia_s32 nand_erase(struct flash_part_dev *part_dev, xmedia_u32 addr, xmedia_u32 size) { xmedia_u32 cur_offset = addr; xmedia_u32 remaining_len = size; xmedia_u32 block_size = part_dev->flash.block_size; xmedia_u32 block_mask = block_size - 1; struct erase_info_user64 eraseinfo; xmedia_s32 ret; if ((addr & block_mask) || (size & block_mask)) { return XMEDIA_ERRCODE_INVALID_PARAM; } /* check addr and size */ if (addr > part_dev->flash.part_size || addr + size > part_dev->flash.part_size) return XMEDIA_ERRCODE_INVALID_PARAM; while (remaining_len && remaining_len <= size) { xmedia_s32 block_index, bad_skipped; block_index = cur_offset / block_size; bad_skipped = check_skip_badblock(part_dev, &block_index, block_size); if (bad_skipped < 0) { return XMEDIA_ERRCODE_BAD_ADDRESS; } else { cur_offset += bad_skipped * block_size; } eraseinfo.start = cur_offset; eraseinfo.length = block_size; ret = ioctl(part_dev->part_fd, MEMERASE64, &eraseinfo); if (ret != 0) { PM_USR_TRACE(MODULE_DBG_ERR, "%s erase error ret:%d\n", __func__, ret); return XMEDIA_ERRCODE_INVALID_PARAM; } cur_offset += block_size; remaining_len -= block_size; } return XMEDIA_SUCCESS; } static xmedia_s32 nand_open(struct flash_part_dev *part_dev, unsigned int flag) { if (part_dev->part.partname[0] != 0) get_dname_from_pname(part_dev); part_dev->part_fd = open(part_dev->part.devname, flag); return part_dev->part_fd; } static xmedia_s32 nand_close(struct flash_part_dev *part_dev) { close(part_dev->part_fd); part_dev->part_fd = -1; return XMEDIA_SUCCESS; } static xmedia_s32 nand_init(struct flash_part_dev *part_dev) { struct mtd_info_user mtdinfo; xmedia_s32 ret; ret = ioctl(part_dev->part_fd, MEMGETINFO, &mtdinfo); if (ret != 0) { PM_USR_TRACE(MODULE_DBG_ERR, "%s mem get info error ret:%d\n", __func__, ret); return XMEDIA_ERRCODE_INVALID_PARAM; } part_dev->flash.block_size = mtdinfo.erasesize; part_dev->flash.page_size = mtdinfo.writesize; part_dev->flash.oob_size = mtdinfo.oobsize; part_dev->flash.part_size = mtdinfo.size; return XMEDIA_SUCCESS; } static xmedia_s32 nand_getinfo(struct flash_part_dev *part_dev) { xmedia_s32 ret = -1; xmedia_s32 i = 0; xmedia_char *buff; FILE *fd; xmedia_char *p; xmedia_char tmp[32]; buff = (xmedia_char *)malloc(TMP_READ_BUF_SIZE); if (buff == XMEDIA_NULL) return XMEDIA_ERRCODE_NOT_ENOUGH_MEMORY; fd = fopen(MTD_INFO_DEV, "r"); if (fd) { if (NULL == fgets(buff, TMP_READ_BUF_SIZE, fd)) {//skip first line fclose(fd); goto ERR_END; } for (i = 0; i < MAX_MTD_PARTITION; i++) { if (fgets(buff, TMP_READ_BUF_SIZE, fd) == NULL) { break; } p = &buff[0]; p = get_word(p, tmp); if (strncmp(tmp, &part_dev->part.devname[5], strlen(&part_dev->part.devname[5])) == 0) { p = get_word(p, tmp); part_dev->part.size = (xmedia_u32)strtoull(tmp, NULL, 16); p = get_word(p, tmp); part_dev->part.block_size = (xmedia_u32)strtoull(tmp, NULL, 16); p = get_word(p, tmp); strncpy(part_dev->part.partname, &tmp[1], (strlen(&tmp[1]) - 1) < FLASH_NAME_LEN ? (strlen(&tmp[1]) - 1) : FLASH_NAME_LEN); ret = XMEDIA_SUCCESS; break; } } fclose(fd); } ERR_END: free(buff); return ret; } static struct flash_opt nand_opt = { .read = nand_read, .write = nand_write, .erase = nand_erase, .init = nand_init, .open = nand_open, .close = nand_close, .get_info = nand_getinfo, }; struct flash_opt *get_nand_opt(void) { return &nand_opt; }