Linux设备驱动模型——总线、设备、驱动

总线

内核所支持的每一种总线类型都由一个bus_type描述。因为bus_type结构体中定义了struct subsystem subsys,所以总线的级别处于subsystem,即总线的父结构也一定是一个subsystem。同时bus_type结构体中也定义了struct kset drivers和struct kset devices,这两个kset的父结构都是bus_type的struct subsystem subsys。

struct bus_type {
	/**
	 * 总线类型的名称。例如"pci"
	 */
	char			* name;

	/**
	 * 与总线类型相关的kobject子系统。这些子系统并不在sysfs的顶层。
	 * 一个总线包含两个kset,分别代表了总线的驱动程序和插入总线的所有设备。
	 */
	struct subsystem	subsys;
	/**
	 * 驱动程序的kobject集合
	 */
	struct kset		drivers;
	/**
	 * 设备的kobject集合
	 */
	struct kset		devices;

	/**
	 * 指向对象的指针,该对象包含总线属性和用于导出此属性到sysfs文件系统的方法
	 */
	struct bus_attribute	* bus_attrs;
	/**
	 * 指向对象的指针,该对象包含设备属性和用于导出此属性到sysfs文件系统的方法
	 */
	struct device_attribute	* dev_attrs;
	/**
	 * 指向对象的指针,该对象包含驱动程序属性和用于导出此属性到sysfs文件系统的方法
	 */
	struct driver_attribute	* drv_attrs;

	/**
	 * 检验给定的设备驱动程序是否支持特定设备的方法.
	 * 当一个总线上的新设备或者新驱动程序被添加时,会一次或多次调用这个函数。如果指定的驱动程序能够处理指定的设备,该函数返回非0值。
	 */
	int		(*match)(struct device * dev, struct device_driver * drv);
	/**
	 * 注册设备时调用的方法
	 * 在为用户空间产生热插拨事件前,这个方法允许总线添加环境变量。
	 */
	int		(*hotplug) (struct device *dev, char **envp, 
				    int num_envp, char *buffer, int buffer_size);
	/**
	 * 保存硬件设备的上下文状态并改变设备供电状态的方法
	 */
	int		(*suspend)(struct device * dev, pm_message_t state);
	/**
	 * 改变供电状态和恢复硬件设备上下文的方法
	 */
	int		(*resume)(struct device * dev);
};

注册总线

新注册的总线被注册到sysfs/bus中

