diff -Nru a/Documentation/fb/00-INDEX b/Documentation/fb/00-INDEX --- a/Documentation/fb/00-INDEX 2004-08-23 11:56:23 +02:00 +++ b/Documentation/fb/00-INDEX 2004-08-23 11:56:23 +02:00 @@ -19,6 +19,8 @@ - info on the Matrox frame buffer driver pvr2fb.txt - info on the PowerVR 2 frame buffer driver +splash.txt + - info on the Framebuffer Splash tgafb.txt - info on the TGA (DECChip 21030) frame buffer driver vesafb.txt diff -Nru a/Documentation/fb/splash.txt b/Documentation/fb/splash.txt --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/fb/splash.txt 2004-08-23 11:56:23 +02:00 @@ -0,0 +1,230 @@ +What is it? +----------- + +The framebuffer splash is a kernel feature that allows displaying a background +picture on selected consoles and switching the first console to the so-called +silent mode, while booting/rebooting/shutting down the system. + +What do I need to get it to work? +--------------------------------- + +To get fb splash up-and-running you will have to: + 1) get a copy of splashutils from (FIXME URL) + 2) get some splash themes (FIXME URL) + 3) build the kernel helper program + 4) build your kernel with the FB_SPLASH option enabled. + +To get fb splash operational right after fbcon initialization is finished, you +will have to include a theme and the kernel helper into your initramfs image. +Please refer to splashutils documentation for instructions on how to do that. + +Operation modes +--------------- + +The framebuffer splash can work in two modes: verbose and silent. The first +one means "console with a background image". The latter one is a concept +first introduced in bootsplash. When silent mode is active, the console is +switched to a graphic mode and no text is displayed. It's up to the userspace +programs to display something (a progress bar for example) on the screen. + +Kernel command line parameters +------------------------------ + +Framebuffer splash can be configured from the kernel command line by passing +options in the following way: "splash=option1,option2". + +The following options are recognized: + +off - do not enable fbsplash after fbcon initialization; this is + default behaviour +verbose - switch fbsplash to verbose mode after fbcon is initialized +silent - switch fbsplash to silent mode after fbcon is initialized + +theme: - use theme 'name' on the first console, default theme name + is 'default' (who'd have guessed?) + +Example - you want to get verbose splash with the theme 'tux' right after +fbcon is up: + splash=verbose,theme:tux + +The userspace helper +-------------------- + +The userspace splash helper (by default: /sbin/splash_helper) is called by the +kernel whenever an important event occurs and the kernel needs some kind of +job to be carried out. Important events include console switches and graphic +mode switches (the kernel requests background images and config for the current +console). The splash helper must be accessible at all times. If it's not, +fbsplash will be switched off automatically. + +It's possible to set path to the splash helper by writing it to +/proc/sys/kernel/fbsplash. + +***************************************************************************** + +The information below is mostly technical stuff. If you don't plan to develop +something fbsplash-related, there's probably no need to read it. + +The splash protocol +------------------- + +The splash protocol defines a communication interface between the kernel and +the userspace splash helper. + +The kernel side is responsible for: + + o rendering console text, using an image as a background (instead of a + standard solid color fbcon uses), + o accepting commands from the user via ioctls on the fbsplash device, + o calling the userspace helper to set things up as soon as the fb subsystem is + initialized. + +The userspace helper is responsible for everything else, including parsing +configuration files, decompressing the image files whenever the kernel needs +it, and communicating with the kernel if necessary. + +The splash protocol specifies how communication is done in both ways: +kernel->userspace and userspace->helper. + +Kernel -> Userspace +------------------- + +The kernel communicates with the userspace helper by calling it and specifying +the task that to be done in a series of arguments. + +The arguments follow the pattern: + + +All commands defined in splash protocol v1 have the following parameters: + virtual console + framebuffer number + splash mode - 'v' indicates verbose, 's' indicates silent + theme + +Splash protocol v1 specifies the following commands: + +getpic +------ + The kernel issues this command to request image data. It's up to the userspace + helper to find a background image appropriate for the specified theme and the + current resolution. The userspace helper should respond by issuing the + FBIOSPLASH_SETPIC ioctl. + +init +---- + The kernel issues this command if either 'verbose' or 'silent' has been + specified in splash= on the kernel command line. Upon receiving this command, + the userspace helper should find a proper configuration file for the + specified theme and the current resolution, and issue the FBIOSPLASH_SETCFG. + FBIOSPLASH_SETPIC and FBIOSPLASH_SETSTATE commands. + + When the userspace helper is called in an early phase of the boot process + (right after the initialization of fbcon), no filesystems will be mounted. + The helper program should mount sysfs and then create the appropriate + framebuffer, fbsplash and tty0 devices (if they don't already exist) to get + current display settings and to be able to communicate with the kernel side. + + Note that the console sem is not held when the kernel calls splash_helper + with the 'init' command. The splash helper should perform all ioctls with + origin set to FB_SPLASH_IO_ORIG_USER. + +modechange +---------- + The kernel issues this command on a mode change. The helper's response should + be similar to the response to the 'init' command. Note that this time the + console sem is held and all ioctls must be performed with origin set to + FB_SPLASH_IO_ORIG_KERNEL. + + +Userspace -> Kernel +------------------- + +Userspace programs can communicate with the kernel side via ioctls on the +on the fbsplash device. These ioctls are to be used by both the userspace helper +(called only by the kernel) and userspace configuration tools (run by the users). +The splash helper should set the origin field to FB_SPLASH_IO_ORIG_KERNEL when +doing the appropriate ioctls. All userspace configuration tools should use +FB_SPLASH_IO_ORIG_USER. Failure to set the appropriate value in the origin +field when performing ioctls from the kernel helper may result in a deadlock. + +The framebuffer splash provides the following ioctls (all defined in linux/fb.h): + +FBIOSPLASH_SETMODE +description: sets the global fbsplash mode +argument: unsigned int; values: FB_SPLASH_MODE_VERBOSE, FB_SPLASH_MODE_SILENT + +FBIOSPLASH_GETMODE +description: gets the global fbsplash mode +argument: unsigned int*; values as in FBIOSPLASH_SETMODE + +FBIOSPLASH_SETPIC +description: loads a background picture for a virtual console +argument: struct fb_splash_iowrapper*; data: struct fb_image* +notes: +If called for consoles other than the current foreground one, the picture data +will be ignored. + +If the current virtual console is running in a 8-bpp mode, the cmap substruct +of fb_image has to be filled appropriately: start should be set to 16 (first 16 +colors are reserved for fbcon), len to a value <= 240 and red, green and blue +should point to valid cmap data. The transp field is ingored. The fields dx, dy +bg_color, fg_color in fb_image are ignored as well. + +FBIOSPLASH_SETCFG +description: sets the fbsplash config for a virtual console +argument: struct fb_splash_iowrapper*; data: struct vc_splash*; + the structure has to be filled with valid data. + +FBIOSPLASH_GETCFG +description: gets the fbsplash config for a virtual console +argument: struct fb_splash_iowrapper*; data: struct vc_splash* + +FBIOSPLASH_SETSTATE +description: sets the fbsplash state for a virtual console +argument: struct fb_splash_iowrapper*; data: unsigned int* + values: 0 = disabled, 1 = enabled. + +FBIOSPLASH_GETSTATE +description: gets the fbsplash state for a virtual console +argument: struct fb_splash_iowrapper*; data: unsigned int* + values: as in FBIOSPLASH_SETSTATE + +Info on used structures: + +Definition of struct vc_splash can be found in linux/console_splash.h, it's +heavily commented, so no special descriptions are necessary. Note that +the 'theme' field should point to a string no longer than FB_SPLASH_THEME_LEN. +When FBIOSPLASH_GETCFG call is performed, the theme field should point to a +char buffer of length FB_SPLASH_THEME_LEN. + +Definition of struct fb_splash_iowrapper can be found in linux/fb.h. +The fields in this struct have the following meaning: + +vc: +Virtual console number. + +origin: +Specifies if the ioctl is performed as a response to a kernel request. The +splash helper should set this field to FB_SPLASH_IO_ORIG_KERNEL, userspace +programs should set it to FB_SPLASH_IO_ORIG_USER. This field is necessary to +avoid console semaphore deadlocks. + +data: +Pointer to a data structure appropriate for the performed ioctl. Type of +the data struct is specified in the ioctls description, for example 'data: +struct vc_splash*' means that the 'data' field should be a pointer to a +vc_splash struct. + +***************************************************************************** + +Credit +------ + +Original idea & implementation by: + Volker Poplawski , Stefan Reinauer , + Steffen Winterfeldt , Michael Schroeder , + Ken Wimer . + +Splash protocol redesign, current implementation, docs by: + Michal Januszewski + diff -Nru a/drivers/char/keyboard.c b/drivers/char/keyboard.c --- a/drivers/char/keyboard.c 2004-08-23 11:56:23 +02:00 +++ b/drivers/char/keyboard.c 2004-08-23 11:56:23 +02:00 @@ -41,6 +41,8 @@ #include #include +#include "../video/fbsplash.h" + static void kbd_disconnect(struct input_handle *handle); extern void ctrl_alt_del(void); @@ -1057,6 +1059,12 @@ if (emulate_raw(vc, keycode, !down << 7)) if (keycode < BTN_MISC) printk(KERN_WARNING "keyboard.c: can't emulate rawmode for keycode %d\n", keycode); + + /* switch splash to verbose mode if ESC or F2 is pressed */ + if (down == 1 && (keycode == KEY_ESC || keycode == KEY_F2)) { + if (fbsplash_verbose()) + return; + } #ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ if (keycode == KEY_SYSRQ && (sysrq_down || (down == 1 && sysrq_alt))) { diff -Nru a/drivers/char/n_tty.c b/drivers/char/n_tty.c --- a/drivers/char/n_tty.c 2004-08-23 11:56:23 +02:00 +++ b/drivers/char/n_tty.c 2004-08-23 11:56:23 +02:00 @@ -49,6 +49,8 @@ #include #include +#include "../video/fbsplash.h" + /* number of characters left in xmit buffer before select has we have room */ #define WAKEUP_CHARS 256 @@ -987,6 +989,15 @@ if (!tty->read_buf) { printk("n_tty_read_chan: called with read_buf == NULL?!?\n"); return -EIO; + } + + /* automatically switch splash to verbose mode if someone tries to + read from tty0 */ + if (file->f_dentry->d_inode->i_rdev == MKDEV(TTY_MAJOR,0) || + file->f_dentry->d_inode->i_rdev == MKDEV(TTY_MAJOR,1) || + file->f_dentry->d_inode->i_rdev == MKDEV(TTYAUX_MAJOR,0) || + file->f_dentry->d_inode->i_rdev == MKDEV(TTYAUX_MAJOR,1)) { + fbsplash_verbose(); } /* Job control check -- must be done at start and after diff -Nru a/drivers/video/Kconfig b/drivers/video/Kconfig --- a/drivers/video/Kconfig 2004-08-23 11:56:23 +02:00 +++ b/drivers/video/Kconfig 2004-08-23 11:56:23 +02:00 @@ -1002,5 +1002,15 @@ source "drivers/video/logo/Kconfig" endif -endmenu +config FB_SPLASH + bool "Support for the framebuffer splash" + depends on FRAMEBUFFER_CONSOLE=y + default n + ---help--- + This option enables support for the Linux boot-up splash screen and + graphical backgrounds on consoles. Note that you will need userspace + splashutils in order to take advantage of these features. Refer to + Documentation/fb/splash.txt for more information. + If unsure, say N. +endmenu diff -Nru a/drivers/video/Makefile b/drivers/video/Makefile --- a/drivers/video/Makefile 2004-08-23 11:56:23 +02:00 +++ b/drivers/video/Makefile 2004-08-23 11:56:23 +02:00 @@ -6,6 +6,7 @@ obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ +obj-$(CONFIG_FB_SPLASH) += fbsplash.o cfbsplash.o obj-$(CONFIG_FB) += fbmem.o fbmon.o fbcmap.o fbsysfs.o modedb.o softcursor.o # Only include macmodes.o if we have FB support and are PPC diff -Nru a/drivers/video/cfbsplash.c b/drivers/video/cfbsplash.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/video/cfbsplash.c 2004-08-23 11:56:23 +02:00 @@ -0,0 +1,471 @@ +/* + * linux/drivers/video/cfbsplash.c -- Framebuffer splash render functions + * + * Copyright (C) 2004 Michal Januszewski + * + * Code based upon "Bootsplash" (C) 2001-2003 + * Volker Poplawski , + * Stefan Reinauer , + * Steffen Winterfeldt , + * Michael Schroeder , + * Ken Wimer . + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "console/fbcon.h" +#include "fbsplash.h" + +#define parse_pixel(shift,bpp,type) \ + do { \ + if (d & (0x80 >> (shift))) \ + dd2[(shift)] = fgx; \ + else \ + dd2[(shift)] = transparent ? *(type *)splash_src : bgx; \ + splash_src += (bpp); \ + } while (0) \ + +void fbsplash_renderc(struct fb_info *info, int ypos, int xpos, int height, + int width, u8* src, u32 fgx, u32 bgx, u8 transparent) +{ + unsigned int x, y; + u32 dd; + int bytespp = ((info->var.bits_per_pixel + 7) >> 3); + unsigned int d = ypos * info->fix.line_length + xpos * bytespp; + u16 dd2[4]; + + u8* splash_src = (u8 *)(info->splash.data + d); + u8* dst = (u8 *)(info->screen_base + d); + + if ((ypos + height) > info->var.yres || (xpos + width) > info->var.xres) + return; + + for (y = 0; y < height; y++) { + switch (info->var.bits_per_pixel) { + + case 32: + for (x = 0; x < width; x++) { + + if ((x & 7) == 0) + d = *src++; + if (d & 0x80) + dd = fgx; + else + dd = transparent ? + *(u32 *)splash_src : bgx; + + d <<= 1; + splash_src += 4; + fb_writel(dd, dst); + dst += 4; + } + break; + case 24: + for (x = 0; x < width; x++) { + + if ((x & 7) == 0) + d = *src++; + if (d & 0x80) + dd = fgx; + else + dd = transparent ? + (*(u32 *)splash_src & 0xffffff) : bgx; + + d <<= 1; + splash_src += 3; +#ifdef __LITTLE_ENDIAN + fb_writew(dd & 0xffff, dst); + dst += 2; + fb_writeb((dd >> 16), dst); +#else + fb_writew(dd >> 8, dst); + dst += 2; + fb_writeb(dd & 0xff, dst); +#endif + dst++; + } + break; + case 16: + for (x = 0; x < width; x += 2) { + if ((x & 7) == 0) + d = *src++; + + parse_pixel(0, 2, u16); + parse_pixel(1, 2, u16); +#ifdef __LITTLE_ENDIAN + dd = dd2[0] | (dd2[1] << 16); +#else + dd = dd2[1] | (dd2[1] << 16); +#endif + d <<= 2; + fb_writel(dd, dst); + dst += 4; + } + break; + + case 8: + for (x = 0; x < width; x += 4) { + if ((x & 7) == 0) + d = *src++; + + parse_pixel(0, 1, u8); + parse_pixel(1, 1, u8); + parse_pixel(2, 1, u8); + parse_pixel(3, 1, u8); + +#ifdef __LITTLE_ENDIAN + dd = dd2[0] | (dd2[1] << 8) | (dd2[2] << 16) | (dd2[3] << 24); +#else + dd = dd2[3] | (dd2[2] << 8) | (dd2[1] << 16) | (dd2[0] << 24); +#endif + d <<= 4; + fb_writel(dd, dst); + dst += 4; + } + } + + d = info->fix.line_length - width * bytespp; + dst += d; + splash_src += d; + } +} + +#define cc2cx(a) \ + ((info->fix.visual == FB_VISUAL_TRUECOLOR || \ + info->fix.visual == FB_VISUAL_DIRECTCOLOR) ? \ + ((u32*)info->pseudo_palette)[a] : a) + +void fbsplash_putcs(struct vc_data *vc, struct fb_info *info, + const unsigned short *s, int count, int yy, int xx) +{ + unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; + int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; + int fgshift = (vc->vc_hi_font_mask) ? 9 : 8; + int fg_color, bg_color, transparent; + + u8 *src; + u32 bgx, fgx; + + u16 c = scr_readw(s); + + fg_color = attr_fgcol(fgshift, c); + bg_color = attr_bgcol(bgshift, c); + transparent = vc->vc_splash.bg_color == bg_color; + + xx = xx * vc->vc_font.width + vc->vc_splash.tx; + yy = yy * vc->vc_font.height + vc->vc_splash.ty; + + fgx = cc2cx(fg_color); + bgx = cc2cx(bg_color); + + while (count--) { + c = scr_readw(s++); + src = vc->vc_font.data + (c & charmask) * vc->vc_font.height * + ((vc->vc_font.width + 7) >> 3); + + fbsplash_renderc(info, yy, xx, vc->vc_font.height, + vc->vc_font.width, src, fgx, bgx, transparent); + xx += vc->vc_font.width; + } +} + +void fbsplash_putc(struct vc_data *vc, struct fb_info *info, int c, + int ypos, int xpos) +{ + unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; + int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; + int fgshift = (vc->vc_hi_font_mask) ? 9 : 8; + u32 bg_color = attr_bgcol(bgshift, c); + u32 fg_color = attr_fgcol(fgshift, c); + u8 *src = vc->vc_font.data + (c & charmask) * vc->vc_font.height * + ((vc->vc_font.width + 7) >> 3); + xpos = xpos * vc->vc_font.width + vc->vc_splash.tx; + ypos = ypos * vc->vc_font.height + vc->vc_splash.ty; + + fbsplash_renderc(info, ypos, xpos, vc->vc_font.height, vc->vc_font.width, + src, cc2cx(fg_color), cc2cx(bg_color), + vc->vc_splash.bg_color == bg_color); +} + +void fbsplash_cursor(struct fb_info *info, struct fb_cursor *cursor) +{ + int i; + unsigned int dsize, s_pitch; + char *t = (char *)info->cursor.image.data; + struct vc_data* vc; + + vc = vc_cons[info->currcon].d; + + if (cursor->set & FB_CUR_SETSIZE) { + info->cursor.image.height = cursor->image.height; + info->cursor.image.width = cursor->image.width; + } + if (cursor->set & FB_CUR_SETPOS) { + info->cursor.image.dx = cursor->image.dx; + info->cursor.image.dy = cursor->image.dy; + } + if (cursor->set & FB_CUR_SETHOT) + info->cursor.hot = cursor->hot; + if (cursor->set & FB_CUR_SETCMAP) { + if (cursor->image.depth == 1) { + info->cursor.image.bg_color = cursor->image.bg_color; + info->cursor.image.fg_color = cursor->image.fg_color; + } else { + if (cursor->image.cmap.len) + fb_copy_cmap(&cursor->image.cmap, + &info->cursor.image.cmap); + } + info->cursor.image.depth = cursor->image.depth; + } + s_pitch = (info->cursor.image.width + 7) >> 3; + dsize = s_pitch * info->cursor.image.height; + if (info->cursor.enable) { + switch (info->cursor.rop) { + case ROP_XOR: + for (i = 0; i < dsize; i++) + t[i] = cursor->image.data[i] ^ info->cursor.mask[i]; + break; + case ROP_COPY: + default: + for (i = 0; i < dsize; i++) + t[i] = cursor->image.data[i] & info->cursor.mask[i]; + break; + } + } else if (t != cursor->image.data) + memcpy(t, cursor->image.data, dsize); + + fbsplash_renderc(info, + info->cursor.image.dy + vc->vc_splash.ty, + info->cursor.image.dx + vc->vc_splash.tx, + info->cursor.image.height, + info->cursor.image.width, + (u8*)info->cursor.image.data, + cc2cx(info->cursor.image.fg_color), + cc2cx(info->cursor.image.bg_color), + info->cursor.image.bg_color == vc->vc_splash.bg_color); +} + +static void splashset(u8 *dst, int height, int width, int dstbytes, + u32 bgx, int bpp) +{ + int i; + + if (bpp == 8) + bgx |= bgx << 8; + if (bpp == 16 || bpp == 8) + bgx |= bgx << 16; + + while (height-- > 0) { + u8 *p = dst; + + switch (bpp) { + + case 32: + for (i=0; i < width; i++) { + fb_writel(bgx, p); p += 4; + } + break; + case 24: + for (i=0; i < width; i++) { +#ifdef __LITTLE_ENDIAN + fb_writew((bgx & 0xffff),(u16*)p); p += 2; + fb_writeb((bgx >> 16),p++); +#else + fb_writew((bgx >> 8),(u16*)p); p += 2; + fb_writeb((bgx & 0xff),p++); +#endif + } + case 16: + for (i=0; i < width/4; i++) { + fb_writel(bgx,p); p += 4; + fb_writel(bgx,p); p += 4; + } + if (width & 2) { + fb_writel(bgx,p); p += 4; + } + if (width & 1) + fb_writew(bgx,(u16*)p); + break; + case 8: + for (i=0; i < width/4; i++) { + fb_writel(bgx,p); p += 4; + } + + if (width & 2) { + fb_writew(bgx,p); p += 2; + } + if (width & 1) + fb_writeb(bgx,(u8*)p); + break; + + } + dst += dstbytes; + } +} + +void fbsplash_copy(u8 *dst, u8 *src, int height, int width, int linebytes, + int bpp) +{ + int i; + + while (height-- > 0) { + u32 *p = (u32 *)dst; + u32 *q = (u32 *)src; + + switch (bpp) { + + case 32: + for (i=0; i < width; i++) + fb_writel(*q++, p++); + break; + case 24: + for (i=0; i < (width*3/4); i++) + fb_writel(*q++, p++); + if ((width*3) % 4) { + if (width & 2) { + fb_writeb(*(u8*)q, (u8*)p); + } else if (width & 1) { + fb_writew(*(u16*)q, (u16*)p); + fb_writeb(*(u8*)((u16*)q+1),(u8*)((u16*)p+2)); + } + } + break; + case 16: + for (i=0; i < width/4; i++) { + fb_writel(*q++, p++); + fb_writel(*q++, p++); + } + if (width & 2) + fb_writel(*q++, p++); + if (width & 1) + fb_writew(*(u16*)q, (u16*)p); + break; + case 8: + for (i=0; i < width/4; i++) + fb_writel(*q++, p++); + + if (width & 2) { + fb_writew(*(u16*)q, (u16*)p); + q = (u32*) ((u16*)q + 1); + p = (u32*) ((u16*)p + 1); + } + if (width & 1) + fb_writeb(*(u8*)q, (u8*)p); + break; + } + + dst += linebytes; + src += linebytes; + } +} + +static void splashfill(struct fb_info *info, int sy, int sx, int height, + int width) +{ + int d = sy * info->fix.line_length + sx * ((info->var.bits_per_pixel + 7) >> 3); + + fbsplash_copy((u8 *)(info->screen_base + d), (u8 *)(info->splash.data + d), + height, width, info->fix.line_length, info->var.bits_per_pixel); +} + +void fbsplash_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, + int height, int width) +{ + int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; + int bg_color = attr_bgcol_ec(bgshift, vc); + int transparent = vc->vc_splash.bg_color == bg_color; + u8 *dst; + + sy = sy * vc->vc_font.height + vc->vc_splash.ty; + sx = sx * vc->vc_font.width + vc->vc_splash.tx; + height *= vc->vc_font.height; + width *= vc->vc_font.width; + + if (transparent) { + splashfill(info, sy, sx, height, width); + } else { + dst = (u8 *)(info->screen_base + sy * info->fix.line_length + + sx * ((info->var.bits_per_pixel + 7) >> 3)); + splashset(dst, height, width, info->fix.line_length, cc2cx(bg_color), + info->var.bits_per_pixel); + } +} + +void fbsplash_clear_margins(struct vc_data *vc, struct fb_info *info, + int bottom_only) +{ + unsigned int tw = vc->vc_cols*vc->vc_font.width; + unsigned int th = vc->vc_rows*vc->vc_font.height; + + if (!bottom_only) { + /* top margin */ + splashfill(info, 0, 0, vc->vc_splash.ty, info->var.xres); + /* left margin */ + splashfill(info, vc->vc_splash.ty, 0, th, vc->vc_splash.tx); + /* right margin */ + splashfill(info, vc->vc_splash.ty, vc->vc_splash.tx + tw, th, + info->var.xres - vc->vc_splash.tx - tw); + } + splashfill(info, vc->vc_splash.ty + th, 0, + info->var.yres - vc->vc_splash.ty - th, info->var.xres); +} + +void fbsplash_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, + int sx, int dx, int width) +{ + u16 *d = (u16 *) (vc->vc_origin + vc->vc_size_row * y + dx * 2); + u16 *s = d + (dx - sx); + u16 *start = d; + u16 *ls = d; + u16 *le = d + width; + u16 c; + int x = dx; + u16 attr = 1; + + do { + c = scr_readw(d); + if (attr != (c & 0xff00)) { + attr = c & 0xff00; + if (d > start) { + fbsplash_putcs(vc, info, start, d - start, y, x); + x += d - start; + start = d; + } + } + if (s >= ls && s < le && c == scr_readw(s)) { + if (d > start) { + fbsplash_putcs(vc, info, start, d - start, y, x); + x += d - start + 1; + start = d + 1; + } else { + x++; + start++; + } + } + s++; + d++; + } while (d < le); + if (d > start) + fbsplash_putcs(vc, info, start, d - start, y, x); +} + +void fbsplash_blank(struct vc_data *vc, struct fb_info *info, int blank) +{ + if (blank) { + splashset((u8 *)info->screen_base, info->var.yres, info->var.xres, + info->fix.line_length, 0, info->var.bits_per_pixel); + } else { + update_screen(vc->vc_num); + fbsplash_clear_margins(vc, info, 0); + } +} + diff -Nru a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c --- a/drivers/video/console/fbcon.c 2004-08-23 11:56:23 +02:00 +++ b/drivers/video/console/fbcon.c 2004-08-23 11:56:23 +02:00 @@ -93,6 +93,7 @@ #endif #include "fbcon.h" +#include "../fbsplash.h" #ifdef FBCONDEBUG # define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) @@ -210,9 +211,14 @@ info->cursor.rop == ROP_COPY || !vc || !CON_IS_VISIBLE(vc) || registered_fb[(int) con2fb_map[vc->vc_num]] != info) return; + acquire_console_sem(); info->cursor.enable ^= 1; - info->fbops->fb_cursor(info, &info->cursor); + if (fbsplash_active(info, vc_cons[info->currcon].d)) { + fbsplash_cursor(info, &info->cursor); + } else { + info->fbops->fb_cursor(info, &info->cursor); + } release_console_sem(); } @@ -414,6 +420,14 @@ area.sy = sy * vc->vc_font.height; area.dx = dx * vc->vc_font.width; area.dy = dy * vc->vc_font.height; + + if (fbsplash_active(info, vc)) { + area.sx += vc->vc_splash.tx; + area.sy += vc->vc_splash.ty; + area.dx += vc->vc_splash.tx; + area.dy += vc->vc_splash.ty; + } + area.height = height * vc->vc_font.height; area.width = width * vc->vc_font.width; @@ -426,6 +440,11 @@ int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; struct fb_fillrect region; + if (fbsplash_active(info, vc)) { + fbsplash_clear(vc, info, sy, sx, height, width); + return; + } + region.color = attr_bgcol_ec(bgshift, vc); region.dx = sx * vc->vc_font.width; region.dy = sy * vc->vc_font.height; @@ -458,6 +477,11 @@ struct fb_image image; u8 *src, *dst; + if (fbsplash_active(info, vc)) { + fbsplash_putcs(vc, info, s, count, yy, xx); + return; + } + image.fg_color = attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8, scr_readw(s)); image.bg_color = attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12, @@ -526,6 +550,13 @@ unsigned int bs = info->var.yres - bh; struct fb_fillrect region; + if (fbsplash_active(info, vc)) { + if (!fbsplash_isverbose) + return; + fbsplash_clear_margins(vc, info, bottom_only); + return; + } + region.color = 0; region.rop = ROP_COPY; @@ -622,6 +653,12 @@ cols = info->var.xres / vc->vc_font.width; rows = info->var.yres / vc->vc_font.height; + + if (fbsplash_active(info, vc)) { + cols = vc->vc_splash.twidth / vc->vc_font.width; + rows = vc->vc_splash.theight / vc->vc_font.height; + } + vc_resize(vc->vc_num, cols, rows); DPRINTK("mode: %s\n", info->fix.id); @@ -718,7 +755,7 @@ if (info_idx == -1 || info == NULL) return; if (vc->vc_num != display_fg || (info->flags & FBINFO_MODULE) || - (info->fix.type == FB_TYPE_TEXT)) + (info->fix.type == FB_TYPE_TEXT) || fbsplash_active(info, vc)) logo = 0; info->var.xoffset = info->var.yoffset = p->yscroll = 0; /* reset wrap/pan */ @@ -948,6 +985,11 @@ if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT) return; + if (fbsplash_active(info, vc)) { + fbsplash_putc(vc, info, c, ypos, xpos); + return; + } + image.dx = xpos * vc->vc_font.width; image.dy = real_y(p, ypos) * vc->vc_font.height; image.width = vc->vc_font.width; @@ -1025,7 +1067,11 @@ if (info->cursor.rop == ROP_XOR) { info->cursor.enable = 0; info->cursor.rop = ROP_COPY; - info->fbops->fb_cursor(info, &cursor); + if (fbsplash_active(info, vc)) { + fbsplash_cursor(info, &cursor); + } else { + info->fbops->fb_cursor(info, &cursor); + } } break; case CM_MOVE: @@ -1101,7 +1147,11 @@ mask[i++] = 0xff; } info->cursor.rop = ROP_XOR; - info->fbops->fb_cursor(info, &cursor); + if (fbsplash_active(info, vc)) { + fbsplash_cursor(info, &cursor); + } else { + info->fbops->fb_cursor(info, &cursor); + } vbl_cursor_cnt = CURSOR_DRAW_DELAY; break; } @@ -1538,7 +1588,7 @@ count = vc->vc_rows; if (softback_top) fbcon_softback_note(vc, t, count); - if (logo_shown >= 0) + if (logo_shown >= 0 || fbsplash_active(info, vc)) goto redraw_up; switch (p->scrollmode) { case SCROLL_MOVE: @@ -1625,6 +1675,8 @@ case SM_DOWN: if (count > vc->vc_rows) /* Maximum realistic size */ count = vc->vc_rows; + if (fbsplash_active(info, vc)) + goto redraw_down; switch (p->scrollmode) { case SCROLL_MOVE: accel_bmove(vc, info, t, 0, t + count, 0, @@ -1767,6 +1819,13 @@ } return; } + + if (fbsplash_active(info, vc) && sy == dy && height == 1) { + /* must use slower redraw bmove to keep background pic intact */ + fbsplash_bmove_redraw(vc, info, sy, sx, dx, width); + return; + } + accel_bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, height, width); } @@ -1821,7 +1880,7 @@ var.yres = height * fh; x_diff = info->var.xres - var.xres; y_diff = info->var.yres - var.yres; - if (x_diff < 0 || x_diff > fw || (y_diff < 0 || y_diff > fh)) { + if ((x_diff < 0 || x_diff > fw || (y_diff < 0 || y_diff > fh)) && !vc->vc_splash.state) { char mode[40]; DPRINTK("attempting resize %ix%i\n", var.xres, var.yres); @@ -1849,6 +1908,15 @@ struct display *p = &fb_display[vc->vc_num]; int i; + if (fbsplash_active_vc(vc)) { + struct vc_data *vc_curr = vc_cons[info->currcon].d; + + if (!vc_curr->vc_splash.theme || strcmp(vc->vc_splash.theme, vc_curr->vc_splash.theme)) { + if (fbsplash_call_helper("getpic", vc->vc_num)) + fbsplash_disable(vc, 0); + } + } + if (softback_top) { int l = fbcon_softback_size / vc->vc_size_row; if (softback_lines) @@ -1966,6 +2034,12 @@ fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW); if (!info->fbops->fb_blank) { + + if (fbsplash_active(info, vc)) { + fbsplash_blank(vc, info, blank); + return 0; + } + if (blank) { unsigned short oldc; u_int height; @@ -2134,9 +2208,16 @@ } if (resize) { + u32 xres = info->var.xres, yres = info->var.yres; /* reset wrap/pan */ info->var.xoffset = info->var.yoffset = p->yscroll = 0; - vc_resize(vc->vc_num, info->var.xres / w, info->var.yres / h); + + if (fbsplash_active(info, vc)) { + xres = vc->vc_splash.twidth; + yres = vc->vc_splash.theight; + } + + vc_resize(vc->vc_num, xres / w, yres / h); if (CON_IS_VISIBLE(vc) && softback_buf) { int l = fbcon_softback_size / vc->vc_size_row; if (l > 5) @@ -2310,7 +2391,63 @@ else palette_cmap.len = 16; palette_cmap.start = 0; - return fb_set_cmap(&palette_cmap, info); + + if (fbsplash_active(info, vc) && info->fix.visual == FB_VISUAL_DIRECTCOLOR) { + + u16 *red, *green, *blue; + u32 col; + int minlen = min(min(info->var.red.length, info->var.green.length), + info->var.blue.length); + int h; + + struct fb_cmap cmap = { + .start = 0, + .len = (1 << minlen), + .red = NULL, + .green = NULL, + .blue = NULL, + .transp = NULL + }; + + red = kmalloc(256 * sizeof(u16) * 3, GFP_KERNEL); + + if (!red) + goto out; + + green = red + 256; + blue = green + 265; + cmap.red = red; + cmap.green = green; + cmap.blue = blue; + + for (i = 0; i < cmap.len; i++) { + red[i] = green[i] = blue[i] = (0xffff * i)/(cmap.len-1); + } + + h = fb_set_cmap(&cmap, info); + + for (j = i = 0; i < 16; i++) { + k = table[i]; + + col = ((vc->vc_palette[j++] >> (8-minlen)) + << info->var.red.offset); + col |= ((vc->vc_palette[j++] >> (8-minlen)) + << info->var.green.offset); + col |= ((vc->vc_palette[j++] >> (8-minlen)) + << info->var.blue.offset); + + ((u32 *)info->pseudo_palette)[k] = col; + } + + kfree(red); + + return h; + + } else if (fbsplash_active(info, vc) && info->var.bits_per_pixel == 8 && + info->splash.cmap.red != NULL) + fb_set_cmap(&info->splash.cmap, info); + +out: return fb_set_cmap(&palette_cmap, info); } static u16 *fbcon_screen_pos(struct vc_data *vc, int offset) @@ -2490,7 +2627,10 @@ { /* Clear cursor, restore saved data */ info->cursor.enable = 0; - info->fbops->fb_cursor(info, &info->cursor); + if (fbsplash_active(info, vc_cons[info->currcon].d)) + fbsplash_cursor(info, &info->cursor); + else + info->fbops->fb_cursor(info, &info->cursor); } static void fbcon_resumed(struct fb_info *info) @@ -2522,7 +2662,14 @@ if (CON_IS_VISIBLE(vc)) { cols = info->var.xres / vc->vc_font.width; rows = info->var.yres / vc->vc_font.height; - vc_resize(vc->vc_num, cols, rows); + + if (!fbsplash_active(info, vc)) { + vc_resize(vc->vc_num, cols, rows); + } else { + if (fbsplash_call_helper("modechange", vc->vc_num)) + fbsplash_disable(vc, 0); + } + updatescrollmode(p, info, vc); scrollback_max = 0; scrollback_current = 0; @@ -2632,6 +2779,9 @@ fbcon_event_notifier_registered = 1; } release_console_sem(); + + fbsplash_init(); + return 0; } diff -Nru a/drivers/video/fbsplash.c b/drivers/video/fbsplash.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/video/fbsplash.c 2004-08-23 11:56:23 +02:00 @@ -0,0 +1,527 @@ +/* + * linux/drivers/video/fbsplash.c -- Framebuffer splash routines + * + * Copyright (C) 2004 Michal Januszewski + * + * Code based upon "Bootsplash" (C) 2001-2003 + * Volker Poplawski , + * Stefan Reinauer , + * Steffen Winterfeldt , + * Michael Schroeder , + * Ken Wimer . + * + * Splash render routines are located in /linux/drivers/video/cfbsplash.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "console/fbcon.h" +#include "fbsplash.h" + +#define SPLASH_VERSION "0.9" + +#ifdef DEBUG +#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +static void splash_work_verbose(void *data); +static int fbsplash_enable(struct vc_data *vc); + +DECLARE_WORK(splash_wq_verbose, splash_work_verbose, NULL); + +int fbsplash_mode = 0; +char fbsplash_path[KMOD_PATH_LEN] = "/sbin/splash_helper"; +static char fbsplash_theme[FB_SPLASH_THEME_LEN] __initdata = "default"; + +int fbsplash_call_helper(char* cmd, unsigned short vc) +{ + char *envp[] = { + "HOME=/", + "PATH=/sbin:/bin", + NULL + }; + + char tfb[5]; + char tcons[5]; + unsigned char fb = (int) con2fb_map[vc]; + + char *argv[] = { + fbsplash_path, + "1", + cmd, + tcons, + tfb, + (fbsplash_mode == FB_SPLASH_MODE_SILENT) ? "s" : "v", + vc_cons[vc].d->vc_splash.theme, + NULL + }; + + snprintf(tfb,5,"%d",fb); + snprintf(tcons,5,"%d",vc); + + return call_usermodehelper(fbsplash_path, argv, envp, 1); +} + +int fbsplash_verbose(void) +{ + if (fbsplash_mode == FB_SPLASH_MODE_VERBOSE) + return 0; + + fbsplash_mode = FB_SPLASH_MODE_VERBOSE; + + /* We have to do the switch from a workqueue helper, because chances are this + * function gets called from interrupt context, and we can't get hold of the + * console sem this way. */ + schedule_work(&splash_wq_verbose); + + return 1; +} + +/* Switches the first console to verbose mode. */ +static void splash_work_verbose(void *data) +{ + struct fb_info *info; + struct vc_data *vc; + + vc = vc_cons[0].d; + info = registered_fb[(int) con2fb_map[vc->vc_num]]; + + if (info == NULL) + return; + + printk(KERN_INFO "fbsplash: switching to verbose mode\n"); + + vt_cons[0]->vc_mode = KD_TEXT; + + acquire_console_sem(); + do_unblank_screen(0); + + if (info->splash.data) { + fbsplash_enable(vc); + } else { + fbsplash_disable(vc, 1); + } + release_console_sem(); +} + +/* Disables fbsplash on a virtual console; called with console sem held. */ +int fbsplash_disable(struct vc_data *vc, unsigned char redraw) +{ + struct fb_info* info; + + if (!vc->vc_splash.state) + return -EINVAL; + + info = registered_fb[(int) con2fb_map[vc->vc_num]]; + + if (info == NULL) + return -EINVAL; + + vc->vc_splash.state = 0; + vc_resize(vc->vc_num, info->var.xres / vc->vc_font.width, + info->var.yres / vc->vc_font.height); + + if (fg_console == vc->vc_num && redraw) { + redraw_screen(fg_console, 0); + update_region(fg_console, vc->vc_origin + + vc->vc_size_row * vc->vc_top, + vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); + } + + printk(KERN_INFO "fbsplash: switched splash state to 'off' on console %d\n", + vc->vc_num); + + return 0; +} + +/* Enables fbsplash on a virtual console; called with console sem held. */ +static int fbsplash_enable(struct vc_data *vc) +{ + struct fb_info* info; + + info = registered_fb[(int) con2fb_map[vc->vc_num]]; + + if (vc->vc_splash.twidth == 0 || vc->vc_splash.theight == 0 || + info == NULL || !info->splash.data || vc->vc_splash.state) + return -EINVAL; + + vc->vc_splash.state = 1; + vc_resize(vc->vc_num, vc->vc_splash.twidth / vc->vc_font.width, + vc->vc_splash.theight / vc->vc_font.height); + + if (fg_console == vc->vc_num) { + redraw_screen(fg_console, 0); + update_region(fg_console, vc->vc_origin + + vc->vc_size_row * vc->vc_top, + vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); + fbsplash_clear_margins(vc, info, 0); + } + + printk(KERN_INFO "fbsplash: switched splash state to 'on' on console %d\n", + vc->vc_num); + + return 0; +} + +static int __init splash_setup(char *options) +{ + char *this_opt; + + while ((this_opt = strsep(&options, ",")) != NULL) { + + if (!strcmp(this_opt, "silent")) { + fbsplash_mode = FB_SPLASH_MODE_SILENT; + printk(KERN_INFO "fbsplash: silent\n"); + } else if (!strcmp(this_opt, "verbose")) { + fbsplash_mode = FB_SPLASH_MODE_VERBOSE; + printk(KERN_INFO "fbsplash: verbose\n"); + } else if (!strcmp(this_opt, "off")) { + fbsplash_mode = 0; + } else if (!strncmp(this_opt, "theme:", 6)) { + strncpy(fbsplash_theme, this_opt+6, 64); + printk(KERN_INFO "fbsplash: theme %s\n", fbsplash_theme); + } else { + printk(KERN_WARNING "fbsplash: unrecognized option %s\n", this_opt); + } + } + return 0; +} + +__setup("splash=", splash_setup); + +static int splash_get_info(char *buf, char **start, off_t fpos, int length) +{ + char *p = buf; + + p += sprintf(p, "Framebuffer splash v%s, mode: %s\n", SPLASH_VERSION, + ((fbsplash_mode == 2) ? "silent" : (fbsplash_mode == 1) ? "verbose" : "off")); + + return p - buf; +} + +static inline int fbsplash_ioctl_dosetstate(struct vc_data *vc, unsigned int __user* state, unsigned char origin) +{ + int tmp, ret; + + if (get_user(tmp, state)) + return -EFAULT; + + if (origin == FB_SPLASH_IO_ORIG_USER) + acquire_console_sem(); + if (!tmp) + ret = fbsplash_disable(vc, 1); + else + ret = fbsplash_enable(vc); + if (origin == FB_SPLASH_IO_ORIG_USER) + release_console_sem(); + + return ret; +} + +static inline int fbsplash_ioctl_dogetstate(struct vc_data *vc, unsigned int __user *state) +{ + return put_user(vc->vc_splash.state, (unsigned int __user*) state); +} + +static int fbsplash_ioctl_dosetcfg(struct vc_data *vc, struct vc_splash __user *arg, unsigned char origin) +{ + struct vc_splash cfg; + struct fb_info *info; + int len; + char *tmp; + + info = registered_fb[(int) con2fb_map[vc->vc_num]]; + + if (copy_from_user(&cfg, arg, sizeof(struct vc_splash))) + return -EFAULT; + if (info == NULL || !cfg.twidth || !cfg.theight || + cfg.tx + cfg.twidth > info->var.xres || + cfg.ty + cfg.theight > info->var.yres) + return -EINVAL; + + len = strlen_user(cfg.theme); + if (!len || len > FB_SPLASH_THEME_LEN) + return -EINVAL; + tmp = kmalloc(len, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + if (copy_from_user(tmp, (void __user *)cfg.theme, len)) + return -EFAULT; + cfg.theme = tmp; + cfg.state = 0; + + /* If this ioctl is a response to a request from kernel, the console sem + * is already held; we also don't need to disable splash because either the + * new config and background picture will be successfully loaded, and the + * splash will stay on, or in case of a failure it'll be turned off in fbcon. */ + if (origin == FB_SPLASH_IO_ORIG_USER) { + acquire_console_sem(); + if (vc->vc_splash.state) + fbsplash_disable(vc, 1); + } + + if (vc->vc_splash.theme) + kfree(vc->vc_splash.theme); + + vc->vc_splash = cfg; + + if (origin == FB_SPLASH_IO_ORIG_USER) + release_console_sem(); + + printk(KERN_INFO "fbsplash: console %d using theme '%s'\n", + vc->vc_num, vc->vc_splash.theme); + return 0; +} + +static int fbsplash_ioctl_dogetcfg(struct vc_data *vc, struct vc_splash __user *arg) +{ + struct vc_splash splash; + char __user *tmp; + + if (get_user(tmp, &arg->theme)) + return -EFAULT; + + splash = vc->vc_splash; + splash.theme = tmp; + + if (vc->vc_splash.theme) { + if (copy_to_user(tmp, vc->vc_splash.theme, strlen(vc->vc_splash.theme) + 1)) + return -EFAULT; + } else + if (put_user(0, tmp)) + return -EFAULT; + + if (copy_to_user(arg, &splash, sizeof(struct vc_splash))) + return -EFAULT; + + return 0; +} + +static int fbsplash_ioctl_dosetpic(struct vc_data *vc, struct fb_image __user *arg, unsigned char origin) +{ + struct fb_image img; + struct fb_info *info; + int len; + u8 *tmp; + + if (vc->vc_num != fg_console) + return -EINVAL; + + info = registered_fb[(int) con2fb_map[vc->vc_num]]; + + if (info == NULL) + return -EINVAL; + + if (copy_from_user(&img, arg, sizeof(struct fb_image))) + return -EFAULT; + + if (img.width != info->var.xres || img.height != info->var.yres) { + printk(KERN_ERR "fbsplash: picture dimensions mismatch\n"); + return -EINVAL; + } + + if (img.depth != info->var.bits_per_pixel) { + printk(KERN_ERR "fbsplash: picture depth mismatch\n"); + return -EINVAL; + } + + if (img.depth == 8) { + if (!img.cmap.len || !img.cmap.red || !img.cmap.green || + !img.cmap.blue) + return -EINVAL; + + tmp = vmalloc(img.cmap.len * 3 * 2); + if (!tmp) + return -ENOMEM; + + if (copy_from_user(tmp, (void __user*)img.cmap.red, img.cmap.len * 2) || + copy_from_user(tmp + (img.cmap.len << 1), + (void __user*)img.cmap.green, (img.cmap.len << 1)) || + copy_from_user(tmp + (img.cmap.len << 2), + (void __user*)img.cmap.blue, (img.cmap.len << 1))) { + vfree(tmp); + return -EFAULT; + } + + img.cmap.transp = NULL; + img.cmap.red = (u16*)tmp; + img.cmap.green = img.cmap.red + img.cmap.len; + img.cmap.blue = img.cmap.green + img.cmap.len; + } else { + img.cmap.red = NULL; + } + + len = ((img.depth + 7) >> 3) * img.width * img.height; + tmp = vmalloc(len); + + if (!tmp) + goto out; + + if (copy_from_user(tmp, (void __user*)img.data, len)) + goto out; + + img.data = tmp; + + /* If this ioctl is a response to a request from kernel, the console sem + * is already held. */ + if (origin == FB_SPLASH_IO_ORIG_USER) + acquire_console_sem(); + + if (info->splash.data) + vfree((u8*)info->splash.data); + if (info->splash.cmap.red) + vfree(info->splash.cmap.red); + + info->splash = img; + + if (origin == FB_SPLASH_IO_ORIG_USER) + release_console_sem(); + + return 0; + +out: if (img.cmap.red) + vfree(img.cmap.red); + if (tmp) + vfree(tmp); + return -ENOMEM; +} + +static int splash_ioctl(struct inode * inode, struct file *filp, u_int cmd, + u_long arg) +{ + struct fb_splash_iowrapper __user *wrapper = (void __user*) arg; + struct vc_data *vc = NULL; + unsigned short vc_num = 0; + unsigned char origin = 0; + void __user *data = NULL; + + if (cmd != FBIOSPLASH_SETMODE && cmd != FBIOSPLASH_GETMODE) + { + if (verify_area(VERIFY_READ, wrapper, + sizeof(struct fb_splash_iowrapper))) + return -EFAULT; + + __get_user(vc_num, &wrapper->vc); + __get_user(origin, &wrapper->origin); + __get_user(data, &wrapper->data); + + if (!vc_cons_allocated(vc_num)) + return -EINVAL; + + vc = vc_cons[vc_num].d; + } + + switch (cmd) { + + case FBIOSPLASH_SETMODE: + if (arg != FB_SPLASH_MODE_SILENT && arg != FB_SPLASH_MODE_VERBOSE) + return -EINVAL; + + if (fbsplash_mode == arg) + return 0; + + fbsplash_mode = arg; + if (arg == FB_SPLASH_MODE_VERBOSE) + splash_work_verbose(NULL); + return 0; + + case FBIOSPLASH_GETMODE: + return put_user(fbsplash_mode, (unsigned int __user *)arg); + case FBIOSPLASH_SETPIC: + return fbsplash_ioctl_dosetpic(vc, (struct fb_image __user*)data, origin); + case FBIOSPLASH_SETCFG: + return fbsplash_ioctl_dosetcfg(vc, (struct vc_splash*)data, origin); + case FBIOSPLASH_GETCFG: + return fbsplash_ioctl_dogetcfg(vc, (struct vc_splash*)data); + case FBIOSPLASH_SETSTATE: + return fbsplash_ioctl_dosetstate(vc, (unsigned int *)data, origin); + case FBIOSPLASH_GETSTATE: + return fbsplash_ioctl_dogetstate(vc, (unsigned int *)data); + default: + return -ENOIOCTLCMD; + } +} + +static struct file_operations splash_ops = { + .owner = THIS_MODULE, + .ioctl = splash_ioctl +}; + +static struct miscdevice splash_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "fbsplash", + .fops = &splash_ops +}; + +int fbsplash_init(void) +{ + struct proc_dir_entry *splash_proc; + struct fb_info *info; + struct vc_data *vc; + int i; + + vc = vc_cons[0].d; + info = registered_fb[0]; + + for (i = 0; i < num_registered_fb; i++) { + registered_fb[i]->splash.data = NULL; + registered_fb[i]->splash.cmap.red = NULL; + } + + for (i = 0; i < MAX_NR_CONSOLES && vc_cons[i].d; i++) { + vc_cons[i].d->vc_splash.state = vc_cons[i].d->vc_splash.twidth = + vc_cons[i].d->vc_splash.theight = 0; + vc_cons[i].d->vc_splash.theme = NULL; + } + + i = misc_register(&splash_dev); + if (i) { + printk(KERN_ERR "fbsplash: failed to register device\n"); + return i; + } + +#ifdef CONFIG_PROC_FS + splash_proc = create_proc_info_entry("fbsplash", 0, NULL, splash_get_info); + if (splash_proc) + splash_proc->owner = THIS_MODULE; +#endif + + if (fbsplash_mode && info) { + vc->vc_splash.theme = kmalloc((strlen(fbsplash_theme)+1) * sizeof(char), GFP_KERNEL); + strcpy(vc->vc_splash.theme, fbsplash_theme); + if (fbsplash_call_helper("init", 0)) + fbsplash_mode = FB_SPLASH_MODE_VERBOSE; + } else { + fbsplash_mode = FB_SPLASH_MODE_VERBOSE; + } + + return 0; +} + +EXPORT_SYMBOL(fbsplash_path); diff -Nru a/drivers/video/fbsplash.h b/drivers/video/fbsplash.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/video/fbsplash.h 2004-08-23 11:56:23 +02:00 @@ -0,0 +1,69 @@ +/* + * linux/drivers/video/fbsplash.h -- Framebuffer splash headers + * + * Copyright (C) 2004 Michal Januszewski + * + */ + +#ifndef __FB_SPLASH_H +#define __FB_SPLASH_H + +struct fb_cursor; +struct fb_info; +struct vc_data; + +#ifdef CONFIG_FB_SPLASH +/* fbsplash.c */ +int fbsplash_init(void); +int fbsplash_verbose(void); +int fbsplash_call_helper(char* cmd, unsigned short cons); +int fbsplash_disable(struct vc_data *vc, unsigned char redraw); + +/* cfbsplash.c */ +void fbsplash_putcs(struct vc_data *vc, struct fb_info *info, const unsigned short *s, int count, int yy, int xx); +void fbsplash_putc(struct vc_data *vc, struct fb_info *info, int c, int ypos, int xpos); +void fbsplash_cursor(struct fb_info *info, struct fb_cursor *cursor); +void fbsplash_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width); +void fbsplash_clear_margins(struct vc_data *vc, struct fb_info *info, int bottom_only); +void fbsplash_blank(struct vc_data *vc, struct fb_info *info, int blank); +void fbsplash_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width); +void fbsplash_copy(u8 *dst, u8 *src, int height, int width, int linebytes, int bpp); + +/* vt.c */ +void acquire_console_sem(void); +void release_console_sem(void); +void do_unblank_screen(int entering_gfx); + +extern int fbsplash_mode; +#define fbsplash_isverbose (fbsplash_mode == FB_SPLASH_MODE_VERBOSE) + +/* struct vc_data *y */ +#define fbsplash_active_vc(y) (y->vc_splash.state && y->vc_splash.theme) + +/* struct fb_info *x, struct vc_data *y */ +#define fbsplash_active(x,y) (x->splash.data && fbsplash_active_vc(y) && \ + x->splash.width == x->var.xres && \ + x->splash.height == x->var.yres && \ + x->splash.depth == x->var.bits_per_pixel) + +#else /* CONFIG_FB_SPLASH */ + +static inline void fbsplash_putcs(struct vc_data *vc, struct fb_info *info, const unsigned short *s, int count, int yy, int xx) {} +static inline void fbsplash_putc(struct vc_data *vc, struct fb_info *info, int c, int ypos, int xpos) {} +static inline void fbsplash_cursor(struct fb_info *info, struct fb_cursor *cursor) {} +static inline void fbsplash_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width) {} +static inline void fbsplash_clear_margins(struct vc_data *vc, struct fb_info *info, int bottom_only) {} +static inline void fbsplash_blank(struct vc_data *vc, struct fb_info *info, int blank) {} +static inline void fbsplash_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width) {} +static inline int fbsplash_call_helper(char* cmd, unsigned short cons) { return 0; } +static inline int fbsplash_init(void) { return 0; } +static inline int fbsplash_verbose(void) { return 0; } +static inline int fbsplash_disable(struct vc_data *vc, unsigned char redraw) { return 0; } + +#define fbsplash_isverbose (0) +#define fbsplash_active_vc(y) (0) +#define fbsplash_active(x,y) (0) + +#endif /* CONFIG_FB_SPLASH */ + +#endif /* __FB_SPLASH_H */ diff -Nru a/include/linux/console_splash.h b/include/linux/console_splash.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/include/linux/console_splash.h 2004-08-23 11:56:23 +02:00 @@ -0,0 +1,13 @@ +#ifndef _LINUX_CONSOLE_SPLASH_H_ +#define _LINUX_CONSOLE_SPLASH_H_ 1 + +/* A structure used by the framebuffer splash code (drivers/video/fbsplash.c) */ +struct vc_splash { + __u8 bg_color; /* The color that is to be treated as transparent */ + __u8 state; /* Current splash state: 0 = off, 1 = on */ + __u16 tx, ty; /* Top left corner coordinates of the text field */ + __u16 twidth, theight; /* Width and height of the text field */ + char* theme; +}; + +#endif diff -Nru a/include/linux/console_struct.h b/include/linux/console_struct.h --- a/include/linux/console_struct.h 2004-08-23 11:56:23 +02:00 +++ b/include/linux/console_struct.h 2004-08-23 11:56:23 +02:00 @@ -10,6 +10,7 @@ */ #define NPAR 16 +#include struct vc_data { unsigned short vc_num; /* Console number */ @@ -87,6 +88,8 @@ struct vc_data **vc_display_fg; /* [!] Ptr to var holding fg console for this display */ unsigned long vc_uni_pagedir; unsigned long *vc_uni_pagedir_loc; /* [!] Location of uni_pagedir variable for this console */ + + struct vc_splash vc_splash; /* additional information is in vt_kern.h */ }; diff -Nru a/include/linux/fb.h b/include/linux/fb.h --- a/include/linux/fb.h 2004-08-23 11:56:23 +02:00 +++ b/include/linux/fb.h 2004-08-23 11:56:23 +02:00 @@ -8,6 +8,13 @@ #define FB_MAJOR 29 #define FB_MAX 32 /* sufficient for now */ +struct fb_splash_iowrapper +{ + unsigned short vc; /* Virtual console */ + unsigned char origin; /* Point of origin of the request */ + void *data; +}; + /* ioctls 0x46 is 'F' */ #define FBIOGET_VSCREENINFO 0x4600 @@ -35,7 +42,19 @@ #define FBIOGET_HWCINFO 0x4616 #define FBIOPUT_MODEINFO 0x4617 #define FBIOGET_DISPINFO 0x4618 - +#define FBIOSPLASH_SETCFG _IOWR('F', 0x19, struct fb_splash_iowrapper) +#define FBIOSPLASH_GETCFG _IOR('F', 0x1A, struct fb_splash_iowrapper) +#define FBIOSPLASH_SETSTATE _IOWR('F', 0x1B, struct fb_splash_iowrapper) +#define FBIOSPLASH_GETSTATE _IOR('F', 0x1C, struct fb_splash_iowrapper) +#define FBIOSPLASH_SETMODE _IOWR('F', 0x1D, unsigned int) +#define FBIOSPLASH_GETMODE _IOR('F', 0x1E, unsigned int) +#define FBIOSPLASH_SETPIC _IOWR('F', 0x1F, struct fb_splash_iowrapper) + +#define FB_SPLASH_MODE_VERBOSE 1 /* Verbose mode */ +#define FB_SPLASH_MODE_SILENT 2 /* Silent mode */ +#define FB_SPLASH_THEME_LEN 128 /* Maximum lenght of a theme name */ +#define FB_SPLASH_IO_ORIG_KERNEL 0 /* Kernel ioctl origin */ +#define FB_SPLASH_IO_ORIG_USER 1 /* User ioctl origin */ #define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ #define FB_TYPE_PLANES 1 /* Non interleaved planes */ @@ -588,6 +607,8 @@ #define FBINFO_STATE_RUNNING 0 #define FBINFO_STATE_SUSPENDED 1 u32 state; /* Hardware state i.e suspend */ + + struct fb_image splash; /* From here on everything is device dependent */ void *par; diff -Nru a/include/linux/sysctl.h b/include/linux/sysctl.h --- a/include/linux/sysctl.h 2004-08-23 11:56:23 +02:00 +++ b/include/linux/sysctl.h 2004-08-23 11:56:23 +02:00 @@ -133,6 +133,7 @@ KERN_NGROUPS_MAX=63, /* int: NGROUPS_MAX */ KERN_SPARC_SCONS_PWROFF=64, /* int: serial console power-off halt */ KERN_HZ_TIMER=65, /* int: hz timer on or off */ + KERN_FBSPLASH=66, /* string: path to fbsplash helper */ }; diff -Nru a/kernel/panic.c b/kernel/panic.c --- a/kernel/panic.c 2004-08-23 11:56:23 +02:00 +++ b/kernel/panic.c 2004-08-23 11:56:23 +02:00 @@ -19,6 +19,7 @@ #include #include #include +#include "../drivers/video/fbsplash.h" int panic_timeout; int panic_on_oops; @@ -82,6 +83,8 @@ * We can't use the "normal" timers since we just panicked.. */ printk(KERN_EMERG "Rebooting in %d seconds..",panic_timeout); + fbsplash_verbose(); + for (i = 0; i < panic_timeout; i++) { touch_nmi_watchdog(); mdelay(1000); @@ -105,6 +108,8 @@ disabled_wait(caller); #endif local_irq_enable(); + fbsplash_verbose(); + for (;;) ; } diff -Nru a/kernel/sysctl.c b/kernel/sysctl.c --- a/kernel/sysctl.c 2004-08-23 11:56:23 +02:00 +++ b/kernel/sysctl.c 2004-08-23 11:56:23 +02:00 @@ -77,6 +77,9 @@ #ifdef CONFIG_HOTPLUG extern char hotplug_path[]; #endif +#ifdef CONFIG_FB_SPLASH +extern char fbsplash_path[]; +#endif #ifdef CONFIG_CHR_DEV_SG extern int sg_big_buff; #endif @@ -403,6 +406,17 @@ .ctl_name = KERN_HOTPLUG, .procname = "hotplug", .data = &hotplug_path, + .maxlen = KMOD_PATH_LEN, + .mode = 0644, + .proc_handler = &proc_dostring, + .strategy = &sysctl_string, + }, +#endif +#ifdef CONFIG_FB_SPLASH + { + .ctl_name = KERN_FBSPLASH, + .procname = "fbsplash", + .data = &fbsplash_path, .maxlen = KMOD_PATH_LEN, .mode = 0644, .proc_handler = &proc_dostring,