使用设备树来编写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\n", 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)\n", 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\n", 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)\n", 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\n"); 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");

 



 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM