1. 程式人生 > >Linux下的LCD驅動(二)

Linux下的LCD驅動(二)

3.3 LCD檔案層

幀緩衝裝置作為一個字元裝置,其檔案操作函式就定義在檔案層fbmem.c

static const struct file_operations fb_fops = {

       .owner =       THIS_MODULE,

       .read =          fb_read,   //

       .write =  fb_write,      //

       .unlocked_ioctl = fb_ioctl,  //控制

#ifdef CONFIG_COMPAT

       .compat_ioctl = fb_compat_ioctl,

#endif

       .mmap =        fb_mmap, //對映

       .open =         fb_open,   //開啟

       .release =      fb_release, //釋放

#ifdef HAVE_ARCH_FB_UNMAPPED_AREA

       .get_unmapped_area = get_fb_unmapped_area,

#endif

#ifdef CONFIG_FB_DEFERRED_IO

       .fsync =  fb_deferred_io_fsync,

#endif

};

幀緩衝裝置驅動的檔案操作介面已經在fbmem.c

中被統一實現了,一般不需要驅動工程師編寫了。對於這個fbmem.c檔案,它一方面在我們的幀緩衝檔案層為使用者提供了訪問介面函式,一方面還設計了一些供核心其他函式呼叫的介面函式,例如register_framebufferfb_set_varfb_blank等。

使用者空間對幀裝置的操作主要包括opencloseioctlmmap實現,下面我們主要看看ioctlmmap的實現

static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

{

       struct inode *inode = file->f_path.dentry->d_inode;

       int fbidx = iminor(inode);  //獲得索引號

       struct fb_info *info = registered_fb[fbidx];  //獲取fb_info結構體

       return do_fb_ioctl(info, cmd, arg);  //呼叫二次ioctl函式

}

static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,

                     unsigned long arg)

{

       struct fb_ops *fb;

       struct fb_var_screeninfo var;

       struct fb_fix_screeninfo fix;

       struct fb_con2fbmap con2fb;

       struct fb_cmap cmap_from;

       struct fb_cmap_user cmap;

       struct fb_event event;

       void __user *argp = (void __user *)arg;

       long ret = 0;

       switch (cmd) {

       case FBIOGET_VSCREENINFO:  //獲得可變螢幕引數

              if (!lock_fb_info(info))

                     return -ENODEV;

              var = info->var;

              unlock_fb_info(info);

              ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;

              break;

       case FBIOPUT_VSCREENINFO:    //設定可變螢幕引數

              if (copy_from_user(&var, argp, sizeof(var)))

                     return -EFAULT;

              if (!lock_fb_info(info))

                     return -ENODEV;

              acquire_console_sem();

              info->flags |= FBINFO_MISC_USEREVENT;

              ret = fb_set_var(info, &var);

              info->flags &= ~FBINFO_MISC_USEREVENT;

              release_console_sem();

              unlock_fb_info(info);

              if (!ret && copy_to_user(argp, &var, sizeof(var)))

                     ret = -EFAULT;

              break;

       case FBIOGET_FSCREENINFO:  //獲得固定螢幕引數

              if (!lock_fb_info(info))

                     return -ENODEV;

              fix = info->fix;

              unlock_fb_info(info);

              ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;

              break;

       case FBIOPUTCMAP:      //設定顏色表

              if (copy_from_user(&cmap, argp, sizeof(cmap)))

                     return -EFAULT;

              ret = fb_set_user_cmap(&cmap, info);

              break;

       case FBIOGETCMAP:       //獲得顏色表

              if (copy_from_user(&cmap, argp, sizeof(cmap)))

                     return -EFAULT;

              if (!lock_fb_info(info))

                     return -ENODEV;

              cmap_from = info->cmap;

              unlock_fb_info(info);

              ret = fb_cmap_to_user(&cmap_from, &cmap);

              break;

       case FBIOPAN_DISPLAY:

              if (copy_from_user(&var, argp, sizeof(var)))

                     return -EFAULT;

              if (!lock_fb_info(info))

                     return -ENODEV;

              acquire_console_sem();

              ret = fb_pan_display(info, &var);

              release_console_sem();

              unlock_fb_info(info);

              if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))

                     return -EFAULT;

              break;

       case FBIO_CURSOR:

              ret = -EINVAL;

              break;

       case FBIOGET_CON2FBMAP:

              if (copy_from_user(&con2fb, argp, sizeof(con2fb)))

                     return -EFAULT;

              if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)

                     return -EINVAL;

              con2fb.framebuffer = -1;

              event.data = &con2fb;

              if (!lock_fb_info(info))

                     return -ENODEV;

              event.info = info;

              fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);

              unlock_fb_info(info);

              ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;

              break;

       case FBIOPUT_CON2FBMAP:

              if (copy_from_user(&con2fb, argp, sizeof(con2fb)))

                     return -EFAULT;

              if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)

                     return -EINVAL;

              if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)

                     return -EINVAL;

              if (!registered_fb[con2fb.framebuffer])

                     request_module("fb%d", con2fb.framebuffer);

              if (!registered_fb[con2fb.framebuffer]) {

                     ret = -EINVAL;

                     break;

              }

              event.data = &con2fb;

              if (!lock_fb_info(info))

                     return -ENODEV;

              event.info = info;

              ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);

              unlock_fb_info(info);

              break;

       case FBIOBLANK:

              if (!lock_fb_info(info))

                     return -ENODEV;

              acquire_console_sem();

              info->flags |= FBINFO_MISC_USEREVENT;

              ret = fb_blank(info, arg);

              info->flags &= ~FBINFO_MISC_USEREVENT;

              release_console_sem();

              unlock_fb_info(info);

              break;

       default:

              if (!lock_fb_info(info))

                     return -ENODEV;

              fb = info->fbops;

              if (fb->fb_ioctl)

                     ret = fb->fb_ioctl(info, cmd, arg);

              else

                     ret = -ENOTTY;

              unlock_fb_info(info);

       }

       return ret;

}

