平台设备和驱动是怎么匹配的?

本文介绍了Linux系统中平台设备(platform_device)与平台驱动(platform_driver)的匹配原理及过程。详细解析了两种匹配方式:使用id_table进行匹配和使用设备名与驱动名进行匹配,并通过代码示例展示了如何实现多设备的兼容。

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

平台设备和驱动是怎么匹配的?

Linux源码的sourceindight工程下搜索platform_device.h,这个文件是设备结构体,驱动结构体的定义,还有操作这两个结构体的函数如:平台设备注册,平台驱动注册。先看两个结构体:

//平台设备

 

 

struct   platform_device   {
	const   char   *   name;     //设备名,可以用来和驱动进行匹配
	int              id;            //作用?
	struct   device       dev;         //  platform_device相当于是从dev继承派生而来的
	u32            num_resources;         //资源数目
	struct   resource   *   resource;               //资源数组首地址

	struct   platform_device_id  *id_entry;            //和驱动匹配后用来记录下此设备属于驱动idtable中的哪一个

	/*   arch  specific   additions   */
	struct   pdev_archdata          archdata;
};
//平台驱动
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   (*resume)(struct   platform_device   *);
	struct   device_driver   driver;//匹配的设备的所有信息,platform_driver相当于是从driver继承派生而来的
	struct   platform_device_id   *id_table;      //所有可以匹配的设备的idtable
};
/**********************/转载请注明原文地址 http://blog.youkuaiyun.com/oyhb_1992/article/details/77477112
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   */

	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;


//匹配算法:打开driver\base\platform.c文件,有个匹配函数
	方法一:用id_table进行匹配
	static  int   platform_match(struct   device  *dev,   struct   device_driver   *drv)
	 {
		 //由前面介绍platform_device相当于是从dev继承派生而来的,platform_driver相当于是从driver继承派生而来的,这里由结构体中的成员变量可以反推出整个结构体变量的首地址
		 struct   platform_device   *pdev  =   to_platform_device(dev);
		 struct   platform_driver   *pdrv  =   to_platform_driver(drv);

		 /*   match  against   the   id   table   first  */
		 if   (pdrv->id_table)//如果id_table不为空,则用id进行匹配,然后直接返回不再调用name匹配算法
			 return   platform_match_id(pdrv->id_table,   pdev)  !=   NULL;
		 //匹配方法二
		 /*   fall-back  to   driver   name  match   */
		 return   (strcmp(pdev->name,   drv->name)   ==   0);
	 }

	 static  const   struct   platform_device_id   *platform_match_id(
							 struct   platform_device_id   *id,
							 struct   platform_device   *pdev)
	 {
		 while   (id->name[0])   {        //id_table数组没有到尾
			 if   (strcmp(pdev->name,   id->name)   ==   0)   {     //本质上还是还是通过名字对应来匹配,但是匹配的名字被列在一个表中,这样一个驱动就可以匹配多个不同name的驱动了,只要设备的pdev->name在id_table数组列表的name中就可以匹配。
				 pdev->id_entry   =   id;   //和设备匹配后用来记录下此设备属于驱动idtable中的哪一个
				 return   id;
			 }
			 id++;      //指向id_table数组的下一个数组成员
		 }
		 return   NULL;
	 }

	 方法二:用设备名和驱动名进行匹配,这样就只能一个驱动匹配一个设备


		 源码举例1:
		 /drivers/mtd/nand/S3C2410.c
		 static   struct  platform_device_id   s3c24xx_driver_ids[]   =   {
			 {
				 .name                =   "s3c2410-nand",
					 .driver_data     =   TYPE_S3C2410,
			 },   {
				 .name                =   "s3c2440-nand",
					 .driver_data     =   TYPE_S3C2440,
			 },   {
				 .name                =   "s3c2412-nand",
					 .driver_data     =   TYPE_S3C2412,
				 },   {
					 .name                =   "s3c6400-nand",
						 .driver_data     =   TYPE_S3C2412,   /*   compatible   with  2412   */
				 },
				 {   }
	 };

	 static  struct   platform_driver   s3c24xx_nand_driver   =   {
		 .probe               =   s3c24xx_nand_probe,
		 .remove            =  s3c24xx_nand_remove,
		 .suspend  =   s3c24xx_nand_suspend,
		 .resume             =  s3c24xx_nand_resume,
		 .id_table  =   s3c24xx_driver_ids,//这个驱动支持的设备id表
		 .driver                =   {
			 .name       =  "s3c24xx-nand",
			 .owner     =   THIS_MODULE,
		 },
	 };

 

源码举例2:

 

