Linux驱动:platform总线设备解析
背景
嵌入式系统中有很多的物理总线:I2c、SPI、USB、uart、PCIE、APB、AHB
linux从2.6起就加入了一套新的驱动管理和注册的机制platform平台总线,是一条虚拟的总线,并不是一个物理的总线。
相比 PCI、USB,它主要用于描述SOC上的片上资源。platform 所描述的资源有一个共同点:在CPU 的总线上直接取址。
平台设备会分到一个名称(用在驱动绑定中)以及一系列诸如地址和中断请求号(IRQ)之类的资源。
设备用platform_device表示,驱动用platform_driver进行注册。
与传统的bus/device/driver机制相比,platform由内核进行统一管理,在驱动中使用资源,提高了代码的安全性和可移植性
三个重要的数据结构:bus_type、platform_device、platform_driver
其中,bus_type表示总线的类型,bus_type 中有个成员name 是用来指示bus类型的名字比如“platform”、"i2c"等。
每个总线类型都定义一个实体,然后会通过bus_register进行注册。如:
platform总线:
i2c总线:
下面,来看一下三个结构体定义:
bus_type
struct bus_type {
const char *name;
struct bus_attribute *bus_attrs;
struct device_attribute *dev_attrs;
struct driver_attribute *drv_attrs;
int (*match)(struct device *dev, struct device_driver *drv);//这个就是总线设备和驱动匹配函数
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm;
struct bus_type_private *p;
};
其中,要注意的是match成员,这个就是将来安装驱动的时候用来跟设备匹配的函数。该接口已经再platform.c中实现
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* match against the id table first */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);//实际上就是对比名字是否相等
}
另外就是probe接口和remove接口,用来注册和卸载bus的时候调用。
platform_device
struct platform_device {
const char * name; // 平台总线下设备的名字
int id;
struct device dev; // 所有设备通用的属性部分
u32 num_resources; // 设备使用到的resource的个数
struct resource * resource; // 设备使用到的资源数组的首地址
const struct platform_device_id *id_entry; // 设备ID表
/* arch specific additions */
struct pdev_archdata archdata; // 自留地,用来提供扩展性的
};
需要注意的时name ,表示设备的名字,在/sys/bus/platform/ 下会显示,其次用来跟驱动中的driver.name进行match。
另外需要注意的是:
num_resource:用来记录资源数量
resource: 指向资源结构体数组的首地址
dev:这是device 结构体实体,通常会在驱动中通过platform_device结构体传参,然后间接访问该结构体成员。比如:
上面的platform_data可以指向用户定义的设备属性,资源等信息。
platform_driver
struct platform_driver {
int (*probe)(struct platform_device *);//探测一个设备
int (*remove)(struct platform_device *);//移除一个设备
void (*shutdown)(struct platform_device *);//关掉一个设备
int (*suspend)(struct platform_device *, pm_message_t state);//挂起设备,跟电源管理有关
int (*resume)(struct platform_device *);//恢复设备,跟电源管理相关
struct device_driver driver;// 所有设备共用的一些属性
const struct platform_device_id *id_table;// 设备ID表,可以有多个设备公用一个驱动
};
其中probe就是安装驱动时,通过platform总线找到(match)对应的设备后,调用的探测函数。在该函数内部可以进行设备资源获取,设置等操作。
另外,里面包含了struct device_driver driver成员,driver中的name就是用来跟platform_device 中的name进行match的材料。
注册接口
1)注册驱动platform_device_register
/**
* platform_device_register - add a platform-level device
* @pdev: platform device we're adding
*/
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
arch_setup_pdev_archdata(pdev);
return platform_device_add(pdev);
}
2) 注册设备platform_driver_register
#define platform_driver_register(drv) \
__platform_driver_register(drv, THIS_MODULE)
注册流程
platform_device定义和注册:
1 #include <linux/init.h>
2 #include <linux/module.h>
3 #include <linux/platform_device.h>
4 #include <linux/ioport.h>
5
6 static void hello_release(struct device *dev)
7 {
8 return;
9 }
10 static struct platform_device hello_device =
11 {
12 .name = "hello",
13 .id = -1,
14 .dev.release = hello_release,
15 };
16
17
18 static int hello_init(void)
19 {
20 printk("hello_init \n");
21 return platform_device_register(&hello_device);
22
23 }
24 static void hello_exit(void)
25 {
26 printk("hello_exit \n");
27 platform_device_unregister(&hello_device);
28 return;
29 }
30 MODULE_LICENSE("GPL");
31 module_init(hello_init);
32 module_exit(hello_exit);
platform_driver 定义和注册:
1 #include <linux/init.h>
2 #include <linux/module.h>
3 #include <linux/platform_device.h>
4 #include <linux/ioport.h>
5
6 static int hello_probe(struct platform_device *pdev)
7 {
8 printk("match ok \n");
9 return 0;
10 }
11 static int hello_remove(struct platform_device *pdev)
12 {
13 printk("hello_remove \n");
14 return 0;
15 }
16 static struct platform_driver hello_driver =
17 {
18 .probe = hello_probe,
19 .driver.name = "helle",
20 .remove = hello_remove,
21 };
22 static int hello_init(void)
23 {
24 printk("hello_init \n");
25 return platform_driver_register(&hello_driver);
26 }
27 static void hello_exit(void)
28 {
29 printk("hello_exit \n");
30 platform_driver_unregister(&hello_driver);
31 return;
32 }
33 MODULE_LICENSE("GPL");
34 module_init(hello_init);
35 module_exit(hello_exit);