framebuffer

pan -- 平移 
zoom -- 缩放


虚拟分辨率和可见分辨率:
目前的理解为:物理分辨率为240*320的显示器(横向320个像素,纵向240个像素),无缩放显示1024*768的图像时,320*240就是可见分辨率,1024*768就是虚拟分辨率.



    逻辑图像
    |----------------------------------------------------------------------------------------|
    |                                                                                        |
    |                                                                                        |
    |                                                                                        |
    |               物理显示器                                                                |
    |               =====================================================                    |
    |               +                                                   +                    |
    |               +                                                   +                    |
    |               +                                                   +                    |
    |               +                                                   +                    |
    |               +                                                   +                    |
    |               +                                                   +                    |
    |               +                                                   +                    |
    |               +                                                   +                    |
    |               =====================================================                    |
    |                                                                                        |
    |                                                                                        |
    |                                                                                        |
    |----------------------------------------------------------------------------------------|


FBIOPAN_DISPLAY:
我们知道用户空间的显示管理程序(比如xserver)都是直接映射fb设备的显存进入用户空间,然后直接操作这块内存。但是有的fb设备实现了双缓冲,那显示管理程序怎么在这两块缓存间切换呢?就是用这个FBIOPAN_DISPLAY操作。

FBIOPAN_DISPLAY在linux的注释里是“平移显示”的意思。怎么理解呢?就是按照y坐标平移显示缓存中的内容。调用FBIOPAN_DISPLAY时,会传一个y坐标偏移量yoffset给驱动,然后驱动会把当前显存的指针偏移 “yoffset X 屏幕宽度 X 位色字节数” 个字节,这样就好像实现了图像的y坐标平移,也就是“平移显示”。当这个yoffset等于屏幕高度的时候,就实现了显存的切换。



struct fb_info { 
        int node;                     /* 设备节点 */ 
        int flags; 
        struct fb_var_screeninfo var; /* 当前可变参数 */ 
        struct fb_fix_screeninfo fix; /* 当前固定参数 */ 
        struct fb_monspecs monspecs;  /* 当前监视器特征 */ 
        struct work_struct queue;     /* 帧缓冲事件队列 */ 
        struct fb_pixmap pixmap;      /* 图象硬件映射变量 */ 
        struct fb_pixmap sprite;      /* 光标硬件映射变量 */ 
        struct fb_cmap cmap;          /* 当前颜色映射变量 */ 
        struct list_head modelist;    /* 模式列表*/ 
        struct fb_videomode *mode;    /* 当前模式*/ 
        ...... 
        struct fb_ops *fbops;         /* 该指针指向驱动函数集 */ 
        ...... 
        struct device *dev;           /* 代表此帧缓冲设备 */ 
        ...... 
        char __iomem *screen_base;    /* IO映射基址(虚地址) */ 
        unsigned long screen_size;    /* Amount of ioremapped VRAM or 0 */ 
        void *pseudo_palette;         /* 调色板内存地址 */ 
        ...... 
};

struct fb_fix_screeninfo { 
        char id[16];                  /* 设备名*/ 
        unsigned long smem_start;     /* frame buffer 缓冲区起始地址(物理地址)*/ 
        __u32 smem_len;               /* 缓冲区长度*/ 
        __u32 type;                   /* 设备类型,例如TFT或STN*/ 
        ...... 
        __u32 visual;                 /* 色彩类型,真彩色、假彩色或单色*/ 
        ...... 
        __u32 line_length;            /* 屏幕上每行的字节数 */ 
        unsigned long mmio_start;     /* IO映射区起始地址(物理地址) */ 
        __u32 mmio_len;               /* IO 映射区长度 */ 
        __u32 accel;                  /* 指出使用的加速卡是哪些特定的芯片 */ 
        __u16 reserved[3];            /* 系统保留*/ 
};

struct fb_var_screeninfo { 
        __u32 xres;                   /* visible resolution 可见分辨率(显示器的物理分辨率) */ 
        __u32 yres; 
        __u32 xres_virtual;           /* virtual resolution 虚拟分辨率 */ 
        __u32 yres_virtual; 
        __u32 xoffset;                /* 从虚拟分辨率到可见分辨率的偏移量*/ 
        __u32 yoffset; 
        __u32 bits_per_pixel;         /* 像素深度 */ 
        __u32 grayscale;              /* 灰度级 */ 
        struct fb_bitfield red; 
        struct fb_bitfield green; 
        struct fb_bitfield blue; 
        struct fb_bitfield transp;    /* 透明度 */
        __u32 nonstd;                 /* 非标准像素格式 */ 
        ...... 
        __u32 pixclock;               /* 像素时钟,单位是皮秒*/ 
        __u32 left_margin;            /* 左侧边缘区*/ 
        __u32 right_margin;           /* 右侧边缘区 */ 
        __u32 upper_margin;           /* 顶部边缘区 */ 
        __u32 lower_margin; 
        __u32 hsync_len;              /* 水平扫描边缘区 */ 
        __u32 vsync_len;              /* 垂直扫描边缘区 */ 
        ...... 
};


