
[历史归档]本文原发布于 cstriker1407.info 个人博客内容为历史存档仅供参考。发布时间2016-12-13 标题Linux设备模型简单笔记分类操作系统 / linux / kernelLinux设备模型简单笔记busdevicedriverclassplatform\_device最近分析了下Linux设备管理相关的代码【linux/drivers】,本来想偷个懒直接转一篇Linux设备模型的文章呢结果找了半天发现还不如自己笔记呢。。哎。。越来越懒了。。这里我们以Linux2.6.11的源码为基础简单的分析下设备模型框架相关的代码和实现。由于代码比较多这里只简单的笔记下主干流程代码。busLinux设备驱动模型是以总线bus为基础框架的各种设备和驱动均挂载到指定的bus上。bus的数据类型定义device.h:structbus_type{char*name;structsubsystemsubsys;/* bus实际上是以subsys为基础构建的 */structksetdrivers;/* bus上可以挂载多个driver */structksetdevices;/* bus上可以挂载多个device */。。。。。 。。。。。};当添加一个新的bus时调用函数bus_registerbus.c/* 这里声明了bus管理子系统bus_subsys用来管理所有的bus实例。*/decl_subsys(bus,ktype_bus,NULL);/* 注册一个bus到kernel中这里可以搜索下会发现有很多地方调用了该函数 */intbus_register(structbus_type*bus){intretval;retvalkobject_set_name(bus-subsys.kset.kobj,%s,bus-name);if(retval)gotoout;/* 将bus实例添加到bus_subsys中 */subsys_set_kset(bus,bus_subsys);retvalsubsystem_register(bus-subsys);if(retval)gotoout;/* bus实例下管理的devices这里也放到subsys中 */kobject_set_name(bus-devices.kobj,devices);bus-devices.subsysbus-subsys;retvalkset_register(bus-devices);if(retval)gotobus_devices_fail;/* bus实例下管理的drivers这里也放到subsys中 */kobject_set_name(bus-drivers.kobj,drivers);bus-drivers.subsysbus-subsys;bus-drivers.ktypektype_driver;retvalkset_register(bus-drivers);if(retval)gotobus_drivers_fail;bus_add_attrs(bus);pr_debug(bus type%sregistered ,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我们来看下devicedevice的数据类型定义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_registercore.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*parentNULL;interror-EINVAL;。。。。。 。。。。。gotoError;if((errordevice_pm_add(dev)))gotoPMError;/* 将device挂到bus上 */if((errorbus_add_device(dev)))gotoBusError;down_write(devices_subsys.rwsem);。。。。。 。。。。。}bus.c:intbus_add_device(structdevice*dev){structbus_type*busget_bus(dev-bus);interror0;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接着分析driverdriver数据类型定义device.h:structdevice_driver{char*name;/* driver也是挂在bus上 */structbus_type*bus;structsemaphoreunload_sem;structkobjectkobj;/* 一个driver可以对应多个device比如一个USB驱动可以同时对应多个USB设备 */structlist_headdevices;structmodule*owner;。。。。。 。。。。。};当添加一个新的driver时会调用函数driver_registerdriver.cintdriver_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*busget_bus(drv-bus);interror0;if(bus){pr_debug(bus%s:add driver%s ,bus-name,drv-name);errorkobject_set_name(drv-kobj,%s,drv-name);if(error){put_bus(bus);returnerror;}/* 将driver挂到bus上 */drv-kobj.ksetbus-drivers;if((errorkobject_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-driverdrv;/* driver如果有自己的绑定方法也尝试绑定下 */if(drv-probe){interrordrv-probe(dev);if(error){dev-driverNULL;returnerror;}}device_bind_driver(dev);return0;}/* 绑定device实例对应的driver */intdevice_attach(structdevice*dev){structbus_type*busdev-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*drvto_drv(entry);errordriver_probe_device(drv,dev);if(!error)/* success, driver matched */return1;if(error!-ENODEVerror!-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*busdrv-bus;structlist_head*entry;interror;if(!bus-match)return;list_for_each(entry,bus-devices.list){/* 遍历当前bus上的device如果有device还没有绑定driver就尝试和本driver绑定下 当前bus上所有的device都要遍历一遍。 */structdevice*devcontainer_of(entry,structdevice,bus_list);if(!dev-driver){errordriver_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除了busdevicedriver这种竖向结构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);errorkobject_set_name(cls-subsys.kset.kobj,%s,cls-name);if(error)returnerror;subsys_set_kset(cls,class_subsys);/* 将class注册到class_subsys上 */errorsubsystem_register(cls-subsys);if(!error){erroradd_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.cintclass_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(errorparent)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;parentclass_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代码。