本系列文章涉及sysfs文件系统的内容,如需要了解sysfs的内容,请参考之前写的sysfs相关的文档。
我们已经完成了总线、驱动模块相关接口的分析,本章我们主要对设备模块进行分析。在上面两章的分析中,我们知道注册在总线上的驱动模块对应的kobject是通过bus->p->drivers_kset汇聚至一起,且drivers_kset对应kobject是该总线上注册驱动模块的父kobject或者祖先kobject。而针对device-kobject而言,虽然其依附的bus变量定义device_kset变量(kset类型),但device_kset->kobject与device->kobject两者之间是通过sysfs 链接方式,实现两者kobject之间的绑定操作。如下为device、bus模块间kobject变量的关联,这两者之间通过sysfs link模式实现kobject的关联。
与之前两章类似,本章也分别从相关数据结构、device模块初始化、device的注册与注销接口分析这三个部分分析device模块。
device模块相关数据结构体分析
Device相关的结构体包括struct device、struct device_private、struct device_type这三个结构
体,我们接下来分析这三个结构体
struct device_private结构体分析
该结构体为device结构体的私有变量,该结构体定义如下,该结构体主要实现与bus、driver
、父device、所有子device的关联
struct device_private {
struct klist klist_children;
/*链接至父device的klist_children链表*/
struct klist_node knode_parent;
/*链接至device_driver类型变量的klist_devices链表*/
struct klist_node knode_driver;
/*链接至其所依附总线的klist_devices链表上*/
struct klist_node knode_bus;
struct list_head deferred_probe;
/*驱动数据*/
void *driver_data;
/*用于device与device_private的绑定*/
struct device *device;
};
struct device_type结构体分析
该结构体为device类型相关的结构体,包含类型的名称、类型对应属性组、该类型的uevent接口、release接口、电源管理相关ops等。如iic_client类型设备,其type为i2c_client_type,而iic_adapter类型的设备,其type为i2c_adapter_type,还有usb_device_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, umode_t *mode,
kuid_t *uid, kgid_t *gid);
void (*release)(struct device *dev);
const struct dev_pm_ops *pm;
};
struct device结构体分析
该结构体标识一个设备,其包括几方面的内容:
- 与device的关联(包括与父device、子device的关联,通过链表关联)
- 与class的关联(实现device与class的互相关联)
- 与bus的关联(实现device与bus的互相关联)
- 与driver的关联(实现device与driver的互相关联,包括两个结构体变量的关联以及两个结构体中kobject成员变量的关联)
- 与of模块的关联(主要是存储of的设备节点信息,用于设备树相关的内容)
- 存放platform相关的设备信息(主要供platform模块使用)
struct device {
/*父设备*/
struct device *parent;
/*该device的私有变量*/
struct device_private *p;
/*该设备对应的kobject*/
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.
*/
/*该device所需要注册的总线*/
struct bus_type *bus; /* type of bus device is on */
/*该设备所绑定的驱动*/
struct device_driver *driver; /* which driver has allocated this
device */
/*platform模块相关的参数,device-bus-driver模型不关心该变量*/
void *platform_data; /* Platform specific data, device
core doesn't touch it */
/*电源管理相关的内容*/
struct dev_pm_info power;
struct dev_pm_domain *pm_domain;
/*引脚配置相关的内容,可实现对该设备关联的引脚进行初始化与设置操作*/
#ifdef CONFIG_PINCTRL
struct dev_pin_info *pins;
#endif
/*numa相关的内容,本次不涉及*/
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
/*以下为dma 相关的内容*/
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 */
#ifdef CONFIG_CMA
struct cma *cma_area; /* contiguous memory area for dma
allocations */
#endif
/* arch specific additions */
struct dev_archdata archdata;
/*该变量主要用于设备树模块使用,该device_node表示dts中的节点信息,当开启OF宏时,driver会根据该device_node
中的节点名称以及driver中of相关的节点名称,进行device-driver的匹配检测操作,从而支持设备树*/
struct device_node *of_node; /* associated device tree node */
struct acpi_dev_node acpi_node; /* associated ACPI device node */
/*设备号,当为block/char设备时,需要设置该值,通过该值会实现该设备对应kobject与/sysfs/dev/char 、/sysfs/dev/block
对应的kobject的链接关联*/
dev_t devt; /* dev_t, creates the sysfs "dev" */
u32 id; /* device instance */
spinlock_t devres_lock;
struct list_head devres_head;
/*链接至系统class模块的kset的链表中*/
struct klist_node knode_class;
/*该设备所属的类*/
struct class *class;
/*该设备的默认属性组*/
const struct attribute_group **groups; /* optional groups */
/*release接口,当对该设备进行释放其kobject时,会通过dev_kobj_type的release接口进行device的释放(
该接口会调用device->release进行device的释放操作)。*/
void (*release)(struct device *dev);
struct iommu_group *iommu_group;
};
device-bus-driver结构体间的关联
上面介绍了device项目的结构体,下面我们介绍这些结构体与bus、driver模块的关联,以下为device、bus、driver三个模块相关结构体之间的关联图,我们下面详细说明下这三个模块之间的关联情况。
- bus与device之间的关联包括两部分:
- bus、device通过klist_devices,实现两个结构体通过内嵌的链表变量实现关联
- bus、device对应的kobject之间互相建立sysfs的链接,实现kobject之间的关联
- device与driver的关联,device与driver通过内嵌的klist节点以及klist_devices链表,实现结构体间的关联。
device间的关联
主要用于说明已创建的设备变量是如何关联的。它们之间通过链表进行链接
这些设备通过device->p中的klist_children、knode_parent实现父子设备之间的关联操作。它们的关联图如下所示。
device、xxx_device、kobject、sysfs之间的关联
在之前我们介绍driver模块的时候,我们也说过driver模块一般嵌入到更大的结构体中xxx_driver,实现具体类型驱动相关结构体的成员。针对device也是一样,一般也是嵌入到更大的结构体中(此处我们命名为xxx_device,如spi_device、pci_dev、i2c_client等)。
如下图即为这几个模块间的结构体之间的关联。我们具体以几个方面说明:
- 通过全局变量devices_kset,将所有创建的device变量,通过其对应的kobject链接至devices_kset中;
- device对应的kobject通过sysfs_dirent结构体的关联,实现为该device在sysfs文件系统下创建对应的目录、子目录、子文件等内容,同时通过kobj_type实现对该device属性的读写操作(show/store)
- 通过指针实现xxx_device、device之间的关联。
以上即是device模块内部结构体以及device、driver、bus、kobject、sysfs之间的关联。其实针对device相关的创建以及注册,也就是建立上述所说模块间的关联(上面没有说明与class模块的关联)。
device模块初始化
由之前的分析我们知道,所有注册的设备是汇聚至devices_kset变量的list链表下的,而devices_kset是一个全局变量,因此其应该是在设备模块初始化中进行创建。下面我们来分析下device模块的初始化接口,该接口名称为devices_init,该接口主要实现如下功能(流程图如下):
- 调用kset_create_and_add创建kset类型的全局变量devices_kset,并在sysfs文件系统的根目录下创建devices目录,并设置其kobj_type为kset_ktype;
- 调用kobject_create_and_add创建kobject类型的全局变量dev_kobj,并根据对应的kobject,在sysfs文件系统的根目录下创建dev目录;
- 调用kobject_create_and_add在dev_kobj下创建子kobject类型的全局变量sysfs_dev_block_kobj,并根据对应的kobject,在sysfs文件系统的dev/目录下block子目录
- 调用kobject_create_and_add在dev_kobj下创建子kobject类型的全局变量sysfs_dev_char_kobj,并根据对应的kobject,在sysfs文件系统的dev/目录下char子目录
以上内容即为device模块的初始化接口,其完成创建了devices_kset,以便链接所有device的kobject,同时在sysfs文件系统下完成/dev、/dev/block、/dev/char、/devices目录。其中/dev/block、/dev/char目录下主要用于创建链接目录的,其均是链接至/devices目录下相应device。
执行完devices_init接口后,dev_kobj、sysfs_dev_block_kobj、sysfs_dev_char_kobj
这三个变量的关联如下所示。
device的注册与注销接口分析
以上我们完成了相关结构体的介绍以及结构体之间的关联介绍,此处我们介绍device的注册与注销接口。
device注册接口device_register分析
该接口主要通过调用device_initialize、device_add接口实现device的初始化以及device的添加操作,我们下面分析下这两个接口。
这三个函数的调用关系如下,可以看到设备注册的主要工作由device_add接口实现。
device_add接口分析
device_add接口实现的功能即是上述结构体之间的关联,具体内容包括:
- 创建device的私有变量(即device_private类型的变量,用于与bus、driver等进行关联)
- 设置device的名称
- 调用kobject_add实现在sysfs的devices目录下创建该设备对应kobject对应的目录以及kobj_type的设置等;
- 为设备的属性,在sysfs中创建对应的文件(属性包括该设备私有属性,该设备所属class定义的通用设备默认属性、该设备所属bus定义的通用设备默认属性);
- 若该设备存在设备号,则在sysfs文件系统下的/dev目录下创建与该设备的链接,并调用devtmpfs_create_mknod,为该设备创建设备文件(若devtmpfs被编译进内核的情况)。
若devtmpfs未被编译进内核,则在该接口调用kobject_uevent,将该设备对应kobject添加的事件发送给应用层后,应用层的udev/mdev接收后,则会根据该设备的设备id属性,为其创建设备文件(通过mknod系统调用)
- 若该设备属于某一个class,则完成class与设备间的kobject的链接操作;
- 调用bus_add_device接口,完成设备与总线间通过内嵌链表成员与链表头的关联,同时完成设备与总线的kobject互相链接。
- 调用bus_probe_device,为该添加的设备,在已注册的驱动链表中进行匹配与绑定操作,这一部分的操作与上一篇介绍的driver添加时的绑定类似(不同点为driver添加时,是遍历所有已注册的设备,而device添加时,是遍历所有已注册的驱动),本章不再赘述,读者可自行跟踪代码查看。
以上即是device_add、device_register这两个接口的主要内容(电源管理等一些内容此处未做深入说明)。
device注销接口device_unregister分析
device的注销过程即是device注册的反过程,执行的操作无非是删除创建的文件及目录、取消设备驱动的绑定、取消设备与总线的关联、取消设备与class的关联等,此处不再细说。
至此完成device相关接口的分析。