二、s3c2410fb_probe 函数分析
2.1 驱动的入口点
摆在面前的第一个问题相信应该是,这个函数是从那里开始运行的。这里就应该从long long ago 开始了,打开drivers/video/s3c2410fb.c 文件,然后找到s3c2410fb_init 函数,先不管它里面是怎么回事,再把目光下移就会看到这样一串字符串module_init(s3c2410fb_init) ,郁闷,这和S3C2410fb_probe 有啥关系嘛?这个问题问的好!不要着急慢慢往下面走。先摸摸module_init 是何方神圣再说,于是乎我就登陆了http://lxr.linux.no/linux+v2.6.20/ 网站,在上面一搜,原来module_init 老家在include/linux/init.h ,原来它居然还有两重身份,其原型如下:
#ifndef MODULE
……
#define module_init(x) __initcall(x); ①
……
#else
……
#define module_init(initfn) \ ②
static inline initcall_t __inittest(void) \
{ return initfn; } \
int init_module(void) __attribute__((alias(#c)));
……
#endif
从上面可以看出,module_init 到底用哪个,就取决于MODULE 了,那么MODULE 的作用是什么呢?我们知道Linux 可以将设备当作模块动态加进内核,也可以直接编译进内核,说到这里大概有点明白MODULE 的作用了,不错!它就是要控制一个驱动加入内核的方式。定义了MODULE 就表示将设备当作模块动态加入。所以上面的①表示将设备加进内核。在②中的__attribute__((alias(#initfn))) 很有意思,这代表什么呢?主要alias 就是属性的意思,它的英文意思是别名,可以在http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=/com.ibm.xlcpp8l.doc/language/ref/fn_attrib_alias.htm 找到它的详细说明,这里简单的说int init_module(void) __attribute__((alias(#initfn))); 的意思为init_module 是initfn 的别名,或者init_module 是initfn 的一个连接,再简单一点说这个时候module_init 宏基因突变成了init_module() 了。对于第一种情况,__initcall(fn) 又被宏定义成了device_initcall(fn) ,也就是说module_init(x) 等于device_initcall(fn) 。对于device_initcall(fn) 又是一个宏定义,它被定义成了__define_initcall("6",fn,6) ,至于这个宏表示什么意思,在这里就不啰嗦重复了,在Linux-2.6.20 的cs8900 驱动分析( 一) 这篇文章中有对它的揭秘。
上面啰嗦了这么多,最终是要说明只要用module_init 申明了一个函数,该函数就会被Linux 内核在适当的时机运行,这些时机包括在linux 启动的do_initcalls() 时调用(设备被编译进内核),或者在动态插入时调用。
回到上面的module_init(s3c2410fb_init) 处,也就是说内核与buffer 驱动发生关系的第一次地点是在s3c2410fb_init 函数,该函数就只有一条语句
platform_driver_register (&s3c2410fb_driver) ;
??????……
2.2 platform 是何许人也
platform 可以理解成一种设备类型,就像字符设备、块设备和网络设备一样,而LCD 就属于这种设备。对于platform 设备Linux 为应用添加了相关的接口,在这里只是简单的说说这些接口的用法,而不去深入探讨这些接口的实现(我现在还没有那个能力呢!)。说到这里,马上就有个问题涌上心头了,那就是Linux 提供了那些接口呢?如果我们需要添加这些设备应该怎么样做呢?
platform 中的相关数据结构是应用的关键,为了向内核添加一个platform 设备,程序员应该填写两个数据结构platform_device 和platform_driver ,这两个数据结构的定义都可以在include/linux/platform_device.h 文件中找到。看看LCD 驱动是怎么做的,第一步是填写platform_device ,在arch/arm/mach-s3c2410/devs.c 可以找到填写platform_device 的代码,如下:
static u64 s3c_device_lcd_dmamask = 0xffffffffUL;
struct platform_device s3c_device_lcd = {
.name = "s3c2410-lcd",
.id = -1,
.num_resources = ARRAY_SIZE (s3c_lcd_resource),
.resource = s3c_lcd_resource,
.dev = {
.dma_mask = &s3c_device_lcd_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
这里面的各个数据成员的意思,在platform_device 数据结构中有详细的说明,这里不赘述。上面的代码中的ARRAY_SIZE 宏还是比较有意思的,其实是个c 的编程技巧,这个技巧很有用哦!可以在include/linux/kernel.h 中找到它的定义:
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
该宏可以方便的求出一个数组中有多少数据成员,这在很多情况下是很有用的,比如对于 int a[]={1,5,65,23,12,20,3} 数组,可以使用该宏求出a[] 有7 个元素。 另外,platform_device 的另外一项重要成员是resource ,在上面的代码中此域被赋予了s3c_lcd_resource ,s3c_lcd_resource 也可以在arch/arm/mach-s3c2410/devs.c 找到。 static struct resource s3c_lcd_resource[] = { [0] = { .start = S3C24XX_PA_LCD, .end = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1, .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_LCD, .end = IRQ_LCD, .flags = IORESOURCE_IRQ, } }; struct resource 结构实际上描述了该设备占用的硬件资源(如地址空间,中断号等s ),s3c_lcd_resource 描述了内存空间和中断分配情况。 最后在smdk2410_devices 指针数组中添加上s3c_device_lcd 的大名,Linux 在初始化platform 的时候就知道系统中有个s3c_device_lcd 设备了。注意了这里只是向Linux 描述了设备需要的资源情况,不代表内核会给这些资源的。如果设备要得到这些设备还需要在自己的初始化函数中去申请。 static struct platform_device *smdk2410_devices[] __initdata = { &s3c_device_usb, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c, &s3c_device_iis, &s3c_device_ts, }; 说到这里,应该说向Linux 添加一个platform 设备应该很容易。 2.2 回到s3c2410fb_init 终于把platform 的相关知识啰嗦了一番,下面回到s3c2410fb_init 函数所调用platform_driver_register(&s3c2410fb_driver) 。简单地说platform_driver_register 要将向内核注册一个platform 设备的驱动,这里是要注册LCD 设备。上面说过platform 有两个重要的数据结构platform_device 和platform_driver ,现在是应该提到后者的时候了。platform_driver 也在include/linux/platform_device.h 中,它的各个成员应该再明白不过来吧!在LCD 驱动程序(drivers/video/s3c2410fb.c )中定义了填充了platform_driver 这个结构,如下: static struct platform_driver s3c2410fb_driver = { .probe = s3c2410fb_probe, .remove = s3c2410fb_remove, .suspend = s3c2410fb_suspend, .resume = s3c2410fb_resume, .driver = { .name = "s3c2410-lcd", .owner = THIS_MODULE, }, }; 可以看到该platform 设备的驱动函数有s3c2410fb_probe 、s3c2410fb_remove 等等。通过platform_driver_register 函数注册该设备的过程中,它会回调.probe 函数,说到这里也就明白s3c2410fb_probe 是在platform_driver_registe 中回调的。到目前为止,经过二万五千里长征终于到达s3c2410fb_probe (LCD 的驱动程序)了。
转载自:http://blog.21ic.com/user1/5823/archives/2009/60264.html