LED驅動之資源驅動一個檔案
阿新 • • 發佈:2022-03-23
我們之前瞭解了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; 24static 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 }