linux驱动模型

驱动模型
驱动模型的原理
参考文件:
E:\01-code\linux-4.1.6\linux-4.1.6\include\linux\device.h
Device
Driver
Kobject
Kset:
Platform_driver
Platform_device
Sysfs
Attribute
固件加载
可插拔设备驱动
Platform: 是一个虚拟总线
Platform device
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u32 num_resources;
struct resource *resource;

const struct platform_device_id *id_entry;
char *driver_override; /* Driver name to force a match */

/* MFD cell pointer */
struct mfd_cell *mfd_cell;

/* arch specific additions */
struct pdev_archdata    archdata;

};

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;
bool prevent_deferred_probe;
};

/**
* __platform_driver_register - register a driver for platform-level devices
* @drv: platform driver structure
* @owner: owning module/driver
*/
int __platform_driver_register(struct platform_driver *drv,
struct module *owner)
{
drv->driver.owner = owner;
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;

return driver_register(&drv->driver);

}

调用流程如下:
__platform_driver_register
|–drv->driver.bus = &platform_bus_type;
|–driver_register
|–bus_add_driver
|–driver_attach
|–__driver_attach
|–driver_probe_device
|–really_probe
|–driver_add_groups
注册函数:
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev); //初始化device相关的kobject
arch_setup_pdev_archdata(pdev);
return platform_device_add(pdev); //建立device与bus的关系,这里的总线为platform总线,在bus中增加设备。
}

platform_device_register
|–platform_device_add :add a platform device to device hierarchy
|–device_add :add device to device hierarchy.
|–device_create_file
|–device_add_attrs
|–bus_add_device: add device to bus
|–device_create_file
|–bus_probe_device :probe drivers for a new device
|–device_attach
|–__device_attach
|–driver_probe_device

Device,Driver,Bus
一、驱动注册:
platform_driver_register -》driver_register -》 bus_add_driver -》driver_attach -》__driver_attach -》driver_probe_device -》really_probe
1. 在driver_register函数中,会通过driver_find函数根据struct platform_driver.driver.name和drv->bus,即platform_bus_type来判断此name是否已经有注册。
假如没有注册就通过bus_add_driver函数把驱动添加到bus上, 通过创建struct driver_private *priv; 把driver的对应的kset指向bus的 priv->kobj.kset = bus->p->drivers_kset; 驱动与设备和总线的关系主要就是通过struct driver_private这个结构体。在driver_attach函数中会遍历整个bus上的设备,并在__driver_attach函数中会判断设备和驱动是否匹配。
匹配的话就通过driver_probe_device来probe。通过really_probe来真正实现probe操作。在really_probe中,首先bind pinctrl,然后建立sysfs文件节点。然后执行probe操作。
在probe操作中,首先判断设备的总线probe是否存在,假如存在就执行bus的probe操作;然后判断driver的probe函数指针是否存在,假如存在就执行driver的probe操作。

二、设备注册
platform_device_register -》platform_device_add -》device_add -》
说明:在platform_device_register函数中主要做两件事,
第一件事是device_initialize,初始化device的kobj以及device的属于全局的devices_kset,初始化device的PM操作。
第二件事是通过platform_device_add函数把platform_device增加到设备树中。主要是设置该设备属于什么总线,该设备的父设备。插入设备的resource。然后通过device_add来增加新的设备。
然后通过device和其父设备的关系,建立设备的kobject和父设备的kobject关系。创建设备相关的sysfs文件,然后把设备添加到对应的bus上。然后通过bus_probe_device来probe device. probe首先是判断总线的驱动是否支持drivers_autoprobe,假如支持就执行device_attach操作。

三、总线的注册
1. platform总线的注册:platform bus是一类虚拟的总线,可以把一些常见的外设当做platform device,挂载platform bus上,例如,RTC, WATCHDOG, SPI, IIC, LCD, touch等设备。除了platform总线,此外还有一些实际的物理总线:usb, pCI, SDIO, AMBA, MIPI, I2C, IDE, spi等。
platform_bus_init -》bus_register
在platform_bus_init是通过int __init platform_bus_init(void)来初始化的,是在系统初始化的过程中完成的。然后通过bus_register注册bus_register(&platform_bus_type);
在bus_register函数中,通过分配struct subsys_private结构体,然后建立该结构体中subsys, device, driver三个kset, 并建立device和driver的双链表。

