LED驱动的分离分层法(上)

我们之前了解了Hello驱动程序。如果在一个文件中实现LED的驱动程序,就是在write函数中实现对硬件的操作就可以了。

但是我们为了适应不同的板子,需要将其做分层。

1. 把驱动拆分为通用的框架(leddrv.c)、具体的硬件操作(board_X.c)。

2. 以面向对象的思想,改进代码:

抽象出一个结构体:

每个单板相关的board_X.c实现自己的led_operations结构体,供上层的leddrv.c调用:

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

在向下调用的过程中。需要调用init 和ctl 。所以此处需要调用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 #endif

具体的函数实现我们在board_dome.c文件中实现。

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

现在我们以STM32MP157的板子为例。我们的board_dome.c改为board_stm32mp157.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 #include <asm/io.h>
 18 
 19 #include "led_opr.h"
 20 
 21 /* registers */
 22 // RCC_PLL4CR地址:0x50000000 + 0x894
 23 static volatile unsigned int *RCC_PLL4CR;
 24 
 25 // RCC_MP_AHB4ENSETR 地址:0x50000000 + 0xA28
 26 static volatile unsigned int *RCC_MP_AHB4ENSETR;
 27 
 28 // GPIOA_MODER 地址:0x50002000 + 0x00
 29 static volatile unsigned int *GPIOA_MODER;
 30 
 31 // GPIOA_BSRR 地址: 0x50002000 + 0x18
 32 static volatile unsigned int *GPIOA_BSRR;
 33 
 34 
 35 static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */       
 36 {
 37 
 38     if (!RCC_PLL4CR)
 39     {
 40         // RCC_PLL4CR地址:0x50000000 + 0x894
 41         RCC_PLL4CR = ioremap(0x50000000 + 0x894, 4);
 42         
 43         // RCC_MP_AHB4ENSETR 地址:0x50000000 + 0xA28
 44         RCC_MP_AHB4ENSETR = ioremap(0x50000000 + 0xA28, 4);
 45         
 46         // GPIOA_MODER 地址:0x50002000 + 0x00
 47         GPIOA_MODER = ioremap(0x50002000 + 0x00, 4);
 48         
 49         // GPIOA_BSRR 地址: 0x50002000 + 0x18
 50         GPIOA_BSRR = ioremap(0x50002000 + 0x18, 4);
 51     }
 52 
 53     if (which == 0)
 54     {
 55         /* enalbe PLL4, it is clock source for all gpio */
 56         *RCC_PLL4CR |= (1<<0);
 57         while ((*RCC_PLL4CR & (1<<1)) == 0);
 58         
 59         /* enable gpioA */
 60         *RCC_MP_AHB4ENSETR |= (1<<0);
 61         
 62         /*
 63          * configure gpa10 as gpio
 64          * configure gpio as output 
 65          */
 66         *GPIOA_MODER &= ~(3<<20);
 67         *GPIOA_MODER |= (1<<20);
 68     }
 69 
 70     return 0;
 71 }
 72 
 73 static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
 74 {
 75     if (which == 0)
 76     {
 77         /* to set gpio register: out 1/0 */
 78         if (status)
 79         {
 80             /* set gpa10 to let led on */
 81             *GPIOA_BSRR = (1<<26);
 82         }
 83         else
 84         {
 85             /* set gpa10 to let led off */
 86             *GPIOA_BSRR = (1<<10);
 87         }
 88     }
 89     return 0;
 90 }
 91 
 92 static struct led_operations board_demo_led_opr = {
 93     .num  = 1,
 94     .init = board_demo_led_init,
 95     .ctl  = board_demo_led_ctl,
 96 };
 97 
 98 struct led_operations *get_board_led_opr(void)
 99 {
100     return &board_demo_led_opr;
101 }