下面是fb_mmap函式

static int  fb_mmap(struct file *file, struct vm_area_struct * vma)

{

       int fbidx = iminor(file->f_path.dentry->d_inode);

       struct fb_info *info = registered_fb[fbidx];

       struct fb_ops *fb = info->fbops;

       unsigned long off;

       unsigned long start;

       u32 len;

       if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))

              return -EINVAL;

       off = vma->vm_pgoff << PAGE_SHIFT;

       if (!fb)

              return -ENODEV;

       mutex_lock(&info->mm_lock);

       if (fb->fb_mmap) {

              int res;

              res = fb->fb_mmap(info, vma);

              mutex_unlock(&info->mm_lock);

              return res;

       }

       start = info->fix.smem_start;  //幀緩衝區記憶體

       len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);

       if (off >= len) {  //記憶體對映的I/O

              off -= len;

              if (info->var.accel_flags) {

                     mutex_unlock(&info->mm_lock);

                     return -EINVAL;

              }

              start = info->fix.mmio_start;

              len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);

       }

       mutex_unlock(&info->mm_lock);

       start &= PAGE_MASK;

       if ((vma->vm_end - vma->vm_start + off) > len)

              return -EINVAL;

       off += start;

       vma->vm_pgoff = off >> PAGE_SHIFT;

       //這是一個I/O對映,告訴maydump跳過此VMA

       vma->vm_flags |= VM_IO | VM_RESERVED;

       fb_pgprotect(file, vma, off);

       if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,

                          vma->vm_end - vma->vm_start, vma->vm_page_prot))

              return -EAGAIN;

       return 0;

}

好了,我們已經分析完了幀緩衝驅動的檔案層和裝置層程式碼了。

總結下,幀緩衝裝置為使用者提供file_operations結構體,其實現定義在fbmem.c中;特定的幀緩衝裝置fb_info結構體成員的註冊,尤其是fb_ops中成員的實現則是由s3c2410fb.c實現,fb_ops中的成員將最終操作lcd控制器硬體暫存器。那fbmem.cs3c2410fb.c怎麼相連的呢?其實是通過fb_info結構體,在s3c2410fb.c中呼叫register_framebuffer註冊fb_info時,其實是把fb_info註冊到一個叫struct fb_info *registered_fb[FB_MAX]這樣的一個數組中的,那麼我們在fbmem.c中就可以通過次裝置號作為registered_fb陣列的索引號查詢到相應的fb_info,從而能夠呼叫fb_info中實現的fb_ops的。

四.LCD驅動測試

對於我們的Mini2440開發板,使用的是X35型號的LCD,根據X35LCD屏的說明文件,進行移植LCD驅動,並編譯成核心,燒寫到開發板中。

實驗環境:核心linux2.6.32.2arm-linux-gcc交叉編譯器,mini2440開發板。

核心配置:配置時候我們需要選中fbmem.cs3c2410.c檔案以及X35LCD型號

具體測試程式碼如下

#include <unistd.h>

#include <stdlib.h>

#include <stdio.h>

#include <fcntl.h>

#include <linux/fb.h>

#include <sys/mman.h>

int main()

{

       int fbfd=0;

       struct fb_var_screeninfo vinfo;

       unsigned long screensize=0;

       char *fbp=0;

       int x=0,y=0,i=0;

       fbfd=open("/dev/fb0",O_RDWR);  //開啟幀緩衝裝置

       if(!fbfd){

              printf("error\n");

              exit(1);

       }

       if(ioctl(fbfd,FBIOGET_VSCREENINFO,&vinfo)){  //獲取螢幕可變引數

              printf("error\n");

              exit(1);

       }

       //列印螢幕可變引數

       printf("%dx%d,%dbpp\n",vinfo.xres,vinfo.yres,vinfo.bits_per_pixel);

       screensize=vinfo.xres*vinfo.yres*2;  //緩衝區位元組大小

       fbp=(char *)mmap(0,screensize,PROT_READ|PROT_WRITE,MAP_SHARED,fbfd,0);//對映

       if((int)fbp==-1){

              printf("error\n");

              exit(4);

       }    

       for(i=0;i<3;i++){ //畫圖

              for(y=i*(vinfo.yres/3);y<(i+1)*(vinfo.yres/3);y++){

              for(x=0;x<vinfo.xres;x++){

              long location=x*2+y*vinfo.xres*2;

              int r=0,g=0,b=0;

              unsigned short rgb;

              if (i==0)

                     r=((x*1.0)/vinfo.xres)*32;

              if (i==1)

                     g=((x*1.0)/vinfo.xres)*64;

              if (i==2)

                     b=((x*1.0)/vinfo.xres)*32;

              rgb=(r<<11)|(g<<5)|b;

              *((unsigned short *)(fbp+location))=rgb;

       }

       }

    }

       munmap(fbp,screensize);

       close(fbfd);

       return 0;

}

虛擬機器下編譯arm-linux-gcc  lcd.c  -o  lcd

在超級終端下執行:./lcd

可以見到:螢幕上有三道顏色