1. 程式人生 > 其它 >LED驅動之資源驅動一個檔案

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 }