blob: 57543685706eb7610c6805836b353a91656a3613 [file] [log] [blame]
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <mtd/mtd-abi.h>
#include <errno.h>
#include <sys/ioctl.h>
#include "mtd_api.h"
#define MTD_PATH_LENGTH_MAX (256)
static ssize_t readn(int fd, void *vptr, size_t n);
static ssize_t writen(int fd, const void *vptr, size_t n);
/*******************************************************************************
* 内部函数/mtd_private.h *
*******************************************************************************/
/**
* @brief 从文件描述符读取数据
* @param fd 入参,文件描述符
* @param vptr 出参,数据缓冲区
* @param n 入参,内容字节数
* @return 成功返回成功读取的字节数,失败返回-1
* @retval
* @note 封装read接口,读取被打断时会重试直到全部读取成功
* @warning
*/
static ssize_t readn(int fd, void *vptr, size_t n)
{
size_t nleft;
ssize_t nread;
char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0)
{
if ((nread = read(fd, ptr, nleft)) < 0)
{
if (errno == EINTR)
{
nread = 0; /* and call read() again */
}
else
{
return (-1);
}
}
else if (nread == 0)
{
break; /* EOF */
}
nleft -= nread;
ptr += nread;
}
return (n - nleft); /* return >= 0 */
}
/**
* @brief 向文件描述符写入数据
* @param fd 入参,文件描述符
* @param vptr 入参,数据缓冲区
* @param n 入参,内容字节数
* @return 成功返回成功写入的字节数,失败返回-1
* @retval
* @note 封装write接口,写入被打断时会重试直到全部写入成功
* @warning
*/
static ssize_t writen(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0)
{
nwritten = write(fd, ptr, nleft);
if (nwritten < 0)
{
if (errno == EINTR)
{
nwritten = 0; /* call write() again */
}
else
{
return (-1); /* error */
}
}
else if (nwritten == 0)
{
break;
}
nleft -= nwritten;
ptr += nwritten;
}
return (n - nleft); /* return >= 0 */
}
/*******************************************************************************
* 外部函数定义 *
*******************************************************************************/
int mtd_find(const char *i_parti_name, device_type_t device_type, char *o_mtd_path, unsigned int o_mtd_path_len)
{
FILE *fp_mtd = 0;
char buf[128];
char *line_str;
if (!o_mtd_path_len)
{
return -1;
}
fp_mtd = fopen("/proc/mtd", "r+");
if (NULL == fp_mtd)
{
printf("[libmtd]: mtd_find, open file error:%s", strerror(errno));
return -1;
}
// printf("[libmtd]: partition name:%s\n", i_parti_name);
while (1)
{
int matches = 0;
char mtdname[64] = {0};
int mtdnum = 0;
unsigned int mtdsize, mtderasesize;
memset(buf, 0, sizeof(buf));
line_str = fgets(buf, sizeof(buf) - 1, fp_mtd);
if (NULL == line_str)
{
printf("[libmtd]: mtd_find, get info from mtd error:%s\n", strerror(errno));
fclose(fp_mtd);
return -1;
}
// mtd5: 00100000 00020000 "fotaflag"
matches = sscanf(buf, "mtd%d: %x %x \"%63[^\"]",
&mtdnum, &mtdsize, &mtderasesize, mtdname);
mtdname[63] = '\0';
if ((matches == 4) && (strcmp(mtdname, i_parti_name) == 0))
{
memset(o_mtd_path, 0, o_mtd_path_len);
if (device_type == DEVICE_MTD_BLOCK)
{
snprintf(o_mtd_path, o_mtd_path_len, "/dev/mtdblock%d", mtdnum);
}
else if (device_type == DEVICE_MTD)
{
snprintf(o_mtd_path, o_mtd_path_len, "/dev/mtd%d", mtdnum);
}
else if (device_type == DEVICE_ZFTL)
{
snprintf(o_mtd_path, o_mtd_path_len, "/dev/zftl%d", mtdnum);
}
else
{
printf("[libmtd]: mtd_find, unknown device type %d\n", device_type);
fclose(fp_mtd);
return -1;
}
// printf("[libmtd]: o_mtd_path=[%s]\n", o_mtd_path);
break;
}
}
fclose(fp_mtd);
return 0;
}
int mtd_get_info(int fd, struct mtd_info_user *info)
{
if (0 != ioctl(fd, MEMGETINFO, info))
{
printf("[libmtd]: mtd_get_info, get fd(%d) info error, %s\n", fd, strerror(errno));
return -1;
}
return 0;
}
int mtd_block_isbad(int fd, off_t offset)
{
int ret = 0;
ret = ioctl(fd, MEMGETBADBLOCK, &offset);
if (ret > 0)
{
printf("[libmtd]: mtd_block_isbad, bad block at 0x%lx, ret = %d\n", offset, ret);
ret = 1;
}
else if (ret < 0)
{
printf("[libmtd]: mtd_block_isbad, ioctl(MEMGETBADBLOCK) error at 0x%lx, %s, ret = %d\n", offset, strerror(errno), ret);
ret = -1;
}
return ret;
}
int mtd_erase_partition(const char *partition_name)
{
int ret = 0;
char mtd_path[MTD_PATH_LENGTH_MAX] = {0};
int fd_mtd = -1;
struct mtd_info_user meminfo = {0};
struct erase_info_user64 erase_info = {0};
if (NULL == partition_name)
{
return -1;
}
ret = mtd_find(partition_name, DEVICE_MTD, mtd_path, MTD_PATH_LENGTH_MAX);
if (ret < 0)
{
printf("[libmtd]: mtd_erase_partition, mtd_find %s failed\n", partition_name);
ret = -1;
goto out;
}
fd_mtd = open(mtd_path, O_RDWR);
if (fd_mtd < 0)
{
printf("[libmtd]: mtd_erase_partition, open %s error, %s\n", partition_name, strerror(errno));
return -1;
}
ret = mtd_get_info(fd_mtd, &meminfo);
if (ret < 0)
{
printf("[libmtd]: mtd_erase_partition, get %s info error, %s\n", partition_name, strerror(errno));
ret = -1;
goto out;
}
erase_info.length = meminfo.erasesize;
for (erase_info.start = 0; erase_info.start < meminfo.size; erase_info.start += meminfo.erasesize)
{
ret = mtd_block_isbad(fd_mtd, erase_info.start);
if (1 == ret)
{
continue;
}
else if (-1 == ret)
{
printf("[libmtd]: mtd_erase_partition, mtd_block_isbad %s error\n", partition_name);
goto out;
}
if (0 != ioctl(fd_mtd, MEMERASE64, &erase_info))
{
printf("[libmtd]: mtd_erase_partition, erasing %s failure at 0x%llx\n", partition_name, erase_info.start);
if (ioctl(fd_mtd, MEMSETBADBLOCK, &(erase_info.start)) < 0)
{
printf("[libmtd]: mtd_erase_partition, mark %s bad block error, %s\n", partition_name, strerror(errno));
ret = -1;
goto out;
}
continue;
}
}
ret = 0;
out:
if (fd_mtd >= 0)
{
close(fd_mtd);
}
return ret;
}
int mtd_write_partition(const char *partition_name, const char *image_file)
{
int ret = 0;
ssize_t wr_len;
char mtd_path[MTD_PATH_LENGTH_MAX] = {0};
int fd_mtd = -1;
struct mtd_info_user meminfo = {0};
off_t data_size = 0;
off_t index = 0;
off_t fd_img_index = 0;
int fd_img = -1;
char *buf = NULL;
struct stat statbuff = {0};
if (NULL == partition_name || NULL == image_file)
{
return -1;
}
ret = mtd_find(partition_name, DEVICE_MTD, mtd_path, MTD_PATH_LENGTH_MAX);
if (ret < 0)
{
printf("[libmtd]: mtd_write_partition, mtd_find %s failed\n", partition_name);
ret = -1;
goto out;
}
fd_mtd = open(mtd_path, O_RDWR);
if (fd_mtd < 0)
{
printf("[libmtd]: mtd_write_partition, open %s error, %s\n", partition_name, strerror(errno));
return -1;
}
ret = mtd_get_info(fd_mtd, &meminfo);
if (ret < 0)
{
printf("[libmtd]: mtd_write_partition, get %s info error, %s\n", partition_name, strerror(errno));
ret = -1;
goto out;
}
if (stat(image_file, &statbuff) < 0)
{
printf("[libmtd]: mtd_write_partition, stat %s failed, %s\n", image_file, strerror(errno));
ret = -1;
goto out;
}
fd_img = open(image_file, O_RDONLY);
if (fd_img < 0)
{
printf("[libmtd]: mtd_write_partition, open %s failed, %s\n", image_file, strerror(errno));
ret = -1;
goto out;
}
buf = (char *)malloc(meminfo.erasesize);
if (!buf)
{
printf("[libmtd]: mtd_write_partition, malloc failed\n");
ret = -1;
goto out;
}
for (index = 0; index < meminfo.size && fd_img_index < statbuff.st_size; index += meminfo.erasesize)
{
ret = mtd_block_isbad(fd_mtd, index);
if (1 == ret)
{
continue;
}
else if (-1 == ret)
{
printf("[libmtd]: mtd_write_partition, mtd_block_isbad %s error,at 0x%llx\n", partition_name, index);
goto out;
}
if (lseek(fd_img, fd_img_index, SEEK_SET) < 0)
{
printf("[libmtd]: mtd_write_partition, lseek %s error = %s!\n", image_file, strerror(errno));
ret = -1;
goto out;
}
if (fd_img_index + meminfo.erasesize > statbuff.st_size)
{
data_size = statbuff.st_size - fd_img_index;
}
else
{
data_size = meminfo.erasesize;
}
wr_len = readn(fd_img, buf, (size_t)data_size);
if (wr_len < 0)
{
printf("[libmtd]: mtd_write_partition, read %s error, %s\n", image_file, strerror(errno));
ret = -1;
goto out;
}
fd_img_index += data_size;
if (lseek(fd_mtd, index, SEEK_SET) < 0)
{
printf("[libmtd]: mtd_write_partition, lseek %s error = %s!\n", partition_name, strerror(errno));
ret = -1;
goto out;
}
wr_len = writen(fd_mtd, buf, (size_t)data_size);
if (wr_len < 0)
{
printf("[libmtd]: mtd_write_partition, write %s error, %s\n", partition_name, strerror(errno));
ret = -1;
goto out;
}
memset(buf, 0xFF, meminfo.erasesize);
}
if (fd_img_index < statbuff.st_size)
{
printf("[libmtd]: mtd_write_partition, No space left, writelen=0x%lx, filesize=0x%lx\n", fd_img_index, statbuff.st_size);
ret = -1;
goto out;
}
ret = 0;
out:
if (fd_mtd >= 0)
{
close(fd_mtd);
}
if (buf != NULL)
{
// memset(buf, 0xFF, meminfo.erasesize);
free(buf);
}
if (fd_img >= 0)
{
close(fd_img);
}
return ret;
}
int mtd_erase_offset(int fd, off_t offset)
{
struct mtd_info_user meminfo = {0};
struct erase_info_user64 erase_info = {0};
if (fd < 0)
{
printf("[libmtd]: mtd_erase_offset, fd(%d) error\n", fd);
return -1;
}
if (0 != mtd_get_info(fd, &meminfo))
{
printf("[libmtd]: mtd_erase_offset, get fd(%d) info error, %s\n", fd, strerror(errno));
return -1;
}
if (0 != (offset % meminfo.erasesize))
{
printf("[libmtd]: mtd_erase_offset, not at the beginning of a block, erasesize is %d\n", meminfo.erasesize);
return -1;
}
erase_info.length = meminfo.erasesize;
erase_info.start = offset;
if (mtd_block_isbad(fd, erase_info.start))
{
return -1;
}
if (0 != ioctl(fd, MEMERASE64, &erase_info))
{
printf("[libmtd]: mtd_erase_offset, erasing failure at 0x%llx\n", erase_info.start);
if (ioctl(fd, MEMSETBADBLOCK, &(erase_info.start)) < 0)
{
printf("[libmtd]: mtd_erase_offset, mark bad block error, %s\n", strerror(errno));
}
return -1;
}
return 0;
}
ssize_t mtd_write_offset(int fd, off_t offset, const void *buf, size_t count)
{
ssize_t writen_len;
struct mtd_info_user meminfo = {0};
if (fd < 0)
{
return -1;
}
if (0 != mtd_get_info(fd, &meminfo))
{
printf("[libmtd]: mtd_write_offset, get fd(%d) info error, %s\n", fd, strerror(errno));
return -1;
}
if (count <= 0 || count > meminfo.erasesize)
{
printf("[libmtd]: mtd_write_offset, count(0x%lx), less than 0, or larger than erasesize(0x%x)\n", count, meminfo.erasesize);
return -1;
}
if (0 != (offset % meminfo.erasesize))
{
printf("[libmtd]: mtd_write_offset, not at the beginning of a block, erasesize is 0x%x\n", meminfo.erasesize);
return -1;
}
if (mtd_block_isbad(fd, offset))
{
return -1;
}
if (lseek(fd, offset, SEEK_SET) < 0)
{
printf("[libmtd]: mtd_write_offset, lseek error = %s!\n", strerror(errno));
return -1;
}
writen_len = writen(fd, buf, count);
if (writen_len != count)
{
if (-1 == writen_len)
{
printf("[libmtd]: mtd_write_offset, write error, %s\n", strerror(errno));
}
return -1;
}
return writen_len;
}
ssize_t mtd_read_offset(int fd, off_t offset, void *buf, size_t count)
{
ssize_t readn_len;
struct mtd_info_user meminfo = {0};
if (fd < 0)
{
return -1;
}
if (0 != mtd_get_info(fd, &meminfo))
{
printf("[libmtd]: mtd_read_offset, get fd(%d) info error, %s\n", fd, strerror(errno));
return -1;
}
if (count <= 0 || count > meminfo.erasesize)
{
printf("[libmtd]: mtd_read_offset, count(0x%lx), less than 0, or larger than erasesize(0x%x)\n", count, meminfo.erasesize);
return -1;
}
if (0 != (offset % meminfo.erasesize))
{
printf("[libmtd]: mtd_read_offset, not at the beginning of a block, erasesize is %x\n", meminfo.erasesize);
return -1;
}
if (mtd_block_isbad(fd, offset))
{
return -1;
}
if (lseek(fd, offset, SEEK_SET) < 0)
{
printf("[libmtd]: mtd_read_offset, lseek error = %s!\n", strerror(errno));
return -1;
}
readn_len = readn(fd, buf, count);
if (readn_len != count)
{
if (-1 == readn_len)
{
printf("[libmtd]: mtd_read_offset, read error, %s\n", strerror(errno));
}
return -1;
}
return readn_len;
}
static int zftl_verify_partition(const char *partition_name, const char *image_file)
{
int ret = 0;
ssize_t rd_len;
size_t read_size = 4096;
char zftl_path[MTD_PATH_LENGTH_MAX] = {0};
int fd_zftl = -1;
struct mtd_info_user meminfo = {0};
off_t data_size = 0;
off_t index = 0;
off_t fd_img_index = 0;
int fd_img = -1;
char *buf = NULL;
char *buf2 = NULL;
struct stat statbuff = {0};
if (NULL == partition_name || NULL == image_file)
{
return -1;
}
ret = mtd_find(partition_name, DEVICE_ZFTL, zftl_path, MTD_PATH_LENGTH_MAX);
if (ret < 0)
{
printf("[libmtd]: zftl_verify_partition, mtd_find %s failed\n", partition_name);
return -1;
}
fd_zftl = open(zftl_path, O_RDONLY);
if (fd_zftl < 0)
{
printf("[libmtd]: zftl_verify_partition, open %s error, %s\n", partition_name, strerror(errno));
return -1;
}
if (stat(image_file, &statbuff) < 0)
{
printf("[libmtd]: zftl_verify_partition, stat %s failed, %s\n", image_file, strerror(errno));
ret = -1;
goto out;
}
fd_img = open(image_file, O_RDONLY);
if (fd_img < 0)
{
printf("[libmtd]: zftl_verify_partition, open %s failed, %s\n", image_file, strerror(errno));
ret = -1;
goto out;
}
buf = (char *)malloc(read_size);
if (!buf)
{
printf("[libmtd]: zftl_verify_partition, malloc failed\n");
ret = -1;
goto out;
}
buf2 = (char *)malloc(read_size);
if (!buf2)
{
printf("[libmtd]: zftl_verify_partition, malloc2 failed\n");
ret = -1;
goto out;
}
for (index = 0; fd_img_index < statbuff.st_size; index += read_size)
{
//memset(buf, 0xFF, read_size);
if (fd_img_index + read_size > statbuff.st_size)
{
data_size = statbuff.st_size - fd_img_index;
}
else
{
data_size = read_size;
}
// data_size = (fd_img_index + read_size > statbuff.st_size) ? (statbuff.st_size - fd_img_index) : (read_size);
rd_len = readn(fd_img, buf, (size_t)data_size);
if (rd_len < 0)
{
printf("[libmtd]: zftl_verify_partition, read image file %s error, %s\n", image_file, strerror(errno));
ret = -1;
goto out;
}
fd_img_index += data_size;
rd_len = readn(fd_zftl, buf2, (size_t)data_size);
if (rd_len < 0)
{
printf("[libmtd]: zftl_verify_partition, read zftl %s error, %s\n", partition_name, strerror(errno));
ret = -1;
goto out;
}
if (memcmp(buf, buf2, (size_t)data_size) != 0)
{
printf("[libmtd]: zftl_verify_partition, data memcmp %s error\n", partition_name);
ret = -1;
goto out;
}
}
ret = 0;
out:
if (fd_zftl >= 0)
{
//fsync(fd_zftl);
close(fd_zftl);
}
if (buf != NULL)
{
// memset(buf, 0xFF, meminfo.erasesize);
free(buf);
}
if (buf2 != NULL)
{
// memset(buf, 0xFF, meminfo.erasesize);
free(buf2);
}
if (fd_img >= 0)
{
close(fd_img);
}
if (ret == 0)
{
printf("[libmtd]: zftl %s verify sucess\n", partition_name);
}
else
{
printf("[libmtd]: zftl %s verify fail\n", partition_name);
}
return ret;
}
int zftl_write_partition(const char *partition_name, const char *image_file)
{
int ret = 0;
ssize_t wr_len;
size_t write_size = 4096;
char zftl_path[MTD_PATH_LENGTH_MAX] = {0};
int fd_zftl = -1;
struct mtd_info_user meminfo = {0};
off_t data_size = 0;
off_t index = 0;
off_t fd_img_index = 0;
int fd_img = -1;
char *buf = NULL;
struct stat statbuff = {0};
if (NULL == partition_name || NULL == image_file)
{
return -1;
}
ret = mtd_find(partition_name, DEVICE_ZFTL, zftl_path, MTD_PATH_LENGTH_MAX);
if (ret < 0)
{
printf("[libmtd]: zftl_write_partition, mtd_find %s failed\n", partition_name);
return -1;
}
fd_zftl = open(zftl_path, O_RDWR);
if (fd_zftl < 0)
{
printf("[libmtd]: zftl_write_partition, open %s error, %s\n", partition_name, strerror(errno));
return -1;
}
if (stat(image_file, &statbuff) < 0)
{
printf("[libmtd]: zftl_write_partition, stat %s failed, %s\n", image_file, strerror(errno));
ret = -1;
goto out;
}
fd_img = open(image_file, O_RDONLY);
if (fd_img < 0)
{
printf("[libmtd]: zftl_write_partition, open %s failed, %s\n", image_file, strerror(errno));
ret = -1;
goto out;
}
buf = (char *)malloc(write_size);
if (!buf)
{
printf("[libmtd]: zftl_write_partition, malloc failed\n");
ret = -1;
goto out;
}
for (index = 0; fd_img_index < statbuff.st_size; index += write_size)
{
memset(buf, 0xFF, write_size);
if (fd_img_index + write_size > statbuff.st_size)
{
data_size = statbuff.st_size - fd_img_index;
}
else
{
data_size = write_size;
}
// data_size = (fd_img_index + write_size > statbuff.st_size) ? (statbuff.st_size - fd_img_index) : (write_size);
wr_len = readn(fd_img, buf, (size_t)data_size);
if (wr_len < 0)
{
printf("[libmtd]: zftl_write_partition, read %s error, %s\n", image_file, strerror(errno));
ret = -1;
goto out;
}
fd_img_index += data_size;
wr_len = writen(fd_zftl, buf, (size_t)data_size);
if (wr_len < 0)
{
printf("[libmtd]: zftl_write_partition, write %s error, %s\n", partition_name, strerror(errno));
ret = -1;
goto out;
}
}
if (fd_img_index < statbuff.st_size)
{
printf("[libmtd]: zftl_write_partition, No space left, writelen=0x%lx, filesize=0x%lx\n", fd_img_index, statbuff.st_size);
ret = -1;
goto out;
}
ret = 0;
out:
if (fd_zftl >= 0)
{
fsync(fd_zftl);
close(fd_zftl);
}
if (buf != NULL)
{
// memset(buf, 0xFF, meminfo.erasesize);
free(buf);
}
if (fd_img >= 0)
{
close(fd_img);
}
if (ret == 0)
{
//write sucess then do verify
ret = zftl_verify_partition(partition_name, image_file);
}
return ret;
}