二、s3cfb_probe函数分析

2.1 驱动的入口点

    摆在面前的第一个问题相信应该是,这个函数是从那里开始运行的。这里就应该从long long ago 开始了,打开linux-2.6.38\drivers\video\samsung\s3cfb.c文件,然后找到s3cfb_init函数,先不管它里面是怎么回事,再把目光下移就会看到这样一串字符串module_init(s3cfb_init),郁闷,这和s3cfb_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.38的cs8900驱动分析(一)这篇文章中有对它的揭秘。
       上面啰嗦了这么多,最终是要说明只要用module_init申明了一个函数,该函数就会被Linux内核在适当的时机运行,这些时机包括在linux启动的do_initcalls()时调用(设备被编译进内核),或者在动态插入时调用。
       回到上面的module_init(s3cfb_init)处,也就是说内核与buffer驱动发生关系的第一次地点是在s3cfb_init函数,该函数就只有一条语句
platform_driver_register (&s3cfb_driver);
??????……
2.2 platform是何许人也
       platform可以理解成一种设备类型,就像字符设备、块设备和网络设备一样,而LCD就属于这种设备。对于platform设备Linux为应用添加了相关的接口,在这里只是简单的说说这些接口的用法,而不去深入探讨这些接口的实现(我现在还没有那个能力呢!)。说到这里,马上就有个问题涌上心头了,那就是Linux提供了那些接口呢?如果我们需要添加这些设备应该怎么样做呢?
       platform中的相关数据结构是应用的关键,为了向内核添加一个platform设备,程序员应该填写两个数据结构platform_device 和platform_driver,这两个数据结构的定义都可以在include/linux/platform_device.h文件中找到。看看LCD驱动是怎么做的,第一步是填写platform_device,在J:\Tiny6410\最新arch\arm\mach-s3c64xx\mach-mini6410.c可以找到填写platform_device的代码,如下:
/* framebuffer and LCD setup. */
/* GPF15 = LCD backlight control
 * GPF13 => Panel power
 * GPN5 = LCD nRESET signal
 * PWM_TOUT1 => backlight brightness
 */
static void mini6410_lcd_power_set(struct plat_lcd_data *pd,
  unsigned int power)
{
if (power) {
gpio_direction_output(S3C64XX_GPF(13), 1);
gpio_direction_output(S3C64XX_GPF(15), 1);

/* fire nRESET on power up */
gpio_direction_output(S3C64XX_GPN(5), 0);
msleep(10);
gpio_direction_output(S3C64XX_GPN(5), 1);
msleep(1);
} else {
gpio_direction_output(S3C64XX_GPF(15), 0);
gpio_direction_output(S3C64XX_GPF(13), 0);
}
}

static struct plat_lcd_data mini6410_lcd_power_data = {
.set_power = mini6410_lcd_power_set,
};

static struct platform_device mini6410_lcd_powerdev = {
.name = "platform-lcd",
.dev.parent = &s3c_device_fb.dev,
.dev.platform_data= &mini6410_lcd_power_data,
};

   
       第二步在struct platform_device *mini6410_devices[]指针数组中添加上mini6410_lcd_powerdev的大名,Linux在初始化platform的时候就知道系统中有个mini6410_lcd_powerdev设备了。注意了这里只是向Linux描述了设备需要的资源情况,不代表内核会给这些资源的。如果设备要得到这些设备还需要在自己的初始化函数中去申请。
static struct platform_device *mini6410_devices[] __initdata = {
&s3c_device_i2c0,
&s3c_device_nand,
&s3c_device_fb,
&s3c_device_ohci,
&s3c_device_usb_hsotg,
&samsung_asoc_dma,
&mini6410_lcd_powerdev,//向内核注册lcd device
......
/* Multimedia support */
#ifdef CONFIG_VIDEO_SAMSUNG
&s3c_device_vpp,
&s3c_device_mfc,
&s3c_device_tvenc,
&s3c_device_tvscaler,
&s3c_device_rotator,
&s3c_device_jpeg,
&s3c_device_fimc0,
&s3c_device_fimc1,
&s3c_device_g2d,
&s3c_device_g3d,
#endif
};
说到这里,应该说向Linux添加一个platform设备应该很容易。
 
