一、fbcon fbcon是帧缓冲区控制台,它在帧缓冲系统初始化时初始化:
static int __init
fbmem_init(void)
{
...
fb_console_init();
return 0;
}
void __init fb_console_init(void)
{
int i;
console_lock();
fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL,
"fbcon");
if (IS_ERR(fbcon_device)) {
printk(KERN_WARNING "Unable to create device "
"for fbcon; errno = %ld\n",
PTR_ERR(fbcon_device));
fbcon_device = NULL;
} else
fbcon_init_device();
for (i = 0; i < MAX_NR_CONSOLES; i++)
con2fb_map[i] = -1;
fbcon_start();
console_unlock();
}
static void fbcon_start(void)
{
WARN_CONSOLE_UNLOCKED();
#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
if (conswitchp != &dummy_con)
deferred_takeover = false;
if (deferred_takeover) {
fbcon_output_nb.notifier_call = fbcon_output_notifier;
dummycon_register_output_notifier(&fbcon_output_nb);
return;
}
#endif
if (num_registered_fb) {
int i;
for_each_registered_fb(i) {
info_idx = i;
break;
}
do_fbcon_takeover(0);
}
}
在帧缓冲硬件设备注册的函数中,调用fbcon_fb_registered来处理
static int do_register_framebuffer(struct fb_info *fb_info)
{
#ifdef CONFIG_GUMSTIX_AM200EPD
{
struct fb_event event;
event.info = fb_info;
fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
}
#endif
if (!lockless_register_fb)
console_lock();
else
atomic_inc(&ignore_console_lock_warning);
lock_fb_info(fb_info);
ret = fbcon_fb_registered(fb_info);
unlock_fb_info(fb_info);
if (!lockless_register_fb)
console_unlock();
else
atomic_dec(&ignore_console_lock_warning);
return ret;
}
int fbcon_fb_registered(struct fb_info *info)
{
int ret = 0, i, idx;
WARN_CONSOLE_UNLOCKED();
idx = info->node;
fbcon_select_primary(info);
if (deferred_takeover) {
pr_info("fbcon: Deferring console take-over\n");
return 0;
}
if (info_idx == -1) {
for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (con2fb_map_boot[i] == idx) {
info_idx = idx;
break;
}
}
if (info_idx != -1)
ret = do_fbcon_takeover(1);
} else {
for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (con2fb_map_boot[i] == idx)
set_con2fb_map(i, idx, 0);
}
}
return ret;
}
假设系统只有一个帧缓冲硬件设备,那么当它被注册时,全局变量info_idx 的值就为-1,当在全局数组con2fb_map_boot中发现有一个控制台的编号与这个帧缓冲硬件设备编号idx对应时,接下来会调用do_fbcon_takeover来设置系统所使用的控制台。
static int do_fbcon_takeover(int show_logo)
{
int err, i;
if (!num_registered_fb)
return -ENODEV;
if (!show_logo)
logo_shown = FBCON_LOGO_DONTSHOW;
for (i = first_fb_vc; i <= last_fb_vc; i++)
con2fb_map[i] = info_idx;
err = do_take_over_console(&fb_con, first_fb_vc, last_fb_vc,
fbcon_is_default);
if (err) {
for (i = first_fb_vc; i <= last_fb_vc; i++)
con2fb_map[i] = -1;
info_idx = -1;
} else {
fbcon_has_console_bind = 1;
}
return err;
}
该函数实际是向系统注册一系列回调函数
static const struct consw fb_con = {
.owner = THIS_MODULE,
.con_startup = fbcon_startup,
.con_init = fbcon_init,
.con_deinit = fbcon_deinit,
.con_clear = fbcon_clear,
.con_putc = fbcon_putc,
.con_putcs = fbcon_putcs,
.con_cursor = fbcon_cursor,
.con_scroll = fbcon_scroll,
.con_switch = fbcon_switch,
.con_blank = fbcon_blank,
.con_font_set = fbcon_set_font,
.con_font_get = fbcon_get_font,
.con_font_default = fbcon_set_def_font,
.con_font_copy = fbcon_copy_font,
.con_set_palette = fbcon_set_palette,
.con_scrolldelta = fbcon_scrolldelta,
.con_set_origin = fbcon_set_origin,
.con_invert_region = fbcon_invert_region,
.con_screen_pos = fbcon_screen_pos,
.con_getxy = fbcon_getxy,
.con_resize = fbcon_resize,
.con_debug_enter = fbcon_debug_enter,
.con_debug_leave = fbcon_debug_leave,
};
我们主要关注fbcon_init和fbcon_switch,系统就是通过它来初始化和切换控制台的。在初始化的过程中,会决定是否准备第一个开机画面的内容,而在切换控制台的过程中,会决定是否显示第一个开机画面的内容。
static void fbcon_init(struct vc_data *vc, int init)
{
struct fb_info *info;
struct fbcon_ops *ops;
struct vc_data **default_mode = vc->vc_display_fg;
struct vc_data *svc = *default_mode;
struct fbcon_display *t, *p = &fb_display[vc->vc_num];
int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256;
int cap, ret;
...
if (logo_shown < 0 && console_loglevel <= CONSOLE_LOGLEVEL_QUIET)
logo_shown = FBCON_LOGO_DONTSHOW;
if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW ||
(info->fix.type == FB_TYPE_TEXT))
logo = 0;
...
if (logo)
fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows);
ops->p = &fb_display[fg_console];
}
当logo的值为1时,调用fbcon_prepare_logo来准备要显示的第一个画面内容
#ifdef MODULE
static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
int cols, int rows, int new_cols, int new_rows)
{
logo_shown = FBCON_LOGO_DONTSHOW;
}
#else
static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
int cols, int rows, int new_cols, int new_rows)
{
struct fbcon_ops *ops = info->fbcon_par;
int cnt, erase = vc->vc_video_erase_char, step;
unsigned short *save = NULL, *r, *q;
int logo_height;
if (info->fbops->owner) {
logo_shown = FBCON_LOGO_DONTSHOW;
return;
}
if (fb_get_color_depth(&info->var, &info->fix) == 1)
erase &= ~0x400;
logo_height = fb_prepare_logo(info, ops->rotate);
logo_lines = DIV_ROUND_UP(logo_height, vc->vc_font.height);
q = (unsigned short *) (vc->vc_origin +
vc->vc_size_row * rows);
step = logo_lines * cols;
for (r = q - logo_lines * cols; r < q; r++)
if (scr_readw(r) != vc->vc_video_erase_char)
break;
if (r != q && new_rows >= rows + logo_lines) {
save = kmalloc(array3_size(logo_lines, new_cols, 2),
GFP_KERNEL);
if (save) {
int i = cols < new_cols ? cols : new_cols;
scr_memsetw(save, erase, logo_lines * new_cols * 2);
r = q - step;
for (cnt = 0; cnt < logo_lines; cnt++, r += i)
scr_memcpyw(save + cnt * new_cols, r, 2 * i);
r = q;
}
}
if (r == q) {
r = q - step - cols;
for (cnt = rows - logo_lines; cnt > 0; cnt--) {
scr_memcpyw(r + step, r, vc->vc_size_row);
r -= cols;
}
if (!save) {
int lines;
if (vc->vc_y + logo_lines >= rows)
lines = rows - vc->vc_y - 1;
else
lines = logo_lines;
vc->vc_y += lines;
vc->vc_pos += lines * vc->vc_size_row;
}
}
scr_memsetw((unsigned short *) vc->vc_origin,
erase,
vc->vc_size_row * logo_lines);
if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
fbcon_clear_margins(vc, 0);
update_screen(vc);
}
if (save) {
q = (unsigned short *) (vc->vc_origin +
vc->vc_size_row *
rows);
scr_memcpyw(q, save, logo_lines * new_cols * 2);
vc->vc_y += logo_lines;
vc->vc_pos += logo_lines * vc->vc_size_row;
kfree(save);
}
if (logo_shown == FBCON_LOGO_DONTSHOW)
return;
if (logo_lines > vc->vc_bottom) {
logo_shown = FBCON_LOGO_CANSHOW;
printk(KERN_INFO
"fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
} else {
logo_shown = FBCON_LOGO_DRAW;
vc->vc_top = logo_lines;
}
}
#endif
通过调用fb_prepare_logo来准备要显示的第一个画面
int fb_prepare_logo(struct fb_info *info, int rotate)
{
int depth = fb_get_color_depth(&info->var, &info->fix);
unsigned int yres;
int height;
memset(&fb_logo, 0, sizeof(struct logo_data));
if (info->flags & FBINFO_MISC_TILEBLITTING ||
info->fbops->owner)
return 0;
fb_logo.logo = fb_find_logo(depth);
}
const struct linux_logo * __ref fb_find_logo(int depth)
{
const struct linux_logo *logo = NULL;
if (nologo || logos_freed)
return NULL;
if (depth >= 1) {
#ifdef CONFIG_LOGO_LINUX_MONO
logo = &logo_linux_mono;
#endif
#ifdef CONFIG_LOGO_SUPERH_MONO
logo = &logo_superh_mono;
#endif
}
if (depth >= 4) {
#ifdef CONFIG_LOGO_LINUX_VGA16
logo = &logo_linux_vga16;
#endif
#ifdef CONFIG_LOGO_SUPERH_VGA16
logo = &logo_superh_vga16;
#endif
}
if (depth >= 8) {
#ifdef CONFIG_LOGO_LINUX_CLUT224
logo = &logo_linux_clut224;
#endif
#ifdef CONFIG_LOGO_DEC_CLUT224
logo = &logo_dec_clut224;
#endif
#ifdef CONFIG_LOGO_MAC_CLUT224
if (MACH_IS_MAC)
logo = &logo_mac_clut224;
#endif
#ifdef CONFIG_LOGO_PARISC_CLUT224
logo = &logo_parisc_clut224;
#endif
#ifdef CONFIG_LOGO_SGI_CLUT224
logo = &logo_sgi_clut224;
#endif
#ifdef CONFIG_LOGO_SUN_CLUT224
logo = &logo_sun_clut224;
#endif
#ifdef CONFIG_LOGO_SUPERH_CLUT224
logo = &logo_superh_clut224;
#endif
}
return logo;
}
这一步执行完成之后,第一个开机画面的图形就保存在fbmem的全局变量fb_logo的成员变量logo中了,接下来系统就会执行切换控制台的操作,系统会执行fbcon_switch函数 static int fbcon_switch(struct vc_data *vc) { … if (logo_shown == FBCON_LOGO_DRAW) {
logo_shown = fg_console;
/* This is protected above by initmem_freed */
fb_show_logo(info, ops->rotate);
update_region(vc,
vc->vc_origin + vc->vc_size_row * vc->vc_top,
vc->vc_size_row * (vc->vc_bottom -
vc->vc_top) / 2);
return 0;
}
return 1;
} 调用fb_show_logo来显示第一个开机界面
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;
}
static int fb_show_logo_line(struct fb_info *info, int rotate,
const struct linux_logo *logo, int y,
unsigned int n)
{
...
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 image.dy + logo->height;
}
该函数将linux_logo的数据转换为fb_image结构体的图形,调用fb_do_show_logo
static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,
int rotate, unsigned int num)
{
unsigned int x;
if (image->width > info->var.xres || image->height > info->var.yres)
return;
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) {
u32 dx = image->dx;
for (x = 0; x < num && image->dx <= dx; 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) {
u32 dy = image->dy;
for (x = 0; x < num && image->dy <= dy; x++) {
info->fbops->fb_imageblit(info, image);
image->dy -= image->height + 8;
}
}
}
回调函数fb_imageblit就是用来在指定的帧缓冲硬件设备渲染图像
struct fb_ops {
struct module *owner;
int (*fb_open)(struct fb_info *info, int user);
int (*fb_release)(struct fb_info *info, int user);
ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
size_t count, loff_t *ppos);
ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
size_t count, loff_t *ppos);
int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
int (*fb_set_par)(struct fb_info *info);
int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp, struct fb_info *info);
int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
int (*fb_blank)(int blank, struct fb_info *info);
int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
int (*fb_sync)(struct fb_info *info);
int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
unsigned long arg);
int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
unsigned long arg);
int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
struct fb_var_screeninfo *var);
void (*fb_destroy)(struct fb_info *info);
int (*fb_debug_enter)(struct fb_info *info);
int (*fb_debug_leave)(struct fb_info *info);
};
|