2012年(11)
分类:
2012-05-25 16:47:49
原文地址:Linux设备模型--总线、驱动、设备、设备类 作者:迷墙人
前面的文章分析设备模型中的最基础部分,下面就要更跨入现实,看看如何在这些基础之上构 建整个设备驱动子系统。谈到设备驱动,总会涉及到三个概念:总线、驱动、设备。而在Linux中,为了便于用户管理一些功能不同但是使用方式却很接近的设 备,开发者们定义了一个设备类的概念。
总线
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 subsys_private *p; }; |
@name:总线名称
@bus_attrs:总线默认属性
struct bus_attribute { struct attribute attr; ssize_t (*show)(struct bus_type *bus, char *buf); ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count); }; |
@dev_attrs:所有挂载设备默认属性
struct device_attribute { struct attribute attr; ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf); ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); }; |
@drv_attrs:所有挂载驱动默认属性
struct driver_attribute { struct attribute attr; ssize_t (*show)(struct device_driver *driver, char *buf); ssize_t (*store)(struct device_driver *driver, const char *buf, size_t count); }; |
以上的属性是该总线所有设备或者驱动通用的,
@match:在有新设备或者新驱动挂载到本总线上时,该函数会被调用多次以匹配设备以及相应的驱动。一般这种匹配很简单,都是比较一下设备和驱动的名字,因此保证设备和相应的驱动具有相同的名字是很重要的。
@uevent:总线通过这个函数向uevent事件中添加环境变量。
@probe:检测总线上的驱动是否支持这个设备,如果支持则将使用match函数进行匹配。
@remove:移除设备
@shutdown:关闭设备,内部调用device_driver中的shutdown函数
@suspend:挂起设备
@resume:重新启动设备
@pm:电源管理相关函数。
@p:总线私有数据。
struct subsys_private { struct kset subsys; struct kset *devices_kset; struct kset *drivers_kset; struct klist klist_devices; struct klist klist_drivers; struct blocking_notifier_head bus_notifier; unsigned int drivers_autoprobe:1; struct bus_type *bus; struct list_head class_interfaces; struct kset glue_dirs; struct mutex class_mutex; struct class *class; }; |
@@subsys:内嵌的kset用于定义本子系统,在/sys/bus目录下可以看到很多目录如pci、i2c、spi等,这些目录就对应此处kset内嵌的kobject结构体。在后面分析相关函数时
@@devices_kset、@@drivers_kset:如果你进入目录/sys/bus/pci,就会发现其中包含两个目录:devices和 drivers。这里的两个kset就与之对应。在驱动模型中,总线上所有设备对应的kobject结构体都属于devices_kset指向的 kset,同理,所有驱动对应的kobject结构体都属于drivers_kset指向的结构体。这里之所以没有使用内嵌kset结构体的方式,我才原 因大体有两个,一是上述两个kset都会作为subsys的字节点,如果内嵌的话对于理解有困难。第二个原因我认为才是最根本的,因为后面的驱动也是采用 这种相关信息和核心设备模型分开管理的方式,这样既增强了模块之间的独立性,又便于理解。
@@klist_devices、@@klist_drivers:这是两个klist链表的头节点,链表中的元素分别代表driver_private 和device。以上四个成员变量构成了两个连个层次,一个是按照设备模型构建的层次,一个是私有数据所构建的层次。
@@bus_notifier:用于实现内核通知机制。有的子系统可能对总线状态变化感兴趣,这个成员变量就是用于通知内核中的其他子系统总线状态发生了变化。
@@drivers_autoprobe:
@@bus: 指向相关联的struct bus_type。
和设备类相关:
@@class_interfaces:所提供的设备类接口,包括两个指针,分别指向添加设备和删除设备的函数。
@@glue_dirs:和sysfs文件系统相关。
@@class_mutex:锁,用于实现访问相对应设备类时的互斥性。
@@class:所对应的设备类
设备类
struct class { const char *name; struct module *owner; struct class_attribute *class_attrs; struct device_attribute *dev_attrs; struct bin_attribute *dev_bin_attrs; struct kobject *dev_kobj; int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env); char *(*devnode)(struct device *dev, mode_t *mode); void (*class_release)(struct class *class); void (*dev_release)(struct device *dev); int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); const struct kobj_ns_type_operations *ns_type; const void *(*namespace)(struct device *dev); const struct dev_pm_ops *pm; struct subsys_private *p; }; |
@name: 设备类的名称
@owner:所属模块
@class_attrs:本设备类的默认属性
@dev_attrs:本设备类下所有设备的默认属性
@dev_bin_attrs:本设备类下所有设备的默认二进制属性
@dev_kobj:本设备类对应的kobject结构体
@dev_uevent:用于向用户空间发送uevent事件时添加环境变量。
@devnode:和devtmpfs相关。
@class_release:释放本设备类对象时的回调函数
@dev_release:释放本设备类下设备对象时的回调函数
@suspend:挂起设备
@resume:重新运行设备
@ns_type:
@pm:电源管理相关函数指针
@p:私有数据,只有设备驱动核心部分可以使用这些数据。
驱动
struct device_driver { const char *name; struct bus_type *bus; struct module *owner; const char *mod_name; /* used for built-in modules */ bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ const struct of_device_id *of_match_table; 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 attribute_group **groups; const struct dev_pm_ops *pm; struct driver_private *p; }; |
@name:驱动名称
@bus:所挂载的总线
@owner:所属模块
@mod_name:模块名称
@suppress_bind_attrs:
@of_device_id:指向一个静态数组,数组元素为所支持的设备ID,
@probe:检测本驱动是否支持某个设备
@remove:设备移除时调用
@shutdown:关闭某个设备
@suspend:挂起某个设备
@resume:继续运行某个设备
@groups:属性组,这些属性是驱动可以导出的,并且与任何特定设备都无关的。
@pm:电源管理操作指针
@p:驱动私有数据,这些数据仅能被驱动模型核心部分访问。
struct driver_private { struct kobject kobj; struct klist klist_devices; struct klist_node knode_bus; struct module_kobject *mkobj; struct device_driver *driver; }; |
@@kobj:内嵌的kobject结构体
@@klist_devices:驱动管理的所有设备组成一个klist,此为头节点。
@@knode_bus:所有的总线组成klist双向链表,这是本驱动对应的链表节点。
@@mkobj:所属模块对应的内核对象结构,用于驱动模型(内嵌kobject)。
@@driver:所关联的设备驱动
设备
struct device { struct device *parent; struct device_private *p; struct kobject kobj; const char *init_name; /* initial name of the device */ const struct device_type *type; struct mutex mutex; /* mutex to synchronize calls to * its driver. */ struct bus_type *bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */ void *platform_data; /* Platform specific data, device core doesn't touch it */ struct dev_pm_info power; struct dev_power_domain *pwr_domain; #ifdef CONFIG_NUMA int numa_node; /* NUMA node this device is close to */ #endif u64 *dma_mask; /* dma mask (if dma'able device) */ u64 coherent_dma_mask;/* Like dma_mask, but for alloc_coherent mappings as not all hardware supports 64 bit addresses for consistent allocations such descriptors. */ struct device_dma_parameters *dma_parms; struct list_head dma_pools; /* dma pools (if dma'ble) */ struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */ /* arch specific additions */ struct dev_archdata archdata; struct device_node *of_node; /* associated device tree node */ dev_t devt; /* dev_t, creates the sysfs "dev" */ spinlock_t devres_lock; struct list_head devres_head; struct klist_node knode_class; struct class *class; const struct attribute_group **groups; /* optional groups */ void (*release)(struct device *dev); }; |
@parent:父设备节点,通常为总线控制器。
@kobj:内嵌的kobject节点
@init_name:设备初始名称
@type:设备类行,类似kobj_type
struct device_type { const char *name; const struct attribute_group **groups; int (*uevent)(struct device *dev, struct kobj_uevent_env *env); char *(*devnode)(struct device *dev, mode_t *mode); void (*release)(struct device *dev); const struct dev_pm_ops *pm; }; |
@@name:类型名称
@@attribute_group:属性组
@@uevent:发送uevent事件时添加环境变量
@@devnode:
@@release:释放设备
@@pm:电源管理操作
@mutex:用于串行化对设备驱动的访问。
@bus:所挂载的总线
@driver:所对应的设备驱动
@platform_data:平台相关数据
@power:和电源管理相关的信息
@pwr_domain:电源管理相关的操作
@numa_node:NUMA节点,与机器内存模型相关。
DMA相关
@dma_mask:
@coherent_dma_mask:
@dma_pools:
@archdata:
@of_node:设备数节点,of代表Open Firmware.
@devres_lock:用于保护设备资源列表
@devres_head:设备资源列表
@knode_class:设备类所组成链表的对应节点
@class:所属设备类
@groups:属性组
@release:设备释放函数
@p:私有数据
struct device_private { struct klist klist_children; struct klist_node knode_parent; struct klist_node knode_driver; struct klist_node knode_bus; void *driver_data; struct device *device; }; |
@@klist_children:所有的子设备节点组成的链表的头节点
@@knode_parent:与父节点中klist_children构成链表
@@knode_dirver:与设备驱动中的klist_devices构成链表
@@knode_bus:与总线的klist_devices构成链表
@@driver_data:驱动相关的数据
@@device:相关联的设备