📄 fbcon.c
字号:
options += 3; if (*options) first_fb_vc = simple_strtoul(options, &options, 10) - 1; if (first_fb_vc < 0) first_fb_vc = 0; if (*options++ == '-') last_fb_vc = simple_strtoul(options, &options, 10) - 1; fbcon_is_default = 0; } if (!strncmp(options, "rotate:", 7)) { options += 7; if (*options) rotate = simple_strtoul(options, &options, 0); if (rotate > 3) rotate = 0; } } return 0;}__setup("fbcon=", fb_console_setup);#endifstatic int search_fb_in_map(int idx){ int i, retval = 0; for (i = 0; i < MAX_NR_CONSOLES; i++) { if (con2fb_map[i] == idx) retval = 1; } return retval;}static int search_for_mapped_con(void){ int i, retval = 0; for (i = 0; i < MAX_NR_CONSOLES; i++) { if (con2fb_map[i] != -1) retval = 1; } return retval;}static int 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 = 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; } return err;}static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, int cols, int rows, int new_cols, int new_rows){ /* Need to make room for the logo */ struct fbcon_ops *ops = info->fbcon_par; int cnt, erase = vc->vc_video_erase_char, step; unsigned short *save = NULL, *r, *q; /* * remove underline attribute from erase character * if black and white framebuffer. */ if (fb_get_color_depth(&info->var, &info->fix) == 1) erase &= ~0x400; logo_height = fb_prepare_logo(info, ops->rotate); logo_lines = (logo_height + vc->vc_font.height - 1) / 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(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) { /* We can scroll screen down */ 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) { vc->vc_y += logo_lines; vc->vc_pos += logo_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_lines > vc->vc_bottom) { logo_shown = FBCON_LOGO_CANSHOW; printk(KERN_INFO "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n"); } else if (logo_shown != FBCON_LOGO_DONTSHOW) { logo_shown = FBCON_LOGO_DRAW; vc->vc_top = logo_lines; }}#ifdef CONFIG_FB_TILEBLITTINGstatic void set_blitting_type(struct vc_data *vc, struct fb_info *info, struct display *p){ struct fbcon_ops *ops = info->fbcon_par; ops->p = (p) ? p : &fb_display[vc->vc_num]; if ((info->flags & FBINFO_MISC_TILEBLITTING)) fbcon_set_tileops(vc, info, p, ops); else { fbcon_set_rotation(info, ops->p); fbcon_set_bitops(ops); }}#elsestatic void set_blitting_type(struct vc_data *vc, struct fb_info *info, struct display *p){ struct fbcon_ops *ops = info->fbcon_par; info->flags &= ~FBINFO_MISC_TILEBLITTING; ops->p = (p) ? p : &fb_display[vc->vc_num]; fbcon_set_rotation(info, ops->p); fbcon_set_bitops(ops);}#endif /* CONFIG_MISC_TILEBLITTING */static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info, int unit, int oldidx){ struct fbcon_ops *ops = NULL; int err = 0; if (!try_module_get(info->fbops->owner)) err = -ENODEV; if (!err && info->fbops->fb_open && info->fbops->fb_open(info, 0)) err = -ENODEV; if (!err) { ops = kmalloc(sizeof(struct fbcon_ops), GFP_KERNEL); if (!ops) err = -ENOMEM; } if (!err) { memset(ops, 0, sizeof(struct fbcon_ops)); info->fbcon_par = ops; set_blitting_type(vc, info, NULL); } if (err) { con2fb_map[unit] = oldidx; module_put(info->fbops->owner); } return err;}static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo, struct fb_info *newinfo, int unit, int oldidx, int found){ struct fbcon_ops *ops = oldinfo->fbcon_par; int err = 0; if (oldinfo->fbops->fb_release && oldinfo->fbops->fb_release(oldinfo, 0)) { con2fb_map[unit] = oldidx; if (!found && newinfo->fbops->fb_release) newinfo->fbops->fb_release(newinfo, 0); if (!found) module_put(newinfo->fbops->owner); err = -ENODEV; } if (!err) { fbcon_del_cursor_timer(oldinfo); kfree(ops->cursor_state.mask); kfree(ops->cursor_data); kfree(ops->fontbuffer); kfree(oldinfo->fbcon_par); oldinfo->fbcon_par = NULL; module_put(oldinfo->fbops->owner); /* If oldinfo and newinfo are driving the same hardware, the fb_release() method of oldinfo may attempt to restore the hardware state. This will leave the newinfo in an undefined state. Thus, a call to fb_set_par() may be needed for the newinfo. */ if (newinfo->fbops->fb_set_par) newinfo->fbops->fb_set_par(newinfo); } return err;}static void con2fb_init_display(struct vc_data *vc, struct fb_info *info, int unit, int show_logo){ struct fbcon_ops *ops = info->fbcon_par; ops->currcon = fg_console; if (info->fbops->fb_set_par && !(ops->flags & FBCON_FLAGS_INIT)) info->fbops->fb_set_par(info); ops->flags |= FBCON_FLAGS_INIT; ops->graphics = 0; if (vc) fbcon_set_disp(info, &info->var, vc); else fbcon_preset_disp(info, &info->var, unit); if (show_logo) { struct vc_data *fg_vc = vc_cons[fg_console].d; struct fb_info *fg_info = registered_fb[con2fb_map[fg_console]]; fbcon_prepare_logo(fg_vc, fg_info, fg_vc->vc_cols, fg_vc->vc_rows, fg_vc->vc_cols, fg_vc->vc_rows); } update_screen(vc_cons[fg_console].d);}/** * set_con2fb_map - map console to frame buffer device * @unit: virtual console number to map * @newidx: frame buffer index to map virtual console to * @user: user request * * Maps a virtual console @unit to a frame buffer device * @newidx. */static int set_con2fb_map(int unit, int newidx, int user){ struct vc_data *vc = vc_cons[unit].d; int oldidx = con2fb_map[unit]; struct fb_info *info = registered_fb[newidx]; struct fb_info *oldinfo = NULL; int found, err = 0; if (oldidx == newidx) return 0; if (!info) err = -EINVAL; if (!err && !search_for_mapped_con()) { info_idx = newidx; return fbcon_takeover(0); } if (oldidx != -1) oldinfo = registered_fb[oldidx]; found = search_fb_in_map(newidx); acquire_console_sem(); con2fb_map[unit] = newidx; if (!err && !found) err = con2fb_acquire_newinfo(vc, info, unit, oldidx); /* * If old fb is not mapped to any of the consoles, * fbcon should release it. */ if (!err && oldinfo && !search_fb_in_map(oldidx)) err = con2fb_release_oldinfo(vc, oldinfo, info, unit, oldidx, found); if (!err) { int show_logo = (fg_console == 0 && !user && logo_shown != FBCON_LOGO_DONTSHOW); if (!found) fbcon_add_cursor_timer(info); con2fb_map_boot[unit] = newidx; con2fb_init_display(vc, info, unit, show_logo); } release_console_sem(); return err;}/* * Low Level Operations *//* NOTE: fbcon cannot be __init: it may be called from take_over_console later */static int var_to_display(struct display *disp, struct fb_var_screeninfo *var, struct fb_info *info){ disp->xres_virtual = var->xres_virtual; disp->yres_virtual = var->yres_virtual; disp->bits_per_pixel = var->bits_per_pixel; disp->grayscale = var->grayscale; disp->nonstd = var->nonstd; disp->accel_flags = var->accel_flags; disp->height = var->height; disp->width = var->width; disp->red = var->red; disp->green = var->green; disp->blue = var->blue; disp->transp = var->transp; disp->rotate = var->rotate; disp->mode = fb_match_mode(var, &info->modelist); if (disp->mode == NULL) /* This should not happen */ return -EINVAL; return 0;}static void display_to_var(struct fb_var_screeninfo *var, struct display *disp){ fb_videomode_to_var(var, disp->mode); var->xres_virtual = disp->xres_virtual; var->yres_virtual = disp->yres_virtual; var->bits_per_pixel = disp->bits_per_pixel; var->grayscale = disp->grayscale; var->nonstd = disp->nonstd; var->accel_flags = disp->accel_flags; var->height = disp->height; var->width = disp->width; var->red = disp->red; var->green = disp->green; var->blue = disp->blue; var->transp = disp->transp; var->rotate = disp->rotate;}static const char *fbcon_startup(void){ const char *display_desc = "frame buffer device"; struct display *p = &fb_display[fg_console]; struct vc_data *vc = vc_cons[fg_console].d; const struct font_desc *font = NULL; struct module *owner; struct fb_info *info = NULL; struct fbcon_ops *ops; int rows, cols; int irqres; irqres = 1; /* * If num_registered_fb is zero, this is a call for the dummy part. * The frame buffer devices weren't initialized yet. */ if (!num_registered_fb || info_idx == -1) return display_desc; /* * Instead of blindly using registered_fb[0], we use info_idx, set by * fb_console_init(); */ info = registered_fb[info_idx]; if (!info) return NULL; owner = info->fbops->owner; if (!try_module_get(owner)) return NULL; if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) { module_put(owner); return NULL; } ops = kmalloc(sizeof(struct fbcon_ops), GFP_KERNEL); if (!ops) { module_put(owner); return NULL; } memset(ops, 0, sizeof(struct fbcon_ops)); ops->currcon = -1; ops->graphics = 1; ops->cur_rotate = -1; info->fbcon_par = ops; p->con_rotate = rotate; set_blitting_type(vc, info, NULL); if (info->fix.type != FB_TYPE_TEXT) { if (fbcon_softback_size) { if (!softback_buf) { softback_buf = (unsigned long) kmalloc(fbcon_softback_size, GFP_KERNEL); if (!softback_buf) { fbcon_softback_size = 0; softback_top = 0; } } } else { if (softback_buf) { kfree((void *) softback_buf); softback_buf = 0; softback_top = 0; } } if (softback_buf) softback_in = softback_top = softback_curr = softback_buf; softback_lines = 0; } /* Setup default font */ if (!p->fontdata) { if (!fontname[0] || !(font = find_font(fontname))) font = get_default_font(info->var.xres, info->var.yres); vc->vc_font.width = font->width; vc->vc_font.height = font->height; vc->vc_font.data = (void *)(p->fontdata = font->data); vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */ } cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); cols /= vc->vc_font.width; rows /= vc->vc_font.height; vc_resize(vc, cols, rows); DPRINTK("mode: %s\n", info->fix.id); DPRINTK("visual: %d\n", info->fix.visual); DPRINTK("res: %dx%d-%d\n", info->var.xres, info->var.yres, info->var.bits_per_pixel);#ifdef CONFIG_ATARI if (MACH_IS_ATARI) { cursor_blink_rate = ATARI_CURSOR_BLINK_RATE; irqres = request_irq(IRQ_AUTO_4, fb_vbl_handler, IRQ_TYPE_PRIO, "framebuffer vbl", info); }#endif /* CONFIG_ATARI */#ifdef CONFIG_MAC /* * On a Macintoy, the VBL interrupt may or may not be active. * As interrupt based cursor is more reliable and race free, we * probe for VBL interrupts. */ if (MACH_IS_MAC) { int ct = 0; /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -