内核支持大量总线,可能涉及多种硬件平台,也有可能只涉及一种平台。在Linux支持的大多数体系结构上都使用了PCI总线。我还会讨论广泛使用、系统无关的USB总线,该总线用于外设。
通用驱动程序模型
现代总线系统在布局和结构的细节上可能有所不同,但也有许多共同之处,内核的数据结构即反映了这个事实。结构中的许多成员用于所有的总线(以及相关设备的数据结构中)。在内核版本2.6开发期间,一个通用驱动程序模型(设备模型,device model)并入内该,以防止不必要的复制。所有总线共有的屈性封装到特殊的、可以用通用方法处理的数据结构中,再关联到总线相关的成员。
设备的表示
驱动程序模型采用一种持殊数据结构来表示几乎所有总线类型通用的设备属性。该结构直接嵌入到特定于总线的数据结构中,而不是通过指针引用。
struct device
//设备驱动模型中的device结构体 struct device { struct device *parent;//指向设备的“父”设备,它所连接的设备 struct device_private *p;//用于保存设备驱动核心部分的私有数据 struct kobject kobj;//嵌入的struct kobject对象实例 const char *init_name; //设备的初始化名称 /* initial name of the device */ const 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;//分配device实例的驱动程序 /* 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/get_drvdata */ struct dev_pm_info power; struct dev_pm_domain *pm_domain; #ifdef CONFIG_PINCTRL struct dev_pin_info *pins; #endif #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. */ unsigned long dma_pfn_offset; 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 */ #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 */ dev_t devt;//设备的设备号 /* dev_t, creates the sysfs "dev" */ u32 id; /* device instance */ spinlock_t devres_lock;//保护资源的自旋锁 struct list_head devres_head;//设备资源的双向链表 struct klist_node knode_class;//接入class链表时所需要的klist节点 struct class *class;//指向设备所属的class指针 const struct attribute_group **groups;//设备属性集合 /* optional groups */ void (*release)(struct device *dev);//函数指针,当设备需要释放时调用此函数 struct iommu_group *iommu_group; bool offline_disabled:1; bool offline:1; };
通用驱动程序模型也为设驱动程序单独设计一种数据结构源码如下:
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 */ const struct of_device_id *of_match_table; const struct acpi_device_id *acpi_match_table; 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; };
总线的表示
通用驱动程序模型不仅表示了设备,还用另一个数据结构表示了总线,定义如下:
bus_type
//总线数据结构类型的表示,成员: struct bus_type { const char *name;//bus名称 const char *dev_name; struct device *dev_root; struct device_attribute *dev_attrs; /* use dev_groups instead */ 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); //匹配总线中dev和driver,返回1 匹配成功,否则匹配失败 int (*uevent)(struct device *dev, struct kobj_uevent_env *env); //用于总线对uevent的环境变量增加,但在总线下设备的dev_uevent处理函数也有对他的调用 int (*probe)(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);//在总线上设备恢复时调用 const struct dev_pm_ops *pm; const struct iommu_ops *iommu_ops; struct subsys_private *p; struct lock_class_key lock_key; };
注册过程
为说明表示总线、设备和设备驱动程序的各个數据结构之间彼此的关联,了解各种类型数据结构的注册过程是很有用处的。
注册总线---->注册设备---->注册设备驱动程序
注册总线
注册总线:初始化设备的数据结构,将它加入到数据结构的网络当中。
bus_register
//注册总线 /*在可以注册设备及驱动程序之前,需要有总线,提供此函数注册总线*/ int bus_register(struct bus_type *bus) { int retval; struct subsys_private *priv; struct lock_class_key *key = &bus->lock_key; priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); if (!priv) return -ENOMEM; priv->bus = bus; bus->p = priv; BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); if (retval) goto out; priv->subsys.kobj.kset = bus_kset; priv->subsys.kobj.ktype = &bus_ktype; priv->drivers_autoprobe = 1; retval = kset_register(&priv->subsys); if (retval) goto out; retval = bus_create_file(bus, &bus_attr_uevent); if (retval) goto bus_uevent_fail; priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj); if (!priv->devices_kset) { retval = -ENOMEM; goto bus_devices_fail; } priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj); if (!priv->drivers_kset) { retval = -ENOMEM; goto bus_drivers_fail; } INIT_LIST_HEAD(&priv->interfaces); __mutex_init(&priv->mutex, "subsys mutex", key); klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); klist_init(&priv->klist_drivers, NULL, NULL); retval = add_probe_files(bus); if (retval) goto bus_probe_files_fail; retval = bus_add_groups(bus, bus->bus_groups); if (retval) goto bus_groups_fail; pr_debug("bus: '%s': registered\n", bus->name); return 0; bus_groups_fail: remove_probe_files(bus); bus_probe_files_fail: kset_unregister(bus->p->drivers_kset); bus_drivers_fail: kset_unregister(bus->p->devices_kset); bus_devices_fail: bus_remove_file(bus, &bus_attr_uevent); bus_uevent_fail: kset_unregister(&bus->p->subsys); out: kfree(bus->p); bus->p = NULL; return retval; }
注册设备
注册设备:初始化设备的数据结构,并将它加入到数据结构的网络当中。
device_add
//注册设备 int device_add(struct device *dev) { struct device *parent = NULL; struct kobject *kobj; struct class_interface *class_intf; int error = -EINVAL; dev = get_device(dev); if (!dev) goto done; if (!dev->p) { error = device_private_init(dev); if (error) goto done; } /* * for statically allocated devices, which should all be converted * some day, we need to initialize the name. We prevent reading back * the name, and force the use of dev_name() */ if (dev->init_name) { dev_set_name(dev, "%s", dev->init_name); dev->init_name = NULL; } /* subsystems can specify simple device enumeration */ if (!dev_name(dev) && dev->bus && dev->bus->dev_name) dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id); if (!dev_name(dev)) { error = -EINVAL; goto name_error; } pr_debug("device: '%s': %s\n", dev_name(dev), __func__); parent = get_device(dev->parent); kobj = get_device_parent(dev, parent); if (kobj) dev->kobj.parent = kobj; /* use parent numa_node */ if (parent) set_dev_node(dev, dev_to_node(parent)); /* first, register with generic layer. */ /* we require the name to be set before, and pass NULL */ error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); if (error) goto Error; /* notify platform of device entry */ if (platform_notify) platform_notify(dev); error = device_create_file(dev, &dev_attr_uevent); if (error) goto attrError; error = device_add_class_symlinks(dev); if (error) goto SymlinkError; error = device_add_attrs(dev); if (error) goto AttrsError; error = bus_add_device(dev); if (error) goto BusError; error = dpm_sysfs_add(dev); if (error) goto DPMError; device_pm_add(dev); if (MAJOR(dev->devt)) { error = device_create_file(dev, &dev_attr_dev); if (error) goto DevAttrError; error = device_create_sys_dev_entry(dev); if (error) goto SysEntryError; devtmpfs_create_node(dev); } /* Notify clients of device addition. This call must come * after dpm_sysfs_add() and before kobject_uevent(). */ if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev); kobject_uevent(&dev->kobj, KOBJ_ADD); bus_probe_device(dev); if (parent) klist_add_tail(&dev->p->knode_parent, &parent->p->klist_children); if (dev->class) { mutex_lock(&dev->class->p->mutex); /* tie the class to the device */ klist_add_tail(&dev->knode_class, &dev->class->p->klist_devices); /* notify any interfaces that the device is here */ list_for_each_entry(class_intf, &dev->class->p->interfaces, node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); mutex_unlock(&dev->class->p->mutex); } done: put_device(dev); return error; SysEntryError: if (MAJOR(dev->devt)) device_remove_file(dev, &dev_attr_dev); DevAttrError: device_pm_remove(dev); dpm_sysfs_remove(dev); DPMError: bus_remove_device(dev); BusError: device_remove_attrs(dev); AttrsError: device_remove_class_symlinks(dev); SymlinkError: device_remove_file(dev, &dev_attr_uevent); attrError: kobject_uevent(&dev->kobj, KOBJ_REMOVE); kobject_del(&dev->kobj); Error: cleanup_device_parent(dev); put_device(parent); name_error: kfree(dev->p); dev->p = NULL; goto done; }