字符驱动设备(2)
__attribute__((section(".xxx")))子项section的使用方法,可以用来修饰变量或函数:
修饰变量:
int var __attribute__((section(".xdata"))) = 0;
这样定义的变量 var 将被放入名为 .xdata 的输入段,(注意:__attribute__这种用法中的括号好像很严格,这里的几个括号好象一个也不能少。)
修饰函数:
1 static int __attribute__((section(".xinit"))) function(void) 2 3 { 4 ..... 5 }
这个例子将函数 function 被放入名叫 .xinit 的输入段。
需要着重注意的是,__attribute__的section属性只指定对象的输入段,它并不能影响所指定对象最终会放在可执行文件的什么段。
这样再看字符设备的加载和卸载模块的模板函数就可以看懂了:
1 /*驱动入口函数*/ 2 static int __init xxx_init(void) 3 { 4 /*入口函数的具体内容*/ 5 6 return 0; 7 } 8 9 /*驱动出口函数*/ 10 static void __exit xxx_exit(void) 11 { 12 /*出口函数的具体内容*/ 13 14 } 15 16 /*将上面两个函数指定为驱动入口和出口函数*/ 17 module_init(xxx_init); 18 module_exit(xxx_exit);
对于字符驱动设备而言,当驱动模块加载成功以后需要注册字符设备,同样,卸载驱动模块的时候也需要注销字符设备,注册字符设备和注销字符设备函数原型如下:
1 int register_chrdev(unsigned int major, const char *name, 2 const struct file_operations *fops) 3 int unregister_chrdev(unsigned int major, const char *name)
一般字符设备的注册在在驱动入口函数xxx_init中进行,字符设备的注销也在驱动出口函数xxx_exit中进行,模板如下:
1 static struct file_operations led_fops; 2 3 /*驱动入口函数*/ 4 static int __init xxx_init(void) 5 { 6 int ret = 0; 7 8 /*注册字符设备驱动*/ 9 ret = register_chrdev(200, "led_drv",&led_fops); 10 11 if(ret<0) 12 { 13 /*注册失败,自行处理*/ 14 } 15 16 return 0; 17 } 18 19 /*驱动出口函数*/ 20 static void __exit xxx_exit(void) 21 { 22 /*注销字符驱动设备*/ 23 unregister_chrdev(200,"led_drv"); 24 } 25 26 /*将上面两个函数指定为驱动入口和出口函数*/ 27 module_init(xxx_init); 28 module_exit(xxx_exit);
实现设备的具体操作函数:
file_operations结构体就是设备的具体操作函数,前面我们定义了file_operations结构体类型变量led_fops,但是还没有对其进行初始化,也就是初始化里面的open(),release(),read(),write()等具体的设备操作函数。在初始化之前我们需要分析一下需求,也就是对 led_drv 这个设备进行哪些操作,只有确定了需求我们才知道应该实现哪些操作函数。对于led_drv我们应该有以下的需求:
1.能够对 led_drv 设备进行打开和关闭操作
几乎所有的设备都需要打开和关闭两个操作,因此我们需要实现file_operations的open()和release()两个函数。
2.能够对 led_drv 设备进行读与写操作
假设 led_drv 这个设备控制着一段缓冲区(内存,寄存器等),应用程序需要通过read()和write()函数对 led_drv 的缓冲区(内存,寄存器等)进行读写操作,所以,我们还要实现file_operations的read()和write()两个函数。
1 /*打开设备*/ 2 static int led_open(struct inode *, struct file *) 3 { 4 /*用户实现的具体功能*/ 5 6 return 0; 7 } 8 9 /*从设备读取*/ 10 static ssize_t led_read(struct file *, char __user *, size_t, loff_t *) 11 { 12 /*用户实现的具体功能*/ 13 14 return 0; 15 } 16 17 /*向设备写数据*/ 18 static ssize_t led_write(struct file *, const char __user *, size_t, loff_t *) 19 { 20 /*用户实现的具体功能*/ 21 22 return 0; 23 } 24 25 /*关闭设备*/ 26 static int led_release(struct inode *, struct file *) 27 { 28 /*用户实现的具体功能*/ 29 30 return 0; 31 } 32 33 static struct file_operations led_fops = { 34 .owner = THIS_MODULE, 35 .open = led_open, 36 .read = led_read, 37 .write = led_write, 38 .release = led_release, 39 }; 40 41 /*驱动入口函数*/ 42 static int __init xxx_init(void) 43 { 44 int ret = 0; 45 46 /*注册字符设备驱动*/ 47 ret = register_chrdev(200, led_drv,&led_fops); 48 49 if(ret<0) 50 { 51 /*注册失败,自行处理*/ 52 } 53 54 return 0; 55 } 56 57 /*驱动出口函数*/ 58 static void __exit xxx_exit(void) 59 { 60 /*注销字符驱动设备*/ 61 unregister_chrdev(200,"led_drv"); 62 } 63 64 /*将上面两个函数指定为驱动入口和出口函数*/ 65 module_init(xxx_init); 66 module_exit(xxx_exit);
添加LICENSE和作者信息
最后我们需要在驱动中加入LICENSE信息和作者信息,其中LICENSE信息是必须加的,不然编译会报错。作者信息可加可不加。函数原型如下:
1 MODULE_LICENSE(); 2 MODULE_AUTHOR();