网站建设属于无形资产哪一类,定海网,农业网站建设公司,wordpress layer 主题一#xff0c;设备驱动模型
1#xff0c;概述
在前面写的驱动中#xff0c;我们发现编写驱动有个固定的模式只有往里面套代码就可以了#xff0c;它们之间的大致流程可以总结如下#xff1a; 实现入口函数xxx_init()和卸载函数xxx_exit() 申请设备号 register_chrdev_r…一设备驱动模型
1概述
在前面写的驱动中我们发现编写驱动有个固定的模式只有往里面套代码就可以了它们之间的大致流程可以总结如下 实现入口函数xxx_init()和卸载函数xxx_exit() 申请设备号 register_chrdev_region() 初始化字符设备cdev_init函数、cdev_add函数 硬件初始化如时钟寄存器配置使能GPIO设置为输入输出模式等。 构建file_operation结构体内容实现硬件各个相关的操作 在终端上使用mknod根据设备号来进行创建设备文件(节点) (也可以在驱动使用class_create创建设备类、在类的下面device_create创建设备节点)
因此在Linux开发驱动只要能够掌握了这些“套路”开发一个驱动便不是难事。但是如果我们将硬件的信息都写进了驱动里了 根据某个硬件编写的驱动只要修改了一下引脚接口这个驱动代码就得重新修改才能使用这显然是不合理的。 那有没有合适的解决方案呢答案是肯定的
Linux引入了设备驱动模型分层的概念 将我们编写的驱动代码分成设备与驱动。 设备负责提供硬件资源 驱动代码负责去使用这些设备提供的硬件资源 总线将它们联系起来
这样子就构成以下图形中的关系: 在实际操作上 每次有新设备device添加时bus就会去匹配合适的驱动 每次有新驱动driver添加时bus就会去匹配合适的设备 这样device代表的硬件设置就和driver代表的软件设计解耦了 当然实际中同一总线下的设备有很多驱动也有很多在总线上管理着两个链表分别管理着设备和驱动当我们向系统注册一个驱动时便会向驱动的管理链表插入我们的新驱动 同样当我们向系统注册一个设备时便会向设备的管理链表插入我们的新设备。 在插入的同时总线会执行一个匹配方法对新插入的设备/驱动进行匹配在匹配成功的时候会调用驱动中的初始化方法在移除设备或驱动时会调用注销方法。
以上只是设备驱动模型的机制 。 2设备驱动模型初始化
从内核启动到driver_init()的流程
start_kernel(void) // msm_kernel\init\main.carch_call_rest_init();rest_init();kernel_thread(kernel_init, NULL, CLONE_FS);kernel_init_freeable();do_basic_setup();driver_init();
driver_init()调用流程
msm_kernel\drivers\base\init.c
/**
* driver_init - initialize driver model.
*
* Call the driver model init functions to initialize their
* subsystems. Called early from init/main.c.
*/
void __init driver_init(void)
{/* These are the core pieces */devtmpfs_init();devices_init(); //初始化 devices_kset, /sys/devices/buses_init(); //初始化bus_kset, /sys/bus/classes_init(); //初始化class_kset, /sys/class firmware_init();hypervisor_init();/* These are also core pieces, but must come after the* core core pieces.*/of_core_init(); //初始化of_ksetplatform_bus_init(); //注册platform_bus device和platform_bus_type buscpu_dev_init();memory_dev_init();container_dev_init();
}
二总线的注册
在Linux设备模型中Bus总线是一类特殊的设备它是连接处理器和其它设备之间的通道channel。为了方便设备模型的实现内核规定系统中的每个设备都要连接在一个Bus上这个Bus可以是一个内部Bus、虚拟Bus或者Platform Bus。内核通过struct bus_type结构抽象Bus。
1struct bus_type结构体
/**
* struct bus_type - The bus type of the device
*
* name: The name of the bus.
* dev_name: Used for subsystems to enumerate devices like (foo%u, dev-id).
* dev_root: Default device to use as the parent.
* bus_groups: Default attributes of the bus.
* dev_groups: Default attributes of the devices on the bus.
* drv_groups: Default attributes of the device drivers on the bus.
* match: Called, perhaps multiple times, whenever a new device or driver
* is added for this bus. It should return a positive value if the
* given device can be handled by the given driver and zero
* otherwise. It may also return error code if determining that
* the driver supports the device is not possible. In case of
* -EPROBE_DEFER it will queue the device for deferred probing.
* uevent: Called when a device is added, removed, or a few other things
* that generate uevents to add the environment variables.
* probe: Called when a new device or driver add to this bus, and callback
* the specific drivers probe to initial the matched device.
* sync_state: Called to sync device state to software state after all the
* state tracking consumers linked to this device (present at
* the time of late_initcall) have successfully bound to a
* driver. If the device has no consumers, this function will
* be called at late_initcall_sync level. If the device has
* consumers that are never bound to a driver, this function
* will never get called until they do.
* remove: Called when a device removed from this bus.
* shutdown: Called at shut-down time to quiesce the device.
*
* online: Called to put the device back online (after offlining it).
* offline: Called to put the device offline for hot-removal. May fail.
*
* suspend: Called when a device on this bus wants to go to sleep mode.
* resume: Called to bring a device on this bus out of sleep mode.
* num_vf: Called to find out how many virtual functions a device on this
* bus supports.
* dma_configure: Called to setup DMA configuration on a device on
* this bus.
* pm: Power management operations of this bus, callback the specific
* device drivers pm-ops.
* iommu_ops: IOMMU specific operations for this bus, used to attach IOMMU
* driver implementations to a bus and allow the driver to do
* bus-specific setup
* p: The private data of the driver core, only the driver core can
* touch this.
* lock_key: Lock class key for use by the lock validator
* need_parent_lock: When probing or removing a device on this bus, the
* device core should lock the devices parent.
*
* A bus is a channel between the processor and one or more devices. For the
* purposes of the device model, all devices are connected via a bus, even if
* it is an internal, virtual, platform bus. Buses can plug into each other.
* A USB controller is usually a PCI device, for example. The device model
* represents the actual connections between buses and the devices they control.
* A bus is represented by the bus_type structure. It contains the name, the
* default attributes, the bus methods, PM operations, and the driver cores
* private data.
*/
struct bus_type {const char *name; //总线的名字在syfs中以目录的形式存在例如/sys/bus/i2cconst char *dev_name; //子系统枚举设备时的名字struct device *dev_root;const struct attribute_group **bus_groups; //bus属性集const struct attribute_group **dev_groups; //device属性集const struct attribute_group **drv_groups; //driver属性集int (*match)(struct device *dev, struct device_driver *drv); /* bus提供的match函数, 一个由具体的bus driver实现的回调函数。当任何属于该Bus的device或者device_driver添加到内核时内核都会调用该接口 */int (*uevent)(struct device *dev, struct kobj_uevent_env *env); /* 一个由具体的bus driver实现的回调函数。当任何属于该Bus的device发生添加、移除或者其它动作时Bus模块的核心逻辑就会调用该接口以便bus driver能够修改环境变量 */int (*probe)(struct device *dev); /* bus提供的probe函数, 如果需要probe其实就是初始化指定的device话需要保证该device所在的bus是被初始化过、确保能正确工作的。这就要就在执行device_driver的probe前先执行它的bus的probe */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; //bus的私有成员struct lock_class_key lock_key;bool need_parent_lock;ANDROID_KABI_RESERVE(1);ANDROID_KABI_RESERVE(2);ANDROID_KABI_RESERVE(3);ANDROID_KABI_RESERVE(4);
};
bus私有成员结构体这个结构就是集合了一些bus模块需要使用的私有数据
/**
* struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure.
*
* subsys - the struct kset that defines this subsystem
* devices_kset - the subsystems devices directory
* interfaces - list of subsystem interfaces associated
* mutex - protect the devices, and interfaces lists.
*
* drivers_kset - the list of drivers associated
* klist_devices - the klist to iterate over the devices_kset
* klist_drivers - the klist to iterate over the drivers_kset
* bus_notifier - the bus notifier list for anything that cares about things
* on this bus.
* bus - pointer back to the struct bus_type that this structure is associated
* with.
*
* glue_dirs - glue directory to put in-between the parent device to
* avoid namespace conflicts
* class - pointer back to the struct class that this structure is associated
* with.
*
* This structure is the one that is the actual kobject allowing struct
* bus_type/class to be statically allocated safely. Nothing outside of the
* driver core should ever touch these fields.
*/
struct subsys_private {struct kset subsys; //bus内嵌的kset代表其自身 eg: /sys/bus/i2ckset是一个特殊的kobject用来集合相似的kobject它在sysfs中也会以目录的形式体现struct kset *devices_kset; //属于subsys kset, eg: /sys/bus/i2c/devicesstruct list_head interfaces;struct mutex mutex;struct kset *drivers_kset; //属于subsys kset, eg: /sys/bus/i2c/driversstruct klist klist_devices; //本bus包含的所有设备struct klist klist_drivers; //本bus包含的所有driverstruct blocking_notifier_head bus_notifier;unsigned int drivers_autoprobe:1; //device与driver是否自动probestruct bus_type *bus; //回指到bus_typestruct kset glue_dirs;struct class *class;
};
2bus_register()流程 3关键代码流程分析
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); //为struct subsys_private分配空间if (!priv)return -ENOMEM;priv-bus bus; //私有成员的bus回指该busbus-p priv; //初始化bus-p,即其私有属性BLOCKING_INIT_NOTIFIER_HEAD(priv-bus_notifier);retval kobject_set_name(priv-subsys.kobj, %s, bus-name); //设置该bus的名字bus是kset的封装if (retval)goto out;priv-subsys.kobj.kset bus_kset; //bus_ket即为所有bus的总起始端点,本bus从属于bus_ket,/sys/bus/priv-subsys.kobj.ktype bus_ktype; //属性操作级别统一为bus_ktypepriv-drivers_autoprobe 1; //device与driver自动进行proberetval kset_register(priv-subsys); //注册kset,创建目录结构以及层级关系 /sys/bus/i2c/if (retval)goto out;retval bus_create_file(bus, bus_attr_uevent); //当前bus下生成uevent attribute文件 /sys/bus/i2c/ueventif (retval)goto bus_uevent_fail;priv-devices_kset kset_create_and_add(devices, NULL, //初始化bus目录下的devices目录里面级联了该bus下的设备仍然以ket为原型priv-subsys.kobj); // /sys/bus/i2c/devicesif (!priv-devices_kset) {retval -ENOMEM;goto bus_devices_fail;}priv-drivers_kset kset_create_and_add(drivers, NULL, //初始化bus目录下的drivers目录里面级联了该bus下设备的driverpriv-subsys.kobj); // /sys/bus/i2c/driversif (!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_devices里的操作函数成员 klist_init(priv-klist_drivers, NULL, NULL); //klist_drivers里的操作函数置空retval add_probe_files(bus); /* 增加drivers_autoprobe和drivers_probe属性文件/sys/bus/i2c/drivers_probe, /sys/bus/i2c/drivers_autoprobe */if (retval)goto bus_probe_files_fail;retval bus_add_groups(bus, bus-bus_groups); //增加bus默认的属性文件, const struct attribute_group **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;
}
由此可见bus又是kset的封装bus_register主要完成了其私有成员priv的初始化并初始化了其下的两个目录devices和drivers及其属性文件bus有个自己的根目录也就是bus有个起始端点是bus_kset经过此番的注册bus目录下将会出现我们注册的bus并且其下会有device和driver两个子目录代表它下面的driver和device链表。 三设备的注册
1struct device 结构体
/**
* struct device - The basic device structure
* parent: The devices parent device, the device to which it is attached.
* In most cases, a parent device is some sort of bus or host
* controller. If parent is NULL, the device, is a top-level device,
* which is not usually what you want.
* p: Holds the private data of the driver core portions of the device.
* See the comment of the struct device_private for detail.
* kobj: A top-level, abstract class from which other classes are derived.
* init_name: Initial name of the device.
* type: The type of device.
* This identifies the device type and carries type-specific
* information.
* mutex: Mutex to synchronize calls to its driver.
* lockdep_mutex: An optional debug lock that a subsystem can use as a
* peer lock to gain localized lockdep coverage of the device_lock.
* bus: Type of bus device is on.
* driver: Which driver has allocated this
* platform_data: Platform data specific to the device.
* Example: For devices on custom boards, as typical of embedded
* and SOC based hardware, Linux often uses platform_data to point
* to board-specific structures describing devices and how they
* are wired. That can include what ports are available, chip
* variants, which GPIO pins act in what additional roles, and so
* on. This shrinks the Board Support Packages (BSPs) and
* minimizes board-specific #ifdefs in drivers.
* driver_data: Private pointer for driver specific info.
* links: Links to suppliers and consumers of this device.
* power: For device power management.
* See Documentation/driver-api/pm/devices.rst for details.
* pm_domain: Provide callbacks that are executed during system suspend,
* hibernation, system resume and during runtime PM transitions
* along with subsystem-level and driver-level callbacks.
* em_pd: devices energy model performance domain
* pins: For device pin management.
* See Documentation/driver-api/pinctl.rst for details.
* msi_list: Hosts MSI descriptors
* msi_domain: The generic MSI domain this device is using.
* numa_node: NUMA node this device is close to.
* dma_ops: DMA mapping operations for this device.
* dma_mask: Dma mask (if dmable device).
* coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all
* hardware supports 64-bit addresses for consistent allocations
* such descriptors.
* bus_dma_limit: Limit of an upstream bridge or bus which imposes a smaller
* DMA limit than the device itself supports.
* dma_range_map: map for DMA memory ranges relative to that of RAM
* dma_parms: A low level driver may set these to teach IOMMU code about
* segment limitations.
* dma_pools: Dma pools (if dmable device).
* dma_mem: Internal for coherent mem override.
* cma_area: Contiguous memory area for dma allocations
* archdata: For arch-specific additions.
* of_node: Associated device tree node.
* fwnode: Associated device node supplied by platform firmware.
* devt: For creating the sysfs dev.
* id: device instance
* devres_lock: Spinlock to protect the resource of the device.
* devres_head: The resources list of the device.
* knode_class: The node used to add the device to the class list.
* class: The class of the device.
* groups: Optional attribute groups.
* release: Callback to free the device after all references have
* gone away. This should be set by the allocator of the
* device (i.e. the bus driver that discovered the device).
* iommu_group: IOMMU group the device belongs to.
* iommu: Per device generic IOMMU runtime data
*
* offline_disabled: If set, the device is permanently online.
* offline: Set after successful invocation of bus types .offline().
* of_node_reused: Set if the device-tree node is shared with an ancestor
* device.
* state_synced: The hardware state of this device has been synced to match
* the software state of this device by calling the driver/bus
* sync_state() callback.
* dma_coherent: this particular device is dma coherent, even if the
* architecture supports non-coherent devices.
* dma_ops_bypass: If set to %true then the dma_ops are bypassed for the
* streaming DMA operations (-map_* / -unmap_* / -sync_*),
* and optionall (if the coherent mask is large enough) also
* for dma allocations. This flag is managed by the dma ops
* instance from -dma_supported.
*
* At the lowest level, every device in a Linux system is represented by an
* instance of struct device. The device structure contains the information
* that the device model core needs to model the system. Most subsystems,
* however, track additional information about the devices they host. As a
* result, it is rare for devices to be represented by bare device structures;
* instead, that structure, like kobject structures, is usually embedded within
* a higher-level representation of the device.
*/
struct device {struct kobject kobj; //该数据结构对应的struct kobject,代表自身struct device *parent; //该设备的父设备一般是该设备所从属的bus、controller等设备struct device_private *p; //一个用于struct device的私有数据结构指针const char *init_name; /* initial name of the device */ /* 任何注册到内核中的设备都必须有一个合法的名称可以在初始化时给出也可以由内核根据“bus name device ID”的方式创造 */const struct device_type *type;struct bus_type *bus; /* type of bus device is on */struct device_driver *driver; /* which driver has allocated thisdevice */void *platform_data; /* Platform specific data, devicecore doesnt touch it */void *driver_data; /* Driver data, set and get withdev_set_drvdata/dev_get_drvdata */
#ifdef CONFIG_PROVE_LOCKINGstruct mutex lockdep_mutex;
#endifstruct 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_ENERGY_MODELstruct em_perf_domain *em_pd;
#endif#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAINstruct irq_domain *msi_domain;
#endif
#ifdef CONFIG_PINCTRLstruct dev_pin_info *pins;
#endif
#ifdef CONFIG_GENERIC_MSI_IRQstruct list_head msi_list;
#endif
#ifdef CONFIG_DMA_OPSconst struct dma_map_ops *dma_ops;
#endifu64 *dma_mask; /* dma mask (if dmaable device) */u64 coherent_dma_mask;/* Like dma_mask, but foralloc_coherent mappings asnot all hardware supports64 bit addresses for consistentallocations such descriptors. */u64 bus_dma_limit; /* upstream dma constraint */const struct bus_dma_region *dma_range_map;struct device_dma_parameters *dma_parms;struct list_head dma_pools; /* dma pools (if dmable) */#ifdef CONFIG_DMA_DECLARE_COHERENTstruct dma_coherent_mem *dma_mem; /* internal for coherent memoverride */
#endif
#ifdef CONFIG_DMA_CMAstruct cma *cma_area; /* contiguous memory area for dmaallocations */
#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_NUMAint numa_node; /* NUMA node this device is close to */
#endifdev_t devt; /* dev_t, creates the sysfs dev */ /* 该变量主要用于在sys文件系统中为每个具有设备号的device创建/sys/dev/* 下的对应目录 */u32 id; /* device instance */spinlock_t devres_lock;struct list_head devres_head;struct class *class; //该设备属于哪个classconst struct attribute_group **groups; /* optional groups */ /* 该设备的默认attribute集合。将会在设备注册时自动在sysfs中创建对应的文件 */void (*release)(struct device *dev);struct iommu_group *iommu_group;struct dev_iommu *iommu;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
#ifdef CONFIG_DMA_OPS_BYPASSbool dma_ops_bypass : 1;
#endifANDROID_KABI_RESERVE(1);ANDROID_KABI_RESERVE(2);ANDROID_KABI_RESERVE(3);ANDROID_KABI_RESERVE(4);ANDROID_KABI_RESERVE(5);ANDROID_KABI_RESERVE(6);ANDROID_KABI_RESERVE(7);ANDROID_KABI_RESERVE(8);
};
设备的私有属性结构
/**
* struct device_private - structure to hold the private to the driver core portions of the device structure.
*
* klist_children - klist containing all children of this device
* knode_parent - node in sibling list
* knode_driver - node in driver list
* knode_bus - node in bus list
* knode_class - node in class list
* deferred_probe - entry in deferred_probe_list which is used to retry the
* binding of drivers which were unable to get all the resources needed by
* the device; typically because it depends on another driver getting
* probed first.
* async_driver - pointer to device driver awaiting probe via async_probe
* device - pointer back to the struct device that this structure is
* associated with.
* dead - This device is currently either in the process of or has been
* removed from the system. Any asynchronous events scheduled for this
* device should exit without taking any action.
*
* Nothing outside of the driver core should ever touch these fields.
*/
struct device_private {struct klist klist_children; //包含的子设备struct klist_node knode_parent; //父级挂接点struct klist_node knode_driver; //driver list挂接点struct klist_node knode_bus; //bus list挂接点struct klist_node knode_class; //class list挂接点struct list_head deferred_probe;struct device_driver *async_driver;char *deferred_probe_reason;struct device *device; //回指向该设备u8 dead:1;
};
2device_register()流程 清晰分解图 3关键代码流程分析
/* device_initialize */
void device_initialize(struct device *dev)
{dev-kobj.kset devices_kset; //device kobject属于devices_ksetkobject_init(dev-kobj, device_ktype); //初始化这个kobj并建立层次关系以及属性文件此时是放到了总的device文件目录下面INIT_LIST_HEAD(dev-dma_pools);mutex_init(dev-mutex);
#ifdef CONFIG_PROVE_LOCKINGmutex_init(dev-lockdep_mutex);
#endiflockdep_set_novalidate_class(dev-mutex);spin_lock_init(dev-devres_lock);INIT_LIST_HEAD(dev-devres_head);device_pm_init(dev);set_dev_node(dev, -1);
#ifdef CONFIG_GENERIC_MSI_IRQINIT_LIST_HEAD(dev-msi_list);
#endifINIT_LIST_HEAD(dev-links.consumers);INIT_LIST_HEAD(dev-links.suppliers);INIT_LIST_HEAD(dev-links.defer_sync);dev-links.status DL_DEV_NO_DRIVER;
}
/* device_add */
int device_add(struct device *dev)
{struct device *parent;struct kobject *kobj;struct class_interface *class_intf;int error -EINVAL;struct kobject *glue_dir NULL;dev get_device(dev);if (!dev)goto done;if (!dev-p) {error device_private_init(dev); //初始化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); //设置kobject的名字kobj-name sdev-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); //如果kobj的名字为空则使用总线名字设备的ID组合当做设备名字if (!dev_name(dev)) {error -EINVAL;goto name_error; //如果dev的名字还是空则退出,必须有正确的设备名字才能被注册}pr_debug(device: %s: %s\n, dev_name(dev), __func__);parent get_device(dev-parent);kobj get_device_parent(dev, parent); //获取父亲节点if (IS_ERR(kobj)) {error PTR_ERR(kobj);goto parent_error;}if (kobj)dev-kobj.parent kobj;/* use parent numa_node */if (parent (dev_to_node(dev) NUMA_NO_NODE))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); //初始化kobj与其父亲节点的连接在父亲dev的目录下会创建该device的sysfs path并设置kobj-state_in_sysfs为1表示该设备已被注册if (error) {glue_dir get_glue_dir(dev);goto Error;}/* notify platform of device entry */error device_platform_notify(dev, KOBJ_ADD);if (error)goto platform_error;error device_create_file(dev, dev_attr_uevent); //产生uevent属性文件,/sys/devices/llcc-pmu/ueventif (error)goto attrError;error device_add_class_symlinks(dev); //在dev的path创建一些符号链接具体链接上述流程图中有示例if (error)goto SymlinkError;error device_add_attrs(dev); //在dev的path添加一些属性文件if (error)goto AttrsError;error bus_add_device(dev); //把设备添加到busif (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); //发送该设备被ADD的uevent消息 KOBJ_ADD/** Check if any of the other devices (consumers) have been waiting for* this device (supplier) to be added so that they can create a device* link to it.** This needs to happen after device_pm_add() because device_link_add()* requires the supplier be registered before its called.** But this also needs to happen before bus_probe_device() to make sure* waiting consumers can link to it before the driver is bound to the* device and the driver sync_state callback is called for this device.*/if (dev-fwnode !dev-fwnode-dev) {dev-fwnode-dev dev;fw_devlink_link_device(dev);}bus_probe_device(dev); //为一个新的device探测driverif (parent)klist_add_tail(dev-p-knode_parent,parent-p-klist_children); //如果该设备有关联的父设备将该设备挂接到父设备的children listif (dev-class) {mutex_lock(dev-class-p-mutex);/* tie the class to the device */klist_add_tail(dev-p-knode_class,dev-class-p-klist_devices); //如果设备跟class关联将设备挂接到class list/* 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); //减少device的引用计数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:device_platform_notify(dev, KOBJ_REMOVE);
platform_error:kobject_uevent(dev-kobj, KOBJ_REMOVE);glue_dir get_glue_dir(dev);kobject_del(dev-kobj);
Error:cleanup_glue_dir(dev, glue_dir);
parent_error:put_device(parent);
name_error:kfree(dev-p);dev-p NULL;goto done;
}
/* bus_add_device */
int bus_add_device(struct device *dev)
{struct bus_type *bus bus_get(dev-bus);int error 0;if (bus) {pr_debug(bus: %s: add device %s\n, bus-name, dev_name(dev));error device_add_groups(dev, bus-dev_groups); //在该设备的sysfs目录中创建bus上默认的属性文件if (error)goto out_put;error sysfs_create_link(bus-p-devices_kset-kobj,dev-kobj, dev_name(dev)); /* 调用sysfs_create_link接口将该device在sysfs中的目录链接到该bus的device目录下eg: /sys/bus/platform/devices/soc:gpio_keys - ../../../devices/platform/soc/soc:gpio_keys */if (error)goto out_groups;error sysfs_create_link(dev-kobj,dev-bus-p-subsys.kobj, subsystem); /* 调用sysfs_create_link接口在该device的sysfs中创建一个指向该设备所在bus目录的链接取名为subsystem eg: /sys/devices/platform/soc/soc:gpio_keys/subsystem - ../../../../bus/platform */if (error)goto out_subsys;klist_add_tail(dev-p-knode_bus, bus-p-klist_devices); //将该设备挂接到bus的device list链表}return 0;out_subsys:sysfs_remove_link(bus-p-devices_kset-kobj, dev_name(dev));
out_groups:device_remove_groups(dev, bus-dev_groups);
out_put:bus_put(dev-bus);return error;
}
/*__device_attach */
static int __device_attach(struct device *dev, bool allow_async)
{int ret 0;bool async false;device_lock(dev);if (dev-p-dead) {goto out_unlock; //如果设备已经dead直接退出} else if (dev-driver) { //默认指定了driver就直接绑定if (device_is_bound(dev)) { //判断该设备是否已经driver绑定这一点很重要通过它可以使同一个Driver驱动相同名称的多个设备ret 1;goto out_unlock; //如果设备已经绑定过驱动直接退出}ret device_bind_driver(dev); //将一个driver与一个device绑定if (ret 0)ret 1;else {dev-driver NULL;ret 0;}} else {struct device_attach_data data {.dev dev,.check_async allow_async,.want_async false,};if (dev-parent)pm_runtime_get_sync(dev-parent);ret bus_for_each_drv(dev-bus, NULL, data,__device_attach_driver); //没有给设备指定driver就进行遍历匹配if (!ret allow_async data.have_async) {/** If we could not find appropriate driver* synchronously and we are allowed to do* async probes and there are drivers that* want to probe asynchronously, well* try them.*/dev_dbg(dev, scheduling asynchronous probe\n);get_device(dev);async true;} else {pm_request_idle(dev);}if (dev-parent)pm_runtime_put(dev-parent);}
out_unlock:device_unlock(dev);if (async)async_schedule_dev(__device_attach_async_helper, dev);return ret;
}
/* bus_for_each_drv */
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,void *data, int (*fn)(struct device_driver *, void *))
{struct klist_iter i;struct device_driver *drv;int error 0;if (!bus)return -EINVAL;klist_iter_init_node(bus-p-klist_drivers, i,start ? start-p-knode_bus : NULL); /* 从头开始遍历bus的driver链表发现一个driver就调用fn即__device_attach_driver进行匹配 */while ((drv next_driver(i)) !error)error fn(drv, data);klist_iter_exit(i);return error;
}
/* __device_attach_driver */
static int __device_attach_driver(struct device_driver *drv, void *_data)
{struct device_attach_data *data _data;struct device *dev data-dev;bool async_allowed;int ret;ret driver_match_device(drv, dev); /* drv-bus-match(dev, drv)调用bus的match函数检查device与driver是否匹配device和device_driver必须具备相同的名称内核才能完成匹配操作进而调用device_driver中的相应接口。这里的同名作用范围是同一个bus下的所有device和device_driver */if (ret 0) {/* no match */return 0;} else if (ret -EPROBE_DEFER) {dev_dbg(dev, Device match requests probe deferral\n);driver_deferred_probe_add(dev);/** Device cant match with a driver right now, so dont attempt* to match or bind with other drivers on the bus.*/return ret;} else if (ret 0) {dev_dbg(dev, Bus failed to match device: %d\n, ret);return ret;} /* ret 0 means positive match */async_allowed driver_allows_async_probing(drv);if (async_allowed)data-have_async true;if (data-check_async async_allowed ! data-want_async)return 0;return driver_probe_device(drv, dev); //device与driver匹配成功将device与driver绑定
}
/* really_probe */
static int really_probe(struct device *dev, struct device_driver *drv)
{int ret -EPROBE_DEFER;int local_trigger_count atomic_read(deferred_trigger_count);bool test_remove IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) !drv-suppress_bind_attrs;ret device_links_check_suppliers(dev);if (ret -EPROBE_DEFER)driver_deferred_probe_add_trigger(dev, local_trigger_count);if (ret)return ret;atomic_inc(probe_count);pr_debug(bus: %s: %s: probing driver %s with device %s\n,drv-bus-name, __func__, drv-name, dev_name(dev));if (!list_empty(dev-devres_head)) {dev_crit(dev, Resources present before probing\n);ret -EBUSY;goto done;}re_probe:dev-driver drv; //将绑定的driver赋值给dev-driver/* If using pinctrl, bind pins now before probing */ret pinctrl_bind_pins(dev);if (ret)goto pinctrl_bind_failed;if (dev-bus-dma_configure) {ret dev-bus-dma_configure(dev);if (ret)goto probe_failed;}ret driver_sysfs_add(dev); //创建driver与device之间的符号链接if (ret) {pr_err(%s: driver_sysfs_add(%s) failed\n,__func__, dev_name(dev));goto probe_failed;}if (dev-pm_domain dev-pm_domain-activate) {ret dev-pm_domain-activate(dev);if (ret)goto probe_failed;}if (dev-bus-probe) { //如果bus有probe函数优先执行bus的probe函数如果没有则执行驱动的probe函数ret dev-bus-probe(dev);if (ret)goto probe_failed;} else if (drv-probe) {ret drv-probe(dev);if (ret)goto probe_failed;}ret device_add_groups(dev, drv-dev_groups); //创建属性文件const struct attribute_group **dev_groupsif (ret) {dev_err(dev, device_add_groups() failed\n);goto dev_groups_failed;}if (dev_has_sync_state(dev)) {ret device_create_file(dev, dev_attr_state_synced);if (ret) {dev_err(dev, state_synced sysfs add failed\n);goto dev_sysfs_state_synced_failed;}}pinctrl_init_done(dev);if (dev-pm_domain dev-pm_domain-sync)dev-pm_domain-sync(dev);driver_bound(dev); //将该设备挂接到driver的device list,一个driver可以对应于几个设备因此driver同样有其设备链表用于挂接可以操作的设备ret 1;pr_debug(bus: %s: %s: bound device %s to driver %s\n,drv-bus-name, __func__, dev_name(dev), drv-name);goto done;dev_sysfs_state_synced_failed:device_remove_groups(dev, drv-dev_groups);
dev_groups_failed:if (dev-bus-remove)dev-bus-remove(dev);else if (drv-remove)drv-remove(dev);
probe_failed:if (dev-bus)blocking_notifier_call_chain(dev-bus-p-bus_notifier,BUS_NOTIFY_DRIVER_NOT_BOUND, dev);
pinctrl_bind_failed:device_links_no_driver(dev);devres_release_all(dev);arch_teardown_dma_ops(dev);kfree(dev-dma_range_map);dev-dma_range_map NULL;driver_sysfs_remove(dev);dev-driver NULL;dev_set_drvdata(dev, NULL);if (dev-pm_domain dev-pm_domain-dismiss)dev-pm_domain-dismiss(dev);pm_runtime_reinit(dev);dev_pm_set_driver_flags(dev, 0);switch (ret) {case -EPROBE_DEFER:/* Driver requested deferred probing */dev_dbg(dev, Driver %s requests probe deferral\n, drv-name);driver_deferred_probe_add_trigger(dev, local_trigger_count);break;case -ENODEV:case -ENXIO:pr_debug(%s: probe of %s rejects match %d\n,drv-name, dev_name(dev), ret);break;default:/* driver matched but the probe failed */pr_warn(%s: probe of %s failed with error %d\n,drv-name, dev_name(dev), ret);}/** Ignore errors returned by -probe so that the next driver can try* its luck.*/ret 0;
done:atomic_dec(probe_count);wake_up_all(probe_waitqueue);return ret;
}
/* driver_sysfs_add */
static int driver_sysfs_add(struct device *dev)
{int ret;if (dev-bus)blocking_notifier_call_chain(dev-bus-p-bus_notifier,BUS_NOTIFY_BIND_DRIVER, dev);ret sysfs_create_link(dev-driver-p-kobj, dev-kobj,kobject_name(dev-kobj)); /* 在该设备绑定的bus下的driver目录中创建一个指向该device在sysfs下的目录名字为设备的名字 eg: /sys/bus/platform/drivers/gpio-keys/soc:gpio_keys - ../../../../devices/platform/soc/soc:gpio_keys */if (ret)goto fail;ret sysfs_create_link(dev-kobj, dev-driver-p-kobj,driver); /* 在该device的sysfs目录下创建一个指向该设备绑定的bus下的driver目录的链接名字为driver, eg: /sys/devices/platform/soc/soc:gpio_keys/driver - ../../../../bus/platform/drivers/gpio-keys */if (ret)goto rm_dev;if (!IS_ENABLED(CONFIG_DEV_COREDUMP) || !dev-driver-coredump ||!device_create_file(dev, dev_attr_coredump))return 0;sysfs_remove_link(dev-kobj, driver);rm_dev:sysfs_remove_link(dev-driver-p-kobj,kobject_name(dev-kobj));fail:return ret;
}
/* driver_bound */
static void driver_bound(struct device *dev)
{if (device_is_bound(dev)) {pr_warn(%s: device %s already bound\n,__func__, kobject_name(dev-kobj));return;}pr_debug(driver: %s: %s: bound to device %s\n, dev-driver-name,__func__, dev_name(dev));klist_add_tail(dev-p-knode_driver, dev-driver-p-klist_devices); //将该设备挂接到driver的device listdevice_links_driver_bound(dev);device_pm_check_callbacks(dev);/** Make sure the device is no longer in one of the deferred lists and* kick off retrying all pending devices*/driver_deferred_probe_del(dev);driver_deferred_probe_trigger();if (dev-bus)blocking_notifier_call_chain(dev-bus-p-bus_notifier,BUS_NOTIFY_BOUND_DRIVER, dev);kobject_uevent(dev-kobj, KOBJ_BIND); //device与driver已经绑定发出该设备的KOBJ_BIND uevent消息
}
device注册主要是在devices_kset的目录中创建了属于该设备的目录结构并将该设备挂接在bus的device list中然后遍历bus的driver list为该设备查找匹配的驱动当匹配成功之后将device与driver绑定一个device只能与一个driver对应一个driver能驱动多个device整个过程中还伴随着一些属性文件的创建通过这些属性文件可以直接读写设备的信息以及一些符号链接的创建通过这些符号链接class/bus/device/driver可以关联在一起bus下的device会指向实际的sysfs下的devicebus下的driver也会指向实际的sysfs下的devicesysfs下的device也会有名字为driver指向bus下driver的软链接class下也有指向实际的sysfs下的device的链接。