linux下驱动学习—平台总线 (3)

发布时间:2024年01月11日

platform 设备驱动

在设备驱动模型中, 引入总线的概念可以对驱动代码和设备信息进行分离。但是驱动中总线的概念是软件层面的一种抽象,与我们SOC中物理总线的概念并不严格相等:

物理总线:芯片与各个功能外设之间传送信息的公共通信干线,其中又包括数据总线、地址总线和控制总线,以此来传输各种通信时序。

驱动总线:负责管理设备和驱动。制定设备和驱动的匹配规则,一旦总线上注册了新的设备或者是新的驱动,总线将尝试为它们进行配对。

一般对于I2C、SPI、USB这些常见类型的物理总线来说,Linux内核会自动创建与之相应的驱动总线,因此I2C设备、SPI设备、 USB设备自然是注册挂载在相应的总线上。但是,实际项目开发中还有很多结构简单的设备,对它们进行控制并不需要特殊的时序。 它们也就没有相应的物理总线,比如led、rtc时钟、蜂鸣器、按键等等,Linux内核将不会为它们创建相应的驱动总线。

为了使这部分设备的驱动开发也能够遵循设备驱动模型,Linux内核引入了一种虚拟的总线——平台总线(platform bus)。 平台总线用于管理、挂载那些没有相应物理总线的设备,这些设备被称为平台设备,对应的设备驱动则被称为平台驱动。 平台设备驱动的核心依然是Linux设备驱动模型,平台设备使用platform_device结构体来进行表示,其继承了设备驱动模型中的device结构体。 而平台驱动使用platform_driver结构体来进行表示,其则是继承了设备驱动模型中的device_driver结构体。

platfrom_device结构体

内核使用platform_device结构体来描述平台设备,结构体原型如下:
platform_device结构体在内核源码/include/linux/platform_device.h中

struct platform_device {
     const char *name;
     int id;
     struct device dev;
     u32 num_resources;
     struct resource *resource;
     const struct platform_device_id *id_entry;
     /* 省略部分成员 */
 };

name: 设备名称,总线进行匹配时,会比较设备和驱动的名称是否一致;
id: 指定设备的编号,Linux支持同名的设备,而同名设备之间则是通过该编号进行区分; dev:Linux设备模型中的device结构体,linux内核大量使用了面向对象思想,platform_device通过继承该结构体可复用它的相关代码,方便内核管理平台设备;
num_resources: 记录资源的个数,当结构体成员resource存放的是数组时,需要记录resource数组的个数,内核提供了宏;

platform_driver结构体

内核中使用platform_driver结构体来描述平台驱动,结构体原型如下所示:
platform_driver结构体在内核源码/include/platform_device.h中

struct platform_driver {

    int (*probe)(struct platform_device *);
    int (*remove)(struct platform_device *);
    struct device_driver driver;
    const struct platform_device_id *id_table;

};

probe:函数指针,驱动开发人员需要在驱动程序中初始化该函数指针,当总线为设备和驱动匹配上之后,会回调执行该函数。我们一般通过该函数,对设备进行一系列的初始化。
remove:函数指针,驱动开发人员需要在驱动程序中初始化该函数指针,当我们移除某个平台设备时,会回调执行该函数指针,该函数实现的操作,通常是probe函数实现操作的逆过程。
driver:Linux设备模型中用于抽象驱动的device_driver结构体,platform_driver继承该结构体,也就获取了设备模型驱动对象的特性;
id_table:表示该驱动能够兼容的设备类型。

现在,如果写platform驱动,第一反应应该是先写设备树,就不要想着platform_device了,就当它被设备树替代了吧。

简介

在代码中添加of_device_id类型结构体数组。

platform驱动是如何被加载的。就是通过.compatible匹配完成的。如下所示

static const struct of_device_id my_of_ids[] = {
                {.compatible = "my_platform"},
                {},
};
MODULE_DEVICE_TABLE(of,my_of_ids);

##注意两点:

1.这个MODULE_DEVICE_TABLE宏一定不能忘记。不写不会报错。但是匹配不到。

2.my_of_id是一个数组,且最后一项必须空值。否则一定出错。

修改设备树

