转自:http://blog.sina.com.cn/s/blog_55465b470100eb8c.html
这里贴部分内容:
Chapter 2. 设备驱动程序开发
在编程思路上,机制表示需要提供什么功能,策略表示如何使用这些功能。区分机制和策略是UNIX设计最重要和最好的思想之一。如X系统就由X服务器和X客户端组成。X服务器实现机制,负责操作硬件,给用户程序提供一个统一的接口。而X客户端实现策略,负责如何使用X服务器提供的功能。设备驱动程序也是机制与策略分离的典型应用。在编写硬件驱动程序时,不要强加任何特定的策略。
Linux系统将设备分成三种类型,分别是字符设备、块设备和网络接口设备。
在linux中通过设备文件访问硬件,设备文件位于/dev目录下。设备文件是一种信息文件,普通文件的目的在于存储数据,设备文件的目的在于向内核提供控制硬件的设备驱动程序的信息。设备文件保存了多种信息,其中重要的有设备类型信息,主设备号(major),次设备号(minor)。主设备号与次设备号起到连接应用程序和设备驱动程序的作用。当应用程序利用open()函数打开设备文件时,内核从相应的设备文件中得到主设备号,从而查找到相应的设备驱动程序,由次设备号查找实际设备。所以主设备号对应设备驱动程序,次设备号对应由该驱动程序所驱动的实际设备。通过设备文件可以向硬件传送数据,也可从硬件接收数据。
设备文件使用mknod命令生成。mknod命令语法如下:
mknod [设备文件名] [设备文件类型] [主设备号] [次设备号]
字符设备用c表示,块设备用b表示,网络设备没有专门的设备文件。
读写设备文件时要使用低级输入输出函数,不要使用带缓冲的以f开头的流文件输入输出函数。但并不是所有低级输入输出函数都可以用在设备文件上,可以用在设备文件的低级输入输出函数有以下几个:
open() 打开文件或设备 close() 关闭文件 read() 读取数据 write() 写数据 lseek() 改变文件的读写位置 ioctl() 实现read(),write()外的特殊控制,该函数只在设备文件中使用 fsync() 实现写入文件上的数据和实际硬件的同步
Chapter 3. 字符设备驱动程序
3.1. 设备号
字符设备在系统中以设备文件的形式表示,位于/dev目录下。每个字符设备都有一个主设备号和次设备号,主设备号标识设备对应的驱动程序,次设备号标识设备文件所指的具体设备。
主次设备号的数据类型是dev_t,在/linux/types.h中定义。在2.6内核中,dev_t是一个32位的数,其中12位用来表示主设备号,其余20位用来表示次设备号。要获得设备的主次设备号可以使用内核提供的宏:
MAJOR(dev_t dev); #获得主设备号 MINOR(dev_t dev); #获得次设备号
这些宏定义位于linux/kdev_t.h中。如果要把主次设备号转换成dev_t类型,则可使用:
MKDEV(int major, int minor);
3.2. 设备号的分配和释放
在建立一个字符设备之前,需要为它分配一个或多个设备号。使用register_chrdev_region()函数完成设备号的分配。该函数在linux/fs.h中声明。原型如下:
int register_chrdev_region(dev_t first, unsigned int count, char *name); first:是要分配的主设备号范围的起始值,次设备号一般设置为0; count:是所请求的连续设备号的个数; name:是和该设备号范围关联的设备名称,它将出现在/proc/devices或/sysfs中。
如果分配成功则返回0,分配失败则返回一个负的错误码,所请求的设备号无效。
还有一个自动分配设备号的函数alloc_chrdev_region(),原型如下:
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name); dev: 自动分配到设备号范围中的第一个主设备号; firstminor:自动分配的第一个次设备号,通常为0; count: 是所请求的连续设备号的个数; name: 是和该设备号范围关联的设备名称,它将出现在/proc/devices或/sysfs中。
如果我们不再使用设备号,则要使用unregister_chrdev_region()函数释放它。函数原型如下:
void unregister_chrdev_region(dev_t first, unsigned int count); 函数的参数作用同上
我们一般在模块的清除函数中调用设备号释放函数。
在内核源码目录的Documentation/devices.txt文件中列出了已静态分配给常用设备的主设备号。为了减少设备号分配的冲突,我们一般要使用alloc_chrdev_region()函数来自动分配设备号。
3.3. 重要的数据结构
文件操作结构:struct file_operations,在linux/fs.h中定义。它包含一组函数指针,实现文件操作的系统调用,如read、write等。每个打开的文件都和一个文件操作结构关联(通过file结构中指向file_operations结构的f_op字段进行关联)。
文件结构:struct file,在linux/fs.h中定义。file结构代表一个打开的文件,由内核在open时创建。指向文件结构的指针在内核中通常称为filp(文件指针)。当文件的所有实例都被关闭之后,内核会释放这个数据结构。
节点结构:struct inode,在linux/fs.h中定义。inode结构是内核表示文件的方法,而file结构是以文件描述符的方式表示文件的方法。结构中以下两个字段对编写驱动程序有用:
- dev_t i_rdev,该字段包含了真正的设备编号。
- struct cdev *i_cdev,该字段包含指向struct cdev结构的指针。
从设备的inode获取主次设备号的宏:
unsigned int iminor(struct inode *inode); unsigned int imajor(struct inode *inode);
3.4. 读和写
下面两个是字符设备读写操作最重要的内核函数。
unsigned long copy_to_user (void __user * to, const void * from, unsigned long n); 读操作,把数据从内核空间复制到用户空间,返回不能复制的字节数,如果成功则返回0。 to 目的地址,在用户空间中; from 源地址,在用户空间; n 要复制的字节数。 unsigned long copy_from_user (void * to, const void __user * from, unsigned long n); 写操作,把数据从用户空间复制到内核空间,返回不能复制的字节数,如果成功则返回0。 to 目的地址,在内核空间中; from 源地址,在用户空间; n 要复制的字节数。
Linux Kernel学习笔记,布布扣,bubuko.com