《C语言编程思想 — 用结构体实现面向对象和分离》

说明: 

  以下示例是看到Linux中驱动一个比较简单的架构,然后记录下来。

  示例的功能是:将led通用的一些驱动代码和硬件相关代码分离开。

  什么是通用的驱动代码:比如注册file_operation结构体啊,class类等一些。就算我们修改驱动,这些也不会变动的代码。

  硬件相关代码:比如led的引脚地址

 

为什么要这样做?

  1.减少耦合性。将通用代码和硬件相关代码分离开。这样,当我们修改LED的驱动的时候,就不用看一段很长的代码。只需要单独修改跟硬件相关代码的那个文件。

  2.扩展性。我们需要去驱动其他板卡的LED的时候,那么我们也只需要修改跟硬件相关代码的那个文件。并且可以同时支持一个驱动代码对应多个不同的板卡。

 

 led_opr.h

 1 #ifndef _LED_OPR_H
 2 #define _LED_OPR_H
 3 
 4 struct led_operations {
 5     int (*init) (int which); /* 初始化LED, which-哪个LED */       
 6     int (*ctl) (int which, char status); /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
 7 };
 8 
 9 struct led_operations *get_board_led_opr(void);
10 
11 
12 #endif

  led_opr.h中定义了一个结构体,结构体里面定义了函数指针init和ctl。并且有一个指针函数,返回的是led_operations类型的结构体指针

  首先分析一下为什么要使用函数指针。

  函数指针就是一个指向函数的指针,我们可以把写好的函数赋给这个函数指针。其他.c文件就可以直接用led_operations->init(参数)来调用我们在其他文件编写好的函数。就不需要每次都要声明一下函数,然后再调用,也不用担心函数名的问题。

  为什么要使用指针函数?

  指针函数就是返回指针的函数。我们需要把在board_demo.c中定义的led_operations结构体变量给其他.c的函数用。

 

board_demo.c

 1 board_demo.c
 2 
 3 #include <linux/module.h>
 4 
 5 #include <linux/fs.h>
 6 #include <linux/errno.h>
 7 #include <linux/miscdevice.h>
 8 #include <linux/kernel.h>
 9 #include <linux/major.h>
10 #include <linux/mutex.h>
11 #include <linux/proc_fs.h>
12 #include <linux/seq_file.h>
13 #include <linux/stat.h>
14 #include <linux/init.h>
15 #include <linux/device.h>
16 #include <linux/tty.h>
17 #include <linux/kmod.h>
18 #include <linux/gfp.h>
19 #include "led_opr.h"
20 
21 static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */       
22 {
23     
24     printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
25     return 0;
26 }
27 
28 static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
29 {
30     printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");
31     return 0;
32 }
33 
34 static struct led_operations board_demo_led_opr = {
35     .init = board_demo_led_init,
36     .ctl  = board_demo_led_ctl,
37 };
38 
39 struct led_operations *get_board_led_opr(void)
40 {
41     return &board_demo_led_opr;
42 }

   在board_demo.c中实现了board_demo_led_init和board_demo_led_ctl两个函数。然后将这两个函数赋给led_operations类型结构体变量board_demo_led_opr中的成员init和ctl。然后通过get_board_led_opr函数(指针函数:返回led_operations *类型的指针)。这样就可以在leddrv.c中获取到board_demo_led_opr变量的地址,在其他.c中可以调用board_demo_led_init和board_demo_led_ctl这两个函数。

 

leddrv.c

  1 #include <linux/module.h>
  2 
  3 #include <linux/fs.h>
  4 #include <linux/errno.h>
  5 #include <linux/miscdevice.h>
  6 #include <linux/kernel.h>
  7 #include <linux/major.h>
  8 #include <linux/mutex.h>
  9 #include <linux/proc_fs.h>
 10 #include <linux/seq_file.h>
 11 #include <linux/stat.h>
 12 #include <linux/init.h>
 13 #include <linux/device.h>
 14 #include <linux/tty.h>
 15 #include <linux/kmod.h>
 16 #include <linux/gfp.h>
 17 
 18 #include "led_opr.h"
 19 
 20 #define LED_NUM 2
 21 
 22 /* 1. 确定主设备号                                                                 */
 23 static int major = 0;
 24 static struct class *led_class;
 25 struct led_operations *p_led_opr;
 26 
 27 
 28 #define MIN(a, b) (a < b ? a : b)
 29 
 30 /* 3. 实现对应的open/read/write等函数,填入file_operations结构体                   */
 31 static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
 32 {
 33     printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
 34     return 0;
 35 }
 36 
 37 /* write(fd, &val, 1); */
 38 static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
 39 {
 40     int err;
 41     char status;
 42     struct inode *inode = file_inode(file);
 43     int minor = iminor(inode);
 44     
 45     printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
 46     err = copy_from_user(&status, buf, 1);
 47 
 48     /* 根据次设备号和status控制LED */
 49     p_led_opr->ctl(minor, status);
 50     
 51     return 1;
 52 }
 53 
 54 static int led_drv_open (struct inode *node, struct file *file)
 55 {
 56     int minor = iminor(node);
 57     
 58     printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
 59     /* 根据次设备号初始化LED */
 60     p_led_opr->init(minor);
 61     
 62     return 0;
 63 }
 64 
 65 static int led_drv_close (struct inode *node, struct file *file)
 66 {
 67     printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
 68     return 0;
 69 }
 70 
 71 /* 2. 定义自己的file_operations结构体                                              */
 72 static struct file_operations led_drv = {
 73     .owner     = THIS_MODULE,
 74     .open    = led_drv_open,
 75     .read    = led_drv_read,
 76     .write   = led_drv_write,
 77     .release = led_drv_close,
 78 };
 79 
 80 /* 4. 把file_operations结构体告诉内核:注册驱动程序                                */
 81 /* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */
 82 static int __init led_init(void)
 83 {
 84     int err;
 85     int i;
 86     
 87     printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
 88     major = register_chrdev(0, "100ask_led", &led_drv);  /* /dev/led */
 89 
 90 
 91     led_class = class_create(THIS_MODULE, "100ask_led_class");
 92     err = PTR_ERR(led_class);
 93     if (IS_ERR(led_class)) {
 94         printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
 95         unregister_chrdev(major, "led");
 96         return -1;
 97     }
 98 
 99     for (i = 0; i < LED_NUM; i++)
100         device_create(led_class, NULL, MKDEV(major, i), NULL, "100ask_led%d", i); /* /dev/100ask_led0,1,... */
101 
102     p_led_opr = get_board_led_opr();
103     
104     return 0;
105 }
106 
107 /* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数           */
108 static void __exit led_exit(void)
109 {
110     int i;
111     printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
112 
113     for (i = 0; i < LED_NUM; i++)
114         device_destroy(led_class, MKDEV(major, i)); /* /dev/100ask_led0,1,... */
115 
116     device_destroy(led_class, MKDEV(major, 0));
117     class_destroy(led_class);
118     unregister_chrdev(major, "100ask_led");
119 }
120 
121 
122 /* 7. 其他完善:提供设备信息,自动创建设备节点                                     */
123 
124 module_init(led_init);
125 module_exit(led_exit);
126 
127 MODULE_LICENSE("GPL");

  25行struct led_operations *p_led_opr,定义了一个led_operations结构体指针变量。

  102行中get_board_led_opr()函数(就是一个指针函数,返回一个led_operations结构体变量)。

  49行和60行通过p_led_opr去调用在board_demo.c中写好的函数。








posted @ 2020-01-11 18:05  一个不知道干嘛的小萌新  阅读(857)  评论(0编辑  收藏  举报