struct fb_ops {
        //open/release and usage marking
        struct module *owner;
        int (*fb_open)(struct fb_info *info, int user);
        int (*fb_release)(struct fb_info *info, int user);

        //For framebuffers with strange non linear layouts or that do not
        //work with normal memory mapped access
        ssize_t (*fb_read)(struct fb_info *info, char __user *buf, size_t count, loff_t *ppos);
        ssize_t (*fb_write)(struct fb_info *info, const char __user *buf, size_t count, loff_t *ppos);

        //checks var and eventually tweaks it to something supported,
        //DO NOT MODIFY PAR
        int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);

        //set the video mode according to info->var
        int (*fb_set_par)(struct fb_info *info);

        //set color register
        int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
                            unsigned blue, unsigned transp, struct fb_info *info);

        //set color registers in batch
        int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);

        //blank display
        int (*fb_blank)(int blank, struct fb_info *info);

        //pan display
        int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);


        //Draws a rectangle
        void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
        //Copy data from area to another
        void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
        //Draws a image to the display
        void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);

        //Draws cursor
        int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);

        //Rotates the display
        void (*fb_rotate)(struct fb_info *info, int angle);

        //wait for blit idle, optional
        int (*fb_sync)(struct fb_info *info);

        //perform fb specific ioctl (optional)
        int (*fb_ioctl)(struct fb_info *info, unsigned int cmd, unsigned long arg);

        //Handle 32bit compat ioctl (optional)
        int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd, unsigned long arg);

        //perform fb specific mmap
        int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);

        //get capability given var
        void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps, struct fb_var_screeninfo *var);

        //teardown any resources to do with this framebuffer
        void (*fb_destroy)(struct fb_info *info);
};





static int
fb_open(struct inode *inode, struct file *file)
__acquires(&info->lock)
__releases(&info->lock)
{
        int fbidx = iminor(inode); //提取次设备号作为frame buffer设备的索引
        struct fb_info *info;
        int res = 0;

        if (fbidx >= FB_MAX)
                return -ENODEV;
        //如果frame buffer已经注册,从已注册frame buffer数组中找到索引对应的frame buffer
        info = registered_fb[fbidx]; 
        //如果frame buffer没有注册,则加载这个module
        if (!info)
                request_module("fb%d", fbidx);  
        //再次从已注册frame buffer数组中找索引对应的frame buffer.
        info = registered_fb[fbidx];
        //如果仍然没有找到,则返回出错值-ENODEV
        if (!info)
                return -ENODEV;
        mutex_lock(&info->lock);
        //走到这说明模块已经加载了,增加frame buffer对应的module的引用计数.
        if (!try_module_get(info->fbops->owner)) {
                res = -ENODEV;
                goto out;
        }
        //将frame buffer描述符fb_info载入file->private_data
        file->private_data = info;
        //如果info->fbops->fb_open()有定义,则调用之
        if (info->fbops->fb_open) {
                res = info->fbops->fb_open(info,1);
                if (res)
                        module_put(info->fbops->owner);
        }
#ifdef CONFIG_FB_DEFERRED_IO
        //如果info->fbdefio为真,则调用fb_deferred_io_open().
        if (info->fbdefio)
                fb_deferred_io_open(info, inode, file);
#endif
out:
        mutex_unlock(&info->lock);
        return res;
}


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];

        return do_fb_ioctl(info, cmd, arg);
}

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;       //从frame buffer描述符中拿到设备无关的[可变]参数集
                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); //将用户空间传入的[可变]参数集写入frame buffer描述符
                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;            //从frame buffer描述符中拿到设备无关的[固定]参数集
                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);  //设置color map
                break;
        case FBIOGETCMAP:
                if (copy_from_user(&cmap, argp, sizeof(cmap)))
                        return -EFAULT;
                if (!lock_fb_info(info))
                        return -ENODEV;
                cmap_from = info->cmap;              //取得color map
                unlock_fb_info(info);
                ret = fb_cmap_to_user(&cmap_from, &cmap);
                break;
        //按照参数var->xoffset 和var->yoffset平移frame buffer中的内容, 可以用在双buffer的切换
        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;
        //con2fb : console to frame_buffer      
        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); //notifier ...
                unlock_fb_info(info);
                ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
                break;
        //con2fb : console to frame_buffer
        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);  //加载fbN模块
                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;
        //FBIOBLANK:使能或者点亮frame buffer, 参数arg可以是POWERDOWN, NORMAL HSYNC_SUSPEND, VSYNC_SUSPEND UNBLANK
        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;
        //调用frame buffer specific的IOCTL选项
        default:
                fb = info->fbops;
                if (fb->fb_ioctl)
                        ret = fb->fb_ioctl(info, cmd, arg);
                else
                        ret = -ENOTTY;
        }
        return ret;
}