static struct platform_device_id s3c_rtc_driver_ids[] = {
	{
		.name		= "s3c2410-rtc",
			.driver_data	= TYPE_S3C2410,
	}, {
		.name		= "s3c2416-rtc",
			.driver_data	= TYPE_S3C2416,
	}, {
		.name		= "s3c2443-rtc",
			.driver_data	= TYPE_S3C2443,
		}, {
			.name		= "s3c64xx-rtc",
				.driver_data	= TYPE_S3C64XX,
		},
		{ }
};

static struct platform_device_id s3c_rtc_driver_ids[] = {
	{
		.name  = "s3c2410-rtc",
			.driver_data = TYPE_S3C2410,
	}, {
		.name  = "s3c2416-rtc",
			.driver_data = TYPE_S3C2416,
	}, {
		.name  = "s3c2443-rtc",
			.driver_data = TYPE_S3C2443,
		}, {
			.name  = "s3c64xx-rtc",
				.driver_data = TYPE_S3C64XX,
		},
		{ }
}; 

上面就是platform_device_id,可以看到这个驱动支持的设备不只一种,包括2410、2416等等。而Platform_driver定义如下:



static struct platform_driver s3c_rtc_driver = {
	.probe  = s3c_rtc_probe,
	.remove  = __devexit_p(s3c_rtc_remove),
	.suspend = s3c_rtc_suspend,
	.resume  = s3c_rtc_resume,
	.id_table = s3c_rtc_driver_ids,
	.driver  = {
 		.name = "s3c-rtc",
      		.owner = THIS_MODULE,
		.of_match_table = s3c_rtc_dt_match,
	},
};

2410的platfrom_device定义如下:



struct platform_device s3c_device_rtc = {
	.name  = "s3c2410-rtc",
	.id  = -1,
	.num_resources = ARRAY_SIZE(s3c_rtc_resource),
	.resource = s3c_rtc_resource,
};


通过id_table这种方式有什么好处呢,如果只是简单的比较驱动里的.driver.name字段和设备里的.name是否相同,那么一个驱动只能支持特定的一个设备,而如果通过id_table的方式呢,一个驱动.id_table可以和多个驱动的设备里的.name匹配是否相同,支持很多个设备,而它们只是name字段不同而已。



 


