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相关的选项 

原创粉丝点击