最近学习了 linux设备驱动模型 相关内容,记录一下。
设备驱动模型的底层架构
1、/sys 目录下的层次结构依赖于 kobject.parent
①parent不为空,parent指向另一个kobj作为上层目录;
②parent为空,指定 kset.kobject 作为上层目录;
③如果都没有,就出现在 /sys 目录下,属于/sys顶层目录;
2、该 kobject 目录下的属性文件依赖于 kobject.ktype
例如,电源管理、执插拨事性管理、sysfs中文件读写操作(show和store)等;
3、Kobj和Kset内部都有链表,都可以作为树状结构的目录(都是通过kobj来构建的) 只不过有些kobj嵌在Kset里,分析目录结构时把Kset当成一个普通的Kobj(可以认为是Kobj的容器类或者直接子类);
相关数据结构:
struct kobject
struct kobject {
const char *name;
struct list_head entry;
struct kobject *parent;
struct kset *kset;
struct kobj_type *ktype;
struct sysfs_dirent *sd;
struct kref kref;
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
};
struct kset
struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
};
struct kobj_type
struct kobj_type {
void (*release)(struct kobject *kobj);
const struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
const void *(*namespace)(struct kobject *kobj);
};
关于kobject kset ktype这三者的关系(特别是kobject和kset如何组织出/sys下的结构目录),可参考如下两篇博客:
嵌入式Linux驱动笔记(十六)------设备驱动模型(kobject、kset、ktype)
Linux设备驱动之Kobject、Kset
目录结构主要关注kobj中的parent,源码中有相关的说明:
kobject_add
/**
* kobject_add - the main kobject add function
* @kobj: the kobject to add
* @parent: pointer to the parent of the kobject.
* @fmt: format to name the kobject with.
*
* The kobject name is set and added to the kobject hierarchy in this
* function.
*
* If @parent is set, then the parent of the @kobj will be set to it.
* If @parent is NULL, then the parent of the @kobj will be set to the
* kobject associted with the kset assigned to this kobject. If no kset
* is assigned to the kobject, then the kobject will be located in the
* root of the sysfs tree.
*
* If this function returns an error, kobject_put() must be called to
* properly clean up the memory associated with the object.
* Under no instance should the kobject that is passed to this function
* be directly freed with a call to kfree(), that can leak memory.
*
* Note, no "add" uevent will be created with this call, the caller should set
* up all of the necessary sysfs files for the object and then call
* kobject_uevent() with the UEVENT_ADD parameter to ensure that
* userspace is properly notified of this kobject's creation.
*/
int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...)
{
va_list args;
int retval;
if (!kobj)
return -EINVAL;
if (!kobj->state_initialized) {
printk(KERN_ERR "kobject '%s' (%p): tried to add an "
"uninitialized object, something is seriously wrong.\n",
kobject_name(kobj), kobj);
dump_stack();
return -EINVAL;
}
va_start(args, fmt);
retval = kobject_add_varg(kobj, parent, fmt, args);
va_end(args);
return retval;
}
- If @parent is set, then the parent of the @kobj will be set to it.
- If @parent is NULL, then the parent of the @kobj will be set to the kobject associted with the kset assigned to this kobject. If no kset is assigned to the kobject, then the kobject will be located in the root of the sysfs tree.
1)入参的parent如果设置了,那么入参的kobj的parent就是它;
2)入参的parent如果为NULL,那么入参的kobj的parent就是其关联的kset中的kobj;
3)基于2),如果也没有kset关联,那么这个kobj就是顶层目录;
总线式设备驱动组织方式
1、总线 bus_type,关键是match函数和uevent函数
int (*match)(struct device * dev, struct device_driver * drv);
/* 当一个新设备或者驱动被添加到这个总线时,这个方法会被调用一次或多次,若指定的驱动程序能够处理指定的设备,则返回非零值。 */
/* 必须在总线层使用这个函数, 因为那里存在正确的逻辑,内核不知道如何为每个总线类型匹配设备和驱动程序 */
int (*uevent)(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size);
/* 在为用户空间产生热插拔事件之前,这个方法允许总线添加环境变量(参数和 kset 的uevent方法相同) */
2、设备 struct device
device_register用于向内核驱动框架注册一个设备;
通常device不会单独使用,而是被包含在一个具体设备结构体中,如struct usb_device;
3、驱动 struct device_driver
name,驱动程序的名字,很重要,经常被用来作为驱动和设备的匹配依据
probe,驱动程序的探测函数,用来检测一个设备是否可以被该驱动所管理
4、类 struct class
class的真正意义在于作为同属于一个class的多个设备的容器。也就是说,class是一种人造概念,目的就是为了对各种设备进行分类管理;
udev的使用离不开Class;
相关数据结构:
struct device
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj;
const char *init_name; /* initial name of the device */
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;
#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;
#ifdef CONFIG_OF
struct device_node *of_node;
#endif
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);
};
struct device_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 */
#if defined(CONFIG_OF)
const struct of_device_id *of_match_table;
#endif
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;
};
struct bus_type
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 bus_type_private *p;
};
struct class
struct class {
const char *name;
struct module *owner;
struct class_attribute *class_attrs;
struct device_attribute *dev_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 class_private *p;
};
具体的总线
以 “platform平台总线” 为例。
一、平台总线体系的工作流程
1、系统启动时在bus系统中注册Platform;
2、内核移植的人负责提供platform_device;
3、写驱动的人负责提供platform_driver;
4、platform的match函数发现driver和device匹配后(代码上看是Device(一个)去找对应的Driver(多个)),调用driver的probe函数来完成驱动的初始化和安装,然后设备就工作了;match方法是每种总线都会带的。
二、platform设备和驱动的注册过程
1、platform_driver_register 对应platform_driver的注册;
2、platform_device_register 对应platform_device的注册;
三、platdata的作用
1、platdata其实就是设备注册时提供的设备有关的一些数据(譬如设备对应的gpio、使用到的中断号、设备名称····)
2、这些数据在设备和驱动match之后,会由设备方转给驱动方。驱动拿到这些数据后,通过这些数据得知设备的具体信息,然后来操作设备。
3、这样做的好处是:驱动源码中不携带数据,只负责算法(对硬件的操作方法)。现代驱动设计理念就是算法和数据分离,这样最大程度保持驱动的独立性和适应性(可重用性)。
相关数据结构:
struct platform_device
struct platform_device {
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;
const struct platform_device_id *id_entry;
/* arch specific additions */
struct pdev_archdata archdata;
};
struct platform_driver
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
最后,附上visio画图
设备驱动模型-数据结构相互关系.vsdx