2 * kernel/power/tuxonice_power_off.c
4 * Copyright (C) 2006-2010 Nigel Cunningham (nigel at tuxonice net)
6 * This file is released under the GPLv2.
8 * Support for powering down.
11 #include <linux/device.h>
12 #include <linux/suspend.h>
15 #include <linux/reboot.h>
16 #include <linux/cpu.h>
17 #include <linux/console.h>
20 #include "tuxonice_ui.h"
21 #include "tuxonice_power_off.h"
22 #include "tuxonice_sysfs.h"
23 #include "tuxonice_modules.h"
24 #include "tuxonice_io.h"
26 unsigned long toi_poweroff_method
; /* 0 - Kernel power off */
27 EXPORT_SYMBOL_GPL(toi_poweroff_method
);
29 static int wake_delay
;
30 static char lid_state_file
[256], wake_alarm_dir
[256];
31 static struct file
*lid_file
, *alarm_file
, *epoch_file
;
32 static int post_wake_state
= -1;
34 static int did_suspend_to_both
;
36 int hybrid_sleep_mode(void)
39 if (toi_poweroff_method
== 3 && did_suspend_to_both
== 1)
43 EXPORT_SYMBOL_GPL(hybrid_sleep_mode
);
47 * Functionality : Powers down or reboots the computer once the image
48 * has been written to disk.
49 * Key Assumptions : Able to reboot/power down via code called or that
50 * the warning emitted if the calls fail will be visible
51 * to the user (ie printk resumes devices).
54 static void __toi_power_down(int method
)
58 toi_cond_pause(1, test_action_state(TOI_REBOOT
) ? "Ready to reboot." : "Powering down.");
60 if (test_result_state(TOI_ABORTED
))
63 if (test_action_state(TOI_REBOOT
))
71 * Re-read the overwritten part of pageset2 to make post-resume
75 panic("Attempt to reload pagedir 2 failed. " "Try rebooting.");
79 error
= pm_notifier_call_chain(PM_SUSPEND_PREPARE
);
81 pm_restore_gfp_mask();
82 error
= suspend_devices_and_enter(PM_SUSPEND_MEM
);
83 pm_restrict_gfp_mask();
85 did_suspend_to_both
= 1;
87 pm_notifier_call_chain(PM_POST_SUSPEND
);
90 /* Success - we're now post-resume-from-ram */
91 if (did_suspend_to_both
)
94 /* Failed to suspend to ram - do normal power off */
98 * If succeeds, doesn't return. If fails, do a simple
101 hibernation_platform_enter();
104 /* Historic entry only now */
108 if (method
&& method
!= 5)
109 toi_cond_pause(1, "Falling back to alternate power off method.");
111 if (test_result_state(TOI_ABORTED
))
116 toi_cond_pause(1, "Powerdown failed.");
121 if (read_pageset2(1))
122 panic("Attempt to reload pagedir 2 failed. Try rebooting.");
126 #define CLOSE_FILE(file) \
128 filp_close(file, NULL); file = NULL; \
131 static void powerdown_cleanup(int toi_or_resume
)
136 CLOSE_FILE(lid_file
);
137 CLOSE_FILE(alarm_file
);
138 CLOSE_FILE(epoch_file
);
141 static void open_file(char *format
, char *arg
, struct file
**var
, int mode
, char *desc
)
146 sprintf(buf
, format
, arg
);
147 *var
= filp_open(buf
, mode
, 0);
148 if (IS_ERR(*var
) || !*var
) {
149 printk(KERN_INFO
"Failed to open %s file '%s' (%p).\n", desc
, buf
, *var
);
155 static int powerdown_init(int toi_or_resume
)
160 did_suspend_to_both
= 0;
162 open_file("/proc/acpi/button/%s/state", lid_state_file
, &lid_file
, O_RDONLY
, "lid");
164 if (strlen(wake_alarm_dir
)) {
165 open_file("/sys/class/rtc/%s/wakealarm", wake_alarm_dir
,
166 &alarm_file
, O_WRONLY
, "alarm");
168 open_file("/sys/class/rtc/%s/since_epoch", wake_alarm_dir
,
169 &epoch_file
, O_RDONLY
, "epoch");
175 static int lid_closed(void)
184 size
= vfs_read(lid_file
, (char __user
*)array
, 25, &pos
);
186 printk(KERN_INFO
"Failed to read lid state file (%d).\n", (int)size
);
190 if (!strcmp(array
, "state: closed\n"))
196 static void write_alarm_file(int value
)
205 sprintf(buf
, "%d\n", value
);
207 size
= vfs_write(alarm_file
, (char __user
*)buf
, strlen(buf
), &pos
);
210 printk(KERN_INFO
"Error %d writing alarm value %s.\n", (int)size
, buf
);
214 * toi_check_resleep: See whether to powerdown again after waking.
216 * After waking, check whether we should powerdown again in a (usually
217 * different) way. We only do this if the lid switch is still closed.
219 void toi_check_resleep(void)
221 /* We only return if we suspended to ram and woke. */
222 if (lid_closed() && post_wake_state
>= 0)
223 __toi_power_down(post_wake_state
);
226 void toi_power_down(void)
228 if (alarm_file
&& wake_delay
) {
231 size_t size
= vfs_read(epoch_file
, (char __user
*)array
, 25,
235 printk(KERN_INFO
"Failed to read epoch file (%d).\n", (int)size
);
237 unsigned long since_epoch
;
238 if (!strict_strtoul(array
, 0, &since_epoch
)) {
239 /* Clear any wakeup time. */
242 /* Set new wakeup time. */
243 write_alarm_file(since_epoch
+ wake_delay
);
248 __toi_power_down(toi_poweroff_method
);
252 EXPORT_SYMBOL_GPL(toi_power_down
);
254 static struct toi_sysfs_data sysfs_params
[] = {
255 #if defined(CONFIG_ACPI)
256 SYSFS_STRING("lid_file", SYSFS_RW
, lid_state_file
, 256, 0, NULL
),
257 SYSFS_INT("wake_delay", SYSFS_RW
, &wake_delay
, 0, INT_MAX
, 0, NULL
),
258 SYSFS_STRING("wake_alarm_dir", SYSFS_RW
, wake_alarm_dir
, 256, 0, NULL
),
259 SYSFS_INT("post_wake_state", SYSFS_RW
, &post_wake_state
, -1, 5, 0,
262 SYSFS_UL("powerdown_method", SYSFS_RW
, &toi_poweroff_method
, 0, 5, 0),
263 SYSFS_INT("did_suspend_to_both", SYSFS_READONLY
, &did_suspend_to_both
,
267 static struct toi_module_ops powerdown_ops
= {
268 .type
= MISC_HIDDEN_MODULE
,
270 .initialise
= powerdown_init
,
271 .cleanup
= powerdown_cleanup
,
272 .directory
= "[ROOT]",
273 .module
= THIS_MODULE
,
274 .sysfs_data
= sysfs_params
,
275 .num_sysfs_entries
= sizeof(sysfs_params
) / sizeof(struct toi_sysfs_data
),
278 int toi_poweroff_init(void)
280 return toi_register_module(&powerdown_ops
);
283 void toi_poweroff_exit(void)
285 toi_unregister_module(&powerdown_ops
);