【Linux驱动】linux设备驱动模型

最近学习了 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值