帧缓冲字符设备接口

Posted laoyaodada

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了帧缓冲字符设备接口相关的知识,希望对你有一定的参考价值。

帧缓冲设备的file_operations中的成员函数都在fbmem.c中实现,一般不需要驱动工程师修改。这里主要分析它的write、mmap和ioctl方法。

 1 static ssize_t fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {
 2     unsigned long p = *ppos;
 3     struct fb_info *info = file_fb_info(file);    // 获取相应的帧缓冲描述结构,fb_info结构
 4     u8 *buffer, *src;
 5     u8 __iomem *dst;
 6     int c, cnt = 0, err = 0;
 7     unsigned long total_size;
 8 
 9     if (!info || !info->screen_base)
10         return -ENODEV;
11 
12     if (info->state != FBINFO_STATE_RUNNING)
13         return -EPERM;
14 
15     if (info->fbops->fb_write)    // 如果fb_ops中定义了特定的写函数则调用之
16         return info->fbops->fb_write(info, buf, count, ppos);
17     
18     /* 如果fb_ops中没有定义写函数则执行下面通用的写缓冲代码 */
19     total_size = info->screen_size;
20 
21     if (total_size == 0)
22         total_size = info->fix.smem_len;
23 
24     if (p > total_size)    // 写入位置超过帧缓冲大小则返回-EFBIG错误
25         return -EFBIG;
26 
27     if (count > total_size) {    
28         err = -EFBIG;
29         count = total_size;
30     }
31 
32     if (count + p > total_size) {    // 对写入大小进行调整
33         if (!err)
34             err = -ENOSPC;
35 
36         count = total_size - p;
37     }
38     /* 分配用于临时存放显示缓冲区数据的buffer */
39     buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
40              GFP_KERNEL);
41     if (!buffer)
42         return -ENOMEM;
43     /* 目的地址是帧缓冲虚拟地址加上偏移 */
44     dst = (u8 __iomem *) (info->screen_base + p);
45 
46     if (info->fbops->fb_sync)    // 如果fb_ops中实现了同步方法则调用之
47         info->fbops->fb_sync(info);
48 
49     while (count) {
50         /* 将预写数据一次读到buffer中,每次写数据不超过PAGE_SIZE大小 */
51         c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
52         src = buffer;
53 
54         if (copy_from_user(src, buf, c)) {
55             err = -EFAULT;
56             break;
57         }
58         /* 将实际数据写入 */
59         fb_memcpy_tofb(dst, src, c);
60         dst += c;
61         src += c;
62         *ppos += c;
63         buf += c;
64         cnt += c;
65         count -= c;
66     }
67 
68     kfree(buffer);
69 
70     return (cnt) ? cnt : err;
71 }

帧缓冲设备的mmap()操作函数比较重要,因为多数情况下访问帧缓冲设备不是通过其读写方法,而是通过mmap()系统调用将帧缓冲映射到用户空间直接访问。这样做不仅可以省去一次用户空间与内核空间的数据拷贝,而且操作起来更加方便快捷。

 1 static int fb_mmap(struct file *file, struct vm_area_struct * vma) {
 2     struct fb_info *info = file_fb_info(file);
 3     struct fb_ops *fb;
 4     unsigned long off;
 5     unsigned long start;
 6     u32 len;
 7 
 8     if (!info)
 9         return -ENODEV;
10     if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
11         return -EINVAL;
12     off = vma->vm_pgoff << PAGE_SHIFT;    // 取得此VMA在映射设备文件中的偏移
13     fb = info->fbops;
14     if (!fb)
15         return -ENODEV;
16     mutex_lock(&info->mm_lock);
17     if (fb->fb_mmap) {     // 如果fb_ops中实现了mmap()方法则调用之
18         int res;
19         res = fb->fb_mmap(info, vma);
20         mutex_unlock(&info->mm_lock);
21         return res;
22     }
23 
24     /* 如果fb_ops中没有实现mmap()方法则执行下面通用的映射代码
25        获取映射帧缓冲的物理起始地址和长度 */
26     start = info->fix.smem_start;
27     len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
28     if (off >= len) {
29         /* 如果off大于帧缓冲长度,则认为映射的是内存映射IO */
30         off -= len;
31         if (info->var.accel_flags) {
32             mutex_unlock(&info->mm_lock);
33             return -EINVAL;
34         }
35         /* 获取内存映射IO的物理起始地址和长度 */
36         start = info->fix.mmio_start;
37         len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
38     }
39     mutex_unlock(&info->mm_lock);
40     start &= PAGE_MASK;    // 保证页对齐
41     if ((vma->vm_end - vma->vm_start + off) > len)
42         return -EINVAL;
43     off += start;    // 现在off表示的是映射设备内存实际的物理地址
44     vma->vm_pgoff = off >> PAGE_SHIFT;
45     /* This is an IO map - tell maydump to skip this VMA */
46     vma->vm_flags |= VM_IO | VM_RESERVED;
47     vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
48     fb_pgprotect(file, vma, off);
49     /* 建立从物理页帧号为off>>PAGE_SHIFT的物理内存,到虚拟地址为vma->vm_start,大小为vma->
50        vm_end - vma->vm_start,页保护标志为vma->vm_page_prot的映射 */
51     if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
52                  vma->vm_end - vma->vm_start, vma->vm_page_prot))
53         return -EAGAIN;
54     return 0;
55 }

最后给出的是ioctl()方法的实现代码,我们需要关注的是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;
        console_lock();
        if (!lock_fb_info(info)) {
            console_unlock();
            return -ENODEV;
        }
        info->flags |= FBINFO_MISC_USEREVENT;
        ret = fb_set_var(info, &var);
        info->flags &= ~FBINFO_MISC_USEREVENT;
        unlock_fb_info(info);
        console_unlock();
        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;
        console_lock();
        if (!lock_fb_info(info)) {
            console_unlock();
            return -ENODEV;
        }
        ret = fb_pan_display(info, &var);
        unlock_fb_info(info);
        console_unlock();
        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;
        console_lock();
        if (!lock_fb_info(info)) {
            console_unlock();
            return -ENODEV;
        }
        event.info = info;
        ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
        unlock_fb_info(info);
        console_unlock();
        break;
    case FBIOBLANK:
        console_lock();
        if (!lock_fb_info(info)) {
            console_unlock();
            return -ENODEV;
        }
        info->flags |= FBINFO_MISC_USEREVENT;
        ret = fb_blank(info, arg);
        info->flags &= ~FBINFO_MISC_USEREVENT;
        unlock_fb_info(info);
        console_unlock();
        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;
}

常用的命令有:

  • FBIOGET_VSCREENINFO:获得可变的屏幕参数。
  • FBIOPUT_VSCREENINFO:设置可变的屏幕参数。
  • FBIOGET_FSCREENINFO:获得固定的屏幕参数设置,不能由用户设置。
  • FBIOPUTCMAP:设置颜色表。
  • FBIOGETCMAP:获得颜色表。

以上是关于帧缓冲字符设备接口的主要内容,如果未能解决你的问题,请参考以下文章

帧缓冲纹理出现白色(片段着色器不影响它)

帧缓存的详细介绍

Linux 帧缓冲子系统详解:LCD介绍framebuffer驱动框架LCD驱动源码分析

Linux驱动开发: FrameBuffe(LCD)驱动开发

嵌入式Linux下完成LCD屏文字显示(帧缓冲框架)

#导入Word文档图片# Linux下FrameBuffe(LCD)驱动编写