去年想移植Android到omap3530,今年想移植DVSDK4.0到omap3530和DM368_ipnc,按照说明书移植总感觉很蒙,没搞明白没有把握。要是在上层开发,像是写windows程序一样,那么只要求会装操作系统就够了,但是要是想把没有板级支持的系统装上去,必须得搞清楚移植的原理和工作过程。感觉以前老是到处找怎么移植,一遍一遍的实验,浪费了不少时间,安心下来研究了一下代码,发现清晰多了。下面以2.6.28版本board_omap3devkit8000.c的lcd设备添加为例说明一下驱动程序的调用过程。
1。初始化与调用
/arch/arm/mach-omap2/board-omap3devkit8000.c中,在文件最后,有如下宏定义:
MACHINE_START(OMAP3_DEVKIT8000, "OMAP3 DevKit8000 Board")
.phys_io = 0x48000000,
.io_pg_offst = ((0xd8000000) >> 18) & 0xfffc,
.boot_params = 0x80000100,
.map_io = omap3_devkit8000_map_io,
.init_irq = omap3_devkit8000_init_irq,
.init_machine = omap3_devkit8000_init,
.timer = &omap_timer,
MACHINE_END
MACHINE_START和MACHINE_END宏定义在arch/arm/include/asm/mach/arch.h
MACHINE_START主要是定义了"struct machine_desc"的类型,放在 section(".arch.info.init"),是初始化数据,kernel boot 起来的时候期望 bootloader 传参数进来,其中包括 Machine Type,参考arch/arm/tools/mach-types 和MACHINE_START() 第一个参数对上号。因此,哪个MACHINE 是 run-time 的时候决定的,this way, you can pack as many machine as you want, and dynamically initialize the specific platforms.
各个成员函数在不同时期被调用:
.init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall() 段里面,会自动按顺序被调用。
.init_irq在start_kernel() --> init_IRQ() --> init_arch_irq() 被调用
.map_io 在 setup_arch() --> paging_init() --> devicemaps_init()被调用
其他主要都在 setup_arch() 中用到。
添加设备在成员函数omap3_devkit8000_init()中,此函数初始化了很多项,I2C、SPI之类的,其中添加设备也在里面:
platform_add_devices(omap3_devkit8000_devices,ARRAY_SIZE(omap3_devkit8000_devices));
这个函数在platform_deveice.h中定义,添加一系列的设备
其中的参数omap3_devkit8000_devices列表在下面定义
static struct platform_device *omap3_devkit8000_devices[] __initdata = {
#ifdef CONFIG_OMAP2_DSS
&omap3_evm_dss_device,
&omap3evm_vout_device,
#else
&omap3_devkit8000_lcd_device,
#endif
&leds_gpio,
&keys_gpio,
&w1_device,
&omap_dm9000_dev,
};
platform_device数据结构类型也在platform_deveice.h中定义,
struct platform_device {
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
添加的设备omap3_devkit8000_lcd_device的定义
static struct platform_device omap3_devkit8000_lcd_device = {
.name = "omap3_devkit8000_lcd",
.id = -1,
};
添加LCD设备只是使用了name="omap3_devkit8000_lcd"和id这两个参数,omap3_devkit8000_lcd这个模块名称在drivers/video/omap/lcd_omap3devkit8000.c定义,id=-1应该是系统分配设备号的意思,id>0应该就是用户指定
2。添加设备列表
platform_add_devices()函数在drivers/base/platform.c中,把一系列的设备注册
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]);
if (ret) {
while (--i >= 0)
platform_device_unregister(devs[i]);
break;
}
}
return ret;
}
其中注册函数
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
return platform_device_add(pdev);
}
添加设备函数
int platform_device_add(struct platform_device *pdev)
{
int i, ret = 0;
if (!pdev)
return -EINVAL;
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus;
pdev->dev.bus = &platform_bus_type;
if (pdev->id != -1)
snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name,
pdev->id);
else
strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i];
if (r->name == NULL)
r->name = pdev->dev.bus_id;
p = r->parent;
if (!p) {
if (resource_type(r) == IORESOURCE_MEM)
p = &iomem_resource;
else if (resource_type(r) == IORESOURCE_IO)
p = &ioport_resource;
}
if (p && insert_resource(p, r)) {
printk(KERN_ERR
"%s: failed to claim resource %d/n",
pdev->dev.bus_id, i);
ret = -EBUSY;
goto failed;
}
}
pr_debug("Registering platform device '%s'. Parent at %s/n",
pdev->dev.bus_id, pdev->dev.parent->bus_id);
ret = device_add(&pdev->dev);
if (ret == 0)
return ret;
failed:
while (--i >= 0) {
struct resource *r = &pdev->resource[i];
unsigned long type = resource_type(r);
if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
release_resource(r);
}
return ret;
}
3。对应设备驱动
在drivers/video/omap/lcd_omap3devkit8000.c,包含omap3_devkit8000_lcd启动模块,名称与添加设备的名称一直
struct platform_driver omap3_devkit8000_panel_driver = {
.probe = omap3_devkit8000_panel_probe,
.remove = omap3_devkit8000_panel_remove,
.suspend = omap3_devkit8000_panel_suspend,
.resume = omap3_devkit8000_panel_resume,
.driver = {
.name = "omap3_devkit8000_lcd",
.owner = THIS_MODULE,
},
};
用platform_driver_register 向系统注册驱动程序时,会在s3c2410sdi_driver的信息里提取name为搜索内容,搜索系统注册的device中有没有这个 platform_device。 如果有注册,那么接着会执行platform_driver 里probe函数用的最多和刚才platform_device有关的语句是platform_get_resource,这条语句用于获取 platform_device里的resource资料.例如映射的IO地址,中断等,剩下等得就是ioremap,和 request_irq等的事情了