镇江网站制作哪家公司比较好百度号码认证平台官网首页
阅读引言:本文会从平台总线的介绍,注册平台设备和驱动, 源码分析, 总结五个部分展开, 源码分析platform放在了最后面。
目录
一、平台总线介绍
二、平台总线如何使用
三、平台总线是如何工作的
四、注册platform设备
五、注册platform驱动
六、编写probe函数
七、平台总线源码分析
一、平台总线介绍
平台总线模型也叫platform总线模型。平台总线是Linux系统虚拟出来的总线。所谓虚拟出来的总线其实就是使用软件来模拟物理总线的一些性质, linux各种子系统还是别的, 都特别爱玩匹配这套东西, 挺好。
二、平台总线如何使用
平台总线模型将一个驱动分成了俩个部分,分别是device.c和driver.c,用来描述device.c硬件,driver. c 用来控制硬件。这样做的目的主要是为了提高代码的重复利用性, 降低耦合度。
三、平台总线是如何工作的
平台总线通过字符串比较,将name相同的device.c和driver.c匹配到一起来控制硬件。平台总线原则:先分离,后搭档。
note:源码分析这个位置放在后面些
四、注册platform设备
platform设备驱动(device.c)里面写的是硬件资源。这里的硬件资源指的是寄存器地址,中断号以及其他硬件资源等。在linux内核里面用struct platform_device结构体来描述硬件资源。这个结构体定义在include/linux/platform_device.h文件当中,结构体原型如下:
struct platform_device {const char *name;int id;bool id_auto;struct device dev;u64 platform_dma_mask;struct device_dma_parameters dma_parms;u32 num_resources;struct resource *resource;const struct platform_device_id *id_entry;/** Driver name to force a match. Do not set directly, because core* frees it. Use driver_set_override() to set or clear it.*/const char *driver_override;/* MFD cell pointer */struct mfd_cell *mfd_cell;/* arch specific additions */struct pdev_archdata archdata;
};
name可以匹配, 也可以在sys/bus/devices下展示。
/** Resources are tree-like, allowing* nesting etc..*/
struct resource {resource_size_t start;resource_size_t end;const char *name;unsigned long flags;unsigned long desc;struct resource *parent, *sibling, *child;
};
资源的种类:ioport.h文件中
使用struct resource描述硬件资源信息示例:
struct resource my_dev_resource[] = {[0] = {.start = 0x1000, .end = 0x1000, .flags = IORESOURCE_MEM},[1] = {.start = 13, .end = 13, .flags = IORESOURCE_IRQ}
};
在my_dev_resource这个结构体数字中包含了俩组资源,第一组资源的类型是IORESOURCE_MEM,表示这组资源是一组内存类型的资源。起始地址是0xFDD60000,终止地址是0xFDD60004。第二组资源的类型是IORESOURCE_IRQ,表示这是一组中断资源, 中断很号是13。
platform设备加载和卸载函数
platform设备加载函数: 函数原型:int platform_device_register(struct platform_device *device) 函数作用:加载platform设备。
platform设备卸载函数: 函数原型:void platform_device_unregister(struct platform_device *device) 函数作用:卸载platform设备。
代码结构:
void my_dev_release(struct device *dev)
{printk("%s is called",__func__);
}struct resource my_dev_resource[] = {[0] = {.start = 0xFDD60000, .end = 0xFDD60004, .flags = IORESOURCE_MEM},[1] = {.start = 13, .end = 13, .flags = IORESOURCE_IRQ}
};struct platform_device my_dev = {.name = "mydev",.id = -1,.resource = my_dev_resource,.num_resources = ARRAY_SIZE(my_dev_resource),.dev = {.release = my_dev_release},
};int __init my_dev_init(void)
{platform_device_register(&my_dev);
}void __exit my_dev_exit(void)
{platform_device_unregister(&my_dev);
}
五、注册platform驱动
关键数据结构, platform设备驱动(driver.c)里面写的软件驱动。在driver.c文件中首先需要定义一个platform_driver结构体。然后去实现这个结构体中的各个成员变量。当driver.c和device.c匹配成功以后,会执行driver.c里面的probe函数。platform_driver这个结构体定义在include/linux/platform_device.h文件当中,结构体原型如下:
匹配过程和调用驱动中的probe函数后面会再说。
platform设备加载和卸载函数
1.platform设备加载函数:
函数原型:intIlatform_driver_registepr(struct platform_driver *driver)
函数作用:加载platform设备。
2. platform设备卸载函数:
函数原型:void platform_driver_register(struct platform_driver *driver)
函数作用:卸载platform设备。
int my_probe(struct platform_device *)
{}int my_remove(struct platform_device *)
{}const struct platform_device_id my_id_table = {.name = "my_driver",
};static struct platform_driver my_driver = {.probe = my_probe,.remove = my_remove,.driver = {.name = "my_driver",.owner = THIS_MODULE,},.id_table = &my_id_table,
};int __init my_input_init(void)
{platform_driver_register(&my_driver);return ret;
}void __exit my_input_exit(void)
{platform_driver_unregister(&my_driver);
}
注册平台驱动框架如上。
三种匹配方式, 名称匹配, id匹配, 设备树匹配。优先级由低到高。
- 设备与驱动的名称匹配
- 设备与驱动的ID匹配
- 设备树匹配
设备树匹配是不需要设备端的代码的, 由系统启动的时候解析设备树, 得到的device_node,满足条件的再转为平台设备。后面可能还会再出设备树, 设备模型相关的文章。
六、编写probe函数
驱动是要控制硬件的。但是平台总线模型对硬件的描述是在设备(device)中的。所以在驱动(driver)中,我们需要得到设备(device)中的硬件资源。当设备(device)和驱动(driver)匹配成功以后,会执行驱动(driver)中的probe函数,所以我们要在probe函数中拿到设备(device)中的硬件资源。所以,重点是怎么拿到这些硬件资源呢?
函数原型:extern struct resource *platform_get_resource(struct platform_device *,unsigned int,
unsigned int);
函数作用:获取device中的硬件资源。
函数参数:第一个参数platform_device结构体。第二个参数:资源的类型。第三个参数:索引号。资源处在同类资源的哪个位置上。同类资源指的是flags是一样的同类资源。
返回资源数组中的指定的哪一个的地址。
int my_probe(struct platform_device *dev)
{if (dev == NULL){goto err_probe;}struct resource dev_source;dev_source = platform_get_resource(dev, IORESOURCE_MEM, 0)if (dev_source == NULL) {printk("platform_get_resource failed\r\n");return -2;}/* use dev_source */err_probe:return -1;
}
拿到resource结构体之后, 使用gpio, 内存, 中断都是有相应的函数的, 根据不同的功能去选择。
七、平台总线源码分析
先从注册设备端的源码入手。设备基类数据结构如下
/*** struct device - The basic device structure* @parent: The device's "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.* @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: device's energy model performance domain* @pins: For device pin management.* See Documentation/driver-api/pin-control.rst for details.* @msi: MSI related data* @numa_node: NUMA node this device is close to.* @dma_ops: DMA mapping operations for this device.* @dma_mask: Dma mask (if dma'ble 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 dma'ble device).* @dma_mem: Internal for coherent mem override.* @cma_area: Contiguous memory area for dma allocations* @dma_io_tlb_mem: Software IO TLB allocator. Not for driver use.* @dma_io_tlb_pools: List of transient swiotlb memory pools.* @dma_io_tlb_lock: Protects changes to the list of active pools.* @dma_uses_io_tlb: %true if device has used the software IO TLB.* @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* @physical_location: Describes physical location of the device connection* point in the system housing.* @removable: Whether the device can be removed from the system. This* should be set by the subsystem / bus driver that discovered* the device.** @offline_disabled: If set, the device is permanently online.* @offline: Set after successful invocation of bus type's .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.* @can_match: The device has matched with a driver at least once or it is in* a bus (like AMBA) which can't check for matching drivers until* other devices probe successfully.* @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 device *parent;struct device_private *p;const char *init_name; /* initial name of the device */const struct device_type *type;const 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 doesn't touch it */void *driver_data; /* Driver data, set and get withdev_set_drvdata/dev_get_drvdata */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_ENERGY_MODELstruct em_perf_domain *em_pd;
#endif#ifdef CONFIG_PINCTRLstruct dev_pin_info *pins;
#endifstruct dev_msi_info msi;
#ifdef CONFIG_DMA_OPSconst struct dma_map_ops *dma_ops;
#endifu64 *dma_mask; /* dma mask (if dma'able 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 dma'ble) */#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
#ifdef CONFIG_SWIOTLBstruct io_tlb_mem *dma_io_tlb_mem;
#endif
#ifdef CONFIG_SWIOTLB_DYNAMICstruct list_head dma_io_tlb_pools;spinlock_t dma_io_tlb_lock;bool dma_uses_io_tlb;
#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" */u32 id; /* device instance */spinlock_t devres_lock;struct list_head devres_head;const struct class *class;const struct attribute_group **groups; /* optional groups */void (*release)(struct device *dev);struct iommu_group *iommu_group;struct dev_iommu *iommu;struct device_physical_location *physical_location;enum device_removable removable;bool offline_disabled:1;bool offline:1;bool of_node_reused:1;bool state_synced:1;bool can_match: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;
#endif
};
注册平台设备调用了三个函数, 按照顺序查看。
这里涉及到了很多的sys, 也就是设备模型的东西, 后面单独出文章讲讲这个sys, 也随便我自己梳理一下这个sys设备模型这块的东西。重点还是看这个函数platform_device_add, 文件路径drivers\base\platform.c
/*** platform_device_add - 将平台设备添加到设备层次结构中* @pdev: 要添加的平台设备** 这是 platform_device_register() 的第二部分,可以单独调用,但前提是 pdev 是通过 platform_device_alloc() 分配的。*/
int platform_device_add(struct platform_device *pdev)
{u32 i; // 用于遍历资源的索引int ret; // 返回值if (!pdev)return -EINVAL; // 如果 pdev 为空,返回无效参数错误if (!pdev->dev.parent)pdev->dev.parent = &platform_bus; // 设置设备的父设备为 platform_buspdev->dev.bus = &platform_bus_type; // 设置设备的总线类型为 platform_bus_type// 根据设备 ID 设置设备名称switch (pdev->id) {default:dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); // 设置设备名称为 "name.id"break;case PLATFORM_DEVID_NONE:dev_set_name(&pdev->dev, "%s", pdev->name); // 设置设备名称为 "name"break;case PLATFORM_DEVID_AUTO:/** 自动分配的设备 ID。我们将其标记为自动分配,以便记住它需要被释放,并附加一个后缀* 以避免与显式 ID 的命名空间冲突。*/ret = ida_alloc(&platform_devid_ida, GFP_KERNEL); // 分配一个新的设备 IDif (ret < 0)goto err_out; // 如果分配失败,跳转到错误处理pdev->id = ret; // 设置设备 IDpdev->id_auto = true; // 标记为自动分配的 IDdev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id); // 设置设备名称为 "name.id.auto"break;}// 遍历设备的资源并插入到资源树中for (i = 0; i < pdev->num_resources; i++) {struct resource *p, *r = &pdev->resource[i]; // 获取当前资源if (r->name == NULL)r->name = dev_name(&pdev->dev); // 如果资源名称为空,设置为设备名称p = r->parent; // 获取资源的父资源if (!p) {if (resource_type(r) == IORESOURCE_MEM)p = &iomem_resource; // 如果是内存资源,父资源为 iomem_resourceelse if (resource_type(r) == IORESOURCE_IO)p = &ioport_resource; // 如果是 I/O 资源,父资源为 ioport_resource}if (p) {ret = insert_resource(p, r); // 将资源插入到资源树中if (ret) {dev_err(&pdev->dev, "failed to claim resource %d: %pR\n", i, r); // 如果插入失败,记录错误goto failed; // 跳转到错误处理}}}pr_debug("Registering platform device '%s'. Parent at %s\n",dev_name(&pdev->dev), dev_name(pdev->dev.parent)); // 打印调试信息ret = device_add(&pdev->dev); // 将设备添加到设备层次结构中if (ret == 0)return ret; // 如果成功,返回 0failed:if (pdev->id_auto) {ida_free(&platform_devid_ida, pdev->id); // 如果 ID 是自动分配的,释放 IDpdev->id = PLATFORM_DEVID_AUTO; // 重置 ID}// 释放所有已分配的资源while (i--) {struct resource *r = &pdev->resource[i];if (r->parent)release_resource(r); // 释放资源}err_out:return ret; // 返回错误码
}
EXPORT_SYMBOL_GPL(platform_device_add); // 导出符号,使其可以被 GPL 模块使用
数据结构原型:
/*** 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).* @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 driver's 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.* @dma_cleanup: Called to cleanup DMA configuration on a device on* this bus.* @pm: Power management operations of this bus, callback the specific* device driver's 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* @need_parent_lock: When probing or removing a device on this bus, the* device core should lock the device's 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 core's* private data.*/
struct bus_type {const char *name;const char *dev_name;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)(const struct device *dev, struct kobj_uevent_env *env);int (*probe)(struct device *dev);void (*sync_state)(struct device *dev);void (*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);void (*dma_cleanup)(struct device *dev);const struct dev_pm_ops *pm;const struct iommu_ops *iommu_ops;bool need_parent_lock;
};
后面设备和驱动匹配的时候就是调用了这些当中的一些函数。
调用过程关系
platform_device_register
platform_device_add
device_add
bus_probe_device
device_initial_probe
__device_attach
__device_attach_driver
driver_probe_device
__driver_probe_device
really_probe
在really_probe函数中调用找到的匹配上的驱动中的probe函数。这里看了驱动端的platform_driver_register函数的实现, 发现和设备端的实现差不多。直接上实现的原理
原理分析:
维护了两条链表,一个是设备的, 一个是驱动的, 当注册设备的时候, 调用总线的匹配函数, 这个匹配函数会根据规则匹配, 用当前注册的设备和驱动链表中的一 一做匹配, 匹配上之后, 调用驱动中的probe函数, 注册驱动也是一样的道理。