大部分驱动程序都是内核自带的,主要是为了进行驱动移植。驱动模型分为驱动层和设备层。现在内核已经带有驱动层带代码,还缺少设备层代码。我们可以根据内核提供的LCD驱动程序,分析出设备层代码。
在“平台设备驱动之平台设备”中,可以知道设备层步骤:
1.设备占用的资源;2.设备的平台数据结构struct platform_data(可以从设备相应的驱动程序中获得);3.定义并填充struct platform_device结构;4.注册;5.注销
而1,2中的问题可以从两个方面得到:(1)设备硬件原理图;(2)驱动层的探测函数(probe)
s3c2410fb_probe (drivers\video\s3c2410fb.c)
1 static int __init s3c24xxfb_probe(struct platform_device *pdev, 2 enum s3c_drv_type drv_type) 3 { 4 struct s3c2410fb_info *info; 5 struct s3c2410fb_display *display; 6 struct fb_info *fbinfo; 7 struct s3c2410fb_mach_info *mach_info; 8 struct resource *res; 9 int ret; 10 int irq; 11 int i; 12 int size; 13 u32 lcdcon1; 14 15 mach_info = pdev->dev.platform_data; 16 if (mach_info == NULL) { 17 dev_err(&pdev->dev, 18 "no platform data for lcd, cannot attach\n"); 19 return -EINVAL; 20 }
platform_data是平台数据结构,其对应的是mach_info,struct s3c2410fb_mach_info(); (include\mach\fb.h)
1 struct s3c2410fb_mach_info { 2 3 struct s3c2410fb_display *displays; /* attached diplays info */ 4 unsigned num_displays; /* number of defined displays */ 5 unsigned default_display; 6 7 /* GPIOs */ 8 9 unsigned long gpcup; 10 unsigned long gpcup_mask; 11 unsigned long gpccon; 12 unsigned long gpccon_mask; 13 unsigned long gpdup; 14 unsigned long gpdup_mask; 15 unsigned long gpdcon; 16 unsigned long gpdcon_mask; 17 18 /* lpc3600 control register */ 19 unsigned long lpcsel; 20 };
struct s3c2410fb_mach_info()中的struct s3c2410fb_display()
1 struct s3c2410fb_display { 2 /* LCD type */ 3 unsigned type; 4 5 /* Screen size */ 6 unsigned short width; 7 unsigned short height; 8 9 /* Screen info */ 10 unsigned short xres; 11 unsigned short yres; 12 unsigned short bpp; 13 14 unsigned pixclock; /* pixclock in picoseconds */ 15 unsigned short left_margin; /* value in pixels (TFT) or HCLKs (STN) */ 16 unsigned short right_margin; /* value in pixels (TFT) or HCLKs (STN) */ 17 unsigned short hsync_len; /* value in pixels (TFT) or HCLKs (STN) */ 18 unsigned short upper_margin; /* value in lines (TFT) or 0 (STN) */ 19 unsigned short lower_margin; /* value in lines (TFT) or 0 (STN) */ 20 unsigned short vsync_len; /* value in lines (TFT) or 0 (STN) */ 21 22 /* lcd configuration registers */ 23 unsigned long lcdcon5; 24 };
平台数据已经找到,接下来是平台数据的初始化和struct platform_device的定义 (arch\arm\mach-s3c2440\mach-mini2440.c)
1 //(arch\arm\mach-s3c2440\mach-mini2440.c) 2 static struct s3c2410fb_display mini2440_lcd_cfg __initdata = { 3 4 #if !defined (LCD_CON5) 5 .lcdcon5 = S3C2410_LCDCON5_FRM565 | 6 S3C2410_LCDCON5_INVVLINE | 7 S3C2410_LCDCON5_INVVFRAME | 8 S3C2410_LCDCON5_PWREN | 9 S3C2410_LCDCON5_HWSWP, 10 #else 11 .lcdcon5 = LCD_CON5, 12 #endif 13 14 .type = S3C2410_LCDCON1_TFT, 15 16 .width = LCD_WIDTH, 17 .height = LCD_HEIGHT, 18 19 .pixclock = LCD_PIXCLOCK, 20 .xres = LCD_WIDTH, 21 .yres = LCD_HEIGHT, 22 .bpp = 16, 23 .left_margin = LCD_LEFT_MARGIN + 1, 24 .right_margin = LCD_RIGHT_MARGIN + 1, 25 .hsync_len = LCD_HSYNC_LEN + 1, 26 .upper_margin = LCD_UPPER_MARGIN + 1, 27 .lower_margin = LCD_LOWER_MARGIN + 1, 28 .vsync_len = LCD_VSYNC_LEN + 1, 29 }; 30 31 32 static struct s3c2410fb_mach_info mini2440_fb_info __initdata = { 33 .displays = &mini2440_lcd_cfg, 34 .num_displays = 1, 35 .default_display = 0, 36 37 .gpccon = 0xaa955699, 38 .gpccon_mask = 0xffc003cc, 39 .gpcup = 0x0000ffff, 40 .gpcup_mask = 0xffffffff, 41 42 .gpdcon = 0xaa95aaa1, 43 .gpdcon_mask = 0xffc0fff0, 44 .gpdup = 0x0000faff, 45 .gpdup_mask = 0xffffffff, 46 47 48 .lpcsel = 0xf82, 49 }; 50 51 #endif 52 53 /* (arch\arm\plat-s3c24xx\dev.c)*/ 54 55 static struct resource s3c_lcd_resource[] = { 56 [0] = { 57 .start = S3C24XX_PA_LCD, 58 .end = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1, 59 .flags = IORESOURCE_MEM, 60 }, 61 [1] = { 62 .start = IRQ_LCD, 63 .end = IRQ_LCD, 64 .flags = IORESOURCE_IRQ, 65 } 66 67 }; 68 69 70 //struct platform_device的定义 (arch\arm\plat-s3c24xx\dev.c) 71 72 struct platform_device s3c_device_lcd = { 73 .name = "s3c2410-lcd", 74 .id = -1, 75 .num_resources = ARRAY_SIZE(s3c_lcd_resource), 76 .resource = s3c_lcd_resource, 77 .dev = { 78 .dma_mask = &s3c_device_lcd_dmamask, 79 .coherent_dma_mask = 0xffffffffUL 80 } 81 };
我在看struct platform_device时,发现里面少了struct platform_data的定义,于是便有了在probe函数中,为什么struct s3c2410fb_mach_info *mach_info=pdev->dev.platform_data?
也就是说为什么pdev->dev.platform_data结构类型会是struct s3c2410fb_mach_info, 比如在LED驱动时,dev.platform_data的结构类型就会是s3c2410_leds_drv_data.led_pdata, 就是说platform_data是怎么实现不同数据结构之间的赋值的?
经查找资料知道,在dev.c ( arch\arm\plat-s3c24xx\dev.c)中,会调用函数void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd), 代码如下
1 void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd) 2 { 3 struct s3c2410fb_mach_info *npd; 4 5 npd = kmalloc(sizeof(*npd), GFP_KERNEL); 6 if (npd) { 7 memcpy(npd, pd, sizeof(*npd)); 8 s3c_device_lcd.dev.platform_data = npd; 9 } else { 10 printk(KERN_ERR "no memory for LCD platform data\n"); 11 } 12 }
在这个程序完成了对dev.platformdata的定义,对函数void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)的理解,可以参考http://www.cnblogs.com/armlinux/archive/2010/07/28/2396954.html
p.s. void __init s3c24xx_...注意(初始化函数)init前面的__ , 而不是_ ;之前在调程序时,遇到了这种提示,找了大半天才发现,是这错了_^_
参考资料:
《嵌入式Linux高级驱动教程》 电子工业出版社 深圳信盈达电子有限公司 陈志发 周中孝 李志超 编著
http://www.cnblogs.com/armlinux/archive/2010/07/28/2396954.html