int bus_register(struct bus_type * bus)
{
	/*
	注册总线的子系统,这里将所属总线子系统的目录创建在bus_subsys下面,也就是/sysfs/bus下面
	*/
	kobject_set_name(&bus->subsys.kset.kobj, "%s", bus->name);
	subsys_set_kset(bus, bus_subsys);
		(obj)->subsys.kset.kobj.kset = &(_subsys).kset
	subsystem_register(&bus->subsys);

		/*
		注册该总线的设备kset,挂载到该总线上的所有设备都属于这个kset,并在该总线目录下创建devices目录
		*/
		kobject_set_name(&bus->devices.kobj, "devices");
		bus->devices.subsys = &bus->subsys;
		kset_register(&bus->devices);

		/*
		注册该总线的驱动kset,该总线的所有驱动都属于这个kset,并在该总线目录下创建drivers目录
		*/
		kobject_set_name(&bus->drivers.kobj, "drivers");
		bus->drivers.subsys = &bus->subsys;
		bus->drivers.ktype = &ktype_driver;
		kset_register(&bus->drivers);
	
		bus_add_attrs(bus);

驱动

设备驱动程序模型中的每个设备由device表示,device结构体中定义了struct kobject kobj;所以device在层级上属于kobject。

struct device {
	/**
	 * 指向兄弟设备的指针
	 */
	struct list_head node;		/* node in sibling list */
	/**
	 * 指向连于同一类型总路线上的设备链表的指针。
	 */
	struct list_head bus_list;	/* node in bus's list */
	/**
	 * 指向设备驱动程序链表的指针。
	 */
	struct list_head driver_list;
	/**
	 * 子设备链表的首部
	 */
	struct list_head children;
	/**
	 * 指向父设备的指针
	 * 父设备。即该设备所属的设备。
	 * 大多数情况下,一个父设备通常是某种总线或者是宿主控制器。
	 * 如果parent为NULL,表示该设备是顶层设备。
	 */
	struct device 	* parent;

	/**
	 * 内嵌kobject。
	 * 通过该字段将设备连接到结构体系中。
	 * 作为通用准则,device->kobj->parent和device->parent->kobj是相同的。
	 */
	struct kobject kobj;
	/**
	 * 连接到总线上设备的位置
	 * 在总线上唯一标识该设备的字符串。如PCI设备使用了标准PCI ID格式,它包括:域编号、总线编号、设备编号和功能编号。
	 */
	char	bus_id[BUS_ID_SIZE];	/* position on parent bus */

	/**
	 * 指向所连接总线的指针
	 * 标识该设备连接在何种类型的总线上。
	 */
	struct bus_type	* bus;		/* type of bus device is on */
	/**
	 * 指向控制设备驱动程序的指针
	 */
	struct device_driver *driver;	/* which driver has allocated this
					   device */
	/**
	 * 指向驱动程序私有数据的指针
	 */
	void		*driver_data;	/* data private to the driver */
	
	/**
	 * 指向遗留设备驱动程序私有数据的指针
	 */
	void		*platform_data;	/* Platform specific data (e.g. ACPI,
					   BIOS data relevant to device) */
	/**
	 * 电源管理信息
	 */
	struct dev_pm_info	power;

	/**
	 * 卸载设备驱动程序时电源进入的状态
	 */
	u32		detach_state;	/* State to enter when device is
					   detached from its driver. */

	/**
	 * 指向设备的DMA屏蔽字的指针
	 */
	u64		*dma_mask;	/* dma mask (if dma'able device) */
	/**
	 * 设备的一致性DMA的屏蔽字
	 */
	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. */

	/**
	 * DMA缓冲池链表的首部
	 */
	struct list_head	dma_pools;	/* dma pools (if dma'ble) */

	/**
	 * 指向设备所使用的一致性DMA存储器描述符的指针
	 */
	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem
					     override */

	/**
	 * 释放回调函数
	 */
	void	(*release)(struct device * dev);
};

设备注册

/**

  • 往设备驱动程序模型中插入一个新的设备驱动。
  • 并自动的在sysfs文件系统下为其创建一个新的目录
    */
int device_register(struct device *dev)
{
	device_initialize(dev);
		// 设置该设备的父kset为devices_subsys.kset
		kobj_set_kset_s(dev, devices_subsys);
			(obj)->kobj.kset = &(subsys).kset
		kobject_init(&dev->kobj);
		INIT_LIST_HEAD(&dev->node);
		INIT_LIST_HEAD(&dev->children);
		INIT_LIST_HEAD(&dev->driver_list);
		INIT_LIST_HEAD(&dev->bus_list);
		INIT_LIST_HEAD(&dev->dma_pools);
	return device_add(dev);
		dev = get_device(dev);
		parent = get_device(dev->parent);
		kobject_set_name(&dev->kobj, "%s", dev->bus_id);
		// 如果父设备存在,则对应kobj就指向父设备的kobj
		if (parent)
			dev->kobj.parent = &parent->kobj;
		// 创建设备kobj对应的目录,如果父设备存在,就在父设备目录下创建,否则就在devices_subsys对应目录(/sys/devices)下创建
		kobject_add(&dev->kobj)
			device_pm_add(dev)
			bus_add_device(dev)
				struct bus_type * bus = get_bus(dev->bus);
				list_add_tail(&dev->bus_list, &dev->bus->devices.list);
				device_attach(dev);
				device_add_attrs(bus, dev);
				// 在对应总线的devices目录下创建一个链接文件dev->bus_id,链接到dev->kobj
				sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id);
		if (parent)
			list_add_tail(&dev->node, &parent->children);
		if (platform_notify)
			platform_notify(dev);
}

