fbmem.c分析
来源:互联网 发布:php sendmail 抄送 编辑:程序博客网 时间:2024/05/02 02:52
int fb_get_color_depth(struct fb_var_screeninfo *var,struct fb_fix_screeninfo *fix){ int depth = 0; if (fix->visual == FB_VISUAL_MONO01 || fix->visual == FB_VISUAL_MONO10) depth = 1; else { if (var->green.length == var->blue.length && var->green.length == var->red.length && var->green.offset == var->blue.offset && var->green.offset == var->red.offset) depth = var->green.length; else depth = var->green.length + var->red.length + var->blue.length; } return depth;}该函数获取颜色深度,很简单啊,对于单色深度为1,否则深度为red blue green三个分量的和 char* fb_get_buffer_offset(struct fb_info *info, struct fb_pixmap *buf, u32 size) { u32 align = buf->buf_align - 1, offset; char *addr = buf->addr; /* If IO mapped, we need to sync before access, no sharing of * the pixmap is done */ if (buf->flags & FB_PIXMAP_IO) { if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC)) info->fbops->fb_sync(info); return addr; } /* See if we fit in the remaining pixmap space */ offset = buf->offset + align; offset &= ~align; if (offset + size > buf->size) { /* We do not fit. In order to be able to re-use the buffer, * we must ensure no asynchronous DMA'ing or whatever operation * is in progress, we sync for that. */ if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC)) info->fbops->fb_sync(info); offset = 0; } buf->offset = offset + size; addr += offset; return addr; }这个函数看似简单,就是获取@buf中符合@size大小的空闲位置 如果剩余空间小于需要的大小,那么fb_sync后就可以使用@buffer的所有空间 这个函数看起来总是怪挂的,因为fb_sync的参数没有涉及到@buf, 所以fb_sync跟@buf有毛关系呀 虽然调用fb_get_buffer_offset时的@info和@buf的关系是@info->pixmap == @buf,那为毛不只传一个参数? static void fb_set_logocmap(struct fb_info *info,const struct linux_logo *logo) { struct fb_cmap palette_cmap; u16 palette_green[16]; u16 palette_blue[16]; u16 palette_red[16]; int i, j, n; const unsigned char *clut = logo->clut; palette_cmap.start = 0; palette_cmap.len = 16; palette_cmap.red = palette_red; palette_cmap.green = palette_green; palette_cmap.blue = palette_blue;#define FB_VISUAL_MONO01 0 /* Monochr. 1=Black 0=White */ palette_cmap.transp = NULL; for (i = 0; i < logo->clutsize; i += n) { n = logo->clutsize - i; /* palette_cmap provides space for only 16 colors at once */ if (n > 16) n = 16; palette_cmap.start = 32 + i; palette_cmap.len = n; for (j = 0; j < n; ++j) { palette_cmap.red[j] = clut[0] << 8 | clut[0]; palette_cmap.green[j] = clut[1] << 8 | clut[1]; palette_cmap.blue[j] = clut[2] << 8 | clut[2]; clut += 3; } fb_set_cmap(&palette_cmap, info); } }在介绍这个函数前,先了解下调色板 在linux系统中,支持以下几种色彩模式 #define FB_VISUAL_MONO01 0 #define FB_VISUAL_MONO10 1 /* Monochr. 1=White 0=Black */ #define FB_VISUAL_TRUECOLOR 2 /* True color */ #define FB_VISUAL_PSEUDOCOLOR 3 /* Pseudo color (like atari) */ #define FB_VISUAL_DIRECTCOLOR 4 /* Direct color */ FB_VISUAL_MONO10 FB_VISUAL_MONO01 每个像素为黑或者白 FB_VISUAL_TRUECOLOR 真彩色,分为红蓝绿三基色 FB_VISUAL_PSEUDOCOLOR 伪彩色,采用索引颜色显示,需要根据颜色index查找colormap,找到相应的颜色值 FB_VISUAL_DIRECTORCOLOR 每个像素颜色也是由红绿蓝三种颜色组成,不过每个颜色都是索引值,需要查表 注意FB_VISUAL_PSEUDOCOLOR和FB_VISUAL_DIRECTORCOLOR都是使用颜色所以,需要查表 看下fb_cmap结构,这个结构定义了颜色表(color map) struct fb_cmap { __u32 start; /* 第一个entry, 没看出start的作用 */ __u32 len; /* 每个颜色分量的长度 */ __u16 *red; /* 红色分量 */ __u16 *green; __u16 *blue; __u16 *transp; /* 透明度,可以为空 */}; 结构linux_logo 描述了一个linux logo的全部信息 struct linux_logo { int type; /* one of LINUX_LOGO_*, logo的类型 */ unsigned int width; /* logo的宽度*/ unsigned int height; /* logo的高度*/ unsigned int clutsize; /* LINUX_LOGO_CLUT224 only, 颜色查找表的尺寸 */ const unsigned char *clut; /* LINUX_LOGO_CLUT224 only, 颜色查找表*/ const unsigned char *data; /* logo 文件数据,对于LINUX_LOGO_CLUT224,data保存的是查找表的位置 */ }; 回头来看 fb_set_logocmap, 这个函数写的非常的恶心,我从来没见过这么恶心的kernel代码,当然我也够贱,非要分析如此恶心的代码 这个函数是一个大循环,要用log->clut这个colormap去设置@info device 的colormap,每次最多处理16x3个颜色索引 palette_cmap.start = 32 + i; 这里加了个32,很讨厌这种数字写法,这里之所以选32是因为CLUT224这种格式的index值从32直到255,即我们在linux_logo->data中只能找到0值,以及32~255之间的值 fb_set_cmap(&palette_cmap, info); 这个函数会设置硬件调色板以及info->cmap static void fb_set_logo_truepalette(struct fb_info *info, const struct linux_logo *logo, u32 *palette){ static const unsigned char mask[] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff }; unsigned char redmask, greenmask, bluemask; int redshift, greenshift, blueshift; int i; const unsigned char *clut = logo->clut; /* * We have to create a temporary palette since console palette is only * 16 colors long. */ /* Bug: Doesn't obey msb_right ... (who needs that?) */ redmask = mask[info->var.red.length < 8 ? info->var.red.length : 8]; greenmask = mask[info->var.green.length < 8 ? info->var.green.length : 8]; bluemask = mask[info->var.blue.length < 8 ? info->var.blue.length : 8]; redshift = info->var.red.offset - (8 - info->var.red.length); greenshift = info->var.green.offset - (8 - info->var.green.length); blueshift = info->var.blue.offset - (8 - info->var.blue.length); for ( i = 0; i < logo->clutsize; i++) { palette[i+32] = (safe_shift((clut[0] & redmask), redshift) | safe_shift((clut[1] & greenmask), greenshift) | safe_shift((clut[2] & bluemask), blueshift)); clut += 3; }}这个函数为FB_VISUAL_PSEUDOCOLOR彩色模式的logo生成一个调色板,从32开始是因为CLUT224只支持32~255范围内的index值 static void fb_set_logo_directpalette(struct fb_info *info, const struct linux_logo *logo, u32 *palette){ int redshift, greenshift, blueshift; int i; redshift = info->var.red.offset; greenshift = info->var.green.offset; blueshift = info->var.blue.offset; for (i = 32; i < 32 + logo->clutsize; i++) palette[i] = i << redshift | i << greenshift | i << blueshift;}为FB_VISUAL_DIRECTCOLOR彩色模式生成一个调色板,只需生成32 ~ clutsize static void fb_set_logo(struct fb_info *info, const struct linux_logo *logo, u8 *dst, int depth) { int i, j, k; const u8 *src = logo->data; u8 xor = (info->fix.visual == FB_VISUAL_MONO01) ? 0xff : 0; u8 fg = 1, d; switch (fb_get_color_depth(&info->var, &info->fix)) { case 1: fg = 1; break; case 2: fg = 3; break; default: fg = 7; break; } if (info->fix.visual == FB_VISUAL_MONO01 || info->fix.visual == FB_VISUAL_MONO10) fg = ~((u8) (0xfff << info->var.green.length)); switch (depth) { case 4: for (i = 0; i < logo->height; i++) for (j = 0; j < logo->width; src++) { *dst++ = *src >> 4; j++; if (j < logo->width) { *dst++ = *src & 0x0f; j++; } } break; case 1: for (i = 0; i < logo->height; i++) { for (j = 0; j < logo->width; src++) { d = *src ^ xor; for (k = 7; k >= 0; k--) { *dst++ = ((d >> k) & 1) ? fg : 0; j++; } } } break; } } linux_logo->data中保存的是logo的data数据,如果对于mono或者16 色的数据来说,linxu_logo->data内的每个字节保存的是多个像素点的数据,fb_set_logo这个函数根据颜色深度把 linux_logo->data的数据转换到@dst中,@dst中的每个字节,代表这一个像素索引 参见源码注视就很好理解为什么要做转换了 /* * Three (3) kinds of logo maps exist. linux_logo_clut224 (>16 colors), * linux_logo_vga16 (16 colors) and linux_logo_mono (2 colors). Depending on * the visual format and color depth of the framebuffer, the DAC, the * pseudo_palette, and the logo data will be adjusted accordingly. * * Case 1 - linux_logo_clut224: * Color exceeds the number of console colors (16), thus we set the hardware DAC * using fb_set_cmap() appropriately. The "needs_cmapreset" flag will be set. * * For visuals that require color info from the pseudo_palette, we also construct * one for temporary use. The "needs_directpalette" or "needs_truepalette" flags * will be set. * * Case 2 - linux_logo_vga16: * The number of colors just matches the console colors, thus there is no need * to set the DAC or the pseudo_palette. However, the bitmap is packed, ie, * each byte contains color information for two pixels (upper and lower nibble). * To be consistent with fb_imageblit() usage, we therefore separate the two * nibbles into separate bytes. The "depth" flag will be set to 4. * * Case 3 - linux_logo_mono: * This is similar with Case 2. Each byte contains information for 8 pixels. * We isolate each bit and expand each into a byte. The "depth" flag will * be set to 1. */static struct logo_data { int depth; int needs_directpalette; int needs_truepalette; int needs_cmapreset; const struct linux_logo *logo;} fb_logo __read_mostly; @depth是logo的深度 @logo是linux_logo数据 static void fb_rotate_logo_ud(const u8 *in, u8 *out, u32 width, u32 height){ u32 size = width * height, i; out += size - 1; for (i = size; i--; ) *out-- = *in++;}static void fb_rotate_logo_cw(const u8 *in, u8 *out, u32 width, u32 height){ int i, j, h = height - 1; for (i = 0; i < height; i++) for (j = 0; j < width; j++) out[height * j + h - i] = *in++;}static void fb_rotate_logo_ccw(const u8 *in, u8 *out, u32 width, u32 height){ int i, j, w = width - 1; for (i = 0; i < height; i++) for (j = 0; j < width; j++) out[height * (w - j) + i] = *in++;} 实现了logo的几种软件旋转 这几个函数再此验证了代码的恶心程度,没人知道ud, cw ccw是什么含义 fb_rotate_logo_ud旋转180度 fb_rotate_logo_cw 顺时针转动90度 fb_rotate_logo_ccw 逆时针转动90度 static void fb_do_show_logo(struct fb_info *info, struct fb_image *image, int rotate, unsigned int num){ unsigned int x; if (rotate == FB_ROTATE_UR) { for (x = 0; x < num && image->dx + image->width <= info->var.xres; x++) { info->fbops->fb_imageblit(info, image); image->dx += image->width + 8; } } else if (rotate == FB_ROTATE_UD) { for (x = 0; x < num && image->dx >= 0; x++) { info->fbops->fb_imageblit(info, image); image->dx -= image->width + 8; } } else if (rotate == FB_ROTATE_CW) { for (x = 0; x < num && image->dy + image->height <= info->var.yres; x++) { info->fbops->fb_imageblit(info, image); image->dy += image->height + 8; } } else if (rotate == FB_ROTATE_CCW) { for (x = 0; x < num && image->dy >= 0; x++) { info->fbops->fb_imageblit(info, image); image->dy -= image->height + 8; } }}显示@image内的logo数据, @rotate是旋转方式, @num没看懂社么意思阿 FB_ROTATE_UD upper down 颠倒旋转(180度旋转) FB_ROTATE_CW clockwise旋转(顺时针) FB_ROTATE_CCW counter clockwise旋转(逆时针旋转) static int fb_show_logo_line(struct fb_info *info, int rotate, const struct linux_logo *logo, int y, unsigned int n) { u32 *palette = NULL, *saved_pseudo_palette = NULL; unsigned char *logo_new = NULL, *logo_rotate = NULL; struct fb_image image; /* Return if the frame buffer is not mapped or suspended */ if (logo == NULL || info->state != FBINFO_STATE_RUNNING || info->flags & FBINFO_MODULE) return 0; image.depth = 8; image.data = logo->data; if (fb_logo.needs_cmapreset) fb_set_logocmap(info, logo); if (fb_logo.needs_truepalette || fb_logo.needs_directpalette) { palette = kmalloc(256 * 4, GFP_KERNEL); if (palette == NULL) return 0; if (fb_logo.needs_truepalette) fb_set_logo_truepalette(info, logo, palette); else fb_set_logo_directpalette(info, logo, palette); saved_pseudo_palette = info->pseudo_palette; info->pseudo_palette = palette; } if (fb_logo.depth <= 4) { logo_new = kmalloc(logo->width * logo->height, GFP_KERNEL); if (logo_new == NULL) { kfree(palette); if (saved_pseudo_palette) info->pseudo_palette = saved_pseudo_palette; return 0; } image.data = logo_new; fb_set_logo(info, logo, logo_new, fb_logo.depth); } image.dx = 0; image.dy = y; image.width = logo->width; image.height = logo->height; if (rotate) { logo_rotate = kmalloc(logo->width * logo->height, GFP_KERNEL); if (logo_rotate) fb_rotate_logo(info, logo_rotate, &image, rotate); } fb_do_show_logo(info, &image, rotate, n); kfree(palette); if (saved_pseudo_palette != NULL) info->pseudo_palette = saved_pseudo_palette; kfree(logo_new); kfree(logo_rotate); return logo->height;}我无语了,这代码写的,为毛有个@y参数呀 void fb_append_extra_logo(const struct linux_logo *logo, unsigned int n){ if (!n || fb_logo_ex_num == FB_LOGO_EX_NUM_MAX) return; fb_logo_ex[fb_logo_ex_num].logo = logo; fb_logo_ex[fb_logo_ex_num].n = n; fb_logo_ex_num++;}这个函数把给定的logo设置到fb_logo_ex这个全局extend logo数组中, @n作用未知 static int fb_prepare_extra_logos(struct fb_info *info, unsigned int height, unsigned int yres){ unsigned int i; /* FIXME: logo_ex supports only truecolor fb. */ if (info->fix.visual != FB_VISUAL_TRUECOLOR) fb_logo_ex_num = 0; for (i = 0; i < fb_logo_ex_num; i++) { if (fb_logo_ex[i].logo->type != fb_logo.logo->type) { fb_logo_ex[i].logo = NULL; continue; } height += fb_logo_ex[i].logo->height; if (height > yres) { height -= fb_logo_ex[i].logo->height; fb_logo_ex_num = i; break; } } return height;}这段代码写的相当不好,单独引入的fb_logo_ex_num极其恶劣 这段代码的意思也就是计算height,以及fb_logo_ex_num height是logo和有效extend logo的高度和,fb_log_ex_num是有效extend logo的最大索引 static int fb_show_extra_logos(struct fb_info *info, int y, int rotate){ unsigned int i; for (i = 0; i < fb_logo_ex_num; i++) y += fb_show_logo_line(info, rotate, fb_logo_ex[i].logo, y, fb_logo_ex[i].n); return y;}该函数显示保存在fb_logo_ex中的extend logo, @y表示这个extend logo要在屏幕显示的位置 int fb_prepare_logo(struct fb_info *info, int rotate){ int depth = fb_get_color_depth(&info->var, &info->fix); unsigned int yres; memset(&fb_logo, 0, sizeof(struct logo_data)); if (info->flags & FBINFO_MISC_TILEBLITTING || info->flags & FBINFO_MODULE) return 0; if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) { depth = info->var.blue.length; if (info->var.red.length < depth) depth = info->var.red.length; if (info->var.green.length < depth) depth = info->var.green.length; } if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR && depth > 4) { /* assume console colormap */ depth = 4; } /* Return if no suitable logo was found */ fb_logo.logo = fb_find_logo(depth); if (!fb_logo.logo) { return 0; } if (rotate == FB_ROTATE_UR || rotate == FB_ROTATE_UD) yres = info->var.yres; else yres = info->var.xres; if (fb_logo.logo->height > yres) { fb_logo.logo = NULL; return 0; } /* What depth we asked for might be different from what we get */ if (fb_logo.logo->type == LINUX_LOGO_CLUT224) fb_logo.depth = 8; else if (fb_logo.logo->type == LINUX_LOGO_VGA16) fb_logo.depth = 4; else fb_logo.depth = 1; if (fb_logo.depth > 4 && depth > 4) { switch (info->fix.visual) { case FB_VISUAL_TRUECOLOR: fb_logo.needs_truepalette = 1; break; case FB_VISUAL_DIRECTCOLOR: fb_logo.needs_directpalette = 1; fb_logo.needs_cmapreset = 1; break; case FB_VISUAL_PSEUDOCOLOR: fb_logo.needs_cmapreset = 1; break; } } return fb_prepare_extra_logos(info, fb_logo.logo->height, yres);}到587行都是根据fb_info获取颜色depth 590根据depth获取合适的logo, fb_find_logo看起来很简单,就是根据depth找到适合的logo 606~612 是根据获得的logo类型,计算logo的depth, 这可能和fb_find_logo传入的depth不一样 int fb_show_logo(struct fb_info *info, int rotate){ int y; y = fb_show_logo_line(info, rotate, fb_logo.logo, 0, num_online_cpus()); y = fb_show_extra_logos(info, y, rotate); return y;}先显示logo,fb_show_logo_line会返回logo占用的vertical height 然后在logo下显示extra logo, 传入的@y就是logo 的height 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; 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; 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;}一般来说read write函数都没什么可分析的,read无非就是读取设备文件的一段数据, 对于framebuffer来说,这些数据就保存在虚拟地址info->screen_base,info->screen_base是 framebuffer mem的虚拟地址,info->fix.smem_start是framebuffer mem的物理地址,正常来说,驱动都是访问info->screen_base。 711~712 framebuffer驱动可以实现特定的read函数,也可以使用通用的实现 read的主体很简单就是通过fb_readl和fb_readb来读取info->screen_base的内容,copy到参数@buf中去 int fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var){ struct fb_fix_screeninfo *fix = &info->fix; unsigned int yres = info->var.yres; int err = 0; if (var->yoffset > 0) { if (var->vmode & FB_VMODE_YWRAP) { if (!fix->ywrapstep || (var->yoffset % fix->ywrapstep)) err = -EINVAL; else yres = 0; } else if (!fix->ypanstep || (var->yoffset % fix->ypanstep)) err = -EINVAL; } if (var->xoffset > 0 && (!fix->xpanstep || (var->xoffset % fix->xpanstep))) err = -EINVAL; if (err || !info->fbops->fb_pan_display || var->yoffset > info->var.yres_virtual - yres || var->xoffset > info->var.xres_virtual - info->var.xres) return -EINVAL; if ((err = info->fbops->fb_pan_display(var, info))) return err; info->var.xoffset = var->xoffset; info->var.yoffset = var->yoffset; if (var->vmode & FB_VMODE_YWRAP) info->var.vmode |= FB_VMODE_YWRAP; else info->var.vmode &= ~FB_VMODE_YWRAP; return 0;}这个函数是FBIOPAN_DISPLAY的实现,关于FBIOPAN_DISPLAY的用途, linux kernel对这个定义也非常模糊,网上的说法也是很不确定。我的看法是这个函数用到了var参数的xoffser和yoffset,通过这两个参数可以 实现屏幕内容的平滑移动。 这个调用在Android平台上还有个很重要的作用,UI刷屏就是通过FBIOPAN_DISPLAY实现的,可以实现双buffer的切换,防止tear-drop效果。 int fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var){ int flags = info->flags; int ret = 0; if (var->activate & FB_ACTIVATE_INV_MODE) { struct fb_videomode mode1, mode2; fb_var_to_videomode(&mode1, var); fb_var_to_videomode(&mode2, &info->var); /* make sure we don't delete the videomode of current var */ ret = fb_mode_is_equal(&mode1, &mode2); if (!ret) { struct fb_event event; event.info = info; event.data = &mode1; ret = fb_notifier_call_chain(FB_EVENT_MODE_DELETE, &event); } if (!ret) fb_delete_videomode(&mode1, &info->modelist); ret = (ret) ? -EINVAL : 0; goto done; } if ((var->activate & FB_ACTIVATE_FORCE) || memcmp(&info->var, var, sizeof(struct fb_var_screeninfo))) { u32 activate = var->activate; if (!info->fbops->fb_check_var) { *var = info->var; goto done; } ret = info->fbops->fb_check_var(var, info); if (ret) goto done; if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { struct fb_var_screeninfo old_var; struct fb_videomode mode; if (info->fbops->fb_get_caps) { ret = fb_check_caps(info, var, activate); if (ret) goto done; } old_var = info->var; info->var = *var; if (info->fbops->fb_set_par) { ret = info->fbops->fb_set_par(info); if (ret) { info->var = old_var; printk(KERN_WARNING "detected " "fb_set_par error, " "error code: %d\n", ret); goto done; } } fb_pan_display(info, &info->var); fb_set_cmap(&info->cmap, info); fb_var_to_videomode(&mode, &info->var); if (info->modelist.prev && info->modelist.next && !list_empty(&info->modelist)) ret = fb_add_videomode(&mode, &info->modelist); if (!ret && (flags & FBINFO_MISC_USEREVENT)) { struct fb_event event; int evnt = (activate & FB_ACTIVATE_ALL) ? FB_EVENT_MODE_CHANGE_ALL : FB_EVENT_MODE_CHANGE; info->flags &= ~FBINFO_MISC_USEREVENT; event.info = info; event.data = &mode; fb_notifier_call_chain(evnt, &event); } } } done: return ret;}这个函数处理两类情况, 第一种从fb_info->modelist中删除@var对应的mode, 922~923转换var和当前fb_info->var 到viewmode 如果@var对应的viewmode不是当前正在使用的viewmode那么调用notifier函数,并从info->modelist中删除所有匹配的viewmode 第二种情况,如果有FB_ACTIVATE_FORCE标记或者新@var不等与fb_info当前的var: fb_info->var 952 一般来说驱动的fb_check_var会check @var参数,并且调整到有效值 957行,如果var->active是FB_ACTIVE_NOW, 那么激活给定的@var 968~972 设置info->var为@var, 并且调用fb_set_par设置新的framebuffer参数,改变操作模式 983 在设置新的framebuffer后需要调用fb_pan_display来更新pan display, fb_pan_display需要特定的framebuffer实现 985~989 把var对应的videomode加入到modelist中去 991~1000 广播framebuffer事件 int fb_blank(struct fb_info *info, int blank){ int ret = -EINVAL; if (blank > FB_BLANK_POWERDOWN) blank = FB_BLANK_POWERDOWN; if (info->fbops->fb_blank) ret = info->fbops->fb_blank(blank, info); if (!ret) { struct fb_event event; event.info = info; event.data = ␣ fb_notifier_call_chain(FB_EVENT_BLANK, &event); } return ret;}这个函数调用info->fbops->fb_blank, @blank指定了blank的类型,包括POWERDOWN, NORMAL HSYNC_SUSPEND, VSYNC_SUSPEND 以及重新点亮display, 对于mxc framebuffer驱动, 就是使能/无效 ipu channel 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; } 对于这个函数没什么可说的了,介绍下每个ioctl命令的含义 FBIOGET_VSCREENINFO: Used to get the variable screen information of the frame buffer FBIOPUT_VSCREENINFO: Used to set variable screen parameters for the frame buffer FBIOGET_FSCREENINFO: Used to get fixiable screen parameters for the frame buffer FBIOPUTCMAP: 设置framebuffer的color map FBIOGETCMAP: 获取framebuffer的color map FBIOPAN_DISPLAY:按照参数var->xoffset 和var->yoffset平移frame buffer中的内容, 可以用在双buffer的切换 FBIOGET_CON2FBMAP和FBIOPUT_CON2FBMAP实在没看明白什么意思 FBIOBLANK:使能或者点亮frame buffer, 参数arg可以是POWERDOWN, NORMAL HSYNC_SUSPEND, VSYNC_SUSPEND UNBLANK 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; } /* frame buffer memory */ start = info->fix.smem_start; len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len); if (off >= len) { /* memory mapped io */ 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) + 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; 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;}man mmap可以知道mmap的作用是映射文件或设备到内存中,因此fb_mmap的作用就是把framebuffer的物理内存映射到进程的虚拟地址空间。 1334 off = vma->vm_pgoff << PAGE_SHIFT; off是这个vm area对应的文件偏移 1346 fix.smem_start是frame buffer的起始物理地址 1367~1368应该是映射为物理地址到vm area中 intregister_framebuffer(struct fb_info *fb_info){ int i; struct fb_event event; struct fb_videomode mode; if (num_registered_fb == FB_MAX) return -ENXIO; if (fb_check_foreignness(fb_info)) return -ENOSYS; remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id, fb_is_primary_device(fb_info)); num_registered_fb++; for (i = 0 ; i < FB_MAX; i++) if (!registered_fb[i]) break; fb_info->node = i; mutex_init(&fb_info->lock); mutex_init(&fb_info->mm_lock); fb_info->dev = device_create(fb_class, fb_info->device, MKDEV(FB_MAJOR, i), NULL, "fb%d", i); if (IS_ERR(fb_info->dev)) { /* Not fatal */ printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev)); fb_info->dev = NULL; } else fb_init_device(fb_info); if (fb_info->pixmap.addr == NULL) { fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); if (fb_info->pixmap.addr) { fb_info->pixmap.size = FBPIXMAPSIZE; fb_info->pixmap.buf_align = 1; fb_info->pixmap.scan_align = 1; fb_info->pixmap.access_align = 32; fb_info->pixmap.flags = FB_PIXMAP_DEFAULT; } } fb_info->pixmap.offset = 0; if (!fb_info->pixmap.blit_x) fb_info->pixmap.blit_x = ~(u32)0; if (!fb_info->pixmap.blit_y) fb_info->pixmap.blit_y = ~(u32)0; if (!fb_info->modelist.prev || !fb_info->modelist.next) INIT_LIST_HEAD(&fb_info->modelist); fb_var_to_videomode(&mode, &fb_info->var); fb_add_videomode(&mode, &fb_info->modelist); registered_fb[i] = fb_info; event.info = fb_info; if (!lock_fb_info(fb_info)) return -ENODEV; fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event); unlock_fb_info(fb_info); return 0;}这个函数为framebuffer 驱动提供了注册一个framebuffer device的接口,该函数会把@fb_info加到registered_fb中去 1570 ~1571 为frame buffer设备创建class device name 1577 fb_init_device创建frame buffer的attr文件 pixmap不知道什么意思 1600~1601 转换fb_info->var为 videomode,然后把videomode加入到modelist中 1602 把@fb_info加到registered_fb数组中 int unregister_framebuffer(struct fb_info *fb_info){ struct fb_event event; int i, ret = 0; i = fb_info->node; if (!registered_fb[i]) { ret = -EINVAL; goto done; } if (!lock_fb_info(fb_info)) return -ENODEV; event.info = fb_info; ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event); unlock_fb_info(fb_info); if (ret) { ret = -EINVAL; goto done; } if (fb_info->pixmap.addr && (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) kfree(fb_info->pixmap.addr); fb_destroy_modelist(&fb_info->modelist); registered_fb[i]=NULL; num_registered_fb--; fb_cleanup_device(fb_info); device_destroy(fb_class, MKDEV(FB_MAJOR, i)); event.info = fb_info; fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); /* this may free fb info */ if (fb_info->fbops->fb_destroy) fb_info->fbops->fb_destroy(fb_info);done: return ret;} unregister_framebuffer实在没什么可看的了 int fb_new_modelist(struct fb_info *info){ struct fb_event event; struct fb_var_screeninfo var = info->var; struct list_head *pos, *n; struct fb_modelist *modelist; struct fb_videomode *m, mode; int err = 1; list_for_each_safe(pos, n, &info->modelist) { modelist = list_entry(pos, struct fb_modelist, list); m = &modelist->mode; fb_videomode_to_var(&var, m); var.activate = FB_ACTIVATE_TEST; err = fb_set_var(info, &var); fb_var_to_videomode(&mode, &var); if (err || !fb_mode_is_equal(m, &mode)) { list_del(pos); kfree(pos); } } err = 1; if (!list_empty(&info->modelist)) { if (!lock_fb_info(info)) return -ENODEV; event.info = info; err = fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event); unlock_fb_info(info); } return err;}测试info->modelist中的每一个mode,从这个modelist中删除无效的mode节点 int fb_get_options(char *name, char **option){ char *opt, *options = NULL; int opt_len, retval = 0; int name_len = strlen(name), i; if (name_len && ofonly && strncmp(name, "offb", 4)) retval = 1; if (name_len && !retval) { for (i = 0; i < FB_MAX; i++) { if (video_options[i] == NULL) continue; opt_len = strlen(video_options[i]); if (!opt_len) continue; opt = video_options[i]; if (!strncmp(name, opt, name_len) && opt[name_len] == ':') options = opt + name_len + 1; } } if (options && !strncmp(options, "off", 3)) retval = 1; if (option) *option = options; return retval;} 从kernel cmd 参数中提取framebuffer相关的选项
阅读全文
0 0
- fbmem.c 分析1
- fbmem.c分析
- Frame buffer分析 - fbmem.c
- Frame buffer分析 - fbmem.c
- Frame buffer分析 - fbmem.c
- Frame buffer分析 - fbmem.c
- fbmem分析
- 读fbmem.c。。。
- linux帧缓冲设备驱动的上层建筑Fbmem.c文件
- linux中LCD设备驱动(6)——Fbmem.c文件
- C、C++、java分析
- C++/C源码分析
- 词法分析程序 C++/C
- C 模拟词法分析
- 汇编分析-改c
- ks_sys.c 文件分析
- C中的Float分析
- mpegts.c文件分析
- 关闭软键盘
- deep learning 深度学习
- 虚拟中机配置CentOS系统IP地址
- 上拉加载更多数据
- 写写 我经历过的前端优化吧
- fbmem.c分析
- jmeter测试dubbo接口
- ligerui初次体验
- IntelliJ IDEA 源值1.5已过时,将在未来所有版本中删除
- Python装饰器
- 一些加载效果备份
- 谷歌论文阅读:Building High-level Features Using Large Scale Unsupervised Learning
- JEESZ分布式框架开发环境部署
- PHP 获取二维数组中某个key的集合