通过mtd读写flash_【转】 Linux下读写FLASH驱动——MTD设备分析
最近在学习驱动读写flash的代码部分。经历了可笑的过程:开始我知道flash用通过spi口来读写。所以就到了driver/spi
下面看相关代码。发现有个spidev.c里面有read/write/ioctl等函数。而且还有一个davinci_spi_master.以为调用spi驱动的时候会首先调用到这里,于是就想怎么在上层应用里将spidev.c里open调用到就可以了。最后修改了一些地方就在应用的地方打开了这个字符设备驱动。在dev下面生成了dev/spidev0.0目录。于是打开它还调用到了spidev.c里的相关函数。甚是窃喜,但突然发现这个跟我要读写的flash又有什么关系呢?flash芯片型号是:m25p40。知道了又怎么样?我该如何读写它呢。后来突然在网上看了这么一段话MTD(memory
technology
device内存技术设备)是用于访问memory设备(ROM、flash)的Linux的子系统。MTD的主要目的是为了使新的memory设备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的接口。MTD的所有源代码在/drivers/mtd子目录下。我将CFI接口的MTD设备分为四层(从设备节点直到底层硬件驱动),这四层从上到下依次是:设备节点、MTD设备层、MTD原始设备层和硬件驱动层。(http://blog.csdn.net/binghuiliang/archive/2008/01/23/2060794.aspx)郁闷之余看到了这样的信息,发现原来flash不是要注册一个什么spi设备,而是要通过mtd设备来读取后来在dev/mtd/devices文件夹里看到了m25p80.c这个代码。打开发现里面是对这一类flash驱动的支持代码。天哪怎么回事,后来就又看了里面的函数有read/write/probe函数但是没有发现open函数,甚是烦恼。不知道open函数哪里去了。是不是probe代替了呢??同时也没有发现ops这样的文件操作结构体。问题出现了。
参看高手说在应用里要 system("flash_eraseall /dev/mtd4");spi_fd
=open("/dev/mtd4",O_RDWR, 0);
这么调用,而mtd4在哪里注册的我就不知道了。现在还在寻找中。read和write都是调用到了m25p80.c里函数。下面具体说一下如何添加m25p80.c驱动吧。
步骤如下:
1、make menuconfig里选择MTD/下相应的选项。内核已经配好了。
2、修改arch/arm/mach-davinci下面的davinci_spi_platform.c 在里面加入
static struct flash_platform_data davinci_m25P40_info =
{
.name = "m25p80",
.parts = NULL,
.nr_parts = 0,
.type = "m25p40",
};
static struct spi_board_info dm6467_spi_board_info[] = {
{
// SPI FLash
.modalias = "m25p80",
.platform_data =
&davinci_m25P40_info,
.mode = SPI_MODE_0,
.irq = 0,
.max_speed_hz = 4 * 1000 *
1000,
.bus_num = 0,
.chip_select = 0, // device
number on bus (0-based)
},
};
然后编译重新编译内核之后就可以发现在dev目录下多了一个dev/mtd4设备节点。这里面有很多奇怪的地方。不知道这个mtd4是怎么生成的。像那个spidev0.0设备节点是因为在文件spidev.c里有赋值给主设备和从设备的地方,而这个在m25p80.c里并没有发现任何迹象,只是看到在probe里加进了add_mtd_partitions()函数,还有这样的语句:
flash->mtd.erase = m25p80_erase;
flash->mtd.read = m25p80_read;
flash->mtd.write = m25p80_write;
然后继续查找probe最后到了那里:
static struct spi_driver m25p80_driver = {
.driver = {
.name = "m25p80",
.bus =
&spi_bus_type,
.owner = THIS_MODULE,
},
.probe = m25p_probe,
.remove = __devexit_p(m25p_remove),
};
貌似这里又用probe来注册了spi_driver结构体。而最后的init和exit函数都用了spi注册。晕倒!那怎么最后是open设备mtd4呢。这中间到底发生了什么?
这里只能说mtd设备利用了spi总线来达到注册自己设备的目的。而这个mtd设备在本质上是一个字符设备。
在板子登陆的内核信息里截获到以下信息:
call video_register_device() in file videodev.c
call video_register_device() in file videodev.c
call adv7343_initialize()
ad9889_i2c_init() OK!
i2c /dev entries driver
nand_davinci nand_davinci.0: Using soft ECC
info->emifregs = 0xc8008000,EMIF_A1CR =
0x3ffffffc
info->emifregs = 0xc8008000,EMIF_A1CR =
0x88442a8
mtd->writesize(pagesize)=2048
mtd->oobsize=64
mtd->erasesize(blocksize)=0x20000
NAND device: Manufacturer ID: 0xec, Chip ID: 0xf1 (Samsung NAND
128MiB 3,3V 8-bit)
Scanning device for bad blocks
chip_delay = 30
Creating 4 MTD partitions on "nand_davinci.0":
0x00000000-0x000e0000 : "bootloader"
0x000e0000-0x00100000 : "params"
0x00100000-0x004a0000 : "kernel"
0x004a0000-0x08000000 : "filesystem"
nand_davinci nand_davinci.0: hardware revision: 2.2
Enter into m25p_probe
m25p80 spi0.0: m25p40 (512 Kbytes)
dm_spi.0: davinci SPI Controller driver at 0xc8002800 (irq = 43)
use_dma=1
pcf8563 0-0051: chip found, driver version 0.4.2
上面的Enter into m25p_probe
是在m25p80.c里probe函数打印出来的。这么早就打印了而不是open时候才调用probe是在内核加载时就调用了。
可见这块板子是用的nandflash并把它分成四部分:bootloader、代码、内核、文件系统。而m25p80 spi0.0:
m25p40 (512 Kbytes)这一句更是
经典,是说生成了m25p80 spi0.0的一个设备m25p40吧,猜的呵呵。
现在发现mtd字符设备都在mtd/mtdchar.c里注册,于是就怀疑是不是open调用到了这里呢,于是打印验证后发现open("/dev/mtd4",O_RDWR,
0);调用到mtdchar.c程序里open函数,那为什么read和write却是调用到m25p80.c呢,这里的read有没有调用到呢?这就更奇怪了,是不是mtd自己有一套机制让打开字符设备的语句直接去打开mtdchar.c的open然后再具体针对某个设备来读写。这也不对呀,open返回的是mtd4的设备号啊。
哈哈,测试了一下原来同样会调用到mtdchar里read和write。m25p80.c里的read和mtdchar.c里的read函数,这个逻辑是怎么回事?
为了查找这个根据我看了:mtdchar.c里mtd_read函数,最后终于明白了:
这个函数的基本功能是:
格式:static ssize_t mtd_read(struct file
*file, char *buf, size_t count,loff_t *ppos)
功能:MTD字符设备的读操作
说明:
当count>0时{
裁减本次操作大小len至min(MAX_KMALLOC_SIZE,count),
申请一块大小为MAX_KMALLOC_SIZE的内核空间kbuf,
调用mtd_info->read将MTD设备中的数据读入kbuf,
将kbuf中的数据拷贝到用户空间buf,
count自减
释放kbuf
}
参数:
file:系统给MTD字符设备驱动程序用于传递参数的file结构,此函数通过file得到下
层的MTD设备
buf:用户空间的指针,用于存放读取的数据
count:被读数据的长度
ppos:被读数据在MTD设备中的位置
返回:
成功:返回实际读取数据的长度
失败:返回错误码
调用:
mtd_info->read()用于从MTD设备中读取数据
被调用:
被注册进mtd_fops结构
源代码:
static ssize_t mtd_read(struct file *file, char __user *buf, size_t
count,loff_t *ppos)
{
struct mtd_file_info *mfi =
file->private_data;
struct mtd_info *mtd = mfi->mtd;
size_t retlen=0;
size_t total_retlen=0;
int ret=0;
int len;
char *kbuf;
DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n");
if (*ppos + count >
mtd->size)
count =
mtd->size - *ppos;
if (!count)
return 0;
if (count > MAX_KMALLOC_SIZE)
kbuf=kmalloc(MAX_KMALLOC_SIZE,
GFP_KERNEL);
else
kbuf=kmalloc(count,
GFP_KERNEL);
if (!kbuf)
return -ENOMEM;
printk("mtd_read
called!!000000000000000\n");
while (count) {
if (count >
MAX_KMALLOC_SIZE)
len =
MAX_KMALLOC_SIZE;
else
len =
count;
switch
(mfi->mode) {
case
MTD_MODE_OTP_FACTORY:
ret =
mtd->read_fact_prot_reg(mtd, *ppos, len,
&retlen, kbuf);
break;
case MTD_MODE_OTP_USER:
ret =
mtd->read_user_prot_reg(mtd, *ppos, len,
&retlen, kbuf);
break;
case MTD_MODE_RAW:
{
struct
mtd_oob_ops ops;
ops.mode
= MTD_OOB_RAW;
ops.datbuf =
kbuf;
ops.oobbuf =
NULL;
ops.len =
len;
ret =
mtd->read_oob(mtd, *ppos,
&ops);
retlen =
ops.retlen;
break;
}
default:
printk("mtd_read called!!111111111111\n");
ret =
mtd->read(mtd, *ppos, len, &retlen,
kbuf);
}
if (!ret || (ret == -EUCLEAN)
|| (ret == -EBADMSG)) {
*ppos +=
retlen;
if
(copy_to_user(buf, kbuf, retlen)) {
kfree(kbuf);
return -EFAULT;
}
else
total_retlen += retlen;
count -=
retlen;
buf +=
retlen;
if (retlen
== 0)
count = 0;
}
else {
kfree(kbuf);
return
ret;
}
}
printk("mtd_read called!!2222222222222222\n");
kfree(kbuf);
return total_retlen;
}
注意到里面这一句:ret = mtd->read(mtd, *ppos, len,
&retlen, kbuf);这个是调用MTD原始设备层的mtd.read函数了。
而我们再回头看看m25p80.c里probe函数里语句:
flash->mtd.erase = m25p80_erase;
flash->mtd.read = m25p80_read;
flash->mtd.write = m25p80_write;
这个就是给mtd结构体赋初值的地方。原来是这么联系起来的。晕倒!
从上面的解释可以看到这个函数
1、先申请一块大小为MAX_KMALLOC_SIZE的内核空间kbuf,
2、调用mtd->read将MTD设备中的数据读入kbuf,
3、将kbuf中的数据拷贝到用户空间buf
可以看到,原来如此mtd是通过这些层次关系来调用底层mtd设备(m25p80.c)的数据来的。这又让我想起了视频video驱动里v4l2层次了。原来复杂的内核哪里都少不了这种层次的调用。这样的话m25p80.c没有open和ops结构体也正常了,因为在mtdchar.c里都已经做好了啊。同样在mtdchar.c里mtd_write()函数也看到了
ret = (*(mtd->write))(mtd, *ppos, len,
&retlen, kbuf);
这样的语句就是调用m25p80.c的m25p80_write函数的语句。现在情况就一目了然了。
小结:原来我们要对flash读取的时候就是要给它完成底层MTD原始设备(本例中的m25p40芯片)的加入(包括配置内核kconfig、makefile及davinci_spi_platform.c
改写),然后这个底层设备就会通过probe函数注册自己的mtd结构体,(struct mtd_file_info *mfi =
file->private_data;struct mtd_info *mtd =
mfi->mtd;)。然后中间的mtd设备层才能够调用这个底层设备的数据,诸如:mtdchar.c里的read、write调用。最终完成擦写具体flash的目的。上层的应用程序要继续研究。而设备节点代表了具体的一个mtd设备我们加载了m25p80.c以后在dev目录下就出现了mtd4这个设备。(至于为什么是mtd4就需要继续学习了)。
通过mtd读写flash_【转】 Linux下读写FLASH驱动——MTD设备分析相关推荐
- nand flash 经典 全面 ------如何编写Linux下Nand Flash驱动
Crifan Li 摘要 本文先解释了Nand Flash相关的一些名词,再从Flash硬件机制开始,介绍到Nand Flash的常见的物理特性,且深入介绍了Nand Flash的一些高级功能,然后开 ...
- 如何编写linux下nand flash驱动
http://www.cnblogs.com/sankye/articles/1638852.html 向作者Sankye致敬 [编写驱动之前要了解的知识] 1. 硬件特性: [Flash ...
- Linux下的硬件驱动——USB设备(下)
Linux下的硬件驱动--USB设备(下)(驱动开发部分) 文档选项 打印本页 将此页作为电子邮件发送 未显示需要 JavaScript 的文档选项 级别: 初级 赵明, 联想软件设计中心嵌入式研发处 ...
- Linux下的硬件驱动——USB设备
想起当初对于破安卓手机,挂在系统上可是费了好些劲,今偶遇USB驱动开发,收集备用,哪天一生气,说不定也写一个linux下的手机驱动,类似于91手机助手的,也不用配置了. Linux下的硬件驱动--US ...
- linux下nand flash驱动工作原理,Linux下Nand Flash 驱动代码分析
随着越来越多的平台支持从Nand Flash 中启动,掌握Nand Flash 的驱动编写有着重要的现实意义,由于内核已经完成了大部分的工作,实际工作中大部分工程师对Nand Flash 驱动只是简单 ...
- linux下nand flash驱动工作原理,1.3.4. Nand flash驱动工作原理
1.3.4. Nand flash驱动工作原理 在介绍具体如何写Nand Flash驱动之前,我们先要了解,大概的整个系统,和Nand Flash相关的部分的驱动工作流程,这样,对于后面的驱动实现,才 ...
- Linux下的硬件驱动——USB设备(上)(驱动配置部分)
USB设备越来越多,而Linux在硬件配置上仍然没有做到完全即插即用,对于Linux怎样配置和使用他们,也越来越成为困扰我们的一大问题.本文着力从Linux系统下设备驱动的架构,去阐述怎样去使用和配置 ...
- linux 下i2c读写命令,S3C2440 Linux下的I2C驱动以及I2C体系下对EEPROM进行读写操作
成员.我们可以看到消息结构体里面有从设备地址,读写标志,数据长度以及存储数据buf.这些成员我们看完之后会发现它大致符合先给设备地址,然后给写信号以及数据的时序.其实但我们写代码的时候并不一定是add ...
- linux下nand flash驱动工作原理,Linux驱动之Nand Flash四问,原理、工作方式都包含了...
Nand Flash 是一个存储芯片.本文引用地址:http://www.eepw.com.cn/article/201801/374606.htm 那么:这样的操作很理"读地址A的数据,把 ...
- Linux下的硬件驱动——USB设备配置以及开发
Linux下的硬件驱动--USB设备(上)(驱动配置部分) USB设备越来越多,而Linux在硬件配置上仍然没有做到完全即插即用,对于Linux怎样配置和使用他们,也越来越成为困扰我们的一大问题.本文 ...
最新文章
- php和python哪个学起来简单一点-python和php哪个容易学
- python 基础命令-Python 命令行(CLI)基础库
- 性能测试、负载测试、压力测试的区别
- 【PC工具】更新免费文库文档下载器,免费下载文库文档
- DHCP协议原理及其实现流程
- java jquery_jQuery数据表和Java集成
- TDengine与OpenTSDB对比测试
- 从欧拉公式看三角波的单边谱与双边谱
- C语言 - printf的占位符(%) 异常
- python人工智能是什么意思_人工智能和python有什么关系?
- java rmi配置_Java、Spring配置RMI连接
- 数据库工作笔记013---如果存在表则删除表然后创建Mysql_drop table
- Elasticsearch 日期时间处理
- Test for Activity to display Deslayed
- 如何制作一个计算机病毒,怎样制作一个简单的电脑病毒
- OpenCv4在Win10 VS2019上环境搭建
- linux网络操作系统项目教程第三版答案,《Linux网络操作系统项目教程(RHEL7.4 CentOS 7.4)(第3版))》习题及答案...
- python计算日期到天数_利用python计算时间差(返回天数)
- 王立柱《C语言程序设计》3.5.3
- [小o地图-数据] - 城市交通态势数据(实时路况)