2 * linux/drivers/video/console/fbcondecor.c -- Framebuffer console decorations
4 * Copyright (C) 2004-2009 Michal Januszewski <spock@gentoo.org>
6 * Code based upon "Bootsplash" (C) 2001-2003
7 * Volker Poplawski <volker@poplawski.de>,
8 * Stefan Reinauer <stepan@suse.de>,
9 * Steffen Winterfeldt <snwint@suse.de>,
10 * Michael Schroeder <mls@suse.de>,
11 * Ken Wimer <wimer@suse.de>.
13 * Compat ioctl support by Thorsten Klein <TK@Thorsten-Klein.de>.
15 * This file is subject to the terms and conditions of the GNU General Public
16 * License. See the file COPYING in the main directory of this archive for
20 #include <linux/module.h>
21 #include <linux/kernel.h>
22 #include <linux/string.h>
23 #include <linux/types.h>
25 #include <linux/vt_kern.h>
26 #include <linux/vmalloc.h>
27 #include <linux/unistd.h>
28 #include <linux/syscalls.h>
29 #include <linux/init.h>
30 #include <linux/proc_fs.h>
31 #include <linux/workqueue.h>
32 #include <linux/kmod.h>
33 #include <linux/miscdevice.h>
34 #include <linux/device.h>
36 #include <linux/compat.h>
37 #include <linux/console.h>
39 #include <asm/uaccess.h>
41 #include <asm/system.h>
44 #include "fbcondecor.h"
46 extern signed char con2fb_map
[];
47 static int fbcon_decor_enable(struct vc_data
*vc
);
48 char fbcon_decor_path
[KMOD_PATH_LEN
] = "/sbin/fbcondecor_helper";
49 static int initialized
= 0;
51 int fbcon_decor_call_helper(char* cmd
, unsigned short vc
)
61 unsigned char fb
= (int) con2fb_map
[vc
];
69 vc_cons
[vc
].d
->vc_decor
.theme
,
73 snprintf(tfb
,5,"%d",fb
);
74 snprintf(tcons
,5,"%d",vc
);
76 return call_usermodehelper(fbcon_decor_path
, argv
, envp
, 1);
79 /* Disables fbcondecor on a virtual console; called with console sem held. */
80 int fbcon_decor_disable(struct vc_data
*vc
, unsigned char redraw
)
84 if (!vc
->vc_decor
.state
)
87 info
= registered_fb
[(int) con2fb_map
[vc
->vc_num
]];
92 vc
->vc_decor
.state
= 0;
93 vc_resize(vc
, info
->var
.xres
/ vc
->vc_font
.width
,
94 info
->var
.yres
/ vc
->vc_font
.height
);
96 if (fg_console
== vc
->vc_num
&& redraw
) {
98 update_region(vc
, vc
->vc_origin
+
99 vc
->vc_size_row
* vc
->vc_top
,
100 vc
->vc_size_row
* (vc
->vc_bottom
- vc
->vc_top
) / 2);
103 printk(KERN_INFO
"fbcondecor: switched decor state to 'off' on console %d\n",
109 /* Enables fbcondecor on a virtual console; called with console sem held. */
110 static int fbcon_decor_enable(struct vc_data
*vc
)
112 struct fb_info
* info
;
114 info
= registered_fb
[(int) con2fb_map
[vc
->vc_num
]];
116 if (vc
->vc_decor
.twidth
== 0 || vc
->vc_decor
.theight
== 0 ||
117 info
== NULL
|| vc
->vc_decor
.state
|| (!info
->bgdecor
.data
&&
118 vc
->vc_num
== fg_console
))
121 vc
->vc_decor
.state
= 1;
122 vc_resize(vc
, vc
->vc_decor
.twidth
/ vc
->vc_font
.width
,
123 vc
->vc_decor
.theight
/ vc
->vc_font
.height
);
125 if (fg_console
== vc
->vc_num
) {
126 redraw_screen(vc
, 0);
127 update_region(vc
, vc
->vc_origin
+
128 vc
->vc_size_row
* vc
->vc_top
,
129 vc
->vc_size_row
* (vc
->vc_bottom
- vc
->vc_top
) / 2);
130 fbcon_decor_clear_margins(vc
, info
, 0);
133 printk(KERN_INFO
"fbcondecor: switched decor state to 'on' on console %d\n",
139 static inline int fbcon_decor_ioctl_dosetstate(struct vc_data
*vc
, unsigned int state
, unsigned char origin
)
143 if (origin
== FBCON_DECOR_IO_ORIG_USER
)
146 ret
= fbcon_decor_disable(vc
, 1);
148 ret
= fbcon_decor_enable(vc
);
149 if (origin
== FBCON_DECOR_IO_ORIG_USER
)
155 static inline void fbcon_decor_ioctl_dogetstate(struct vc_data
*vc
, unsigned int *state
)
157 *state
= vc
->vc_decor
.state
;
160 static int fbcon_decor_ioctl_dosetcfg(struct vc_data
*vc
, struct vc_decor
*cfg
, unsigned char origin
)
162 struct fb_info
*info
;
166 info
= registered_fb
[(int) con2fb_map
[vc
->vc_num
]];
168 if (info
== NULL
|| !cfg
->twidth
|| !cfg
->theight
||
169 cfg
->tx
+ cfg
->twidth
> info
->var
.xres
||
170 cfg
->ty
+ cfg
->theight
> info
->var
.yres
)
173 len
= strlen_user(cfg
->theme
);
174 if (!len
|| len
> FBCON_DECOR_THEME_LEN
)
176 tmp
= kmalloc(len
, GFP_KERNEL
);
179 if (copy_from_user(tmp
, (void __user
*)cfg
->theme
, len
))
184 /* If this ioctl is a response to a request from kernel, the console sem
185 * is already held; we also don't need to disable decor because either the
186 * new config and background picture will be successfully loaded, and the
187 * decor will stay on, or in case of a failure it'll be turned off in fbcon. */
188 if (origin
== FBCON_DECOR_IO_ORIG_USER
) {
190 if (vc
->vc_decor
.state
)
191 fbcon_decor_disable(vc
, 1);
194 if (vc
->vc_decor
.theme
)
195 kfree(vc
->vc_decor
.theme
);
199 if (origin
== FBCON_DECOR_IO_ORIG_USER
)
202 printk(KERN_INFO
"fbcondecor: console %d using theme '%s'\n",
203 vc
->vc_num
, vc
->vc_decor
.theme
);
207 static int fbcon_decor_ioctl_dogetcfg(struct vc_data
*vc
, struct vc_decor
*decor
)
212 *decor
= vc
->vc_decor
;
215 if (vc
->vc_decor
.theme
) {
216 if (copy_to_user(tmp
, vc
->vc_decor
.theme
, strlen(vc
->vc_decor
.theme
) + 1))
219 if (put_user(0, tmp
))
225 static int fbcon_decor_ioctl_dosetpic(struct vc_data
*vc
, struct fb_image
*img
, unsigned char origin
)
227 struct fb_info
*info
;
231 if (vc
->vc_num
!= fg_console
)
234 info
= registered_fb
[(int) con2fb_map
[vc
->vc_num
]];
239 if (img
->width
!= info
->var
.xres
|| img
->height
!= info
->var
.yres
) {
240 printk(KERN_ERR
"fbcondecor: picture dimensions mismatch\n");
241 printk(KERN_ERR
"%dx%d vs %dx%d\n", img
->width
, img
->height
, info
->var
.xres
, info
->var
.yres
);
245 if (img
->depth
!= info
->var
.bits_per_pixel
) {
246 printk(KERN_ERR
"fbcondecor: picture depth mismatch\n");
250 if (img
->depth
== 8) {
251 if (!img
->cmap
.len
|| !img
->cmap
.red
|| !img
->cmap
.green
||
255 tmp
= vmalloc(img
->cmap
.len
* 3 * 2);
259 if (copy_from_user(tmp
,
260 (void __user
*)img
->cmap
.red
, (img
->cmap
.len
<< 1)) ||
261 copy_from_user(tmp
+ (img
->cmap
.len
<< 1),
262 (void __user
*)img
->cmap
.green
, (img
->cmap
.len
<< 1)) ||
263 copy_from_user(tmp
+ (img
->cmap
.len
<< 2),
264 (void __user
*)img
->cmap
.blue
, (img
->cmap
.len
<< 1))) {
269 img
->cmap
.transp
= NULL
;
270 img
->cmap
.red
= (u16
*)tmp
;
271 img
->cmap
.green
= img
->cmap
.red
+ img
->cmap
.len
;
272 img
->cmap
.blue
= img
->cmap
.green
+ img
->cmap
.len
;
274 img
->cmap
.red
= NULL
;
277 len
= ((img
->depth
+ 7) >> 3) * img
->width
* img
->height
;
280 * Allocate an additional byte so that we never go outside of the
281 * buffer boundaries in the rendering functions in a 24 bpp mode.
283 tmp
= vmalloc(len
+ 1);
288 if (copy_from_user(tmp
, (void __user
*)img
->data
, len
))
293 /* If this ioctl is a response to a request from kernel, the console sem
294 * is already held. */
295 if (origin
== FBCON_DECOR_IO_ORIG_USER
)
298 if (info
->bgdecor
.data
)
299 vfree((u8
*)info
->bgdecor
.data
);
300 if (info
->bgdecor
.cmap
.red
)
301 vfree(info
->bgdecor
.cmap
.red
);
303 info
->bgdecor
= *img
;
305 if (fbcon_decor_active_vc(vc
) && fg_console
== vc
->vc_num
) {
306 redraw_screen(vc
, 0);
307 update_region(vc
, vc
->vc_origin
+
308 vc
->vc_size_row
* vc
->vc_top
,
309 vc
->vc_size_row
* (vc
->vc_bottom
- vc
->vc_top
) / 2);
310 fbcon_decor_clear_margins(vc
, info
, 0);
313 if (origin
== FBCON_DECOR_IO_ORIG_USER
)
318 out
: if (img
->cmap
.red
)
319 vfree(img
->cmap
.red
);
326 static long fbcon_decor_ioctl(struct file
*filp
, u_int cmd
, u_long arg
)
328 struct fbcon_decor_iowrapper __user
*wrapper
= (void __user
*) arg
;
329 struct vc_data
*vc
= NULL
;
330 unsigned short vc_num
= 0;
331 unsigned char origin
= 0;
332 void __user
*data
= NULL
;
334 if (!access_ok(VERIFY_READ
, wrapper
,
335 sizeof(struct fbcon_decor_iowrapper
)))
338 __get_user(vc_num
, &wrapper
->vc
);
339 __get_user(origin
, &wrapper
->origin
);
340 __get_user(data
, &wrapper
->data
);
342 if (!vc_cons_allocated(vc_num
))
345 vc
= vc_cons
[vc_num
].d
;
348 case FBIOCONDECOR_SETPIC
:
351 if (copy_from_user(&img
, (struct fb_image __user
*)data
, sizeof(struct fb_image
)))
354 return fbcon_decor_ioctl_dosetpic(vc
, &img
, origin
);
356 case FBIOCONDECOR_SETCFG
:
359 if (copy_from_user(&cfg
, (struct vc_decor __user
*)data
, sizeof(struct vc_decor
)))
362 return fbcon_decor_ioctl_dosetcfg(vc
, &cfg
, origin
);
364 case FBIOCONDECOR_GETCFG
:
369 if (copy_from_user(&cfg
, (struct vc_decor __user
*)data
, sizeof(struct vc_decor
)))
372 rval
= fbcon_decor_ioctl_dogetcfg(vc
, &cfg
);
374 if (copy_to_user(data
, &cfg
, sizeof(struct vc_decor
)))
378 case FBIOCONDECOR_SETSTATE
:
380 unsigned int state
= 0;
381 if (get_user(state
, (unsigned int __user
*)data
))
383 return fbcon_decor_ioctl_dosetstate(vc
, state
, origin
);
385 case FBIOCONDECOR_GETSTATE
:
387 unsigned int state
= 0;
388 fbcon_decor_ioctl_dogetstate(vc
, &state
);
389 return put_user(state
, (unsigned int __user
*)data
);
399 static long fbcon_decor_compat_ioctl(struct file
*filp
, unsigned int cmd
, unsigned long arg
) {
401 struct fbcon_decor_iowrapper32 __user
*wrapper
= (void __user
*)arg
;
402 struct vc_data
*vc
= NULL
;
403 unsigned short vc_num
= 0;
404 unsigned char origin
= 0;
405 compat_uptr_t data_compat
= 0;
406 void __user
*data
= NULL
;
408 if (!access_ok(VERIFY_READ
, wrapper
,
409 sizeof(struct fbcon_decor_iowrapper32
)))
412 __get_user(vc_num
, &wrapper
->vc
);
413 __get_user(origin
, &wrapper
->origin
);
414 __get_user(data_compat
, &wrapper
->data
);
415 data
= compat_ptr(data_compat
);
417 if (!vc_cons_allocated(vc_num
))
420 vc
= vc_cons
[vc_num
].d
;
423 case FBIOCONDECOR_SETPIC32
:
425 struct fb_image32 img_compat
;
428 if (copy_from_user(&img_compat
, (struct fb_image32 __user
*)data
, sizeof(struct fb_image32
)))
431 fb_image_from_compat(img
, img_compat
);
433 return fbcon_decor_ioctl_dosetpic(vc
, &img
, origin
);
436 case FBIOCONDECOR_SETCFG32
:
438 struct vc_decor32 cfg_compat
;
441 if (copy_from_user(&cfg_compat
, (struct vc_decor32 __user
*)data
, sizeof(struct vc_decor32
)))
444 vc_decor_from_compat(cfg
, cfg_compat
);
446 return fbcon_decor_ioctl_dosetcfg(vc
, &cfg
, origin
);
449 case FBIOCONDECOR_GETCFG32
:
452 struct vc_decor32 cfg_compat
;
455 if (copy_from_user(&cfg_compat
, (struct vc_decor32 __user
*)data
, sizeof(struct vc_decor32
)))
457 cfg
.theme
= compat_ptr(cfg_compat
.theme
);
459 rval
= fbcon_decor_ioctl_dogetcfg(vc
, &cfg
);
461 vc_decor_to_compat(cfg_compat
, cfg
);
463 if (copy_to_user((struct vc_decor32 __user
*)data
, &cfg_compat
, sizeof(struct vc_decor32
)))
468 case FBIOCONDECOR_SETSTATE32
:
470 compat_uint_t state_compat
= 0;
471 unsigned int state
= 0;
473 if (get_user(state_compat
, (compat_uint_t __user
*)data
))
476 state
= (unsigned int)state_compat
;
478 return fbcon_decor_ioctl_dosetstate(vc
, state
, origin
);
481 case FBIOCONDECOR_GETSTATE32
:
483 compat_uint_t state_compat
= 0;
484 unsigned int state
= 0;
486 fbcon_decor_ioctl_dogetstate(vc
, &state
);
487 state_compat
= (compat_uint_t
)state
;
489 return put_user(state_compat
, (compat_uint_t __user
*)data
);
497 #define fbcon_decor_compat_ioctl NULL
500 static struct file_operations fbcon_decor_ops
= {
501 .owner
= THIS_MODULE
,
502 .unlocked_ioctl
= fbcon_decor_ioctl
,
503 .compat_ioctl
= fbcon_decor_compat_ioctl
506 static struct miscdevice fbcon_decor_dev
= {
507 .minor
= MISC_DYNAMIC_MINOR
,
508 .name
= "fbcondecor",
509 .fops
= &fbcon_decor_ops
512 void fbcon_decor_reset(void)
514 struct fb_info
*info
;
519 info
= registered_fb
[0];
521 for (i
= 0; i
< num_registered_fb
; i
++) {
522 registered_fb
[i
]->bgdecor
.data
= NULL
;
523 registered_fb
[i
]->bgdecor
.cmap
.red
= NULL
;
526 for (i
= 0; i
< MAX_NR_CONSOLES
&& vc_cons
[i
].d
; i
++) {
527 vc_cons
[i
].d
->vc_decor
.state
= vc_cons
[i
].d
->vc_decor
.twidth
=
528 vc_cons
[i
].d
->vc_decor
.theight
= 0;
529 vc_cons
[i
].d
->vc_decor
.theme
= NULL
;
535 int fbcon_decor_init(void)
544 i
= misc_register(&fbcon_decor_dev
);
546 printk(KERN_ERR
"fbcondecor: failed to register device\n");
550 fbcon_decor_call_helper("init", 0);
555 int fbcon_decor_exit(void)
561 EXPORT_SYMBOL(fbcon_decor_path
);