• Frame buffer分析


    转自:http://www.cnblogs.com/armlinux/archive/2012/03/01/2396753.html

    45 struct fb_info *registered_fb[FB_MAX] __read_mostly;

    这个是全局的变量,通过这个全局变量,在系统内可以随时获取需要的fb_info,具体的获取方法是

    通过比对 registered_fb[i]->fix.id来确定需要的fb_info, 示例代码如下

        for (i = 0; i < num_registered_fb; i++) {
            char *idstr = registered_fb[i]->fix.id;
            if (strcmp(idstr, "DISP3 FG") == 0) {
                fbi = registered_fb[i];
                break;
            }
        }


      63 int fb_get_color_depth(struct fb_var_screeninfo *var,
      64                struct fb_fix_screeninfo *fix)
      65 {  
      66     int depth = 0;
      67
      68     if (fix->visual == FB_VISUAL_MONO01 ||
      69         fix->visual == FB_VISUAL_MONO10)
      70         depth = 1;
      71     else {
      72         if (var->green.length == var->blue.length &&
      73             var->green.length == var->red.length &&
      74             var->green.offset == var->blue.offset &&
      75             var->green.offset == var->red.offset)
      76             depth = var->green.length;
      77         else
      78             depth = var->green.length + var->red.length +
      79                 var->blue.length;
      80     }
      81
      82     return depth;
      83 }

    该函数获取颜色深度,很简单啊,对于单色深度为1,否则深度为red blue green三个分量的和


    129 char* fb_get_buffer_offset(struct fb_info *info, struct fb_pixmap *buf, u32 size)
    130 {
    131     u32 align = buf->buf_align - 1, offset;
    132     char *addr = buf->addr;
    133
    134     /* If IO mapped, we need to sync before access, no sharing of
    135      * the pixmap is done
    136      */
    137     if (buf->flags & FB_PIXMAP_IO) {
    138         if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC))
    139             info->fbops->fb_sync(info);
    140         return addr;
    141     }
    142
    143     /* See if we fit in the remaining pixmap space */
    144     offset = buf->offset + align;
    145     offset &= ~align;
    146     if (offset + size > buf->size) {
    147         /* We do not fit. In order to be able to re-use the buffer,
    148          * we must ensure no asynchronous DMA'ing or whatever operation
    149          * is in progress, we sync for that.
    150          */
    151         if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC))
    152             info->fbops->fb_sync(info);
    153         offset = 0;
    154     }
    155     buf->offset = offset + size;
    156     addr += offset;
    157
    158     return addr;
    159 }


    这个函数看似简单,就是获取@buf中符合@size大小的空闲位置

    146行 如果剩余空间小于需要的大小,那么fb_sync后就可以使用@buffer的所有空间

    这个函数看起来总是怪挂的,因为fb_sync的参数没有涉及到@buf, 所以fb_sync跟@buf有毛关系呀

    虽然调用fb_get_buffer_offset时的@info和@buf的关系是@info->pixmap == @buf,那为毛不只传一个参数?


    168 static void fb_set_logocmap(struct fb_info *info,
    169                    const struct linux_logo *logo)
    170 {
    171     struct fb_cmap palette_cmap;
    172     u16 palette_green[16];
    173     u16 palette_blue[16];
    174     u16 palette_red[16];
    175     int i, j, n;
    176     const unsigned char *clut = logo->clut;
    177
    178     palette_cmap.start = 0;
    179     palette_cmap.len = 16;
    180     palette_cmap.red = palette_red;
    181     palette_cmap.green = palette_green;
    182     palette_cmap.blue = palette_blue;#define FB_VISUAL_MONO01        0   /* Monochr. 1=Black 0=White */
    183     palette_cmap.transp = NULL;
    184
    185     for (i = 0; i < logo->clutsize; i += n) {
    186         n = logo->clutsize - i;
    187         /* palette_cmap provides space for only 16 colors at once */
    188         if (n > 16)
    189             n = 16;
    190         palette_cmap.start = 32 + i;
    191         palette_cmap.len = n;
    192         for (j = 0; j < n; ++j) {
    193             palette_cmap.red[j] = clut[0] << 8 | clut[0];
    194             palette_cmap.green[j] = clut[1] << 8 | clut[1];
    195             palette_cmap.blue[j] = clut[2] << 8 | clut[2];
    196             clut += 3;
    197         }
    198         fb_set_cmap(&palette_cmap, info);
    199     }
    200 }


    在介绍这个函数前,先了解下调色板

    在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个颜色索引

    190         palette_cmap.start = 32 + i;

    这里加了个32,很讨厌这种数字写法,这里之所以选32是因为CLUT224这种格式的index值从32直到255,即我们在linux_logo->data中只能找到0值,以及32~255之间的值

    198         fb_set_cmap(&palette_cmap, info);

    这个函数会设置硬件调色板以及info->cmap


    202 static void  fb_set_logo_truepalette(struct fb_info *info,
    203                         const struct linux_logo *logo,
    204                         u32 *palette)
    205 {
    206     static const unsigned char mask[] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff };
    207     unsigned char redmask, greenmask, bluemask;
    208     int redshift, greenshift, blueshift;
    209     int i;
    210     const unsigned char *clut = logo->clut;
    211
    212     /*
    213      * We have to create a temporary palette since console palette is only
    214      * 16 colors long.
    215      */
    216     /* Bug: Doesn't obey msb_right ... (who needs that?) */
    217     redmask   = mask[info->var.red.length   < 8 ? info->var.red.length   : 8];
    218     greenmask = mask[info->var.green.length < 8 ? info->var.green.length : 8];
    219     bluemask  = mask[info->var.blue.length  < 8 ? info->var.blue.length  : 8];
    220     redshift   = info->var.red.offset   - (8 - info->var.red.length);
    221     greenshift = info->var.green.offset - (8 - info->var.green.length);
    222     blueshift  = info->var.blue.offset  - (8 - info->var.blue.length);
    223
    224     for ( i = 0; i < logo->clutsize; i++) {
    225         palette[i+32] = (safe_shift((clut[0] & redmask), redshift) |
    226                  safe_shift((clut[1] & greenmask), greenshift) |
    227                  safe_shift((clut[2] & bluemask), blueshift));
    228         clut += 3;
    229     }
    230 }

    这个函数为FB_VISUAL_PSEUDOCOLOR彩色模式的logo生成一个调色板,从32开始是因为CLUT224只支持32~255范围内的index值


    232 static void fb_set_logo_directpalette(struct fb_info *info,
    233                          const struct linux_logo *logo,
    234                          u32 *palette)
    235 {
    236     int redshift, greenshift, blueshift;
    237     int i;
    238
    239     redshift = info->var.red.offset;
    240     greenshift = info->var.green.offset;
    241     blueshift = info->var.blue.offset;
    242
    243     for (i = 32; i < 32 + logo->clutsize; i++)
    244         palette[i] = i << redshift | i << greenshift | i << blueshift;
    245 }

    为FB_VISUAL_DIRECTCOLOR彩色模式生成一个调色板,只需生成32 ~ clutsize


    247 static void fb_set_logo(struct fb_info *info,
    248                    const struct linux_logo *logo, u8 *dst,
    249                    int depth)
    250 {
    251     int i, j, k;
    252     const u8 *src = logo->data;
    253     u8 xor = (info->fix.visual == FB_VISUAL_MONO01) ? 0xff : 0;
    254     u8 fg = 1, d;
    255
    256     switch (fb_get_color_depth(&info->var, &info->fix)) {
    257     case 1:
    258         fg = 1;
    259         break;
    260     case 2:
    261         fg = 3;
    262         break;
    263     default:
    264         fg = 7;
    265         break;
    266     }
    267
    268     if (info->fix.visual == FB_VISUAL_MONO01 ||
    269         info->fix.visual == FB_VISUAL_MONO10)
    270         fg = ~((u8) (0xfff << info->var.green.length));
    271
    272     switch (depth) {
    273     case 4:
    274         for (i = 0; i < logo->height; i++)
    275             for (j = 0; j < logo->width; src++) {
    276                 *dst++ = *src >> 4;
    277                 j++;
    278                 if (j < logo->width) {
    279                     *dst++ = *src & 0x0f;
    280                     j++;
    281                 }
    282             }
    283         break;
    284     case 1:
    285         for (i = 0; i < logo->height; i++) {
    286             for (j = 0; j < logo->width; src++) {
    287                 d = *src ^ xor;
    288                 for (k = 7; k >= 0; k--) {
    289                     *dst++ = ((d >> k) & 1) ? fg : 0;
    290                     j++;
    291                 }
    292             }
    293         }
    294         break;
    295     }
    296 }
    297

    linux_logo->data中保存的是logo的data数据,如果对于mono或者16 色的数据来说,linxu_logo->data内的每个字节保存的是多个像素点的数据,fb_set_logo这个函数根据颜色深度把linux_logo->data的数据转换到@dst中,@dst中的每个字节,代表这一个像素索引

    参见源码注视就很好理解为什么要做转换了

    298 /*
    299  * Three (3) kinds of logo maps exist.  linux_logo_clut224 (>16 colors),
    300  * linux_logo_vga16 (16 colors) and linux_logo_mono (2 colors).  Depending on
    301  * the visual format and color depth of the framebuffer, the DAC, the
    302  * pseudo_palette, and the logo data will be adjusted accordingly.
    303  *
    304  * Case 1 - linux_logo_clut224:
    305  * Color exceeds the number of console colors (16), thus we set the hardware DAC
    306  * using fb_set_cmap() appropriately.  The "needs_cmapreset"  flag will be set.
    307  *
    308  * For visuals that require color info from the pseudo_palette, we also construct
    309  * one for temporary use. The "needs_directpalette" or "needs_truepalette" flags
      310  * will be set.
    311  *
    312  * Case 2 - linux_logo_vga16:
    313  * The number of colors just matches the console colors, thus there is no need
    314  * to set the DAC or the pseudo_palette.  However, the bitmap is packed, ie,
    315  * each byte contains color information for two pixels (upper and lower nibble).
    316  * To be consistent with fb_imageblit() usage, we therefore separate the two
    317  * nibbles into separate bytes. The "depth" flag will be set to 4.
    318  *
    319  * Case 3 - linux_logo_mono:
    320  * This is similar with Case 2.  Each byte contains information for 8 pixels.
    321  * We isolate each bit and expand each into a byte. The "depth" flag will
    322  * be set to 1.

    323  */
    324 static struct logo_data {
    325     int depth;
    326     int needs_directpalette;
    327     int needs_truepalette;
    328     int needs_cmapreset;
    329     const struct linux_logo *logo;
    330 } fb_logo __read_mostly;
    @depth是logo的深度

    @logo是linux_logo数据


    332 static void fb_rotate_logo_ud(const u8 *in, u8 *out, u32 width, u32 height)
    333 {
    334     u32 size = width * height, i;
    335
    336     out += size - 1;
    337
    338     for (i = size; i--; )
    339         *out-- = *in++;
    340 }
    341
    342 static void fb_rotate_logo_cw(const u8 *in, u8 *out, u32 width, u32 height)
    343 {
    344     int i, j, h = height - 1;
    345
    346     for (i = 0; i < height; i++)
    347         for (j = 0; j < width; j++)
    348                 out[height * j + h - i] = *in++;
    349 }
    350
    351 static void fb_rotate_logo_ccw(const u8 *in, u8 *out, u32 width, u32 height)
    352 {
    353     int i, j, w = width - 1;
    354
    355     for (i = 0; i < height; i++)
    356         for (j = 0; j < width; j++)
    357             out[height * (w - j) + i] = *in++;
    358 }

    这几个函数再此验证了代码的恶心程度,没人知道ud, cw ccw是什么含义


    393 static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,
    394                 int rotate, unsigned int num)
    395 {
    396     unsigned int x;
    397
    398     if (rotate == FB_ROTATE_UR) {
    399         for (x = 0;
    400              x < num && image->dx + image->width <= info->var.xres;
    401              x++) {
    402             info->fbops->fb_imageblit(info, image);
    403             image->dx += image->width + 8;
    404         }
    405     } else if (rotate == FB_ROTATE_UD) {
    406         for (x = 0; x < num && image->dx >= 0; x++) {
    407             info->fbops->fb_imageblit(info, image);
    408             image->dx -= image->width + 8;
    409         }
    410     } else if (rotate == FB_ROTATE_CW) {
    411         for (x = 0;
    412              x < num && image->dy + image->height <= info->var.yres;
    413              x++) {
    414             info->fbops->fb_imageblit(info, image);
    415             image->dy += image->height + 8;
    416         }
    417     } else if (rotate == FB_ROTATE_CCW) {
    418         for (x = 0; x < num && image->dy >= 0; x++) {
    419             info->fbops->fb_imageblit(info, image);
    420             image->dy -= image->height + 8;
    421         }
    422     }
    423 }

    显示@image内的logo数据, @rotate是旋转方式, @num没看懂社么意思阿


    425 static int fb_show_logo_line(struct fb_info *info, int rotate,
    426                  const struct linux_logo *logo, int y,
    427                  unsigned int n)
    428 {
    429     u32 *palette = NULL, *saved_pseudo_palette = NULL;
    430     unsigned char *logo_new = NULL, *logo_rotate = NULL;
    431     struct fb_image image;
    432
    433     /* Return if the frame buffer is not mapped or suspended */
    434     if (logo == NULL || info->state != FBINFO_STATE_RUNNING ||
    435         info->flags & FBINFO_MODULE)
    436         return 0;
    437
    438     image.depth = 8;
    439     image.data = logo->data;
    440
    441     if (fb_logo.needs_cmapreset)
    442         fb_set_logocmap(info, logo);
    443
    444     if (fb_logo.needs_truepalette ||
    445         fb_logo.needs_directpalette) {
    446         palette = kmalloc(256 * 4, GFP_KERNEL);
    447         if (palette == NULL)
    448             return 0;
    449
    450         if (fb_logo.needs_truepalette)
    451             fb_set_logo_truepalette(info, logo, palette);
    452         else
    453             fb_set_logo_directpalette(info, logo, palette);
    454
    455         saved_pseudo_palette = info->pseudo_palette;
    456         info->pseudo_palette = palette;
    457     }
    458
    459     if (fb_logo.depth <= 4) {
    460         logo_new = kmalloc(logo->width * logo->height, GFP_KERNEL);
    461         if (logo_new == NULL) {
    462             kfree(palette);
    463             if (saved_pseudo_palette)
    464                 info->pseudo_palette = saved_pseudo_palette;
    465             return 0;
    466         }
    467         image.data = logo_new;
    468         fb_set_logo(info, logo, logo_new, fb_logo.depth);
    469     }

    470
    471     image.dx = 0;
    472     image.dy = y;
    473     image.width = logo->width;
    474     image.height = logo->height;
    475
    476     if (rotate) {
    477         logo_rotate = kmalloc(logo->width *
    478                       logo->height, GFP_KERNEL);
    479         if (logo_rotate)
    480             fb_rotate_logo(info, logo_rotate, &image, rotate);
    481     }
    482
    483     fb_do_show_logo(info, &image, rotate, n);
    484
    485     kfree(palette);
    486     if (saved_pseudo_palette != NULL)
    487         info->pseudo_palette = saved_pseudo_palette;
    488     kfree(logo_new);
    489     kfree(logo_rotate);
    490     return logo->height;
    491 }

    我无语了,这代码写的,为毛有个@y参数呀


    503 void fb_append_extra_logo(const struct linux_logo *logo, unsigned int n)
    504 {
    505     if (!n || fb_logo_ex_num == FB_LOGO_EX_NUM_MAX)
    506         return;
    507
    508     fb_logo_ex[fb_logo_ex_num].logo = logo;
    509     fb_logo_ex[fb_logo_ex_num].n = n;
    510     fb_logo_ex_num++;
    511 }
    这个函数把给定的logo设置到fb_logo_ex这个全局extend logo数组中, @n作用未知


    513 static int fb_prepare_extra_logos(struct fb_info *info, unsigned int height,
    514                   unsigned int yres)
    515 {
    516     unsigned int i;
    517
    518     /* FIXME: logo_ex supports only truecolor fb. */
    519     if (info->fix.visual != FB_VISUAL_TRUECOLOR)
    520         fb_logo_ex_num = 0;
    521
    522     for (i = 0; i < fb_logo_ex_num; i++) {
    523         if (fb_logo_ex[i].logo->type != fb_logo.logo->type) {
    524             fb_logo_ex[i].logo = NULL;
    525             continue;
    526         }
    527         height += fb_logo_ex[i].logo->height;
    528         if (height > yres) {
    529             height -= fb_logo_ex[i].logo->height;
    530             fb_logo_ex_num = i;
    531             break;
    532         }
    533     }
    534     return height;
    535 }

    这段代码写的相当不好,单独引入的fb_logo_ex_num极其恶劣

    这段代码的意思也就是计算height,以及fb_logo_ex_num

    height是logo和有效extend logo的高度和,fb_log_ex_num是有效extend logo的最大索引



    537 static int fb_show_extra_logos(struct fb_info *info, int y, int rotate)
    538 {
    539     unsigned int i;
    540
    541     for (i = 0; i < fb_logo_ex_num; i++)
    542         y += fb_show_logo_line(info, rotate,
    543                        fb_logo_ex[i].logo, y, fb_logo_ex[i].n);
    544
    545     return y;
    546 }

    该函数显示保存在fb_logo_ex中的extend logo, @y表示这个extend logo要在屏幕显示的位置


    565 int fb_prepare_logo(struct fb_info *info, int rotate)
    566 {
    567     int depth = fb_get_color_depth(&info->var, &info->fix);
    568     unsigned int yres;
    569
    570     memset(&fb_logo, 0, sizeof(struct logo_data));
    571
    572     if (info->flags & FBINFO_MISC_TILEBLITTING ||
    573         info->flags & FBINFO_MODULE)
    574         return 0;
    575
    576     if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
    577         depth = info->var.blue.length;
    578         if (info->var.red.length < depth)
    579             depth = info->var.red.length;
    580         if (info->var.green.length < depth)
    581             depth = info->var.green.length;
    582     }
    583
    584     if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR && depth > 4) {
    585         /* assume console colormap */
    586         depth = 4;
    587     }
    588
    589     /* Return if no suitable logo was found */
    590     fb_logo.logo = fb_find_logo(depth);
    591
    592     if (!fb_logo.logo) {
    593         return 0;
    594     }
    595
    596     if (rotate == FB_ROTATE_UR || rotate == FB_ROTATE_UD)
    597         yres = info->var.yres;
    598     else
    599         yres = info->var.xres;
    600
    601     if (fb_logo.logo->height > yres) {
    602         fb_logo.logo = NULL;
    603         return 0;
    604     }
    605
    606     /* What depth we asked for might be different from what we get */
    607     if (fb_logo.logo->type == LINUX_LOGO_CLUT224)
    608         fb_logo.depth = 8;
    609     else if (fb_logo.logo->type == LINUX_LOGO_VGA16)
    610         fb_logo.depth = 4;
    611     else
    612         fb_logo.depth = 1;
    613
    614
    615     if (fb_logo.depth > 4 && depth > 4) {
    616         switch (info->fix.visual) {
    617         case FB_VISUAL_TRUECOLOR:
    618             fb_logo.needs_truepalette = 1;
    619             break;
    620         case FB_VISUAL_DIRECTCOLOR:
    621             fb_logo.needs_directpalette = 1;
    622             fb_logo.needs_cmapreset = 1;
    623             break;
    624         case FB_VISUAL_PSEUDOCOLOR:
    625             fb_logo.needs_cmapreset = 1;
    626             break;
    627         }
    628     }
    629
    630     return fb_prepare_extra_logos(info, fb_logo.logo->height, yres);
    631 }

    到587行都是根据fb_info获取颜色depth

    590根据depth获取合适的logo,  fb_find_logo看起来很简单,就是根据depth找到适合的logo

    606~612 是根据获得的logo类型,计算logo的depth, 这可能和fb_find_logo传入的depth不一样


    633 int fb_show_logo(struct fb_info *info, int rotate)
    634 {
    635     int y;
    636
    637     y = fb_show_logo_line(info, rotate, fb_logo.logo, 0,
    638                   num_online_cpus());
    639     y = fb_show_extra_logos(info, y, rotate);
    640
    641     return y;
    642 }

    先显示logo,fb_show_logo_line会返回logo占用的vertical height

    然后在logo下显示extra logo, 传入的@y就是logo 的height


    693 static ssize_t
    694 fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
    695 {
    696     unsigned long p = *ppos;
    697     struct inode *inode = file->f_path.dentry->d_inode;
    698     int fbidx = iminor(inode);
    699     struct fb_info *info = registered_fb[fbidx];
    700     u32 *buffer, *dst;
    701     u32 __iomem *src;
    702     int c, i, cnt = 0, err = 0;
    703     unsigned long total_size;
    704
    705     if (!info || ! info->screen_base)
    706         return -ENODEV;
    707
    708     if (info->state != FBINFO_STATE_RUNNING)
    709         return -EPERM;
    710
    711     if (info->fbops->fb_read)
    712         return info->fbops->fb_read(info, buf, count, ppos);
    713
    714     total_size = info->screen_size;
    715
    716     if (total_size == 0)
    717         total_size = info->fix.smem_len;
    718
    719     if (p >= total_size)
    720         return 0;
    721
    722     if (count >= total_size)
    723         count = total_size;
    724
    725     if (count + p > total_size)
    726         count = total_size - p;
    727
    728     buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
    729              GFP_KERNEL);
    730     if (!buffer)
    731         return -ENOMEM;
    732
    733     src = (u32 __iomem *) (info->screen_base + p);
    734
    735     if (info->fbops->fb_sync)
    736         info->fbops->fb_sync(info);
    737
    738     while (count) {
    739         c  = (count > PAGE_SIZE) ? PAGE_SIZE : count;
    740         dst = buffer;
    741         for (i = c >> 2; i--; )
    742             *dst++ = fb_readl(src++);
    743         if (c & 3) {
    744             u8 *dst8 = (u8 *) dst;
    745             u8 __iomem *src8 = (u8 __iomem *) src;
    746
    747             for (i = c & 3; i--;)
    748                 *dst8++ = fb_readb(src8++);
    749
    750             src = (u32 __iomem *) src8;
    751         }
    752
    753         if (copy_to_user(buf, buffer, c)) {
    754             err = -EFAULT;
    755             break;
    756         }
    757         *ppos += c;
    758         buf += c;
    759         cnt += c;
    760         count -= c;
    761     }
    762
    763     kfree(buffer);
    764
    765     return (err) ? err : cnt;
    766 }

    一般来说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中去


    852 int
    853 fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var)
    854 {
    855     struct fb_fix_screeninfo *fix = &info->fix;
    856     unsigned int yres = info->var.yres;
    857     int err = 0;
    858
    859     if (var->yoffset > 0) {
    860         if (var->vmode & FB_VMODE_YWRAP) {
    861             if (!fix->ywrapstep || (var->yoffset % fix->ywrapstep))
    862                 err = -EINVAL;
    863             else
    864                 yres = 0;
    865         } else if (!fix->ypanstep || (var->yoffset % fix->ypanstep))
    866             err = -EINVAL;
    867     }
    868
    869     if (var->xoffset > 0 && (!fix->xpanstep ||
    870                  (var->xoffset % fix->xpanstep)))
    871         err = -EINVAL;
    872
    873     if (err || !info->fbops->fb_pan_display ||
    874         var->yoffset > info->var.yres_virtual - yres ||
    875         var->xoffset > info->var.xres_virtual - info->var.xres)
    876         return -EINVAL;
    877
    878     if ((err = info->fbops->fb_pan_display(var, info)))
    879         return err;
    880
    881     info->var.xoffset = var->xoffset;
    882     info->var.yoffset = var->yoffset;
    883     if (var->vmode & FB_VMODE_YWRAP)
    884         info->var.vmode |= FB_VMODE_YWRAP;
    885     else
    886         info->var.vmode &= ~FB_VMODE_YWRAP;
    887     return 0;
    888 }
    这个函数是FBIOPAN_DISPLAY的实现,关于FBIOPAN_DISPLAY的用途, linux kernel对这个定义也非常模糊,网上的说法也是很不确定。我的看法是这个函数用到了var参数的xoffser和yoffset,通过这两个参数可以实现屏幕内容的平滑移动。

    这个调用在Android平台上还有个很重要的作用,UI刷屏就是通过FBIOPAN_DISPLAY实现的,可以实现双buffer的切换,防止tear-drop效果。


    913 int
    914 fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
    915 {
    916     int flags = info->flags;
    917     int ret = 0;
    918
    919     if (var->activate & FB_ACTIVATE_INV_MODE) {
    920         struct fb_videomode mode1, mode2;
    921
    922         fb_var_to_videomode(&mode1, var);
    923         fb_var_to_videomode(&mode2, &info->var);
    924         /* make sure we don't delete the videomode of current var */
    925         ret = fb_mode_is_equal(&mode1, &mode2);
    926
    927         if (!ret) {
    928             struct fb_event event;
    929
    930             event.info = info;
    931             event.data = &mode1;
    932             ret = fb_notifier_call_chain(FB_EVENT_MODE_DELETE, &event);
    933         }
    934
    935         if (!ret)
    936             fb_delete_videomode(&mode1, &info->modelist);
    937
    938
    939         ret = (ret) ? -EINVAL : 0;
    940         goto done;
    941     }
    942
    943     if ((var->activate & FB_ACTIVATE_FORCE) ||
    944         memcmp(&info->var, var, sizeof(struct fb_var_screeninfo))) {
    945         u32 activate = var->activate;
    946
    947         if (!info->fbops->fb_check_var) {
    948             *var = info->var;
    949             goto done;
    950         }
    951
    952         ret = info->fbops->fb_check_var(var, info);
    953
    954         if (ret)
    955             goto done;
    956
    957         if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
    958             struct fb_var_screeninfo old_var;
    959             struct fb_videomode mode;
    960
    961             if (info->fbops->fb_get_caps) {
    962                 ret = fb_check_caps(info, var, activate);
    963
    964                 if (ret)
    965                     goto done;
    966             }
    967
    968             old_var = info->var;
    969             info->var = *var;
    970
    971             if (info->fbops->fb_set_par) {
    972                 ret = info->fbops->fb_set_par(info);
    973
    974                 if (ret) {
    975                     info->var = old_var;
    976                     printk(KERN_WARNING "detected "
    977                         "fb_set_par error, "
    978                         "error code: %d ", ret);
    979                     goto done;
    980                 }
    981             }
    982
    983             fb_pan_display(info, &info->var);
    984             fb_set_cmap(&info->cmap, info);
    985             fb_var_to_videomode(&mode, &info->var);
    986
    987             if (info->modelist.prev && info->modelist.next &&
    988                 !list_empty(&info->modelist))
    989                 ret = fb_add_videomode(&mode, &info->modelist);
    990
    991             if (!ret && (flags & FBINFO_MISC_USEREVENT)) {
    992                 struct fb_event event;
    993                 int evnt = (activate & FB_ACTIVATE_ALL) ?
    994                     FB_EVENT_MODE_CHANGE_ALL :
    995                     FB_EVENT_MODE_CHANGE;
    996
    997                 info->flags &= ~FBINFO_MISC_USEREVENT;
    998                 event.info = info;
    999                 event.data = &mode;
    1000                 fb_notifier_call_chain(evnt, &event);
    1001             }
    1002         }
    1003     }
    1004
    1005  done:
    1006     return ret;
    1007 }

    这个函数处理两类情况,

    第一种从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事件


    1009 int
    1010 fb_blank(struct fb_info *info, int blank)
    1011 {  
    1012     int ret = -EINVAL;
    1013
    1014     if (blank > FB_BLANK_POWERDOWN)
    1015         blank = FB_BLANK_POWERDOWN;
    1016
    1017     if (info->fbops->fb_blank)
    1018         ret = info->fbops->fb_blank(blank, info);
    1019
    1020     if (!ret) {
    1021         struct fb_event event;
    1022
    1023         event.info = info;
    1024         event.data = &blank;
    1025         fb_notifier_call_chain(FB_EVENT_BLANK, &event);
    1026     }
    1027
    1028     return ret;
    1029 }
    这个函数调用info->fbops->fb_blank, @blank指定了blank的类型,包括POWERDOWN, NORMAL HSYNC_SUSPEND, VSYNC_SUSPEND

    以及重新点亮display, 对于mxc framebuffer驱动, 就是使能/无效 ipu channel


    1031 static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
    1032             unsigned long arg)
    1033 {
    1034     struct fb_ops *fb;
    1035     struct fb_var_screeninfo var;
    1036     struct fb_fix_screeninfo fix;
    1037     struct fb_con2fbmap con2fb;
    1038     struct fb_cmap cmap_from;
    1039     struct fb_cmap_user cmap;
    1040     struct fb_event event;
    1041     void __user *argp = (void __user *)arg;
    1042     long ret = 0;
    1043
    1044     switch (cmd) {
    1045     case FBIOGET_VSCREENINFO:
    1046         if (!lock_fb_info(info))
    1047             return -ENODEV;
    1048         var = info->var;
    1049         unlock_fb_info(info);
    1050
    1051         ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
    1052         break;
    1053     case FBIOPUT_VSCREENINFO:
    1054         if (copy_from_user(&var, argp, sizeof(var)))
    1055             return -EFAULT;
    1056         if (!lock_fb_info(info))
    1057             return -ENODEV;
    1058         acquire_console_sem();
    1059         info->flags |= FBINFO_MISC_USEREVENT;
    1060         ret = fb_set_var(info, &var);
    1061         info->flags &= ~FBINFO_MISC_USEREVENT;
    1062         release_console_sem();
    1063         unlock_fb_info(info);
    1064         if (!ret && copy_to_user(argp, &var, sizeof(var)))
    1065             ret = -EFAULT;
    1066         break;
    1067     case FBIOGET_FSCREENINFO:
    1068         if (!lock_fb_info(info))
    1069             return -ENODEV;
    1070         fix = info->fix;
    1071         unlock_fb_info(info);
    1072
    1073         ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
    1074         break;
    1075     case FBIOPUTCMAP:
    1076         if (copy_from_user(&cmap, argp, sizeof(cmap)))
    1077             return -EFAULT;
    1078         ret = fb_set_user_cmap(&cmap, info);
    1079         break;
    1080     case FBIOGETCMAP:
    1081         if (copy_from_user(&cmap, argp, sizeof(cmap)))
    1082             return -EFAULT;
    1083         if (!lock_fb_info(info))
    1084             return -ENODEV;
    1085         cmap_from = info->cmap;
    1086         unlock_fb_info(info);
    1087         ret = fb_cmap_to_user(&cmap_from, &cmap);
    1088         break;
    1089     case FBIOPAN_DISPLAY:
    1090         if (copy_from_user(&var, argp, sizeof(var)))
    1091             return -EFAULT;
    1092         if (!lock_fb_info(info))
    1093             return -ENODEV;
    1094         acquire_console_sem();
    1095         ret = fb_pan_display(info, &var);
    1096         release_console_sem();
    1097         unlock_fb_info(info);
    1098         if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
    1099             return -EFAULT;
    1100         break;
    1101     case FBIO_CURSOR:
    1102         ret = -EINVAL;
    1103         break;
    1104     case FBIOGET_CON2FBMAP:
    1105         if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
    1106             return -EFAULT;
    1107         if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
    1108             return -EINVAL;
    1109         con2fb.framebuffer = -1;
    1110         event.data = &con2fb;
    1111         if (!lock_fb_info(info))
    1112             return -ENODEV;
    1113         event.info = info;
    1114         fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
    1115         unlock_fb_info(info);
    1116         ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
    1117         break;
    1118     case FBIOPUT_CON2FBMAP:
    1119         if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
    1120             return -EFAULT;
    1121         if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
    1122             return -EINVAL;
    1123         if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
    1124             return -EINVAL;
    1125         if (!registered_fb[con2fb.framebuffer])
    1126             request_module("fb%d", con2fb.framebuffer);
    1127         if (!registered_fb[con2fb.framebuffer]) {
    1128             ret = -EINVAL;
    1129             break;
    1130         }
    1131         event.data = &con2fb;
    1132         if (!lock_fb_info(info))
    1133             return -ENODEV;
    1134         event.info = info;
    1135         ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
    1136         unlock_fb_info(info);
    1137         break;
    1138     case FBIOBLANK:
    1139         if (!lock_fb_info(info))
    1140             return -ENODEV;
    1141         acquire_console_sem();
    1142         info->flags |= FBINFO_MISC_USEREVENT;
    1143         ret = fb_blank(info, arg);
    1144         info->flags &= ~FBINFO_MISC_USEREVENT;
    1145         release_console_sem();
    1146         unlock_fb_info(info);
    1147         break;
    1148     default:
    1149         if (!lock_fb_info(info))
    1150             return -ENODEV;
    1151         fb = info->fbops;
    1152         if (fb->fb_ioctl)
    1153             ret = fb->fb_ioctl(info, cmd, arg);
    1154         else
    1155             ret = -ENOTTY;
    1156         unlock_fb_info(info);
    1157     }
    1158     return ret;
    1159 }
    1160

    对于这个函数没什么可说的了,介绍下每个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


    1323 fb_mmap(struct file *file, struct vm_area_struct * vma)
    1324 {
    1325     int fbidx = iminor(file->f_path.dentry->d_inode);
    1326     struct fb_info *info = registered_fb[fbidx];
    1327     struct fb_ops *fb = info->fbops;
    1328     unsigned long off;
    1329     unsigned long start;
    1330     u32 len;
    1331
    1332     if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
    1333         return -EINVAL;
    1334     off = vma->vm_pgoff << PAGE_SHIFT;
    1335     if (!fb)
    1336         return -ENODEV;
    1337     mutex_lock(&info->mm_lock);
    1338     if (fb->fb_mmap) {
    1339         int res;
    1340         res = fb->fb_mmap(info, vma);
    1341         mutex_unlock(&info->mm_lock);
    1342         return res;
    1343     }
    1344
    1345     /* frame buffer memory */
    1346     start = info->fix.smem_start;
    1347     len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
    1348     if (off >= len) {
    1349         /* memory mapped io */
    1350         off -= len;
    1351         if (info->var.accel_flags) {
    1352             mutex_unlock(&info->mm_lock);
    1353             return -EINVAL;
    1354         }
    1355         start = info->fix.mmio_start;
    1356         len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
    1357     }
    1358     mutex_unlock(&info->mm_lock);
    1359     start &= PAGE_MASK;
    1360     if ((vma->vm_end - vma->vm_start + off) > len)
    1361         return -EINVAL;
    1362     off += start;
    1363     vma->vm_pgoff = off >> PAGE_SHIFT;
    1364     /* This is an IO map - tell maydump to skip this VMA */
    1365     vma->vm_flags |= VM_IO | VM_RESERVED;
    1366     fb_pgprotect(file, vma, off);
    1367     if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
    1368                  vma->vm_end - vma->vm_start, vma->vm_page_prot))
    1369         return -EAGAIN;
    1370     return 0;
    1371 }

    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中


    1546 int
    1547 register_framebuffer(struct fb_info *fb_info)
    1548 {
    1549     int i;
    1550     struct fb_event event;
    1551     struct fb_videomode mode;
    1552
    1553     if (num_registered_fb == FB_MAX)
    1554         return -ENXIO;
    1555
    1556     if (fb_check_foreignness(fb_info))
    1557         return -ENOSYS;
    1558
    1559     remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,
    1560                      fb_is_primary_device(fb_info));
    1561
    1562     num_registered_fb++;
    1563     for (i = 0 ; i < FB_MAX; i++)
    1564         if (!registered_fb[i])
    1565             break;
    1566     fb_info->node = i;
    1567     mutex_init(&fb_info->lock);
    1568     mutex_init(&fb_info->mm_lock);
    1569
    1570     fb_info->dev = device_create(fb_class, fb_info->device,
    1571                      MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
    1572     if (IS_ERR(fb_info->dev)) {
    1573         /* Not fatal */
    1574         printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld ", i, PTR_ERR(fb_info->dev));
    1575         fb_info->dev = NULL;
    1576     } else
    1577         fb_init_device(fb_info);
    1578
    1579     if (fb_info->pixmap.addr == NULL) {
    1580         fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
    1581         if (fb_info->pixmap.addr) {
    1582             fb_info->pixmap.size = FBPIXMAPSIZE;
    1583             fb_info->pixmap.buf_align = 1;
    1584             fb_info->pixmap.scan_align = 1;
    1585             fb_info->pixmap.access_align = 32;
    1586             fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
    1587         }
    1588     }
    1589     fb_info->pixmap.offset = 0;
    1590
    1591     if (!fb_info->pixmap.blit_x)
    1592         fb_info->pixmap.blit_x = ~(u32)0;
    1593
    1594     if (!fb_info->pixmap.blit_y)
    1595         fb_info->pixmap.blit_y = ~(u32)0;
    1596
    1597     if (!fb_info->modelist.prev || !fb_info->modelist.next)
    1598         INIT_LIST_HEAD(&fb_info->modelist);
    1599
    1600     fb_var_to_videomode(&mode, &fb_info->var);
    1601     fb_add_videomode(&mode, &fb_info->modelist);
    1602     registered_fb[i] = fb_info;
    1603
    1604     event.info = fb_info;
    1605     if (!lock_fb_info(fb_info))
    1606         return -ENODEV;
    1607     fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
    1608     unlock_fb_info(fb_info);
    1609     return 0;
    1610 }
    这个函数为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数组中


    1630 int
    1631 unregister_framebuffer(struct fb_info *fb_info)
    1632 {
    1633     struct fb_event event;
    1634     int i, ret = 0;
    1635
    1636     i = fb_info->node;
    1637     if (!registered_fb[i]) {
    1638         ret = -EINVAL;
    1639         goto done;
    1640     }
    1641
    1642
    1643     if (!lock_fb_info(fb_info))
    1644         return -ENODEV;
    1645     event.info = fb_info;
    1646     ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event);
    1647     unlock_fb_info(fb_info);
    1648
    1649     if (ret) {
    1650         ret = -EINVAL;
    1651         goto done;
    1652     }
    1653
    1654     if (fb_info->pixmap.addr &&
    1655         (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
    1656         kfree(fb_info->pixmap.addr);
    1657     fb_destroy_modelist(&fb_info->modelist);
    1658     registered_fb[i]=NULL;
    1659     num_registered_fb--;
    1660     fb_cleanup_device(fb_info);
    1661     device_destroy(fb_class, MKDEV(FB_MAJOR, i));
    1662     event.info = fb_info;
    1663     fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
    1664
    1665     /* this may free fb info */
    1666     if (fb_info->fbops->fb_destroy)
    1667         fb_info->fbops->fb_destroy(fb_info);
    1668 done:
    1669     return ret;
    1670 }
    unregister_framebuffer实在没什么可看的了

    1740 int fb_new_modelist(struct fb_info *info)
    1741 {
    1742     struct fb_event event;
    1743     struct fb_var_screeninfo var = info->var;
    1744     struct list_head *pos, *n;
    1745     struct fb_modelist *modelist;
    1746     struct fb_videomode *m, mode;
    1747     int err = 1;
    1748
    1749     list_for_each_safe(pos, n, &info->modelist) {
    1750         modelist = list_entry(pos, struct fb_modelist, list);
    1751         m = &modelist->mode;
    1752         fb_videomode_to_var(&var, m);
    1753         var.activate = FB_ACTIVATE_TEST;
    1754         err = fb_set_var(info, &var);
    1755         fb_var_to_videomode(&mode, &var);
    1756         if (err || !fb_mode_is_equal(m, &mode)) {
    1757             list_del(pos);
    1758             kfree(pos);
    1759         }
    1760     }
    1761
    1762     err = 1;
    1763
    1764     if (!list_empty(&info->modelist)) {
    1765         if (!lock_fb_info(info))
    1766             return -ENODEV;
    1767         event.info = info;
    1768         err = fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);
    1769         unlock_fb_info(info);
    1770     }
    1771
    1772     return err;
    1773 }
    测试info->modelist中的每一个mode,从这个modelist中删除无效的mode节点


    1787 int fb_get_options(char *name, char **option)
    1788 {
    1789     char *opt, *options = NULL;
    1790     int opt_len, retval = 0;
    1791     int name_len = strlen(name), i;
    1792
    1793     if (name_len && ofonly && strncmp(name, "offb", 4))
    1794         retval = 1;
    1795
    1796     if (name_len && !retval) {
    1797         for (i = 0; i < FB_MAX; i++) {
    1798             if (video_options[i] == NULL)
    1799                 continue;
    1800             opt_len = strlen(video_options[i]);
    1801             if (!opt_len)
    1802                 continue;
    1803             opt = video_options[i];
    1804             if (!strncmp(name, opt, name_len) &&
    1805                 opt[name_len] == ':')
    1806                 options = opt + name_len + 1;
    1807         }
    1808     }
    1809     if (options && !strncmp(options, "off", 3))
    1810         retval = 1;
    1811
    1812     if (option)
    1813         *option = options;
    1814
    1815     return retval;
    1816 }

    从kernel cmd 参数中提取framebuffer相关的选项

  • 相关阅读:
    c++笔试题3
    C++笔试题
    C++编程指南续(10-11)
    C++详解(8-9)
    C++编程指南(6-7)
    C++编程指南续(4-5)
    C++编程指南续
    C++的编程指南
    HPSocket介绍与使用
    WinForm中TreeView控件实现鼠标拖动节点(可实现同级节点位置互换,或拖到目标子节点)
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/8670490.html
Copyright © 2020-2023  润新知