/* * Copyright (c) Hisilicon Technologies Co., Ltd. 2020-2020. All rights reserved. * Description: external interface module, support emmc, nand, spi-nor * Author: Hisilicon * Create: 2020-10-15 */ #include "flash_ext.h" #include #include #include #include #include "soc_log.h" #include "uapi_flash.h" #include "nand.h" #include "spi_raw.h" #include "nand_raw.h" #include "emmc_raw.h" #include "ufs_raw.h" #include "cmdline_parts.h" #include "securec.h" #define MAX_HANDLE MAX_PARTS /* Flash max handle number */ #undef LOG_MODULE_ID #define LOG_MODULE_ID SOC_ID_FLASH /* Expand flash handle fd, only ext_Flash_OpenByTypeAndName() use it */ #define SPAN_PART_HANDLE 1000 /* Flash operation descriptions */ typedef struct { td_s32(*raw_read)(flash_rw_info_s *rw_info, td_u8 *buffer, td_ulong length, td_s32 skip_badblock); td_s32(*raw_write)(flash_rw_info_s *rw_info, td_u8 *buffer, td_ulong length); td_s64(*raw_erase)(td_s32 fd, td_u64 startaddr, td_u64 length, td_u64 openaddr, td_u64 limit_leng); } flash_opt; /* Flash Infomation */ typedef struct { td_u64 total_size; /* flash total size */ td_u64 part_size; /* flash partition size */ td_u32 block_size; /* flash block size */ td_u32 page_size; /* flash page size */ td_u32 oob_size; /* flash OOB size */ td_void *fd; /* file handle */ td_u64 open_addr; /* flash open address */ td_u64 open_len; /* flash open length */ uapi_flash_type flash_type; /* flash type */ flash_opt *flash_opt; /* operation callbacks on this flash */ uapi_flash_partinfo *part_info; /* parition descriptions on this flash */ } flash_inter_info_s; typedef struct { td_u64 total_size; td_u32 page_size; td_u32 block_size; td_u32 oob_size; td_u32 block_shift; } flash_spinand_info_s; typedef enum { EXT_FLASH_STAT_INSTALL, EXT_FLASH_STAT_UNINSTALL, /* lint -save -e749 */ EXT_FLASH_STAT_BUTT, } flash_dev_stat_e; static flash_inter_info_s g_flash_info[MAX_HANDLE]; static uapi_flash_partinfo g_part_info[MAX_PARTS]; static td_bool g_init_flag = TD_FALSE; static volatile td_bool g_init_mutex = TD_FALSE; static td_u8 g_bootargs_str[COMMAND_LINE_SIZE]; static td_char g_flash_str[UAPI_FLASH_TYPE_MAX][MAX_FLASH_TYPE_LEN] = { "flash_auto", "fmc_sfc:", "fmc_sfc1:", "fmc_sfc2:", "soct_nand:", "soct_nand1:", "soct_nand2:", "soct_nand3:", "mmcblk0:", "mmcblk1:", "mmcblk2:", "mmcblk3:", "sdd:", }; static td_char *g_dev_pos[UAPI_FLASH_TYPE_MAX]; static flash_dev_stat_e g_dev_stat[UAPI_FLASH_TYPE_MAX]; static pthread_mutex_t g_flash_mutex; static flash_opt g_dev_flash_opt[UAPI_FLASH_TYPE_MAX]; static td_s32 check_flash_init(td_handle flash) { do { if (!g_init_flag) { soc_log_err("NOT init yet!\n"); return TD_FAILURE; } if (flash >= MAX_HANDLE || g_flash_info[(flash)].fd == (td_void *)(intptr_t)INVALID_FD) { return TD_FAILURE; } } while (0); return TD_SUCCESS; } static td_s32 check_addr_len(td_u64 address, td_u64 len, td_u64 limit_len) { do { if (((address) >= (limit_len)) || (((address) + (len)) > (limit_len))) { soc_log_err("startaddr(0x%llX) + length(0x%llx) or startaddr should be smaller than partsize(0x%llX)\n", (address), (len), (limit_len)); return TD_FAILURE; } } while (0); return TD_SUCCESS; } /* Call this should init g_bootargs_str and g_dev_pos firstly */ uapi_flash_type get_flashtype_by_bootargs(const td_char *part_name) { td_char *partition_pos = NULL; td_char *partition_pos1 = NULL; td_char *tmp_pos = NULL; td_char tmp_str[64]; /* 64 - The size of storage partition name temporary buffer */ td_u32 i; uapi_flash_type flash_type = UAPI_FLASH_TYPE_MAX; if (part_name == NULL) { return UAPI_FLASH_TYPE_MAX; } if (strchr(part_name, ' ')) { soc_log_err("Invalid partition_name, should not include ' '.\n"); return UAPI_FLASH_TYPE_MAX; } memset_s(tmp_str, sizeof(tmp_str), 0, sizeof(tmp_str)); if (snprintf_s(tmp_str, sizeof(tmp_str), sizeof(tmp_str) - 1, "(%s)", part_name) == -1) { soc_log_err("Failed to snprintf_s.\n"); return UAPI_FLASH_TYPE_MAX; } partition_pos = strstr((td_char *)g_bootargs_str, tmp_str); if (partition_pos == NULL) { return UAPI_FLASH_TYPE_MAX; } /* The bootargs has the same partition call this would failed */ partition_pos1 = strstr((td_char *)partition_pos + 1, tmp_str); if (partition_pos1 != NULL) { soc_log_err("The same patition(%s) on bootargs!%s", part_name, partition_pos); return UAPI_FLASH_TYPE_MAX; } for (i = 0; i < UAPI_FLASH_TYPE_MAX; i++) { if ((g_dev_pos[i] == NULL) || (g_dev_pos[i] > partition_pos)) { continue; } /* tmp_pos is used to be a cursor */ if ((partition_pos >= g_dev_pos[i]) && (g_dev_pos[i] >= tmp_pos)) { flash_type = (uapi_flash_type)i; tmp_pos = g_dev_pos[i]; } } return flash_type; } /* Call this should init g_bootargs_str and g_dev_pos firstly */ static uapi_flash_type ext_get_flash_type(const td_char *partition_name) { int i, flash_num; uapi_flash_type flash_type = UAPI_FLASH_TYPE_MAX; uapi_flash_type boot_flash_type; if (partition_name != NULL) { return get_flashtype_by_bootargs(partition_name); } flash_num = 0; for (i = 0; i < UAPI_FLASH_TYPE_MAX; i++) { if (g_dev_pos[i]) { flash_type = (uapi_flash_type)i; flash_num++; } } /* 2: flash number */ if (flash_num > 2) { soc_log_err("Get flash type failed, %d flashes detected.\n", flash_num); return UAPI_FLASH_TYPE_MAX; } else if (flash_num > 1) { boot_flash_type = get_flashtype_by_bootargs("fastboot"); if (boot_flash_type == UAPI_FLASH_TYPE_MAX) { soc_log_err("Failed to get the boot flash type.\n"); return boot_flash_type; } for (i = 0; i < UAPI_FLASH_TYPE_MAX; i++) { if (g_dev_pos[i] && ((uapi_flash_type)i != boot_flash_type)) { flash_type = (uapi_flash_type)i; break; } } } return flash_type; } static td_s32 permission_check(td_u64 start_addr, td_u64 length) { td_u64 end_addr = start_addr + length - 1; td_u32 i; for (i = 0; i < MAX_PARTS; i++) { if (g_part_info[i].perm != ACCESS_NONE) { continue; } if ((g_part_info[i].start_addr >= start_addr) && (g_part_info[i].start_addr <= end_addr)) { soc_log_info("%s(%s) is not permitted to be opened.\n", g_part_info[i].dev_name, g_part_info[i].part_name); return TD_FAILURE; } } return TD_SUCCESS; } static td_s32 flash_partition_perm(td_s32 part) { td_s32 fd = -1; td_s32 ret; ret = snprintf_s(g_part_info[part].dev_name, FLASH_NAME_LEN, FLASH_NAME_LEN - 1, DEV_MTDBASE"%d", part); if (ret == -1) { ret = snprintf_s(g_part_info[part].dev_name, FLASH_NAME_LEN, FLASH_NAME_LEN - 1, "/dev/mtd%d", part); if (ret == -1) { soc_log_err("Failed to snprintf_s.\n"); return -1; } } fd = open(g_part_info[part].dev_name, O_RDWR); if (fd == -1) { fd = open(g_part_info[part].dev_name, O_RDONLY); if (fd == -1) { soc_log_err("Can't open \"%s\"\n", g_part_info[part].dev_name); g_part_info[part].perm = ACCESS_NONE; return -1; } soc_log_info("access %s readonly!\n", g_part_info[part].dev_name); g_part_info[part].perm = ACCESS_RD; } else { soc_log_info("access %s read and write, i:%d, fd:%x!\n", g_part_info[part].dev_name, part, fd); g_part_info[part].perm = ACCESS_RDWR; } close(fd); return 0; } static td_s32 flash_part_info_init(td_void) { td_s32 i; td_char line[BUF_LINE_SIZE]; td_u64 start_addr[UAPI_FLASH_TYPE_MAX] = {0}; uapi_flash_type flash_type; FILE *fp = NULL; fp = fopen("/proc/mtd", "r"); if (fp == NULL) { return -1; } if (fgets(line, sizeof(line), fp) == NULL) { /* skip first line */ (td_void)fclose(fp); return -1; } /* dev_name part_size block_size part_name Startaddr */ for (i = 0; fgets(line, sizeof(line), fp) != 0; i++) { td_char *p = NULL; td_char argv[4][32]; /* 4 - Get the number of vars, 32 - The size of each var buffer */ p = &line[0]; p = skip_space(p); p = skip_word(p); p = get_word(p, argv[1]); /* 1 - partition size */ p = get_word(p, argv[2]); /* 2 - block size */ p = get_word(p, argv[3]); /* 3 - partition name */ if (i >= MAX_PARTS) { soc_print("Detected there has more than %d partitions.\n" "You should encrease MAX_PARTS in order to use left partitions!\n", MAX_PARTS); break; } g_part_info[i].part_size = (td_u64)(td_s64)strtoull( (const td_char*)argv[1], (td_char**)NULL, HEX); /* 1 - partition size */ memset_s(g_part_info[i].part_name, sizeof(g_part_info[i].part_name), 0, sizeof(g_part_info[i].part_name)); if (strncpy_s(g_part_info[i].part_name, sizeof(g_part_info[i].part_name), (td_char*)(argv[3] + 1), (strlen((td_char*)argv[3]) - 2)) != 0) { /* 3 - partition name, 2 - */ break; } flash_type = get_flashtype_by_bootargs(g_part_info[i].part_name); if (flash_type >= UAPI_FLASH_TYPE_MAX) { continue; } g_part_info[i].start_addr = start_addr[flash_type]; start_addr[flash_type] += g_part_info[i].part_size; if (flash_partition_perm(i) < 0) { continue; } } (td_void)fclose(fp); return 0; } static td_void flash_device_init(td_void) { if (spi_raw_init() == TD_SUCCESS) { g_dev_stat[UAPI_FLASH_TYPE_NOR_0] = EXT_FLASH_STAT_INSTALL; } if (nand_raw_init() == TD_SUCCESS) { g_dev_stat[UAPI_FLASH_TYPE_NAND_0] = EXT_FLASH_STAT_INSTALL; } if (emmc_raw_init((td_char *)g_bootargs_str) == TD_SUCCESS) { g_dev_stat[UAPI_FLASH_TYPE_EMMC_0] = EXT_FLASH_STAT_INSTALL; } if (ufs_raw_init((td_char *)g_bootargs_str) == TD_SUCCESS) { g_dev_stat[UAPI_FLASH_TYPE_UFS_0] = EXT_FLASH_STAT_INSTALL; } } static td_void flash_opt_init(td_void) { g_dev_flash_opt[UAPI_FLASH_TYPE_NOR_0].raw_erase = spi_raw_erase; g_dev_flash_opt[UAPI_FLASH_TYPE_NOR_0].raw_read = spi_raw_read; g_dev_flash_opt[UAPI_FLASH_TYPE_NOR_0].raw_write = spi_raw_write; g_dev_flash_opt[UAPI_FLASH_TYPE_NAND_0].raw_erase = nand_raw_erase; g_dev_flash_opt[UAPI_FLASH_TYPE_NAND_0].raw_read = nand_raw_read; g_dev_flash_opt[UAPI_FLASH_TYPE_NAND_0].raw_write = nand_raw_write; } static td_s32 flash_info_init(td_void) { td_u32 i; for (i = 0; i < MAX_PARTS; i++) { memset_s(&g_part_info[i], sizeof(uapi_flash_partinfo), 0, sizeof(uapi_flash_partinfo)); g_part_info[i].perm = ACCESS_BUTT; } for (i = 0; i < MAX_HANDLE; i++) { (td_void)pthread_mutex_lock(&g_flash_mutex); g_flash_info[i].fd = (td_void *)INVALID_FD; g_flash_info[i].open_addr = 0; g_flash_info[i].open_len = 0; g_flash_info[i].part_info = NULL; g_flash_info[i].flash_type = UAPI_FLASH_TYPE_MAX; (td_void)pthread_mutex_unlock(&g_flash_mutex); } flash_opt_init(); if ((g_dev_stat[UAPI_FLASH_TYPE_NOR_0] == EXT_FLASH_STAT_INSTALL) || (g_dev_stat[UAPI_FLASH_TYPE_NAND_0] == EXT_FLASH_STAT_INSTALL)) { if (flash_part_info_init() < 0) { return TD_FAILURE; } } return TD_SUCCESS; } static td_s32 flash_all_init(uapi_flash_type flash_type) { td_u8 loop = 0; if (g_init_mutex) { while ((g_init_mutex != 0) && (loop < 100)) { /* 100 - Number of cycles */ usleep(100); /* sleep 100us */ loop++; } } else { g_init_mutex = TD_TRUE; } if (!g_init_flag) { (td_void)pthread_mutex_init(&g_flash_mutex, NULL); if (get_bootargs(g_bootargs_str, (sizeof(g_bootargs_str) - 1)) != TD_SUCCESS) { soc_log_err("Failed to get bootargs. \n"); g_init_mutex = TD_FALSE; return TD_FAILURE; } for (loop = 0; loop < UAPI_FLASH_TYPE_MAX; loop++) { g_dev_stat[loop] = EXT_FLASH_STAT_UNINSTALL; g_dev_pos[loop] = strstr((td_char *)g_bootargs_str, g_flash_str[loop]); } flash_device_init(); if (flash_info_init() == TD_FAILURE) { (td_void)spi_raw_destroy(); (td_void)nand_raw_destroy(); soc_log_err("Flash info init fail! \n"); g_init_mutex = TD_FALSE; return TD_FAILURE; } } g_init_flag = TD_TRUE; g_init_mutex = TD_FALSE; return TD_SUCCESS; } static td_s32 flash_unused_handle(void) { td_s8 loop; for (loop = 0; loop < MAX_HANDLE; loop++) { if (g_flash_info[loop].fd == (td_void *)INVALID_FD) { break; } } return loop; } static td_void flash_get_spinand_info(uapi_flash_type flash_type, flash_spinand_info_s *info) { if (flash_type == UAPI_FLASH_TYPE_NOR_0) { spi_raw_get_info(&info->total_size, &info->page_size, &info->block_size, &info->oob_size, &info->block_shift); } else { nand_raw_get_info(&info->total_size, &info->page_size, &info->block_size, &info->oob_size, &info->block_shift); } } static td_s32 flash_open_emmc_get_info(emmc_cb_s *emmc_cb) { td_s32 flash; flash = flash_unused_handle(); if (flash == MAX_HANDLE) { soc_log_err("flash array full! \n"); return TD_FAILURE; } g_flash_info[flash].fd = emmc_cb; g_flash_info[flash].flash_type = UAPI_FLASH_TYPE_EMMC_0; return flash; } static td_handle flash_open_emmc_by_addr(td_u64 address, td_u64 length) { td_s32 flash; emmc_cb_s *emmc_cb; emmc_cb = emmc_raw_open(address, length); if (emmc_cb == NULL) { return (td_handle)INVALID_FD; } flash = flash_open_emmc_get_info(emmc_cb); if (flash < 0) { (td_void)emmc_raw_close(emmc_cb); emmc_cb = NULL; return (td_handle)INVALID_FD; } return (td_handle)flash; } static td_s32 flash_open_ufs_get_info(ufs_cb_s *ufs_cb) { td_s32 flash; flash = flash_unused_handle(); if (flash == MAX_HANDLE) { soc_log_err("flash array full! \n"); return TD_FAILURE; } g_flash_info[flash].fd = ufs_cb; g_flash_info[flash].flash_type = UAPI_FLASH_TYPE_UFS_0; return flash; } static td_handle flash_open_ufs_by_addr(td_u64 address, td_u64 length) { td_s32 flash; ufs_cb_s *ufs_cb; ufs_cb = ufs_raw_open(address, length); if (ufs_cb == NULL) { return (td_handle)INVALID_FD; } flash = flash_open_ufs_get_info(ufs_cb); if (flash < 0) { (td_void)ufs_raw_close(ufs_cb); ufs_cb = NULL; return (td_handle)INVALID_FD; } return (td_handle)flash; } static td_handle flash_open_spi_nand_by_addr(uapi_flash_type flash_type, td_u64 address, td_u64 length) { td_u32 flash; flash_spinand_info_s raw_info; flash_get_spinand_info(flash_type, &raw_info); if (raw_info.block_size == 0) { soc_log_err("block_size shouldn't equal 0!\n"); return (td_handle)INVALID_FD; } if (((td_s32)(address % raw_info.block_size) != 0) || ((td_s32)(length % raw_info.block_size) != 0)) { soc_log_err("Open addr(%#llx) and len(%#llx) should be align with block_size(0x%X)!\n", address, length, raw_info.block_size); return (td_handle)INVALID_FD; } if ((address >= raw_info.total_size) || (address + length) > raw_info.total_size) { soc_log_err("Open addr(%#llx) and len(%#llx) should be smaller than total_size(0x%llX)!\n", address, length, raw_info.total_size); return (td_handle)INVALID_FD; } if (permission_check(address, length) != TD_SUCCESS) { soc_log_info("not permission to be opened.\n"); return (td_handle)INVALID_FD; } flash = (td_u32)flash_unused_handle(); if (flash == MAX_HANDLE) { soc_log_err("flash array full! \n"); return (td_handle)INVALID_FD; } g_flash_info[flash].fd = (td_slong *)(uintptr_t)(SPAN_PART_HANDLE + flash); g_flash_info[flash].open_addr = address; g_flash_info[flash].open_len = length; g_flash_info[flash].part_info = NULL; g_flash_info[flash].flash_type = flash_type; g_flash_info[flash].page_size = raw_info.page_size; g_flash_info[flash].oob_size = raw_info.oob_size; g_flash_info[flash].block_size = raw_info.block_size; if (flash_type == UAPI_FLASH_TYPE_NOR_0) { g_flash_info[flash].flash_opt = (flash_opt *)&g_dev_flash_opt[UAPI_FLASH_TYPE_NOR_0]; } else if (flash_type == UAPI_FLASH_TYPE_NAND_0) { g_flash_info[flash].flash_opt = (flash_opt *)&g_dev_flash_opt[UAPI_FLASH_TYPE_NAND_0]; } return (td_handle)flash; } static td_handle flash_open_emmc_by_node(const td_char *partition_name) { td_u64 address, length; td_s32 flash, ret; emmc_cb_s *emmc_cb = NULL; ret = strncmp(partition_name, "/dev/block/mmcblk0p", strlen("/dev/block/mmcblk0p")); if (ret != 0) { ret = strncmp(partition_name, "/dev/mmcblk0p", strlen("/dev/mmcblk0p")); if (ret != 0) { return (td_handle)INVALID_FD; } } emmc_cb = emmc_node_open((td_u8*)partition_name); if (emmc_cb == NULL) { return (td_handle)INVALID_FD; } if (emmc_find_part_from_devname("mmcblk0", (td_char *)g_bootargs_str, partition_name, &address, &length) != TD_SUCCESS) { soc_log_err("Cannot find partiton from %s\n", partition_name); (td_void)emmc_raw_close(emmc_cb); emmc_cb = NULL; return (td_handle)INVALID_FD; } emmc_cb->part_size = length; flash = flash_open_emmc_get_info(emmc_cb); if (flash < 0) { emmc_raw_close(emmc_cb); emmc_cb = NULL; return (td_handle)INVALID_FD; } return (td_handle)flash; } static td_handle flash_open_emmc_by_bootargs(const td_char *partition_name) { td_u64 address, length; td_s32 flash; td_char *ptr = NULL; emmc_cb_s *emmc_cb = NULL; ptr = strstr((td_char*)g_bootargs_str, "mmcblk0:"); if (ptr == NULL) { return (td_handle)INVALID_FD; } if (find_flash_part(ptr, "mmcblk0", partition_name, &address, &length) == 0) { soc_log_err("Cannot find partition: %s\n", partition_name); return (td_handle)INVALID_FD; } if (length == (td_u64)(-1)) { soc_log_err("Can not contain char '-'\n"); return (td_handle)INVALID_FD; } emmc_cb = emmc_raw_open(address, length); if (emmc_cb == NULL) { return (td_handle)INVALID_FD; } flash = flash_open_emmc_get_info(emmc_cb); if (flash < 0) { emmc_raw_close(emmc_cb); emmc_cb = NULL; return (td_handle)INVALID_FD; } return (td_handle)flash; } static td_handle flash_open_emmc_by_name(const td_char *partition_name) { td_handle flash; flash = flash_open_emmc_by_node(partition_name); if (flash != (td_handle)INVALID_FD) { return flash; } flash = flash_open_emmc_by_bootargs(partition_name); return flash; } static td_handle flash_open_ufs_by_node(const td_char *partition_name) { td_u64 address, length; td_s32 flash, ret; ufs_cb_s *ufs_cb = NULL; ret = strncmp(partition_name, "/dev/block/sdd", strlen("/dev/block/sdd")); if (ret != 0) { ret = strncmp(partition_name, "/dev/sdd", strlen("/dev/sdd")); if (ret != 0) { return (td_handle)INVALID_FD; } } ufs_cb = ufs_node_open((td_u8*)partition_name); if (ufs_cb == NULL) { return (td_handle)INVALID_FD; } if (ufs_find_part_from_devname("sdd", (td_char *)g_bootargs_str, partition_name, &address, &length) != TD_SUCCESS) { soc_log_err("Cannot find partiton from %s\n", partition_name); (td_void)ufs_raw_close(ufs_cb); ufs_cb = NULL; return (td_handle)INVALID_FD; } ufs_cb->part_size = length; flash = flash_open_ufs_get_info(ufs_cb); if (flash < 0) { ufs_raw_close(ufs_cb); ufs_cb = NULL; return (td_handle)INVALID_FD; } return (td_handle)flash; } static td_handle flash_open_ufs_by_bootargs(const td_char *partition_name) { td_u64 address, length; td_s32 flash; td_char *ptr = NULL; ufs_cb_s *ufs_cb = NULL; ptr = strstr((td_char*)g_bootargs_str, "sdd:"); if (ptr == NULL) { return (td_handle)INVALID_FD; } if (find_flash_part(ptr, "sdd", partition_name, &address, &length) == 0) { soc_log_err("Cannot find partition: %s\n", partition_name); return (td_handle)INVALID_FD; } if (length == (td_u64)(-1)) { soc_log_err("Can not contain char '-'\n"); return (td_handle)INVALID_FD; } ufs_cb = ufs_raw_open(address, length); if (ufs_cb == NULL) { return (td_handle)INVALID_FD; } flash = flash_open_ufs_get_info(ufs_cb); if (flash < 0) { ufs_raw_close(ufs_cb); ufs_cb = NULL; return (td_handle)INVALID_FD; } return (td_handle)flash; } static td_handle flash_open_ufs_by_name(const td_char *partition_name) { td_handle flash; flash = flash_open_ufs_by_node(partition_name); if (flash != (td_handle)INVALID_FD) { return flash; } flash = flash_open_ufs_by_bootargs(partition_name); return flash; } static td_s32 flash_partition_match(td_s32 part, uapi_flash_type flash_type, const td_char *partition_name) { uapi_flash_type check_flash_type; check_flash_type = get_flashtype_by_bootargs(partition_name); if ((strncmp(g_part_info[part].dev_name, partition_name, strlen(partition_name) + 1) == 0) && (check_flash_type == flash_type)) { /* eg: "/dev/mtd* " */ return TD_SUCCESS; } if ((strncmp(g_part_info[part].part_name, partition_name, strlen(partition_name) + 1) == 0) && (check_flash_type == flash_type)) { return TD_SUCCESS; } return TD_FAILURE; } static td_s32 flash_partition_get(uapi_flash_type flash_type, const td_char *partition_name) { td_s32 i, j; for (i = 0; i < MAX_PARTS; i++) { if (flash_partition_match(i, flash_type, partition_name) == TD_SUCCESS) { break; } } for (j = MAX_PARTS - 1; j >= 0; j--) { if (flash_partition_match(j, flash_type, partition_name) == TD_SUCCESS) { break; } } /* add i < 0 test in if branch to avoid pclint warning: * Warning 676: Possibly negative subscript (-1) in operator '[' */ if ((i == MAX_PARTS) || (i != j) || i < 0) { soc_log_err("can not find a right flash part(partition: %s, i=%d, j=%d)!\n", partition_name, i, j); return -1; } return i; } static td_s64 flash_open_spi_nand_get_fd(td_s32 part) { td_s32 i; td_s32 fd = -1; td_char dev_name[FLASH_NAME_LEN] = {0}; td_char tmp_name[PATH_MAX] = {0}; if (strncpy_s(dev_name, sizeof(dev_name), g_part_info[part].dev_name, sizeof(dev_name) - 1) != 0) { return (td_handle)INVALID_FD; } dev_name[sizeof(dev_name) - 1] = '\0'; for (i = 0; i < MAX_HANDLE; i++) { /* if the partition open, return index of array(g_flash_info) */ if ((g_flash_info[i].part_info != 0) && (strncmp(g_flash_info[i].part_info->dev_name, dev_name, strlen(dev_name) + 1) == 0)) { if (g_flash_info[i].fd != (td_void *)(intptr_t)INVALID_FD) { soc_log_info("dev_name =\"%s\"(%s)\n", g_flash_info[i].part_info->dev_name, g_flash_info[i].part_info->part_name); return (td_s64)(intptr_t)g_flash_info[i].fd; } } } if (realpath(dev_name, tmp_name) == NULL) { soc_log_err("Failed to realpath: %s.\n", dev_name); return TD_FAILURE; } if (g_part_info[part].perm == ACCESS_RDWR) { fd = open(tmp_name, O_RDWR); } else if (g_part_info[part].perm == ACCESS_RD) { fd = open(tmp_name, O_RDONLY); } else if (g_part_info[part].perm == ACCESS_WR) { fd = open(tmp_name, O_WRONLY); } else { soc_log_err("Device \"%s\"(%s) can not be opened \n", g_part_info[part].dev_name, g_part_info[part].part_name); return ((td_handle)INVALID_FD); } if ((fd < 0) || (fd >= SPAN_PART_HANDLE)) { soc_log_err("Open %s flash partition failure(fd = %lld)!\n", tmp_name, (td_s64)fd); if (fd >= 0) { close(fd); } return TD_FAILURE; } return fd; } static td_void flash_open_spi_nand_get_info(uapi_flash_type flash_type, td_u32 flash, td_s64 fd, td_s32 part) { flash_spinand_info_s raw_info; flash_get_spinand_info(flash_type, &raw_info); g_flash_info[flash].fd = (long *)(intptr_t)fd; g_flash_info[flash].open_len = g_part_info[part].part_size; g_flash_info[flash].part_info = &g_part_info[part]; g_flash_info[flash].open_addr = g_flash_info[flash].part_info->start_addr; g_flash_info[flash].flash_type = flash_type; g_flash_info[flash].page_size = raw_info.page_size; g_flash_info[flash].oob_size = raw_info.oob_size; g_flash_info[flash].block_size = raw_info.block_size; if (flash_type == UAPI_FLASH_TYPE_NOR_0) { g_flash_info[flash].flash_opt = (flash_opt *)&g_dev_flash_opt[UAPI_FLASH_TYPE_NOR_0]; } if (flash_type == UAPI_FLASH_TYPE_NAND_0) { g_flash_info[flash].flash_opt = (flash_opt *)&g_dev_flash_opt[UAPI_FLASH_TYPE_NAND_0]; } soc_log_info("dev_name =\"%s\"(%s)\n", g_flash_info[flash].part_info->dev_name, g_flash_info[flash].part_info->part_name); } static td_handle flash_open_spi_nand_by_name(uapi_flash_type flash_type, const td_char *partition_name) { td_s32 part; td_u32 flash; td_s64 fd; if (!strstr((char*)g_bootargs_str, "soct_nand:") && !strstr((char*)g_bootargs_str, "fmc_sfc:")) { return (td_handle)INVALID_FD; } /* add i < 0 test in if branch to avoid pclint warning: * Warning 676: Possibly negative subscript (-1) in operator '[' */ part = flash_partition_get(flash_type, partition_name); if (part < 0) { return (td_handle)INVALID_FD; } flash = (td_u32)flash_unused_handle(); if (flash == MAX_HANDLE) { soc_log_err("flash array full! \n"); return (td_handle)INVALID_FD; } fd = flash_open_spi_nand_get_fd(part); if (fd < 0) { return (td_handle)INVALID_FD; } flash_open_spi_nand_get_info(flash_type, flash, fd, part); soc_log_info("end.\n"); return (td_handle)flash; } td_handle uapi_flash_open_by_addr(uapi_flash_type flash_type, td_u64 address, td_u64 len) { td_handle flash; uapi_flash_type temp_flash_type = flash_type; if (flash_all_init(temp_flash_type) == TD_FAILURE) { return (td_handle)INVALID_FD; } if (temp_flash_type == UAPI_FLASH_TYPE_AUTO) { temp_flash_type = ext_get_flash_type(NULL); } if (temp_flash_type >= UAPI_FLASH_TYPE_MAX) { soc_log_err("flash_type error! \n"); return (td_handle)INVALID_FD; } if (g_dev_stat[temp_flash_type] != EXT_FLASH_STAT_INSTALL) { soc_log_err("No config flash[type: %d].\n", temp_flash_type); return (td_handle)INVALID_FD; } (td_void)pthread_mutex_lock(&g_flash_mutex); if (temp_flash_type == UAPI_FLASH_TYPE_EMMC_0) { flash = flash_open_emmc_by_addr(address, len); } else if (temp_flash_type == UAPI_FLASH_TYPE_UFS_0) { flash = flash_open_ufs_by_addr(address, len); } else { flash = flash_open_spi_nand_by_addr(temp_flash_type, address, len); } (td_void)pthread_mutex_unlock(&g_flash_mutex); soc_log_info("end.\n"); return flash; } td_handle uapi_flash_open(uapi_flash_type flash_type, const td_char *partition_name) { td_handle flash; uapi_flash_type temp_flash_type = flash_type; if (partition_name == NULL) { return (td_handle)INVALID_FD; } if (flash_all_init(temp_flash_type) == TD_FAILURE) { return (td_handle)INVALID_FD; } if (temp_flash_type == UAPI_FLASH_TYPE_AUTO) { temp_flash_type = ext_get_flash_type(partition_name); } if (temp_flash_type >= UAPI_FLASH_TYPE_MAX) { soc_log_err("flash_type error(flash_type=%d)! \n", temp_flash_type); return (td_handle)INVALID_FD; } if (g_dev_stat[temp_flash_type] != EXT_FLASH_STAT_INSTALL) { soc_log_err("No config flash[type:%d]", temp_flash_type); return (td_handle)INVALID_FD; } (td_void)pthread_mutex_lock(&g_flash_mutex); if (temp_flash_type == UAPI_FLASH_TYPE_EMMC_0) { flash = flash_open_emmc_by_name(partition_name); } else if (temp_flash_type == UAPI_FLASH_TYPE_UFS_0) { flash = flash_open_ufs_by_name(partition_name); } else { flash = flash_open_spi_nand_by_name(temp_flash_type, partition_name); } (td_void)pthread_mutex_unlock(&g_flash_mutex); return flash; } static td_void flash_close_emmc(td_handle flash) { emmc_cb_s *emmc_cb = ((emmc_cb_s *)(g_flash_info[flash].fd)); (td_void)emmc_raw_close(emmc_cb); emmc_cb = NULL; g_flash_info[flash].fd = (td_s64 *)(td_s64)INVALID_FD; g_flash_info[flash].open_addr = 0; g_flash_info[flash].open_len = 0; g_flash_info[flash].part_info = NULL; g_flash_info[flash].flash_type = UAPI_FLASH_TYPE_MAX; } static td_void flash_close_ufs(td_handle flash) { ufs_cb_s *ufs_cb = ((ufs_cb_s *)(g_flash_info[flash].fd)); (td_void)ufs_raw_close(ufs_cb); ufs_cb = NULL; g_flash_info[flash].fd = (td_s64 *)(td_s64)INVALID_FD; g_flash_info[flash].open_addr = 0; g_flash_info[flash].open_len = 0; g_flash_info[flash].part_info = NULL; g_flash_info[flash].flash_type = UAPI_FLASH_TYPE_MAX; } static td_s32 flash_close_spi_nand(td_handle flash) { td_u32 i; if (g_flash_info[flash].fd >= (td_void *)(intptr_t)SPAN_PART_HANDLE) { g_flash_info[flash].fd = (td_s64 *)(td_s64)INVALID_FD; g_flash_info[flash].open_addr = 0; g_flash_info[flash].open_len = 0; g_flash_info[flash].part_info = NULL; } else { if (close((int)(intptr_t)g_flash_info[flash].fd) != 0) { soc_log_err("Close %s flash partition failure!\n", g_flash_info[flash].part_info->dev_name); return TD_FAILURE; } g_flash_info[flash].fd = (td_void *)(td_s64)INVALID_FD; g_flash_info[flash].open_addr = 0; g_flash_info[flash].open_len = 0; g_flash_info[flash].part_info = NULL; } for (i = 0; i < MAX_HANDLE; i++) { if (g_flash_info[i].fd != (td_void *)INVALID_FD) { break; } } if (i == MAX_HANDLE) { (td_void)spi_raw_destroy(); (td_void)nand_raw_destroy(); g_init_flag = TD_FALSE; } return TD_SUCCESS; } td_s32 uapi_flash_close(td_handle handle) { if (check_flash_init(handle) == TD_FAILURE) { return TD_FAILURE; } (td_void)pthread_mutex_lock(&g_flash_mutex); if (g_flash_info[handle].flash_type == UAPI_FLASH_TYPE_EMMC_0) { flash_close_emmc(handle); (td_void)pthread_mutex_unlock(&g_flash_mutex); return TD_SUCCESS; } if (g_flash_info[handle].flash_type == UAPI_FLASH_TYPE_UFS_0) { flash_close_ufs(handle); (td_void)pthread_mutex_unlock(&g_flash_mutex); return TD_SUCCESS; } /* spi nand close */ if (flash_close_spi_nand(handle) != TD_SUCCESS) { (td_void)pthread_mutex_unlock(&g_flash_mutex); return TD_FAILURE; } (td_void)pthread_mutex_unlock(&g_flash_mutex); soc_log_info("end.\n"); return TD_SUCCESS; } static td_s32 compensate_nand_address(td_u64 address, td_u64 *start_addr) { td_s32 idx; flash_spinand_info_s raw_info; flash_get_spinand_info(UAPI_FLASH_TYPE_NAND_0, &raw_info); if (raw_info.block_size == 0) { soc_log_err("block_size shouldn't equal 0!\n"); return TD_FAILURE; } idx = (td_s32)(address >> raw_info.block_shift); if (nand_raw_get_physical_index(*start_addr, &idx, (td_s32)raw_info.block_size) != 0) { soc_log_err("logical addr change to physical addr error!\n"); return TD_FAILURE; } *start_addr += (td_u64)(((td_u32)idx) << raw_info.block_shift); *start_addr += (address % raw_info.block_size); return TD_SUCCESS; } static td_void flash_get_addr_len_value(td_handle flash, td_u64 *start_addr, td_u64 *limit_len) { if (g_flash_info[flash].fd >= (td_void *)(intptr_t)SPAN_PART_HANDLE) { *start_addr = g_flash_info[flash].open_addr; *limit_len = g_flash_info[flash].open_len; } else { *start_addr = g_flash_info[flash].part_info->start_addr; *limit_len = g_flash_info[flash].part_info->part_size; } } td_s64 uapi_flash_erase(td_handle handle, td_u64 offset, td_u64 len) { td_u64 start_addr = 0; td_u64 limit_len = 0; td_s64 ret; if (check_flash_init(handle) == TD_FAILURE) { return TD_FAILURE; } if (g_flash_info[handle].flash_type == UAPI_FLASH_TYPE_EMMC_0) { return (td_s64)len; } if (g_flash_info[handle].flash_type == UAPI_FLASH_TYPE_UFS_0) { return (td_s64)len; } (td_void)pthread_mutex_lock(&g_flash_mutex); flash_get_addr_len_value(handle, &start_addr, &limit_len); if (check_addr_len(offset, len, limit_len) == TD_FAILURE) { (td_void)pthread_mutex_unlock(&g_flash_mutex); return TD_FAILURE; } if (g_flash_info[handle].flash_type == UAPI_FLASH_TYPE_NAND_0) { ret = (td_s64)compensate_nand_address(offset, &start_addr); if (ret != TD_SUCCESS) { (td_void)pthread_mutex_unlock(&g_flash_mutex); return TD_FAILURE; } } else { start_addr += offset; } soc_log_info("HANDLE=%d, addr=0x%llx, len=0x%llx\n", handle, start_addr, len); if (g_flash_info[handle].flash_opt->raw_erase == NULL) { soc_log_err("flash service function ptr(raw_erase) is NULL! \n"); (td_void)pthread_mutex_unlock(&g_flash_mutex); return TD_FAILURE; } ret = (td_s64)g_flash_info[handle].flash_opt->raw_erase((td_s32)(intptr_t)g_flash_info[handle].fd, (unsigned long long)start_addr, len, g_flash_info[handle].open_addr, limit_len); soc_log_info("end.\n"); (td_void)pthread_mutex_unlock(&g_flash_mutex); return ret; } static td_s32 flash_get_rw_info(td_handle flash, td_u64 address, td_u32 len, td_u32 flags, flash_rw_info_s *info) { td_u64 len_without_oob; flash_get_addr_len_value(flash, &info->start_addr, &info->limit_len); if (check_addr_len(address, (td_u64)len, info->limit_len) == TD_FAILURE) { return TD_FAILURE; } if ((flags & UAPI_FLASH_RW_FLAG_WITH_OOB) == UAPI_FLASH_RW_FLAG_WITH_OOB) { len_without_oob = (len / (g_flash_info[flash].oob_size + g_flash_info[flash].page_size)); if ((len_without_oob * g_flash_info[flash].page_size) > MAX_U64) { return TD_FAILURE; } len_without_oob *= g_flash_info[flash].page_size; if ((len % (g_flash_info[flash].oob_size + g_flash_info[flash].page_size)) != 0) { len_without_oob += g_flash_info[flash].page_size; } if (check_addr_len(address, len_without_oob, info->limit_len) == TD_FAILURE) { return TD_FAILURE; } info->with_oob = 1; } else { if (check_addr_len(address, (td_u64)len, info->limit_len) == TD_FAILURE) { return TD_FAILURE; } info->with_oob = 0; } if (g_flash_info[flash].flash_type == UAPI_FLASH_TYPE_NAND_0) { if (compensate_nand_address(address, &info->start_addr) != TD_SUCCESS) { return TD_FAILURE; } } else { info->start_addr += address; } info->fd = (td_s32)(uintptr_t)g_flash_info[flash].fd; info->open_addr = g_flash_info[flash].open_addr; soc_log_info("HANDLE=%d, addr=0x%llx, len=0x%x, Flag=%d\n", flash, info->start_addr, len, flags); return TD_SUCCESS; } static td_s32 flash_read_emmc(td_handle flash, td_u64 address, td_u8 *buf, td_u32 len) { emmc_cb_s *emmc_cb = (emmc_cb_s *)(g_flash_info[flash].fd); return emmc_raw_read(emmc_cb, address, len, buf); } static td_s32 flash_read_ufs(td_handle flash, td_u64 address, td_u8 *buf, td_u32 len) { ufs_cb_s *ufs_cb = (ufs_cb_s *)(g_flash_info[flash].fd); return ufs_raw_read(ufs_cb, address, len, buf); } static td_s64 flash_read_spi_nand(td_handle flash, td_u64 address, td_u8 *buf, td_u32 len, td_u32 flags) { flash_rw_info_s info; if (flash_get_rw_info(flash, address, len, flags, &info) != TD_SUCCESS) { return TD_FAILURE; } if (g_flash_info[flash].flash_opt->raw_read == NULL) { soc_log_err("flash service function ptr(raw_read) is NULL! \n"); return TD_FAILURE; } return g_flash_info[flash].flash_opt->raw_read(&info, buf, len, 1); } td_s64 uapi_flash_read(td_handle handle, td_u64 offset, td_u8 *buf, td_u64 len, td_u32 flags) { td_s64 total_read; if (buf == NULL) { return TD_FAILURE; } if (check_flash_init(handle) == TD_FAILURE) { return TD_FAILURE; } (td_void)pthread_mutex_lock(&g_flash_mutex); if (g_flash_info[handle].flash_type == UAPI_FLASH_TYPE_EMMC_0) { total_read = flash_read_emmc(handle, offset, buf, (td_u32)len); } else if (g_flash_info[handle].flash_type == UAPI_FLASH_TYPE_UFS_0) { total_read = flash_read_ufs(handle, offset, buf, (td_u32)len); } else { total_read = flash_read_spi_nand(handle, offset, buf, (td_u32)len, flags); } (td_void)pthread_mutex_unlock(&g_flash_mutex); soc_log_info("totalread =0x%llx, end.\n", total_read); return total_read; } static td_s32 flash_write_emmc(td_handle flash, td_u64 address, const td_u8 *buf, td_u32 len) { emmc_cb_s *emmc_cb = (emmc_cb_s *)(g_flash_info[flash].fd); return emmc_raw_write(emmc_cb, address, len, buf); } static td_s32 flash_write_ufs(td_handle flash, td_u64 address, const td_u8 *buf, td_u32 len) { ufs_cb_s *ufs_cb = (ufs_cb_s *)(g_flash_info[flash].fd); return ufs_raw_write(ufs_cb, address, len, buf); } static td_s32 flash_write_erase_first(td_handle flash, flash_rw_info_s *rw_info, flash_spinand_info_s *raw_info, td_u32 len, td_u32 flags) { td_s32 ret; td_u64 block_size_new; td_u64 erase_len; if ((flags & UAPI_FLASH_RW_FLAG_WITH_OOB) == UAPI_FLASH_RW_FLAG_WITH_OOB) { block_size_new = raw_info->block_size + raw_info->oob_size * (raw_info->block_size / raw_info->page_size); } else { block_size_new = raw_info->block_size; } if ((flags & UAPI_FLASH_RW_FLAG_ERASE_FIRST) == UAPI_FLASH_RW_FLAG_ERASE_FIRST) { /* avoid pclint div 0 warning */ if (block_size_new == 0) { return TD_FAILURE; } erase_len = len / block_size_new; if ((len % block_size_new) != 0) { erase_len += 1; } if ((erase_len * raw_info->block_size) > MAX_U64) { return TD_FAILURE; } erase_len = erase_len * raw_info->block_size; if (g_flash_info[flash].flash_opt->raw_erase == 0) { soc_log_err("flash service function ptr(raw_erase) is NULL! \n"); return TD_FAILURE; } ret = (td_s32)g_flash_info[flash].flash_opt->raw_erase((td_s32)(uintptr_t)g_flash_info[flash].fd, rw_info->start_addr, erase_len, g_flash_info[flash].open_addr, rw_info->limit_len); if ((ret <= 0) && (ret != UAPI_FLASH_END_DUETO_BADBLOCK)) { soc_log_err("earse fail!\n"); return TD_FAILURE; } } return TD_SUCCESS; } static td_s32 flash_write_spi_nand(td_handle flash, td_u64 address, td_u8 *buf, td_u32 len, td_u32 flags) { flash_rw_info_s rw_info; flash_spinand_info_s raw_info; if (flash_get_rw_info(flash, address, len, flags, &rw_info) != TD_SUCCESS) { return TD_FAILURE; } flash_get_spinand_info(g_flash_info[flash].flash_type, &raw_info); /* avoid pclint div 0 warning */ if (raw_info.page_size == 0) { return TD_FAILURE; } if (flash_write_erase_first(flash, &rw_info, &raw_info, len, flags) != TD_SUCCESS) { return TD_FAILURE; } if (g_flash_info[flash].flash_opt->raw_write == 0) { soc_log_err("flash service function ptr(raw_write) is NULL! \n"); return TD_FAILURE; } return g_flash_info[flash].flash_opt->raw_write(&rw_info, buf, len); } td_s64 uapi_flash_write(td_handle handle, td_u64 offset, td_u8 *buf, td_u64 len, td_u32 flags) { td_s32 total_write; if (buf == NULL) { return TD_FAILURE; } if (check_flash_init(handle) == TD_FAILURE) { return TD_FAILURE; } (td_void)pthread_mutex_lock(&g_flash_mutex); if (g_flash_info[handle].flash_type == UAPI_FLASH_TYPE_EMMC_0) { total_write = flash_write_emmc(handle, offset, buf, (td_u32)len); } else if (g_flash_info[handle].flash_type == UAPI_FLASH_TYPE_UFS_0) { total_write = flash_write_ufs(handle, offset, buf, (td_u32)len); } else { total_write = flash_write_spi_nand(handle, offset, buf, (td_u32)len, flags); } (td_void)pthread_mutex_unlock(&g_flash_mutex); soc_log_info("totalwrite =0x%x, end.\n", total_write); return total_write; } static td_void flash_get_spi_nand_info(td_handle flash, uapi_flash_info *flash_info) { flash_spinand_info_s raw_info; flash_get_spinand_info(g_flash_info[flash].flash_type, &raw_info); flash_info->total_size = raw_info.total_size; if (g_flash_info[flash].fd >= (td_void *)SPAN_PART_HANDLE) { flash_info->part_info = NULL; } else { flash_info->part_info = g_flash_info[flash].part_info; } flash_info->block_size = raw_info.block_size; flash_info->page_size = raw_info.page_size; flash_info->oob_size = raw_info.oob_size; flash_info->flash_type = g_flash_info[flash].flash_type; flash_info->open_addr = g_flash_info[flash].open_addr; flash_info->open_len = g_flash_info[flash].open_len; } td_s32 uapi_flash_getinfo(td_handle handle, uapi_flash_info *flash_info) { if (flash_info == NULL) { return TD_FAILURE; } if (check_flash_init(handle) == TD_FAILURE) { return TD_FAILURE; } if (g_flash_info[handle].flash_type == UAPI_FLASH_TYPE_EMMC_0) { emmc_raw_get_info(handle, flash_info, (emmc_cb_s *)(g_flash_info[handle].fd)); } else if (g_flash_info[handle].flash_type == UAPI_FLASH_TYPE_UFS_0) { ufs_raw_get_info(handle, flash_info, (ufs_cb_s *)(g_flash_info[handle].fd)); } else { flash_get_spi_nand_info(handle, flash_info); } return TD_SUCCESS; }