LDM linux设备模型
《Linux设备驱动开发》读书笔记
LDM数据结构
LDM上层依赖项
- 总线
struct bus_type
- 设备驱动程序
struct device_driver
- 设备
struct device
attribute
属性
总线
- 设备和处理器之间的通道,是所有该类设备的父设备
I2C
总线,I2C
设备挂载到I2C
总线上,I2C
总线提供协议和一些通用性内容
struct bus_type {
const char *name; /*总线名称*/
const char *dev_name;
struct device *dev_root;
struct device_attribute *dev_attrs; /* use dev_groups instead */
const struct attribute_group **bus_groups; /*总线属性*/
const struct attribute_group **dev_groups; /*设备属性*/
const struct attribute_group **drv_groups; /*驱动属性*/
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 (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm;
const struct iommu_ops *iommu_ops;
struct subsys_private *p;
struct lock_class_key lock_key;
}
-
参数
match
让总线确定某个设备能否匹配指定的驱动
例如设备树中table
添加“I2C”
probe
匹配完成 调用该函数
dev_groups
总线上的设备的默认属性
bus_group
保存一套默认属性 -
总线除了定义
bud_type
之外,还需要定义总线特有的驱动程序和设备- 分别拓展通用struct device_driver和struct device
- 例如:
struct mybus_device; struct mybus_driver;
-
总线驱动程序
- 总线本身是驱动,所以也需要自己的
register
接口- 为探测匹配的物理设备分配特定的设备结构
- 依据通用的struct device和struct driver获取总线设备和总线设备驱动程序
- 初始化设备的bus和parent
- 向LDM内核注册设备
- 总线本身是驱动,所以也需要自己的
-
总线管理的列表
- 添加到总线的设备列表
- 添加到总线的驱动程序列表
- 总线提供给
device
的核心接口函数:device
注册和device_driver
注册(alloc
,register
)
mybus_device_alloc mybus_device_register
-
NOTE
I2C
设备需要挂载到I2C
总线上,因为所有的I2C
设备底层通讯的逻辑一样,但是不同的I2C
设备会有不同功能,比如sensor
和led
;所以会有自己的driver
设备驱动程序
-
设备驱动 总线下每种设备的专属驱动程序
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 */ enum probe_type probe_type; const struct of_device_id *of_match_table; const struct acpi_device_id *acpi_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; }
-
核心函数
driver_register
用来向总线注册设备驱动的底层函数,将驱动添加到总线的驱动列表中
设备
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 */
void *driver_data; /* Driver data, set and get with
dev_set/get_drvdata */
struct dev_pm_info power;
struct dev_pm_domain *pm_domain;
#ifdef CONFIG_PINCTRL
struct dev_pin_info *pins;
#endif
#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. */
unsigned long dma_pfn_offset;
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_DMA_CMA
struct cma *cma_area; /* contiguous memory area for dma
allocations */
#endif
/* arch specific additions */
struct dev_archdata archdata;
struct device_node *of_node; /* associated device tree node */
struct fwnode_handle *fwnode; /* firmware device node */
dev_t devt; /* dev_t, creates the sysfs "dev" */
u32 id; /* device instance */
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);
struct iommu_group *iommu_group;
bool offline_disabled:1;
bool offline:1;
};
-
核心函数
device_register
函数功能为遍历驱动程序的总线列表,查找支持改设备的驱动程序,然后把设备添加到总线的设备列表中
-
整个流程
- 会先调用总线驱动程序的匹配方法(
bus_type->match
),如果匹配是该总线类型设备就会调用总线驱动的probe
方法(bus_type->probe
),给出设备和驱动程序作为参数 - 然后由总线驱动程序调用设备驱动程序的
probe
方法(driver->probe
) mybus
为例:mybus_device_register(struct mybus_device *dev)
- 设备驱动程序自己内部会调用
device_registe
,参数来自mybus_device_alloc
- 设备驱动程序自己内部会调用
- 会先调用总线驱动程序的匹配方法(
LDM实现
依赖
kobject
kobj_type
kset
kobject
设备模型的核心
- 主要用于计数以及提供设备层次和他们之间的关系
- 每个内核设备直接或者间接嵌入
kobject
属性 kobject
作用:kobject
代表了sysfs
文件系统的目录,name
就是目录名parent
指定了在sysfs
文件系统中的位置- 若
kobject
的父项为NULL
,那么kobject_add
将其父项设置为kset
;若二者都是NULL
,那么该对象将成为顶级sys
目录的一级子目录
- 若
ktype
是kobject
的属性,属性就用文件表示,放在kobject
对应的目录下
kobj_type
主要用于描述kobject
的行为
- 嵌入
kobjec
t的每个结构都需要相应的kobj_type
struct kobj_type { void (*release)(struct kobject *kobj); 释放kobject和其占用资源的函数 const struct sysfs_ops *sysfs_ops; 操作下一个属性数组的方法 struct attribute **default_attrs; 属性数组 };
- 参数
default_attrs
定义kobject
的默认属性
sysfs_ops
定义如何操作这些属性
kset
将相关的内核对象组合在一起(集合)
属性
-
属性将内核数据映射到
sysfs
中的文件中 -
可以通过属性定义
kobject
在sysfs
系统下的读写权限(o644) -
所谓的
attibute
,就是内核空间和用户空间进行交互的一种方式- 例如某个
driver
定义了一个变量,却希望用户空间程序可以修改该变量,以控制driver
的运行行为,那么就可以将该变量以sysfs attribute
的形式开放出来
- 例如某个
-
属性类型
- 普通的
attribute
struct attribute { const char *name; umode_t mode; #ifdef CONFIG_DEBUG_LOCK_ALLOC bool ignore_lockdep:1; struct lock_class_key *key; struct lock_class_key skey; #endif }
- 二进制
attribute
struct bin_attribute { struct attribute attr; size_t size; void *private; ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *, char *, loff_t, size_t); ssize_t (*write)(struct file *,struct kobject *, struct bin_attribute *, char *, loff_t, size_t); int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr, struct vm_area_struct *vma); };
struct attribute
为普通的attribute
,使用该attribute
生成的sysfs
文件,只能用字符串的形式读写- 而
struct bin_attribute
在struct attribute
的基础上,增加了read/write
等函数,因此它所生成的sysfs
文件可以用任何方式读写
- 而
- 普通的
设备模型和sysfs
linux存在一个虚拟文件系统sysfs
,内核使用它实现与用户空间直接的简单交互,内核启动后会将sysfs
自动挂载到/sys
- 上述功能通过
kobject
,kobj_type
,kset
实现kobject
某个设备/某类设备kobj_type
设备的默认属性(包括sysfs_ops)kset
kobject
的集合
- 用户空间操作内核的设备方法
- 设备虚拟化为
/dev/
下的节点,read/write/ioctl
操作 /proc
/sysfs
- 设备虚拟化为
sysfs
是非持久性虚拟文件,通过kobjec
t显示内核对象的层次结构- 设备和驱动程序的功能通过
sysfs
进行公开(给用户) kobject
显示为目录;目录下的文件称为属性,可以读写
build:/sys$ tree -L 1 . . ├── block ├── bus ├── class ├── dev //以无层次方式包含已注册的设备节点,每个节点都是/sys/device目录中真实设备的符号链接 ├── devices ├── firmware ├── fs ├── hypervisor ├── kernel ├── module └── power
kobject
被添加到sysfs
(kobject_add
),添加位置取决于kobject
的父项- 若父指针已设置,则它被添加到父目录内的子目录
- 若父指针为NULL,将其添加到kset->kobj的子目录
- 若二者都未设备,映射到/sysfs根目录
- 操作
kobject
对象时可以使用sysfs_{create|remove}_link
操作对象的符号链接,允许对象存在于多个位置- 对象被删除时,要删除相应的符号链接
- 设备和驱动程序的功能通过
sysfs文件和属性
sysfs
文件的默认设置是通过kobject
和kset
中的ktype
,kobj_type
和default_attrs
设置
除此之外可以使用除了创建自己的ktype
或kobject
来添加属性外,还可以使用当前存在的设备,驱动程序,总线和类属性
设备属性(DEVICE_ATTR)
-
除了嵌入到设备中的
kobject
的默认属性外,还可以自定义属性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); };
-
使用
DEVICE_ATTR
宏来声明#define DEVICE_ATTR(_name, _mode, _show, _store) \ struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
- 设备属性可以在该设备驱动初始化时使用
sysfs_create_file
接口将属性添加到dev->kob
中,退出时使用sysfs_remove_file
接口删除
- 设备属性可以在该设备驱动初始化时使用
除设备属性外,总线属性(BUS_ATTR),设备驱动程序属性(DRIVER_ATTR),类属性(CLASS_ATTR)使用流程和设备属性相同
- NOTE
- 之前对所有相同的
kobject/ktype
的所有属性定义一套相同的store/show
回调函数,而现在针对每个属性使用自定义回调- 参考C++类的使用,子
kobject
在继承父kobject
的基础上可以定义自己属性结构 - 设备真实使用的回调函数实际上也是每个属性自己定义的回调函数(fix me)
- 参考C++类的使用,子
- 之前对所有相同的