Commit | Line | Data |
---|---|---|
6fa3eb70 S |
1 | /* |
2 | * kernel/power/user_ui.c | |
3 | * | |
4 | * Copyright (C) 2005-2007 Bernard Blackham | |
5 | * Copyright (C) 2002-2010 Nigel Cunningham (nigel at tuxonice net) | |
6 | * | |
7 | * This file is released under the GPLv2. | |
8 | * | |
9 | * Routines for TuxOnIce's user interface. | |
10 | * | |
11 | * The user interface code talks to a userspace program via a | |
12 | * netlink socket. | |
13 | * | |
14 | * The kernel side: | |
15 | * - starts the userui program; | |
16 | * - sends text messages and progress bar status; | |
17 | * | |
18 | * The user space side: | |
19 | * - passes messages regarding user requests (abort, toggle reboot etc) | |
20 | * | |
21 | */ | |
22 | ||
23 | #define __KERNEL_SYSCALLS__ | |
24 | ||
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> | |
34 | #include <linux/vt.h> | |
35 | ||
36 | #include "tuxonice_sysfs.h" | |
37 | #include "tuxonice_modules.h" | |
38 | #include "tuxonice.h" | |
39 | #include "tuxonice_ui.h" | |
40 | #include "tuxonice_netlink.h" | |
41 | #include "tuxonice_power_off.h" | |
42 | ||
43 | static char local_printf_buf[1024]; /* Same as printk - should be safe */ | |
44 | ||
45 | static struct user_helper_data ui_helper_data; | |
46 | static struct toi_module_ops userui_ops; | |
47 | static int orig_kmsg; | |
48 | ||
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. */ | |
53 | ||
54 | /* Number of distinct progress amounts that userspace can display */ | |
55 | static int progress_granularity = 30; | |
56 | ||
57 | static DECLARE_WAIT_QUEUE_HEAD(userui_wait_for_key); | |
58 | ||
59 | /** | |
60 | * ui_nl_set_state - Update toi_action based on a message from userui. | |
61 | * | |
62 | * @n: The bit (1 << bit) to set. | |
63 | */ | |
64 | static void ui_nl_set_state(int n) | |
65 | { | |
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; | |
71 | ||
72 | new_action = (toi_bkd.toi_action & (~toi_action_mask)) | (n & toi_action_mask); | |
73 | ||
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; | |
77 | ||
78 | if (!test_action_state(TOI_PAUSE) && !test_action_state(TOI_SINGLESTEP)) | |
79 | wake_up_interruptible(&userui_wait_for_key); | |
80 | } | |
81 | ||
82 | /** | |
83 | * userui_post_atomic_restore - Tell userui that atomic restore just happened. | |
84 | * | |
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. | |
87 | */ | |
88 | static void userui_post_atomic_restore(struct toi_boot_kernel_data *bkd) | |
89 | { | |
90 | toi_send_netlink_message(&ui_helper_data, USERUI_MSG_POST_ATOMIC_RESTORE, NULL, 0); | |
91 | } | |
92 | ||
93 | /** | |
94 | * userui_storage_needed - Report how much memory in image header is needed. | |
95 | */ | |
96 | static int userui_storage_needed(void) | |
97 | { | |
98 | return sizeof(ui_helper_data.program) + 1 + sizeof(int); | |
99 | } | |
100 | ||
101 | /** | |
102 | * userui_save_config_info - Fill buffer with config info for image header. | |
103 | * | |
104 | * @buf: Buffer into which to put the config info we want to save. | |
105 | */ | |
106 | static int userui_save_config_info(char *buf) | |
107 | { | |
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; | |
111 | } | |
112 | ||
113 | /** | |
114 | * userui_load_config_info - Restore config info from buffer. | |
115 | * | |
116 | * @buf: Buffer containing header info loaded. | |
117 | * @size: Size of data loaded for this module. | |
118 | */ | |
119 | static void userui_load_config_info(char *buf, int size) | |
120 | { | |
121 | progress_granularity = *((int *)buf); | |
122 | size -= sizeof(int); | |
123 | ||
124 | /* Don't load the saved path if one has already been set */ | |
125 | if (ui_helper_changed) | |
126 | return; | |
127 | ||
128 | if (size > sizeof(ui_helper_data.program)) | |
129 | size = sizeof(ui_helper_data.program); | |
130 | ||
131 | memcpy(ui_helper_data.program, buf + sizeof(int), size); | |
132 | ui_helper_data.program[sizeof(ui_helper_data.program) - 1] = '\0'; | |
133 | } | |
134 | ||
135 | /** | |
136 | * set_ui_program_set: Record that userui program was changed. | |
137 | * | |
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 | |
142 | * the image header. | |
143 | */ | |
144 | static void set_ui_program_set(void) | |
145 | { | |
146 | ui_helper_changed = 1; | |
147 | } | |
148 | ||
149 | /** | |
150 | * userui_memory_needed - Tell core how much memory to reserve for us. | |
151 | */ | |
152 | static int userui_memory_needed(void) | |
153 | { | |
154 | /* ball park figure of 128 pages */ | |
155 | return 128 * PAGE_SIZE; | |
156 | } | |
157 | ||
158 | /** | |
159 | * userui_update_status - Update the progress bar and (if on) in-bar message. | |
160 | * | |
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. | |
164 | * | |
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. | |
167 | * | |
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. | |
171 | */ | |
172 | static u32 userui_update_status(u32 value, u32 maximum, const char *fmt, ...) | |
173 | { | |
174 | static u32 last_step = 9999; | |
175 | struct userui_msg_params msg; | |
176 | u32 this_step, next_update; | |
177 | int bitshift; | |
178 | ||
179 | if (ui_helper_data.pid == -1) | |
180 | return 0; | |
181 | ||
182 | if ((!maximum) || (!progress_granularity)) | |
183 | return maximum; | |
184 | ||
185 | if (value < 0) | |
186 | value = 0; | |
187 | ||
188 | if (value > maximum) | |
189 | value = maximum; | |
190 | ||
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; | |
195 | if (bitshift > 0) { | |
196 | u32 temp_maximum = maximum >> bitshift; | |
197 | u32 temp_value = value >> bitshift; | |
198 | this_step = (u32) | |
199 | (temp_value * progress_granularity / temp_maximum); | |
200 | next_update = (((this_step + 1) * temp_maximum / | |
201 | progress_granularity) + 1) << bitshift; | |
202 | } else { | |
203 | this_step = (u32) (value * progress_granularity / maximum); | |
204 | next_update = ((this_step + 1) * maximum / progress_granularity) + 1; | |
205 | } | |
206 | ||
207 | if (this_step == last_step) | |
208 | return next_update; | |
209 | ||
210 | memset(&msg, 0, sizeof(msg)); | |
211 | ||
212 | msg.a = this_step; | |
213 | msg.b = progress_granularity; | |
214 | ||
215 | if (fmt) { | |
216 | va_list args; | |
217 | va_start(args, fmt); | |
218 | vsnprintf(msg.text, sizeof(msg.text), fmt, args); | |
219 | va_end(args); | |
220 | msg.text[sizeof(msg.text) - 1] = '\0'; | |
221 | } | |
222 | ||
223 | toi_send_netlink_message(&ui_helper_data, USERUI_MSG_PROGRESS, &msg, sizeof(msg)); | |
224 | last_step = this_step; | |
225 | ||
226 | return next_update; | |
227 | } | |
228 | ||
229 | /** | |
230 | * userui_message - Display a message without necessarily logging it. | |
231 | * | |
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). | |
236 | * | |
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" | |
240 | * | |
241 | * It may be called from an interrupt context - can't sleep! | |
242 | */ | |
243 | static void userui_message(u32 section, u32 level, u32 normally_logged, const char *fmt, ...) | |
244 | { | |
245 | struct userui_msg_params msg; | |
246 | ||
247 | if ((level) && (level > console_loglevel)) | |
248 | return; | |
249 | ||
250 | memset(&msg, 0, sizeof(msg)); | |
251 | ||
252 | msg.a = section; | |
253 | msg.b = level; | |
254 | msg.c = normally_logged; | |
255 | ||
256 | if (fmt) { | |
257 | va_list args; | |
258 | va_start(args, fmt); | |
259 | vsnprintf(msg.text, sizeof(msg.text), fmt, args); | |
260 | va_end(args); | |
261 | msg.text[sizeof(msg.text) - 1] = '\0'; | |
262 | } | |
263 | ||
264 | if (test_action_state(TOI_LOGALL)) | |
265 | printk(KERN_INFO "%s\n", msg.text); | |
266 | ||
267 | toi_send_netlink_message(&ui_helper_data, USERUI_MSG_MESSAGE, &msg, sizeof(msg)); | |
268 | } | |
269 | ||
270 | /** | |
271 | * wait_for_key_via_userui - Wait for userui to receive a keypress. | |
272 | */ | |
273 | static void wait_for_key_via_userui(void) | |
274 | { | |
275 | DECLARE_WAITQUEUE(wait, current); | |
276 | ||
277 | add_wait_queue(&userui_wait_for_key, &wait); | |
278 | set_current_state(TASK_INTERRUPTIBLE); | |
279 | ||
280 | interruptible_sleep_on(&userui_wait_for_key); | |
281 | ||
282 | set_current_state(TASK_RUNNING); | |
283 | remove_wait_queue(&userui_wait_for_key, &wait); | |
284 | } | |
285 | ||
286 | /** | |
287 | * userui_prepare_status - Display high level messages. | |
288 | * | |
289 | * @clearbar: Whether to clear the progress bar. | |
290 | * @fmt...: New message for the title. | |
291 | * | |
292 | * Prepare the 'nice display', drawing the header and version, along with the | |
293 | * current action and perhaps also resetting the progress bar. | |
294 | */ | |
295 | static void userui_prepare_status(int clearbar, const char *fmt, ...) | |
296 | { | |
297 | va_list args; | |
298 | ||
299 | if (fmt) { | |
300 | va_start(args, fmt); | |
301 | lastheader_message_len = vsnprintf(lastheader, 512, fmt, args); | |
302 | va_end(args); | |
303 | } | |
304 | ||
305 | if (clearbar) | |
306 | toi_update_status(0, 1, NULL); | |
307 | ||
308 | if (ui_helper_data.pid == -1) | |
309 | printk(KERN_EMERG "%s\n", lastheader); | |
310 | else | |
311 | toi_message(0, TOI_STATUS, 1, lastheader, NULL); | |
312 | } | |
313 | ||
314 | /** | |
315 | * toi_wait_for_keypress - Wait for keypress via userui. | |
316 | * | |
317 | * @timeout: Maximum time to wait. | |
318 | * | |
319 | * Wait for a keypress from userui. | |
320 | * | |
321 | * FIXME: Implement timeout? | |
322 | */ | |
323 | static char userui_wait_for_keypress(int timeout) | |
324 | { | |
325 | char key = '\0'; | |
326 | ||
327 | if (ui_helper_data.pid != -1) { | |
328 | wait_for_key_via_userui(); | |
329 | key = ' '; | |
330 | } | |
331 | ||
332 | return key; | |
333 | } | |
334 | ||
335 | /** | |
336 | * userui_abort_hibernate - Abort a cycle & tell user if they didn't request it. | |
337 | * | |
338 | * @result_code: Reason why we're aborting (1 << bit). | |
339 | * @fmt: Message to display if telling the user what's going on. | |
340 | * | |
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. | |
343 | */ | |
344 | static void userui_abort_hibernate(int result_code, const char *fmt, ...) | |
345 | { | |
346 | va_list args; | |
347 | int printed_len = 0; | |
348 | ||
349 | set_result_state(result_code); | |
350 | ||
351 | if (test_result_state(TOI_ABORTED)) | |
352 | return; | |
353 | ||
354 | set_result_state(TOI_ABORTED); | |
355 | ||
356 | if (test_result_state(TOI_ABORT_REQUESTED)) | |
357 | return; | |
358 | ||
359 | va_start(args, fmt); | |
360 | printed_len = vsnprintf(local_printf_buf, sizeof(local_printf_buf), fmt, args); | |
361 | va_end(args); | |
362 | if (ui_helper_data.pid != -1) | |
363 | printed_len = sprintf(local_printf_buf + printed_len, " (Press SPACE to continue)"); | |
364 | ||
365 | toi_prepare_status(CLEAR_BAR, "%s", local_printf_buf); | |
366 | ||
367 | if (ui_helper_data.pid != -1) | |
368 | userui_wait_for_keypress(0); | |
369 | } | |
370 | ||
371 | /** | |
372 | * request_abort_hibernate - Abort hibernating or resuming at user request. | |
373 | * | |
374 | * Handle the user requesting the cancellation of a hibernation or resume by | |
375 | * pressing escape. | |
376 | */ | |
377 | static void request_abort_hibernate(void) | |
378 | { | |
379 | if (test_result_state(TOI_ABORT_REQUESTED) || !test_action_state(TOI_CAN_CANCEL)) | |
380 | return; | |
381 | ||
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)) | |
386 | schedule(); | |
387 | if (toiActiveAllocator->mark_resume_attempted) | |
388 | toiActiveAllocator->mark_resume_attempted(0); | |
389 | toi_power_down(); | |
390 | } | |
391 | ||
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); | |
395 | } | |
396 | ||
397 | /** | |
398 | * userui_user_rcv_msg - Receive a netlink message from userui. | |
399 | * | |
400 | * @skb: skb received. | |
401 | * @nlh: Netlink header received. | |
402 | */ | |
403 | static int userui_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |
404 | { | |
405 | int type; | |
406 | int *data; | |
407 | ||
408 | type = nlh->nlmsg_type; | |
409 | ||
410 | /* A control message: ignore them */ | |
411 | if (type < NETLINK_MSG_BASE) | |
412 | return 0; | |
413 | ||
414 | /* Unknown message: reply with EINVAL */ | |
415 | if (type >= USERUI_MSG_MAX) | |
416 | return -EINVAL; | |
417 | ||
418 | /* All operations require privileges, even GET */ | |
419 | if (!capable(CAP_NET_ADMIN)) | |
420 | return -EPERM; | |
421 | ||
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); | |
426 | return -EBUSY; | |
427 | } | |
428 | ||
429 | data = (int *)NLMSG_DATA(nlh); | |
430 | ||
431 | switch (type) { | |
432 | case USERUI_MSG_ABORT: | |
433 | request_abort_hibernate(); | |
434 | return 0; | |
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)); | |
439 | return 0; | |
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)); | |
444 | return 0; | |
445 | case USERUI_MSG_SET_STATE: | |
446 | if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(int))) | |
447 | return -EINVAL; | |
448 | ui_nl_set_state(*data); | |
449 | return 0; | |
450 | case USERUI_MSG_SET_DEBUG_STATE: | |
451 | if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(int))) | |
452 | return -EINVAL; | |
453 | toi_bkd.toi_debug_state = (*data); | |
454 | return 0; | |
455 | case USERUI_MSG_SPACE: | |
456 | wake_up_interruptible(&userui_wait_for_key); | |
457 | return 0; | |
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)); | |
462 | return 0; | |
463 | case USERUI_MSG_SET_POWERDOWN_METHOD: | |
464 | if (nlh->nlmsg_len != NLMSG_LENGTH(sizeof(char))) | |
465 | return -EINVAL; | |
466 | toi_poweroff_method = (unsigned long)(*data); | |
467 | return 0; | |
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)); | |
473 | return 0; | |
474 | case USERUI_MSG_SET_LOGLEVEL: | |
475 | if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(int))) | |
476 | return -EINVAL; | |
477 | toi_bkd.toi_default_console_level = (*data); | |
478 | return 0; | |
479 | case USERUI_MSG_PRINTK: | |
480 | printk(KERN_INFO "%s", (char *)data); | |
481 | return 0; | |
482 | } | |
483 | ||
484 | /* Unhandled here */ | |
485 | return 1; | |
486 | } | |
487 | ||
488 | /** | |
489 | * userui_cond_pause - Possibly pause at user request. | |
490 | * | |
491 | * @pause: Whether to pause or just display the message. | |
492 | * @message: Message to display at the start of pausing. | |
493 | * | |
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 | |
497 | * like. | |
498 | */ | |
499 | static void userui_cond_pause(int pause, char *message) | |
500 | { | |
501 | int displayed_message = 0, last_key = 0; | |
502 | ||
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; | |
513 | } | |
514 | last_key = userui_wait_for_keypress(0); | |
515 | } | |
516 | schedule(); | |
517 | } | |
518 | ||
519 | /** | |
520 | * userui_prepare_console - Prepare the console for use. | |
521 | * | |
522 | * Prepare a console for use, saving current kmsg settings and attempting to | |
523 | * start userui. Console loglevel changes are handled by userui. | |
524 | */ | |
525 | static void userui_prepare_console(void) | |
526 | { | |
527 | orig_kmsg = vt_kmsg_redirect(fg_console + 1); | |
528 | ||
529 | ui_helper_data.pid = -1; | |
530 | ||
531 | if (!userui_ops.enabled) { | |
532 | printk(KERN_INFO "TuxOnIce: Userui disabled.\n"); | |
533 | return; | |
534 | } | |
535 | ||
536 | if (*ui_helper_data.program) | |
537 | toi_netlink_setup(&ui_helper_data); | |
538 | else | |
539 | printk(KERN_INFO "TuxOnIce: Userui program not configured.\n"); | |
540 | } | |
541 | ||
542 | /** | |
543 | * userui_cleanup_console - Cleanup after a cycle. | |
544 | * | |
545 | * Tell userui to cleanup, and restore kmsg_redirect to its original value. | |
546 | */ | |
547 | ||
548 | static void userui_cleanup_console(void) | |
549 | { | |
550 | if (ui_helper_data.pid > -1) | |
551 | toi_netlink_close(&ui_helper_data); | |
552 | ||
553 | vt_kmsg_redirect(orig_kmsg); | |
554 | } | |
555 | ||
556 | /* | |
557 | * User interface specific /sys/power/tuxonice entries. | |
558 | */ | |
559 | ||
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, | |
563 | TOI_CAN_CANCEL, 0), | |
564 | SYSFS_BIT("pause_between_steps", SYSFS_RW, &toi_bkd.toi_action, | |
565 | TOI_PAUSE, 0), | |
566 | SYSFS_INT("enabled", SYSFS_RW, &userui_ops.enabled, 0, 1, 0, NULL), | |
567 | SYSFS_INT("progress_granularity", SYSFS_RW, &progress_granularity, 1, | |
568 | 2048, 0, NULL), | |
569 | SYSFS_STRING("program", SYSFS_RW, ui_helper_data.program, 255, 0, | |
570 | set_ui_program_set), | |
571 | SYSFS_INT("debug", SYSFS_RW, &ui_helper_data.debug, 0, 1, 0, NULL) | |
572 | #endif | |
573 | }; | |
574 | ||
575 | static struct toi_module_ops userui_ops = { | |
576 | .type = MISC_MODULE, | |
577 | .name = "userui", | |
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), | |
587 | }; | |
588 | ||
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, | |
598 | }; | |
599 | ||
600 | /** | |
601 | * toi_user_ui_init - Boot time initialisation for user interface. | |
602 | * | |
603 | * Invoked from the core init routine. | |
604 | */ | |
605 | static __init int toi_user_ui_init(void) | |
606 | { | |
607 | int result; | |
608 | ||
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); | |
622 | if (!result) | |
623 | result = toi_register_ui_ops(&my_ui_ops); | |
624 | if (result) | |
625 | toi_unregister_module(&userui_ops); | |
626 | ||
627 | return result; | |
628 | } | |
629 | ||
630 | #ifdef MODULE | |
631 | /** | |
632 | * toi_user_ui_ext - Cleanup code for if the core is unloaded. | |
633 | */ | |
634 | static __exit void toi_user_ui_exit(void) | |
635 | { | |
636 | toi_netlink_close_complete(&ui_helper_data); | |
637 | toi_remove_ui_ops(&my_ui_ops); | |
638 | toi_unregister_module(&userui_ops); | |
639 | } | |
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"); | |
645 | #else | |
646 | late_initcall(toi_user_ui_init); | |
647 | #endif |