思路:
- 在linux内核中,自带的驱动都以
platform
模型的形式设计的。 - 一般情况下,所有的
platform device
在一起定义,并且在一起注册。 driver
是设计成了不同的源文件。- 因为
device
放在一起定义,比较好找,我们可以现在找到device
,再找driver
。
步骤:
- 找到针对一个硬件平台的主初始化源文件(
arch/arm/mach-XXXX/mach-XXXX.c
)
如:rkpx3
平台/arch/arm/mach-mach-rkpx3/board-rkpx3-sdk.c
2. 找主初始化源文件中的机器宏
3. 找到主初始化函数(kernel
启动过程中,会调用的一个函数,做嵌入式平台的初始化)
static void __init machine_rk30_board_init(void)
{
...
platform_add_devices(devices, ARRAY_SIZE(devices));
...
}
分析platform_add_devices()
int platform_add_devices(struct platform_device **devs, int num)
{
int i, ret = 0;
for (i = 0; i < num; i++) {
ret = platform_device_register(devs[i]); //注册platform device
if (ret) {
while (--i >= 0) //while(i--)
platform_device_unregister(devs[i]);
break;
}
}
return ret;
}
4. 找到platform device
的总表–>devices
static struct platform_device *devices[] __initdata = {
#ifdef CONFIG_ION
&device_ion,
#endif
#if defined(CONFIG_GPIO_DET)
&hq_gpio_device,
#endif
#ifdef CONFIG_ANDROID_TIMED_GPIO
&rk29_device_vibrator,
#endif
#ifdef CONFIG_LEDS_GPIO_PLATFORM
&rk29_device_gpio_leds,
#endif
...
}
—->条件编译选项,条件编译选项在Kconfig
中定义,通过make menuconfig
给其赋值,赋值后保存在.config
中,由Makefile
来使用该值,以确定如何编译驱动程序。
.config
—->在make
过程中,会生成一个头文件/include/generated/autoconf.h
—>Makefile
使用autoconf.h
5. 在总表中,找到platform device
—>
#ifdef CONFIG_VIDEO_REVERSE_IMAGE
&device_vehicle,
#endif
6. 根据platform device
找到platform driver
依据:二者是同名的
static struct platform_driver radio_tea6851a_pdriver = {
.driver = {
.name = "radio-si4713",
},
.probe = radio_si4713_pdriver_probe,
.remove = __exit_p(radio_tea6851a_pdriver_remove),
};
- 分析
probe
函数
/* Platform driver interface */
/* radio_tea6851a_pdriver_probe - probe for the device */
static int radio_tea6851a_pdriver_probe(struct platform_device *pdev)
{
printk("radio si probe\n");
struct radio_tea6851a_platform_data *pdata = pdev->dev.platform_data;
struct radio_tea6851_device *rsdev;
struct i2c_adapter *adapter;
struct v4l2_subdev *sd;
int rval = 0;
if (!pdata) {
dev_err(&pdev->dev, "Cannot proceed without platform data.\n");
rval = -EINVAL;
goto exit;
}
rsdev = kzalloc(sizeof *rsdev, GFP_KERNEL);
if (!rsdev) {
dev_err(&pdev->dev, "Failed to alloc video device.\n");
rval = -ENOMEM;
goto exit;
}
printk("V4l2_device_register\n");
rval = v4l2_device_register(&pdev->dev, &rsdev->v4l2_dev);
if (rval) {
dev_err(&pdev->dev, "Failed to register v4l2 device.\n");
goto free_rsdev;
}
printk("i2c_get_adapter\n");
adapter = i2c_get_adapter(pdata->i2c_bus);
if (!adapter) {
dev_err(&pdev->dev, "Cannot get i2c adapter %d\n",
pdata->i2c_bus);
rval = -ENODEV;
goto unregister_v4l2_dev;
}
printk("v4l2_i2c_new_subdev_board\n");
sd = v4l2_i2c_new_subdev_board(&rsdev->v4l2_dev, adapter,
pdata->subdev_board_info, NULL);
if (!sd) {
dev_err(&pdev->dev, "Cannot get v4l2 subdevice\n");
rval = -ENODEV;
goto put_adapter;
}
printk("video_device_alloc\n");
rsdev->radio_dev = video_device_alloc();
if (!rsdev->radio_dev) {
dev_err(&pdev->dev, "Failed to alloc video device.\n");
rval = -ENOMEM;
goto put_adapter;
}
memcpy(rsdev->radio_dev, &radio_tea6851a_vdev_template,
sizeof(radio_tea6851a_vdev_template));
video_set_drvdata(rsdev->radio_dev, rsdev);
printk("video_register_device\n");
if (video_register_device(rsdev->radio_dev, VFL_TYPE_RADIO, radio_nr)) {
dev_err(&pdev->dev, "Could not register video device.\n");
rval = -EIO;
goto free_vdev;
}
dev_info(&pdev->dev, "New device successfully probed\n");
goto exit;
free_vdev:
video_device_release(rsdev->radio_dev);
put_adapter:
i2c_put_adapter(adapter);
unregister_v4l2_dev:
v4l2_device_unregister(&rsdev->v4l2_dev);
free_rsdev:
kfree(rsdev);
exit:
return rval;
}
prob
分析待续。