尽管LDD3中说对多数程序员掌握设备驱动模型不是必要的,但对于
嵌入式Linux的底层程序员而言,对设备驱动模型的学习非常重要。
Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一个对系统结构的一般性抽象描述。换句话说,Linux设备模型提取了设备操作的共同属性,进行抽象,并将这部分共同的属性在内核中实现,而为需要新添加设备或驱动提供一般性的统一接口,这使得驱动程序的开发变得更简单了,而程序员只需要去学习接口就行了。
在内核里,有各种各样的总线,如 usb_bus_type、spi_bus_type、pci_bus_type、platform_bus_type、i2c_bus_type 等,内核通过总线将设备与驱动分离。此文,基于 Linux2.6.32.2 简单分析设备驱动模型,以后看具体的总线设备模型时会更加清晰。
设备模型是层次的结构,层次的每一个节点都是通过kobject实现的。在文件上则体现在sysfs文件系统。
关于kobkect,前面的文章已经分析过了,如果不清楚请移步 http://blog.csdn
.NET/lizuobin2/article/details/51523693
kobject 结构可能的层次结构如图:
关于 uevet mdev 前面也说过了,请参考 http://blog.csdn
.Net/lizuobin2/article/details/51534385
对于整个 设备总线驱动模型 的样子,大概如下图吧,也并不复杂。简单来说,bus 负责维护 注册进来的devcie 与 driver ,每注册进来一个device 或者 driver 都会调用 Bus->match 函数 将device 与 driver 进行配对,并将它们加入链表,
如果配对成功,调用Bus->probe或者driver->probe函数, 调用 kobject_uevent 函数设置环境变量,mdev进行创建设备节点等操作。后面,我们从
Bus driver 到 device三个部分进行详细的分析。
一、总线
内核通过 bus_register 进行 Bus 注册,那么它注册到了哪里,“根”在哪? 答案是 bus_kest
[cpp] view
plain copy
print?
-
int __init buses_init(void)
-
{
-
-
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
-
if (!bus_kset)
-
return -ENOMEM;
-
return 0;
-
}
bus 的类型为 bus_type
[cpp] view
plain copy
print?
-
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;
-
};
[cpp] view
plain copy
print?
-
struct bus_type_private {
-
struct kset subsys;
-
struct kset *drivers_kset;
-
struct kset *devices_kset;
-
struct klist klist_devices;
-
struct klist klist_drivers;
-
struct blocking_notifier_head bus_notifier;
-
unsigned int drivers_autoprobe:1;
-
struct bus_type *bus;
-
};
[cpp] view
plain copy
print?
-
int bus_register(struct bus_type *bus)
-
{
-
int retval;
-
struct bus_type_private *priv;
-
-
priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
-
-
-
priv->bus = bus;
-
-
bus->p = priv;
-
-
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
-
-
-
-
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
-
-
priv->subsys.kobj.kset = bus_kset;
-
-
priv->subsys.kobj.ktype = &bus_ktype;
-
-
priv->drivers_autoprobe = 1;
-
-
-
-
retval = kset_register(&priv->subsys);
-
-
retval = bus_create_file(bus, &bus_attr_uevent);
-
-
-
priv->devices_kset = kset_create_and_add("devices", NULL,
-
&priv->subsys.kobj);
-
-
priv->drivers_kset = kset_create_and_add("drivers", NULL,
-
&priv->subsys.kobj);
-
-
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
-
klist_init(&priv->klist_drivers, NULL, NULL);
-
-
retval = add_probe_files(bus);
-
-
retval = bus_add_attrs(bus);
-
-
return 0;
-
-
}
目前,能通过 bus_register 函数处理的工作有:
1、将 Bus 与 priv 相互建立联系
2、BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
3、设置 bus->priv->subsys(kset).kobj 的名字为 bus->name
4、设置 bus->priv->subsys(kset).kobj.kset 指向 bus_kset
5、设置 bus->priv->subsys(kset).kobj.ktype 为 bus_ktype ,提供 show store 函数
6、设置 bus->priv->drivers_autoprobe = 1;
7、注册 bus->priv->subsys(kset) 对应于图中④与⑥的关系
由于4,且没有指定bus->priv->subsys(kset).kobj.Parent,会将 bus_kest.kobj 设置为 bus->priv->subsys(kset).kobj.Parent 因此,会将bus->priv->subsys(kset).kobj.entry 加入 bus_kest 链表,且会在/sys/bus目录下创建相应的总线目录/sys/bus/$(bus->name),例如 /sys/bus/platform
8、创建 bus_attr_uevent->attr 属性文件
9、创建并注册 devices_kset ,devices_kset.kobj.parent = bus->priv->subsys.kobj ,名字为 device ,因此会创建 /sys/bus/$(bus->name)/devices
10、创建并注册 drivers_kset ,drivers_kset.kobj.parent = bus->priv->subsys.kobj ,名字为 drivers ,因此会创建 /sys/bus/$(bus->name)/drivers
11、初始化 bus->priv->klist_devices 链表
12、初始化 bus->priv->klist_drivers 链表
13、创建 bus->bus_attrs 属性文件
下面来看个例子 ,修改自LDD3 。基于Linux 2.6.32.2 内核
[cpp] view
plain copy
print?
-
-
-
-
-
-
-
extern struct device ldd_bus;
-
extern struct bus_type ldd_bus_type;
-