[历史归档]本文原发布于 cstriker1407.info 个人博客,内容为历史存档,仅供参考。
发布时间:2016-12-13| 标题:Linux设备模型简单笔记|分类:操作系统 / linux / kernel
Linux设备模型简单笔记
- bus
- device
- driver
- class
- platform\_device
最近分析了下Linux设备管理相关的代码【linux/drivers】,本来想偷个懒,直接转一篇Linux设备模型的文章呢,结果找了半天发现还不如自己笔记呢。。哎。。越来越懒了。。
这里我们以Linux2.6.11的源码为基础,简单的分析下设备模型框架相关的代码和实现。由于代码比较多,这里只简单的笔记下主干流程代码。
bus
Linux设备驱动模型是以总线bus为基础框架的,各种设备和驱动均挂载到指定的bus上。
bus的数据类型定义:
device.h:structbus_type{char*name;structsubsystemsubsys;/* bus实际上是以subsys为基础构建的 */structksetdrivers;/* bus上可以挂载多个driver */structksetdevices;/* bus上可以挂载多个device */。。。。。 。。。。。};当添加一个新的bus时,调用函数bus_register
bus.c:/* 这里声明了bus管理子系统bus_subsys,用来管理所有的bus实例。*/decl_subsys(bus,&ktype_bus,NULL);/* 注册一个bus到kernel中,这里可以搜索下,会发现有很多地方调用了该函数 */intbus_register(structbus_type*bus){intretval;retval=kobject_set_name(&bus->subsys.kset.kobj,"%s",bus->name);if(retval)gotoout;/* 将bus实例添加到bus_subsys中 */subsys_set_kset(bus,bus_subsys);retval=subsystem_register(&bus->subsys);if(retval)gotoout;/* bus实例下管理的devices,这里也放到subsys中 */kobject_set_name(&bus->devices.kobj,"devices");bus->devices.subsys=&bus->subsys;retval=kset_register(&bus->devices);if(retval)gotobus_devices_fail;/* bus实例下管理的drivers,这里也放到subsys中 */kobject_set_name(&bus->drivers.kobj,"drivers");bus->drivers.subsys=&bus->subsys;bus->drivers.ktype=&ktype_driver;retval=kset_register(&bus->drivers);if(retval)gotobus_drivers_fail;bus_add_attrs(bus);pr_debug("bus type'%s'registered ",bus->name);return0;bus_drivers_fail:kset_unregister(&bus->devices);bus_devices_fail:subsystem_unregister(&bus->subsys);out:returnretval;}简单的分析完bus_register函数,我们以PCI总线为例,看下Linux:
#/sys/bus目录代表了bus_subsys#/sys/bus/pci目录代表了pci目录注册实例# pwd/sys/bus/pci# ls#devices drivers为两个目录,对应于bus_register函数devices drivers drivers_autoprobe drivers_probe rescan resource_alignment slots ueventdevice
分析完bus,我们来看下device,device的数据类型定义:
device.h:structdevice{。。。。。 。。。。。/* device挂在bus上,一个device只能对应一个driver, */structbus_type*bus;/* type of bus device is on */structdevice_driver*driver;/* which driver has allocated this device */。。。。。 。。。。。};当添加一个新的设备时,会调用函数device_register
core.c:/* 这里声明了device管理子系统devices_subsys,用来管理所有的devices实例。同时由于device有挂在bus上, 因此bus上也会有一个device实例,是以超链接的形式链接的。 */decl_subsys(devices,&ktype_device,&device_hotplug_ops);intdevice_register(structdevice*dev){device_initialize(dev);returndevice_add(dev);}voiddevice_initialize(structdevice*dev){/* 将device放到devices_subsys中 */kobj_set_kset_s(dev,devices_subsys);kobject_init(&dev->kobj);INIT_LIST_HEAD(&dev->node);INIT_LIST_HEAD(&dev->children);INIT_LIST_HEAD(&dev->driver_list);INIT_LIST_HEAD(&dev->bus_list);INIT_LIST_HEAD(&dev->dma_pools);}intdevice_add(structdevice*dev){structdevice*parent=NULL;interror=-EINVAL;。。。。。 。。。。。gotoError;if((error=device_pm_add(dev)))gotoPMError;/* 将device挂到bus上 */if((error=bus_add_device(dev)))gotoBusError;down_write(&devices_subsys.rwsem);。。。。。 。。。。。}bus.c:intbus_add_device(structdevice*dev){structbus_type*bus=get_bus(dev->bus);interror=0;if(bus){down_write(&dev->bus->subsys.rwsem);pr_debug("bus%s:add device%s ",bus->name,dev->bus_id);list_add_tail(&dev->bus_list,&dev->bus->devices.list);device_attach(dev);up_write(&dev->bus->subsys.rwsem);device_add_attrs(bus,dev);/* 通过超链接的形式,将device挂到bus上 */sysfs_create_link(&bus->devices.kobj,&dev->kobj,dev->bus_id);}returnerror;}在Linux上找个例子看下:
# pwd/sys/bus/xen/devices# ls -ltotal0#可以看到,这里是超链接的形式lrwxrwxrwx1root root0Dec1223:40 vbd-51712 ->../../../devices/vbd-51712 lrwxrwxrwx1root root0Dec1223:40 vbd-51728 ->../../../devices/vbd-51728 lrwxrwxrwx1root root0Dec1223:40 vif-0 ->../../../devices/vif-0driver
接着分析driver,driver数据类型定义:
device.h:structdevice_driver{char*name;/* driver也是挂在bus上 */structbus_type*bus;structsemaphoreunload_sem;structkobjectkobj;/* 一个driver可以对应多个device,比如一个USB驱动可以同时对应多个USB设备 */structlist_headdevices;structmodule*owner;。。。。。 。。。。。};当添加一个新的driver时,会调用函数driver_register
driver.c:intdriver_register(structdevice_driver*drv){INIT_LIST_HEAD(&drv->devices);init_MUTEX_LOCKED(&drv->unload_sem);returnbus_add_driver(drv);}bus.c:intbus_add_driver(structdevice_driver*drv){structbus_type*bus=get_bus(drv->bus);interror=0;if(bus){pr_debug("bus%s:add driver%s ",bus->name,drv->name);error=kobject_set_name(&drv->kobj,"%s",drv->name);if(error){put_bus(bus);returnerror;}/* 将driver挂到bus上 */drv->kobj.kset=&bus->drivers;if((error=kobject_register(&drv->kobj))){put_bus(bus);returnerror;}down_write(&bus->subsys.rwsem);driver_attach(drv);up_write(&bus->subsys.rwsem);module_add_driver(drv->owner,drv);driver_add_attrs(bus,drv);}returnerror;}那么device和driver是如何相互绑定的呢?这里简单的笔记几个函数:
bus.c:/* 尝试将一个device和一个driver绑定 */intdriver_probe_device(structdevice_driver*drv,structdevice*dev){/* 总线如果有前期方法,先过滤下看能不能绑定 */if(drv->bus->match&&!drv->bus->match(dev,drv))return-ENODEV;dev->driver=drv;/* driver如果有自己的绑定方法,也尝试绑定下 */if(drv->probe){interror=drv->probe(dev);if(error){dev->driver=NULL;returnerror;}}device_bind_driver(dev);return0;}/* 绑定device实例对应的driver */intdevice_attach(structdevice*dev){structbus_type*bus=dev->bus;structlist_head*entry;interror;/* 如果device实例已经声明了要绑定的driver了,那就没啥好说的了。*/if(dev->driver){device_bind_driver(dev);return1;}if(bus->match){/* 遍历当前bus上所有的driver,一个个尝试和device绑定,有一个成功就OK */list_for_each(entry,&bus->drivers.list){structdevice_driver*drv=to_drv(entry);error=driver_probe_device(drv,dev);if(!error)/* success, driver matched */return1;if(error!=-ENODEV&&error!=-ENXIO)/* driver matched but the probe failed */printk(KERN_WARNING "%s:probe of%s failed with error%d ",drv->name,dev->bus_id,error);}}return0;}/* 一个新的driver进来了,看下当前device有没有需要绑定的 */voiddriver_attach(structdevice_driver*drv){structbus_type*bus=drv->bus;structlist_head*entry;interror;if(!bus->match)return;list_for_each(entry,&bus->devices.list){/* 遍历当前bus上的device,如果有device还没有绑定driver,就尝试和本driver绑定下, 当前bus上所有的device都要遍历一遍。 */structdevice*dev=container_of(entry,structdevice,bus_list);if(!dev->driver){error=driver_probe_device(drv,dev);if(error&&(error!=-ENODEV))/* driver matched but the probe failed */printk(KERN_WARNING "%s:probe of%s failed with error%d ",drv->name,dev->bus_id,error);}}}class
除了bus,device,driver这种竖向结构,Linxu设备模型还有一种横向结构,class。class是将不同bus下的同一种类型的device管理起来,比如:
# pwd/sys/class/net# ls -ltotal0lrwxrwxrwx1root root0Dec1223:53 eth0 ->../../devices/vif-0/net/eth0 lrwxrwxrwx1root root0Dec1223:53 lo ->../../devices/virtual/net/loclass数据结构如下:
device.h:structclass{char*name;structsubsystemsubsys;structlist_headchildren;structlist_headinterfaces;structclass_attribute*class_attrs;structclass_device_attribute*class_dev_attrs;int(*hotplug)(structclass_device*dev,char**envp,intnum_envp,char*buffer,intbuffer_size);void(*release)(structclass_device*dev);void(*class_release)(structclass*class);};当添加一个新的class时,会调用函数class_register。
class.c:/* 这里声明了class管理子系统class_subsys,用来管理所有的class实例。*/staticdecl_subsys(class,&ktype_class,NULL);intclass_register(structclass*cls){interror;pr_debug("device class'%s':registering ",cls->name);INIT_LIST_HEAD(&cls->children);INIT_LIST_HEAD(&cls->interfaces);error=kobject_set_name(&cls->subsys.kset.kobj,"%s",cls->name);if(error)returnerror;subsys_set_kset(cls,class_subsys);/* 将class注册到class_subsys上 */error=subsystem_register(&cls->subsys);if(!error){error=add_class_attrs(class_get(cls));class_put(cls);}returnerror;}class和device通过class_device结构体产生联系。
device.h:structclass_device{structlist_headnode;structkobjectkobj;structclass*class;/* required */structdevice*dev;/* not necessary, but nice to have */void*class_data;/* class-specific data */charclass_id[BUS_ID_SIZE];/* unique to this class */};如果要绑定一个class和一个device,调用函数class_device_register。
class.c:intclass_device_register(structclass_device*class_dev){class_device_initialize(class_dev);returnclass_device_add(class_dev);}intclass_device_add(structclass_device*class_dev){。。。。。。 。。。。。。/* now take care of our own registration */if(parent){down_write(&parent->subsys.rwsem);list_add_tail(&class_dev->node,&parent->children);/* 此处循环是在添加device到class前进行前期处理 */list_for_each_entry(class_intf,&parent->interfaces,node)if(class_intf->add)class_intf->add(class_dev);up_write(&parent->subsys.rwsem);}class_device_add_attrs(class_dev);/* 在class目录下创建链接 */class_device_dev_link(class_dev);class_device_driver_link(class_dev);register_done:if(error&&parent)class_put(parent);class_device_put(class_dev);returnerror;}在分析class_device_add时可以看到代码中有一个前期处理循环,这里是用class_interface来定义的。
device.h:structclass_interface{structlist_headnode;structclass*class;int(*add)(structclass_device*);void(*remove)(structclass_device*);};假如我们需要在device加入class前需要额外处理下,就可以通过调用class_interface_register函数来注册一个回调函数。
class.c:intclass_interface_register(structclass_interface*class_intf){structclass*parent;structclass_device*class_dev;if(!class_intf||!class_intf->class)return-ENODEV;parent=class_get(class_intf->class);if(!parent)return-EINVAL;down_write(&parent->subsys.rwsem);list_add_tail(&class_intf->node,&parent->interfaces);if(class_intf->add){list_for_each_entry(class_dev,&parent->children,node)class_intf->add(class_dev);}up_write(&parent->subsys.rwsem);return0;}platform_device
最后在简单的笔记下platform_device结构体。
device.h:/* 个人认为platform_device就是一个device,不过多了一个resource管理功能。 */structplatform_device{char*name;u32 id;structdevicedev;u32 num_resources;structresource*resource;};不过platform_device有两个特点:
1 platform_device全部挂在struct bus_type platform_bus_type总线上。
2 platform_device实例在device目录下有一个共同的父设备(文件夹)struct device platform_bus 。
具体platform_device就不笔记了,可以参考platform.c代码。