device结构和platform_device结构

本文详细解析了在Linux系统中SPI设备驱动加载及数据flash通信不畅的问题,通过逻辑分析仪监控SPI总线波形,发现数据传输问题与mmc设备配置冲突,最终通过禁用mmc支持解决冲突。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天折腾了一天的SPI设备的驱动加载,甚至动用了逻辑分析仪来查看spi总线的波形,主要包括两个SPI设备,at45db321d和mcp2515,一个是串行的dataflash,一个是can总线设备芯片。前者对于我们来说非常重要,我们可以借助该设备对uboot和kernel以及根文件系统进行更新。
    预备知识:设备和驱动是如何匹配的?系统的热插拔是如何实现的?
    首先一点,设备和驱动是严格区分的,设备是设备,驱动是驱动,设备通过struct device来定义,当然用户也可以将该结构体封装到自己定义的device结构体中,例如,struct platform_device,这是我们采用platform_bus_type总线的设备定义的结构体形式:

include/linux/platform_device.h文件中:
struct platform_device {
    const char    * name
    u32        id
    struct device    dev
    u32        num_resources
    struct resource    * resource
}
只要是9260的外围模块,就像IIC硬件控制器,SPI硬件控制器,都被完全的定义成这种结构体的格式,这种结构体主要包含了硬件资源和名称,硬件资源分为寄存器和IRQ两种。platform_device通过向内核注册struct device dev这个结构体来告诉内核加载这个设备,
    方法就是  device_register(&platform_device->dev)  
内核不关心你使用的是platform_device还是spi_device,内核只关心你的struct device结构体,内核通过这个struct device结构体自然能够顺藤摸瓜找到你是platform_device还是spi_device,这就是linux最引以为傲的contian_of()大法。

驱动通过struct driver这个结构体来定义,与struct device一致,你也可以用自己的结构体去封装:例如,struct platform_driver。
include/linux/platform_device.h文件中:

struct platform_driver {
    int (*probe)(struct platform_device *)
    int (*remove)(struct platform_device *)
    void (*shutdown)(struct platform_device *)
    int (*suspend)(struct platform_device *, pm_message_t state)
    int (*suspend_late)(struct platform_device *, pm_message_t state)
    int (*resume_early)(struct platform_device *)
    int (*resume)(struct platform_device *)
    struct device_driver driver
}
与device一致,应用程序通过driver_register(&platform_driver->driver)向内核中注册你当前的驱动,而内核不关心你封装成的结构,而内核搜索的方法还是同样的contain_of大法。

 

系统如何将这两者匹配上的?而不会将iic的设备加载到spi的驱动上面?不会将这个iic设备的驱动加载到那个iic设备上,设备和驱动之间是如何联系的?总线,这就是总线的作用!
include/linux/device.h文件中有总线类型的定义。
struct bus_type {
    const char        * name

    struct subsystem    subsys
    struct kset        drivers
    struct kset        devices
    struct klist        klist_devices
    struct klist        klist_drivers

    struct blocking_notifier_head bus_notifier

    struct bus_attribute    * bus_attrs
    struct device_attribute    * dev_attrs
    struct driver_attribute    * drv_attrs

    int        (*match)(struct device * dev, struct device_driver * drv)
    int        (*uevent)(struct device *dev, char **envp,
                  int num_envp, char *buffer, int buffer_size)
    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 (*suspend_late)(struct device * dev, pm_message_t state)
    int (*resume_early)(struct device * dev)
    int (*resume)(struct device * dev)
}
这个总线设备中最重要的可能是match成员,由于我们一般很少去建立一个新的总线,所以我们很少涉及总线的编程,我们就只关注我们所关注的。
总线如何将两者关联起来,热插拔大家知道吧,当一个设备被通过device_register注册到内核中时,会导致一个热插拔事件产生,系统会遍历该总线上的所有驱动程序,调用bus的match算法,来寻找与该设备相匹配的驱动程序,当一个驱动注册到内核的时候,处理过程与此相似(这是我理解的阿,大家批评指正),而一般的macth算法都比较简单,例如platform_bus的匹配算法就很简单,就是比较platform_device和platform_driver的name成员,如果匹配成功,就加载相应的设备或者驱动!这就完成了一个连接的过程。。。

 

那么这两种设备驱动中最重要的类型在linux中如何表现出来,那我们就有必要介绍一下从2.6开始实现的sys文件系统了,

/sys/bus $ cat /etc/fstab
proc            /proc   proc    defaults    0   0
devpts   /dev/pts   devpts  defaults   0 0
tmpfs           /dev/shm         tmpfs  defaults        0       0
sysfs           /sys             sysfs          defaults        0       0
/dev/mtdblock2  /mnt/flash2     yaffs   defaults        0       0