2.2 回到s3cfb_init
终于把platform的相关知识啰嗦了一番,下面回到s3cfb_init函数所调用platform_driver_register(&s3cfb_driver)。简单地说platform_driver_register要将向内核注册一个platform设备的驱动,这里是要注册LCD设备。上面说过platform有两个重要的数据结构platform_device和platform_driver,现在是应该提到后者的时候了。platform_driver也在include/linux/platform_device.h中,它的各个成员应该再明白不过来吧!在LCD驱动程序(drivers/video/samsung/s3cfb.c)中定义了填充了platform_driver这个结构,如下:

static struct platform_driver s3cfb_driver = {
.probe = s3cfb_probe,
.remove = s3cfb_remove,
.suspend = s3cfb_suspend,
.resume = s3cfb_resume,
        .driver = {
.name = "s3c-fb",
.owner = THIS_MODULE,
},
};

可以看到该platform设备的驱动函数有s3c2410fb_probe、s3c2410fb_remove等等。通过platform_driver_register函数注册该设备的过程中,它会回调.probe函数,说到这里也就明白s3c2410fb_probe是在platform_driver_registe中回调的。到目前为止,经过二万五千里长征终于到达s3c2410fb_probe(LCD的驱动程序)了。
 
2.3 s3c2410fb_probe揭秘
       对于该函数,我想最好的办法就是跟着程序一步一步的解释。OK,let’s go to ……
static int __init s3cfb_probe(struct platform_device *pdev)
{
struct resource *res;    //描述LCD的硬件控制寄存器的结构体.
struct fb_info *fbinfo; //fb_info为内核提供的buffer驱动的接口数据结构, 每个帧缓冲驱动都对应一个这样的结构。s3cfb_probe的最终目的填充该结构,并向内核注册.
s3cfb_info_t *info;     //该结构记录了s3cfb驱动的所有信息.

char driver_name[] = "s3cfb"; //驱动名称
int index = 0, ret, size;
//framebuffer_alloc可以在include/linux/fb.h文件中找到其原型:struct fb_info *framebuffer_alloc(size_t size, struct device *dev); 它的功能是向内核申请一段大小为sizeof(struct fb_info) + size的空间,其中size的大小代表设备的私有数据空间,并用fb_info的par域指向该私有空间。
fbinfo = framebuffer_alloc(sizeof(s3cfb_info_t), &pdev->dev);
if (!fbinfo)
return -ENOMEM;
//以下开始做正经事了,填充fbinfo了。
platform_set_drvdata(pdev, fbinfo);

//该函数的实现非常简单,实际的操作为:pdev->dev.driver_data = fbinfo,device结构的driver_data域指向驱动程序的私有数据空间。
info = fbinfo->par;
info->dev = &pdev->dev;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "failed to get memory registers\n");
ret = -ENXIO;
goto dealloc_fb;
}
//申请内存
size = (res->end - res->start) + 1;
info->mem = request_mem_region(res->start, size, pdev->name);
if (info->mem == NULL) {
dev_err(&pdev->dev, "failed to get memory region\n");
ret = -ENOENT;
goto dealloc_fb;
}
//映射内存
info->io = ioremap(res->start, size);
if (info->io == NULL) {
dev_err(&pdev->dev, "ioremap() of registers failed\n");
ret = -ENXIO;
goto release_mem;
}

s3cfb_pre_init();
s3cfb_set_backlight_power(1);
s3cfb_set_lcd_power(1);
s3cfb_set_backlight_level(S3CFB_DEFAULT_BACKLIGHT_LEVEL);

