diff options
author | Henrique de Moraes Holschuh <hmh@hmh.eng.br> | 2008-01-08 10:02:52 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2008-02-01 22:26:08 -0500 |
commit | a713b4d7bca51e56cdb5357507f46674111d032c (patch) | |
tree | 6269011e5c2971df32c552eb60879c5a176de939 /drivers | |
parent | 3b64b51d20d9b633bb2efe63af785a49f8092898 (diff) |
ACPI: thinkpad-acpi: wakeup on hotunplug reporting
Handle some HKEY events that the firmware uses to report the reason for a
wake up, and to also notify that the system could go back to sleep (if it
woke up just to eject something from the bay, or to undock).
The driver will report the reason of the last wake up in the sysfs
attribute "wakeup_reason": 0 for "none, unknown, or standard ACPI wake up
event", 1 for "bay ejection request" and 2 for "undock request".
The firmware will also report if the operation that triggered the wake up
has been completed, by issuing an HKEY 0x3003 or 0x4003 event. If the
operation fails, no event is sent. When such a hotunplug sucessfull
notification is issued, the driver sets the attribute
"wakeup_hotunplug_complete" to 1.
While the firmware does tell us whether we are waking from a suspend or
hibernation scenario, the Linux way of hibernating makes this information
not reliable, and therefore it is not reported.
The idea is that if any of these attributes are non-zero, userspace might
want to do something at the end of the "wake up from sleep" procedures,
such as offering to send the machine back into sleep as soon as it is safe
to do so.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 91 |
1 files changed, 84 insertions, 7 deletions
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index f5f306ae4413..9b0235dc5308 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c | |||
@@ -1018,6 +1018,14 @@ static unsigned int hotkey_config_change; | |||
1018 | 1018 | ||
1019 | static struct mutex hotkey_mutex; | 1019 | static struct mutex hotkey_mutex; |
1020 | 1020 | ||
1021 | static enum { /* Reasons for waking up */ | ||
1022 | TP_ACPI_WAKEUP_NONE = 0, /* None or unknown */ | ||
1023 | TP_ACPI_WAKEUP_BAYEJ, /* Bay ejection request */ | ||
1024 | TP_ACPI_WAKEUP_UNDOCK, /* Undock request */ | ||
1025 | } hotkey_wakeup_reason; | ||
1026 | |||
1027 | static int hotkey_autosleep_ack; | ||
1028 | |||
1021 | static int hotkey_orig_status; | 1029 | static int hotkey_orig_status; |
1022 | static u32 hotkey_orig_mask; | 1030 | static u32 hotkey_orig_mask; |
1023 | static u32 hotkey_all_mask; | 1031 | static u32 hotkey_all_mask; |
@@ -1661,6 +1669,29 @@ static ssize_t hotkey_report_mode_show(struct device *dev, | |||
1661 | static struct device_attribute dev_attr_hotkey_report_mode = | 1669 | static struct device_attribute dev_attr_hotkey_report_mode = |
1662 | __ATTR(hotkey_report_mode, S_IRUGO, hotkey_report_mode_show, NULL); | 1670 | __ATTR(hotkey_report_mode, S_IRUGO, hotkey_report_mode_show, NULL); |
1663 | 1671 | ||
1672 | /* sysfs wakeup reason ------------------------------------------------- */ | ||
1673 | static ssize_t hotkey_wakeup_reason_show(struct device *dev, | ||
1674 | struct device_attribute *attr, | ||
1675 | char *buf) | ||
1676 | { | ||
1677 | return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_wakeup_reason); | ||
1678 | } | ||
1679 | |||
1680 | static struct device_attribute dev_attr_hotkey_wakeup_reason = | ||
1681 | __ATTR(wakeup_reason, S_IRUGO, hotkey_wakeup_reason_show, NULL); | ||
1682 | |||
1683 | /* sysfs wakeup hotunplug_complete ------------------------------------- */ | ||
1684 | static ssize_t hotkey_wakeup_hotunplug_complete_show(struct device *dev, | ||
1685 | struct device_attribute *attr, | ||
1686 | char *buf) | ||
1687 | { | ||
1688 | return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_autosleep_ack); | ||
1689 | } | ||
1690 | |||
1691 | static struct device_attribute dev_attr_hotkey_wakeup_hotunplug_complete = | ||
1692 | __ATTR(wakeup_hotunplug_complete, S_IRUGO, | ||
1693 | hotkey_wakeup_hotunplug_complete_show, NULL); | ||
1694 | |||
1664 | /* --------------------------------------------------------------------- */ | 1695 | /* --------------------------------------------------------------------- */ |
1665 | 1696 | ||
1666 | static struct attribute *hotkey_attributes[] __initdata = { | 1697 | static struct attribute *hotkey_attributes[] __initdata = { |
@@ -1683,6 +1714,8 @@ static struct attribute *hotkey_mask_attributes[] __initdata = { | |||
1683 | &dev_attr_hotkey_all_mask.attr, | 1714 | &dev_attr_hotkey_all_mask.attr, |
1684 | &dev_attr_hotkey_recommended_mask.attr, | 1715 | &dev_attr_hotkey_recommended_mask.attr, |
1685 | #endif | 1716 | #endif |
1717 | &dev_attr_hotkey_wakeup_reason.attr, | ||
1718 | &dev_attr_hotkey_wakeup_hotunplug_complete.attr, | ||
1686 | }; | 1719 | }; |
1687 | 1720 | ||
1688 | static int __init hotkey_init(struct ibm_init_struct *iibm) | 1721 | static int __init hotkey_init(struct ibm_init_struct *iibm) |
@@ -1822,7 +1855,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
1822 | str_supported(tp_features.hotkey)); | 1855 | str_supported(tp_features.hotkey)); |
1823 | 1856 | ||
1824 | if (tp_features.hotkey) { | 1857 | if (tp_features.hotkey) { |
1825 | hotkey_dev_attributes = create_attr_set(10, NULL); | 1858 | hotkey_dev_attributes = create_attr_set(12, NULL); |
1826 | if (!hotkey_dev_attributes) | 1859 | if (!hotkey_dev_attributes) |
1827 | return -ENOMEM; | 1860 | return -ENOMEM; |
1828 | res = add_many_to_attr_set(hotkey_dev_attributes, | 1861 | res = add_many_to_attr_set(hotkey_dev_attributes, |
@@ -2051,6 +2084,48 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
2051 | unk_ev = 1; | 2084 | unk_ev = 1; |
2052 | } | 2085 | } |
2053 | break; | 2086 | break; |
2087 | case 2: | ||
2088 | /* Wakeup reason */ | ||
2089 | switch (hkey) { | ||
2090 | case 0x2304: /* suspend, undock */ | ||
2091 | case 0x2404: /* hibernation, undock */ | ||
2092 | hotkey_wakeup_reason = TP_ACPI_WAKEUP_UNDOCK; | ||
2093 | ignore_acpi_ev = 1; | ||
2094 | break; | ||
2095 | case 0x2305: /* suspend, bay eject */ | ||
2096 | case 0x2405: /* hibernation, bay eject */ | ||
2097 | hotkey_wakeup_reason = TP_ACPI_WAKEUP_BAYEJ; | ||
2098 | ignore_acpi_ev = 1; | ||
2099 | break; | ||
2100 | default: | ||
2101 | unk_ev = 1; | ||
2102 | } | ||
2103 | if (hotkey_wakeup_reason != TP_ACPI_WAKEUP_NONE) { | ||
2104 | printk(TPACPI_INFO | ||
2105 | "woke up due to a hot-unplug " | ||
2106 | "request...\n"); | ||
2107 | } | ||
2108 | break; | ||
2109 | case 3: | ||
2110 | /* bay-related wakeups */ | ||
2111 | if (hkey == 0x3003) { | ||
2112 | hotkey_autosleep_ack = 1; | ||
2113 | printk(TPACPI_INFO | ||
2114 | "bay ejected\n"); | ||
2115 | } else { | ||
2116 | unk_ev = 1; | ||
2117 | } | ||
2118 | break; | ||
2119 | case 4: | ||
2120 | /* dock-related wakeups */ | ||
2121 | if (hkey == 0x4003) { | ||
2122 | hotkey_autosleep_ack = 1; | ||
2123 | printk(TPACPI_INFO | ||
2124 | "undocked\n"); | ||
2125 | } else { | ||
2126 | unk_ev = 1; | ||
2127 | } | ||
2128 | break; | ||
2054 | case 5: | 2129 | case 5: |
2055 | /* 0x5000-0x5FFF: On screen display helpers */ | 2130 | /* 0x5000-0x5FFF: On screen display helpers */ |
2056 | switch (hkey) { | 2131 | switch (hkey) { |
@@ -2075,12 +2150,6 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
2075 | } | 2150 | } |
2076 | /* fallthrough to default */ | 2151 | /* fallthrough to default */ |
2077 | default: | 2152 | default: |
2078 | /* case 2: dock-related */ | ||
2079 | /* 0x2305 - T43 waking up due to bay lever | ||
2080 | * eject while aslept */ | ||
2081 | /* case 3: ultra-bay related. maybe bay in dock? */ | ||
2082 | /* 0x3003 - T43 after wake up by bay lever | ||
2083 | * eject (0x2305) */ | ||
2084 | unk_ev = 1; | 2153 | unk_ev = 1; |
2085 | } | 2154 | } |
2086 | if (unk_ev) { | 2155 | if (unk_ev) { |
@@ -2105,6 +2174,13 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
2105 | } | 2174 | } |
2106 | } | 2175 | } |
2107 | 2176 | ||
2177 | static void hotkey_suspend(pm_message_t state) | ||
2178 | { | ||
2179 | /* Do these on suspend, we get the events on early resume! */ | ||
2180 | hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE; | ||
2181 | hotkey_autosleep_ack = 0; | ||
2182 | } | ||
2183 | |||
2108 | static void hotkey_resume(void) | 2184 | static void hotkey_resume(void) |
2109 | { | 2185 | { |
2110 | if (hotkey_mask_get()) | 2186 | if (hotkey_mask_get()) |
@@ -2212,6 +2288,7 @@ static struct ibm_struct hotkey_driver_data = { | |||
2212 | .write = hotkey_write, | 2288 | .write = hotkey_write, |
2213 | .exit = hotkey_exit, | 2289 | .exit = hotkey_exit, |
2214 | .resume = hotkey_resume, | 2290 | .resume = hotkey_resume, |
2291 | .suspend = hotkey_suspend, | ||
2215 | .acpi = &ibm_hotkey_acpidriver, | 2292 | .acpi = &ibm_hotkey_acpidriver, |
2216 | }; | 2293 | }; |
2217 | 2294 | ||