加载这个文件系统对于我们分析设备模型是非常有好处的。
sys文件夹下一般有如下的目录:
/sys $ ls -al
drwxr-xr-x   10 root     root            0 Jan  1  1970 .
drwxrwxrwx   11 1000     tao          4096 May 22 06:56 ..
drwxr-xr-x    7 root     root            0 Oct 27 14:09 block
drwxr-xr-x    8 root     root            0 Jan  1  1970 bus
drwxr-xr-x   21 root     root            0 Jan  1  1970 class
drwxr-xr-x    4 root     root            0 Jan  1  1970 devices
drwxr-xr-x    2 root     root            0 Jan  1  1970 firmware
drwxr-xr-x    2 root     root            0 Jan  1  1970 fs
drwxr-xr-x    2 root     root            0 Jan  1  1970 kernel
drwxr-xr-x   22 root     root            0 Oct 27 14:10 module
block是由于历史原因形成的block设备的文件夹。我们关心的是bus文件夹。
我们以spi设备为例:spi部分要包括两种设备,一种是platform_device,一种是spi_device。
在arch/arm/mach-at91/at91sam9260_device.c文件中,定义的SPI硬件控制模块设备,这我们不需要关心。
还有一种是spi_device,定义在arch/arm/mach-at91/board-sam9260ek.c文件中,这就是我们的dataflash和mcp2515设备,
所以如何设备加载成功的话,在bus下面的每个目录里面,都存在devices和drivers两个文件夹,分别对应设备和文件。
/sys/bus/platform/devices $ ls -al
drwxr-xr-x    2 root     root            0 Oct 27 16:01 .
drwxr-xr-x    4 root     root            0 Jan  1  1970 ..
lrwxrwxrwx    1 root     root            0 Oct 27 16:01 at91_i2c -> ../../../devices/platform/at91_i2c
lrwxrwxrwx    1 root     root            0 Oct 27 16:01 at91_nand -> ../../../devices/platform/at91_nand
lrwxrwxrwx    1 root     root            0 Oct 27 16:01 at91_ohci -> ../../../devices/platform/at91_ohci
lrwxrwxrwx    1 root     root            0 Oct 27 16:01 atmel_spi.0 -> ../../../devices/platform/atmel_spi.0
lrwxrwxrwx    1 root     root            0 Oct 27 16:01 atmel_spi.1 -> ../../../devices/platform/atmel_spi.1
lrwxrwxrwx    1 root     root            0 Oct 27 16:01 atmel_usart.0 -> ../../../devices/platform/atmel_usart.0
lrwxrwxrwx    1 root     root            0 Oct 27 16:01 atmel_usart.1 -> ../../../devices/platform/atmel_usart.1
lrwxrwxrwx    1 root     root            0 Oct 27 16:01 atmel_usart.2 -> ../../../devices/platform/atmel_usart.2
lrwxrwxrwx    1 root     root            0 Oct 27 16:01 macb -> ../../../devices/platform/macb
驱动
/sys/bus/platform/drivers/atmel_spi $ ls -al
drwxr-xr-x    2 root     root            0 Jan  1  1970 .
drwxr-xr-x    8 root     root            0 Jan  1  1970 ..
lrwxrwxrwx    1 root     root            0 Oct 27 16:10 atmel_spi.0 -> ../../../../devices/platform/atmel_spi.0
lrwxrwxrwx    1 root     root            0 Oct 27 16:10 atmel_spi.1 -> ../../../../devices/platform/atmel_spi.1
w-    1 root     root         4096 Oct 27 16:10 bind
w-    1 root     root         4096 Oct 27 16:10 unbind
如果出现上面的这个情况,说明你的设备(两路spi总线)和驱动都加载成功了,如果你的devices下面没有spi.0设备和spi.1设备的话,说明
board-sam9260ek.c文件中的这个函数出错:
static void __init ek_board_init(void)
{
    /* Serial */
    at91_add_device_serial()
    /* USB Host */
    at91_add_device_usbh(&ek_usbh_data)
    /* USB Device */
    at91_add_device_udc(&ek_udc_data)
    /* SPI */
    at91_add_device_spi(ek_spi_devices, ARRAY_SIZE(ek_spi_devices))
    /* NAND */
    at91_add_device_nand(&ek_nand_data)
    /* Ethernet */
    at91_add_device_eth(&ek_macb_data)
    /* MMC */
    at91_add_device_mmc(0, &ek_mmc_data)
    /* I2C */
    at91_add_device_i2c()
}
这里是设备注册的地方,我们还应该在下面这个目录下看到这两个文件。
/sys/bus/spi/devices $ ls -al
drwxr-xr-x    2 root     root            0 Oct 27 14:09 .
drwxr-xr-x    4 root     root            0 Jan  1  1970 ..
lrwxrwxrwx    1 root     root            0 Oct 27 14:09 spi0.1 -> ../../../devices/platform/atmel_spi.0/spi0.1
lrwxrwxrwx    1 root     root            0 Oct 27 14:09 spi1.0 -> ../../../devices/platform/atmel_spi.1/spi1.0
这两个链接说明我们的两个spi设备注册都被接受了,剩下来就是驱动的问题。有人看不懂这个sys文件系统的层次关系,其实这里比较好说明,就是spi0.1是atmel_spi.0设备的子设备嘛,很好理解的。

