字符驱动设备(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();

 

posted @ 2020-05-19 15:44  坦率  阅读(179)  评论(0编辑  收藏  举报