15.linux-LCD層次分析(詳解)

来源:http://www.cnblogs.com/lifexy/archive/2017/09/27/7603327.html
-Advertisement-
Play Games

如果我們的系統要用GUI(圖形界面介面),這時LCD設備驅動程式就應該編寫成frambuffer介面,而不是像之前那樣只編寫操作底層的LCD控制器介面。 什麼是frambuffer設備? frambuffer設備層是對圖像設備的一種抽象,它代表了視頻硬體的幀緩存,使得應用程式通過定義好的介面就可以訪 ...


如果我們的系統要用GUI(圖形界面介面),這時LCD設備驅動程式就應該編寫成frambuffer介面,而不是像之前那樣只編寫操作底層的LCD控制器介面。

什麼是frambuffer設備?

   frambuffer設備層是對圖像設備的一種抽象,它代表了視頻硬體的幀緩存,使得應用程式通過定義好的介面就可以訪問硬體。所以應用程式不需要考慮底層的(寄存器級)的操作。應用程式對設備文件的訪問一般在/dev目錄,如 /dev/fb*。


 

 

內核中的frambuffer在drivers/video/fbmem.c(fb: frame buffer)

1.我們進入fbmem.c找到它的入口函數:

static  int __init  fbmem_init(void)
{

         create_proc_read_entry("fb", 0, NULL, fbmem_read_proc, NULL);

         if (register_chrdev(FB_MAJOR,"fb",&fb_fops))           //(1)創建字元設備
                  printk("unable to get major %d for fb devs\n", FB_MAJOR);

         fb_class = class_create(THIS_MODULE, "graphics");       //創建類

         if (IS_ERR(fb_class)) {
                   printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
                  fb_class = NULL;
        }
         return 0;
}

 

(1)創建字元設備"fb", FB_MAJOR=29,主設備號為29,我們cat /proc/devices 也能找到這個字元設備:

 

和我們之前的驅動程式一樣,但是沒有使用創建設備節點,為什麼?

因為需要註冊了LCD驅動後,才會有設備節點,所以這裡的代碼沒有 ,後面會分析哪裡有。

2.我們來看看註冊的file_operations結構體fb_fops的.open函數和.read函數,應用層是如何打開驅動、讀取驅動數據

2.1 fb_open函數如下:

static int fb_open(struct inode *inode, struct file *file)
{
       int fbidx = iminor(inode);      //獲取設備節點的次設備號
       struct fb_info *info;                //定義fb_info結構體
       int res = 0;
       ... ...

if (!(info = registered_fb[fbidx]))   //(1) info= registered_fb[fbidx],獲取此設備號的lcd驅動信息
              try_to_load(fbidx);
       ... ... 

       if (info->fbops->fb_open) {          
              res = info->fbops->fb_open(info,1);  //調用registered_fb[fbidx]->fbops->fb_open
              if (res)
                     module_put(info->fbops->owner);
       }

       return res;
}

 

(1) registered_fb[fbidx] 這個數組也是fb_info結構體,其中fbidx等於次設備號id,顯然這個數組就是保存我們各個lcd驅動的信息

2.2 fb_read函數如下:

static ssize_t fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
       unsigned long p = *ppos;
       struct inode *inode = file->f_path.dentry->d_inode; 
       int fbidx = iminor(inode);                       //獲取次設備號
       struct fb_info *info = registered_fb[fbidx];     //獲取次設備號的lcd驅動的信息
       u32 *buffer, *dst;
       u32 __iomem *src;
       int c, i, cnt = 0, err = 0;
       unsigned long total_size;
       ... ...
       if (info->fbops->fb_read)
              return info->fbops->fb_read(info, buf, count, ppos);
     
       total_size = info->screen_size;     //獲取屏幕長度
    
