fbmem.c分析

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中 




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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值