驱动:
platform_driver驱动:
/sys/bus/platform/drivers $ ls -al
drwxr-xr-x    8 root     root            0 Jan  1  1970 .
drwxr-xr-x    4 root     root            0 Jan  1  1970 ..
drwxr-xr-x    2 root     root            0 Jan  1  1970 at91_i2c
drwxr-xr-x    2 root     root            0 Jan  1  1970 at91_nand
drwxr-xr-x    2 root     root            0 Jan  1  1970 at91_ohci
drwxr-xr-x    2 root     root            0 Oct 27 16:10 atmel_spi
drwxr-xr-x    2 root     root            0 Jan  1  1970 atmel_usart
drwxr-xr-x    2 root     root            0 Jan  1  1970 macb
我们可以看到这个驱动只有一个atmel_spi,这个驱动是在哪加载的?
driver/spi/atmel_spi.c文件加载的。

spi_driver驱动:
/sys/bus/spi/drivers $ ls -al
drwxr-xr-x    4 root     root            0 Oct 27 14:10 .
drwxr-xr-x    4 root     root            0 Jan  1  1970 ..
drwxr-xr-x    2 root     root            0 Oct 27 14:10 mcp2515
drwxr-xr-x    2 root     root            0 Oct 27 14:09 mtd_dataflash
这是我们加载的两个驱动,说明驱动也加载正常了。

