Linux驱动程序工作原理简介
一、linux驱动程序的数据结构
二、设备节点如何产生?
三、应用程序是如何访问设备驱动程序的?
四、为什么要有设备文件系统?
五、设备文件系统如何实现?
六、如何使用设备文件系统?
七、具体设备驱动程序分析
1、
2、
参考书目
一、linux驱动程序的数据结构
设备驱动程序实质上是提供一组供应用程序操作设备的接口函数。
各种设备由于功能不同,驱动程序提高的函数接口也不相同,但linux为了能够统一管理,规定了linux下设备驱动程序必须使用统一的接口函数file_operations 。
所以,一种设备的驱动程序主要内容就是提高这样的一组file_operations接口函数。
那么,linux是如何管理种类繁多的设备驱动程序呢?
linux下设备大体分为块设备和字符设备两类。
内核中用2个全局数组存放这2类驱动程序。
#define MAX_CHRDEV
#define MAX_BLKDEV
struct device_struct {
};
static struct device_structchrdevs[MAX_CHRDEV];
static struct {
} blkdevs[MAX_BLKDEV];
//此处说明一下,struct block_device_operations是块设备驱动程序内部的接口函数,上层文件系统还是通过struct file_operations访问的。
哈哈,现在明白了吧?你的驱动程序调用int register_chrdev(unsigned int major, const char* name, struct file_operations *fops) 就是将你提供的接口函数fops存放到chrdevs[MAX_CHRDEV]这个数组中,数组下标就是你的驱动的主设备号,数组内容包括驱动名称和驱动接口函数,这样,内核就能看到你的驱动程序了。BTW,如果你将major设为0,系统会自动给你分配一个空闲的主设备号。
那么?次设备号呢?别急,马上就出现了:)
二、设备节点如何产生?
所以,文件系统中必须要有一个代表你的设备的文件,应用程序才能访问你的设备驱动程序。
你可以用mknod命令。如使用以下命令可以创建一个mtd4的字符设备节点。
Mknod /dev/ mtd4 cMTD_CHAR_MAJOR 4
我们创建一个普通的磁盘文件,它的内容是我们写入的数据。
那么设备节点的内容是什么?设备节点文件没有数据,它的文件大小为0,它只有文件属性,包括设备类型、主设备号、次设备号。
没有别的了?对,就这些,没别的了。
那设备节点和设备驱动程序是怎么联系起来的啊?
别着急,休息,休息一会儿:)
三、应用程序是如何访问设备驱动程序的?
举个例子:我们要向nor flash的第四个分区的起始位置偏移512字节写入100字节的数据。
我们是如何做的?主要程序片断如下:
上面的代码比较简单,但是似乎没有看到我们的应用程序是如何调用到驱动程序的。
没关系,接下来我将带领你们走通这条道路。
应用程序调用Open函数,这是个系统调用函数,程序会进入内核空间调用sys_open函数。
在sys_open,首先会根据文件路径“/dev/mtd4”找到这个文件节点,这部分工作是属于VFS(虚拟文件系统)的。
“/dev/mtd4”的文件属性是字符设备,于是sys_open会调用函数chrdev_open(),在这个函数里有一句话:
filp->f_op =get_chrfops(MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
哈!看到了眉目吧!猜也能猜到啊,get_chrfops()里面一定会返回chrdevs[major].fops的。
我们终于从文件系统走到驱动程序了,那么,接下来的事情就是可以理解的了。
Write()最终一定会调用到chrdevs[major].fops->write();
Read()最终一定会调用到chrdevs[major].fops->read();
各种驱动程序比较特殊的功能函数都可以通过ioctl()来得到调用。
而次设备号也会作为参数传递给你。
四、为什么要有设备文件系统?
从前面的章节,我们可以看到以主次设备号的形式管理设备驱动程序存在很大的缺点。
首先,设备节点的创建是独立于内核的,是在建立文件系统时就把所有要用到的设备节点都创建好了的,通常我们不会去刻意删除哪些节点,因为我们不知道系统将来会不会用到它们。由于每个设备节点代表唯一的主次设备号,所以每个可能存在的子设备都对应一个设备节点,可见,这样的设备节点数量是很大的,这些数量庞大的设备节点都(文件)存在于存储介质中,对文件系统的效率也是个影响。
其次,文件系统中存在哪些设备节点,并不代表内核中就有这种设备的驱动程序,也不代表系统中有这种设备,因为设备节点不是动态创建的,它是制作文件系统时建立的。因此,/dev目录下的信息大多对我们是无用的,而且那么多的设备节点都平铺在/dev目录下,阅读起来也不直观。
最后,目前主次设备号都是用8位整数表示的,也就是说内核最多管理256种字符设备和256种块设备。现在计算机外设种类越来越多,这样的限制已经不够了。
由于目前的主次设备号的管理形式有以上几个缺点,linux内核小组在2.4版本以后加入了设备文件系统来改进这些缺点。
设备文件系统的思想就是想让设备节点可以动态创建、删除,这样系统中有哪些设备驱动程序就可以一目了然;还要能够把设备节点组织成一棵目录树,方便阅读;最后,希望能够扩大主次设备号的限制,不再限制在256种设备以内。
五、设备文件系统如何实现?
六、如何使用设备文件系统?
1、调用devfs_handle_t devfs_mk_dir (devfs_handle_t dir,const char *name, void *info)创建设备文件所在的目录。Dir是要创建目录的父目录句柄,如为NULL,就是设备文件系统的根目录(/dev);最后一个参数info通常为NULL。
2、调用devfs_handle_t devfs_register (devfs_handle_t dir,const char *name,
注册具体的设备,并在指定目录下创建子设备节点。
这里的dir目录名是要创建的设备文件所在的目录名,目录名一般不能为NULL,因为子设备文件名name通常是以0、1、2、3等数字命名的,会和其它设备文件冲突。
1、
驱动程序为了兼容以前的方案,通常会既注册设备节点,又创建子设备文件,这样不管内核支持不支持设备文件系统,驱动程序都可以工作。
static int __initinit_mtdchar(void)
{
{
}
static inline voidmtdchar_devfs_init(void)
{
}
notifier定义如下,主要是提高创建和删除子设备文件的接口函数
static structmtd_notifier notifier = {
};
static void mtd_notify_add(structmtd_info* mtd)
{
}
static void mtd_notify_remove(structmtd_info* mtd)
{
}
mtd驱动程序中会将一片flash划分为多个逻辑分区,这样的每个逻辑分区也可以被看做是一个子设备,具体flash驱动程序添加逻辑分区时会在数组mtd_table[]中记录分区的位置和大小。
voidregister_mtd_user (struct mtd_notifier *new)
{
}
2、
static void __exitcleanup_mtdchar(void)
{
}
static inline voidmtdchar_devfs_exit(void)
{
}
int unregister_mtd_user (structmtd_notifier *old)
{
}