对于imx_6ul 我们改变board_dome.c改为board_imx6ul.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 #include <asm/io.h>
 18 
 19 #include "led_opr.h"
 20 
 21 static volatile unsigned int *CCM_CCGR1                              ;
 22 static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
 23 static volatile unsigned int *GPIO5_GDIR                             ;
 24 static volatile unsigned int *GPIO5_DR                               ;
 25 
 26 static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */       
 27 {
 28     unsigned int val;
 29 
 30     //printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
 31     if (which == 0)
 32     {
 33         if (!CCM_CCGR1)
 34         {
 35             CCM_CCGR1                               = ioremap(0x20C406C, 4);
 36             IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(0x2290014, 4);
 37             GPIO5_GDIR                              = ioremap(0x020AC000 + 0x4, 4);
 38             GPIO5_DR                                = ioremap(0x020AC000 + 0, 4);
 39         }
 40         
 41         /* GPIO5_IO03 */
 42         /* a. 使能GPIO5
 43          * set CCM to enable GPIO5
 44          * CCM_CCGR1[CG15] 0x20C406C
 45          * bit[31:30] = 0b11
 46          */
 47         *CCM_CCGR1 |= (3<<30);
 48         
 49         /* b. 设置GPIO5_IO03用于GPIO
 50          * set IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3
 51          *      to configure GPIO5_IO03 as GPIO
 52          * IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3  0x2290014
 53          * bit[3:0] = 0b0101 alt5
 54          */
 55         val = *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
 56         val &= ~(0xf);
 57         val |= (5);
 58         *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = val;
 59         
 60         
 61         /* b. 设置GPIO5_IO03作为output引脚
 62          * set GPIO5_GDIR to configure GPIO5_IO03 as output
 63          * GPIO5_GDIR  0x020AC000 + 0x4
 64          * bit[3] = 0b1
 65          */
 66         *GPIO5_GDIR |= (1<<3);
 67     }
 68     
 69     return 0;
 70 }
 71 
 72 static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
 73 {
 74     //printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");
 75     if (which == 0)
 76     {
 77         if (status) /* on: output 0*/
 78         {
 79             /* d. 设置GPIO5_DR输出低电平
 80              * set GPIO5_DR to configure GPIO5_IO03 output 0
 81              * GPIO5_DR 0x020AC000 + 0
 82              * bit[3] = 0b0
 83              */
 84             *GPIO5_DR &= ~(1<<3);
 85         }
 86         else  /* off: output 1*/
 87         {
 88             /* e. 设置GPIO5_IO3输出高电平
 89              * set GPIO5_DR to configure GPIO5_IO03 output 1
 90              * GPIO5_DR 0x020AC000 + 0
 91              * bit[3] = 0b1
 92              */ 
 93             *GPIO5_DR |= (1<<3);
 94         }
 95     
 96     }
 97     return 0;
 98 }
 99 
100 static struct led_operations board_demo_led_opr = {
101     .num  = 1,
102     .init = board_demo_led_init,
103     .ctl  = board_demo_led_ctl,
104 };
105 
106 struct led_operations *get_board_led_opr(void)
107 {
108     return &board_demo_led_opr;
109 }

针对不同的板子,我们只是在改变初始化的操作代码具体看芯片手册。

上层测试代码如下:

 1 #include <sys/types.h>
 2 #include <sys/stat.h>
 3 #include <fcntl.h>
 4 #include <unistd.h>
 5 #include <stdio.h>
 6 #include <string.h>
 7 
 8 /*
 9  * ./ledtest /dev/100ask_led0 on
10  * ./ledtest /dev/100ask_led0 off
11  */
12 int main(int argc, char **argv)
13 {
14     int fd;
15     char status;
16     
17     /* 1. 判断参数 */
18     if (argc != 3) 
19     {
20         printf("Usage: %s <dev> <on | off>\n", argv[0]);
21         return -1;
22     }
23 
24     /* 2. 打开文件 */
25     fd = open(argv[1], O_RDWR);
26     if (fd == -1)
27     {
28         printf("can not open file %s\n", argv[1]);
29         return -1;
30     }
31 
32     /* 3. 写文件 */
33     if (0 == strcmp(argv[2], "on"))
34     {
35         status = 1;
36         write(fd, &status, 1);
37     }
38     else
39     {
40         status = 0;
41         write(fd, &status, 1);
42     }
43     
44     close(fd);
45     
46     return 0;
47 }

 

posted @ 2022-03-23 15:20  咸阳梁硕  阅读(191)  评论(0)    收藏  举报