Linux 设备模型

设备驱动模型

总线

在linux内核中总线用数据结构bus_type表示。

struct bus_type {
	const char		*name;
	const char		*dev_name;
	struct device		*dev_root;
	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);
	void (*sync_state)(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);

	int (*num_vf)(struct device *dev);

	int (*dma_configure)(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;

	bool need_parent_lock;
};
  • name/dev_name: 总线自己和下属设备的名字

  • bus/dev/drv_groups: 相关sysfs的文件

  • match/probe...: 匹配设备,操作设备的接口

  • p.subsys.kobj: kobj/sysfs 树的节点

  • p.subsys.devices_kset:下属设备的kobj/sysfs节点

  • p.subsys.drivers_kset:下属驱动的kobj/sysfs节点

    bus_type
    +------------------------------------+
    |name                                |
    |dev_name                            |
    |     (char *)                       |
    +------------------------------------+
    |bus_groups                          |
    |dev_groups                          |
    |drv_groups                          |
    |     (struct attribute_group**)     |
    +------------------------------------+
    |match                               |
    |uevent                              |
    |probe                               |
    |remove                              |
    |shutdown                            |
    |                                    |
    +------------------------------------+
    |p                                   |
    |   (struct subsys_private*)         |
    |   +--------------------------------+
    |   |bus                             |
    |   |    (struct bus_type*)          |
    |   |subsys                          |
    |   |   +----------------------------+
    |   |   |kobj.name                   | = bus->name
    |   |   |kobj.kset                   | = bus_kset
    |   |   |kobj.ktype                  | = bus_ktype
    |   |   +----------------------------+
    |   |devices_kset                    |
    |   |drivers_kset                    |
    |   |    (struct kset)               |
    |   |klist_devices                   |
    |   |klist_drivers                   |
    |   |    (struct klist)              |
    |   |class                           |
    |   |    (struct class*)             |
    +---+--------------------------------+

驱动

内核中用结构体device_driver来表示一个驱动。不过大家通常在驱动程序中看到是另外的结构体,比如pci驱动用的是pci_driver。但是我们打开这个pci_driver就可以看到其中包含了device_driver。所以驱动的核心数据结构还是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 */
	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);
	void (*sync_state)(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 attribute_group **dev_groups;

	const struct dev_pm_ops *pm;
	void (*coredump) (struct device *dev);

	struct driver_private *p;
};
    device_driver
    +----------------------------------+
    |name                              |
    |    (char *)                      |
    +----------------------------------+
    |bus                               |
    |    (struct bus_type*)            |
    +----------------------------------+
    |owner                             |
    |    (struct module*)              |
    +----------------------------------+
    |probe                             |
    |remove                            |
    |shutdown                          |
    |suspend                           |
    |resume                            |
    |                                  |
    +----------------------------------+
    |p                                 |
    |    (struct driver_private*)      |
    |    +-----------------------------+
    |    |driver                       |
    |    |    (struct device_driver*)  |
    |    |kobj                         |
    |    |    +------------------------+
    |    |    |name                    | = device_driver->name
    |    |    |kset                    | = device_driver->bus->p->drivers_kset
    |    |    |ktype                   | = driver_ktype
    |    |    +------------------------+
    |    |klist_devices                |
    |    |                             |
    +----+-----------------------------+
  • name: 驱动名

  • bus: 对应的总线

  • probe/remove...: 驱动调用接口

  • p->kobj.name: kobj对应的名字

  • p->kobj.kset: kobj的父节点

p->kobj.kset设置成了对应总线的drivers_kset。这点表明了驱动在sysfs中的根是在对应的总线上。区别于设备!

设备

内核中用结构体device来表示一个设备。不过大家通常在驱动程序中看到是另外的结构体,比如pci设备用的是pci_dev。但是我们打开这个pci_dev就可以看到其中包含了device。所以设备的核心数据结构还是device。

struct device {
	struct kobject kobj;
	struct device		*parent;

	struct device_private	*p;

	const char		*init_name; /* initial name of the device */
	const struct device_type *type;

	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_drvdata/dev_get_drvdata */
#ifdef CONFIG_PROVE_LOCKING
	struct mutex		lockdep_mutex;
#endif
	struct mutex		mutex;	/* mutex to synchronize calls to
					 * its driver.
					 */

	struct dev_links_info	links;
	struct dev_pm_info	power;
	struct dev_pm_domain	*pm_domain;

#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
	struct irq_domain	*msi_domain;
#endif
#ifdef CONFIG_PINCTRL
	struct dev_pin_info	*pins;
#endif
#ifdef CONFIG_GENERIC_MSI_IRQ
	struct list_head	msi_list;
#endif

