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