tuxonice_userui.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. /*
  2. * kernel/power/user_ui.c
  3. *
  4. * Copyright (C) 2005-2007 Bernard Blackham
  5. * Copyright (C) 2002-2014 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. #define __KERNEL_SYSCALLS__
  23. #include <linux/suspend.h>
  24. #include <linux/freezer.h>
  25. #include <linux/console.h>
  26. #include <linux/ctype.h>
  27. #include <linux/tty.h>
  28. #include <linux/vt_kern.h>
  29. #include <linux/reboot.h>
  30. #include <linux/security.h>
  31. #include <linux/syscalls.h>
  32. #include <linux/vt.h>
  33. #include "tuxonice_sysfs.h"
  34. #include "tuxonice_modules.h"
  35. #include "tuxonice.h"
  36. #include "tuxonice_ui.h"
  37. #include "tuxonice_netlink.h"
  38. #include "tuxonice_power_off.h"
  39. static char local_printf_buf[1024]; /* Same as printk - should be safe */
  40. static struct user_helper_data ui_helper_data;
  41. static struct toi_module_ops userui_ops;
  42. static int orig_kmsg;
  43. static char lastheader[512];
  44. static int lastheader_message_len;
  45. static int ui_helper_changed; /* Used at resume-time so don't overwrite value
  46. set from initrd/ramfs. */
  47. /* Number of distinct progress amounts that userspace can display */
  48. static int progress_granularity = 30;
  49. static DECLARE_WAIT_QUEUE_HEAD(userui_wait_for_key);
  50. static int userui_wait_should_wake;
  51. #define toi_stop_waiting_for_userui_key() \
  52. { \
  53. userui_wait_should_wake = true; \
  54. wake_up_interruptible(&userui_wait_for_key); \
  55. }
  56. /**
  57. * ui_nl_set_state - Update toi_action based on a message from userui.
  58. *
  59. * @n: The bit (1 << bit) to set.
  60. */
  61. static void ui_nl_set_state(int n)
  62. {
  63. /* Only let them change certain settings */
  64. static const u32 toi_action_mask =
  65. (1 << TOI_REBOOT) | (1 << TOI_PAUSE) |
  66. (1 << TOI_LOGALL) | (1 << TOI_SINGLESTEP) | (1 << TOI_PAUSE_NEAR_PAGESET_END);
  67. static unsigned long new_action;
  68. new_action = (toi_bkd.toi_action & (~toi_action_mask)) | (n & toi_action_mask);
  69. pr_debug("n is %x. Action flags being changed from %lx to %lx.", n, toi_bkd.toi_action, new_action);
  70. toi_bkd.toi_action = new_action;
  71. if (!test_action_state(TOI_PAUSE) && !test_action_state(TOI_SINGLESTEP))
  72. toi_stop_waiting_for_userui_key();
  73. }
  74. /**
  75. * userui_post_atomic_restore - Tell userui that atomic restore just happened.
  76. *
  77. * Tell userui that atomic restore just occurred, so that it can do things like
  78. * redrawing the screen, re-getting settings and so on.
  79. */
  80. static void userui_post_atomic_restore(struct toi_boot_kernel_data *bkd)
  81. {
  82. toi_send_netlink_message(&ui_helper_data, USERUI_MSG_POST_ATOMIC_RESTORE, NULL, 0);
  83. }
  84. /**
  85. * userui_storage_needed - Report how much memory in image header is needed.
  86. */
  87. static int userui_storage_needed(void)
  88. {
  89. return sizeof(ui_helper_data.program) + 1 + sizeof(int);
  90. }
  91. /**
  92. * userui_save_config_info - Fill buffer with config info for image header.
  93. *
  94. * @buf: Buffer into which to put the config info we want to save.
  95. */
  96. static int userui_save_config_info(char *buf)
  97. {
  98. *((int *)buf) = progress_granularity;
  99. memcpy(buf + sizeof(int), ui_helper_data.program, sizeof(ui_helper_data.program));
  100. return sizeof(ui_helper_data.program) + sizeof(int) + 1;
  101. }
  102. /**
  103. * userui_load_config_info - Restore config info from buffer.
  104. *
  105. * @buf: Buffer containing header info loaded.
  106. * @size: Size of data loaded for this module.
  107. */
  108. static void userui_load_config_info(char *buf, int size)
  109. {
  110. progress_granularity = *((int *)buf);
  111. size -= sizeof(int);
  112. /* Don't load the saved path if one has already been set */
  113. if (ui_helper_changed)
  114. return;
  115. if (size > sizeof(ui_helper_data.program))
  116. size = sizeof(ui_helper_data.program);
  117. memcpy(ui_helper_data.program, buf + sizeof(int), size);
  118. ui_helper_data.program[sizeof(ui_helper_data.program) - 1] = '\0';
  119. }
  120. /**
  121. * set_ui_program_set: Record that userui program was changed.
  122. *
  123. * Side effect routine for when the userui program is set. In an initrd or
  124. * ramfs, the user may set a location for the userui program. If this happens,
  125. * we don't want to reload the value that was saved in the image header. This
  126. * routine allows us to flag that we shouldn't restore the program name from
  127. * the image header.
  128. */
  129. static void set_ui_program_set(void)
  130. {
  131. ui_helper_changed = 1;
  132. }
  133. /**
  134. * userui_memory_needed - Tell core how much memory to reserve for us.
  135. */
  136. static int userui_memory_needed(void)
  137. {
  138. /* ball park figure of 128 pages */
  139. return 128 * PAGE_SIZE;
  140. }
  141. /**
  142. * userui_update_status - Update the progress bar and (if on) in-bar message.
  143. *
  144. * @value: Current progress percentage numerator.
  145. * @maximum: Current progress percentage denominator.
  146. * @fmt: Message to be displayed in the middle of the progress bar.
  147. *
  148. * Note that a NULL message does not mean that any previous message is erased!
  149. * For that, you need toi_prepare_status with clearbar on.
  150. *
  151. * Returns an unsigned long, being the next numerator (as determined by the
  152. * maximum and progress granularity) where status needs to be updated.
  153. * This is to reduce unnecessary calls to update_status.
  154. */
  155. static u32 userui_update_status(u32 value, u32 maximum, const char *fmt, ...)
  156. {
  157. static u32 last_step = 9999;
  158. struct userui_msg_params msg;
  159. u32 this_step, next_update;
  160. int bitshift;
  161. if (ui_helper_data.pid == -1)
  162. return 0;
  163. if ((!maximum) || (!progress_granularity))
  164. return maximum;
  165. if (value < 0)
  166. value = 0;
  167. if (value > maximum)
  168. value = maximum;
  169. /* Try to avoid math problems - we can't do 64 bit math here
  170. * (and shouldn't need it - anyone got screen resolution
  171. * of 65536 pixels or more?) */
  172. bitshift = fls(maximum) - 16;
  173. if (bitshift > 0) {
  174. u32 temp_maximum = maximum >> bitshift;
  175. u32 temp_value = value >> bitshift;
  176. this_step = (u32)
  177. (temp_value * progress_granularity / temp_maximum);
  178. next_update = (((this_step + 1) * temp_maximum /
  179. progress_granularity) + 1) << bitshift;
  180. } else {
  181. this_step = (u32) (value * progress_granularity / maximum);
  182. next_update = ((this_step + 1) * maximum / progress_granularity) + 1;
  183. }
  184. if (this_step == last_step)
  185. return next_update;
  186. memset(&msg, 0, sizeof(msg));
  187. msg.a = this_step;
  188. msg.b = progress_granularity;
  189. if (fmt) {
  190. va_list args;
  191. va_start(args, fmt);
  192. vsnprintf(msg.text, sizeof(msg.text), fmt, args);
  193. va_end(args);
  194. msg.text[sizeof(msg.text) - 1] = '\0';
  195. }
  196. toi_send_netlink_message(&ui_helper_data, USERUI_MSG_PROGRESS, &msg, sizeof(msg));
  197. last_step = this_step;
  198. return next_update;
  199. }
  200. /**
  201. * userui_message - Display a message without necessarily logging it.
  202. *
  203. * @section: Type of message. Messages can be filtered by type.
  204. * @level: Degree of importance of the message. Lower values = higher priority.
  205. * @normally_logged: Whether logged even if log_everything is off.
  206. * @fmt: Message (and parameters).
  207. *
  208. * This function is intended to do the same job as printk, but without normally
  209. * logging what is printed. The point is to be able to get debugging info on
  210. * screen without filling the logs with "1/534. ^M 2/534^M. 3/534^M"
  211. *
  212. * It may be called from an interrupt context - can't sleep!
  213. */
  214. static void userui_message(u32 section, u32 level, u32 normally_logged, const char *fmt, ...)
  215. {
  216. struct userui_msg_params msg;
  217. if ((level) && (level > console_loglevel))
  218. return;
  219. memset(&msg, 0, sizeof(msg));
  220. msg.a = section;
  221. msg.b = level;
  222. msg.c = normally_logged;
  223. if (fmt) {
  224. va_list args;
  225. va_start(args, fmt);
  226. vsnprintf(msg.text, sizeof(msg.text), fmt, args);
  227. va_end(args);
  228. msg.text[sizeof(msg.text) - 1] = '\0';
  229. }
  230. if (test_action_state(TOI_LOGALL))
  231. pr_warn("%s\n", msg.text);
  232. toi_send_netlink_message(&ui_helper_data, USERUI_MSG_MESSAGE, &msg, sizeof(msg));
  233. }
  234. /**
  235. * wait_for_key_via_userui - Wait for userui to receive a keypress.
  236. */
  237. static void wait_for_key_via_userui(void)
  238. {
  239. DECLARE_WAITQUEUE(wait, current);
  240. add_wait_queue(&userui_wait_for_key, &wait);
  241. set_current_state(TASK_INTERRUPTIBLE);
  242. wait_event_interruptible(userui_wait_for_key, userui_wait_should_wake);
  243. userui_wait_should_wake = false;
  244. set_current_state(TASK_RUNNING);
  245. remove_wait_queue(&userui_wait_for_key, &wait);
  246. }
  247. /**
  248. * userui_prepare_status - Display high level messages.
  249. *
  250. * @clearbar: Whether to clear the progress bar.
  251. * @fmt...: New message for the title.
  252. *
  253. * Prepare the 'nice display', drawing the header and version, along with the
  254. * current action and perhaps also resetting the progress bar.
  255. */
  256. static void userui_prepare_status(int clearbar, const char *fmt, ...)
  257. {
  258. va_list args;
  259. if (fmt) {
  260. va_start(args, fmt);
  261. lastheader_message_len = vsnprintf(lastheader, 512, fmt, args);
  262. va_end(args);
  263. }
  264. if (clearbar)
  265. toi_update_status(0, 1, NULL);
  266. if (ui_helper_data.pid == -1)
  267. pr_err("%s\n", lastheader);
  268. else
  269. toi_message(0, TOI_STATUS, 1, lastheader, NULL);
  270. }
  271. /**
  272. * toi_wait_for_keypress - Wait for keypress via userui.
  273. *
  274. * @timeout: Maximum time to wait.
  275. *
  276. * Wait for a keypress from userui.
  277. *
  278. * FIXME: Implement timeout?
  279. */
  280. static char userui_wait_for_keypress(int timeout)
  281. {
  282. char key = '\0';
  283. if (ui_helper_data.pid != -1) {
  284. wait_for_key_via_userui();
  285. key = ' ';
  286. }
  287. return key;
  288. }
  289. /**
  290. * userui_abort_hibernate - Abort a cycle & tell user if they didn't request it.
  291. *
  292. * @result_code: Reason why we're aborting (1 << bit).
  293. * @fmt: Message to display if telling the user what's going on.
  294. *
  295. * Abort a cycle. If this wasn't at the user's request (and we're displaying
  296. * output), tell the user why and wait for them to acknowledge the message.
  297. */
  298. static void userui_abort_hibernate(int result_code, const char *fmt, ...)
  299. {
  300. va_list args;
  301. int printed_len = 0;
  302. set_result_state(result_code);
  303. if (test_result_state(TOI_ABORTED))
  304. return;
  305. set_result_state(TOI_ABORTED);
  306. if (test_result_state(TOI_ABORT_REQUESTED))
  307. return;
  308. va_start(args, fmt);
  309. printed_len = vsnprintf(local_printf_buf, sizeof(local_printf_buf), fmt, args);
  310. va_end(args);
  311. if (ui_helper_data.pid != -1)
  312. printed_len = sprintf(local_printf_buf + printed_len, " (Press SPACE to continue)");
  313. toi_prepare_status(CLEAR_BAR, "%s", local_printf_buf);
  314. if (ui_helper_data.pid != -1)
  315. userui_wait_for_keypress(0);
  316. }
  317. /**
  318. * request_abort_hibernate - Abort hibernating or resuming at user request.
  319. *
  320. * Handle the user requesting the cancellation of a hibernation or resume by
  321. * pressing escape.
  322. */
  323. static void request_abort_hibernate(void)
  324. {
  325. if (test_result_state(TOI_ABORT_REQUESTED) || !test_action_state(TOI_CAN_CANCEL))
  326. return;
  327. if (test_toi_state(TOI_NOW_RESUMING)) {
  328. toi_prepare_status(CLEAR_BAR, "Escape pressed. " "Powering down again.");
  329. set_toi_state(TOI_STOP_RESUME);
  330. while (!test_toi_state(TOI_IO_STOPPED))
  331. schedule();
  332. if (toiActiveAllocator->mark_resume_attempted)
  333. toiActiveAllocator->mark_resume_attempted(0);
  334. toi_power_down();
  335. }
  336. toi_prepare_status(CLEAR_BAR, "--- ESCAPE PRESSED :" " ABORTING HIBERNATION ---");
  337. set_abort_result(TOI_ABORT_REQUESTED);
  338. toi_stop_waiting_for_userui_key();
  339. }
  340. /**
  341. * userui_user_rcv_msg - Receive a netlink message from userui.
  342. *
  343. * @skb: skb received.
  344. * @nlh: Netlink header received.
  345. */
  346. static int userui_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
  347. {
  348. int type;
  349. int *data;
  350. type = nlh->nlmsg_type;
  351. /* A control message: ignore them */
  352. if (type < NETLINK_MSG_BASE)
  353. return 0;
  354. /* Unknown message: reply with EINVAL */
  355. if (type >= USERUI_MSG_MAX)
  356. return -EINVAL;
  357. /* All operations require privileges, even GET */
  358. if (!capable(CAP_NET_ADMIN))
  359. return -EPERM;
  360. /* Only allow one task to receive NOFREEZE privileges */
  361. if (type == NETLINK_MSG_NOFREEZE_ME && ui_helper_data.pid != -1) {
  362. pr_warn("Got NOFREEZE_ME request when ui_helper_data.pid is %d.\n", ui_helper_data.pid);
  363. return -EBUSY;
  364. }
  365. data = (int *)NLMSG_DATA(nlh);
  366. switch (type) {
  367. case USERUI_MSG_ABORT:
  368. request_abort_hibernate();
  369. return 0;
  370. case USERUI_MSG_GET_STATE:
  371. toi_send_netlink_message(&ui_helper_data,
  372. USERUI_MSG_GET_STATE, &toi_bkd.toi_action,
  373. sizeof(toi_bkd.toi_action));
  374. return 0;
  375. case USERUI_MSG_GET_DEBUG_STATE:
  376. toi_send_netlink_message(&ui_helper_data,
  377. USERUI_MSG_GET_DEBUG_STATE,
  378. &toi_bkd.toi_debug_state, sizeof(toi_bkd.toi_debug_state));
  379. return 0;
  380. case USERUI_MSG_SET_STATE:
  381. if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(int)))
  382. return -EINVAL;
  383. ui_nl_set_state(*data);
  384. return 0;
  385. case USERUI_MSG_SET_DEBUG_STATE:
  386. if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(int)))
  387. return -EINVAL;
  388. toi_bkd.toi_debug_state = (*data);
  389. return 0;
  390. case USERUI_MSG_SPACE:
  391. toi_stop_waiting_for_userui_key();
  392. return 0;
  393. case USERUI_MSG_GET_POWERDOWN_METHOD:
  394. toi_send_netlink_message(&ui_helper_data,
  395. USERUI_MSG_GET_POWERDOWN_METHOD,
  396. &toi_poweroff_method, sizeof(toi_poweroff_method));
  397. return 0;
  398. case USERUI_MSG_SET_POWERDOWN_METHOD:
  399. if (nlh->nlmsg_len != NLMSG_LENGTH(sizeof(char)))
  400. return -EINVAL;
  401. toi_poweroff_method = (unsigned long)(*data);
  402. return 0;
  403. case USERUI_MSG_GET_LOGLEVEL:
  404. toi_send_netlink_message(&ui_helper_data,
  405. USERUI_MSG_GET_LOGLEVEL,
  406. &toi_bkd.toi_default_console_level,
  407. sizeof(toi_bkd.toi_default_console_level));
  408. return 0;
  409. case USERUI_MSG_SET_LOGLEVEL:
  410. if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(int)))
  411. return -EINVAL;
  412. toi_bkd.toi_default_console_level = (*data);
  413. return 0;
  414. case USERUI_MSG_PRINTK:
  415. pr_warn("%s", (char *)data);
  416. return 0;
  417. }
  418. /* Unhandled here */
  419. return 1;
  420. }
  421. /**
  422. * userui_cond_pause - Possibly pause at user request.
  423. *
  424. * @pause: Whether to pause or just display the message.
  425. * @message: Message to display at the start of pausing.
  426. *
  427. * Potentially pause and wait for the user to tell us to continue. We normally
  428. * only pause when @pause is set. While paused, the user can do things like
  429. * changing the loglevel, toggling the display of debugging sections and such
  430. * like.
  431. */
  432. static void userui_cond_pause(int pause, char *message)
  433. {
  434. int displayed_message = 0, last_key = 0;
  435. while (last_key != 32 &&
  436. ui_helper_data.pid != -1 &&
  437. ((test_action_state(TOI_PAUSE) && pause) || (test_action_state(TOI_SINGLESTEP)))) {
  438. if (!displayed_message) {
  439. toi_prepare_status(DONT_CLEAR_BAR,
  440. "%s Press SPACE to continue.%s",
  441. message ? message : "",
  442. (test_action_state(TOI_SINGLESTEP)) ?
  443. " Single step on." : "");
  444. displayed_message = 1;
  445. }
  446. last_key = userui_wait_for_keypress(0);
  447. }
  448. schedule();
  449. }
  450. /**
  451. * userui_prepare_console - Prepare the console for use.
  452. *
  453. * Prepare a console for use, saving current kmsg settings and attempting to
  454. * start userui. Console loglevel changes are handled by userui.
  455. */
  456. static void userui_prepare_console(void)
  457. {
  458. orig_kmsg = vt_kmsg_redirect(fg_console + 1);
  459. ui_helper_data.pid = -1;
  460. if (!userui_ops.enabled) {
  461. pr_warn("TuxOnIce: Userui disabled.\n");
  462. return;
  463. }
  464. if (*ui_helper_data.program)
  465. toi_netlink_setup(&ui_helper_data);
  466. else
  467. pr_warn("TuxOnIce: Userui program not configured.\n");
  468. }
  469. /**
  470. * userui_cleanup_console - Cleanup after a cycle.
  471. *
  472. * Tell userui to cleanup, and restore kmsg_redirect to its original value.
  473. */
  474. static void userui_cleanup_console(void)
  475. {
  476. if (ui_helper_data.pid > -1)
  477. toi_netlink_close(&ui_helper_data);
  478. vt_kmsg_redirect(orig_kmsg);
  479. }
  480. /*
  481. * User interface specific /sys/power/tuxonice entries.
  482. */
  483. static struct toi_sysfs_data sysfs_params[] = {
  484. #if defined(CONFIG_NET) && defined(CONFIG_SYSFS)
  485. SYSFS_BIT("enable_escape", SYSFS_RW, &toi_bkd.toi_action,
  486. TOI_CAN_CANCEL, 0),
  487. SYSFS_BIT("pause_between_steps", SYSFS_RW, &toi_bkd.toi_action,
  488. TOI_PAUSE, 0),
  489. SYSFS_INT("enabled", SYSFS_RW, &userui_ops.enabled, 0, 1, 0, NULL),
  490. SYSFS_INT("progress_granularity", SYSFS_RW, &progress_granularity, 1,
  491. 2048, 0, NULL),
  492. SYSFS_STRING("program", SYSFS_RW, ui_helper_data.program, 255, 0,
  493. set_ui_program_set),
  494. SYSFS_INT("debug", SYSFS_RW, &ui_helper_data.debug, 0, 1, 0, NULL)
  495. #endif
  496. };
  497. static struct toi_module_ops userui_ops = {
  498. .type = MISC_MODULE,
  499. .name = "userui",
  500. .shared_directory = "user_interface",
  501. .module = THIS_MODULE,
  502. .storage_needed = userui_storage_needed,
  503. .save_config_info = userui_save_config_info,
  504. .load_config_info = userui_load_config_info,
  505. .memory_needed = userui_memory_needed,
  506. .post_atomic_restore = userui_post_atomic_restore,
  507. .sysfs_data = sysfs_params,
  508. .num_sysfs_entries = sizeof(sysfs_params) / sizeof(struct toi_sysfs_data),
  509. };
  510. static struct ui_ops my_ui_ops = {
  511. .update_status = userui_update_status,
  512. .message = userui_message,
  513. .prepare_status = userui_prepare_status,
  514. .abort = userui_abort_hibernate,
  515. .cond_pause = userui_cond_pause,
  516. .prepare = userui_prepare_console,
  517. .cleanup = userui_cleanup_console,
  518. .wait_for_key = userui_wait_for_keypress,
  519. };
  520. /**
  521. * toi_user_ui_init - Boot time initialisation for user interface.
  522. *
  523. * Invoked from the core init routine.
  524. */
  525. static __init int toi_user_ui_init(void)
  526. {
  527. int result;
  528. ui_helper_data.nl = NULL;
  529. strncpy(ui_helper_data.program, CONFIG_TOI_USERUI_DEFAULT_PATH, 255);
  530. ui_helper_data.pid = -1;
  531. ui_helper_data.skb_size = sizeof(struct userui_msg_params);
  532. ui_helper_data.pool_limit = 6;
  533. ui_helper_data.netlink_id = NETLINK_TOI_USERUI;
  534. ui_helper_data.name = "userspace ui";
  535. ui_helper_data.rcv_msg = userui_user_rcv_msg;
  536. ui_helper_data.interface_version = 8;
  537. ui_helper_data.must_init = 0;
  538. ui_helper_data.not_ready = userui_cleanup_console;
  539. init_completion(&ui_helper_data.wait_for_process);
  540. result = toi_register_module(&userui_ops);
  541. if (!result) {
  542. result = toi_register_ui_ops(&my_ui_ops);
  543. if (result)
  544. toi_unregister_module(&userui_ops);
  545. }
  546. return result;
  547. }
  548. #ifdef MODULE
  549. /**
  550. * toi_user_ui_ext - Cleanup code for if the core is unloaded.
  551. */
  552. static __exit void toi_user_ui_exit(void)
  553. {
  554. toi_netlink_close_complete(&ui_helper_data);
  555. toi_remove_ui_ops(&my_ui_ops);
  556. toi_unregister_module(&userui_ops);
  557. }
  558. module_init(toi_user_ui_init);
  559. module_exit(toi_user_ui_exit);
  560. MODULE_AUTHOR("Nigel Cunningham");
  561. MODULE_DESCRIPTION("TuxOnIce Userui Support");
  562. MODULE_LICENSE("GPL");
  563. #else
  564. late_initcall(toi_user_ui_init);
  565. #endif