在根节点下添加这个东西,简单就是美呀。

 hehe{
        compatible = "my_platform";
    };

    MODULE_DEVICE_TABLEmy_of_ids注册到一个总线设备表中,如果设备树中有与它的compatible一致的节点,就是两个字符串相同,就匹配成功。platform_driver的probe就会被调用。没有匹配成功,platform_driver的probe就不会被调用。我们这里都是"my_platform"字符串,所以肯定匹配成功。

源码

    在代码里我故意将driver的name("hello pd")和设备树的节点("hehe")写的不一样,主要是说明以下几点。

1.这两个东西可以不同。

2.根据后面的测试结果可知,sys目录出现的是driver的name属性值,而不是设备树的"hehe"。

3.driver的name值和设备树的compatible的值也没有关系。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/time.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
 
 
static const struct of_device_id my_of_ids[] = {
                {.compatible = "my_platform"},
                {},
};
 
MODULE_DEVICE_TABLE(of,my_of_ids);
static int my_probe(struct platform_device *device)
{
        printk("probe\n");
        return 0;
}
static int my_remove(struct platform_device *device)
{
        printk("remove\n");
        return 0;
}
static struct platform_driver my_platform_driver = {
                .probe = my_probe,
                .remove = my_remove,
                .driver = {
                                .name = "hello pd",
                                .of_match_table = my_of_ids,
                                .owner = THIS_MODULE,
                },
};
 
module_platform_driver(my_platform_driver);
 
MODULE_LICENSE("GPL");

测试

root@host:/server# insmod helloworld.ko
[  301.880475] probe
root@host:/server# 

匹配成功,probe函数执行。

那么.driver的name属性是干什么的?这是容易混淆的地方,看看下面,这个name属性是sys目录下的驱动的名字。位于/sys/bus/platform/drivers下。

root@host:/sys/bus/platform/drivers# ls -ls
 0 drwxr-xr-x    2 root     root             0 Jan  1 05:41 hello pd
/*略*/

从下面可以看出,name 字符串中间有空格是很不好的习惯。加了也没错。

root@host:/sys/bus/platform/drivers# ls hello\ pd/
bind    hehe    module  uevent  unbind
root@host:/sys/bus/platform/drivers#

从这里的结果可知,"hehe"作为"hello pd"的子文件夹出现

遍历hehe文件夹

root@host:/sys/bus/platform/drivers/hello pd# ls hehe
driver           modalias         power            uevent
driver_override  of_node          subsystem

再看of_node文件夹

root@host:/sys/devices/platform/hehe# ls -ls
total 0
     0 lrwxrwxrwx    1 root     root             0 Jan  1 06:13 driver -> ../../../bus/platform/drivers/hello pd
     0 -rw-r--r--    1 root     root          4096 Jan  1 05:36 driver_override
     0 -r--r--r--    1 root     root          4096 Jan  1 05:36 modalias
     0 lrwxrwxrwx    1 root     root             0 Jan  1 05:36 of_node -> ../../../firmware/devicetree/base/hehe
     0 drwxr-xr-x    2 root     root             0 Jan  1 05:36 power
     0 lrwxrwxrwx    1 root     root             0 Jan  1 05:36 subsystem -> ../../../bus/platform
     0 -rw-r--r--    1 root     root          4096 Jan  1 05:36 uevent
root@host:/sys/devices/platform/hehe#

of_node被链接到了…/…/…/firmware/devicetree/base/hehe也就是/sys/firmware/devicetree/base/hehe目录,从这里得到一个信息,/sys/firmware/devicetree里面是设备树。在目录只有一个base目录

在该base目录下有如下内容,他们都是设备树的根节点的子节点。如果想知道设备树里有什么就来这里找。

root@host:/sys/firmware/devicetree/base# ls
#address-cells                 model
#size-cells                    myled
aliases                        name
chosen                         pxp_v4l2
clocks                         regulators
compatible                     reserved-memory
cpus                           sii902x-reset
hehe                           soc
interrupt-controller@00a01000  spi4
leds                           wsinput
memory                         wskey
root@host:/sys/firmware/devicetree/base#
文章来源:https://blog.csdn.net/weixin_45209331/article/details/135451282
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。