请选择 进入手机版 | 继续访问电脑版
    查看: 52|回复: 0

    Linux下的LCD驱动(二)

    [复制链接]

    883

    主题

    941

    帖子

    3575

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    3575
    基情
    1843
    发表于 2016-9-18 15:32:22 | 显示全部楼层 |阅读模式
    版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127

    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) &#43; 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) &#43; info->fix.mmio_len);
           }
           mutex_unlock(&info->mm_lock);
           start &= PAGE_MASK;
           if ((vma->vm_end - vma->vm_start &#43; off) > len)
                  return -EINVAL;
           off &#43;= 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注册到一个叫structfb_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&#43;&#43;){ //画图
                  for(y=i*(vinfo.yres/3);y<(i&#43;1)*(vinfo.yres/3);y&#43;&#43;){
                  for(x=0;x<vinfo.xres;x&#43;&#43;){
                  long location=x*2&#43;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&#43;location))=rgb;
           }
           }
        }
           munmap(fbp,screensize);
           close(fbfd);
           return 0;
    }
    虚拟机下编译arm-linux-gcc  lcd.c  -o  lcd
    在超级终端下运行:./lcd
    可以见到:屏幕上有三道颜色
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|小黑屋|网站地图|DZ商业模板|VR福利资源|嵌入式Linux论坛 ( 粤ICP备15085165号-2 )

    GMT+8, 2017-10-21 08:01 , Processed in 0.079391 second(s), 23 queries .

    Powered by 深嵌论坛 X3.4

    © 2001-2013 Comsenz Inc.

    快速回复 返回顶部 返回列表