自在自线亚洲а∨天堂在线-中文字幕一区视频播放-扒开双腿猛进入喷水高潮叫声-欧美日本亚洲一区二区-老熟妇高潮偷拍一区二区-国产精品高清一区二区不卡-午夜色福利视频一区二区三区-亚洲国产成人精品福利在线观看-亚洲欧美成人一区二区在线电影

機電之家資源網
單片機首頁|單片機基礎|單片機應用|單片機開發(fā)|單片機文案|軟件資料下載|音響制作|電路圖下載 |嵌入式開發(fā)
培訓信息
贊助商
Linux-2.6.20的LCD驅動分析(二) (轉載)
Linux-2.6.20的LCD驅動分析(二) (轉載)
 更新時間:2009-8-12 16:51:21  點擊數(shù):0
【字體: 字體顏色
http://blog.chinaunix.net/u1/49924/showart_499156.html  二、s3c2410fb_probe函數(shù)分析2.1 驅動的入口點擺在面前的第一個問題相信應該是,這個函數(shù)是從那里開始運行的。這里就應該從long long ago 開始了,打開drivers/video/s3c2410fb.c文件,然后找到s3c2410fb_init函數(shù),先不管它里面是怎么回事,再把目光下移就會看到這樣一串字符串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可以將設備當作模塊動態(tài)加進內核,也可以直接編譯進內核,說到這里大概有點明白MODULE的作用了,不錯!它就是要控制一個驅動加入內核的方式。定義了MODULE就表示將設備當作模塊動態(tài)加入。所以上面的①表示將設備加進內核。在②中的__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_moduleinitfn的別名,或者init_moduleinitfn的一個連接,再簡單一點說這個時候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.20cs8900驅動分析()這篇文章中有對它的揭秘。       上面啰嗦了這么多,最終是要說明只要用module_init申明了一個函數(shù),該函數(shù)就會被Linux內核在適當?shù)臅r機運行,這些時機包括在linux啟動的do_initcalls()時調用(設備被編譯進內核),或者在動態(tài)插入時調用。       回到上面的module_init(s3c2410fb_init)處,也就是說內核與buffer驅動發(fā)生關系的第一次地點是在s3c2410fb_init函數(shù),該函數(shù)就只有一條語句platform_driver_register (&s3c2410fb_driver);??????…… 2.2 platform是何許人也       platform可以理解成一種設備類型,就像字符設備、塊設備和網絡設備一樣,而LCD就屬于這種設備。對于platform設備Linux為應用添加了相關的接口,在這里只是簡單的說說這些接口的用法,而不去深入探討這些接口的實現(xiàn)(我現(xiàn)在還沒有那個能力呢。Uf到這里,馬上就有個問題涌上心頭了,那就是Linux提供了那些接口呢?如果我們需要添加這些設備應該怎么樣做呢?       platform中的相關數(shù)據(jù)結構是應用的關鍵,為了向內核添加一個platform設備,程序員應該填寫兩個數(shù)據(jù)結構platform_device platform_driver,這兩個數(shù)據(jù)結構的定義都可以在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       }};    這里面的各個數(shù)據(jù)成員的意思,在platform_device數(shù)據(jù)結構中有詳細的說明,這里不贅述。上面的代碼中的ARRAY_SIZE宏還是比較有意思的,其實是個c的編程技巧,這個技巧很有用哦!可以在include/linux/kernel.h中找到它的定義:#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))該宏可以方便的求出一個數(shù)組中有多少數(shù)據(jù)成員,這在很多情況下是很有用的,比如對于   int a[]={1,5,65,23,12,20,3}數(shù)組,可以使用該宏求出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指針數(shù)組中添加上s3c_device_lcd的大名,Linux在初始化platform的時候就知道系統(tǒng)中有個s3c_device_lcd設備了。注意了這里只是向Linux描述了設備需要的資源情況,不代表內核會給這些資源的。如果設備要得到這些設備還需要在自己的初始化函數(shù)中去申請。 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函數(shù)所調用platform_driver_register(&s3c2410fb_driver)。簡單地說platform_driver_register要將向內核注冊一個platform設備的驅動,這里是要注冊LCD設備。上面說過platform有兩個重要的數(shù)據(jù)結構platform_deviceplatform_driver,現(xiàn)在是應該提到后者的時候了。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設備的驅動函數(shù)有s3c2410fb_probe、s3c2410fb_remove等等。通過platform_driver_register函數(shù)注冊該設備的過程中,它會回調.probe函數(shù),說到這里也就明白s3c2410fb_probe是在platform_driver_registe中回調的。到目前為止,經過二萬五千里長征終于到達s3c2410fb_probeLCD的驅動程序)了。 2.3 s3c2410fb_probe揭秘       對于該函數(shù),我想最好的辦法就是跟著程序一步一步的解釋。OKlet’s go to ……static int __init s3c2410fb_probe(struct platform_device *pdev){       struct s3c2410 fb_info *info;  //s3c2410fb_info結構在driver/video/s3c2410fb.h中定義,//可以說該結構記錄了s3c2410fb驅動的所有信息。       struct fb_info     *fbinfo;    /* fb_info為內核提供的buffer驅動的接口數(shù)據(jù)結構, 每個幀緩沖驅動都對應一個這樣的結構。s3c2410fb_probe的最終目的填充該結構,并向內核注冊。*/       struct s3c2410fb_hw *mregs;  // s3c2410fb_hw為描述LCD的硬件控制寄存器的結構體,//include/asm-arm/arch-s3c2410/fb.h可以找到它的原型。……        mach_info = pdev->dev.platform_data;  /*這一步看來要多費些口舌了。mach_info是一個s3c2410fb_mach_info類型的指針,注意區(qū)分s3c2410fb_mach_infos3c2410fb_info結構,簡單地說前者只是用于描述LCD初始化時所用的值,而后者是描述整個LCD驅動的結構體。s3c2410fb_mach_infoinclude/asm-arm/arch-s3c2410/fb.h中定義,從他的位置可以看出它和平臺相關,也即它不是內核認知的數(shù)據(jù)結構,這只是驅動程序設計者設計的結構。這里的主要疑問是什么呢?從下面的if語句可以看出如果mach_info等于NULL的話,整個驅動程序就退出了,這就引出了問題――pdev->dev.platform_data是在什么時候被初始化的呢?看來要回答這個問題,歷史應該回到孫悟空大鬧天宮的時候了。按住倒帶鍵不放一直到本篇文章的第一部分,看看那個時候做了些什么。放在這里來解釋第一部分的內容希望沒有為時已晚。其實在內核啟動init進程之前就會執(zhí)行smdk2410_map_io( )函數(shù)(內核的啟動分析就免了吧@_@),而在smdk2410_map_io( )中我們加入了s3c24xx_fb_set_platdata (&smdk2410_lcd_platdata);這條語句,s3c24xx_fb_set_platdata()的實現(xiàn)為:void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd){    s3c_device_lcd.dev.platform_data = pd;}根據(jù)這些代碼,可以清楚的看到s3c_device_lcd.dev.platform_data指向了smdk2410_lcd_platdata,而這個smdk2410_lcd_platdata就是一個s3c2410fb_mach_info的變量,它里面就存放了LCD驅動初始化需要的初始數(shù)據(jù)。當s3c2410fb_probe被回調時,所傳給它的參數(shù)實際就是s3c_device_lcd的首地址,說到這里一切應該都明了了吧!好了,又撤了一通,現(xiàn)在假設這步成功,繼續(xù)往下面走。*/      if (mach_info == NULL) {              dev_err(&pdev->dev,"no platform data for lcd, cannot attach\n");              return -EINVAL;       }        mregs = &mach_info->regs;    //mregs指向硬件各控制寄存器的初始值,可參見第一部//分的smdk2410_lcd_platdata變量。        irq = platform_get_irq(pdev, 0);  /*該函數(shù)獲得中斷號,該函數(shù)的實現(xiàn)是通過比較struct resourceflags域,得到irq中斷號,在上2.1的時候提到s3c_lcd_resource[],platform_get_irq函數(shù)檢測到flags==IORESOURCE_IRQ時就返回中斷號IRQ_LCD。詳細的內容請讀它的源代碼吧!*/       if (irq < 0) {          //沒有找到可用的中斷號,返回-ENOENT              dev_err(&pdev->dev, "no irq for device\n");              return -ENOENT;       }        fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);  /* framebuffer_alloc可以在include/linux/fb.h文件中找到其原型:struct fb_info *framebuffer_alloc(size_t size, struct device *dev); 它的功能是向內核申請一段大小為sizeof(struct fb_info) + size的空間,其中size的大小代表設備的私有數(shù)據(jù)空間,并用fb_infopar域指向該私有空間。*/       if (!fbinfo) {              return -ENOMEM;       } //以下開始做正經事了,填充fbinfo了。       info = fbinfo->par;   //你中有我,我中有你!       info->fb = fbinfo;       platform_set_drvdata(pdev, fbinfo);           /*該函數(shù)的實現(xiàn)非常簡單,實際的操作為:pdev->dev.driver_data fbinfo,device結構的driver_data域指向驅動程序的私有數(shù)據(jù)空間。*/        dprintk("devinit\n");        strcpy(fbinfo->fix.id, driver_name);          memcpy(&info->regs, &mach_info->regs, sizeof(info->regs));        /* Stop the video and unset ENVID if set */       info->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID;       lcdcon1 = readl(S3C2410_LCDCON1);       writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1);//停止硬件 /*以下的對fbinfo的填寫就免了吧!對于fb_info結構的各個成員,在include/linux/fb文件中都有詳細的說明,如果不知道說明的意思,就應該找些基本的知識讀讀了。在眾多的初始化中,fbinfo->fbops = &s3c2410fb_ops;是值得一提的,變量s3c2410fb_ops 就在s3c2410fb.c中定義,它記錄了該幀緩沖區(qū)驅動所支持的操作 */ ……        for (i = 0; i < 256; i++)  //初始化調色板緩沖區(qū)              info->palette_buffer[i] = PALETTE_BUFF_CLEAR;        if (!request_mem_region((unsigned long)S3C24XX_VA_LCD, SZ_1M, "s3c2410-lcd")) {/* 向內核申請內存空間,如果request_mem_region返回0表示申請失敗,此時程序跳到dealloc_fb處開始執(zhí)行,該處會調用framebuffer_release釋放剛才由framebuffer_alloc申請的fb_info空間 */              ret = -EBUSY;              goto dealloc_fb;       }……       ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);/* 向內核注冊中斷,如果注冊失敗,程序跳轉到release_mem處運行,此處釋放fb_info和剛才由request_mem_region申請的內存空間 */       if (ret) {              dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);              ret = -EBUSY;              goto release_mem;       }        info->clk = clk_get(NULL, "lcd");  //該函數(shù)得到時鐘源,并與硬件緊密相連,對于我的//板子,可以在arch/arm/mach-s3c2410/clock.c看到它的原型和實現(xiàn)。       if (!info->clk || IS_ERR(info->clk)) {              printk(KERN_ERR "failed to get lcd clock source\n");              ret = -ENOENT;              goto release_irq;  //該處釋放上面申請的fb_info,內存,和irq資源       }        clk_enable(info->clk);   //打開時鐘       dprintk("got and enabled clock\n");        msleep(1);          //運行得太久有點累了,去打個盹再說        /* Initialize video memory */       ret = s3c2410fb_map_video_memory(info);/*此函數(shù)就在s3c2410fb.c文件中被定義,它的作用是申請幀緩沖器內存空間*/

       if (ret) {              printk( KERN_ERR "Failed to allocate video RAM: %d\n", ret);              ret = -ENOMEM;              goto release_clock;            //釋放所有已得到的資源       }       dprintk("got video memory\n");        ret = s3c2410fb_init_registers(info);   //此函數(shù)也在s3c2410fb.c文件中定義,后面會分析        ret = s3c2410fb_check_var(&fbinfo->var, fbinfo);   //此函數(shù)也在s3c2410fb.c文件中定義        ret = register_framebuffer(fbinfo);  //神圣的時刻終于到來,向內核正式注冊。       if (ret < 0) {              printk(KERN_ERR "Failed to register framebuffer device: %d\n", ret);              goto free_video_memory; //不讓注冊真郁悶,那就釋放所有的資源,出家算了!       }        /* create device files */       device_create_file(&pdev->dev, &dev_attr_debug); //為該設備創(chuàng)建一個在sysfs中的屬性        printk(KERN_INFO "fb%d: %s frame buffer device\n",              fbinfo->node, fbinfo->fix.id);        return 0;           //大功告成! free_video_memory:       s3c2410fb_unmap_video_memory(info);release_clock:       clk_disable(info->clk);       clk_put(info->clk);release_irq:       free_irq(irq,info);release_mem:      release_mem_region((unsigned long)S3C24XX_VA_LCD, S3C24XX_SZ_LCD);dealloc_fb:       framebuffer_release(fbinfo);       return ret;}                                                                                     To be continued……                                                                                      ------ anmnmnly                                                                                           ------ 2008.03.18 
  • 上一篇: Linux-2.6.20的LCD驅動分析(一)(轉載)
  • 下一篇: Linux-2.6.20的LCD驅動分析(三)(轉載)
  • 發(fā)表評論   告訴好友   打印此文  收藏此頁  關閉窗口  返回頂部
    熱點文章
     
    推薦文章
     
    相關文章
    網友評論:(只顯示最新5條。)
    關于我們 | 聯(lián)系我們 | 廣告合作 | 付款方式 | 使用幫助 | 機電之家 | 會員助手 | 免費鏈接

    點擊這里給我發(fā)消息66821730(技術支持)點擊這里給我發(fā)消息66821730(廣告投放) 點擊這里給我發(fā)消息41031197(編輯) 點擊這里給我發(fā)消息58733127(審核)
    本站提供的機電設備,機電供求等信息由機電企業(yè)自行提供,該企業(yè)負責信息內容的真實性、準確性和合法性。
    機電之家對此不承擔任何保證責任,有侵犯您利益的地方請聯(lián)系機電之家,機電之家將及時作出處理。
    Copyright 2007 機電之家 Inc All Rights Reserved.機電之家-由機電一體化網更名-聲明
    電話:0571-87774297 傳真:0571-87774298
    杭州濱興科技有限公司提供技術支持

    主辦:杭州市高新區(qū)(濱江)機電一體化學會
    中國行業(yè)電子商務100強網站

    網站經營許可證:浙B2-20080178-1