LCD设备驱动 --- 帧缓冲(简析1)

帧缓冲 ( framebuffer ) Linux 系统为显示设备提供的一个接口,它将显示缓冲区抽象,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。帧缓冲设备为标准字符设备,主设备号为29,对应于 /dev/fbn 设备文件。

见: drivers\video\ fbmem.c  中的  module_init(fbmem_init);  就可知道  fbmem_init  是其字符设备加载函数,具体定义如下

点击(此处)折叠或打开

  1. static int __init
  2. fbmem_init(void)
  3. {
  4.     create_proc_read_entry("fb", 0, NULL, fbmem_read_proc, NULL);

  5.     if (register_chrdev(FB_MAJOR,"fb",&fb_fops))   //可知其file_operations操作集为fb_fops   //#define FB_MAJOR 29
  6.         printk("unable to get major %d for fb devs\n", FB_MAJOR);

  7.     fb_class = class_create(THIS_MODULE, "graphics");
  8.     if (IS_ERR(fb_class)) {
  9.         printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
  10.         fb_class = NULL;
  11.     }
  12.     return 0;
  13. }

其中fb_fops定义如下

点击(此处)折叠或打开

  1. static const struct file_operations fb_fops = {
  2.     .owner =    THIS_MODULE,
  3.     .read =        fb_read,
  4.     .write =    fb_write,
  5.     .ioctl =    fb_ioctl,
  6. #ifdef CONFIG_COMPAT
  7.     .compat_ioctl = fb_compat_ioctl,
  8. #endif
  9.     .mmap =        fb_mmap,
  10.     .open =        fb_open,
  11.     .release =    fb_release,
  12. #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
  13.     .get_unmapped_area = get_fb_unmapped_area,
  14. #endif
  15. #ifdef CONFIG_FB_DEFERRED_IO
  16.     .fsync =    fb_deferred_io_fsync,
  17. #endif
  18. };

同时,fb_open()定义如下

点击(此处)折叠或打开

  1. static int
  2. fb_open(struct inode *inode, struct file *file)
  3. {
  4.     int fbidx = iminor(inode); //获得设备的次设备号,在register_framebuffer中,次设备号被定为缓冲设备在registered_fb数组中的索引值
  5.     struct fb_info *info;
  6.     int res = 0;

  7.     if (fbidx >= FB_MAX) //如果fbidx大于最大设备数目,则返回错误
  8.         return -ENODEV;
  9. #ifdef CONFIG_KMOD
  10.     if (!(info = registered_fb[fbidx]))
  11.         try_to_load(fbidx);
  12. #endif /* CONFIG_KMOD */
  13.     if (!(info = registered_fb[fbidx])) //如果通过fbidx无法在registered_fb中,索引到对象的fb_info结构体实例,则返回错误
  14.         return -ENODEV;
  15.     if (!try_module_get(info->fbops->owner))
  16.         return -ENODEV;
  17.     file->private_data = info;     //将得到的fb_info结构体实例,赋给file->private_data,方便后续的操作,可以通过此方法获取此值
  18.     if (info->fbops->fb_open) { //如果对应的缓冲设备有open函数,则调用。
  19.         res = info->fbops->fb_open(info,1);
  20.         if (res)
  21.             module_put(info->fbops->owner);
  22.     }
  23.     return res;
  24. }

当用户使用系统调用打开设备节点时,内核就会调用 fb_open() 函数,同时帧缓冲又会调用具体设备的fbops中的fb_open()函数。

fb_read()  和  fb_write() 定义如下

