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 }

浙公网安备 33010602011771号