device_register主要内容如下
(1)设置该设备的父kset为devices_subsys.kset,这将使得该设备没有父设备时,会被注册到devices_subsys下。
(2)调用device_add将该设备添加到设备层次结构中,这会在devices_subsys目录下或者父设备目录下创建该设备目录。
(3)调用bus_add_device将设备和驱动绑定在一起。

device_attach(struct device * dev)
	struct bus_type * bus = dev->bus;
	// 如果驱动存在
	if (dev->driver)
		device_bind_driver(dev);
			list_add_tail(&dev->driver_list, &dev->driver->devices);
			// 在驱动对应的目录下创建一个名为 kobject_name(&dev->kobj)的链接文件,链接到dev->kobj目录下
			sysfs_create_link(&dev->driver->kobj, &dev->kobj, kobject_name(&dev->kobj));
			// 在dev->kobj目录下创建一个名为 driver的链接文件,链接到驱动目录下
			sysfs_create_link(&dev->kobj, &dev->driver->kobj, "driver");
	// 如果驱动不存在
	if (bus->match)
		//遍历设备所在总线上的驱动链表
		list_for_each(entry, &bus->drivers.list)
			struct device_driver * drv = to_drv(entry);
			//调用总线的匹配函数或者驱动的探测函数
			driver_probe_device(drv, dev);
			if (drv->bus->match && !drv->bus->match(dev, drv))
				return -ENODEV;
			dev->driver = drv;
			if (drv->probe)
				drv->probe(dev);
			device_bind_driver(dev);

device_attach目的就是把驱动和设备关联起来,主要内容如下:
(1)如果驱动已经存在,就调用device_bind_driver,该函数在驱动对应的目录下创建一个名为 kobject_name(&dev->kobj)的链接文件,链接到dev->kobj目录下,并在在dev->kobj目录下创建一个名为 driver的链接文件,链接到驱动目录下。
(2)如果没有驱动,则调用总线的匹配函数或者驱动的探测函数去寻找驱动,然后再调用device_bind_driver把驱动和设备关联起来。

驱动

驱动由device_driver结构体描述,因为其含有struct kobject kobj;,所以device_driver处于kobject级别


struct device_driver {
	/**
	 * 驱动程序的名称
	 */
	char			* name;
	/**
	 * 指向总线描述符的指针。
	 */
	struct bus_type		* bus;

	/**
	 * 禁止卸载设备驱动程序的信号量。
	 */
	struct semaphore	unload_sem;
	/**
	 * 内嵌kobject
	 */
	struct kobject		kobj;
	/**
	 * 驱动程序所支持的所有设备组成的链表的首部。
	 */
	struct list_head	devices;

	/**
	 * 驱动程序所在模块(如果有的话)
	 */
	struct module 		* owner;

	/**
	 * 探测设备的方法
	 */
	int	(*probe)	(struct device * dev);
	/**
	 * 移走设备的方法(检测设备驱动程序是否可以控制该设备)
	 */
	int 	(*remove)	(struct device * dev);
	/**
	 * 设备断电时调用的方法。
	 */
	void	(*shutdown)	(struct device * dev);
	/**
	 * 设备置于低功率状态时所调用的方法
	 */
	int	(*suspend)	(struct device * dev, u32 state, u32 level);
	/**
	 * 设备恢复正常状态时所调用的方法
	 */
	int	(*resume)	(struct device * dev, u32 level);
};

注册驱动

int driver_register(struct device_driver * drv)
{
	INIT_LIST_HEAD(&drv->devices);
	init_MUTEX_LOCKED(&drv->unload_sem);
	return bus_add_driver(drv);
		struct bus_type * bus = get_bus(drv->bus);
		// 注册driver到bus->drivers目录下
		kobject_set_name(&drv->kobj, "%s", drv->name);
		drv->kobj.kset = &bus->drivers;
		kobject_register(&drv->kobj)
		
		// 遍历总线上的设备,将驱动和设备绑定在一起
		driver_attach(drv);
			struct bus_type * bus = drv->bus;
			list_for_each(entry, &bus->devices.list) 
				struct device * dev = container_of(entry, struct device, bus_list);
				if (!dev->driver)
					driver_probe_device(drv, dev);
		module_add_driver(drv->owner, drv);
		driver_add_attrs(bus, drv);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值