... ... buffer
= kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,GFP_KERNEL); //分配緩衝區 if (!buffer) return -ENOMEM; src = (u32 __iomem *) (info->screen_base + p); //獲取顯存物理基地址 if (info->fbops->fb_sync) info->fbops->fb_sync(info); while (count) { c = (count > PAGE_SIZE) ? PAGE_SIZE : count; //獲取頁地址 dst = buffer; /*因為src是32位,一個src等於4個位元組,所以頁地址c >> 2*/ for (i = c >> 2; i--; ) *dst++ = fb_readl(src++); //讀取顯存每個像素點數據,放到dst地址上 if (c & 3) { u8 *dst8 = (u8 *) dst; u8 __iomem *src8 = (u8 __iomem *) src; for (i = c & 3; i--;) *dst8++ = fb_readb(src8++); src = (u32 __iomem *) src8; } if (copy_to_user(buf, buffer, c)) { //上傳數據,長度等於頁地址大小 err = -EFAULT; break; } *ppos += c; buf += c; cnt += c; count -= c; } kfree(buffer); return (err) ? err : cnt; }

 

從.open和.write函數中可以發現,都依賴於fb_info幀緩衝信息結構體,它從registered_fb[fbidx]數組中得到,這個數組保存我們各個lcd驅動的信息

3.我們來找找這個數組在哪裡被註冊,位於register_framebuffer():

int register_framebuffer(struct fb_info *fb_info)
{
 ... ...
for (i = 0 ; i < FB_MAX; i++)    //查找空的數組
        if (!registered_fb[i])
         break;

fb_info->node = i;           
 ... ...

/*創建設備節點,名稱為fdi,主設備號為29,次設備號為i   */
fb_info->dev = device_create(fb_class, fb_info->device,MKDEV(FB_MAJOR, i), "fb%d", i);
 ... ...

registered_fb[i] = fb_info;
 ... ...
}

 

得出這個register_framebuffer()除了註冊fb_info,還創建了設備節點

所以要註冊驅動時就調用這個,如下圖所示:

 

 

4.我們來看看/drivers/video/s3c2410fb.c 中又是怎麼實現驅動的

4.1先找到入口出口函數:

int __devinit s3c2410fb_init(void)
{
     return platform_driver_register(&s3c2410fb_driver);
}

static void __exit s3c2410fb_cleanup(void)
{
     platform_driver_unregister(&s3c2410fb_driver);
}

 

發現是註冊、註銷設備平臺drv

4.2 來看看s3c2410fb_driver 如何定義的

static struct platform_driver s3c2410fb_driver = {
       .probe           = s3c2410fb_probe,        //檢測函數,註冊設備
       .remove         = s3c2410fb_remove,    //刪除設備
       .suspend = s3c2410fb_suspend,          //休眠
       .resume          = s3c2410fb_resume,    //喚醒
       .driver            = {
              .name     = "s3c2410-lcd",           //drv名字
              .owner    = THIS_MODULE,
       },
};

 

和我們上節分析的platform機制一樣,當與設備匹配成功,就進入probe函數,初始化驅動設備

4.3 來看看.probe函數,如何實現驅動的

static int __init s3c2410fb_probe(struct platform_device *pdev)
{
       struct s3c2410fb_info *info;
       struct fb_info     *fbinfo;
       struct s3c2410fb_hw *mregs;
       int ret;
       int irq;
       int i;
       u32 lcdcon1;
 
       mach_info = pdev->dev.platform_data;     //獲取LCD設備信息(長寬、類型等)

       if (mach_info == NULL) {
              dev_err(&pdev->dev,"no platform data for lcd, cannot attach\n");
              return -EINVAL;
       }
       mregs = &mach_info->regs;


       irq = platform_get_irq(pdev, 0);
       if (irq < 0) {
              dev_err(&pdev->dev, "no irq for device\n");
              return -ENOENT;
       }

 

       fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev); //1.分配一個fb_info結構體
       if (!fbinfo) {
              return -ENOMEM;
       }

 

     /*2.設置fb_info*/
       info = fbinfo->par;
       info->fb = fbinfo;
       info->dev = &pdev->dev;
       ... ...

    /*3.硬體相關的操作,設置中斷,LCD時鐘頻率,顯存地址, 配置引腳... ...*/
       ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info); //設置中斷
       info->clk = clk_get(NULL, "lcd");                    //獲取時鐘
       clk_enable(info->clk);                                  //使能時鐘
       ret = s3c2410fb_map_video_memory(info);               //顯存地址  
       ret = s3c2410fb_init_registers(info);                //設置寄存器,配置引腳
       ... ...