点击(此处)折叠或打开

  1. static ssize_t
  2. fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
  3. {
  4.     unsigned long p = *ppos;
  5.     struct inode *inode = file->f_path.dentry->d_inode;
  6.     int fbidx = iminor(inode);
  7.     struct fb_info *info = registered_fb[fbidx]; //通过fbidx索引registered_fb得到设备的fb_info结构体实例
  8.     u32 *buffer, *dst;
  9.     u32 __iomem *src;
  10.     int c, i, cnt = 0, err = 0;
  11.     unsigned long total_size;

  12.     if (!info || ! info->screen_base) //若info不存在 或 DMA缓冲区的虚拟地址不存在,则出错返回
  13.         return -ENODEV;

  14.     if (info->state != FBINFO_STATE_RUNNING)
  15.         return -EPERM;

  16.     if (info->fbops->fb_read) //若设备fb_ops操作集中有fb_read函数,则调用之
  17.         return info->fbops->fb_read(info, buf, count, ppos);
  18.     
  19.     total_size = info->screen_size;

  20.     if (total_size == 0)
  21.         total_size = info->fix.smem_len; //total_size为0的话,取 info->fix.smem_len

  22.     if (>= total_size) //若 读取偏移量 > total_size,则出错返回。
  23.         return 0;

  24.     if (count >= total_size) //若 读取数目 > total_size,则修正为total_size
  25.         count = total_size;

  26.     if (count + p > total_size) //若 读取数目 + 偏移量 > total_size,则 读取数目 修正为total_size - 偏移量
  27.         count = total_size - p;

  28.     buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
  29.              GFP_KERNEL); //动态申请内存,用于缓冲数据,最大申请PAGE_SIZE大小,后面会分段传输
  30.     if (!buffer)
  31.         return -ENOMEM;

  32.     src = (u32 __iomem *) (info->screen_base + p); //DMA缓冲区的首地址 + 偏移量 设为源地址

  33.     if (info->fbops->fb_sync) //若设备fb_ops操作集中有fb_sync函数,则调用之
  34.         info->fbops->fb_sync(info);

  35.     while (count) {
  36.         c = (count > PAGE_SIZE) ? PAGE_SIZE : count; //缓冲大小为PAGE_SIZE,若count较大,则分段。
  37.         dst = buffer; //目的地址为缓冲区首地址
  38.         for (= c >> 2; i--; ) //每次读取4字节
  39.             *dst++ = fb_readl(src++);
  40.         if (& 3) { //对于最后小于4字节的数据,进行特别操作,每次传输1字节
  41.             u8 *dst8 = (u8 *) dst;
  42.             u8 __iomem *src8 = (u8 __iomem *) src;

  43.             for (= c & 3; i--;)
  44.                 *dst8++ = fb_readb(src8++);

  45.             src = (u32 __iomem *) src8;
  46.         }

  47.         if (copy_to_user(buf, buffer, c)) { //将内核缓冲区中的数据传输给用户层
  48.             err = -EFAULT;
  49.             break;
  50.         }
  51.         *ppos += c; //?
  52.         buf += c; //用户层接收区域需要后移
  53.         cnt += c; //已传输数据数目需要增加
  54.         count -= c; //读取的数目需要减少
  55.     }

  56.     kfree(buffer); //传输完毕后,释放内核缓冲区

  57.     return (err) ? err : cnt; 
  58. }

  59. static ssize_t
  60. fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
  61. {
  62.     unsigned long p = *ppos;
  63.     struct inode *inode = file->f_path.dentry->d_inode;
  64.     int fbidx = iminor(inode);
  65.     struct fb_info *info = registered_fb[fbidx]; //通过fbidx索引registered_fb得到设备的fb_info结构体实例
  66.     u32 *buffer, *src;
  67.     u32 __iomem *dst;
  68.     int c, i, cnt = 0, err = 0;
  69.     unsigned long total_size;

  70.     if (!info || !info->screen_base) //若info不存在 或 DMA缓冲区的虚拟地址不存在,则出错返回
  71.         return -ENODEV;

  72.     if (info->state != FBINFO_STATE_RUNNING)
  73.         return -EPERM;

  74.     if (info->fbops->fb_write) //若设备fb_ops操作集中有fb_write函数,则调用之
  75.         return info->fbops->fb_write(info, buf, count, ppos);
  76.     
  77.     total_size = info->screen_size;

  78.     if (total_size == 0)
  79.         total_size = info->fix.smem_len; //total_size为0的话,取 info->fix.smem_len

  80.     if (> total_size) //若 写入偏移量 > total_size,则出错返回。
  81.         return -EFBIG;

  82.     if (count > total_size) { //若 写入数目 > total_size,则修正为total_size
  83.         err = -EFBIG;
  84.         count = total_size;
  85.     }

  86.     if (count + p > total_size) { //若 读取数目 + 偏移量 > total_size,则 写入数目 修正为 total_size - 偏移量
  87.         if (!err)
  88.             err = -ENOSPC;

  89.         count = total_size - p;
  90.     }

  91.     buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
  92.              GFP_KERNEL); //动态申请内存,用于缓冲数据,最大申请PAGE_SIZE大小,后面会分段传输
  93.     if (!buffer)
  94.         return -ENOMEM;

  95.     dst = (u32 __iomem *) (info->screen_base + p); //DMA缓冲区的首地址 + 偏移量 设为目的地址

  96.     if (info->fbops->fb_sync) //若设备fb_ops操作集中有fb_sync函数,则调用之
  97.         info->fbops->fb_sync(info);

  98.     while (count) { //同读取操作类似,不详述
  99.         c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
  100.         src = buffer;

  101.         if (copy_from_user(src, buf, c)) {
  102.             err = -EFAULT;
  103.             break;
  104.         }

  105.         for (= c >> 2; i--; )
  106.             fb_writel(*src++, dst++);

  107.         if (& 3) {
  108.             u8 *src8 = (u8 *) src;
  109.             u8 __iomem *dst8 = (u8 __iomem *) dst;

  110.             for (= c & 3; i--; )
  111.                 fb_writeb(*src8++, dst8++);

  112.             dst = (u32 __iomem *) dst8;
  113.         }

  114.         *ppos += c;
  115.         buf += c;
  116.         cnt += c;
  117.         count -= c;
  118.     }

  119.     kfree(buffer);

  120.     return (cnt) ? cnt : err;
  121. }

