当前位置: 首页 > 科技观察

TeachLinuxDriver10-PlatformBus详解

时间:2023-03-15 19:30:57 科技观察

platformbus是学习linux驱动必须掌握的一个知识点。本文参考文献已发表:Linux3.14内核一、概念嵌入式系统中物理总线有很多:I2c、SPI、USB、uart、PCIE、APB、AHBlinux从2.6开始新增了一套驱动管理和注册机制platform平台总线是虚拟总线,不是物理总线。与PCI、USB相比,主要用于描述SOC上的片上资源。平台描述的资源有一个共同点:在CPU的总线上直接寻址。平台设备被分配了一个名称(用于驱动程序绑定)和一组资源,例如地址和中断请求号(IRQ)。设备用platform_device表示,驱动注册到platform_driver。与传统的总线/设备/驱动机制相比,平台由内核统一管理,资源在驱动中使用,提高了代码的安全性和可移植性。2.平台1。平台总线的两个最重要的结构。平台维护的所有驱动程序必须使用此结构定义:platform_driverstructplatform_driver{int(*probe)(structplatform_device*);//int(*remove)(structplatform_device*);void(*shutdown)(structplatform_device*);int(*suspend)(structplatform_device*,pm_message_tstate);int(*resume)(structplatform_device*);structdevice_driverdriver;conststructplatform_device_id*id_table;boolprevent_deferred_probe,用于该结构将驱动注册到平台总线,成员含义probe当驱动和硬件信息匹配成功时,会调用probe函数,驱动的所有资源注册和初始化都会放在probe函数中。remove硬件信息被移除,或者驱动被卸载是的,都必须释放,释放资源的操作放在这个函数中structdevice_driverdriver内核维护的所有驱动都必须包含这个成员,通常使用driver->name匹配设备conststructplatform_device_id*id_table通常一个驱动可能会同时支持多个硬件。这些硬件的名称放在结构数组中。我们在写驱动的时候,往往需要填写上面的成员。platform_deviceplatform总线用于描述设备硬件信息的结构体,包括所有的硬件信息资源(io、内存、中断、DMA等)structplatform_device{constchar*name;intid;boolid_auto;structdevicedev;u32num_resources;structresource*resource;conststructplatform_device_id*id_entry;/*MFDcellpointer*/structmfd_cell*mfd_cell;/*archspecificadditions*/structpdev_arch*id_entry;structdevicedev用于匹配驱动程序内核中维护的所有设备都必须包含该成员,u32num_resources资源编号structresource*resource描述资源structdevicedev->release()必须实现,描述硬件信息的成员structresource0x139d0000structresource{resource_size_tstart;//代表资源的起始值,resource_size_tend;//代表资源最后一个字节的地址,如果是中断,end和satrt是一样的constchar*name;//可以不写unsignedlongflags;//资源类型structresource*parent,*sibling,*child;};flags类型描述#defineIORESOURCE_MEM0x00000200//memory#defineIORESOURCE_IRQ0x00000400//中断内核管理的所有驱动程序必须包含一个名为structdevice_driver的成员,//men描述的硬件必须包含structdevice结构成员//femalestructdevice_driver{constchar*name;structbus_type*bus;structmodule*owner;constchar*mod_name;/*usedforbuilt-inmodules*/boolsuppress_bind_attrs;/*disablesbind/unbindviasysfs*/conststructof_device_id*of_match_table;conststructacpi_device_id*acpi_match_table;int(*probe)(structdevice*dev);int(*remove)(structdevice*dev);void(*shutdown)(structdevice*dev);int(*suspend)(structdevice*dev,pm_message_tstate);int(*resume)(structdevice*dev);conststructattribute_group**groups;conststructdev_pm_ops*pm;structdriver_private*p;};其中:constchar*名称;用于与硬件匹配。内核描述硬件,必须包含structdevice结构体成员:structdevice{structdevice*parent;structdevice_private*p;structkobjectkobj;constchar*init_name;/*initialnameofthedevice*/conststructdevice_type*type;structmutexmutex;/*mutextosynchronizecallsto*itsdriver.*/structbus_type;/*typeofbusdeviceison*/structdevice_driver*driver;/*whichdriverhasallocatedthisdevice*/void*platform_data;/*Platformspecificdata,devicecoredoes'ttouchit*/structdev_pm_infopower;structdev_pm_domain*pm_domain;#ifdefCONFIG_PINCTRLstructdev_pin_info*pins;#endif#ifdefCONFIG_NUMAintnuma_node;/*NUMAintnuma_node;/*NUMAnodethisendifu64*dma_mask;/*dmamask(ifdma'abledevice)*/u64coherent_dma_mask;/*Likedma_mask,butforalloc_coherentmappingsasnotallhardwaresupports64bitaddressesforconsistentallocationssuchdescriptors.*/structdevice_dma_parameters*dma_parms;structlist_headdma_pools;/*dmapools(ifdma'ble)*/structdma_coherent_mem*dma_mem;/*internalforcoherentmemoverride*/#ifdefCONFIG_DMA_CMAstructcma*cma_area;/*contiguousmemoryareafordmaallocations*/#endif/*archspecificadditions*/structdev_archdataarchdata;structdevice_node*of_node;/*associateddevicetreenode*/structacpi_dev_nodeacpi_node;/*associatedACPIdevicenode*/dev_tdevt;/*dev_t,createsthesysfs"dev"*/u32id;/*deviceinstance*/spin_headlistrelockdev;/*deviceinstance*/spin_head_tdevlocks;;structklist_nodeknode_class;structclass*class;conststructattribute_group**groups;/*optionalgroups*/void(*release)(structdevice*dev);structiommu_group*iommu_group;booloffline_disabled:1;booloffline:1;};其中:void(*release)(structdevice*dev);不能为空2.如何注册平台驱动注册步骤1)注册驱动platform_device_register/***platform_device_register-addaplatform-leveldevice*@pdev:platformdevicewe'reading*/intplatform_device_register(structplatform_device*pdev){device_initialize(&pdev->dev);arch_setup_pdev_archdata(pdev);returnplatform_device_add(pdev);}2)注册设备platform_driver_register#defineplatform_driver_register(drv)\__platform_driver_register(drv,THIS_MODULE)3.例子1.开发步骤platformbus下驱动的开发步骤为:结构体设备需要实现是:platform_device。1)初始化资源结构变量2)初始化platform_device结构变量3)向系统注册设备:platform_device_register。以上三步必须在设备驱动加载之前完成,即在执行platform_driver_register()之前完成,因为驱动注册需要匹配内核中所有注册的设备名。platform_driver_register()中向内核添加设备是最后调用的device_add函数。Platform_device_add和device_add的主要区别在于多了一个insert_resource(p,r)步骤,即把平台资源(resource)加入内核,由内核管理。在驱动注册中,需要实现的结构是:platform_driver。在driver的初始化函数中,调用platform_driver_register()注册platform_driver。需要注意的是,platform_driver和platform_device中的name变量的值必须相同[不考虑设备树,后面会写一篇新的文章详细介绍设备树]。这样,在注册platform_driver_register()时,会将当前注册的platform_driver中的name变量的值与所有注册的platform_device中的name变量的值进行比较。只有找到同名的platform_device,才能注册成功。注册成功后,会调用pl??atform_driver结构体元素的probe函数指针。示例1本示例比较简单,仅用于测试platform_driver和platform_device是否匹配成功。左边是platform_device结构注册的代码,右边是platform_driver结构注册的代码。platform_driver定义和注册:1#include2#include3#include4#include56staticinthello_probe(structplatform_device*pdev)7{8printk("matchok\n");9return0;10}11staticinthello_remove(structplatform_device*pdev)12{13printk("hello_remove\n");14return0;15}16staticstructplatform_driverhello_driver=17{18.probe=hello_probe,19.driver.name="duang",20.remove=hello_remove,21};22staticinthello_init(void)23{24printk("hello_init\n");25returnplatform_driver_register(&hello_driver);26}27staticvoidhello_exit(void)28{29printk("hello_exit\n");30platform_driver_unregister(&hello_driver);31return;32}33MODULE_LICENSE("GPL");34module_init(hello_init);35module_exit(hello_exit);platform_device定义和注册:1#include2#include3#include4#includeresource[0].start);printk("irq=%d\n",pdev->resource[1].start);//注册中断,申请内存return0;}重新编译,卸载第一个例子的模块,清空日志:makesudormmoddevicesudormmoddriversudodmesg-c执行结果显示probe函数正确读取了硬件信息。4.platform_device是如何管理的?1.没有设备树当没有设备树时,以SamsungCortex-A8s5pc100为例,硬件信息放在如下位置arch\arm\mach-s5pc100\Mach-smdkc100.carch\arm\plat-samsung\registerplatform_deviceplatform_device定义了这个数组存放的是内核启动时需要初始化的硬件信息。2.如果有设备树,内核就会有完整的设备初始化代码。它会在内核启动时解析并初始化设备树信息,并将硬件信息初始化到对应的链表中。总线匹配成功后,会将硬件信息传递给probe()函数。四、其他与总线相关的知识点1、内核总线相关的结构体变量内核维护的所有总线都需要注册一个如下结构体的变量。structbus_type{constchar*name;constchar*dev_name;structdevice*dev_root;structdevice_attribute*dev_attrs;/*usedev_groupsinstead*/conststructattribute_group**bus_groups;conststructattribute_group**dev_groups;conststructattribute_group**drv_groups;int(*匹配)(structdevice*dev,structdevice_driver*drv);int(*uevent)(structdevice*dev,structkobj_uevent_env*env);int(*probe)(structdevice*dev);int(*remove)(structdevice*dev);void(*shutdown)(structdevice*dev);int(*online)(structdevice*dev);int(*offline)(structdevice*dev);int(*suspend)(structdevice*dev,pm_message_tstate);int(*resume)(structdevice*dev);conststructdev_pm_ops*pm;structiommu_ops*iommu_ops;structsubsys_private*p;structlock_class_keylock_key;};platform总线变化量的定义structbus_typeplatform_bus_type定义如下:structbus_typeplatform_bus_type={.name="platform",.dev_groups=platform_dev_groups,.match=platform_match,.uevent.=platform_uevent.pm=&platform_dev_pm_ops,};其中最重要的成员是**.match**。当设备的硬件信息注册到platform_bus_type总线时,会遍历所有platformbus维护的驱动,按名字匹配。如果相同,说明硬件信息与驱动匹配,会调用驱动的platform_driver->probe函数初始化驱动的所有资源,使驱动生效。当一个设备驱动程序注册到platform_bus_type总线时,它会遍历该平台总线维护的所有硬件信息,并通过名称进行匹配。如果相同,说明硬件信息与驱动匹配,会调用驱动的platform_driver->probe函数初始化驱动的所有资源,使驱动生效。注册位置drivers\base\Platform.cplatform_bus_type的注册五、注册码流程详解该架构的好处是可以帮助我们定位问题1、match函数什么时候调用?2、什么时候调用probe函数?下面是上面两个问题代码的调用过程:后面的代码调用过程我们会详细介绍设备树。本文转载自微信公众号“一口Linux”,可通过以下二维码关注。转载本文请联系OneLinux公众号。