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.

1418 lines
43 KiB

/*
* 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 <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#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;
}