<think>我们正在讨论Linux设备驱动设备匹配的机制。根据之前的引用问题,我们需要解释设备驱动如何与设备匹配。 参考引用[1]提到设备驱动有两个任务方向:操作硬件驱动程序通入内核,实现面向操作系统内核的接口。同时,引用[2]提到了i2c驱动注册的过程,涉及一系列函数调用,最终会调用到驱动的probe函数。引用[3]则提供了中断类型的信息,但与我们当前的问题关系不大。 设备驱动设备匹配的核心机制在于Linux的设备模型,包括总线(bus)、设备(device)、驱动(driver)类(class)等概念。总线负责将设备驱动进行匹配。当注册一个设备或者注册一个驱动时,总线会尝试进行匹配匹配成功后会调用驱动的probe函数。 具体过程如下: 1. 设备在总线上注册(例如,在系统启动时由固件(如ACPI、设备树)描述的设备,或者热插拔设备)。 2. 驱动在总线上注册(通过调用driver_register或类似的总线特定注册函数)。 3. 总线驱动会遍历总线上已注册的设备,对每个设备尝试与驱动进行匹配(通过匹配函数)。 4. 匹配的依据通常是设备标识(如设备树中的compatible字符串、ACPI ID、PCI设备ID等)与驱动提供的标识表进行比较。 5. 如果匹配成功,则调用驱动的probe函数,进行设备初始化。 下面我们详细说明。 总线类型(如PCI、USB、I2C、SPI、平台总线等)都定义了自己的匹配方法。以平台总线(platform bus)为例,它是Linux中用于片上系统(SoC)设备的总线。平台设备通常通过设备树(Device Tree)或ACPI描述。 平台设备匹配: - 设备通过设备树描述时,设备节点中有一个"compatible"属性。 - 驱动在定义时,会提供一个of_match_table(用于设备匹配)或acpi_match_table(用于ACPI匹配),其中包含一个或多个字符串(或结构体)。 - 总线匹配函数(如platform_match)会比较设备树节点的"compatible"属性与驱动提供的of_match_table中的字符串。如果找到相同的字符串,则匹配成功。 其他总线(如I2C)的匹配: - I2C设备通常由设备树描述,但也可以由板级代码静态注册。 - I2C驱动提供一个id_table,包含设备的名字或设备ID。 - 总线匹配函数(i2c_device_match)会比较设备的名称或ID与驱动id_table中的项。 匹配的关键函数: - 设备注册:device_add -> bus_probe_device -> device_initial_probe -> __device_attach -> bus_for_each_drv -> __device_attach_driver -> driver_match_device(调用总线的匹配函数)-> 如果匹配成功,则调用driver_probe_device。 - 驱动注册:driver_register -> bus_add_driver -> driver_attach -> bus_for_each_dev -> __driver_attach -> driver_match_device -> 如果匹配成功,则调用driver_probe_device。 在driver_probe_device中,会调用really_probe函数,该函数会调用总线的probe方法(如果存在)或者驱动的probe方法。 以上过程可以总结为:设备驱动分别向总线注册,总线负责匹配匹配成功则调用驱动的probe函数。 下面我们通过一个简单的平台设备驱动示例来说明: 设备树节点: mydevice { compatible = "vendor,mydevice"; reg = <0x10000000 0x1000>; }; 驱动代码: static const struct of_device_id mydevice_of_match[] = { { .compatible = "vendor,mydevice" }, {} }; MODULE_DEVICE_TABLE(of, mydevice_of_match); static struct platform_driver mydevice_driver = { .probe = mydevice_probe, .driver = { .name = "mydevice", .of_match_table = mydevice_of_match, }, }; module_platform_driver(mydevice_driver); 当驱动注册时(通过module_platform_driver宏),平台总线会遍历所有已注册的平台设备,找到compatible为"vendor,mydevice"的设备,然后调用mydevice_probe函数。 因此,匹配的核心在于总线类型定义的匹配函数,以及设备驱动提供的匹配信息。 相关问题: 1. 设备驱动匹配失败可能的原因有哪些? 2. Linux设备模型中的总线、设备驱动是如何协作的? 3. 如何编写一个支持设备匹配的平台驱动?</think>### Linux设备驱动设备匹配机制详解 在Linux系统中,设备驱动设备匹配主要通过**总线-设备-驱动模型**实现,核心流程如下: #### 一、匹配基础框架 1. **总线核心作用** 总线(如PCI、I2C、平台总线等)作为中介,维护两个链表: - `devices`:已注册的设备列表 - `drivers`:已注册的驱动列表 当设备驱动注册时,总线触发匹配流程[^1]。 2. **关键数据结构** ```c struct bus_type { int (*match)(struct device *dev, struct device_driver *drv); // 匹配函数 struct subsys_private *p; // 包含设备驱动链表 }; ``` 每种总线需实现自己的`match()`函数。 #### 二、匹配触发场景 1. **设备注册时** ```mermaid graph LR A[device_add] --> B[bus_add_device] B --> C[bus_probe_device] C --> D{遍历驱动链表} D -->|调用match函数| E[匹配成功?] E -->|是| F[执行驱动probe] ``` 2. **驱动注册时** ```mermaid graph LR A[driver_register] --> B[bus_add_driver] B --> C[driver_attach] C --> D{遍历设备链表} D -->|调用match函数| E[匹配成功?] E -->|是| F[执行驱动probe] ``` #### 三、匹配依据(不同总线类型) | 总线类型 | 设备标识来源 | 驱动标识定义 | 匹配函数逻辑 | |------------|----------------------------|---------------------------|----------------------------------| | **平台总线** | 设备树`compatible`属性 | `of_match_table`数组 | 比较设备树节点与驱动的compatible字符串 | | **I2C总线** | 设备地址/ACPI ID | `id_table`数组 | 检查设备地址是否在驱动支持的ID列表中 | | **PCI总线** | Vendor/Device ID | `pci_device_id`表 | 匹配厂商ID设备ID | > 示例:I2C驱动注册(引用[2]) > 通过`module_i2c_driver`宏注册 → 调用`i2c_register_driver()` → 最终触发`driver_match_device()``really_probe()` #### 四、匹配成功后的关键流程 1. **执行probe函数** - 驱动开发者实现的初始化函数(如`my_driver_probe()`) - 完成资源分配(内存、IRQ等) 2. **设备绑定** - 通过`driver_bound()`建立`dev->driver`关联 - 设备出现在`/sys/bus/<bus>/devices/`中 #### 五、典型匹配失败原因 1. 设备未声明(如设备树节点缺失) 2. 驱动未提供匹配标识(如缺少`of_match_table`) 3. 总线类型不兼容(如USB设备注册到I2C总线) 4. 资源冲突(如IRQ已被占用) > 中断匹配示例(引用[3]): > GIC中断控制器通过设备树`interrupt`属性与驱动匹配,如`<0 116 4>`对应SPI类型中断 #### 六、调试方法 1. 查看sysfs信息: ```bash ls /sys/bus/platform/devices/ # 列出所有平台设备 cat /sys/bus/i2c/drivers/at24/name # 查看驱动名称 ``` 2. 内核日志: ```bash dmesg | grep -i "probe" # 查看probe执行记录 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值