Created
December 5, 2025 23:27
-
-
Save Doridian/452d96dca13a32549f98331203492730 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| From 013b0a05fb674a0de1986f10af15ee27789e367b Mon Sep 17 00:00:00 2001 | |
| From: Doridian <git@doridian.net> | |
| Date: Sat, 2 Aug 2025 11:21:22 -0700 | |
| Subject: [PATCH] Port fbcondecor patchset to 6.16 | |
| --- | |
| Documentation/fb/fbcondecor.txt | 207 ++++++++++ | |
| drivers/Makefile | 9 +- | |
| drivers/video/console/Kconfig | 13 + | |
| drivers/video/console/Makefile | 2 + | |
| drivers/video/console/cfbcondecor.c | 488 ++++++++++++++++++++++++ | |
| drivers/video/console/fbcondecor.c | 551 +++++++++++++++++++++++++++ | |
| drivers/video/console/fbcondecor.h | 71 ++++ | |
| drivers/video/fbdev/core/bitblit.c | 20 +- | |
| drivers/video/fbdev/core/fb_chrdev.c | 9 - | |
| drivers/video/fbdev/core/fbcmap.c | 9 +- | |
| drivers/video/fbdev/core/fbcon.c | 163 +++++++- | |
| include/linux/console_decor.h | 46 +++ | |
| include/linux/console_struct.h | 2 + | |
| include/linux/fb.h | 31 ++ | |
| include/uapi/linux/fb.h | 59 +++ | |
| kernel/sysctl.c | 14 + | |
| 16 files changed, 1654 insertions(+), 40 deletions(-) | |
| create mode 100644 Documentation/fb/fbcondecor.txt | |
| create mode 100644 drivers/video/console/cfbcondecor.c | |
| create mode 100644 drivers/video/console/fbcondecor.c | |
| create mode 100644 drivers/video/console/fbcondecor.h | |
| create mode 100644 include/linux/console_decor.h | |
| diff --git a/Documentation/fb/fbcondecor.txt b/Documentation/fb/fbcondecor.txt | |
| new file mode 100644 | |
| index 00000000000000..637209e11ccd90 | |
| --- /dev/null | |
| +++ b/Documentation/fb/fbcondecor.txt | |
| @@ -0,0 +1,207 @@ | |
| +What is it? | |
| +----------- | |
| + | |
| +The framebuffer decorations are a kernel feature which allows displaying a | |
| +background picture on selected consoles. | |
| + | |
| +What do I need to get it to work? | |
| +--------------------------------- | |
| + | |
| +To get fbcondecor up-and-running you will have to: | |
| + 1) get a copy of splashutils [1] or a similar program | |
| + 2) get some fbcondecor themes | |
| + 3) build the kernel helper program | |
| + 4) build your kernel with the FB_CON_DECOR option enabled. | |
| + | |
| +To get fbcondecor 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. | |
| + | |
| +[1] The splashutils package can be downloaded from: | |
| + https://github.com/alanhaggai/fbsplash | |
| + | |
| +The userspace helper | |
| +-------------------- | |
| + | |
| +The userspace fbcondecor helper (by default: /sbin/fbcondecor_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 video | |
| +mode switches (the kernel requests background images and configuration | |
| +parameters for the current console). The fbcondecor helper must be accessible at | |
| +all times. If it's not, fbcondecor will be switched off automatically. | |
| + | |
| +It's possible to set path to the fbcondecor helper by writing it to | |
| +/proc/sys/kernel/fbcondecor. | |
| + | |
| +***************************************************************************** | |
| + | |
| +The information below is mostly technical stuff. There's probably no need to | |
| +read it unless you plan to develop a userspace helper. | |
| + | |
| +The fbcondecor protocol | |
| +----------------------- | |
| + | |
| +The fbcondecor protocol defines a communication interface between the kernel and | |
| +the userspace fbcondecor helper. | |
| + | |
| +The kernel side is responsible for: | |
| + | |
| + * rendering console text, using an image as a background (instead of a | |
| + standard solid color fbcon uses), | |
| + * accepting commands from the user via ioctls on the fbcondecor device, | |
| + * 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 fbcondecor 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 to be done in a series of arguments. | |
| + | |
| +The arguments follow the pattern: | |
| +<fbcondecor protocol version> <command> <parameters> | |
| + | |
| +All commands defined in fbcondecor protocol v2 have the following parameters: | |
| + virtual console | |
| + framebuffer number | |
| + theme | |
| + | |
| +Fbcondecor protocol v1 specified an additional 'fbcondecor mode' after the | |
| +framebuffer number. Fbcondecor protocol v1 is deprecated and should not be used. | |
| + | |
| +Fbcondecor protocol v2 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 FBIOCONDECOR_SETPIC ioctl. | |
| + | |
| +init | |
| +---- | |
| + The kernel issues this command after the fbcondecor device is created and | |
| + the fbcondecor interface is initialized. Upon receiving 'init', the userspace | |
| + helper should parse the kernel command line (/proc/cmdline) or otherwise | |
| + decide whether fbcondecor is to be activated. | |
| + | |
| + To activate fbcondecor on the first console the helper should issue the | |
| + FBIOCONDECOR_SETCFG, FBIOCONDECOR_SETPIC and FBIOCONDECOR_SETSTATE commands, | |
| + in the above-mentioned order. | |
| + | |
| + 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, fbcondecor and tty0 devices (if they don't already exist) to get | |
| + current display settings and to be able to communicate with the kernel side. | |
| + It should probably also mount the procfs to be able to parse the kernel | |
| + command line parameters. | |
| + | |
| + Note that the console sem is not held when the kernel calls fbcondecor_helper | |
| + with the 'init' command. The fbcondecor helper should perform all ioctls with | |
| + origin set to FBCON_DECOR_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 | |
| + FBCON_DECOR_IO_ORIG_KERNEL. | |
| + | |
| + | |
| +Userspace -> Kernel | |
| +------------------- | |
| + | |
| +Userspace programs can communicate with fbcondecor via ioctls on the | |
| +fbcondecor 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 fbcondecor helper should set the origin field to FBCON_DECOR_IO_ORIG_KERNEL | |
| +when doing the appropriate ioctls. All userspace configuration tools should | |
| +use FBCON_DECOR_IO_ORIG_USER. Failure to set the appropriate value in the origin | |
| +field when performing ioctls from the kernel helper will most likely result | |
| +in a console deadlock. | |
| + | |
| +FBCON_DECOR_IO_ORIG_KERNEL instructs fbcondecor not to try to acquire the console | |
| +semaphore. Not surprisingly, FBCON_DECOR_IO_ORIG_USER instructs it to acquire | |
| +the console sem. | |
| + | |
| +The framebuffer console decoration provides the following ioctls (all defined in | |
| +linux/fb.h): | |
| + | |
| +FBIOCONDECOR_SETPIC | |
| +description: loads a background picture for a virtual console | |
| +argument: struct fbcon_decor_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. | |
| + | |
| +FBIOCONDECOR_SETCFG | |
| +description: sets the fbcondecor config for a virtual console | |
| +argument: struct fbcon_decor_iowrapper*; data: struct vc_decor* | |
| +notes: The structure has to be filled with valid data. | |
| + | |
| +FBIOCONDECOR_GETCFG | |
| +description: gets the fbcondecor config for a virtual console | |
| +argument: struct fbcon_decor_iowrapper*; data: struct vc_decor* | |
| + | |
| +FBIOCONDECOR_SETSTATE | |
| +description: sets the fbcondecor state for a virtual console | |
| +argument: struct fbcon_decor_iowrapper*; data: unsigned int* | |
| + values: 0 = disabled, 1 = enabled. | |
| + | |
| +FBIOCONDECOR_GETSTATE | |
| +description: gets the fbcondecor state for a virtual console | |
| +argument: struct fbcon_decor_iowrapper*; data: unsigned int* | |
| + values: as in FBIOCONDECOR_SETSTATE | |
| + | |
| +Info on used structures: | |
| + | |
| +Definition of struct vc_decor can be found in linux/console_decor.h. It's | |
| +heavily commented. Note that the 'theme' field should point to a string | |
| +no longer than FBCON_DECOR_THEME_LEN. When FBIOCONDECOR_GETCFG call is | |
| +performed, the theme field should point to a char buffer of length | |
| +FBCON_DECOR_THEME_LEN. | |
| + | |
| +Definition of struct fbcon_decor_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 | |
| +fbcondecor helper should set this field to FBCON_DECOR_IO_ORIG_KERNEL, userspace | |
| +programs should set it to FBCON_DECOR_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. | |
| + | |
| +***************************************************************************** | |
| + | |
| +Credit | |
| +------ | |
| + | |
| +Original 'bootsplash' project & implementation by: | |
| + Volker Poplawski <volker@poplawski.de>, Stefan Reinauer <stepan@suse.de>, | |
| + Steffen Winterfeldt <snwint@suse.de>, Michael Schroeder <mls@suse.de>, | |
| + Ken Wimer <wimer@suse.de>. | |
| + | |
| +Fbcondecor, fbcondecor protocol design, current implementation & docs by: | |
| + Michal Januszewski <michalj+fbcondecor@gmail.com> | |
| + | |
| diff --git a/drivers/Makefile b/drivers/Makefile | |
| index b5749cf67044ce..d57aaf6f66452a 100644 | |
| --- a/drivers/Makefile | |
| +++ b/drivers/Makefile | |
| @@ -24,6 +24,10 @@ obj-y += pci/ | |
| obj-$(CONFIG_PARISC) += parisc/ | |
| obj-$(CONFIG_RAPIDIO) += rapidio/ | |
| +# tty/ comes before char/ so that the VT console is the boot-time | |
| +# default. | |
| +obj-y += tty/ | |
| +obj-y += char/ | |
| obj-y += video/ | |
| obj-y += idle/ | |
| @@ -56,11 +60,6 @@ obj-$(CONFIG_REGULATOR) += regulator/ | |
| # reset controllers early, since gpu drivers might rely on them to initialize | |
| obj-$(CONFIG_RESET_CONTROLLER) += reset/ | |
| -# tty/ comes before char/ so that the VT console is the boot-time | |
| -# default. | |
| -obj-y += tty/ | |
| -obj-y += char/ | |
| - | |
| # iommu/ comes before gpu as gpu are using iommu controllers | |
| obj-y += iommu/ | |
| diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig | |
| index 12f54480f57f89..4b8b8034beb4ee 100644 | |
| --- a/drivers/video/console/Kconfig | |
| +++ b/drivers/video/console/Kconfig | |
| @@ -137,6 +137,19 @@ config FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER | |
| by the firmware in place, rather then replacing the contents with a | |
| black screen as soon as fbcon loads. | |
| +config FB_CON_DECOR | |
| + bool "Support for the Framebuffer Console Decorations" | |
| + depends on FRAMEBUFFER_CONSOLE=y && !FB_TILEBLITTING | |
| + default n | |
| + help | |
| + This option enables support for framebuffer console decorations which | |
| + makes it possible to display images in the background of the system | |
| + consoles. Note that userspace utilities are necessary in order to take | |
| + advantage of these features. Refer to Documentation/fb/fbcondecor.txt | |
| + for more information. | |
| + | |
| + If unsure, say N. | |
| + | |
| config STI_CONSOLE | |
| bool "STI text console" | |
| depends on PARISC && HAS_IOMEM | |
| diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile | |
| index fd79016a0d95b4..35ba21c61ad4c1 100644 | |
| --- a/drivers/video/console/Makefile | |
| +++ b/drivers/video/console/Makefile | |
| @@ -8,3 +8,5 @@ obj-$(CONFIG_SGI_NEWPORT_CONSOLE) += newport_con.o | |
| obj-$(CONFIG_STI_CONSOLE) += sticon.o | |
| obj-$(CONFIG_VGA_CONSOLE) += vgacon.o | |
| obj-$(CONFIG_MDA_CONSOLE) += mdacon.o | |
| + | |
| +obj-$(CONFIG_FB_CON_DECOR) += fbcondecor.o cfbcondecor.o | |
| diff --git a/drivers/video/console/cfbcondecor.c b/drivers/video/console/cfbcondecor.c | |
| new file mode 100644 | |
| index 00000000000000..ea1c76cd800a78 | |
| --- /dev/null | |
| +++ b/drivers/video/console/cfbcondecor.c | |
| @@ -0,0 +1,488 @@ | |
| +/* | |
| + * linux/drivers/video/cfbcon_decor.c -- Framebuffer decor render functions | |
| + * | |
| + * Copyright (C) 2004 Michal Januszewski <michalj+fbcondecor@gmail.com> | |
| + * | |
| + * Code based upon "Bootdecor" (C) 2001-2003 | |
| + * Volker Poplawski <volker@poplawski.de>, | |
| + * Stefan Reinauer <stepan@suse.de>, | |
| + * Steffen Winterfeldt <snwint@suse.de>, | |
| + * Michael Schroeder <mls@suse.de>, | |
| + * Ken Wimer <wimer@suse.de>. | |
| + * | |
| + * 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 <linux/module.h> | |
| +#include <linux/types.h> | |
| +#include <linux/fb.h> | |
| +#include <linux/selection.h> | |
| +#include <linux/slab.h> | |
| +#include <linux/vt_kern.h> | |
| +#include <asm/irq.h> | |
| + | |
| +#include "../fbdev/core/fbcon.h" | |
| +#include "../drm/drm_fb_helper.h" | |
| +#include "fbcondecor.h" | |
| + | |
| +#ifndef CONFIG_DRM_FBDEV_EMULATION | |
| +static inline void drm_fb_helper_damage_area(struct fb_info *info, u32 x, u32 y, u32 width, u32 height) { } | |
| +#endif | |
| + | |
| +#define parse_pixel(shift, bpp, type) \ | |
| + do { \ | |
| + if (d & (0x80 >> (shift))) \ | |
| + dd2[(shift)] = fgx; \ | |
| + else \ | |
| + dd2[(shift)] = transparent ? *(type *)decor_src : bgx; \ | |
| + decor_src += (bpp); \ | |
| + } while (0) \ | |
| + | |
| +extern int get_color(struct vc_data *vc, struct fb_info *info, | |
| + u16 c, int is_fg); | |
| + | |
| +void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc) | |
| +{ | |
| + int i, j, k; | |
| + int minlen = min(min(info->var.red.length, info->var.green.length), | |
| + info->var.blue.length); | |
| + u32 col; | |
| + | |
| + for (j = i = 0; i < 16; i++) { | |
| + k = color_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; | |
| + } | |
| +} | |
| + | |
| +static void fbcon_decor_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; | |
| + unsigned int ds = (ypos * info->var.xres + xpos) * bytespp; | |
| + u16 dd2[4]; | |
| + | |
| + u8 *decor_src = (u8 *)(info->bgdecor.data + ds); | |
| + 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 *)decor_src : bgx; | |
| + | |
| + d <<= 1; | |
| + decor_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 *)decor_src & 0xffffff) : bgx; | |
| + | |
| + d <<= 1; | |
| + decor_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[0] << 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; | |
| + } | |
| + } | |
| + | |
| + dst += info->fix.line_length - width * bytespp; | |
| + decor_src += (info->var.xres - width) * bytespp; | |
| + } | |
| +} | |
| + | |
| +#define cc2cx(a) \ | |
| + ((info->fix.visual == FB_VISUAL_TRUECOLOR || \ | |
| + info->fix.visual == FB_VISUAL_DIRECTCOLOR) ? \ | |
| + ((u32 *)info->pseudo_palette)[a] : a) | |
| + | |
| +void fbcon_decor_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; | |
| + struct fbcon_ops *ops = info->fbcon_par; | |
| + int fg_color, bg_color, transparent; | |
| + u8 *src; | |
| + u32 bgx, fgx; | |
| + u16 c = scr_readw(s); | |
| + | |
| + fg_color = get_color(vc, info, c, 1); | |
| + bg_color = get_color(vc, info, c, 0); | |
| + | |
| + /* Don't paint the background image if console is blanked */ | |
| + transparent = ops->blank_state ? 0 : | |
| + (vc->vc_decor.bg_color == bg_color); | |
| + | |
| + xx = xx * vc->vc_font.width + vc->vc_decor.tx; | |
| + yy = yy * vc->vc_font.height + vc->vc_decor.ty; | |
| + | |
| + int sx = xx; | |
| + int sy = yy; | |
| + | |
| + 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); | |
| + | |
| + fbcon_decor_renderc(info, yy, xx, vc->vc_font.height, | |
| + vc->vc_font.width, src, fgx, bgx, transparent); | |
| + xx += vc->vc_font.width; | |
| + } | |
| + | |
| + drm_fb_helper_damage_area(info, sx, sy, xx - sx, vc->vc_font.height); | |
| +} | |
| + | |
| +void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor) | |
| +{ | |
| + int i; | |
| + unsigned int dsize, s_pitch; | |
| + struct fbcon_ops *ops = info->fbcon_par; | |
| + struct vc_data *vc; | |
| + u8 *src; | |
| + | |
| + /* we really don't need any cursors while the console is blanked */ | |
| + if (info->state != FBINFO_STATE_RUNNING || ops->blank_state) | |
| + return; | |
| + | |
| + vc = vc_cons[ops->currcon].d; | |
| + | |
| + src = kmalloc(64 + sizeof(struct fb_image), GFP_ATOMIC); | |
| + if (!src) | |
| + return; | |
| + | |
| + s_pitch = (cursor->image.width + 7) >> 3; | |
| + dsize = s_pitch * cursor->image.height; | |
| + if (cursor->enable) { | |
| + switch (cursor->rop) { | |
| + case ROP_XOR: | |
| + for (i = 0; i < dsize; i++) | |
| + src[i] = cursor->image.data[i] ^ cursor->mask[i]; | |
| + break; | |
| + case ROP_COPY: | |
| + default: | |
| + for (i = 0; i < dsize; i++) | |
| + src[i] = cursor->image.data[i] & cursor->mask[i]; | |
| + break; | |
| + } | |
| + } else | |
| + memcpy(src, cursor->image.data, dsize); | |
| + | |
| + fbcon_decor_renderc(info, | |
| + cursor->image.dy + vc->vc_decor.ty, | |
| + cursor->image.dx + vc->vc_decor.tx, | |
| + cursor->image.height, | |
| + cursor->image.width, | |
| + (u8 *)src, | |
| + cc2cx(cursor->image.fg_color), | |
| + cc2cx(cursor->image.bg_color), | |
| + cursor->image.bg_color == vc->vc_decor.bg_color); | |
| + | |
| + drm_fb_helper_damage_area( | |
| + info, | |
| + cursor->image.dx + vc->vc_decor.tx, | |
| + cursor->image.dy + vc->vc_decor.ty, | |
| + cursor->image.width, | |
| + cursor->image.height); | |
| + | |
| + kfree(src); | |
| +} | |
| + | |
| +static void decorset(struct fb_info *info, int sy, int sx, int height, int width, u32 bgx) | |
| +{ | |
| + int bpp = info->var.bits_per_pixel; | |
| + int dstbytes = info->fix.line_length; | |
| + u8 *dst = (u8 *)(info->screen_base + sy * info->fix.line_length + | |
| + sx * ((info->var.bits_per_pixel + 7) >> 3)); | |
| + | |
| + 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 | |
| + } | |
| + break; | |
| + 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; | |
| + } | |
| + | |
| + drm_fb_helper_damage_area(info, sx, sy, width, height); | |
| +} | |
| + | |
| +static void decorfill(struct fb_info *info, int sy, int sx, int height, | |
| + int width) | |
| +{ | |
| + int bytespp = ((info->var.bits_per_pixel + 7) >> 3); | |
| + int d = sy * info->fix.line_length + sx * bytespp; | |
| + int ds = (sy * info->var.xres + sx) * bytespp; | |
| + | |
| + u8 *dst = (u8 *)(info->screen_base + d); | |
| + u8 *src = (u8 *)(info->bgdecor.data + ds); | |
| + | |
| + int i; | |
| + | |
| + while (height-- > 0) { | |
| + u32 *p = (u32 *)dst; | |
| + u32 *q = (u32 *)src; | |
| + | |
| + switch (info->var.bits_per_pixel) { | |
| + | |
| + 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 += info->fix.line_length; | |
| + src += info->var.xres * bytespp; | |
| + } | |
| + | |
| + drm_fb_helper_damage_area(info, sx, sy, width, height); | |
| +} | |
| + | |
| +void fbcon_decor_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; | |
| + struct fbcon_ops *ops = info->fbcon_par; | |
| + int transparent, bg_color = attr_bgcol(bgshift, vc->vc_video_erase_char); | |
| + | |
| + transparent = (vc->vc_decor.bg_color == bg_color); | |
| + sy = sy * vc->vc_font.height + vc->vc_decor.ty; | |
| + sx = sx * vc->vc_font.width + vc->vc_decor.tx; | |
| + height *= vc->vc_font.height; | |
| + width *= vc->vc_font.width; | |
| + | |
| + /* Don't paint the background image if console is blanked */ | |
| + if (transparent && !ops->blank_state) { | |
| + decorfill(info, sy, sx, height, width); | |
| + } else { | |
| + decorset(info, sy, sx, height, width, cc2cx(bg_color)); | |
| + } | |
| +} | |
| + | |
| +void fbcon_decor_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 */ | |
| + decorfill(info, 0, 0, vc->vc_decor.ty, info->var.xres); | |
| + /* left margin */ | |
| + decorfill(info, vc->vc_decor.ty, 0, th, vc->vc_decor.tx); | |
| + /* right margin */ | |
| + decorfill(info, vc->vc_decor.ty, vc->vc_decor.tx + tw, th, | |
| + info->var.xres - vc->vc_decor.tx - tw); | |
| + } | |
| + decorfill(info, vc->vc_decor.ty + th, 0, | |
| + info->var.yres - vc->vc_decor.ty - th, info->var.xres); | |
| +} | |
| + | |
| +void fbcon_decor_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) { | |
| + fbcon_decor_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) { | |
| + fbcon_decor_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) | |
| + fbcon_decor_putcs(vc, info, start, d - start, y, x); | |
| +} | |
| + | |
| +void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank) | |
| +{ | |
| + if (blank) { | |
| + decorset(info, 0, 0, info->var.yres, info->var.xres, 0); | |
| + } else { | |
| + update_screen(vc); | |
| + fbcon_decor_clear_margins(vc, info, 0); | |
| + } | |
| +} | |
| + | |
| diff --git a/drivers/video/console/fbcondecor.c b/drivers/video/console/fbcondecor.c | |
| new file mode 100644 | |
| index 00000000000000..d70e064e226158 | |
| --- /dev/null | |
| +++ b/drivers/video/console/fbcondecor.c | |
| @@ -0,0 +1,551 @@ | |
| +/* | |
| + * linux/drivers/video/console/fbcondecor.c -- Framebuffer console decorations | |
| + * | |
| + * Copyright (C) 2004-2009 Michal Januszewski <michalj+fbcondecor@gmail.com> | |
| + * | |
| + * Code based upon "Bootsplash" (C) 2001-2003 | |
| + * Volker Poplawski <volker@poplawski.de>, | |
| + * Stefan Reinauer <stepan@suse.de>, | |
| + * Steffen Winterfeldt <snwint@suse.de>, | |
| + * Michael Schroeder <mls@suse.de>, | |
| + * Ken Wimer <wimer@suse.de>. | |
| + * | |
| + * Compat ioctl support by Thorsten Klein <TK@Thorsten-Klein.de>. | |
| + * | |
| + * 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 <linux/module.h> | |
| +#include <linux/kernel.h> | |
| +#include <linux/string.h> | |
| +#include <linux/types.h> | |
| +#include <linux/fb.h> | |
| +#include <linux/vt_kern.h> | |
| +#include <linux/vmalloc.h> | |
| +#include <linux/unistd.h> | |
| +#include <linux/syscalls.h> | |
| +#include <linux/init.h> | |
| +#include <linux/proc_fs.h> | |
| +#include <linux/workqueue.h> | |
| +#include <linux/kmod.h> | |
| +#include <linux/miscdevice.h> | |
| +#include <linux/device.h> | |
| +#include <linux/fs.h> | |
| +#include <linux/compat.h> | |
| +#include <linux/console.h> | |
| +#include <linux/binfmts.h> | |
| +#include <linux/uaccess.h> | |
| +#include <asm/irq.h> | |
| + | |
| +#include "../fbdev/core/fbcon.h" | |
| +#include "../fbdev/core/fb_internal.h" | |
| + | |
| +#include "fbcondecor.h" | |
| + | |
| +extern signed char con2fb_map[]; | |
| +static int fbcon_decor_enable(struct vc_data *vc); | |
| + | |
| +static int initialized; | |
| + | |
| +char fbcon_decor_path[KMOD_PATH_LEN] = "/sbin/fbcondecor_helper"; | |
| +EXPORT_SYMBOL(fbcon_decor_path); | |
| + | |
| +int fbcon_decor_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[] = { | |
| + fbcon_decor_path, | |
| + "2", | |
| + cmd, | |
| + tcons, | |
| + tfb, | |
| + vc_cons[vc].d->vc_decor.theme, | |
| + NULL | |
| + }; | |
| + | |
| + snprintf(tfb, 5, "%d", fb); | |
| + snprintf(tcons, 5, "%d", vc); | |
| + | |
| + return call_usermodehelper(fbcon_decor_path, argv, envp, UMH_WAIT_EXEC); | |
| +} | |
| + | |
| +/* Disables fbcondecor on a virtual console; called with console sem held. */ | |
| +int fbcon_decor_disable(struct vc_data *vc, unsigned char redraw) | |
| +{ | |
| + struct fb_info *info; | |
| + | |
| + if (!vc->vc_decor.state) | |
| + return -EINVAL; | |
| + | |
| + info = registered_fb[(int) con2fb_map[vc->vc_num]]; | |
| + | |
| + if (info == NULL) | |
| + return -EINVAL; | |
| + | |
| + vc->vc_decor.state = 0; | |
| + vc_resize(vc, info->var.xres / vc->vc_font.width, | |
| + info->var.yres / vc->vc_font.height); | |
| + | |
| + if (fg_console == vc->vc_num && redraw) { | |
| + redraw_screen(vc, 0); | |
| + update_region(vc, vc->vc_origin + | |
| + vc->vc_size_row * vc->vc_top, | |
| + vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); | |
| + } | |
| + | |
| + printk(KERN_INFO "fbcondecor: switched decor state to 'off' on console %d\n", | |
| + vc->vc_num); | |
| + | |
| + return 0; | |
| +} | |
| + | |
| +/* Enables fbcondecor on a virtual console; called with console sem held. */ | |
| +static int fbcon_decor_enable(struct vc_data *vc) | |
| +{ | |
| + struct fb_info *info; | |
| + | |
| + info = registered_fb[(int) con2fb_map[vc->vc_num]]; | |
| + | |
| + if (vc->vc_decor.twidth == 0 || vc->vc_decor.theight == 0 || | |
| + info == NULL || vc->vc_decor.state || (!info->bgdecor.data && | |
| + vc->vc_num == fg_console)) | |
| + return -EINVAL; | |
| + | |
| + vc->vc_decor.state = 1; | |
| + vc_resize(vc, vc->vc_decor.twidth / vc->vc_font.width, | |
| + vc->vc_decor.theight / vc->vc_font.height); | |
| + | |
| + if (fg_console == vc->vc_num) { | |
| + redraw_screen(vc, 0); | |
| + update_region(vc, vc->vc_origin + | |
| + vc->vc_size_row * vc->vc_top, | |
| + vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); | |
| + fbcon_decor_clear_margins(vc, info, 0); | |
| + } | |
| + | |
| + printk(KERN_INFO "fbcondecor: switched decor state to 'on' on console %d\n", | |
| + vc->vc_num); | |
| + | |
| + return 0; | |
| +} | |
| + | |
| +static inline int fbcon_decor_ioctl_dosetstate(struct vc_data *vc, unsigned int state, unsigned char origin) | |
| +{ | |
| + int ret; | |
| + | |
| + console_lock(); | |
| + if (!state) | |
| + ret = fbcon_decor_disable(vc, 1); | |
| + else | |
| + ret = fbcon_decor_enable(vc); | |
| + console_unlock(); | |
| + | |
| + return ret; | |
| +} | |
| + | |
| +static inline void fbcon_decor_ioctl_dogetstate(struct vc_data *vc, unsigned int *state) | |
| +{ | |
| + *state = vc->vc_decor.state; | |
| +} | |
| + | |
| +static int fbcon_decor_ioctl_dosetcfg(struct vc_data *vc, struct vc_decor *cfg, unsigned char origin) | |
| +{ | |
| + struct fb_info *info; | |
| + int len; | |
| + char *tmp; | |
| + | |
| + info = registered_fb[(int) con2fb_map[vc->vc_num]]; | |
| + | |
| + if (info == NULL || !cfg->twidth || !cfg->theight || | |
| + cfg->tx + cfg->twidth > info->var.xres || | |
| + cfg->ty + cfg->theight > info->var.yres) | |
| + return -EINVAL; | |
| + | |
| + len = strnlen_user(cfg->theme, MAX_ARG_STRLEN); | |
| + if (!len || len > FBCON_DECOR_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; | |
| + | |
| + console_lock(); | |
| + if (vc->vc_decor.state) | |
| + fbcon_decor_disable(vc, 1); | |
| + kfree(vc->vc_decor.theme); | |
| + vc->vc_decor = *cfg; | |
| + console_unlock(); | |
| + | |
| + printk(KERN_INFO "fbcondecor: console %d using theme '%s'\n", | |
| + vc->vc_num, vc->vc_decor.theme); | |
| + return 0; | |
| +} | |
| + | |
| +static int fbcon_decor_ioctl_dogetcfg(struct vc_data *vc, | |
| + struct vc_decor *decor) | |
| +{ | |
| + char __user *tmp; | |
| + | |
| + tmp = decor->theme; | |
| + *decor = vc->vc_decor; | |
| + decor->theme = tmp; | |
| + | |
| + if (vc->vc_decor.theme) { | |
| + if (copy_to_user(tmp, vc->vc_decor.theme, | |
| + strlen(vc->vc_decor.theme) + 1)) | |
| + return -EFAULT; | |
| + } else | |
| + if (put_user(0, tmp)) | |
| + return -EFAULT; | |
| + | |
| + return 0; | |
| +} | |
| + | |
| +static int fbcon_decor_ioctl_dosetpic(struct vc_data *vc, struct fb_image *img, | |
| + unsigned char origin) | |
| +{ | |
| + 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 (img->width != info->var.xres || img->height != info->var.yres) { | |
| + printk(KERN_ERR "fbcondecor: picture dimensions mismatch\n"); | |
| + printk(KERN_ERR "%dx%d vs %dx%d\n", img->width, img->height, | |
| + info->var.xres, info->var.yres); | |
| + return -EINVAL; | |
| + } | |
| + | |
| + if (img->depth != info->var.bits_per_pixel) { | |
| + printk(KERN_ERR "fbcondecor: 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 << 1)) || | |
| + 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; | |
| + | |
| + /* | |
| + * Allocate an additional byte so that we never go outside of the | |
| + * buffer boundaries in the rendering functions in a 24 bpp mode. | |
| + */ | |
| + tmp = vmalloc(len + 1); | |
| + | |
| + if (!tmp) | |
| + goto out; | |
| + | |
| + if (copy_from_user(tmp, (void __user *)img->data, len)) | |
| + goto out; | |
| + | |
| + img->data = tmp; | |
| + | |
| + console_lock(); | |
| + | |
| + if (info->bgdecor.data) | |
| + vfree((u8 *)info->bgdecor.data); | |
| + if (info->bgdecor.cmap.red) | |
| + vfree(info->bgdecor.cmap.red); | |
| + | |
| + info->bgdecor = *img; | |
| + | |
| + if (fbcon_decor_active_vc(vc) && fg_console == vc->vc_num) { | |
| + redraw_screen(vc, 0); | |
| + update_region(vc, vc->vc_origin + | |
| + vc->vc_size_row * vc->vc_top, | |
| + vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); | |
| + fbcon_decor_clear_margins(vc, info, 0); | |
| + } | |
| + | |
| + console_unlock(); | |
| + | |
| + return 0; | |
| + | |
| +out: | |
| + if (img->cmap.red) | |
| + vfree(img->cmap.red); | |
| + | |
| + if (tmp) | |
| + vfree(tmp); | |
| + return -ENOMEM; | |
| +} | |
| + | |
| +static long fbcon_decor_ioctl(struct file *filp, u_int cmd, u_long arg) | |
| +{ | |
| + struct fbcon_decor_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 (!access_ok(wrapper, | |
| + sizeof(struct fbcon_decor_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 FBIOCONDECOR_SETPIC: | |
| + { | |
| + struct fb_image img; | |
| + | |
| + if (copy_from_user(&img, (struct fb_image __user *)data, sizeof(struct fb_image))) | |
| + return -EFAULT; | |
| + | |
| + return fbcon_decor_ioctl_dosetpic(vc, &img, origin); | |
| + } | |
| + case FBIOCONDECOR_SETCFG: | |
| + { | |
| + struct vc_decor cfg; | |
| + | |
| + if (copy_from_user(&cfg, (struct vc_decor __user *)data, sizeof(struct vc_decor))) | |
| + return -EFAULT; | |
| + | |
| + return fbcon_decor_ioctl_dosetcfg(vc, &cfg, origin); | |
| + } | |
| + case FBIOCONDECOR_GETCFG: | |
| + { | |
| + int rval; | |
| + struct vc_decor cfg; | |
| + | |
| + if (copy_from_user(&cfg, (struct vc_decor __user *)data, sizeof(struct vc_decor))) | |
| + return -EFAULT; | |
| + | |
| + rval = fbcon_decor_ioctl_dogetcfg(vc, &cfg); | |
| + | |
| + if (copy_to_user(data, &cfg, sizeof(struct vc_decor))) | |
| + return -EFAULT; | |
| + return rval; | |
| + } | |
| + case FBIOCONDECOR_SETSTATE: | |
| + { | |
| + unsigned int state = 0; | |
| + | |
| + if (get_user(state, (unsigned int __user *)data)) | |
| + return -EFAULT; | |
| + return fbcon_decor_ioctl_dosetstate(vc, state, origin); | |
| + } | |
| + case FBIOCONDECOR_GETSTATE: | |
| + { | |
| + unsigned int state = 0; | |
| + | |
| + fbcon_decor_ioctl_dogetstate(vc, &state); | |
| + return put_user(state, (unsigned int __user *)data); | |
| + } | |
| + | |
| + default: | |
| + return -ENOIOCTLCMD; | |
| + } | |
| +} | |
| + | |
| +#ifdef CONFIG_COMPAT | |
| + | |
| +static long fbcon_decor_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |
| +{ | |
| + struct fbcon_decor_iowrapper32 __user *wrapper = (void __user *)arg; | |
| + struct vc_data *vc = NULL; | |
| + unsigned short vc_num = 0; | |
| + unsigned char origin = 0; | |
| + compat_uptr_t data_compat = 0; | |
| + void __user *data = NULL; | |
| + | |
| + if (!access_ok(wrapper, | |
| + sizeof(struct fbcon_decor_iowrapper32))) | |
| + return -EFAULT; | |
| + | |
| + __get_user(vc_num, &wrapper->vc); | |
| + __get_user(origin, &wrapper->origin); | |
| + __get_user(data_compat, &wrapper->data); | |
| + data = compat_ptr(data_compat); | |
| + | |
| + if (!vc_cons_allocated(vc_num)) | |
| + return -EINVAL; | |
| + | |
| + vc = vc_cons[vc_num].d; | |
| + | |
| + switch (cmd) { | |
| + case FBIOCONDECOR_SETPIC32: | |
| + { | |
| + struct fb_image32 img_compat; | |
| + struct fb_image img; | |
| + | |
| + if (copy_from_user(&img_compat, (struct fb_image32 __user *)data, sizeof(struct fb_image32))) | |
| + return -EFAULT; | |
| + | |
| + fb_image_from_compat(img, img_compat); | |
| + | |
| + return fbcon_decor_ioctl_dosetpic(vc, &img, origin); | |
| + } | |
| + | |
| + case FBIOCONDECOR_SETCFG32: | |
| + { | |
| + struct vc_decor32 cfg_compat; | |
| + struct vc_decor cfg; | |
| + | |
| + if (copy_from_user(&cfg_compat, (struct vc_decor32 __user *)data, sizeof(struct vc_decor32))) | |
| + return -EFAULT; | |
| + | |
| + vc_decor_from_compat(cfg, cfg_compat); | |
| + | |
| + return fbcon_decor_ioctl_dosetcfg(vc, &cfg, origin); | |
| + } | |
| + | |
| + case FBIOCONDECOR_GETCFG32: | |
| + { | |
| + int rval; | |
| + struct vc_decor32 cfg_compat; | |
| + struct vc_decor cfg; | |
| + | |
| + if (copy_from_user(&cfg_compat, (struct vc_decor32 __user *)data, sizeof(struct vc_decor32))) | |
| + return -EFAULT; | |
| + cfg.theme = compat_ptr(cfg_compat.theme); | |
| + | |
| + rval = fbcon_decor_ioctl_dogetcfg(vc, &cfg); | |
| + | |
| + vc_decor_to_compat(cfg_compat, cfg); | |
| + | |
| + if (copy_to_user((struct vc_decor32 __user *)data, &cfg_compat, sizeof(struct vc_decor32))) | |
| + return -EFAULT; | |
| + return rval; | |
| + } | |
| + | |
| + case FBIOCONDECOR_SETSTATE32: | |
| + { | |
| + compat_uint_t state_compat = 0; | |
| + unsigned int state = 0; | |
| + | |
| + if (get_user(state_compat, (compat_uint_t __user *)data)) | |
| + return -EFAULT; | |
| + | |
| + state = (unsigned int)state_compat; | |
| + | |
| + return fbcon_decor_ioctl_dosetstate(vc, state, origin); | |
| + } | |
| + | |
| + case FBIOCONDECOR_GETSTATE32: | |
| + { | |
| + compat_uint_t state_compat = 0; | |
| + unsigned int state = 0; | |
| + | |
| + fbcon_decor_ioctl_dogetstate(vc, &state); | |
| + state_compat = (compat_uint_t)state; | |
| + | |
| + return put_user(state_compat, (compat_uint_t __user *)data); | |
| + } | |
| + | |
| + default: | |
| + return -ENOIOCTLCMD; | |
| + } | |
| +} | |
| +#else | |
| + #define fbcon_decor_compat_ioctl NULL | |
| +#endif | |
| + | |
| +static struct file_operations fbcon_decor_ops = { | |
| + .owner = THIS_MODULE, | |
| + .unlocked_ioctl = fbcon_decor_ioctl, | |
| + .compat_ioctl = fbcon_decor_compat_ioctl | |
| +}; | |
| + | |
| +static struct miscdevice fbcon_decor_dev = { | |
| + .minor = MISC_DYNAMIC_MINOR, | |
| + .name = "fbcondecor", | |
| + .fops = &fbcon_decor_ops | |
| +}; | |
| + | |
| +static void fbcon_decor_reset(void) | |
| +{ | |
| + int i; | |
| + | |
| + for (i = 0; i < num_registered_fb; i++) { | |
| + registered_fb[i]->bgdecor.data = NULL; | |
| + registered_fb[i]->bgdecor.cmap.red = NULL; | |
| + } | |
| + | |
| + for (i = 0; i < MAX_NR_CONSOLES && vc_cons[i].d; i++) { | |
| + vc_cons[i].d->vc_decor.state = vc_cons[i].d->vc_decor.twidth = | |
| + vc_cons[i].d->vc_decor.theight = 0; | |
| + vc_cons[i].d->vc_decor.theme = NULL; | |
| + } | |
| +} | |
| + | |
| +int fbcon_decor_init(void) | |
| +{ | |
| + int i; | |
| + | |
| + fbcon_decor_reset(); | |
| + | |
| + if (initialized) | |
| + return 0; | |
| + | |
| + i = misc_register(&fbcon_decor_dev); | |
| + if (i) { | |
| + printk(KERN_ERR "fbcondecor: failed to register device\n"); | |
| + return i; | |
| + } | |
| + | |
| + fbcon_decor_call_helper("init", 0); | |
| + initialized = 1; | |
| + return 0; | |
| +} | |
| + | |
| +int fbcon_decor_exit(void) | |
| +{ | |
| + fbcon_decor_reset(); | |
| + return 0; | |
| +} | |
| diff --git a/drivers/video/console/fbcondecor.h b/drivers/video/console/fbcondecor.h | |
| new file mode 100644 | |
| index 00000000000000..881861a75c35f4 | |
| --- /dev/null | |
| +++ b/drivers/video/console/fbcondecor.h | |
| @@ -0,0 +1,71 @@ | |
| +/* | |
| + * linux/drivers/video/console/fbcondecor.h -- Framebuffer Console Decoration headers | |
| + * | |
| + * Copyright (C) 2004 Michal Januszewski <michalj+fbcondecor@gmail.com> | |
| + * | |
| + */ | |
| + | |
| +#ifndef __FBCON_DECOR_H | |
| +#define __FBCON_DECOR_H | |
| + | |
| +#ifndef _LINUX_FB_H | |
| +#include <linux/fb.h> | |
| +#endif | |
| + | |
| +/* This is needed for vc_cons in fbcmap.c */ | |
| +#include <linux/vt_kern.h> | |
| + | |
| +struct fb_cursor; | |
| +struct fb_info; | |
| +struct vc_data; | |
| + | |
| +#ifdef CONFIG_FB_CON_DECOR | |
| +/* fbcondecor.c */ | |
| +int fbcon_decor_init(void); | |
| +int fbcon_decor_exit(void); | |
| +int fbcon_decor_call_helper(char *cmd, unsigned short cons); | |
| +int fbcon_decor_disable(struct vc_data *vc, unsigned char redraw); | |
| + | |
| +/* cfbcondecor.c */ | |
| +void fbcon_decor_putcs(struct vc_data *vc, struct fb_info *info, const unsigned short *s, int count, int yy, int xx); | |
| +void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor); | |
| +void fbcon_decor_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width); | |
| +void fbcon_decor_clear_margins(struct vc_data *vc, struct fb_info *info, int bottom_only); | |
| +void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank); | |
| +void fbcon_decor_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width); | |
| +void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc); | |
| + | |
| +/* struct vc_data *y */ | |
| +#define fbcon_decor_active_vc(y) (y->vc_decor.state && y->vc_decor.theme) | |
| + | |
| +/* struct fb_info *x, struct vc_data *y */ | |
| +#define fbcon_decor_active_nores(x, y) (x->bgdecor.data && fbcon_decor_active_vc(y)) | |
| + | |
| +/* struct fb_info *x, struct vc_data *y */ | |
| +#define fbcon_decor_active(x, y) (fbcon_decor_active_nores(x, y) && \ | |
| + x->bgdecor.width == x->var.xres && \ | |
| + x->bgdecor.height == x->var.yres && \ | |
| + x->bgdecor.depth == x->var.bits_per_pixel) | |
| + | |
| +#else /* CONFIG_FB_CON_DECOR */ | |
| + | |
| +static inline void fbcon_decor_putcs(struct vc_data *vc, struct fb_info *info, const unsigned short *s, int count, int yy, int xx) {} | |
| +static inline void fbcon_decor_putc(struct vc_data *vc, struct fb_info *info, int c, int ypos, int xpos) {} | |
| +static inline void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor) {} | |
| +static inline void fbcon_decor_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width) {} | |
| +static inline void fbcon_decor_clear_margins(struct vc_data *vc, struct fb_info *info, int bottom_only) {} | |
| +static inline void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank) {} | |
| +static inline void fbcon_decor_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width) {} | |
| +static inline void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc) {} | |
| +static inline int fbcon_decor_call_helper(char *cmd, unsigned short cons) { return 0; } | |
| +static inline int fbcon_decor_init(void) { return 0; } | |
| +static inline int fbcon_decor_exit(void) { return 0; } | |
| +static inline int fbcon_decor_disable(struct vc_data *vc, unsigned char redraw) { return 0; } | |
| + | |
| +#define fbcon_decor_active_vc(y) (0) | |
| +#define fbcon_decor_active_nores(x, y) (0) | |
| +#define fbcon_decor_active(x, y) (0) | |
| + | |
| +#endif /* CONFIG_FB_CON_DECOR */ | |
| + | |
| +#endif /* __FBCON_DECOR_H */ | |
| diff --git a/drivers/video/fbdev/core/bitblit.c b/drivers/video/fbdev/core/bitblit.c | |
| index f9475c14f7339b..6bed5e46eacba2 100644 | |
| --- a/drivers/video/fbdev/core/bitblit.c | |
| +++ b/drivers/video/fbdev/core/bitblit.c | |
| @@ -18,6 +18,7 @@ | |
| #include <linux/console.h> | |
| #include <asm/types.h> | |
| #include "fbcon.h" | |
| +#include "../../console/fbcondecor.h" | |
| /* | |
| * Accelerated handlers. | |
| @@ -55,6 +56,13 @@ static void bit_bmove(struct vc_data *vc, struct fb_info *info, int sy, | |
| area.height = height * vc->vc_font.height; | |
| area.width = width * vc->vc_font.width; | |
| + if (fbcon_decor_active(info, vc)) { | |
| + area.sx += vc->vc_decor.tx; | |
| + area.sy += vc->vc_decor.ty; | |
| + area.dx += vc->vc_decor.tx; | |
| + area.dy += vc->vc_decor.ty; | |
| + } | |
| + | |
| info->fbops->fb_copyarea(info, &area); | |
| } | |
| @@ -363,11 +371,15 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info, bool enable, | |
| cursor.image.depth = 1; | |
| cursor.rop = ROP_XOR; | |
| - if (info->fbops->fb_cursor) | |
| - err = info->fbops->fb_cursor(info, &cursor); | |
| + if (fbcon_decor_active(info, vc)) { | |
| + fbcon_decor_cursor(info, &cursor); | |
| + } else { | |
| + if (info->fbops->fb_cursor) | |
| + err = info->fbops->fb_cursor(info, &cursor); | |
| - if (err) | |
| - soft_cursor(info, &cursor); | |
| + if (err) | |
| + soft_cursor(info, &cursor); | |
| + } | |
| ops->cursor_reset = 0; | |
| } | |
| diff --git a/drivers/video/fbdev/core/fb_chrdev.c b/drivers/video/fbdev/core/fb_chrdev.c | |
| index 4ebd16b7e3b8d6..a8ad512209572b 100644 | |
| --- a/drivers/video/fbdev/core/fb_chrdev.c | |
| +++ b/drivers/video/fbdev/core/fb_chrdev.c | |
| @@ -187,15 +187,6 @@ struct fb_fix_screeninfo32 { | |
| u16 reserved[3]; | |
| }; | |
| -struct fb_cmap32 { | |
| - u32 start; | |
| - u32 len; | |
| - compat_caddr_t red; | |
| - compat_caddr_t green; | |
| - compat_caddr_t blue; | |
| - compat_caddr_t transp; | |
| -}; | |
| - | |
| static int fb_getput_cmap(struct fb_info *info, unsigned int cmd, | |
| unsigned long arg) | |
| { | |
| diff --git a/drivers/video/fbdev/core/fbcmap.c b/drivers/video/fbdev/core/fbcmap.c | |
| index ff09e57f3c3801..0bc40d793e1502 100644 | |
| --- a/drivers/video/fbdev/core/fbcmap.c | |
| +++ b/drivers/video/fbdev/core/fbcmap.c | |
| @@ -17,6 +17,8 @@ | |
| #include <linux/slab.h> | |
| #include <linux/uaccess.h> | |
| +#include "../../console/fbcondecor.h" | |
| + | |
| static u16 red2[] __read_mostly = { | |
| 0x0000, 0xaaaa | |
| }; | |
| @@ -258,9 +260,12 @@ int fb_set_cmap(struct fb_cmap *cmap, struct fb_info *info) | |
| break; | |
| } | |
| } | |
| - if (rc == 0) | |
| + if (rc == 0) { | |
| fb_copy_cmap(cmap, &info->cmap); | |
| - | |
| + if (fbcon_decor_active(info, vc_cons[fg_console].d) && | |
| + info->fix.visual == FB_VISUAL_DIRECTCOLOR) | |
| + fbcon_decor_fix_pseudo_pal(info, vc_cons[fg_console].d); | |
| + } | |
| return rc; | |
| } | |
| diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c | |
| index 2df48037688d1d..9b06dc67854f55 100644 | |
| --- a/drivers/video/fbdev/core/fbcon.c | |
| +++ b/drivers/video/fbdev/core/fbcon.c | |
| @@ -82,6 +82,8 @@ | |
| #include "fbcon.h" | |
| #include "fb_internal.h" | |
| +#include "../../console/fbcondecor.h" | |
| + | |
| /* | |
| * FIXME: Locking | |
| * | |
| @@ -112,7 +114,7 @@ static int fbcon_num_registered_fb; | |
| for (i = 0; WARN_CONSOLE_UNLOCKED(), i < FB_MAX; i++) \ | |
| if (!fbcon_registered_fb[i]) {} else | |
| -static signed char con2fb_map[MAX_NR_CONSOLES]; | |
| +signed char con2fb_map[MAX_NR_CONSOLES]; | |
| static signed char con2fb_map_boot[MAX_NR_CONSOLES]; | |
| static struct fb_info *fbcon_info_from_console(int console) | |
| @@ -296,7 +298,7 @@ static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info) | |
| vc->vc_mode != KD_TEXT || ops->graphics || fbcon_skip_panic(info)); | |
| } | |
| -static int get_color(struct vc_data *vc, struct fb_info *info, | |
| +int get_color(struct vc_data *vc, struct fb_info *info, | |
| u16 c, int is_fg) | |
| { | |
| int depth = fb_get_color_depth(&info->var, &info->fix); | |
| @@ -559,6 +561,9 @@ static int do_fbcon_takeover(int show_logo) | |
| info_idx = -1; | |
| } else { | |
| fbcon_has_console_bind = 1; | |
| +#ifdef CONFIG_FB_CON_DECOR | |
| + fbcon_decor_init(); | |
| +#endif | |
| } | |
| return err; | |
| @@ -998,6 +1003,12 @@ static const char *fbcon_startup(void) | |
| rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); | |
| cols /= vc->vc_font.width; | |
| rows /= vc->vc_font.height; | |
| + | |
| + if (fbcon_decor_active(info, vc)) { | |
| + cols = vc->vc_decor.twidth / vc->vc_font.width; | |
| + rows = vc->vc_decor.theight / vc->vc_font.height; | |
| + } | |
| + | |
| vc_resize(vc, cols, rows); | |
| pr_debug("mode: %s\n", info->fix.id); | |
| @@ -1032,7 +1043,7 @@ static void fbcon_init(struct vc_data *vc, bool init) | |
| logo_shown = FBCON_LOGO_DONTSHOW; | |
| if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW || | |
| - (info->fix.type == FB_TYPE_TEXT)) | |
| + (info->fix.type == FB_TYPE_TEXT) || fbcon_decor_active(info, vc)) | |
| logo = 0; | |
| if (var_to_display(p, &info->var, info)) | |
| @@ -1283,6 +1294,11 @@ static void __fbcon_clear(struct vc_data *vc, unsigned int sy, unsigned int sx, | |
| fbcon_clear_margins(vc, 0); | |
| } | |
| + if (fbcon_decor_active(info, vc)) { | |
| + fbcon_decor_clear(vc, info, sy, sx, height, width); | |
| + return; | |
| + } | |
| + | |
| fg = get_color(vc, info, vc->vc_video_erase_char, 1); | |
| bg = get_color(vc, info, vc->vc_video_erase_char, 0); | |
| /* Split blits that cross physical y_wrap boundary */ | |
| @@ -1310,10 +1326,14 @@ static void fbcon_putcs(struct vc_data *vc, const u16 *s, unsigned int count, | |
| struct fbcon_display *p = &fb_display[vc->vc_num]; | |
| struct fbcon_ops *ops = info->fbcon_par; | |
| - if (!fbcon_is_inactive(vc, info)) | |
| - ops->putcs(vc, info, s, count, real_y(p, ypos), xpos, | |
| - get_color(vc, info, scr_readw(s), 1), | |
| - get_color(vc, info, scr_readw(s), 0)); | |
| + if (!fbcon_is_inactive(vc, info)) { | |
| + if (fbcon_decor_active(info, vc)) | |
| + fbcon_decor_putcs(vc, info, s, count, ypos, xpos); | |
| + else | |
| + ops->putcs(vc, info, s, count, real_y(p, ypos), xpos, | |
| + get_color(vc, info, scr_readw(s), 1), | |
| + get_color(vc, info, scr_readw(s), 0)); | |
| + } | |
| } | |
| static void fbcon_clear_margins(struct vc_data *vc, int bottom_only) | |
| @@ -1321,8 +1341,12 @@ static void fbcon_clear_margins(struct vc_data *vc, int bottom_only) | |
| struct fb_info *info = fbcon_info_from_console(vc->vc_num); | |
| struct fbcon_ops *ops = info->fbcon_par; | |
| - if (!fbcon_is_inactive(vc, info)) | |
| - ops->clear_margins(vc, info, margin_color, bottom_only); | |
| + if (!fbcon_is_inactive(vc, info)) { | |
| + if (fbcon_decor_active(info, vc)) | |
| + fbcon_decor_clear_margins(vc, info, bottom_only); | |
| + else | |
| + ops->clear_margins(vc, info, margin_color, bottom_only); | |
| + } | |
| } | |
| static void fbcon_cursor(struct vc_data *vc, bool enable) | |
| @@ -1729,6 +1753,13 @@ static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, | |
| } | |
| return; | |
| } | |
| + | |
| + if (fbcon_decor_active(info, vc) && sy == dy && height == 1) { | |
| + /* must use slower redraw bmove to keep background pic intact */ | |
| + fbcon_decor_bmove_redraw(vc, info, sy, sx, dx, width); | |
| + return; | |
| + } | |
| + | |
| ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, | |
| height, width); | |
| } | |
| @@ -1778,6 +1809,8 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, | |
| case SM_UP: | |
| if (count > vc->vc_rows) /* Maximum realistic size */ | |
| count = vc->vc_rows; | |
| + if (fbcon_decor_active(info, vc)) | |
| + goto redraw_up; | |
| switch (fb_scrollmode(p)) { | |
| case SCROLL_MOVE: | |
| fbcon_redraw_blit(vc, info, p, t, b - t - count, | |
| @@ -1866,6 +1899,8 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, | |
| case SM_DOWN: | |
| if (count > vc->vc_rows) /* Maximum realistic size */ | |
| count = vc->vc_rows; | |
| + if (fbcon_decor_active(info, vc)) | |
| + goto redraw_down; | |
| switch (fb_scrollmode(p)) { | |
| case SCROLL_MOVE: | |
| fbcon_redraw_blit(vc, info, p, b - 1, b - t - count, | |
| @@ -2054,8 +2089,8 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width, | |
| var.yres = virt_h * virt_fh; | |
| x_diff = info->var.xres - var.xres; | |
| y_diff = info->var.yres - var.yres; | |
| - if (x_diff < 0 || x_diff > virt_fw || | |
| - y_diff < 0 || y_diff > virt_fh) { | |
| + if ((x_diff < 0 || x_diff > virt_fw || | |
| + y_diff < 0 || y_diff > virt_fh) && !vc->vc_decor.state) { | |
| const struct fb_videomode *mode; | |
| pr_debug("attempting resize %ix%i\n", var.xres, var.yres); | |
| @@ -2091,6 +2126,22 @@ static bool fbcon_switch(struct vc_data *vc) | |
| info = fbcon_info_from_console(vc->vc_num); | |
| ops = info->fbcon_par; | |
| + prev_console = ops->currcon; | |
| + if (prev_console != -1) | |
| + old_info = registered_fb[con2fb_map[prev_console]]; | |
| + | |
| +#ifdef CONFIG_FB_CON_DECOR | |
| + if (!fbcon_decor_active_vc(vc) && info->fix.visual == FB_VISUAL_DIRECTCOLOR) { | |
| + struct vc_data *vc_curr = vc_cons[prev_console].d; | |
| + | |
| + if (vc_curr && fbcon_decor_active_vc(vc_curr)) { | |
| + // Clear the screen to avoid displaying funky colors | |
| + // during palette updates. | |
| + memset((u8 *)info->screen_base + info->fix.line_length * info->var.yoffset, | |
| + 0, info->var.yres * info->fix.line_length); | |
| + } | |
| + } | |
| +#endif | |
| if (logo_shown >= 0) { | |
| struct vc_data *conp2 = vc_cons[logo_shown].d; | |
| @@ -2101,9 +2152,6 @@ static bool fbcon_switch(struct vc_data *vc) | |
| logo_shown = FBCON_LOGO_CANSHOW; | |
| } | |
| - prev_console = ops->currcon; | |
| - if (prev_console != -1) | |
| - old_info = fbcon_info_from_console(prev_console); | |
| /* | |
| * FIXME: If we have multiple fbdev's loaded, we need to | |
| * update all info->currcon. Perhaps, we can place this | |
| @@ -2147,6 +2195,18 @@ static bool fbcon_switch(struct vc_data *vc) | |
| fbcon_del_cursor_work(old_info); | |
| } | |
| + if (fbcon_decor_active_vc(vc)) { | |
| + struct vc_data *vc_curr = vc_cons[prev_console].d; | |
| + | |
| + if (!vc_curr->vc_decor.theme || | |
| + strcmp(vc->vc_decor.theme, vc_curr->vc_decor.theme) || | |
| + (fbcon_decor_active_nores(info, vc_curr) && | |
| + !fbcon_decor_active(info, vc_curr))) { | |
| + fbcon_decor_disable(vc, 0); | |
| + fbcon_decor_call_helper("modechange", vc->vc_num); | |
| + } | |
| + } | |
| + | |
| if (fbcon_is_inactive(vc, info) || | |
| ops->blank_state != FB_BLANK_UNBLANK) | |
| fbcon_del_cursor_work(info); | |
| @@ -2249,8 +2309,12 @@ static bool fbcon_blank(struct vc_data *vc, enum vesa_blank_mode blank, | |
| fbcon_cursor(vc, !blank); | |
| ops->cursor_flash = (!blank); | |
| - if (fb_blank(info, blank)) | |
| - fbcon_generic_blank(vc, info, blank); | |
| + if (fb_blank(info, blank)) { | |
| + if (fbcon_decor_active(info, vc)) | |
| + fbcon_decor_blank(vc, info, blank); | |
| + else | |
| + fbcon_generic_blank(vc, info, blank); | |
| + } | |
| } | |
| if (!blank) | |
| @@ -2446,6 +2510,12 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount, | |
| cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); | |
| rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); | |
| + | |
| + if (fbcon_decor_active(info, vc)) { | |
| + info->var.xoffset = info->var.yoffset = p->yscroll = 0; | |
| + cols = vc->vc_decor.twidth; | |
| + rows = vc->vc_decor.theight; | |
| + } | |
| cols /= w; | |
| rows /= h; | |
| ret = vc_resize(vc, cols, rows); | |
| @@ -2587,7 +2657,11 @@ static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table) | |
| int i, j, k, depth; | |
| u8 val; | |
| - if (fbcon_is_inactive(vc, info)) | |
| + if (fbcon_is_inactive(vc, info) | |
| +#ifdef CONFIG_FB_CON_DECOR | |
| + || vc->vc_num != fg_console | |
| +#endif | |
| + ) | |
| return; | |
| if (!con_is_visible(vc)) | |
| @@ -2613,7 +2687,47 @@ static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table) | |
| } else | |
| fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap); | |
| - fb_set_cmap(&palette_cmap, info); | |
| + if (fbcon_decor_active(info, vc_cons[fg_console].d) && | |
| + info->fix.visual == FB_VISUAL_DIRECTCOLOR) { | |
| + | |
| + u16 *red, *green, *blue; | |
| + int minlen = min(min(info->var.red.length, info->var.green.length), | |
| + info->var.blue.length); | |
| + | |
| + 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 + 256; | |
| + 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); | |
| + | |
| + fb_set_cmap(&cmap, info); | |
| + fbcon_decor_fix_pseudo_pal(info, vc_cons[fg_console].d); | |
| + kfree(red); | |
| + | |
| + return; | |
| + | |
| + } else if (fbcon_decor_active(info, vc_cons[fg_console].d) && | |
| + info->var.bits_per_pixel == 8 && info->bgdecor.cmap.red != NULL) | |
| + fb_set_cmap(&info->bgdecor.cmap, info); | |
| + | |
| +out: fb_set_cmap(&palette_cmap, info); | |
| } | |
| /* As we might be inside of softback, we may work with non-contiguous buffer, | |
| @@ -2682,7 +2796,14 @@ static void fbcon_modechanged(struct fb_info *info) | |
| 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); | |
| + | |
| + if (!fbcon_decor_active_nores(info, vc)) { | |
| + vc_resize(vc, cols, rows); | |
| + } else { | |
| + fbcon_decor_disable(vc, 0); | |
| + fbcon_decor_call_helper("modechange", vc->vc_num); | |
| + } | |
| + | |
| updatescrollmode(p, info, vc); | |
| scrollback_max = 0; | |
| scrollback_current = 0; | |
| @@ -2725,7 +2846,8 @@ static void fbcon_set_all_vcs(struct fb_info *info) | |
| 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); | |
| + if (!fbcon_decor_active_nores(info, vc)) | |
| + vc_resize(vc, cols, rows); | |
| } | |
| if (fg != -1) | |
| @@ -3095,6 +3217,7 @@ void fbcon_get_requirement(struct fb_info *info, | |
| caps->len = vc->vc_font.charcount; | |
| } | |
| } | |
| + fbcon_decor_exit(); | |
| } | |
| int fbcon_set_con2fb_map_ioctl(void __user *argp) | |
| diff --git a/include/linux/console_decor.h b/include/linux/console_decor.h | |
| new file mode 100644 | |
| index 00000000000000..15143556c2aa8a | |
| --- /dev/null | |
| +++ b/include/linux/console_decor.h | |
| @@ -0,0 +1,46 @@ | |
| +#ifndef _LINUX_CONSOLE_DECOR_H_ | |
| +#define _LINUX_CONSOLE_DECOR_H_ 1 | |
| + | |
| +/* A structure used by the framebuffer console decorations (drivers/video/console/fbcondecor.c) */ | |
| +struct vc_decor { | |
| + __u8 bg_color; /* The color that is to be treated as transparent */ | |
| + __u8 state; /* Current decor 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; | |
| +}; | |
| + | |
| +#ifdef __KERNEL__ | |
| +#ifdef CONFIG_COMPAT | |
| +#include <linux/compat.h> | |
| + | |
| +struct vc_decor32 { | |
| + __u8 bg_color; /* The color that is to be treated as transparent */ | |
| + __u8 state; /* Current decor 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 */ | |
| + compat_uptr_t theme; | |
| +}; | |
| + | |
| +#define vc_decor_from_compat(to, from) \ | |
| + (to).bg_color = (from).bg_color; \ | |
| + (to).state = (from).state; \ | |
| + (to).tx = (from).tx; \ | |
| + (to).ty = (from).ty; \ | |
| + (to).twidth = (from).twidth; \ | |
| + (to).theight = (from).theight; \ | |
| + (to).theme = compat_ptr((from).theme) | |
| + | |
| +#define vc_decor_to_compat(to, from) \ | |
| + (to).bg_color = (from).bg_color; \ | |
| + (to).state = (from).state; \ | |
| + (to).tx = (from).tx; \ | |
| + (to).ty = (from).ty; \ | |
| + (to).twidth = (from).twidth; \ | |
| + (to).theight = (from).theight; \ | |
| + (to).theme = ptr_to_compat((from).theme) | |
| + | |
| +#endif /* CONFIG_COMPAT */ | |
| +#endif /* __KERNEL__ */ | |
| + | |
| +#endif | |
| diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h | |
| index 59b4fec5f2548c..a9191531c7521a 100644 | |
| --- a/include/linux/console_struct.h | |
| +++ b/include/linux/console_struct.h | |
| @@ -21,6 +21,7 @@ struct uni_pagedict; | |
| #define NPAR 16 | |
| #define VC_TABSTOPS_COUNT 256U | |
| +#include <linux/console_decor.h> | |
| enum vc_intensity { | |
| VCI_HALF_BRIGHT, | |
| @@ -159,6 +160,7 @@ struct vc_data { | |
| struct uni_pagedict *uni_pagedict; | |
| struct uni_pagedict **uni_pagedict_loc; /* [!] Location of uni_pagedict variable for this console */ | |
| u32 **vc_uni_lines; /* unicode screen content */ | |
| + struct vc_decor vc_decor; | |
| /* additional information is in vt_kern.h */ | |
| }; | |
| diff --git a/include/linux/fb.h b/include/linux/fb.h | |
| index 05cc251035da98..f60422772ca136 100644 | |
| --- a/include/linux/fb.h | |
| +++ b/include/linux/fb.h | |
| @@ -226,6 +226,34 @@ struct fb_deferred_io { | |
| }; | |
| #endif | |
| +#ifdef __KERNEL__ | |
| +#ifdef CONFIG_COMPAT | |
| +struct fb_image32 { | |
| + __u32 dx; /* Where to place image */ | |
| + __u32 dy; | |
| + __u32 width; /* Size of image */ | |
| + __u32 height; | |
| + __u32 fg_color; /* Only used when a mono bitmap */ | |
| + __u32 bg_color; | |
| + __u8 depth; /* Depth of the image */ | |
| + const compat_uptr_t data; /* Pointer to image data */ | |
| + struct fb_cmap32 cmap; /* color map info */ | |
| +}; | |
| + | |
| +#define fb_image_from_compat(to, from) \ | |
| + (to).dx = (from).dx; \ | |
| + (to).dy = (from).dy; \ | |
| + (to).width = (from).width; \ | |
| + (to).height = (from).height; \ | |
| + (to).fg_color = (from).fg_color; \ | |
| + (to).bg_color = (from).bg_color; \ | |
| + (to).depth = (from).depth; \ | |
| + (to).data = compat_ptr((from).data); \ | |
| + fb_cmap_from_compat((to).cmap, (from).cmap) | |
| + | |
| +#endif /* CONFIG_COMPAT */ | |
| +#endif /* __KERNEL__ */ | |
| + | |
| /* | |
| * Frame buffer operations | |
| * | |
| @@ -511,6 +539,9 @@ struct fb_info { | |
| #define FBINFO_STATE_SUSPENDED 1 | |
| u32 state; /* Hardware state i.e suspend */ | |
| void *fbcon_par; /* fbcon use-only private area */ | |
| + | |
| + struct fb_image bgdecor; | |
| + | |
| /* From here on everything is device dependent */ | |
| void *par; | |
| diff --git a/include/uapi/linux/fb.h b/include/uapi/linux/fb.h | |
| index cde8f173f566b8..58027efb9ee2e9 100644 | |
| --- a/include/uapi/linux/fb.h | |
| +++ b/include/uapi/linux/fb.h | |
| @@ -10,6 +10,23 @@ | |
| #define FB_MAX 32 /* sufficient for now */ | |
| +struct fbcon_decor_iowrapper { | |
| + unsigned short vc; /* Virtual console */ | |
| + unsigned char origin; /* Point of origin of the request */ | |
| + void *data; | |
| +}; | |
| + | |
| +#ifdef __KERNEL__ | |
| +#ifdef CONFIG_COMPAT | |
| +#include <linux/compat.h> | |
| +struct fbcon_decor_iowrapper32 { | |
| + unsigned short vc; /* Virtual console */ | |
| + unsigned char origin; /* Point of origin of the request */ | |
| + compat_uptr_t data; | |
| +}; | |
| +#endif /* CONFIG_COMPAT */ | |
| +#endif /* __KERNEL__ */ | |
| + | |
| /* ioctls | |
| 0x46 is 'F' */ | |
| #define FBIOGET_VSCREENINFO 0x4600 | |
| @@ -37,6 +54,25 @@ | |
| #define FBIOGET_DISPINFO 0x4618 | |
| #define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) | |
| +#define FBIOCONDECOR_SETCFG _IOWR('F', 0x19, struct fbcon_decor_iowrapper) | |
| +#define FBIOCONDECOR_GETCFG _IOR('F', 0x1A, struct fbcon_decor_iowrapper) | |
| +#define FBIOCONDECOR_SETSTATE _IOWR('F', 0x1B, struct fbcon_decor_iowrapper) | |
| +#define FBIOCONDECOR_GETSTATE _IOR('F', 0x1C, struct fbcon_decor_iowrapper) | |
| +#define FBIOCONDECOR_SETPIC _IOWR('F', 0x1D, struct fbcon_decor_iowrapper) | |
| +#ifdef __KERNEL__ | |
| +#ifdef CONFIG_COMPAT | |
| +#define FBIOCONDECOR_SETCFG32 _IOWR('F', 0x19, struct fbcon_decor_iowrapper32) | |
| +#define FBIOCONDECOR_GETCFG32 _IOR('F', 0x1A, struct fbcon_decor_iowrapper32) | |
| +#define FBIOCONDECOR_SETSTATE32 _IOWR('F', 0x1B, struct fbcon_decor_iowrapper32) | |
| +#define FBIOCONDECOR_GETSTATE32 _IOR('F', 0x1C, struct fbcon_decor_iowrapper32) | |
| +#define FBIOCONDECOR_SETPIC32 _IOWR('F', 0x1D, struct fbcon_decor_iowrapper32) | |
| +#endif /* CONFIG_COMPAT */ | |
| +#endif /* __KERNEL__ */ | |
| + | |
| +#define FBCON_DECOR_THEME_LEN 128 /* Maximum length of a theme name */ | |
| +#define FBCON_DECOR_IO_ORIG_KERNEL 0 /* Kernel ioctl origin */ | |
| +#define FBCON_DECOR_IO_ORIG_USER 1 /* User ioctl origin */ | |
| + | |
| #define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ | |
| #define FB_TYPE_PLANES 1 /* Non interleaved planes */ | |
| #define FB_TYPE_INTERLEAVED_PLANES 2 /* Interleaved planes */ | |
| @@ -280,6 +316,29 @@ struct fb_var_screeninfo { | |
| __u32 reserved[4]; /* Reserved for future compatibility */ | |
| }; | |
| +#ifdef __KERNEL__ | |
| +#ifdef CONFIG_COMPAT | |
| +struct fb_cmap32 { | |
| + __u32 start; | |
| + __u32 len; /* Number of entries */ | |
| + compat_uptr_t red; /* Red values */ | |
| + compat_uptr_t green; | |
| + compat_uptr_t blue; | |
| + compat_uptr_t transp; /* transparency, can be NULL */ | |
| +}; | |
| + | |
| +#define fb_cmap_from_compat(to, from) \ | |
| + (to).start = (from).start; \ | |
| + (to).len = (from).len; \ | |
| + (to).red = compat_ptr((from).red); \ | |
| + (to).green = compat_ptr((from).green); \ | |
| + (to).blue = compat_ptr((from).blue); \ | |
| + (to).transp = compat_ptr((from).transp) | |
| + | |
| +#endif /* CONFIG_COMPAT */ | |
| +#endif /* __KERNEL__ */ | |
| + | |
| + | |
| struct fb_cmap { | |
| __u32 start; /* First entry */ | |
| __u32 len; /* Number of entries */ | |
| diff --git a/kernel/sysctl.c b/kernel/sysctl.c | |
| index 9b4f0cff76eadd..89fc87da63856e 100644 | |
| --- a/kernel/sysctl.c | |
| +++ b/kernel/sysctl.c | |
| @@ -1580,6 +1580,10 @@ int proc_do_static_key(const struct ctl_table *table, int write, | |
| return ret; | |
| } | |
| +#ifdef CONFIG_FB_CON_DECOR | |
| +extern char fbcon_decor_path[]; | |
| +#endif | |
| + | |
| static const struct ctl_table kern_table[] = { | |
| #ifdef CONFIG_PROC_SYSCTL | |
| { | |
| @@ -1758,6 +1762,15 @@ static const struct ctl_table kern_table[] = { | |
| .extra2 = SYSCTL_INT_MAX, | |
| }, | |
| #endif | |
| +#ifdef CONFIG_FB_CON_DECOR | |
| + { | |
| + .procname = "fbcondecor", | |
| + .data = &fbcon_decor_path, | |
| + .maxlen = KMOD_PATH_LEN, | |
| + .mode = 0644, | |
| + .proc_handler = &proc_dostring, | |
| + }, | |
| +#endif | |
| }; | |
| int __init sysctl_init_bases(void) | |
| @@ -1766,6 +1779,7 @@ int __init sysctl_init_bases(void) | |
| return 0; | |
| } | |
| + | |
| #endif /* CONFIG_SYSCTL */ | |
| /* | |
| * No sense putting this after each symbol definition, twice, |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment