使用设备树来编写led驱动程序

在总线设备驱动模型中,平台设备是写在c文件中。使用设备树时,平台设备事先并不存在,在dts文件中构造节点,节点里面含有资源。dts文件被编译成dtb文件,然后传递给内核。内核会解析dtb文件,得到一个个device_node,每个节点对应一个device_node结构体,每个device_node结构体变成一个platform_device结构体,该结构体中就含有资源,这些资源来源于dts文件。接下来的处理过程和总线设备驱动模型就一样了,如果设备与驱动相匹配,就调用驱动中的probe函数。可以这么认为,设备树是对总线设备驱动模型的一种改进,它仍然属于总线设备驱动模型的一种。

看一下这个设备树文件:

 1 // SPDX-License-Identifier: GPL-2.0
 2 /*
 3  * SAMSUNG SMDK2440 board device tree source
 4  *
 5  * Copyright (c) 2018 weidongshan@qq.com
 6  * dtc -I dtb -O dts -o jz2440.dts jz2440.dtb
 7  */
 8  //这里定义了一些宏,使用的是c语言的语法
 9 #define S3C2410_GPA(_nr)    ((0<<16) + (_nr))
10 #define S3C2410_GPB(_nr)    ((1<<16) + (_nr))
11 #define S3C2410_GPC(_nr)    ((2<<16) + (_nr))
12 #define S3C2410_GPD(_nr)    ((3<<16) + (_nr))
13 #define S3C2410_GPE(_nr)    ((4<<16) + (_nr))
14 #define S3C2410_GPF(_nr)    ((5<<16) + (_nr))
15 #define S3C2410_GPG(_nr)    ((6<<16) + (_nr))
16 #define S3C2410_GPH(_nr)    ((7<<16) + (_nr))
17 #define S3C2410_GPJ(_nr)    ((8<<16) + (_nr))
18 #define S3C2410_GPK(_nr)    ((9<<16) + (_nr))
19 #define S3C2410_GPL(_nr)    ((10<<16) + (_nr))
20 #define S3C2410_GPM(_nr)    ((11<<16) + (_nr))
21 
22 /dts-v1/;
23 
24 / {
25     model = "SMDK24440";
26     compatible = "samsung,smdk2440";
27 
28     #address-cells = <1>;
29     #size-cells = <1>;
30         
31     memory@30000000 {
32         device_type = "memory";
33         reg =  <0x30000000 0x4000000>;
34     };
35 /*
36     cpus {
37         cpu {
38             compatible = "arm,arm926ej-s";
39         };
40     };
41 */    
42     chosen {  //设置了内核的命令行参数
43         bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
44     };
45 
46     
47     led {
48         compatible = "jz2440_led"; //以后就使用compatible在内核里面找到能够支持这个节点的驱动程序,即找到能够支持这个节点的平台drv.
49         reg = <S3C2410_GPF(5) 1>;//reg是register的缩写。在arm系统里面,寄存器和内存是被同样对待的,因为寄存器的访问空间与内存的访问空间没什么差别。
50     };
51 };

 单板启动之后,

cd /sys/devices/platform/

ls 下会看到xxxxxxx,假设是50005.led。

cd 50005.led

ls会看到这样的文件

driver_override    of_node       subsystem      modalias      power   uevent

cd of_node 

ls

compatible     name      reg

cat compatible     就会显示  jz2440_led

cat  name             就会显示led

hexdump -C reg

对应着8个字节,

00 05   00 05  00  00  00  01

对应着两个数值,来源于    reg = <S3C2410_GPF(5) 1>

00 05   00 05    对应S3C2410_GPF(5)代表着寄存器的起始地址,对应着这个宏    #define S3C2410_GPF(_nr) ((5<<16) + (_nr)),表示高16位是5,低16位也是5.

00 00 00 01对应着是1,本意就是寄存器的大小。

在总线设备驱动模型中,是通过名字来进行设备与驱动的匹配。那么在利用设备树时,是利用什么进行匹配的呢。

 1 static int platform_match(struct device *dev, struct device_driver *drv)
 2 {
 3     struct platform_device *pdev = to_platform_device(dev);
 4     struct platform_driver *pdrv = to_platform_driver(drv);
 5 
 6     /* Attempt an OF style match first */
 7     if (of_driver_match_device(dev, drv))//用来判断从设备树中得到的platform_devcie与提供的platform_drv
                         //是否匹配,
8 return 1; 9 10 /* Then try to match against the id table */ 11 if (pdrv->id_table) 12 return platform_match_id(pdrv->id_table, pdev) != NULL; 13 14 /* fall-back to driver name match */ 15 return (strcmp(pdev->name, drv->name) == 0); 16 }
1 static inline int of_driver_match_device(struct device *dev,
2                      const struct device_driver *drv)
3 {
  /*从这里可以看出,平台drv中有个成员变量,of_match_table*/
4 return of_match_device(drv->of_match_table, dev) != NULL; 5 }
 1 struct device_driver {
 2     const char        *name;
 3     struct bus_type        *bus;
 4 
 5     struct module        *owner;
 6     const char        *mod_name;    /* used for built-in modules */
 7 
 8     bool suppress_bind_attrs;    /* disables bind/unbind via sysfs */
 9 
10     const struct of_device_id    *of_match_table; //该指针指向一项或多项of_device_id
11 
12     int (*probe) (struct device *dev);
13     int (*remove) (struct device *dev);
14     void (*shutdown) (struct device *dev);
15     int (*suspend) (struct device *dev, pm_message_t state);
16     int (*resume) (struct device *dev);
17     const struct attribute_group **groups;
18 
19     const struct dev_pm_ops *pm;
20 
21     struct driver_private *p;
22 };
 1 struct of_device_id
 2 {
 3     char    name[32];
 4     char    type[32];
 5     char    compatible[128];//从dts里得到的platform_device里有compatible属性,两者比较,如果一样的话,就是匹配。
 6 #ifdef __KERNEL__
 7     void    *data;
 8 #else
 9     kernel_ulong_t data;
10 #endif
11 };
 1 struct platform_device {
 2     const char    * name;
 3     int        id;
 4     struct device    dev;//对于dts生成的设备platform_device,这里含有Of_node,of_node中含有属性,这含有哪些属性呢,这取决于设备树,比如:
//compatible pin等。这个compatible属性用来寻找支持它的platform_driver.这个compatible是最优先比较。
5 u32 num_resources; 6 struct resource * resource; 7 8 const struct platform_device_id *id_entry; 9 10 /* MFD cell pointer */ 11 struct mfd_cell *mfd_cell; 12 13 /* arch specific additions */ 14 struct pdev_archdata archdata; 15 };

利用设备树编写的led驱动程序如下:

  1 #include <linux/module.h>
  2 #include <linux/kernel.h>
  3 #include <linux/fs.h>
  4 #include <linux/init.h>
  5 #include <linux/delay.h>
  6 #include <linux/uaccess.h>
  7 #include <asm/irq.h>
  8 #include <asm/io.h>
  9 #include <linux/of.h>
 10 #include <linux/of_device.h>
 11 #include <linux/of_platform.h>
 12 #include <linux/platform_device.h>
 13 
 14 #define S3C2440_GPA(n)  (0<<16 | n)
 15 #define S3C2440_GPB(n)  (1<<16 | n)
 16 #define S3C2440_GPC(n)  (2<<16 | n)
 17 #define S3C2440_GPD(n)  (3<<16 | n)
 18 #define S3C2440_GPE(n)  (4<<16 | n)
 19 #define S3C2440_GPF(n)  (5<<16 | n)
 20 #define S3C2440_GPG(n)  (6<<16 | n)
 21 #define S3C2440_GPH(n)  (7<<16 | n)
 22 #define S3C2440_GPI(n)  (8<<16 | n)
 23 #define S3C2440_GPJ(n)  (9<<16 | n)
 24 
 25 static int led_pin;
 26 static volatile unsigned int *gpio_con;
 27 static volatile unsigned int *gpio_dat;
 28 
 29 /* 123. 分配/设置/注册file_operations 
 30  * 4. 入口
 31  * 5. 出口
 32  */
 33 
 34 static int major;
 35 static struct class *led_class;
 36 
 37 static unsigned int gpio_base[] = {
 38     0x56000000, /* GPACON */
 39     0x56000010, /* GPBCON */
 40     0x56000020, /* GPCCON */
 41     0x56000030, /* GPDCON */
 42     0x56000040, /* GPECON */
 43     0x56000050, /* GPFCON */
 44     0x56000060, /* GPGCON */
 45     0x56000070, /* GPHCON */
 46     0,          /* GPICON */
 47     0x560000D0, /* GPJCON */
 48 };
 49 
 50 static int led_open (struct inode *node, struct file *filp)
 51 {
 52     /* 把LED引脚配置为输出引脚 */
 53     /* GPF5 - 0x56000050 */
 54     int bank = led_pin >> 16;
 55     int base = gpio_base[bank];
 56 
 57     int pin = led_pin & 0xffff;
 58     gpio_con = ioremap(base, 8);
 59     if (gpio_con) {
 60         printk("ioremap(0x%x) = 0x%x
", base, gpio_con);
 61     }
 62     else {
 63         return -EINVAL;
 64     }
 65     
 66     gpio_dat = gpio_con + 1;
 67 
 68     *gpio_con &= ~(3<<(pin * 2));
 69     *gpio_con |= (1<<(pin * 2));  
 70 
 71     return 0;
 72 }
 73 
 74 static ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *off)
 75 {
 76     /* 根据APP传入的值来设置LED引脚 */
 77     unsigned char val;
 78     int pin = led_pin & 0xffff;
 79     
 80     copy_from_user(&val, buf, 1);
 81 
 82     if (val)
 83     {
 84         /* 点灯 */
 85         *gpio_dat &= ~(1<<pin);
 86     }
 87     else
 88     {
 89         /* 灭灯 */
 90         *gpio_dat |= (1<<pin);
 91     }
 92 
 93     return 1; /* 已写入1个数据 */
 94 }
 95 
 96 static int led_release (struct inode *node, struct file *filp)
 97 {
 98     printk("iounmap(0x%x)
", gpio_con);
 99     iounmap(gpio_con);
100     return 0;
101 }
102 
103 
104 static struct file_operations myled_oprs = {
105     .owner = THIS_MODULE,
106     .open  = led_open,
107     .write = led_write,
108     .release = led_release,
109 };
110 
111 
112 static int led_probe(struct platform_device *pdev)
113 {
114     struct resource        *res;
115 
116     /* 根据platform_device的资源进行ioremap */
117     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
118     led_pin = res->start;
119 
120     major = register_chrdev(0, "myled", &myled_oprs);
121 
122     led_class = class_create(THIS_MODULE, "myled");
123     device_create(led_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
124     
125     return 0;
126 }
127 
128 static int led_remove(struct platform_device *pdev)
129 {
130     unregister_chrdev(major, "myled");
131     device_destroy(led_class,  MKDEV(major, 0));
132     class_destroy(led_class);
133     
134     return 0;
135 }
136 
137 
138 static const struct of_device_id of_match_leds[] = {
139     { .compatible = "jz2440_led", .data = NULL },
140     { /* sentinel */ }
141 };
142 
143 
144 struct platform_driver led_drv = {
145     .probe        = led_probe,
146     .remove        = led_remove,
147     .driver        = {
148         .name    = "myled",
149         .of_match_table = of_match_leds, /* 能支持哪些来自于dts的platform_device */
150     }
151 };
152 
153 
154 static int myled_init(void)
155 {
156     platform_driver_register(&led_drv);
157     return 0;
158 }
159 
160 static void myled_exit(void)
161 {
162     platform_driver_unregister(&led_drv);
163 }
164 
165 
166 
167 module_init(myled_init);
168 module_exit(myled_exit);
169 
170 
171 MODULE_LICENSE("GPL");

 在设备树中形如下面这样的写法太别扭了

reg = <S3C2410_GPF(5) 1>;  
S3C2410_GPF(5)这个地方本来定义了引脚,你非要把它当做register。看一下能否将register去掉,能不能使用另外一种方法指定引脚。
 1 // SPDX-License-Identifier: GPL-2.0
 2 /*
 3  * SAMSUNG SMDK2440 board device tree source
 4  *
 5  * Copyright (c) 2018 weidongshan@qq.com
 6  * dtc -I dtb -O dts -o jz2440.dts jz2440.dtb
 7  */
 8  
 9 #define S3C2410_GPA(_nr)    ((0<<16) + (_nr))
10 #define S3C2410_GPB(_nr)    ((1<<16) + (_nr))
11 #define S3C2410_GPC(_nr)    ((2<<16) + (_nr))
12 #define S3C2410_GPD(_nr)    ((3<<16) + (_nr))
13 #define S3C2410_GPE(_nr)    ((4<<16) + (_nr))
14 #define S3C2410_GPF(_nr)    ((5<<16) + (_nr))
15 #define S3C2410_GPG(_nr)    ((6<<16) + (_nr))
16 #define S3C2410_GPH(_nr)    ((7<<16) + (_nr))
17 #define S3C2410_GPJ(_nr)    ((8<<16) + (_nr))
18 #define S3C2410_GPK(_nr)    ((9<<16) + (_nr))
19 #define S3C2410_GPL(_nr)    ((10<<16) + (_nr))
20 #define S3C2410_GPM(_nr)    ((11<<16) + (_nr))
21 
22 /dts-v1/;
23 
24 / {
25     model = "SMDK24440";
26     compatible = "samsung,smdk2440";
27 
28     #address-cells = <1>;
29     #size-cells = <1>;
30         
31     memory@30000000 {
32         device_type = "memory";
33         reg =  <0x30000000 0x4000000>;
34     };
35 /*
36     cpus {
37         cpu {
38             compatible = "arm,arm926ej-s";
39         };
40     };
41 */    
42     chosen {
43         bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
44     };
45 
46     
47     led {
48         compatible = "jz2440_led";
49         pin = <S3C2410_GPF(5)>;
50     };
51 };
  1 #include <linux/module.h>
  2 #include <linux/kernel.h>
  3 #include <linux/fs.h>
  4 #include <linux/init.h>
  5 #include <linux/delay.h>
  6 #include <linux/uaccess.h>
  7 #include <asm/irq.h>
  8 #include <asm/io.h>
  9 #include <linux/of.h>
 10 #include <linux/of_device.h>
 11 #include <linux/of_platform.h>
 12 #include <linux/platform_device.h>
 13 
 14 #define S3C2440_GPA(n)  (0<<16 | n)
 15 #define S3C2440_GPB(n)  (1<<16 | n)
 16 #define S3C2440_GPC(n)  (2<<16 | n)
 17 #define S3C2440_GPD(n)  (3<<16 | n)
 18 #define S3C2440_GPE(n)  (4<<16 | n)
 19 #define S3C2440_GPF(n)  (5<<16 | n)
 20 #define S3C2440_GPG(n)  (6<<16 | n)
 21 #define S3C2440_GPH(n)  (7<<16 | n)
 22 #define S3C2440_GPI(n)  (8<<16 | n)
 23 #define S3C2440_GPJ(n)  (9<<16 | n)
 24 
 25 static int led_pin;
 26 static volatile unsigned int *gpio_con;
 27 static volatile unsigned int *gpio_dat;
 28 
 29 /* 123. 分配/设置/注册file_operations 
 30  * 4. 入口
 31  * 5. 出口
 32  */
 33 
 34 static int major;
 35 static struct class *led_class;
 36 
 37 static unsigned int gpio_base[] = {
 38     0x56000000, /* GPACON */
 39     0x56000010, /* GPBCON */
 40     0x56000020, /* GPCCON */
 41     0x56000030, /* GPDCON */
 42     0x56000040, /* GPECON */
 43     0x56000050, /* GPFCON */
 44     0x56000060, /* GPGCON */
 45     0x56000070, /* GPHCON */
 46     0,          /* GPICON */
 47     0x560000D0, /* GPJCON */
 48 };
 49 
 50 static int led_open (struct inode *node, struct file *filp)
 51 {
 52     /* 把LED引脚配置为输出引脚 */
 53     /* GPF5 - 0x56000050 */
 54     int bank = led_pin >> 16;
 55     int base = gpio_base[bank];
 56 
 57     int pin = led_pin & 0xffff;
 58     gpio_con = ioremap(base, 8);
 59     if (gpio_con) {
 60         printk("ioremap(0x%x) = 0x%x
", base, gpio_con);
 61     }
 62     else {
 63         return -EINVAL;
 64     }
 65     
 66     gpio_dat = gpio_con + 1;
 67 
 68     *gpio_con &= ~(3<<(pin * 2));
 69     *gpio_con |= (1<<(pin * 2));  
 70 
 71     return 0;
 72 }
 73 
 74 static ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *off)
 75 {
 76     /* 根据APP传入的值来设置LED引脚 */
 77     unsigned char val;
 78     int pin = led_pin & 0xffff;
 79     
 80     copy_from_user(&val, buf, 1);
 81 
 82     if (val)
 83     {
 84         /* 点灯 */
 85         *gpio_dat &= ~(1<<pin);
 86     }
 87     else
 88     {
 89         /* 灭灯 */
 90         *gpio_dat |= (1<<pin);
 91     }
 92 
 93     return 1; /* 已写入1个数据 */
 94 }
 95 
 96 static int led_release (struct inode *node, struct file *filp)
 97 {
 98     printk("iounmap(0x%x)
", gpio_con);
 99     iounmap(gpio_con);
100     return 0;
101 }
102 
103 
104 static struct file_operations myled_oprs = {
105     .owner = THIS_MODULE,
106     .open  = led_open,
107     .write = led_write,
108     .release = led_release,
109 };
110 
111 
112 static int led_probe(struct platform_device *pdev)
113 {
114     struct resource        *res;
115 
116     /* 根据platform_device的资源进行ioremap */
117     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
118     if (res) {
119         led_pin = res->start;
120     }
121     else {
122         /* 获得pin属性 */
123         of_property_read_s32(pdev->dev.of_node, "pin", &led_pin);
124     }
125 
126     if (!led_pin) 
127     {
128         printk("can not get pin for led
");
129         return -EINVAL;
130     }
131         
132 
133     major = register_chrdev(0, "myled", &myled_oprs);
134 
135     led_class = class_create(THIS_MODULE, "myled");
136     device_create(led_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
137     
138     return 0;
139 }
140 
141 static int led_remove(struct platform_device *pdev)
142 {
143     unregister_chrdev(major, "myled");
144     device_destroy(led_class,  MKDEV(major, 0));
145     class_destroy(led_class);
146     
147     return 0;
148 }
149 
150 
151 static const struct of_device_id of_match_leds[] = {
152     { .compatible = "jz2440_led", .data = NULL },
153     { /* sentinel */ }
154 };
155 
156 
157 struct platform_driver led_drv = {
158     .probe        = led_probe,
159     .remove        = led_remove,
160     .driver        = {
161         .name    = "myled",
162         .of_match_table = of_match_leds, /* 能支持哪些来自于dts的platform_device */
163     }
164 };
165 
166 
167 static int myled_init(void)
168 {
169     platform_driver_register(&led_drv);
170     return 0;
171 }
172 
173 static void myled_exit(void)
174 {
175     platform_driver_unregister(&led_drv);
176 }
177 
178 
179 
180 module_init(myled_init);
181 module_exit(myled_exit);
182 
183 
184 MODULE_LICENSE("GPL");


 

原文地址:https://www.cnblogs.com/-glb/p/11216214.html