//该函数得到时钟源,并与硬件紧密相连
info->clk = clk_get(NULL, "lcd");
if (!info->clk || IS_ERR(info->clk)) {
printk(KERN_INFO "failed to get lcd clock source\n");
ret =  -ENOENT;
goto release_io;
}
  //打开时钟
clk_enable(info->clk);
printk("S3C_LCD clock got enabled :: %ld.%03ld Mhz\n", PRINT_MHZ(clk_get_rate(info->clk)));

s3cfb_fimd.vsync_info.count = 0;
init_waitqueue_head(&s3cfb_fimd.vsync_info.wait_queue);

res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
dev_err(&pdev->dev, "failed to get irq\n");
ret = -ENXIO;
goto release_clock;
}
//向内核注册中断
ret = request_irq(res->start, s3cfb_irq, 0, "s3c-lcd", pdev);
if (ret != 0) {
printk("Failed to install irq (%d)\n", ret);
goto release_clock;
}

msleep(5);
for (index = 0; index < S3CFB_NUM; index++) {
s3cfb_info[index].mem = info->mem;
s3cfb_info[index].io = info->io;
s3cfb_info[index].clk = info->clk;

s3cfb_init_fbinfo(&s3cfb_info[index], driver_name, index);


/* Initialize video memory */
ret = s3cfb_map_video_memory(&s3cfb_info[index]);
if (ret) {
printk("Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM;
goto release_irq;
}

ret = s3cfb_init_registers(&s3cfb_info[index]);
ret = s3cfb_check_var(&s3cfb_info[index].fb.var, &s3cfb_info[index].fb);

if (index < 2){
if (fb_alloc_cmap(&s3cfb_info[index].fb.cmap, 256, 0) < 0)
goto dealloc_fb;
} else {
if (fb_alloc_cmap(&s3cfb_info[index].fb.cmap, 16, 0) < 0)
goto dealloc_fb;
}
 //神圣的时刻终于到来,向内核正式注册。
ret = register_framebuffer(&s3cfb_info[index].fb);
if (ret < 0) {
printk(KERN_ERR "Failed to register framebuffer device: %d\n", ret);
goto free_video_memory;
}
printk(KERN_INFO "fb%d: %s frame buffer device\n", s3cfb_info[index].fb.node, s3cfb_info[index].fb.fix.id);
}

/* create device files */

//为该设备创建一个在sysfs中的属性
ret = device_create_file(&(pdev->dev), &dev_attr_backlight_power);
if (ret < 0)
printk(KERN_WARNING "s3cfb: failed to add entries\n");

ret = device_create_file(&(pdev->dev), &dev_attr_backlight_level);
if (ret < 0)
printk(KERN_WARNING "s3cfb: failed to add entries\n");

ret = device_create_file(&(pdev->dev), &dev_attr_lcd_power);
if (ret < 0)
printk(KERN_WARNING "s3cfb: failed to add entries\n");

return 0;
//错误处理返回
free_video_memory:
s3cfb_unmap_video_memory(&s3cfb_info[index]);


release_irq:
free_irq(res->start, &info);

release_clock:
clk_disable(info->clk);
clk_put(info->clk);

release_io:
iounmap(info->io);

release_mem:
release_resource(info->mem);
kfree(info->mem);

dealloc_fb:
framebuffer_release(fbinfo);
return ret;
}