	const struct dma_map_ops *dma_ops;
	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. */
	u64		bus_dma_mask;	/* upstream dma_mask constraint */
	unsigned long	dma_pfn_offset;

	struct device_dma_parameters *dma_parms;

	struct list_head	dma_pools;	/* dma pools (if dma'ble) */

#ifdef CONFIG_DMA_DECLARE_COHERENT
	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem
					     override */
#endif
#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 */

#ifdef CONFIG_NUMA
	int		numa_node;	/* NUMA node this device is close to */
#endif
	dev_t			devt;	/* dev_t, creates the sysfs "dev" */
	u32			id;	/* device instance */

	spinlock_t		devres_lock;
	struct list_head	devres_head;

	struct class		*class;
	const struct attribute_group **groups;	/* optional groups */

	void	(*release)(struct device *dev);
	struct iommu_group	*iommu_group;
	struct iommu_fwspec	*iommu_fwspec;
	struct iommu_param	*iommu_param;

	bool			offline_disabled:1;
	bool			offline:1;
	bool			of_node_reused:1;
	bool			state_synced:1;
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
    defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
    defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
	bool			dma_coherent:1;
#endif
#if defined(CONFIG_DMA_COHERENT_HINT_CACHED)
	bool			dma_coherent_hint_cached:1;
#endif
};

简化为:

device
+----------------------------------------+
|init_name                               |
|    (char *)                            |
|devt                                    |
|    (dev_t)                             |
|id                                      |
|    (u32)                               |
+----------------------------------------+
|kobj                                    |
|    (struct kobject)                    |
|    +-----------------------------------+
|    |name                               | = device->init_name or
|    |                                   |   device->bus->dev_name + id
|    |kset                               | = devices_kset
|    |ktype                              | = device_ktype
+----+-----------------------------------+
|type                                    |
|    (struct device_type*)               |
+----------------------------------------+
|bus                                     | = [pci_bus_type|nvdimm_bus_type]
|    (struct bus_type)                   |
+----------------------------------------+
|driver                                  |
|    (struct device_driver*)             |
+----------------------------------------+
|p                                       |
|    (struct device_private *)           |
+----------------------------------------+
  • init_name: 设备名(如果有的话)

  • id: 设备号

  • bus: 对应的总线

  • driver: 对应的驱动

  • kobj.name: kobj对应的名字,真正的设备名

  • kobj.kset: kobj的父节点

这里着重强调一点kobj.kset,这个值和驱动的父节点有所不同。驱动的父节点指向了总线,而设备的父节点是另一个根节点。这个时候我们可以来看看有了驱动和设备后,kobj树形结构的样子了。

总线,驱动和设备的树

           bus(bus_kset)      <-------------+     devices(devices_kset)
                |                           |            |
  --+-----------+------+-----               |   ----+----+-------------
    |                  |                    |       |
    +- drivers         +- devices           |       |
       |                  |                 |       |
       +- drvA   <- - -   +- devA   - * - * |  >    +- devA
       |                  |  |              |       |  
       |                  |  +- subsystem --+       |  
       |                  |                 |       |  
       +- drvB   <- - -   +- devB   - * - * |  >    +- devB
       |                  |  |              |       |  
       |                  |  +- subsystem --+       |  
       |                  |                 |       |  
       +- drvC   <- - -   +- devC   - * - * |  >    +- devC
       |                  |  |              |       |  
       |                  |  +- subsystem --+       |  
       |                  |                         |  
       +- drvD   <- - -   +- devD   - * - * -  >    +- devD
  • 总线下有驱动和设备

  • 设备和驱动存在着关联

  • 总线下的设备实际是一个软链接

  • 设备真正的根在devices_kset

Bind

在内核中驱动和设备发生关联称之为绑定bind。而且有意思的是绑定的过程可以在两个地方发生。

  • 驱动加载

  • 设备发现

不过这两个地方最后都会通过函数__driver_attach()来处理。而这个过程又可以分成两步:

  • 匹配

  • 探测

匹配

匹配的过程由总线的match函数来处理。

static inline int driver_match_device(struct device_driver *drv,
				      struct device *dev)
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

那具体的操作就由总线来决定。比如pci总线上的match函数就是pci_match_device()。它的过程就是匹配pci的厂商号和设备号。

探测

找到了匹配的设备和驱动后,就可以执行探测了。这个过程相对比较复杂,在当前的内核中,大多最后由总线的probe函数执行。

比如pci总线上的probe函数就是pci_device_probe()。它的作用就是去调用pci_driver中的probe函数。

流程图

  __driver_attach
      driver_match_device()
        bus->match()
      driver_probe_device()
        bus->probe()

Last updated