四、device和driver,bus是如何建立联系的呢?
1. device使用struct device, bus使用struct bus_type结构体; driver使用struct device_driver;

首先看下truct device_driver结构体:它包含name, struct bus_type的指针变量, struct of_device_id(用于匹配device的结构体指针), probe,remove, shutdown, suspend, resuem等回调函数。
以及与PM相关的struct dev_pm_ops结构体指针。以及driver属性等成员、和私有的结构体struct driver_private *p;

struct device结构体包括如下成员:
1. struct device *parent; 当前设备的父设备
2. struct bus_type; 当前设备属于哪个总线
3. struct device_type;
4. struct device_driver; 此设备对应的driver
5. void *platform_data;
6. u32 id; device instance
7. struct class *class;
8. struct attribute_group **groups;
9. struct device_node of_node; / associated device tree node */
10. struct kobject kobj;

struct bus_type结构体主要包括如下成员:
1. const char *name; 总线的名称
2. struct device *dev_root; 根设备
3. struct device_attribute *dev_attrs; 设备属性
4. bus的回调函数:probe, match, remove, shutdown, suspend, resume, 以及PM相关的函数操作指针。
5. struct subsys_private *p; 子系统相关的私有结构体, 其中包括了subsys、device和driver的kset,以及struct class指针。
Kobject与kset
struct class结构体:设备类,是一个抽象出来的概念,常用的class有block, tty, usb, input等。
1. char *name;
2. struct class_attribute *class_attrs;
3. struct kobject *dev_kobj;
4. struct subsys_private *p;
5. 类相关的结构体

struct subsys结构体:

struct kobject结构体:
1. name:
2. struct kobject *parent;
3. struct kset *kset;
4. struct kobj_type *ktype;

struct kobj_type结构体:

struct kset结构体:
1. 包含了一个双链表
2. 包含了一个struct kobject成员
3. 包含了一个kset_uevent_ops结构体指针变量
实例分析
参考:E:\01-code\linux-4.1.6\linux-4.1.6\drivers\net\wireless\ath\wcn36xx\main.c
static int __init wcn36xx_init(void)
{
platform_driver_register(&wcn36xx_driver);
return 0;
}
static struct platform_driver wcn36xx_driver = {
.probe = wcn36xx_probe,
.remove = wcn36xx_remove,
.driver = {
.name = “wcn36xx”,
},
.id_table = wcn36xx_platform_id_table,
};
static const struct platform_device_id wcn36xx_platform_id_table[] = {
{
.name = “wcn36xx”,
.driver_data = 0
},
{}
};

Platform driver的注册:

Platform device的注册:

总结
========================== 总结
上面几个结构体的关系?
1. device与driver, bus的关系? 这三者的关系主要是通过kobject,kset来建立的。
2. kobject, kset的关系是怎样的?
每增加一个driver时都会增加一个kobject,并把这个kobject的kset指针与对应的bus的bus->p->drivers_kset关联起来。这样就初步确定了driver与bus的关系。
3. class, subsys的关系
4. sysfs 与各个结构体的关系。
Device tree的实现机制
设备树配置文件
配置文件存放在:linux\arch\arm\boot\dts\
文件格式要求
1. Compatible属性:定义系统的名称,它的组织形式为:,。Linux内核透过root结点”/”的compatible 属性即可判断它启动的是什么machine。
在.dts文件的每个设备,都有一个compatible 属性,compatible属性用户驱动和设备的绑定。compatible 属性是一个字符串的列表,列表中的第一个字符串表征了结点代表的确切设备,形式为”,”,其后的字符串表征可兼容的其他设备。
例如:
/ {
compatible = “samsung,s3c24xx”;
interrupt-parent = <&intc>;

  1. reg的组织形式为reg =

ifdef CONFIG_OF

static const struct of_device_id wlcore_sdio_of_match_table[] = {
{ .compatible = “ti,wl1271” },
{ .compatible = “ti,wl1273” },
{ .compatible = “ti,wl1281” },
{ .compatible = “ti,wl1283” },
{ .compatible = “ti,wl1801” },
{ .compatible = “ti,wl1805” },
{ .compatible = “ti,wl1807” },
{ .compatible = “ti,wl1831” },
{ .compatible = “ti,wl1835” },
{ .compatible = “ti,wl1837” },
{ }
};

下面分析: E:\01-code\linux-4.1.6\linux-4.1.6\arch\arm\boot\dts\omap3-cm-t3730.dts
行号 80 - compatible = “

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值