Merge tag 'v3.3.7' into 3.3/master
[zen-stable.git] / drivers / video / console / fbcondecor.c
blobbe42784cd9fc38ed52a585ab171333e9151652ef
1 /*
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
17 * more details.
20 #include <linux/module.h>
21 #include <linux/kernel.h>
22 #include <linux/string.h>
23 #include <linux/types.h>
24 #include <linux/fb.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>
35 #include <linux/fs.h>
36 #include <linux/compat.h>
37 #include <linux/console.h>
39 #include <asm/uaccess.h>
40 #include <asm/irq.h>
41 #include <asm/system.h>
43 #include "fbcon.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)
53 char *envp[] = {
54 "HOME=/",
55 "PATH=/sbin:/bin",
56 NULL
59 char tfb[5];
60 char tcons[5];
61 unsigned char fb = (int) con2fb_map[vc];
63 char *argv[] = {
64 fbcon_decor_path,
65 "2",
66 cmd,
67 tcons,
68 tfb,
69 vc_cons[vc].d->vc_decor.theme,
70 NULL
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)
82 struct fb_info* info;
84 if (!vc->vc_decor.state)
85 return -EINVAL;
87 info = registered_fb[(int) con2fb_map[vc->vc_num]];
89 if (info == NULL)
90 return -EINVAL;
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) {
97 redraw_screen(vc, 0);
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",
104 vc->vc_num);
106 return 0;
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))
119 return -EINVAL;
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",
134 vc->vc_num);
136 return 0;
139 static inline int fbcon_decor_ioctl_dosetstate(struct vc_data *vc, unsigned int state, unsigned char origin)
141 int ret;
143 if (origin == FBCON_DECOR_IO_ORIG_USER)
144 console_lock();
145 if (!state)
146 ret = fbcon_decor_disable(vc, 1);
147 else
148 ret = fbcon_decor_enable(vc);
149 if (origin == FBCON_DECOR_IO_ORIG_USER)
150 console_unlock();
152 return ret;
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;
163 int len;
164 char *tmp;
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)
171 return -EINVAL;
173 len = strlen_user(cfg->theme);
174 if (!len || len > FBCON_DECOR_THEME_LEN)
175 return -EINVAL;
176 tmp = kmalloc(len, GFP_KERNEL);
177 if (!tmp)
178 return -ENOMEM;
179 if (copy_from_user(tmp, (void __user *)cfg->theme, len))
180 return -EFAULT;
181 cfg->theme = tmp;
182 cfg->state = 0;
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) {
189 console_lock();
190 if (vc->vc_decor.state)
191 fbcon_decor_disable(vc, 1);
194 if (vc->vc_decor.theme)
195 kfree(vc->vc_decor.theme);
197 vc->vc_decor = *cfg;
199 if (origin == FBCON_DECOR_IO_ORIG_USER)
200 console_unlock();
202 printk(KERN_INFO "fbcondecor: console %d using theme '%s'\n",
203 vc->vc_num, vc->vc_decor.theme);
204 return 0;
207 static int fbcon_decor_ioctl_dogetcfg(struct vc_data *vc, struct vc_decor *decor)
209 char __user *tmp;
211 tmp = decor->theme;
212 *decor = vc->vc_decor;
213 decor->theme = tmp;
215 if (vc->vc_decor.theme) {
216 if (copy_to_user(tmp, vc->vc_decor.theme, strlen(vc->vc_decor.theme) + 1))
217 return -EFAULT;
218 } else
219 if (put_user(0, tmp))
220 return -EFAULT;
222 return 0;
225 static int fbcon_decor_ioctl_dosetpic(struct vc_data *vc, struct fb_image *img, unsigned char origin)
227 struct fb_info *info;
228 int len;
229 u8 *tmp;
231 if (vc->vc_num != fg_console)
232 return -EINVAL;
234 info = registered_fb[(int) con2fb_map[vc->vc_num]];
236 if (info == NULL)
237 return -EINVAL;
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);
242 return -EINVAL;
245 if (img->depth != info->var.bits_per_pixel) {
246 printk(KERN_ERR "fbcondecor: picture depth mismatch\n");
247 return -EINVAL;
250 if (img->depth == 8) {
251 if (!img->cmap.len || !img->cmap.red || !img->cmap.green ||
252 !img->cmap.blue)
253 return -EINVAL;
255 tmp = vmalloc(img->cmap.len * 3 * 2);
256 if (!tmp)
257 return -ENOMEM;
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))) {
265 vfree(tmp);
266 return -EFAULT;
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;
273 } else {
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);
285 if (!tmp)
286 goto out;
288 if (copy_from_user(tmp, (void __user*)img->data, len))
289 goto out;
291 img->data = tmp;
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)
296 console_lock();
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)
314 console_unlock();
316 return 0;
318 out: if (img->cmap.red)
319 vfree(img->cmap.red);
321 if (tmp)
322 vfree(tmp);
323 return -ENOMEM;
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)))
336 return -EFAULT;
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))
343 return -EINVAL;
345 vc = vc_cons[vc_num].d;
347 switch (cmd) {
348 case FBIOCONDECOR_SETPIC:
350 struct fb_image img;
351 if (copy_from_user(&img, (struct fb_image __user *)data, sizeof(struct fb_image)))
352 return -EFAULT;
354 return fbcon_decor_ioctl_dosetpic(vc, &img, origin);
356 case FBIOCONDECOR_SETCFG:
358 struct vc_decor cfg;
359 if (copy_from_user(&cfg, (struct vc_decor __user *)data, sizeof(struct vc_decor)))
360 return -EFAULT;
362 return fbcon_decor_ioctl_dosetcfg(vc, &cfg, origin);
364 case FBIOCONDECOR_GETCFG:
366 int rval;
367 struct vc_decor cfg;
369 if (copy_from_user(&cfg, (struct vc_decor __user *)data, sizeof(struct vc_decor)))
370 return -EFAULT;
372 rval = fbcon_decor_ioctl_dogetcfg(vc, &cfg);
374 if (copy_to_user(data, &cfg, sizeof(struct vc_decor)))
375 return -EFAULT;
376 return rval;
378 case FBIOCONDECOR_SETSTATE:
380 unsigned int state = 0;
381 if (get_user(state, (unsigned int __user *)data))
382 return -EFAULT;
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);
392 default:
393 return -ENOIOCTLCMD;
397 #ifdef CONFIG_COMPAT
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)))
410 return -EFAULT;
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))
418 return -EINVAL;
420 vc = vc_cons[vc_num].d;
422 switch (cmd) {
423 case FBIOCONDECOR_SETPIC32:
425 struct fb_image32 img_compat;
426 struct fb_image img;
428 if (copy_from_user(&img_compat, (struct fb_image32 __user *)data, sizeof(struct fb_image32)))
429 return -EFAULT;
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;
439 struct vc_decor cfg;
441 if (copy_from_user(&cfg_compat, (struct vc_decor32 __user *)data, sizeof(struct vc_decor32)))
442 return -EFAULT;
444 vc_decor_from_compat(cfg, cfg_compat);
446 return fbcon_decor_ioctl_dosetcfg(vc, &cfg, origin);
449 case FBIOCONDECOR_GETCFG32:
451 int rval;
452 struct vc_decor32 cfg_compat;
453 struct vc_decor cfg;
455 if (copy_from_user(&cfg_compat, (struct vc_decor32 __user *)data, sizeof(struct vc_decor32)))
456 return -EFAULT;
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)))
464 return -EFAULT;
465 return rval;
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))
474 return -EFAULT;
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);
492 default:
493 return -ENOIOCTLCMD;
496 #else
497 #define fbcon_decor_compat_ioctl NULL
498 #endif
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;
515 struct vc_data *vc;
516 int i;
518 vc = vc_cons[0].d;
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;
532 return;
535 int fbcon_decor_init(void)
537 int i;
539 fbcon_decor_reset();
541 if (initialized)
542 return 0;
544 i = misc_register(&fbcon_decor_dev);
545 if (i) {
546 printk(KERN_ERR "fbcondecor: failed to register device\n");
547 return i;
550 fbcon_decor_call_helper("init", 0);
551 initialized = 1;
552 return 0;
555 int fbcon_decor_exit(void)
557 fbcon_decor_reset();
558 return 0;
561 EXPORT_SYMBOL(fbcon_decor_path);