Linux下的设备驱动程序被组织为一组完成不同任务的函数的集合,通过这些函数使得Windows的设备操作犹如文件一般。在应用程序看来,硬件设备只 是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作,如open ()、close ()、read ()、write () 等。
Linux主要将设备分为二类:字符设备和块设备。字符设备是指设备发送和接收数据以字符的形式进行;而块设备则以整个数据缓冲区的形式进行。字符设备的驱动相对比较简单。
下面我们来假设一个非常简单的虚拟字符设备:这个设备中只有一个4个字节的全局变量int global_var,而这个设备的名字叫做"gobalvar"。对"gobalvar"设备的读写等操作即是对其中全局变量global_var的操作。
驱动程序是内核的一部分,因此我们需要给其添加模块初始化函数,该函数用来完成对所控设备的初始化工作,并调用register_chrdev() 函数注册字符设备:
其中,register_chrdev函数中的参数MAJOR_NUM为主设备号,"gobalvar"为设备名,gobalvar_fops为包含基 本函数入口点的结构体,类型为file_operations。当gobalvar模块被加载时,gobalvar_init被执行,它将调用内核函数 register_chrdev,把驱动程序的基本入口点指针存放在内核的字符设备地址表中,在用户进程对该设备执行系统调用时提供入口地址。
与模块初始化函数对应的就是模块卸载函数,需要调用register_chrdev()的"反函数" unregister_chrdev():
Linux主要将设备分为二类:字符设备和块设备。字符设备是指设备发送和接收数据以字符的形式进行;而块设备则以整个数据缓冲区的形式进行。字符设备的驱动相对比较简单。
下面我们来假设一个非常简单的虚拟字符设备:这个设备中只有一个4个字节的全局变量int global_var,而这个设备的名字叫做"gobalvar"。对"gobalvar"设备的读写等操作即是对其中全局变量global_var的操作。
驱动程序是内核的一部分,因此我们需要给其添加模块初始化函数,该函数用来完成对所控设备的初始化工作,并调用register_chrdev() 函数注册字符设备:
static int __init gobalvar_init(void) { if (register_chrdev(MAJOR_NUM, " gobalvar ", &gobalvar_fops)) { //…注册失败 } else { //…注册成功 } } |
其中,register_chrdev函数中的参数MAJOR_NUM为主设备号,"gobalvar"为设备名,gobalvar_fops为包含基 本函数入口点的结构体,类型为file_operations。当gobalvar模块被加载时,gobalvar_init被执行,它将调用内核函数 register_chrdev,把驱动程序的基本入口点指针存放在内核的字符设备地址表中,在用户进程对该设备执行系统调用时提供入口地址。
与模块初始化函数对应的就是模块卸载函数,需要调用register_chrdev()的"反函数" unregister_chrdev():
static void __exit gobalvar_exit(void) { if (unregister_chrdev(MAJOR_NUM, " gobalvar ")) { //…卸载失败 } else { //…卸载成功 } } |
随着内核不断增加新的功能,file_operations结构体已逐渐变得越来越大,但是大多数的驱动程序只是利用了其中的一部分。对于字符设备来 说,要提供的主要入口有:open ()、release ()、read ()、write ()、ioctl ()、llseek()、poll()等
使用register_chrdev注册字符设备
注册字符设备可以使用register_chrdev函数。
- int register_chrdev (unsigned int major, const char *name, struct file_operations*fops);
register_chrdev函数的major参数如果等于0,则表示采用系统动态分配的主设备号。
注销字符设备可以使用unregister_chrdev函数。
- int unregister_chrdev(unsigned int major, const char *name);
例1.3 register_chrdev注册字符设备实例
代码见光盘\src\1drivermodel\1-3register_chrdev。核心代码如下所示:
- static unsigned char simple_inc=0;
- static unsigned char demoBuffer[256];
- int simple_open(struct inode *inode, struct file *filp)
- {
- if(simple_inc>0)return -ERESTARTSYS;
- simple_inc++;
- return 0;
- }
- int simple_release(struct inode *inode, struct file *filp)
- {
- simple_inc--;
- return 0;
- }
- ssize_t simple_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos)
- {
- /* 把数据复制到应用程序空间 */
- if (copy_to_user(buf,demoBuffer,count))
- {
- count=-EFAULT;
- }
- return count;
- }
- ssize_t simple_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
- {
- /* 把数据复制到内核空间 */
- if (copy_from_user(demoBuffer+*f_pos, buf, count))
- {
- count = -EFAULT;
- }
- return count;
- }
- struct file_operations simple_fops = {
- .owner = THIS_MODULE,
- .read = simple_read,
- .write = simple_write,
- .open = simple_open,
- .release = simple_release,
- };
- /*******************************************************
- MODULE ROUTINE
- *******************************************************/
- void simple_cleanup_module(void)
- {
- unregister_chrdev(simple_MAJOR, "simple");
- printk("simple_cleanup_module!\n");
- }
- int simple_init_module(void)
- {
- int ret;
- //根据设备号与设备名注册字符设备
- ret = register_chrdev(simple_MAJOR, "simple", &simple_fops);
- if (ret < 0)
- {
- printk("Unable to register character device %d!\n",simple_MAJOR);
- return ret;
- }
- return 0;
- }
- module_init(simple_init_module);
- module_exit(simple_cleanup_module);
应用程序的代码如下:
- void main(void)
- {
- int fd;
- int i;
- char data[256];
- int retval;
- fd=open("/dev/fgj",O_RDWR);
- if(fd==-1)
- {
- perror("error open\n");
- exit(-1);
- }
- printf("open /dev/fgj successfully\n");
- //写数据
- retval=write(fd,"fgj",3);
- if(retval==-1)
- {
- perror("write error\n");
- exit(-1);
- }
- //读数据
- retval=read(fd,data,3);
- if(retval==-1)
- {
- perror("read error\n");
- exit(-1);
- }
- data[retval]=0;
- printf("read successfully:%s\n",data);
- //关闭设备
- close(fd);
- }
字符设备模块使用insmod加载,加载完毕需要在/dev目录下使用mkmod命令建立相应的文件结点,编译生成的应用层可执行程序为test。本例运行结果如下:
- [root@/home]#insmod demo.ko
- [root@urbetter /home]# mknod /dev/fgj c 224 0
- [root@urbetter /home]# ./test
- open /dev/fgj successfully
- read successfully:fgj