ret
= register_framebuffer(fbinfo); //4.註冊一個fb_info結構體 if (ret < 0) { printk(KERN_ERR "Failed to register framebuffer device: %d\n", ret); goto free_video_memory; } ... ... return ret; }

 

4.4 顯然要寫個LCD驅動程式,需要以下4步:

1) 分配一個fb_info結構體: framebuffer_alloc();

2) 設置fb_info

3) 硬體相關的操作(設置中斷,LCD時鐘頻率,顯存地址, 配置引腳... ...)

4 註冊fb_info: register_framebuffer()

 

下節就開始如何來寫LCD驅動

 (LCD驅動詳解入口地址: http://www.cnblogs.com/lifexy/p/7604011.html)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 第四章 高級查詢(二) (1)通過在子查詢中使用EXISTS子句,可以對子查詢中的行是否存在進行檢查。子查詢可以出現在表達式出現的如何位置 (2)子查詢語句可以嵌套在SQL語句中任何表達式出現的位置。 一、EXISTS子查詢 1.使用EXISTS語句判斷該資料庫對象是否存在: DROP TABLE ...
  • 在執行mysqldump命令時候報如下錯誤: [root@localhost ~]# mysqldump -uroot -p -d test> test.sql mysqldump: Got error: 2002: Can't connect to local MySQL server throu ...
  • 1 修改network配置文件 vi /etc/sysconfig/network 將配置文件中的HOSTNAME的內容改成需要修改的主機名 2 修改hosts配置文件 vi /etc/hosts 將配置文件中的localhost.localdomain改成需要修改的主機名 3 重啟機器 reboo ...
  • 本文目錄1.1 下載和解決依賴關係1.2 httpd編譯選項1.3 模塊動靜態編譯1.4 動靜態編譯的優先順序規則1.5 MPM的安裝1.6 關於"--enable-so"1.7 開始編譯httpd1.8 編譯後的規範操作 1.1 下載和解決依賴 以httpd 2.4.27為例。 資源下載: apac ...
  • 在上一節LCD層次分析中,得出寫個LCD驅動入口函數,需要以下4步: 1) 分配一個fb_info結構體: framebuffer_alloc(); 2) 設置fb_info 3) 設置硬體相關的操作 4) 使能LCD,並註冊fb_info: register_framebuffer() 本節需要用 ...
  • 1》rsync概述 rsync的優點與不足 rsync與傳統的cp、tar備份方式相比,rsync具有安全性高、備份迅速、支持增量備份等優點,通過rsync可以解決對實時性要求不高的數據備份需求,例如定期的 備份文件伺服器數據到遠端伺服器,對本地磁碟定期做數據鏡像等。 隨著應用系統規模的不斷擴大,對 ...
  • 以下是playbook的內容。它的處理流程是: 1.先在本地下載apr,apr-util,httpd共3個.tar.gz文件。 2.解壓這3個文件。 3.安裝pcre和pcre-devel依賴包。 4.編譯安裝apr。 5.編譯安裝apr-util。 6.編譯安裝httpd。 編譯完成後,還有一系列 ...
  • 摘要:涉及到網路安全這一塊,想必大家都聽過CA吧。像百度、淘寶、京東等這些知名網站,每年都要花費一筆money來買CA證書。但其實簡單的企業內的CA認證,我們自己就可以實現,今天小編我就講解一下怎麼在企業局部實現CA認證。 一、CA介紹 1、電子商務認證授權機構(CA, Certificate Au ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...