前面的文章分析设备模型中的最基础部分,下面就要更跨入现实,看看如何在这些基础之上构建整个设备驱动子系统。谈到设备驱动,总会涉及到三个概念:总线、驱动、设备。而在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;<br> 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:相关联的设备