当用户使用系统调用读写设备节点时,内核就会调用 fb_read()  fb_write() 函数,同时帧缓冲又会调用具体设备的fbops中的fb_read()fb_write()函数。


fb_ioctl()函数定义如下

点击(此处)折叠或打开

  1. static int 
  2. fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
  3.      unsigned long arg)
  4. {
  5.     int fbidx = iminor(inode);
  6.     struct fb_info *info = registered_fb[fbidx]; //通过fbidx索引registered_fb得到设备的fb_info结构体实例
  7.     struct fb_ops *fb = info->fbops; //得到设备的fb_ops操作集
  8.     struct fb_var_screeninfo var;
  9.     struct fb_fix_screeninfo fix;
  10.     struct fb_con2fbmap con2fb;
  11.     struct fb_cmap_user cmap;
  12.     struct fb_event event;
  13.     void __user *argp = (void __user *)arg;
  14.     int i;
  15.     
  16.     if (!fb) //如果设备没有自定义fb_ops操作集,则返回出错
  17.         return -ENODEV;
  18.     switch (cmd) { //根据cmd命令,执行相应的操作
  19.     case FBIOGET_VSCREENINFO: //获取fb_var_screeninfo(可变参数),直接将info->var传输给用户层
  20.         return copy_to_user(argp, &info->var,
  21.                  sizeof(var)) ? -EFAULT : 0;
  22.     case FBIOPUT_VSCREENINFO: //设置fb_var_screeninfo(可变参数)
  23.         if (copy_from_user(&var, argp, sizeof(var))) //通过argp,将用户层传递过来的参数保存在var变量中
  24.             return -EFAULT;
  25.         acquire_console_sem(); //lock the console system for exclusive use
  26.         info->flags |= FBINFO_MISC_USEREVENT;
  27.         i = fb_set_var(info, &var); //调用fb_set_var,进行设定,FBINFO_MISC_USEREVENT用于指定需要设定参数
  28.         info->flags &= ~FBINFO_MISC_USEREVENT;
  29.         release_console_sem(); //unlock the console system
  30.         if (i) return i; //若fb_set_var出错,则将错误码返回
  31.         if (copy_to_user(argp, &var, sizeof(var))) //若没出错,则通过argp地址将var参数返回
  32.             return -EFAULT;
  33.         return 0;
  34.     case FBIOGET_FSCREENINFO: //获取fb_fix_screeninfo(可变参数),直接将info->fix传输给用户层
  35.         return copy_to_user(argp, &info->fix,
  36.                  sizeof(fix)) ? -EFAULT : 0;
  37.     case FBIOPUTCMAP: //设置用户提供的颜色表
  38.         if (copy_from_user(&cmap, argp, sizeof(cmap))) //通过argp,将用户设定的调色板参数保存在cmap中
  39.             return -EFAULT;
  40.         return (fb_set_user_cmap(&cmap, info)); //调用fb_set_user_cmap,设置调色表
  41.     case FBIOGETCMAP: //查询用户提供的颜色表
  42.         if (copy_from_user(&cmap, argp, sizeof(cmap)))
  43.             return -EFAULT;
  44.         return fb_cmap_to_user(&info->cmap, &cmap);
  45.     case FBIOPAN_DISPLAY:
  46.         if (copy_from_user(&var, argp, sizeof(var)))
  47.             return -EFAULT;
  48.         acquire_console_sem();
  49.         i = fb_pan_display(info, &var);
  50.         release_console_sem();
  51.         if (i)
  52.             return i;
  53.         if (copy_to_user(argp, &var, sizeof(var)))
  54.             return -EFAULT;
  55.         return 0;
  56.     case FBIO_CURSOR:
  57.         return -EINVAL;
  58.     case FBIOGET_CON2FBMAP:
  59.         if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
  60.             return -EFAULT;
  61.         if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
  62.          return -EINVAL;
  63.         con2fb.framebuffer = -1;
  64.         event.info = info;
  65.         event.data = &con2fb;
  66.         fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
  67.         return copy_to_user(argp, &con2fb,
  68.                  sizeof(con2fb)) ? -EFAULT : 0;
  69.     case FBIOPUT_CON2FBMAP:
  70.         if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
  71.             return - EFAULT;
  72.         if (con2fb.console < 0 || con2fb.console > MAX_NR_CONSOLES)
  73.          return -EINVAL;
  74.         if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
  75.          return -EINVAL;
  76. #ifdef CONFIG_KMOD
  77.         if (!registered_fb[con2fb.framebuffer])
  78.          try_to_load(con2fb.framebuffer);
  79. #endif /* CONFIG_KMOD */
  80.         if (!registered_fb[con2fb.framebuffer])
  81.          return -EINVAL;
  82.         event.info = info;
  83.         event.data = &con2fb;
  84.         return fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP,
  85.                      &event);
  86.     case FBIOBLANK:
  87.         acquire_console_sem();
  88.         info->flags |= FBINFO_MISC_USEREVENT;
  89.         i = fb_blank(info, arg);
  90.         info->flags &= ~FBINFO_MISC_USEREVENT;
  91.         release_console_sem();
  92.         return i;
  93.     default:
  94.         if (fb->fb_ioctl == NULL)
  95.             return -EINVAL;
  96.         return fb->fb_ioctl(info, cmd, arg);
  97.     }
  98. }

其中,也会尝试去调用 具体设备的 fbops 中的 相关函数


大家可以参考下面的结构图,它说明了 Linux 帧缓冲设备驱动的主要结构,帧缓冲设备提供给用户空间的file_operations结构体由fbmem.c中的 fb_fops实例提供,并向下提供了相应的接口,让驱动开发者挂接具体的设备驱动。




来源:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=11366591&id=3864559

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值