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

       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);  //神圣的時(shí)刻終于到來,向內(nèi)核正式注冊。       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); //為該設(shè)備創(chuàng)建一個(gè)在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驅(qū)動(dòng)分析(一)(轉(zhuǎn)載)
  • 下一篇: Linux-2.6.20的LCD驅(qū)動(dòng)分析(三)(轉(zhuǎn)載)
  • 發(fā)表評論   告訴好友   打印此文  收藏此頁  關(guān)閉窗口  返回頂部
    熱點(diǎn)文章
     
    推薦文章
     
    相關(guān)文章
    網(wǎng)友評論:(只顯示最新5條。)
    關(guān)于我們 | 聯(lián)系我們 | 廣告合作 | 付款方式 | 使用幫助 | 機(jī)電之家 | 會員助手 | 免費(fèi)鏈接

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

    主辦:杭州市高新區(qū)(濱江)機(jī)電一體化學(xué)會
    中國行業(yè)電子商務(wù)100強(qiáng)網(wǎng)站

    網(wǎng)站經(jīng)營許可證:浙B2-20080178-1