2 * kernel/power/user_ui.c
4 * Copyright (C) 2005-2007 Bernard Blackham
5 * Copyright (C) 2002-2010 Nigel Cunningham (nigel at tuxonice 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
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/reboot.h>
32 #include <linux/security.h>
33 #include <linux/syscalls.h>
36 #include "tuxonice_sysfs.h"
37 #include "tuxonice_modules.h"
39 #include "tuxonice_ui.h"
40 #include "tuxonice_netlink.h"
41 #include "tuxonice_power_off.h"
43 static char local_printf_buf
[1024]; /* Same as printk - should be safe */
45 static struct user_helper_data ui_helper_data
;
46 static struct toi_module_ops userui_ops
;
49 static char lastheader
[512];
50 static int lastheader_message_len
;
51 static int ui_helper_changed
; /* Used at resume-time so don't overwrite value
52 set from initrd/ramfs. */
54 /* Number of distinct progress amounts that userspace can display */
55 static int progress_granularity
= 30;
57 static DECLARE_WAIT_QUEUE_HEAD(userui_wait_for_key
);
60 * ui_nl_set_state - Update toi_action based on a message from userui.
62 * @n: The bit (1 << bit) to set.
64 static void ui_nl_set_state(int n
)
66 /* Only let them change certain settings */
67 static const u32 toi_action_mask
=
68 (1 << TOI_REBOOT
) | (1 << TOI_PAUSE
) |
69 (1 << TOI_LOGALL
) | (1 << TOI_SINGLESTEP
) | (1 << TOI_PAUSE_NEAR_PAGESET_END
);
70 static unsigned long new_action
;
72 new_action
= (toi_bkd
.toi_action
& (~toi_action_mask
)) | (n
& toi_action_mask
);
74 printk(KERN_DEBUG
"n is %x. Action flags being changed from %lx "
75 "to %lx.", n
, toi_bkd
.toi_action
, new_action
);
76 toi_bkd
.toi_action
= new_action
;
78 if (!test_action_state(TOI_PAUSE
) && !test_action_state(TOI_SINGLESTEP
))
79 wake_up_interruptible(&userui_wait_for_key
);
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(struct toi_boot_kernel_data
*bkd
)
90 toi_send_netlink_message(&ui_helper_data
, USERUI_MSG_POST_ATOMIC_RESTORE
, NULL
, 0);
94 * userui_storage_needed - Report how much memory in image header is needed.
96 static int userui_storage_needed(void)
98 return sizeof(ui_helper_data
.program
) + 1 + sizeof(int);
102 * userui_save_config_info - Fill buffer with config info for image header.
104 * @buf: Buffer into which to put the config info we want to save.
106 static int userui_save_config_info(char *buf
)
108 *((int *)buf
) = progress_granularity
;
109 memcpy(buf
+ sizeof(int), ui_helper_data
.program
, sizeof(ui_helper_data
.program
));
110 return sizeof(ui_helper_data
.program
) + sizeof(int) + 1;
114 * userui_load_config_info - Restore config info from buffer.
116 * @buf: Buffer containing header info loaded.
117 * @size: Size of data loaded for this module.
119 static void userui_load_config_info(char *buf
, int size
)
121 progress_granularity
= *((int *)buf
);
124 /* Don't load the saved path if one has already been set */
125 if (ui_helper_changed
)
128 if (size
> sizeof(ui_helper_data
.program
))
129 size
= sizeof(ui_helper_data
.program
);
131 memcpy(ui_helper_data
.program
, buf
+ sizeof(int), size
);
132 ui_helper_data
.program
[sizeof(ui_helper_data
.program
) - 1] = '\0';
136 * set_ui_program_set: Record that userui program was changed.
138 * Side effect routine for when the userui program is set. In an initrd or
139 * ramfs, the user may set a location for the userui program. If this happens,
140 * we don't want to reload the value that was saved in the image header. This
141 * routine allows us to flag that we shouldn't restore the program name from
144 static void set_ui_program_set(void)
146 ui_helper_changed
= 1;
150 * userui_memory_needed - Tell core how much memory to reserve for us.
152 static int userui_memory_needed(void)
154 /* ball park figure of 128 pages */
155 return 128 * PAGE_SIZE
;
159 * userui_update_status - Update the progress bar and (if on) in-bar message.
161 * @value: Current progress percentage numerator.
162 * @maximum: Current progress percentage denominator.
163 * @fmt: Message to be displayed in the middle of the progress bar.
165 * Note that a NULL message does not mean that any previous message is erased!
166 * For that, you need toi_prepare_status with clearbar on.
168 * Returns an unsigned long, being the next numerator (as determined by the
169 * maximum and progress granularity) where status needs to be updated.
170 * This is to reduce unnecessary calls to update_status.
172 static u32
userui_update_status(u32 value
, u32 maximum
, const char *fmt
, ...)
174 static u32 last_step
= 9999;
175 struct userui_msg_params msg
;
176 u32 this_step
, next_update
;
179 if (ui_helper_data
.pid
== -1)
182 if ((!maximum
) || (!progress_granularity
))
191 /* Try to avoid math problems - we can't do 64 bit math here
192 * (and shouldn't need it - anyone got screen resolution
193 * of 65536 pixels or more?) */
194 bitshift
= fls(maximum
) - 16;
196 u32 temp_maximum
= maximum
>> bitshift
;
197 u32 temp_value
= value
>> bitshift
;
199 (temp_value
* progress_granularity
/ temp_maximum
);
200 next_update
= (((this_step
+ 1) * temp_maximum
/
201 progress_granularity
) + 1) << bitshift
;
203 this_step
= (u32
) (value
* progress_granularity
/ maximum
);
204 next_update
= ((this_step
+ 1) * maximum
/ progress_granularity
) + 1;
207 if (this_step
== last_step
)
210 memset(&msg
, 0, sizeof(msg
));
213 msg
.b
= progress_granularity
;
218 vsnprintf(msg
.text
, sizeof(msg
.text
), fmt
, args
);
220 msg
.text
[sizeof(msg
.text
) - 1] = '\0';
223 toi_send_netlink_message(&ui_helper_data
, USERUI_MSG_PROGRESS
, &msg
, sizeof(msg
));
224 last_step
= this_step
;
230 * userui_message - Display a message without necessarily logging it.
232 * @section: Type of message. Messages can be filtered by type.
233 * @level: Degree of importance of the message. Lower values = higher priority.
234 * @normally_logged: Whether logged even if log_everything is off.
235 * @fmt: Message (and parameters).
237 * This function is intended to do the same job as printk, but without normally
238 * logging what is printed. The point is to be able to get debugging info on
239 * screen without filling the logs with "1/534. ^M 2/534^M. 3/534^M"
241 * It may be called from an interrupt context - can't sleep!
243 static void userui_message(u32 section
, u32 level
, u32 normally_logged
, const char *fmt
, ...)
245 struct userui_msg_params msg
;
247 if ((level
) && (level
> console_loglevel
))
250 memset(&msg
, 0, sizeof(msg
));
254 msg
.c
= normally_logged
;
259 vsnprintf(msg
.text
, sizeof(msg
.text
), fmt
, args
);
261 msg
.text
[sizeof(msg
.text
) - 1] = '\0';
264 if (test_action_state(TOI_LOGALL
))
265 printk(KERN_INFO
"%s\n", msg
.text
);
267 toi_send_netlink_message(&ui_helper_data
, USERUI_MSG_MESSAGE
, &msg
, sizeof(msg
));
271 * wait_for_key_via_userui - Wait for userui to receive a keypress.
273 static void wait_for_key_via_userui(void)
275 DECLARE_WAITQUEUE(wait
, current
);
277 add_wait_queue(&userui_wait_for_key
, &wait
);
278 set_current_state(TASK_INTERRUPTIBLE
);
280 interruptible_sleep_on(&userui_wait_for_key
);
282 set_current_state(TASK_RUNNING
);
283 remove_wait_queue(&userui_wait_for_key
, &wait
);
287 * userui_prepare_status - Display high level messages.
289 * @clearbar: Whether to clear the progress bar.
290 * @fmt...: New message for the title.
292 * Prepare the 'nice display', drawing the header and version, along with the
293 * current action and perhaps also resetting the progress bar.
295 static void userui_prepare_status(int clearbar
, const char *fmt
, ...)
301 lastheader_message_len
= vsnprintf(lastheader
, 512, fmt
, args
);
306 toi_update_status(0, 1, NULL
);
308 if (ui_helper_data
.pid
== -1)
309 printk(KERN_EMERG
"%s\n", lastheader
);
311 toi_message(0, TOI_STATUS
, 1, lastheader
, NULL
);
315 * toi_wait_for_keypress - Wait for keypress via userui.
317 * @timeout: Maximum time to wait.
319 * Wait for a keypress from userui.
321 * FIXME: Implement timeout?
323 static char userui_wait_for_keypress(int timeout
)
327 if (ui_helper_data
.pid
!= -1) {
328 wait_for_key_via_userui();
336 * userui_abort_hibernate - Abort a cycle & tell user if they didn't request it.
338 * @result_code: Reason why we're aborting (1 << bit).
339 * @fmt: Message to display if telling the user what's going on.
341 * Abort a cycle. If this wasn't at the user's request (and we're displaying
342 * output), tell the user why and wait for them to acknowledge the message.
344 static void userui_abort_hibernate(int result_code
, const char *fmt
, ...)
349 set_result_state(result_code
);
351 if (test_result_state(TOI_ABORTED
))
354 set_result_state(TOI_ABORTED
);
356 if (test_result_state(TOI_ABORT_REQUESTED
))
360 printed_len
= vsnprintf(local_printf_buf
, sizeof(local_printf_buf
), fmt
, args
);
362 if (ui_helper_data
.pid
!= -1)
363 printed_len
= sprintf(local_printf_buf
+ printed_len
, " (Press SPACE to continue)");
365 toi_prepare_status(CLEAR_BAR
, "%s", local_printf_buf
);
367 if (ui_helper_data
.pid
!= -1)
368 userui_wait_for_keypress(0);
372 * request_abort_hibernate - Abort hibernating or resuming at user request.
374 * Handle the user requesting the cancellation of a hibernation or resume by
377 static void request_abort_hibernate(void)
379 if (test_result_state(TOI_ABORT_REQUESTED
) || !test_action_state(TOI_CAN_CANCEL
))
382 if (test_toi_state(TOI_NOW_RESUMING
)) {
383 toi_prepare_status(CLEAR_BAR
, "Escape pressed. " "Powering down again.");
384 set_toi_state(TOI_STOP_RESUME
);
385 while (!test_toi_state(TOI_IO_STOPPED
))
387 if (toiActiveAllocator
->mark_resume_attempted
)
388 toiActiveAllocator
->mark_resume_attempted(0);
392 toi_prepare_status(CLEAR_BAR
, "--- ESCAPE PRESSED :" " ABORTING HIBERNATION ---");
393 set_abort_result(TOI_ABORT_REQUESTED
);
394 wake_up_interruptible(&userui_wait_for_key
);
398 * userui_user_rcv_msg - Receive a netlink message from userui.
400 * @skb: skb received.
401 * @nlh: Netlink header received.
403 static int userui_user_rcv_msg(struct sk_buff
*skb
, struct nlmsghdr
*nlh
)
408 type
= nlh
->nlmsg_type
;
410 /* A control message: ignore them */
411 if (type
< NETLINK_MSG_BASE
)
414 /* Unknown message: reply with EINVAL */
415 if (type
>= USERUI_MSG_MAX
)
418 /* All operations require privileges, even GET */
419 if (!capable(CAP_NET_ADMIN
))
422 /* Only allow one task to receive NOFREEZE privileges */
423 if (type
== NETLINK_MSG_NOFREEZE_ME
&& ui_helper_data
.pid
!= -1) {
424 printk(KERN_INFO
"Got NOFREEZE_ME request when "
425 "ui_helper_data.pid is %d.\n", ui_helper_data
.pid
);
429 data
= (int *)NLMSG_DATA(nlh
);
432 case USERUI_MSG_ABORT
:
433 request_abort_hibernate();
435 case USERUI_MSG_GET_STATE
:
436 toi_send_netlink_message(&ui_helper_data
,
437 USERUI_MSG_GET_STATE
, &toi_bkd
.toi_action
,
438 sizeof(toi_bkd
.toi_action
));
440 case USERUI_MSG_GET_DEBUG_STATE
:
441 toi_send_netlink_message(&ui_helper_data
,
442 USERUI_MSG_GET_DEBUG_STATE
,
443 &toi_bkd
.toi_debug_state
, sizeof(toi_bkd
.toi_debug_state
));
445 case USERUI_MSG_SET_STATE
:
446 if (nlh
->nlmsg_len
< NLMSG_LENGTH(sizeof(int)))
448 ui_nl_set_state(*data
);
450 case USERUI_MSG_SET_DEBUG_STATE
:
451 if (nlh
->nlmsg_len
< NLMSG_LENGTH(sizeof(int)))
453 toi_bkd
.toi_debug_state
= (*data
);
455 case USERUI_MSG_SPACE
:
456 wake_up_interruptible(&userui_wait_for_key
);
458 case USERUI_MSG_GET_POWERDOWN_METHOD
:
459 toi_send_netlink_message(&ui_helper_data
,
460 USERUI_MSG_GET_POWERDOWN_METHOD
,
461 &toi_poweroff_method
, sizeof(toi_poweroff_method
));
463 case USERUI_MSG_SET_POWERDOWN_METHOD
:
464 if (nlh
->nlmsg_len
!= NLMSG_LENGTH(sizeof(char)))
466 toi_poweroff_method
= (unsigned long)(*data
);
468 case USERUI_MSG_GET_LOGLEVEL
:
469 toi_send_netlink_message(&ui_helper_data
,
470 USERUI_MSG_GET_LOGLEVEL
,
471 &toi_bkd
.toi_default_console_level
,
472 sizeof(toi_bkd
.toi_default_console_level
));
474 case USERUI_MSG_SET_LOGLEVEL
:
475 if (nlh
->nlmsg_len
< NLMSG_LENGTH(sizeof(int)))
477 toi_bkd
.toi_default_console_level
= (*data
);
479 case USERUI_MSG_PRINTK
:
480 printk(KERN_INFO
"%s", (char *)data
);
489 * userui_cond_pause - Possibly pause at user request.
491 * @pause: Whether to pause or just display the message.
492 * @message: Message to display at the start of pausing.
494 * Potentially pause and wait for the user to tell us to continue. We normally
495 * only pause when @pause is set. While paused, the user can do things like
496 * changing the loglevel, toggling the display of debugging sections and such
499 static void userui_cond_pause(int pause
, char *message
)
501 int displayed_message
= 0, last_key
= 0;
503 while (last_key
!= 32 &&
504 ui_helper_data
.pid
!= -1 &&
505 ((test_action_state(TOI_PAUSE
) && pause
) || (test_action_state(TOI_SINGLESTEP
)))) {
506 if (!displayed_message
) {
507 toi_prepare_status(DONT_CLEAR_BAR
,
508 "%s Press SPACE to continue.%s",
509 message
? message
: "",
510 (test_action_state(TOI_SINGLESTEP
)) ?
511 " Single step on." : "");
512 displayed_message
= 1;
514 last_key
= userui_wait_for_keypress(0);
520 * userui_prepare_console - Prepare the console for use.
522 * Prepare a console for use, saving current kmsg settings and attempting to
523 * start userui. Console loglevel changes are handled by userui.
525 static void userui_prepare_console(void)
527 orig_kmsg
= vt_kmsg_redirect(fg_console
+ 1);
529 ui_helper_data
.pid
= -1;
531 if (!userui_ops
.enabled
) {
532 printk(KERN_INFO
"TuxOnIce: Userui disabled.\n");
536 if (*ui_helper_data
.program
)
537 toi_netlink_setup(&ui_helper_data
);
539 printk(KERN_INFO
"TuxOnIce: Userui program not configured.\n");
543 * userui_cleanup_console - Cleanup after a cycle.
545 * Tell userui to cleanup, and restore kmsg_redirect to its original value.
548 static void userui_cleanup_console(void)
550 if (ui_helper_data
.pid
> -1)
551 toi_netlink_close(&ui_helper_data
);
553 vt_kmsg_redirect(orig_kmsg
);
557 * User interface specific /sys/power/tuxonice entries.
560 static struct toi_sysfs_data sysfs_params
[] = {
561 #if defined(CONFIG_NET) && defined(CONFIG_SYSFS)
562 SYSFS_BIT("enable_escape", SYSFS_RW
, &toi_bkd
.toi_action
,
564 SYSFS_BIT("pause_between_steps", SYSFS_RW
, &toi_bkd
.toi_action
,
566 SYSFS_INT("enabled", SYSFS_RW
, &userui_ops
.enabled
, 0, 1, 0, NULL
),
567 SYSFS_INT("progress_granularity", SYSFS_RW
, &progress_granularity
, 1,
569 SYSFS_STRING("program", SYSFS_RW
, ui_helper_data
.program
, 255, 0,
571 SYSFS_INT("debug", SYSFS_RW
, &ui_helper_data
.debug
, 0, 1, 0, NULL
)
575 static struct toi_module_ops userui_ops
= {
578 .shared_directory
= "user_interface",
579 .module
= THIS_MODULE
,
580 .storage_needed
= userui_storage_needed
,
581 .save_config_info
= userui_save_config_info
,
582 .load_config_info
= userui_load_config_info
,
583 .memory_needed
= userui_memory_needed
,
584 .post_atomic_restore
= userui_post_atomic_restore
,
585 .sysfs_data
= sysfs_params
,
586 .num_sysfs_entries
= sizeof(sysfs_params
) / sizeof(struct toi_sysfs_data
),
589 static struct ui_ops my_ui_ops
= {
590 .update_status
= userui_update_status
,
591 .message
= userui_message
,
592 .prepare_status
= userui_prepare_status
,
593 .abort
= userui_abort_hibernate
,
594 .cond_pause
= userui_cond_pause
,
595 .prepare
= userui_prepare_console
,
596 .cleanup
= userui_cleanup_console
,
597 .wait_for_key
= userui_wait_for_keypress
,
601 * toi_user_ui_init - Boot time initialisation for user interface.
603 * Invoked from the core init routine.
605 static __init
int toi_user_ui_init(void)
609 ui_helper_data
.nl
= NULL
;
610 strncpy(ui_helper_data
.program
, CONFIG_TOI_USERUI_DEFAULT_PATH
, 255);
611 ui_helper_data
.pid
= -1;
612 ui_helper_data
.skb_size
= sizeof(struct userui_msg_params
);
613 ui_helper_data
.pool_limit
= 6;
614 ui_helper_data
.netlink_id
= NETLINK_TOI_USERUI
;
615 ui_helper_data
.name
= "userspace ui";
616 ui_helper_data
.rcv_msg
= userui_user_rcv_msg
;
617 ui_helper_data
.interface_version
= 8;
618 ui_helper_data
.must_init
= 0;
619 ui_helper_data
.not_ready
= userui_cleanup_console
;
620 init_completion(&ui_helper_data
.wait_for_process
);
621 result
= toi_register_module(&userui_ops
);
623 result
= toi_register_ui_ops(&my_ui_ops
);
625 toi_unregister_module(&userui_ops
);
632 * toi_user_ui_ext - Cleanup code for if the core is unloaded.
634 static __exit
void toi_user_ui_exit(void)
636 toi_netlink_close_complete(&ui_helper_data
);
637 toi_remove_ui_ops(&my_ui_ops
);
638 toi_unregister_module(&userui_ops
);
640 module_init(toi_user_ui_init
);
641 module_exit(toi_user_ui_exit
);
642 MODULE_AUTHOR("Nigel Cunningham");
643 MODULE_DESCRIPTION("TuxOnIce Userui Support");
644 MODULE_LICENSE("GPL");
646 late_initcall(toi_user_ui_init
);