tuxonice_power_off.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*
  2. * kernel/power/tuxonice_power_off.c
  3. *
  4. * Copyright (C) 2006-2014 Nigel Cunningham (nigel at tuxonice net)
  5. *
  6. * This file is released under the GPLv2.
  7. *
  8. * Support for powering down.
  9. */
  10. #include <linux/device.h>
  11. #include <linux/suspend.h>
  12. #include <linux/mm.h>
  13. #include <linux/pm.h>
  14. #include <linux/reboot.h>
  15. #include <linux/cpu.h>
  16. #include <linux/console.h>
  17. #include <linux/fs.h>
  18. #include "tuxonice.h"
  19. #include "tuxonice_ui.h"
  20. #include "tuxonice_power_off.h"
  21. #include "tuxonice_sysfs.h"
  22. #include "tuxonice_modules.h"
  23. #include "tuxonice_io.h"
  24. unsigned long toi_poweroff_method; /* 0 - Kernel power off */
  25. EXPORT_SYMBOL_GPL(toi_poweroff_method);
  26. static int wake_delay;
  27. static char lid_state_file[256], wake_alarm_dir[256];
  28. static struct file *lid_file, *alarm_file, *epoch_file;
  29. static int post_wake_state = -1;
  30. static int did_suspend_to_both;
  31. #ifdef CONFIG_TOI_ENHANCE
  32. int hybrid_sleep_mode(void)
  33. {
  34. int retval = 0;
  35. if (toi_poweroff_method == 3 && did_suspend_to_both == 1)
  36. retval = 1;
  37. return retval;
  38. }
  39. EXPORT_SYMBOL_GPL(hybrid_sleep_mode);
  40. #endif
  41. /*
  42. * __toi_power_down
  43. * Functionality : Powers down or reboots the computer once the image
  44. * has been written to disk.
  45. * Key Assumptions : Able to reboot/power down via code called or that
  46. * the warning emitted if the calls fail will be visible
  47. * to the user (ie print resumes devices).
  48. */
  49. static void __toi_power_down(int method)
  50. {
  51. int error;
  52. toi_cond_pause(1, test_action_state(TOI_REBOOT) ? "Ready to reboot." :
  53. "Powering down.");
  54. if (test_result_state(TOI_ABORTED))
  55. goto out;
  56. if (test_action_state(TOI_REBOOT))
  57. kernel_restart(NULL);
  58. switch (method) {
  59. case 0:
  60. break;
  61. case 3:
  62. /*
  63. * Re-read the overwritten part of pageset2 to make post-resume
  64. * faster.
  65. */
  66. if (read_pageset2(1))
  67. panic("Attempt to reload pagedir 2 failed. Try rebooting.");
  68. pm_prepare_console();
  69. error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
  70. if (!error) {
  71. pm_restore_gfp_mask();
  72. error = suspend_devices_and_enter(PM_SUSPEND_MEM);
  73. pm_restrict_gfp_mask();
  74. if (!error)
  75. did_suspend_to_both = 1;
  76. }
  77. pm_notifier_call_chain(PM_POST_SUSPEND);
  78. pm_restore_console();
  79. /* Success - we're now post-resume-from-ram */
  80. if (did_suspend_to_both)
  81. return;
  82. /* Failed to suspend to ram - do normal power off */
  83. break;
  84. case 4:
  85. /*
  86. * If succeeds, doesn't return. If fails, do a simple
  87. * powerdown.
  88. */
  89. hibernation_platform_enter();
  90. break;
  91. case 5:
  92. /* Historic entry only now */
  93. break;
  94. }
  95. if (method && method != 5)
  96. toi_cond_pause(1,
  97. "Falling back to alternate power off method.");
  98. if (test_result_state(TOI_ABORTED))
  99. goto out;
  100. kernel_power_off();
  101. kernel_halt();
  102. toi_cond_pause(1, "Powerdown failed.");
  103. while (1)
  104. cpu_relax();
  105. out:
  106. if (read_pageset2(1))
  107. panic("Attempt to reload pagedir 2 failed. Try rebooting.");
  108. }
  109. #define CLOSE_FILE(file) \
  110. { \
  111. if (file) { \
  112. filp_close(file, NULL); \
  113. file = NULL; \
  114. } \
  115. }
  116. static void powerdown_cleanup(int toi_or_resume)
  117. {
  118. if (!toi_or_resume)
  119. return;
  120. CLOSE_FILE(lid_file);
  121. CLOSE_FILE(alarm_file);
  122. CLOSE_FILE(epoch_file);
  123. }
  124. static void open_file(char *format, char *arg, struct file **var, int mode,
  125. char *desc)
  126. {
  127. char buf[256];
  128. if (strlen(arg)) {
  129. sprintf(buf, format, arg);
  130. *var = filp_open(buf, mode, 0);
  131. if (IS_ERR(*var) || !*var) {
  132. pr_warn("Failed to open %s file '%s' (%p).\n",
  133. desc, buf, *var);
  134. *var = NULL;
  135. }
  136. }
  137. }
  138. static int powerdown_init(int toi_or_resume)
  139. {
  140. if (!toi_or_resume)
  141. return 0;
  142. did_suspend_to_both = 0;
  143. open_file("/proc/acpi/button/%s/state", lid_state_file, &lid_file,
  144. O_RDONLY, "lid");
  145. if (strlen(wake_alarm_dir)) {
  146. open_file("/sys/class/rtc/%s/wakealarm", wake_alarm_dir,
  147. &alarm_file, O_WRONLY, "alarm");
  148. open_file("/sys/class/rtc/%s/since_epoch", wake_alarm_dir,
  149. &epoch_file, O_RDONLY, "epoch");
  150. }
  151. return 0;
  152. }
  153. static int lid_closed(void)
  154. {
  155. char array[25];
  156. ssize_t size;
  157. loff_t pos = 0;
  158. if (!lid_file)
  159. return 0;
  160. size = vfs_read(lid_file, (char __user *) array, 25, &pos);
  161. if ((int) size < 1) {
  162. pr_warn("Failed to read lid state file (%d).\n",
  163. (int) size);
  164. return 0;
  165. }
  166. if (!strcmp(array, "state: closed\n"))
  167. return 1;
  168. return 0;
  169. }
  170. static void write_alarm_file(int value)
  171. {
  172. ssize_t size;
  173. char buf[40];
  174. loff_t pos = 0;
  175. if (!alarm_file)
  176. return;
  177. sprintf(buf, "%d\n", value);
  178. size = vfs_write(alarm_file, (char __user *)buf, strlen(buf), &pos);
  179. if (size < 0)
  180. pr_warn("Error %d writing alarm value %s.\n",
  181. (int) size, buf);
  182. }
  183. /**
  184. * toi_check_resleep: See whether to powerdown again after waking.
  185. *
  186. * After waking, check whether we should powerdown again in a (usually
  187. * different) way. We only do this if the lid switch is still closed.
  188. */
  189. void toi_check_resleep(void)
  190. {
  191. /* We only return if we suspended to ram and woke. */
  192. if (lid_closed() && post_wake_state >= 0)
  193. __toi_power_down(post_wake_state);
  194. }
  195. void toi_power_down(void)
  196. {
  197. if (alarm_file && wake_delay) {
  198. char array[25];
  199. loff_t pos = 0;
  200. size_t size = vfs_read(epoch_file, (char __user *) array, 25,
  201. &pos);
  202. if (((int) size) < 1)
  203. pr_warn("Failed to read epoch file (%d).\n",
  204. (int) size);
  205. else {
  206. unsigned long since_epoch;
  207. if (!kstrtoul(array, 0, &since_epoch)) {
  208. /* Clear any wakeup time. */
  209. write_alarm_file(0);
  210. /* Set new wakeup time. */
  211. write_alarm_file(since_epoch + wake_delay);
  212. }
  213. }
  214. }
  215. __toi_power_down(toi_poweroff_method);
  216. toi_check_resleep();
  217. }
  218. EXPORT_SYMBOL_GPL(toi_power_down);
  219. static struct toi_sysfs_data sysfs_params[] = {
  220. #if defined(CONFIG_ACPI)
  221. SYSFS_STRING("lid_file", SYSFS_RW, lid_state_file, 256, 0, NULL),
  222. SYSFS_INT("wake_delay", SYSFS_RW, &wake_delay, 0, INT_MAX, 0, NULL),
  223. SYSFS_STRING("wake_alarm_dir", SYSFS_RW, wake_alarm_dir, 256, 0, NULL),
  224. SYSFS_INT("post_wake_state", SYSFS_RW, &post_wake_state, -1, 5, 0,
  225. NULL),
  226. SYSFS_UL("powerdown_method", SYSFS_RW, &toi_poweroff_method, 0, 5, 0),
  227. SYSFS_INT("did_suspend_to_both", SYSFS_READONLY, &did_suspend_to_both,
  228. 0, 0, 0, NULL)
  229. #endif
  230. };
  231. static struct toi_module_ops powerdown_ops = {
  232. .type = MISC_HIDDEN_MODULE,
  233. .name = "poweroff",
  234. .initialise = powerdown_init,
  235. .cleanup = powerdown_cleanup,
  236. .directory = "[ROOT]",
  237. .module = THIS_MODULE,
  238. .sysfs_data = sysfs_params,
  239. .num_sysfs_entries = sizeof(sysfs_params) /
  240. sizeof(struct toi_sysfs_data),
  241. };
  242. int toi_poweroff_init(void)
  243. {
  244. return toi_register_module(&powerdown_ops);
  245. }
  246. void toi_poweroff_exit(void)
  247. {
  248. toi_unregister_module(&powerdown_ops);
  249. }