在Linux中操作MTD分区下的NAND闪存:烧录与读取指南
最编程
2024-07-24 12:39:50
...
使用mtd-utils工具实现对flash的升级分区的烧写yaffs2
yaffs2的格式是根据所使用的nandflash来制作的,不同的nandflash,得到的yaffs2是不一样的,具体可以参考自己所用的nandflash,以及生成yaffs2文件系统的工具mkyaffs2image。
yaffs2包含了oob数据,所以写flash的时候要分开,本文所使用的是256M oob是64bit,page是2048byte-2kByte,block=64page制作的yaffs2的大小是(2048+64)的倍数!
每次写入是按页(page)的大小写入,而擦除是按照块来的,坏块也是按照块来的,如果当前块是坏的就必须跳过该块!
下载mtd-utils源码!
yaffs2的写入函数
int mtd_write_yaffs2_skip_bad(libmtd_t desc,const struct mtd_dev_info *mtd, int fd, int eb, int offs,
const char *img_name)函数用来烧写flash,如下:
{
int tmp, ret, in_fd, len, written = 0;
int write_eb_num,i;
int data_length,left_to_write,writesize;
off_t seek;
struct stat st;
char *buf,*temp,*dataAddr,*oobAddr;
if (offs < 0 || offs >= mtd->eb_size) {
errmsg("bad offset %d, mtd%d eraseblock size is %d",
offs, mtd->mtd_num, mtd->eb_size);
errno = EINVAL;
return -1;
}
if (offs % mtd->subpage_size) {
//start address must align to page (2048)0x800
errmsg("write offset %d is not aligned to mtd%d min. I/O size %d",
offs, mtd->mtd_num, mtd->subpage_size);
errno = EINVAL;
return -1;
}
in_fd = open(img_name, O_RDONLY | O_CLOEXEC);
if (in_fd == -1)
return sys_errmsg("cannot open \"%s\"", img_name);
if (fstat(in_fd, &st)){
sys_errmsg("cannot stat %s", img_name);
goto out_close;
}
len = st.st_size;
if (len % (mtd->subpage_size + mtd->oob_size)){
errmsg("size of \"%s\" is %d byte, which is not aligned to "
"mtd%d min. I/O size %d, it is not a yaffs2 file ", img_name, len, mtd->mtd_num,
mtd->subpage_size + mtd->oob_size);
errno = EINVAL;
goto out_close;
}
data_length = len / (mtd->subpage_size + mtd->oob_size) * mtd->subpage_size;
tmp = (offs + data_length + mtd->eb_size - 1) / mtd->eb_size;
if (eb + tmp > mtd->eb_cnt) {
errmsg("\"%s\" image size(except oob size) is %d bytes, mtd%d size is %d "
"eraseblocks, the image does not fit if we write it "
"starting from eraseblock %d, offset %d",
img_name, data_length, mtd->mtd_num, mtd->eb_cnt, eb, offs);
errno = EINVAL;
goto out_close;
}
/* Seek to the beginning of the eraseblock */
seek = (off_t)eb * mtd->eb_size + offs;
if (lseek(fd, seek, SEEK_SET) != seek) {
sys_errmsg("cannot seek mtd%d to offset %llu",
mtd->mtd_num, (unsigned long long)seek);
goto out_close;
}
writesize = (mtd->eb_size / mtd->subpage_size) *( mtd->subpage_size + mtd->oob_size);
printf("write size with oob size is: %d \n",writesize);
buf = xmalloc(writesize);
left_to_write = len;
write_eb_num = eb;
///writeoffs = eb*mtd->eb_size + offs;
while (left_to_write > 0) {
int rd = 0;
ret = mtd_is_bad(mtd,fd,write_eb_num);//判断当前块是否是坏块!
if(ret >0){ //是坏块!
write_eb_num = write_eb_num + 1;
if(write_eb_num >= mtd->eb_cnt)
{
if(left_to_write < 1){
goto out_free;
}
}
else {
printf("skip bad blocks at offset: %d \n",write_eb_num);
continue;
}
}else if(ret <0){
printf("get bad blocks error: %d\n",errno);
}
if(left_to_write < (mtd->eb_size +mtd->oob_size)){
writesize = left_to_write;
}
else{
writesize = (mtd->eb_size / mtd->subpage_size) *( mtd->subpage_size + mtd->oob_size);
}
ret = read(in_fd, buf, writesize);
if(ret == -1) {
sys_errmsg("cannot read \"%s\"", img_name);
goto out_free;
}
temp = buf;
dataAddr = temp;
oobAddr = temp + mtd->subpage_size;
for(i=0;i< mtd->eb_size/mtd->subpage_size;i++){ //完成一个块的写入!
ret = mtd_write(desc,mtd,fd,write_eb_num,i*mtd->subpage_size,dataAddr,mtd->subpage_size,oobAddr,mtd->oob_size,MTD_OPS_RAW);
if(ret < 0){
printf("write data witd oob error : %d \n",errno);
}
temp = oobAddr + mtd->oob_size;
dataAddr = temp;
oobAddr = temp + mtd->subpage_size;
}
write_eb_num = write_eb_num +1;
left_to_write -= writesize;
printf("left_to_write :%d write_eb_num: %d,writesize:%d\n",left_to_write,write_eb_num,writesize);
}
free(buf);
close(in_fd);
return 0;
out_free:
free(buf);
out_close:
close(in_fd);
return -1;
}
yaffs2的读取函数
int mtd_read_yaffs2_skip_bad(libmtd_t desc,const struct mtd_dev_info *mtd, int fd, int eb, int offs,
const char *img_name)
{
int tmp, ret, out_fd, len, written = 0;
int read_eb_num,i,sekOffs;
int data_length,left_to_read,readsize;
off_t seek;
struct stat st;
char *buf,*dataAddr,*oobAddr;
if (offs < 0 || offs >= mtd->eb_size) {
errmsg("bad offset %d, mtd%d eraseblock size is %d",
offs, mtd->mtd_num, mtd->eb_size);
errno = EINVAL;
return -1;
}
if (offs % mtd->subpage_size) {
//start address must align to page (2048)0x800
errmsg("write offset %d is not aligned to mtd%d min. I/O size %d",
offs, mtd->mtd_num, mtd->subpage_size);
errno = EINVAL;
return -1;
}
len = 0x193b3c0;//for test's length, you can read nand flash frome straddr to endaddr!!!
//also can read all mtdx device!
if (len % (mtd->subpage_size + mtd->oob_size)){
errmsg("size of \"%s\" is %d byte, which is not aligned to "
"mtd%d min. I/O size %d, it is not a yaffs2 file ", img_name, len, mtd->mtd_num,
mtd->subpage_size + mtd->oob_size);
errno = EINVAL;
goto out_close;
}
data_length = len / (mtd->subpage_size + mtd->oob_size) * mtd->subpage_size;
tmp = (offs + data_length + mtd->eb_size - 1) / mtd->eb_size;
if (eb + tmp > mtd->eb_cnt) {
errmsg("\"%s\" image size(except oob size) is %d bytes, mtd%d size is %d "
"eraseblocks, the image does not fit if we write it "
"starting from eraseblock %d, offset %d",
img_name, data_length, mtd->mtd_num, mtd->eb_cnt, eb, offs);
errno = EINVAL;
goto out_close;
}
/* Seek to the beginning of the eraseblock */
seek = (off_t)eb * mtd->eb_size + offs;
if (lseek(fd, seek, SEEK_SET) != seek) {
sys_errmsg("cannot seek mtd%d to offset %llu",
mtd->mtd_num, (unsigned long long)seek);
goto out_close;
}
readsize = mtd->subpage_size + mtd->oob_size;
printf("read size with oob size is: %d \n",readsize);
buf = xmalloc(readsize);
dataAddr = buf;
oobAddr = buf + mtd->subpage_size;
left_to_read = len;
read_eb_num = eb;
out_fd = open(img_name,O_WRONLY|O_CREAT|O_TRUNC|O_APPEND, S_IRUSR | S_IWUSR);
if(out_fd < 0){
printf("open write file error: %d \n",errno);
return -1;
}
while (left_to_read > 0) {
int rd = 0;
ret = mtd_is_bad(mtd,fd,read_eb_num);
if(ret >0){
read_eb_num = read_eb_num + 1;
if(read_eb_num >= mtd->eb_cnt)
{
if(left_to_read < 1){
goto out_free;
}
}
else {
printf("skip bad blocks at offset: %d \n",read_eb_num);
continue;
}
}else if(ret <0){
printf("get bad blocks error: %d\n",errno);
}
if(left_to_read < (mtd->eb_size +mtd->oob_size)){
readsize = left_to_read;
}
else{
readsize = (mtd->eb_size / mtd->subpage_size) *( mtd->subpage_size + mtd->oob_size);
}
for(i=0;i< mtd->eb_size/mtd->subpage_size;i++){
ret = mtd_read(mtd,fd,read_eb_num,i*mtd->subpage_size,dataAddr,mtd->subpage_size);
if(ret < 0){
printf("read data error: %d \n",errno);
goto out_free;
}
sekOffs = read_eb_num * mtd->eb_size + i*mtd->subpage_size;
seek = (off_t)sekOffs;
//printf("seek %#llx sek offs %#x \n",seek,sekOffs);
ret = mtd_read_oob(desc,mtd,fd,seek,mtd->oob_size,oobAddr);
if(ret < 0){
printf("read oob error: %d \n",errno);
goto out_free;
}
ret = write(out_fd, dataAddr, mtd->subpage_size);
if(ret == -1) {
sys_errmsg("cannot write data \"%s\"", img_name);
goto out_free;
}
ret = write(out_fd, oobAddr, mtd->oob_size);
if(ret == -1) {
sys_errmsg("cannot write oob \"%s\"", img_name);
goto out_free;
}
}
read_eb_num = read_eb_num +1;
left_to_read -= readsize;
printf("left_to_read :%-7d write_eb_num: %-3d,readsize:%-6d\n",left_to_read,read_eb_num,readsize);
}
free(buf);
close(out_fd);
return 0;
out_free:
free(buf);
out_close:
close(out_fd);
return -1;
}
测试烧写;
具体如下:
int mtdDevFd1 =0,ret =0,i;
libmtd_t mtd_desc;
char* image_file = FILE_NAME;
struct mtd_dev_info mtd;
//open mtd device, see it at /dev/mtdX
//get mtd message : cat /proc/mtd
if ((mtdDevFd1 = open(mtdDev1, O_RDWR)) < 0){
_SEND_DBUG_MSG("open %s error \n",mtdDev2);
ret =-1;
goto out_close;
}
mtd_desc = libmtd_open();
if(mtd_desc == NULL){
_SEND_DBUG_MSG("can not initlize mtd lib \n");
ret =-1;
goto out_close;
}
if (mtd_get_dev_info(mtd_desc,mtdDev1,&mtd) < 0){
ret =-1;
goto out_close;;
_SEND_DBUG_MSG("get dev info error!\n");
}
printf("size:%#x \n",mtd.size);
printf("eb_size:%#x \n",mtd.eb_size);
printf("name:%s \n",mtd.name);
printf("subpage size: %#x \n",mtd.subpage_size);
printf("oob size: %#x \n",mtd.oob_size);
printf("eb_cnt: %#x \n",mtd.eb_cnt);
for(i = 0; i< mtd.eb_cnt;i++){
if(mtd_is_bad(&mtd, mtdDevFd1, i))
{
printf("erase skip bad block num: %d \n",i);
continue;
}
if(0 != mtd_erase(mtd_desc,&mtd,mtdDevFd1,i)){//擦除!
_SEND_DBUG_MSG("ersae error \n");
ret =-1;
goto out_close;
}
}
if(0 != mtd_write_yaffs2_skip_bad(mtd_desc,&mtd,mtdDevFd1,0,0,image_file)){
_SEND_DBUG_MSG("write yaffs2 error \n");
ret = -1;
}
out_close:
libmtd_close(mtd_desc);
close(mtdDevFd1);
//write end!!!
上一篇: 玩转Linux内核:如何利用堆溢出技巧
下一篇: 剖析与实现Linux热管理框架深度解析
推荐阅读
-
在Linux中操作MTD分区下的NAND闪存:烧录与读取指南
-
紧急模式问题处理 - 图 1 紧急模式 根本原因分析 应急模式提供了尽可能小的环境,即使无法进入应急模式,也可以在其中修复系统。在应急模式下,系统只安装根文件系统供读取,不尝试安装任何其他本地文件系统,不激活网络接口,只启动一些基本服务。 进入应急模式的原因通常是 /etc/fstab 文件中存在错误,导致文件系统挂载失败。 文件系统中存在错误,导致。 约束和限制 本节适用于 Linux 操作系统紧急模式。程序涉及修复文件系统。修复文件系统有丢失数据的风险,因此请先备份数据,然后再执行修复操作。 处理方法 输入根密码,然后进入修复模式。 在应急模式下,根分区以只读模式挂载。要修改根目录中的文件,需要执行以下命令以读写模式重新挂载根分区。# mount -o rw,remount / 请执行以下命令首先检查 fstab 文件是否有误,然后尝试挂载所有未挂载的文件系统。# mount -a 如果挂载点不存在,请创建一个挂载点。 如果不存在此类设备,请注释或删除挂载行。 如果指定了不正确的挂载选项,请将挂载参数更改为正确的参数。 如果没有发生错误,但出现 UNEXPECTED INCONSISTENCY;RUN fsck MANUALLY 消息(通常是由文件系统错误引起的),请跳至第 7 步。 执行以下命令打开 /etc/fstab 以修改相应的错误。# vi /etc/fstab /etc/fstab 文件包含以下字段,以空格分隔:[文件系统] [dir] [type] [options] [dump] [fsck] 表 1 /etc/fstab 参数 说明 参数 说明 [文件系统] 要挂载的分区或存储设备。 文件系统]列建议以 UUID 的形式写入。执行 blkid 命令可查询设备文件系统 UUID。 参考格式如下: # <device> <dir> <type> <options> <dump> <fsck>; UUID=b411dc99-f0a0-4c87-9e05-184977be8539 /home ext4 defaults 0 2 使用 UUID 的好处是,它们与磁盘顺序无关。如果你在 BIOS 中更改了存储设备的顺序,或重新插入了存储设备,或者因为某些 BIOS 可能会随机更改存储设备的顺序,那么使用 UUID 会更有效率。 [文件系统] 文件系统]的挂载位置。 类型 挂载设备或分区的文件系统类型,支持多种不同的文件系统:ext2、ext3、ext4、reiserfs、xfs、jfs、smbfs、iso9660、vfat、ntfs、swap 和 auto。 设置为自动类型后,挂载命令会猜测所使用的文件系统类型,这对 CDROM 和 DVD 等移动设备非常有用。 选项 挂载时要使用的参数,有些参数是特定文件系统特有的。例如,默认值参数使用文件系统的默认挂载参数,ext4 的默认参数为:rw、suid、dev、exec、auto、nouser、async。 有关更多参数,请执行以下命令查看 man 手册:# man mount