| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- /*
- * kernel/power/tuxonice_power_off.c
- *
- * Copyright (C) 2006-2014 Nigel Cunningham (nigel at tuxonice net)
- *
- * This file is released under the GPLv2.
- *
- * Support for powering down.
- */
- #include <linux/device.h>
- #include <linux/suspend.h>
- #include <linux/mm.h>
- #include <linux/pm.h>
- #include <linux/reboot.h>
- #include <linux/cpu.h>
- #include <linux/console.h>
- #include <linux/fs.h>
- #include "tuxonice.h"
- #include "tuxonice_ui.h"
- #include "tuxonice_power_off.h"
- #include "tuxonice_sysfs.h"
- #include "tuxonice_modules.h"
- #include "tuxonice_io.h"
- unsigned long toi_poweroff_method; /* 0 - Kernel power off */
- EXPORT_SYMBOL_GPL(toi_poweroff_method);
- static int wake_delay;
- static char lid_state_file[256], wake_alarm_dir[256];
- static struct file *lid_file, *alarm_file, *epoch_file;
- static int post_wake_state = -1;
- static int did_suspend_to_both;
- #ifdef CONFIG_TOI_ENHANCE
- int hybrid_sleep_mode(void)
- {
- int retval = 0;
- if (toi_poweroff_method == 3 && did_suspend_to_both == 1)
- retval = 1;
- return retval;
- }
- EXPORT_SYMBOL_GPL(hybrid_sleep_mode);
- #endif
- /*
- * __toi_power_down
- * Functionality : Powers down or reboots the computer once the image
- * has been written to disk.
- * Key Assumptions : Able to reboot/power down via code called or that
- * the warning emitted if the calls fail will be visible
- * to the user (ie print resumes devices).
- */
- static void __toi_power_down(int method)
- {
- int error;
- toi_cond_pause(1, test_action_state(TOI_REBOOT) ? "Ready to reboot." :
- "Powering down.");
- if (test_result_state(TOI_ABORTED))
- goto out;
- if (test_action_state(TOI_REBOOT))
- kernel_restart(NULL);
- switch (method) {
- case 0:
- break;
- case 3:
- /*
- * Re-read the overwritten part of pageset2 to make post-resume
- * faster.
- */
- if (read_pageset2(1))
- panic("Attempt to reload pagedir 2 failed. Try rebooting.");
- pm_prepare_console();
- error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
- if (!error) {
- pm_restore_gfp_mask();
- error = suspend_devices_and_enter(PM_SUSPEND_MEM);
- pm_restrict_gfp_mask();
- if (!error)
- did_suspend_to_both = 1;
- }
- pm_notifier_call_chain(PM_POST_SUSPEND);
- pm_restore_console();
- /* Success - we're now post-resume-from-ram */
- if (did_suspend_to_both)
- return;
- /* Failed to suspend to ram - do normal power off */
- break;
- case 4:
- /*
- * If succeeds, doesn't return. If fails, do a simple
- * powerdown.
- */
- hibernation_platform_enter();
- break;
- case 5:
- /* Historic entry only now */
- break;
- }
- if (method && method != 5)
- toi_cond_pause(1,
- "Falling back to alternate power off method.");
- if (test_result_state(TOI_ABORTED))
- goto out;
- kernel_power_off();
- kernel_halt();
- toi_cond_pause(1, "Powerdown failed.");
- while (1)
- cpu_relax();
- out:
- if (read_pageset2(1))
- panic("Attempt to reload pagedir 2 failed. Try rebooting.");
- }
- #define CLOSE_FILE(file) \
- { \
- if (file) { \
- filp_close(file, NULL); \
- file = NULL; \
- } \
- }
- static void powerdown_cleanup(int toi_or_resume)
- {
- if (!toi_or_resume)
- return;
- CLOSE_FILE(lid_file);
- CLOSE_FILE(alarm_file);
- CLOSE_FILE(epoch_file);
- }
- static void open_file(char *format, char *arg, struct file **var, int mode,
- char *desc)
- {
- char buf[256];
- if (strlen(arg)) {
- sprintf(buf, format, arg);
- *var = filp_open(buf, mode, 0);
- if (IS_ERR(*var) || !*var) {
- pr_warn("Failed to open %s file '%s' (%p).\n",
- desc, buf, *var);
- *var = NULL;
- }
- }
- }
- static int powerdown_init(int toi_or_resume)
- {
- if (!toi_or_resume)
- return 0;
- did_suspend_to_both = 0;
- open_file("/proc/acpi/button/%s/state", lid_state_file, &lid_file,
- O_RDONLY, "lid");
- if (strlen(wake_alarm_dir)) {
- open_file("/sys/class/rtc/%s/wakealarm", wake_alarm_dir,
- &alarm_file, O_WRONLY, "alarm");
- open_file("/sys/class/rtc/%s/since_epoch", wake_alarm_dir,
- &epoch_file, O_RDONLY, "epoch");
- }
- return 0;
- }
- static int lid_closed(void)
- {
- char array[25];
- ssize_t size;
- loff_t pos = 0;
- if (!lid_file)
- return 0;
- size = vfs_read(lid_file, (char __user *) array, 25, &pos);
- if ((int) size < 1) {
- pr_warn("Failed to read lid state file (%d).\n",
- (int) size);
- return 0;
- }
- if (!strcmp(array, "state: closed\n"))
- return 1;
- return 0;
- }
- static void write_alarm_file(int value)
- {
- ssize_t size;
- char buf[40];
- loff_t pos = 0;
- if (!alarm_file)
- return;
- sprintf(buf, "%d\n", value);
- size = vfs_write(alarm_file, (char __user *)buf, strlen(buf), &pos);
- if (size < 0)
- pr_warn("Error %d writing alarm value %s.\n",
- (int) size, buf);
- }
- /**
- * toi_check_resleep: See whether to powerdown again after waking.
- *
- * After waking, check whether we should powerdown again in a (usually
- * different) way. We only do this if the lid switch is still closed.
- */
- void toi_check_resleep(void)
- {
- /* We only return if we suspended to ram and woke. */
- if (lid_closed() && post_wake_state >= 0)
- __toi_power_down(post_wake_state);
- }
- void toi_power_down(void)
- {
- if (alarm_file && wake_delay) {
- char array[25];
- loff_t pos = 0;
- size_t size = vfs_read(epoch_file, (char __user *) array, 25,
- &pos);
- if (((int) size) < 1)
- pr_warn("Failed to read epoch file (%d).\n",
- (int) size);
- else {
- unsigned long since_epoch;
- if (!kstrtoul(array, 0, &since_epoch)) {
- /* Clear any wakeup time. */
- write_alarm_file(0);
- /* Set new wakeup time. */
- write_alarm_file(since_epoch + wake_delay);
- }
- }
- }
- __toi_power_down(toi_poweroff_method);
- toi_check_resleep();
- }
- EXPORT_SYMBOL_GPL(toi_power_down);
- static struct toi_sysfs_data sysfs_params[] = {
- #if defined(CONFIG_ACPI)
- SYSFS_STRING("lid_file", SYSFS_RW, lid_state_file, 256, 0, NULL),
- SYSFS_INT("wake_delay", SYSFS_RW, &wake_delay, 0, INT_MAX, 0, NULL),
- SYSFS_STRING("wake_alarm_dir", SYSFS_RW, wake_alarm_dir, 256, 0, NULL),
- SYSFS_INT("post_wake_state", SYSFS_RW, &post_wake_state, -1, 5, 0,
- NULL),
- SYSFS_UL("powerdown_method", SYSFS_RW, &toi_poweroff_method, 0, 5, 0),
- SYSFS_INT("did_suspend_to_both", SYSFS_READONLY, &did_suspend_to_both,
- 0, 0, 0, NULL)
- #endif
- };
- static struct toi_module_ops powerdown_ops = {
- .type = MISC_HIDDEN_MODULE,
- .name = "poweroff",
- .initialise = powerdown_init,
- .cleanup = powerdown_cleanup,
- .directory = "[ROOT]",
- .module = THIS_MODULE,
- .sysfs_data = sysfs_params,
- .num_sysfs_entries = sizeof(sysfs_params) /
- sizeof(struct toi_sysfs_data),
- };
- int toi_poweroff_init(void)
- {
- return toi_register_module(&powerdown_ops);
- }
- void toi_poweroff_exit(void)
- {
- toi_unregister_module(&powerdown_ops);
- }
|