前言
利用设备树来使用或者编写驱动程序,需要梳理哪些概念?
如何理解驱动程序、设备树、platform、device、driver之间的关系
- 前言
- 一、总线设备驱动模型——总线、设备、驱动
- 二、从代码中看driver与device的关系
- 三、设备树的应用
- (一)设备树的解析
- (二)设备树的解析函数
 
一、总线设备驱动模型——总线、设备、驱动
首先要理解驱动程序模型是分离的,分层的。
-  所谓分离是指:硬件的资源信息(地址、中断、DMA等),与软件驱动代码是分离的。硬件的资源信息是通过设备树来描述的,设备树由驱动代码来解析。 
-  所谓分层是指:分上下两层,device和driver都注册、挂载在bus(总线)下面。 
  
-  设备device与设备树device tree相关 
-  驱动driver代码要重点理解probe函数 
-  device注册到bus的dev链表;driver注册到bus的drv链表。 
-  device与driver相匹配,是通过在drivers/base/dd.c中driver_attach函数来完成。一旦match成功,则调用driver里的probe函数。 
-  对于传统写法,match函数是直接比较 name。对于使用设备树的情况下,match函数通过 platform_driver -> driver -> of_match_table ->compatile来与设备节点做匹配。
另外,bus的注册具体参考下文:
 驱动程序分层分离概念-总线设备驱动模型
二、从代码中看driver与device的关系
以led驱动为例。
- 首先内存分配一个led设备。注意:.name与driver驱动的名字一样。
static struct platform_device led_dev = {
    .name         = "cbt_led",                           //对应的platform_driver驱动的名字
    .id       = -1,                                    //表示只有一个设备
    .num_resources    = ARRAY_SIZE(led_resource), //资源数量,ARRAY_SIZE()函数:获取数量
    .resource     = led_resource,  //资源数组led_resource
    .dev = { 
        .release = led_release,   //释放函数,必须向内核提供一个release函数, 、
                                //否则卸载时,内核找不到该函数会报错
    },
};
- 分配设置一个led的驱动driver
struct platform_driver led_drv = {
    .probe      = led_probe,
    .remove     = led_remove,
    .driver     = {
        .name   = "cbt_led",
    }
};
- 编写probe函数
- 传统方法是:在平台drv中有这个名字(“cbt_led”),来和平台dev中的名字做匹配的,一旦匹配,则调用平台drv中的probe函数。
- 设备树是:通过 compatile来与设备节点做匹配。
- 接下来编写probe函数。
static const struct of_device_id of_match_leds[] = {
    { .compatible = "cbt4412_led", .data = NULL },
    { /* sentinel */ }
};
 
struct platform_driver led_drv = {
    .owner = THIS_MODULE,
    .open  = led_open,
    .unlocked_ioctl = led_unlocked_ioctl,
    .release = led_release,
    .driver     = {
        .name   = "cbt_led",
        .of_match_table = of_match_leds, /* 能支持哪些来自于dts的platform_device */
    }
};
static int led_probe(struct platform_device *pdev)
{
    struct resource     *res;
    /*** 
     * platform_get_resource解析DTS
     * 根据platform_device的资源进行ioremap 
     * 参数 0代表IORESOURCE_MEM这类资源中的第0个,
     * 把他取出来后res->start,代表的就是引脚了
     * ***/
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    led_pin = res->start;
    major = register_chrdev(0, "cbt_led", &cbt_led_ops);
    led_class = class_create(THIS_MODULE, "cbt_led");
    device_create(led_class, NULL, MKDEV(major, 0), NULL, "cbt_led"); /* /dev/cbt_led */
    
    return 0;
}
参考资料:
 字符设备驱动-总线设备驱动模型写法
 设备树之字符设备驱动_LED
三、设备树的应用
(一)设备树的解析
dts中的节点信息终将经过dts -> dtb -> device_node -> platform_device这样的过程。但并不是所有device_node都会被转换成platform_device,需满足:
- 根节点下含有compatile属性的子节点
- 如果一个结点的compatile属性含有这些特殊的值(“simple-bus”,“simple-mfd”,“isa”,“arm,amba-bus”)之一, 那么它的子结点(需含compatile属性)也可以转换为platform_device
- i2c, spi等总线节点下的子节点, 应该交给对应的总线驱动程序来处理, 它们不应该被转换为platform_device
(二)设备树的解析函数
假设节点信息定义如下:
test_nod@12345678{
        compatible = "cbt,led";
        reg = <0x106E0020 0x4>;
        testprop,mytest;
        test_list_string = "red led", "blue led";
        interrupt-parent = <&gpv2>;
        interrupts = <1 4>;
};
1、从节点路径获取信息
struct device_node *np = NULL;
np = of_find_node_by_path("/test_nod@12345678");
printk("node name = %s\n", np->name);
2、获取到节点中的属性
    struct property *prop = NULL;
    prop = of_find_property(np, "compatible",NULL);
    printk("compatible value = %s\n", prop->value);
3、读取到属性中的整数的数组
    u32 regdata[U32_DATA_LEN];
    int ret,i=0;
    
    ret = of_property_read_u32_array(np, "reg", regdata, U32_DATA_LEN);
    printk("regdata[%d] = 0x%x\n", i,regdata[i]);
4、读取到属性中的字符串的数组
const char *pstr[3];
int i=0;
ret = of_property_read_string_index(np, "test_list_string", i, &pstr[i]);
printk("pstr[%d] = %s\n", i,pstr[i]);
5、属性的值为空,实际可以用于设置标志
if(of_find_property(np, "testprop,mytest", NULL))
{
        is_good = 1;
        printk("is_good = %d\n", is_good);
}
6、获取到中断的号码
irqno = irq_of_parse_and_map(np, 0);
printk("-----irqno = %d\n", irqno);
//验证中断号码是否有效
ret = request_irq(irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, 
            "key_irq", NULL);
if(ret)
{
    printk("request_irq error\n");
    return -EBUSY;
}
参考文献:
 设备树 - 应用实例



















