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
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"
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
;
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
);
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
);
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);
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
);
125 /* Don't load the saved path if one has already been set */
126 if (ui_helper_changed
)
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
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
;
180 unsigned long next_update
;
182 if (ui_helper_data
.pid
== -1)
185 if ((!maximum
) || (!progress_granularity
))
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;
199 unsigned long temp_maximum
= maximum
>> bitshift
;
200 unsigned long temp_value
= value
>> bitshift
;
202 (temp_value
* progress_granularity
/ temp_maximum
);
203 next_update
= (((this_step
+ 1) * temp_maximum
/
204 progress_granularity
) + 1) << bitshift
;
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
)
214 memset(&msg
, 0, sizeof(msg
));
217 msg
.b
= progress_granularity
;
222 vsnprintf(msg
.text
, sizeof(msg
.text
), fmt
, args
);
224 msg
.text
[sizeof(msg
.text
)-1] = '\0';
227 toi_send_netlink_message(&ui_helper_data
, USERUI_MSG_PROGRESS
,
229 last_step
= this_step
;
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
))
256 memset(&msg
, 0, sizeof(msg
));
260 msg
.c
= normally_logged
;
265 vsnprintf(msg
.text
, sizeof(msg
.text
), fmt
, 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
,
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
, ...)
308 lastheader_message_len
= vsnprintf(lastheader
, 512, fmt
, args
);
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
)
334 if (ui_helper_data
.pid
!= -1) {
335 wait_for_key_via_userui();
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
, ...)
356 set_result_state(result_code
);
358 if (test_result_state(TOI_ABORTED
))
361 set_result_state(TOI_ABORTED
);
363 if (test_result_state(TOI_ABORT_REQUESTED
))
367 printed_len
= vsnprintf(local_printf_buf
, sizeof(local_printf_buf
),
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
386 static void request_abort_hibernate(void)
388 if (test_result_state(TOI_ABORT_REQUESTED
))
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
))
397 if (toiActiveAllocator
->mark_resume_attempted
)
398 toiActiveAllocator
->mark_resume_attempted(0);
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
)
419 type
= nlh
->nlmsg_type
;
421 /* A control message: ignore them */
422 if (type
< NETLINK_MSG_BASE
)
425 /* Unknown message: reply with EINVAL */
426 if (type
>= USERUI_MSG_MAX
)
429 /* All operations require privileges, even GET */
430 if (security_netlink_recv(skb
, CAP_NET_ADMIN
))
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
);
439 data
= (int*)NLMSG_DATA(nlh
);
442 case USERUI_MSG_ABORT
:
443 request_abort_hibernate();
445 case USERUI_MSG_GET_STATE
:
446 toi_send_netlink_message(&ui_helper_data
,
447 USERUI_MSG_GET_STATE
, &toi_action
,
450 case USERUI_MSG_GET_DEBUG_STATE
:
451 toi_send_netlink_message(&ui_helper_data
,
452 USERUI_MSG_GET_DEBUG_STATE
,
454 sizeof(toi_debug_state
));
456 case USERUI_MSG_SET_STATE
:
457 if (nlh
->nlmsg_len
< NLMSG_LENGTH(sizeof(int)))
459 ui_nl_set_state(*data
);
461 case USERUI_MSG_SET_DEBUG_STATE
:
462 if (nlh
->nlmsg_len
< NLMSG_LENGTH(sizeof(int)))
464 toi_debug_state
= (*data
);
466 case USERUI_MSG_SPACE
:
467 wake_up_interruptible(&userui_wait_for_key
);
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
));
475 case USERUI_MSG_SET_POWERDOWN_METHOD
:
476 if (nlh
->nlmsg_len
< NLMSG_LENGTH(sizeof(int)))
478 toi_poweroff_method
= (*data
);
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
));
486 case USERUI_MSG_SET_LOGLEVEL
:
487 if (nlh
->nlmsg_len
< NLMSG_LENGTH(sizeof(int)))
489 toi_default_console_level
= (*data
);
491 case USERUI_MSG_PRINTK
:
492 printk((char *) data
);
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
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);
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");
549 if (*ui_helper_data
.program
)
550 toi_netlink_setup(&ui_helper_data
);
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
,
598 static struct toi_module_ops userui_ops
= {
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)
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
);
646 result
= toi_register_ui_ops(&my_ui_ops
);
648 toi_unregister_module(&userui_ops
);
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");
669 late_initcall(toi_user_ui_init
);