1.流程说明
2.设备编号
(1)设备编号说明
对字符设备的访问是通过文件系统内设备名称进行的,哪些名称被称为特殊文件或设备文件,通常位于/dev目录下。
在Linux内核中,dev_t类型用于保存主设备号和次设备号。dev_t是一个32位数,其中的12位用于表示主设备号,而其余的20位用于表示次设备号。当然,对于编写驱动程序的人员并不需要关心这些位的分配。在2.6内核中,通过dev_t获得主设备号可以使用以下宏。
头文件:#include <linux/kdev.h>
宏原型:MAJOR(dev_t dev);
MINOR(dev_t dev);
若要将主设备号和次设备号转换为dev_t类型可使用以下宏。
MKDEV(int major, int minor);
在Linux中可以采用动态分配和静态分配设备号的方式。若用户提前知道所需要的设备编号,则可使用register_chrdev_region函数,如果用户并不确知设备编号,则可使用alloc_chrdev_region函数动态分配。
函数格式如下:
包含头文件:#include <linux/fs.h>
(2)获取设备编号实例
在实际应用中,主设备号是一个全局变量,程序可以通过判断主设备号来确定动态分配或手动分配。
若采用手动分配,首先调用MKDEV宏来获得设备的dev_t结构,其次再调用register_chrdev_region函数注册设备。
若采用自动分配,用户直接调用函数alloc_chrdev_region即可
调用成功后,可在/proc/devices里看到相应的设备。
3.重要的数据结构
在Linux驱动程序中,最重要涉及3个重要的内核数据结构,分别是file_operatin、file和inode。
在Linux中inode结构用于表示文件,而file结构则表示打开的文件描述符,因为对于单个文件而言可能会有许多个表示打开的文件描述符,因此就可能会对应有多个file结构,但它们都指向单个inode结构。
此外,每个file结构都与一组函数相关联,这组函数是用file_operations结构来指示的。
4.基本操作-open和release
open主要提供驱动程序初始化的能力,从而为以后的操作完成初始化作准备。在大部分驱动程序中,open完成如下工作:
检查设备的特定错误(诸如设备未就绪或类似的硬件问题)
如果设备初次打开,则对其进行初始化
如有必要,更新f_op指针
分配并填写filp->private_data里的数据结构。
这里,通常需要用到的宏函数是container_of,该函数可以返回包含cdev结构的结构体。
释放设备
释放设备的接口函数是release。要注意释放设备和关闭设备是完全不同的。当一个进程释放设备时,其他进程还能继续使用该设备,只是该进程暂时停止对该设备的使用。而当一个进程关闭设备时,其他进程必须重新打开次设备才能使用。
释放设备时要完成的工作如下:
递减计数器MOD_DEC_USE_COUNT
在最后一次释放设备操作时关闭设备
5.基本操作-read和write
将内核空间缓冲区里的数据复制到用户空间的缓冲区中或者相反。
copy_to_user或copy_from_user,这两个函数不仅实现了用户空间和内核空间的数据转换,而且还会检查用户空间指针的有效性。如果指针无效,那么就不进行复制。
在设备驱动程序中动态开辟内存有基于内存地址和页面两类。其中基于内存地址的函数有kmalloc,注意,kmalloc返回的是物理地址,而malloc返回的是线性地址,因此在驱动程序中不能使用malloc函数。
与malloc()不同,kmalloc()申请空间有大小限制,长度是2的整次方,并且不会对所获取的内存空间清0。
基于页为单位的内存有如下几个:
get_zeroed_page:获得一个已清0页面
get_free_page:获得一个或几个连续页面
get_dma_pages:获得用于DMA传输的页面
与之对应的释放内存也有kfree或kfree_pages函数
6.proc文件系统
/proc文件系统是一种内核和内核模块用来向进程发送信息的机制,是一个伪文件系统,可以让用户和内核内部数据结构进行交互,获取有关进程的有用信息,在运行时通过改变内核参数改变设置。
proc存在内存中而不是硬盘上。
除此之外,还有一些是以数字命名的目录,它们是进程目录。系统中当前运行的每一个进程都有对应的一个目录在/proc下,以进程的PID号为目录名,它们是读取进程信息的接口。