Linux-2.6.38的LCD驱动分析(二)相关推荐

  1. 《Linux驱动:s3c2440 lcd 驱动分析》

    文章目录 一,前言 二,LCD原理和硬件分析 2.1 LCD原理解析 2.2 硬件电路 2.2.1 LCD背光电路 2.2.2 LCD屏 2.2.3 S3c2440主控 三,LCD应用平台总线-设备- ...

  2. LCD驱动分析(一)

    LCD驱动分析 转载自http://blog.chinaunix.net/uid-26021340-id-3011787.html S3C2440上LCD驱动 (FrameBuffer)实例开发讲解 ...

  3. Linux环境下使用 USB转串口驱动(二)

    minicom是linux下串口通信的软件,它的使用完全依靠键盘的操作,虽然没有"超级终端"那么易用,但是使用习惯之后读者将会体会到它的高效与便利,下面将讲解minicom的安装和 ...

  4. LCD驱动分析【转】

    转自:http://blog.csdn.net/hanmengaidudu/article/details/21559153 1.S3C2440上LCD驱动 (FrameBuffer)实例开发讲解 其 ...

  5. lcd驱动分析(读书笔记)

    1.S3C2440上LCD驱动 (FrameBuffer)实例开发讲解 其中的代码也可直接参考:drivers/video/s3c2410fb.c 以下为转载文章,文章原地址:http://blog. ...

  6. Linux-2.6.20的LCD驱动分析(二)

    .1 驱动的入口点 摆在面前的第一个问题相信应该是,这个函数是从那里开始运行的.这里就应该从long long ago 开始了,打开drivers/video/s3c2410fb.c文件,然后找到s3 ...

  7. linux2.6的LCD驱动分析

    原文出自http://www.cnblogs.com/armlinux/archive/2010/07/28/2396954.html 一.让LCD显示可爱的小企鹅 还是先说说环境吧,处理器为S3C2 ...

  8. Linux-2.6.20的LCD驱动分析

    一.让LCD显示可爱的小企鹅 还是先说说环境吧,处理器为S3C2410,linux的版本当然是2.6.20的.下面先说说怎样让LCD上显示出可爱的小企鹅.最直接的步骤如下(记住不要问为什么哈-_-,一 ...

  9. 高通 android平台LCD驱动分析

    目前手机芯片厂家提供的源码里包含整个LCD驱动框架,一般厂家会定义一个xxx_fb.c的源文件,注册一个平台设备和平台驱动,在驱动的probe函数中来调用register_framebuffer(), ...

最新文章

  1. DARPA发布战略框架文件旨在 为美国国家安全创建突破性新技术能力
  2. delphi中关于时间差的实例
  3. Java的回调机制--学习笔记
  4. java opencv人脸识别_java+opencv+intellij idea实现人脸识别
  5. c++ (QT)笔记
  6. 应用程序调试技术(更新程度:完毕)送源码及PPT
  7. java 蓝桥杯 数字游戏
  8. mysql的内连接与外连接
  9. Android中的ANR问题
  10. UVA11233 POJ3366 HDU1804 Deli Deli【水题】
  11. python 学习day3
  12. JAVA偶数分解质数_优化后的寻找偶数是两个质数之和的JAVA代码
  13. linux php gmagick,Linux下编译安装GraphicsMagick及PHP扩展gmagick
  14. aws fargate_使用AWS Fargate部署PyCaret和Streamlit应用程序-无服务器基础架构
  15. 操作系统概念 第九版 Operating System Concepts, 9th Edition 中文译稿(不定时更新)
  16. c语言接收rs232串口速率,基于C语言的RS232串行接口通信实现
  17. linux系统中怎么复制粘贴快捷键设置,复制粘贴的快捷键是什么 复制粘贴的快捷键介绍【步骤】...
  18. 【工具使用】AI帮你写代码
  19. C#支付宝扫码支付代码完整版
  20. seo秘籍,seo秘籍-自学seo零基础知识入门优化教程

热门文章

  1. 【趣学算法】-Day2-贪心算法
  2. 【学习笔记】SpringAOP的用法全解
  3. python偷回灭霸的宝石_复联4不能合理解释:钢铁侠是怎么从灭霸手中偷走无限宝石的!...
  4. PLC是如何读取模拟量的?
  5. 利用python进行身份证号码大全_用 Java 撸一个身份证号码识别系统,准确率高达 90%...
  6. 以中国传统的孔子和老子的思想来分析忍者代码
  7. 铅笔手绘教育教学PPT模板-优页文档
  8. 装完win10系统 散热扇一直转
  9. Python复合语句
  10. golang 本地缓存