# Linux 设备模型

### 设备驱动模型

#### 总线

在linux内核中总线用数据结构bus\_type表示。

```c
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节点

```c
    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。

```c
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;
};
```

```c
    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。

```c
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
};
```

简化为：

```c
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树形结构的样子了。

#### 总线，驱动和设备的树

```c
           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函数来处理。

```c
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函数。

**流程图**

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