static int
fb_mmap(struct file *file, struct vm_area_struct * vma)
{
        int fbidx = iminor(file->f_path.dentry->d_inode);
#includestruct fb_info *info = registered_fb[fbidx];
        struct fb_ops *fb = info->fbops;
#includeunsigned 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;
        }
	 /* frame buffer memory */
        start = info->fix.smem_start; //注意,这里是指向设备的物理地址
        len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len); //调整对齐后长度
        if (off >= len) {  //对应于IO端口统一编址的情况
                /* memory mapped io */
                off -= len;
                if (info->var.accel_flags) {
                        mutex_unlock(&info->mm_lock);
                        return -EINVAL;
                }
                start = info->fix.mmio_start; //当IO端口统一编址时,就使用端口的物理地址
                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;
        /* This is an IO map - tell maydump to skip this VMA */
        vma->vm_flags |= VM_IO | VM_RESERVED;  //本内存页作为IO之用,已经保留
        fb_pgprotect(file, vma, off);          //对帧缓冲的内存页进行标识,不要挪作他用
        //实际的映射操作由io_remap_pfn_range()函数来完成
        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;
}     

//从info->screen_base中的数据读入用户空间.
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];
        u32 *buffer, *dst;
        u32 __iomem *src;
        int c, i, cnt = 0, err = 0;
        unsigned long total_size;

        if (!info || ! info->screen_base)
                return -ENODEV;

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

        //如果info->fbops->read()存在,则利用它来取得相关的数据
        if (info->fbops->fb_read)
                return info->fbops->fb_read(info, buf, count, ppos);

        total_size = info->screen_size
        if (total_size == 0)
                total_size = info->fix.smem_len;

        if (p >= total_size)
                return 0;

        if (count >= total_size)
                count = total_size;

        if (count + p > total_size)
                count = total_size - p;

        //开辟不大于一个page的buffer
        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;
                for (i = c >> 2; i--; )
                        *dst++ = fb_readl(src++);
                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;
}


//将用户空间传入的数据写入info->screen_base
static ssize_t
fb_write(struct file *file, const 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];
        u32 *buffer, *src;
        u32 __iomem *dst;
        int c, i, cnt = 0, err = 0;
        unsigned long total_size;

        if (!info || !info->screen_base)
                return -ENODEV;

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

        //如果有定义info->fbops->fb_write()则调用之
        if (info->fbops->fb_write)
                return info->fbops->fb_write(info, buf, count, ppos);

        total_size = info->screen_size;

        if (total_size == 0)
                total_size = info->fix.smem_len;

        if (p > total_size)
                return -EFBIG;

        if (count > total_size) {
                err = -EFBIG;
                count = total_size;
        }

        if (count + p > total_size) {
                if (!err)
                        err = -ENOSPC;
                count = total_size - p;
        }

        buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
                         GFP_KERNEL);
        if (!buffer)
                return -ENOMEM;

        dst = (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;
                src = buffer;

                if (copy_from_user(src, buf, c)) {
                        err = -EFAULT;
                        break;
                }

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

                if (c & 3) {
                        u8 *src8 = (u8 *) src;
                        u8 __iomem *dst8 = (u8 __iomem *) dst;

                        for (i = c & 3; i--; )
                                fb_writeb(*src8++, dst8++);
                        dst = (u32 __iomem *) dst8;
                }

                *ppos += c;
                buf += c;
                cnt += c;
                count -= c;
        }

        kfree(buffer);

        return (cnt) ? cnt : err;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值