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