|
linux的驱动一般分为两部分,一部分是platform device,这部分代码一般在板级文件中定义,另一部分是platform driver,一般在drivers目录.
厂家给的wifi驱动属于platform driver,但是内核并没有做任何的修改,相当于没有platform device, 但是加载了wifi的platform driver之后
wifi就能用了,很奇怪没有platform device, platform driver是怎样加载进内核的.
先看platform driver的代码:
static struct sdio_driver bcmsdh_sdmmc_driver = {
.probe = bcmsdh_sdmmc_probe,
.remove = bcmsdh_sdmmc_remove,
.name = "bcmsdh_sdmmc",
.id_table = bcmsdh_sdmmc_ids,
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
.drv = {
.pm = &bcmsdh_sdmmc_pm_ops,
},
#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */
};
/*
* module init
*/
int sdio_function_init(void)
{
int error = 0;
sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
gInstance = kzalloc(sizeof(BCMSDH_SDMMC_INSTANCE), GFP_KERNEL);
if (!gInstance)
return -ENOMEM;
error = sdio_register_driver(&bcmsdh_sdmmc_driver);
if (error) {
kfree(gInstance);
gInstance = NULL;
}
return error;
}
注意到sdio_driver有个id_table字段,platform device和platform driver是根据该字段匹配的,而bus负责匹配工作,sdio wifi属于sdio总线,看一下sdio 总线
是怎样做匹配的:
static const struct sdio_device_id *sdio_match_one(struct sdio_func *func,
const struct sdio_device_id *id)
{
if (id->class != (__u8)SDIO_ANY_ID && id->class != func->class)
return NULL;
if (id->vendor != (__u16)SDIO_ANY_ID && id->vendor != func->vendor)
return NULL;
if (id->device != (__u16)SDIO_ANY_ID && id->device != func->device)
return NULL;
return id;
}
static const struct sdio_device_id *sdio_match_device(struct sdio_func *func,
struct sdio_driver *sdrv)
{
const struct sdio_device_id *ids;
ids = sdrv->id_table;
if (ids) {
while (ids->class || ids->vendor || ids->device) {
if (sdio_match_one(func, ids))
return ids;
ids++;
}
}
return NULL;
}
static int sdio_bus_match(struct device *dev, struct device_driver *drv)
{
struct sdio_func *func = dev_to_sdio_func(dev);
struct sdio_driver *sdrv = to_sdio_driver(drv);
if (sdio_match_device(func, sdrv))
return 1;
return 0;
}
可见,sdio总线是根据sdio设备的class, vendor, device来匹配的.platform driver的这几个字段是事先定义好的,
在sdio_driver->id_tables表中:
/* devices we support, null terminated */
static const struct sdio_device_id bcmsdh_sdmmc_ids[] = {
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_DEFAULT) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4319) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4324) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43239) },
{ SDIO_DEVICE_CLASS(SDIO_CLASS_NONE) },
{ /* end: all zeroes */ },
};
但是内核并没有platform device的相关代码,这几个字段从哪来呢?这个是sdio设备跟其他驱动不一样的地方了,
sdio的platform device是动态生成的,当插入sdio wifi的时候会产生中断,然后根据sdio设备的寄存器内容来生成
platform device.该工作由函数mmc_attach_sdio完成(drivers\mmc\core\sdio.c):
/*
* Initialize (but don't add) all present functions.
*/
for (i = 0; i < funcs; i++, card->sdio_funcs++) {
err = sdio_init_func(host->card, i + 1);
if (err)
goto remove;
/*
* Enable Runtime PM for this func (if supported)
*/
if (host->caps & MMC_CAP_POWER_OFF_CARD)
pm_runtime_enable(&card->sdio_func[i]->dev);
}
......
/*
* ...then the SDIO functions.
*/
for (i = 0;i < funcs;i++) {
err = sdio_add_func(host->card->sdio_func[i]);
if (err)
goto remove_added;
}
sdio_init_func读sdio设备的fbr, cis寄存器来给class, vendor和device赋值.
最后sdio_add_func调用device_add注册设备,所以当driver注册时才能匹配成功,driver的probe函数才能被调用. |
阿莫论坛20周年了!感谢大家的支持与爱护!!
知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)
|