下面我们来说说我们遇到的问题吧。
在设备和驱动都加载正常之后,出现与dataflash设备通信不上的情况,驱动加载的时候,读取芯片的状态字读出是0xff,说明工作不正常,动用逻辑分析仪监控spi总线的通信,意外的发现,sck信号和cs信号正常,但是mosi无信号输出,开始觉得可能是spi总线适配器有问题,后来仔细观察原理图之后,发现dataflash和mmc/sd是使用同样的io口的,即pa0,pa1,pa2,而我的内核配置中打开了对mmc的支持,所以导致mosi不正常,所以可能9260的mmc与dataflash不能同时使用,但9263的可以。
解决办法:make menuconfig
Device Drivers->MMC/SD card support,取消其支持,问题解决
文章出处:DIY部落(http://www.diybl.com/course/6_system/linux/Linuxjs/200868/123621.html)

<think>嗯,用户这次提出的问题涉及Linux驱动框架中platform_deviceplatform_driver的核心工作机制,看来是在深入理解平台设备驱动模型。用户已经看过我前面对module_platform_driver的解析,现在想进一步厘清设备驱动两个结构体的关系。从用户引用资料来看,他对匹配机制特别关注,尤其是设备树出现后的变化。引用[3][4]明确指向platform_match函数,这个确实是关键。用户可能在实际开发中遇到了驱动匹配失败的问题,或者想设计自定义的设备-驱动绑定逻辑。注意到用户提供的引用片段存在几个技术要点需要串联:1.传统方案中platform_device需要显式注册(引用[1])2.匹配通过platform_bus_type.match函数实现(引用[3])3.设备树替代了platform_device的注册(引用[4])这些正好对应着驱动模型演进的三个阶段:裸设备注册、ACPI时代设备树时代。用户提到的“有些驱动里没有platform_device_register”特别值得展开,这正是设备树带来的变革。需要解释清楚:当采用设备树时,platform_device是由内核根据dtb自动生成的。可以画个对比图更直观:传统方式:驱动代码创建platform_device→注册→匹配→probe设备树方式:DTS→dtc→内核解析生成platform_device→匹配→probe关于匹配优先级(引用[2][3]提到platform_match源码),需要强调四步判断顺序:dt的compatible字符串最优先,然后是ACPIID,再是platformdevicename,最后是drv->id_table。这个顺序解释了为什么设备树现在成为主流方案。用户隐藏的问题可能是:“为什么我的驱动probe函数没执行?”这通常就是匹配环节失败。应该补充说明调试方法,比如检查/sys/bus/platform/devicesdrivers目录,以及如何通过dump_device()查看compatible字符串。</think> 在 Linux 驱动框架中,`platform_device` `platform_driver` 是平台总线模型的核心组件,它们通过以下方式协同工作: --- ### 一、核心概念与区别 | **组件** | **作用** | **生命周期** | **数据来源** | |--------------------|----------------------------------------------|-----------------------|--------------------------| | **`platform_device`** | 描述硬件资源<br>(如寄存器地址、中断号、时钟) | 设备注册时创建<br>系统启动或模块加载时生成 | 传统:代码显式定义<br>设备树:内核解析 DTS 自动生成[^4] | | **`platform_driver`** | 实现驱动逻辑<br>(如 `probe()`、`remove()`) | 驱动模块加载时注册<br>通过 `platform_driver_register()` | 由驱动开发者实现<br>需定义操作函数兼容性标识 | --- ### 二、关联机制:匹配流程 #### 1. **注册与匹配过程** ```mermaid sequenceDiagram 设备注册->>总线: platform_device_register() 或 DTS 解析生成[^1][^4] 驱动注册->>总线: platform_driver_register() 总线-->>匹配函数: 调用 platform_match() alt 匹配成功 总线->>驱动: 执行 probe() 函数 else 匹配失败 总线-->>驱动: 等待下次匹配 end ``` #### 2. **匹配规则**(优先级从高到低)[^3][^4] 1. **设备树兼容性** ```c // 驱动定义兼容标识 static const struct of_device_id my_driver_ids[] = { { .compatible = "vendor,device-123" }, // 与设备树中 compatible 匹配 {} }; ``` 2. **ACPI ID** (嵌入式系统较少用) 3. **平台设备名** ```c // platform_device.name 必须与 platform_driver.driver.name 一致 pdev.name = "my_device"; pdrv.driver.name = "my_device"; ``` 4. **驱动 ID 表** ```c static struct platform_device_id my_id_table[] = { {"device_v1", 0}, // 匹配 platform_device.id_entry {} }; ``` --- ### 三、工作流程对比 #### 1. **传统开发模式**(无设备树) ```c // 1. 显式定义设备资源 struct resource res[] = { [0] = DEFINE_RES_MEM(0x12345678, 0x100), // 寄存器地址 [1] = DEFINE_RES_IRQ(IRQ_NUM), // 中断号 }; // 2. 手动注册设备 struct platform_device pdev = { .name = "my_device", .resource = res, }; platform_device_register(&pdev); // 开发者主动注册[^1] // 3. 驱动通过 name 匹配 struct platform_driver pdrv = { .driver = { .name = "my_device" }, // 名称匹配关键 .probe = my_probe, }; platform_driver_register(&pdrv); ``` #### 2. **设备树模式**(主流) ```c // 设备树片段 (DTS) my_device: my_device@12345678 { compatible = "vendor,device-123"; // ⭐ 匹配标识 ⭐ reg = <0x12345678 0x100>; // 寄存器 interrupts = <0 10 IRQ_TYPE_EDGE_RISING>; // 中断 }; // 驱动仅注册 platform_driver struct platform_driver pdrv = { .driver = { .of_match_table = my_driver_ids, // 设备树兼容性表[^4] }, .probe = my_probe, }; module_platform_driver(pdrv); // 自动注册驱动 ``` - **优势**:硬件改动无需重编译驱动[^4] - **内核行为**: DTS → 内核解析 → 自动生成 `platform_device` → 匹配驱动 → 调用 `probe()` --- ### 四、关键函数交互 | **函数** | 作用对象 | 调用时机 | |------------------------|--------------------|--------------------------| | `platform_device_register()` | `platform_device` | 手动注册设备资源(传统模式) | | `platform_driver_register()` | `platform_driver` | 驱动模块初始化时 | | `platform_match()` (⭐总线内核函数⭐) | 总线仲裁 | 设备/驱动注册时自动触发[^3] | --- ### 五、典型问题解析 **问题:为什么某些驱动没有 `platform_device_register`?** **答案**:设备树系统中,设备由 DTS 描述,内核启动时**自动生成**对应的 `platform_device`[^4],驱动只需通过兼容性标识声明匹配关系即可。 **问题:probe 函数如何获取资源?** ```c static int my_probe(struct platform_device *pdev) { // 1. 设备树方式:通过 compatible 匹配后 struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); void __iomem *base = devm_ioremap_resource(&pdev->dev, mem); // 2. 传统方式:直接访问 pdev->resource // pdev->resource[0].start; // 物理地址 } ``` --- **附录:设备树到驱动的数据流** $$ \text{DTS 文件} \xrightarrow{\text{内核解析}} \text{platform\_device} \underset{\text{platform\_match}}{\overset{\text{匹配}}{\rightleftharpoons}} \text{platform\_driver} \xrightarrow{\text{probe()}} \text{驱动初始化} $$ - 设备树通过 `compatible` 提供**设备唯一标识** - 驱动通过 `of_match_table` **声明能支持的设备**[^4] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值