忘记了在哪本书上看到过,说必须给 I2C 设备驱动的 id 表数组添加上一个空元素作为最后一个元素,就像下面的代码所展示的那样:
struct i2c_device_id {
char name[I2C_NAME_SIZE];
kernel_ulong_t driver_data; /* Data private to the driver */
}
static const struct i2c_device_id rt5677_i2c_id[] = {
{ "rt5677", 0 },
{ } // 末尾需要是一个空元素
};
struct i2c_driver rt5677_i2c_driver = {
.driver = {
.name = "rt5677",
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &rt5677_pm_ops,
#endif
#ifdef RTACPI_I2C
.acpi_match_table = ACPI_PTR(rt5677_acpi_match),
#endif
},
.probe = rt5677_i2c_probe,
.remove = rt5677_i2c_remove,
.shutdown = rt5677_i2c_shutdown,
.id_table = rt5677_i2c_id, // 指定id_table
};
那么原因是什么呢?这样的声明方式一定和它的使用方式有关,所以我们一起在代码中看看 id_table[] 数组被使用的地方:
/**
* hda_match - bind hda device to hda driver.
* @dev: device.
* @drv: driver.
*
*/
static int soc_hda_match(struct device *dev, struct device_driver *drv)
{
struct snd_soc_hda_device *pdev = to_soc_hda_device(dev);
struct snd_soc_hda_driver *pdrv = to_soc_hda_driver(drv);
/* Then try to match against the id table */
if (pdrv->id_table)
return soc_hda_match_id(pdrv->id_table, pdev) != NULL; // 传入 id_table 指针进行匹配
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
从上方我们看到 id_table 被 soc_hda_match_id() 函数作为入口参数使用,继续深入查看该函数中的操作:
static const struct snd_soc_hda_device_id *soc_hda_match_id(
const struct snd_soc_hda_device_id *id,
struct snd_soc_hda_device *pdev)
{
while (id->name[0]) { // 如果 name 不为空,则执行下列匹配
if (pdev->id == id->id) {
pdev->id_entry = id;
return id;
} else if (strcmp(pdev->name, id->name) == 0) {
pdev->id_entry = id;
return id;
}
id++; // 继续处理 id_table 中的下一个元素
}
return NULL;
}
看到这里,原因就很清楚了:while
语句只有在检测到 id_table 中某一个元素的 name 成员为空时,才会跳出循环,所以在定义 id_table 时必须使用一个空元素{}
来作为结束标记。这个用法就类似字符数组必须使用\0
字符来作为结束符一样(或者表述为:字符串必须以\0
字符结尾),否则程序在处理字符串时将不知何时终止而出现错误。