Complete the renaming to TuxOnIce with function names, vars etc.
[linux-2.6/suspend2-head.git] / kernel / power / tuxonice_userui.c
bloba377c31e83ced162f8cc667486074c32f1cb26b3
1 /*
2 * kernel/power/user_ui.c
4 * Copyright (C) 2005-2007 Bernard Blackham
5 * Copyright (C) 2002-2007 Nigel Cunningham (nigel at suspend2 net)
7 * This file is released under the GPLv2.
9 * Routines for TuxOnIce's user interface.
11 * The user interface code talks to a userspace program via a
12 * netlink socket.
14 * The kernel side:
15 * - starts the userui program;
16 * - sends text messages and progress bar status;
18 * The user space side:
19 * - passes messages regarding user requests (abort, toggle reboot etc)
23 #define __KERNEL_SYSCALLS__
25 #include <linux/suspend.h>
26 #include <linux/freezer.h>
27 #include <linux/console.h>
28 #include <linux/ctype.h>
29 #include <linux/tty.h>
30 #include <linux/vt_kern.h>
31 #include <linux/module.h>
32 #include <linux/reboot.h>
33 #include <linux/kmod.h>
34 #include <linux/security.h>
35 #include <linux/syscalls.h>
37 #include "tuxonice_sysfs.h"
38 #include "tuxonice_modules.h"
39 #include "tuxonice.h"
40 #include "tuxonice_ui.h"
41 #include "tuxonice_netlink.h"
42 #include "tuxonice_power_off.h"
44 static char local_printf_buf[1024]; /* Same as printk - should be safe */
46 static struct user_helper_data ui_helper_data;
47 static struct toi_module_ops userui_ops;
48 static int orig_kmsg;
50 static char lastheader[512];
51 static int lastheader_message_len;
52 static int ui_helper_changed; /* Used at resume-time so don't overwrite value
53 set from initrd/ramfs. */
55 /* Number of distinct progress amounts that userspace can display */
56 static int progress_granularity = 30;
58 DECLARE_WAIT_QUEUE_HEAD(userui_wait_for_key);
60 /**
61 * ui_nl_set_state - Update toi_action based on a message from userui.
63 * @n: The bit (1 << bit) to set.
65 static void ui_nl_set_state(int n)
67 /* Only let them change certain settings */
68 static const int toi_action_mask =
69 (1 << TOI_REBOOT) | (1 << TOI_PAUSE) |
70 (1 << TOI_SLOW) | (1 << TOI_LOGALL) |
71 (1 << TOI_SINGLESTEP) |
72 (1 << TOI_PAUSE_NEAR_PAGESET_END);
74 toi_action = (toi_action & (~toi_action_mask)) |
75 (n & toi_action_mask);
77 if (!test_action_state(TOI_PAUSE) &&
78 !test_action_state(TOI_SINGLESTEP))
79 wake_up_interruptible(&userui_wait_for_key);
82 /**
83 * userui_post_atomic_restore - Tell userui that atomic restore just happened.
85 * Tell userui that atomic restore just occured, so that it can do things like
86 * redrawing the screen, re-getting settings and so on.
88 static void userui_post_atomic_restore(void)
90 toi_send_netlink_message(&ui_helper_data,
91 USERUI_MSG_POST_ATOMIC_RESTORE, NULL, 0);
94 /**
95 * userui_storage_needed - Report how much memory in image header is needed.
97 static int userui_storage_needed(void)
99 return sizeof(ui_helper_data.program) + 1 + sizeof(int);
103 * userui_save_config_info - Fill buffer with config info for image header.
105 * @buf: Buffer into which to put the config info we want to save.
107 static int userui_save_config_info(char *buf)
109 *((int *) buf) = progress_granularity;
110 memcpy(buf + sizeof(int), ui_helper_data.program, sizeof(ui_helper_data.program));
111 return sizeof(ui_helper_data.program) + sizeof(int) + 1;
115 * userui_load_config_info - Restore config info from buffer.
117 * @buf: Buffer containing header info loaded.
118 * @size: Size of data loaded for this module.
120 static void userui_load_config_info(char *buf, int size)
122 progress_granularity = *((int *) buf);
123 size -= sizeof(int);
125 /* Don't load the saved path if one has already been set */
126 if (ui_helper_changed)
127 return;
129 if (size > sizeof(ui_helper_data.program))
130 size = sizeof(ui_helper_data.program);
132 memcpy(ui_helper_data.program, buf + sizeof(int), size);
133 ui_helper_data.program[sizeof(ui_helper_data.program)-1] = '\0';
137 * set_ui_program_set: Record that userui program was changed.
139 * Side effect routine for when the userui program is set. In an initrd or
140 * ramfs, the user may set a location for the userui program. If this happens,
141 * we don't want to reload the value that was saved in the image header. This
142 * routine allows us to flag that we shouldn't restore the program name from
143 * the image header.
145 static void set_ui_program_set(void)
147 ui_helper_changed = 1;
151 * userui_memory_needed - Tell core how much memory to reserve for us.
153 static int userui_memory_needed(void)
155 /* ball park figure of 128 pages */
156 return (128 * PAGE_SIZE);
160 * userui_update_status - Update the progress bar and (if on) in-bar message.
162 * @value: Current progress percentage numerator.
163 * @maximum: Current progress percentage denominator.
164 * @fmt: Message to be displayed in the middle of the progress bar.
166 * Note that a NULL message does not mean that any previous message is erased!
167 * For that, you need toi_prepare_status with clearbar on.
169 * Returns an unsigned long, being the next numerator (as determined by the
170 * maximum and progress granularity) where status needs to be updated.
171 * This is to reduce unnecessary calls to update_status.
173 static unsigned long userui_update_status(unsigned long value,
174 unsigned long maximum, const char *fmt, ...)
176 static int last_step = -1;
177 struct userui_msg_params msg;
178 int bitshift;
179 int this_step;
180 unsigned long next_update;
182 if (ui_helper_data.pid == -1)
183 return 0;
185 if ((!maximum) || (!progress_granularity))
186 return maximum;
188 if (value < 0)
189 value = 0;
191 if (value > maximum)
192 value = maximum;
194 /* Try to avoid math problems - we can't do 64 bit math here
195 * (and shouldn't need it - anyone got screen resolution
196 * of 65536 pixels or more?) */
197 bitshift = fls(maximum) - 16;
198 if (bitshift > 0) {
199 unsigned long temp_maximum = maximum >> bitshift;
200 unsigned long temp_value = value >> bitshift;
201 this_step = (int)
202 (temp_value * progress_granularity / temp_maximum);
203 next_update = (((this_step + 1) * temp_maximum /
204 progress_granularity) + 1) << bitshift;
205 } else {
206 this_step = (int) (value * progress_granularity / maximum);
207 next_update = ((this_step + 1) * maximum /
208 progress_granularity) + 1;
211 if (this_step == last_step)
212 return next_update;
214 memset(&msg, 0, sizeof(msg));
216 msg.a = this_step;
217 msg.b = progress_granularity;
219 if (fmt) {
220 va_list args;
221 va_start(args, fmt);
222 vsnprintf(msg.text, sizeof(msg.text), fmt, args);
223 va_end(args);
224 msg.text[sizeof(msg.text)-1] = '\0';
227 toi_send_netlink_message(&ui_helper_data, USERUI_MSG_PROGRESS,
228 &msg, sizeof(msg));
229 last_step = this_step;
231 return next_update;
235 * userui_message - Display a message without necessarily logging it.
237 * @section: Type of message. Messages can be filtered by type.
238 * @level: Degree of importance of the message. Lower values = higher priority.
239 * @normally_logged: Whether logged even if log_everything is off.
240 * @fmt: Message (and parameters).
242 * This function is intended to do the same job as printk, but without normally
243 * logging what is printed. The point is to be able to get debugging info on
244 * screen without filling the logs with "1/534. ^M 2/534^M. 3/534^M"
246 * It may be called from an interrupt context - can't sleep!
248 static void userui_message(unsigned long section, unsigned long level,
249 int normally_logged, const char *fmt, ...)
251 struct userui_msg_params msg;
253 if ((level) && (level > console_loglevel))
254 return;
256 memset(&msg, 0, sizeof(msg));
258 msg.a = section;
259 msg.b = level;
260 msg.c = normally_logged;
262 if (fmt) {
263 va_list args;
264 va_start(args, fmt);
265 vsnprintf(msg.text, sizeof(msg.text), fmt, args);
266 va_end(args);
267 msg.text[sizeof(msg.text)-1] = '\0';
270 if (test_action_state(TOI_LOGALL))
271 printk("%s\n", msg.text);
273 toi_send_netlink_message(&ui_helper_data, USERUI_MSG_MESSAGE,
274 &msg, sizeof(msg));
278 * wait_for_key_via_userui - Wait for userui to receive a keypress.
280 static void wait_for_key_via_userui(void)
282 DECLARE_WAITQUEUE(wait, current);
284 add_wait_queue(&userui_wait_for_key, &wait);
285 set_current_state(TASK_INTERRUPTIBLE);
287 interruptible_sleep_on(&userui_wait_for_key);
289 set_current_state(TASK_RUNNING);
290 remove_wait_queue(&userui_wait_for_key, &wait);
294 * userui_prepare_status - Display high level messages.
296 * @clearbar: Whether to clear the progress bar.
297 * @fmt...: New message for the title.
299 * Prepare the 'nice display', drawing the header and version, along with the
300 * current action and perhaps also resetting the progress bar.
302 static void userui_prepare_status(int clearbar, const char *fmt, ...)
304 va_list args;
306 if (fmt) {
307 va_start(args, fmt);
308 lastheader_message_len = vsnprintf(lastheader, 512, fmt, args);
309 va_end(args);
312 if (clearbar)
313 toi_update_status(0, 1, NULL);
315 toi_message(0, TOI_STATUS, 1, lastheader, NULL);
317 if (ui_helper_data.pid == -1)
318 printk(KERN_EMERG "%s\n", lastheader);
322 * toi_wait_for_keypress - Wait for keypress via userui.
324 * @timeout: Maximum time to wait.
326 * Wait for a keypress from userui.
328 * FIXME: Implement timeout?
330 static char userui_wait_for_keypress(int timeout)
332 char key = '\0';
334 if (ui_helper_data.pid != -1) {
335 wait_for_key_via_userui();
336 key = ' ';
339 return key;
343 * userui_abort_hibernate - Abort a cycle & tell user if they didn't request it.
345 * @result_code: Reason why we're aborting (1 << bit).
346 * @fmt: Message to display if telling the user what's going on.
348 * Abort a cycle. If this wasn't at the user's request (and we're displaying
349 * output), tell the user why and wait for them to acknowledge the message.
351 static void userui_abort_hibernate(int result_code, const char *fmt, ...)
353 va_list args;
354 int printed_len = 0;
356 set_result_state(result_code);
358 if (test_result_state(TOI_ABORTED))
359 return;
361 set_result_state(TOI_ABORTED);
363 if (test_result_state(TOI_ABORT_REQUESTED))
364 return;
366 va_start(args, fmt);
367 printed_len = vsnprintf(local_printf_buf, sizeof(local_printf_buf),
368 fmt, args);
369 va_end(args);
370 if (ui_helper_data.pid != -1)
371 printed_len = sprintf(local_printf_buf + printed_len,
372 " (Press SPACE to continue)");
374 toi_prepare_status(CLEAR_BAR, local_printf_buf);
376 if (ui_helper_data.pid != -1)
377 userui_wait_for_keypress(0);
381 * request_abort_hibernate - Abort hibernating or resuming at user request.
383 * Handle the user requesting the cancellation of a hibernation or resume by
384 * pressing escape.
386 static void request_abort_hibernate(void)
388 if (test_result_state(TOI_ABORT_REQUESTED))
389 return;
391 if (test_toi_state(TOI_NOW_RESUMING)) {
392 toi_prepare_status(CLEAR_BAR, "Escape pressed. "
393 "Powering down again.");
394 set_toi_state(TOI_STOP_RESUME);
395 while (!test_toi_state(TOI_IO_STOPPED))
396 schedule();
397 if (toiActiveAllocator->mark_resume_attempted)
398 toiActiveAllocator->mark_resume_attempted(0);
399 toi_power_down();
402 toi_prepare_status(CLEAR_BAR, "--- ESCAPE PRESSED :"
403 " ABORTING HIBERNATION ---");
404 set_abort_result(TOI_ABORT_REQUESTED);
405 wake_up_interruptible(&userui_wait_for_key);
409 * userui_user_rcv_msg - Receive a netlink message from userui.
411 * @skb: skb received.
412 * @nlh: Netlink header received.
414 static int userui_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
416 int type;
417 int *data;
419 type = nlh->nlmsg_type;
421 /* A control message: ignore them */
422 if (type < NETLINK_MSG_BASE)
423 return 0;
425 /* Unknown message: reply with EINVAL */
426 if (type >= USERUI_MSG_MAX)
427 return -EINVAL;
429 /* All operations require privileges, even GET */
430 if (security_netlink_recv(skb, CAP_NET_ADMIN))
431 return -EPERM;
433 /* Only allow one task to receive NOFREEZE privileges */
434 if (type == NETLINK_MSG_NOFREEZE_ME && ui_helper_data.pid != -1) {
435 printk("Got NOFREEZE_ME request when ui_helper_data.pid is %d.\n", ui_helper_data.pid);
436 return -EBUSY;
439 data = (int*)NLMSG_DATA(nlh);
441 switch (type) {
442 case USERUI_MSG_ABORT:
443 request_abort_hibernate();
444 break;
445 case USERUI_MSG_GET_STATE:
446 toi_send_netlink_message(&ui_helper_data,
447 USERUI_MSG_GET_STATE, &toi_action,
448 sizeof(toi_action));
449 break;
450 case USERUI_MSG_GET_DEBUG_STATE:
451 toi_send_netlink_message(&ui_helper_data,
452 USERUI_MSG_GET_DEBUG_STATE,
453 &toi_debug_state,
454 sizeof(toi_debug_state));
455 break;
456 case USERUI_MSG_SET_STATE:
457 if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(int)))
458 return -EINVAL;
459 ui_nl_set_state(*data);
460 break;
461 case USERUI_MSG_SET_DEBUG_STATE:
462 if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(int)))
463 return -EINVAL;
464 toi_debug_state = (*data);
465 break;
466 case USERUI_MSG_SPACE:
467 wake_up_interruptible(&userui_wait_for_key);
468 break;
469 case USERUI_MSG_GET_POWERDOWN_METHOD:
470 toi_send_netlink_message(&ui_helper_data,
471 USERUI_MSG_GET_POWERDOWN_METHOD,
472 &toi_poweroff_method,
473 sizeof(toi_poweroff_method));
474 break;
475 case USERUI_MSG_SET_POWERDOWN_METHOD:
476 if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(int)))
477 return -EINVAL;
478 toi_poweroff_method = (*data);
479 break;
480 case USERUI_MSG_GET_LOGLEVEL:
481 toi_send_netlink_message(&ui_helper_data,
482 USERUI_MSG_GET_LOGLEVEL,
483 &toi_default_console_level,
484 sizeof(toi_default_console_level));
485 break;
486 case USERUI_MSG_SET_LOGLEVEL:
487 if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(int)))
488 return -EINVAL;
489 toi_default_console_level = (*data);
490 break;
491 case USERUI_MSG_PRINTK:
492 printk((char *) data);
495 return 1;
499 * userui_cond_pause - Possibly pause at user request.
501 * @pause: Whether to pause or just display the message.
502 * @message: Message to display at the start of pausing.
504 * Potentially pause and wait for the user to tell us to continue. We normally
505 * only pause when @pause is set. While paused, the user can do things like
506 * changing the loglevel, toggling the display of debugging sections and such
507 * like.
509 static void userui_cond_pause(int pause, char *message)
511 int displayed_message = 0, last_key = 0;
513 while (last_key != 32 &&
514 ui_helper_data.pid != -1 &&
515 (!test_result_state(TOI_ABORTED)) &&
516 ((test_action_state(TOI_PAUSE) && pause) ||
517 (test_action_state(TOI_SINGLESTEP)))) {
518 if (!displayed_message) {
519 toi_prepare_status(DONT_CLEAR_BAR,
520 "%s Press SPACE to continue.%s",
521 message ? message : "",
522 (test_action_state(TOI_SINGLESTEP)) ?
523 " Single step on." : "");
524 displayed_message = 1;
526 last_key = userui_wait_for_keypress(0);
528 schedule();
532 * userui_prepare_console - Prepare the console for use.
534 * Prepare a console for use, saving current kmsg settings and attempting to
535 * start userui. Console loglevel changes are handled by userui.
537 static void userui_prepare_console(void)
539 orig_kmsg = kmsg_redirect;
540 kmsg_redirect = fg_console + 1;
542 ui_helper_data.pid = -1;
544 if (!userui_ops.enabled) {
545 printk("TuxOnIce: Userui disabled.\n");
546 return;
549 if (*ui_helper_data.program)
550 toi_netlink_setup(&ui_helper_data);
551 else
552 printk("TuxOnIce: Userui program not configured.\n");
556 * userui_cleanup_console - Cleanup after a cycle.
558 * Tell userui to cleanup, and restore kmsg_redirect to its original value.
561 static void userui_cleanup_console(void)
563 if (ui_helper_data.pid > -1)
564 toi_netlink_close(&ui_helper_data);
566 kmsg_redirect = orig_kmsg;
570 * User interface specific /sys/power/suspend2 entries.
573 static struct toi_sysfs_data sysfs_params[] = {
574 #if defined(CONFIG_NET) && defined(CONFIG_SYSFS)
575 { TOI_ATTR("enable_escape", SYSFS_RW),
576 SYSFS_BIT(&toi_action, TOI_CAN_CANCEL, 0)
579 { TOI_ATTR("pause_between_steps", SYSFS_RW),
580 SYSFS_BIT(&toi_action, TOI_PAUSE, 0)
583 { TOI_ATTR("enabled", SYSFS_RW),
584 SYSFS_INT(&userui_ops.enabled, 0, 1, 0)
587 { TOI_ATTR("progress_granularity", SYSFS_RW),
588 SYSFS_INT(&progress_granularity, 1, 2048, 0)
591 { TOI_ATTR("program", SYSFS_RW),
592 SYSFS_STRING(ui_helper_data.program, 255, 0),
593 .write_side_effect = set_ui_program_set,
595 #endif
598 static struct toi_module_ops userui_ops = {
599 .type = MISC_MODULE,
600 .name = "userui",
601 .shared_directory = "user_interface",
602 .module = THIS_MODULE,
603 .storage_needed = userui_storage_needed,
604 .save_config_info = userui_save_config_info,
605 .load_config_info = userui_load_config_info,
606 .memory_needed = userui_memory_needed,
607 .sysfs_data = sysfs_params,
608 .num_sysfs_entries = sizeof(sysfs_params) / sizeof(struct toi_sysfs_data),
611 static struct ui_ops my_ui_ops = {
612 .post_atomic_restore = userui_post_atomic_restore,
613 .update_status = userui_update_status,
614 .message = userui_message,
615 .prepare_status = userui_prepare_status,
616 .abort = userui_abort_hibernate,
617 .cond_pause = userui_cond_pause,
618 .prepare = userui_prepare_console,
619 .cleanup = userui_cleanup_console,
620 .wait_for_key = userui_wait_for_keypress,
624 * toi_user_ui_init - Boot time initialisation for user interface.
626 * Invoked from the core init routine.
628 static __init int toi_user_ui_init(void)
630 int result;
632 ui_helper_data.nl = NULL;
633 ui_helper_data.program[0] = '\0';
634 ui_helper_data.pid = -1;
635 ui_helper_data.skb_size = sizeof(struct userui_msg_params);
636 ui_helper_data.pool_limit = 6;
637 ui_helper_data.netlink_id = NETLINK_TOI_USERUI;
638 ui_helper_data.name = "userspace ui";
639 ui_helper_data.rcv_msg = userui_user_rcv_msg;
640 ui_helper_data.interface_version = 7;
641 ui_helper_data.must_init = 0;
642 ui_helper_data.not_ready = userui_cleanup_console;
643 init_completion(&ui_helper_data.wait_for_process);
644 result = toi_register_module(&userui_ops);
645 if (!result)
646 result = toi_register_ui_ops(&my_ui_ops);
647 if (result)
648 toi_unregister_module(&userui_ops);
650 return result;
653 #ifdef MODULE
655 * toi_user_ui_ext - Cleanup code for if the core is unloaded.
657 static __exit void toi_user_ui_exit(void)
659 toi_remove_ui_ops(&my_ui_ops);
660 toi_unregister_module(&userui_ops);
663 module_init(toi_user_ui_init);
664 module_exit(toi_user_ui_exit);
665 MODULE_AUTHOR("Nigel Cunningham");
666 MODULE_DESCRIPTION("TuxOnIce Userui Support");
667 MODULE_LICENSE("GPL");
668 #else
669 late_initcall(toi_user_ui_init);
670 #endif