zoukankan      html  css  js  c++  java
  • 使用设备树来编写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");


     

  • 相关阅读:
    WEP编码格式
    OSK VFS read数据流分析
    科学剖析濒死体验 "复生"者讲述"死"前1秒钟
    Android的开发相对于tizen的开发难度
    minix文件系统分析
    贴给小程序(1) 查找第一个0值
    Linux下的QQ
    OSK USB 驱动
    LRU算法之hash+list实现(转)
    插入排序
  • 原文地址:https://www.cnblogs.com/-glb/p/11216214.html
Copyright © 2011-2022 走看看