Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * acpi_button.c - ACPI Button Driver ($Revision: 30 $) | |
3 | * | |
4 | * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> | |
5 | * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> | |
6 | * | |
7 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or (at | |
12 | * your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, but | |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License along | |
20 | * with this program; if not, write to the Free Software Foundation, Inc., | |
21 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | |
22 | * | |
23 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
24 | */ | |
25 | ||
26 | #include <linux/kernel.h> | |
27 | #include <linux/module.h> | |
28 | #include <linux/init.h> | |
1da177e4 LT |
29 | #include <acpi/acpi_bus.h> |
30 | #include <acpi/acpi_drivers.h> | |
31 | ||
32 | ||
33 | #define ACPI_BUTTON_COMPONENT 0x00080000 | |
34 | #define ACPI_BUTTON_DRIVER_NAME "ACPI Button Driver" | |
35 | #define ACPI_BUTTON_CLASS "button" | |
1da177e4 LT |
36 | #define ACPI_BUTTON_NOTIFY_STATUS 0x80 |
37 | ||
38 | #define ACPI_BUTTON_SUBCLASS_POWER "power" | |
39 | #define ACPI_BUTTON_HID_POWER "PNP0C0C" | |
40 | #define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button (CM)" | |
41 | #define ACPI_BUTTON_DEVICE_NAME_POWERF "Power Button (FF)" | |
42 | #define ACPI_BUTTON_TYPE_POWER 0x01 | |
43 | #define ACPI_BUTTON_TYPE_POWERF 0x02 | |
44 | ||
45 | #define ACPI_BUTTON_SUBCLASS_SLEEP "sleep" | |
46 | #define ACPI_BUTTON_HID_SLEEP "PNP0C0E" | |
47 | #define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button (CM)" | |
48 | #define ACPI_BUTTON_DEVICE_NAME_SLEEPF "Sleep Button (FF)" | |
49 | #define ACPI_BUTTON_TYPE_SLEEP 0x03 | |
50 | #define ACPI_BUTTON_TYPE_SLEEPF 0x04 | |
51 | ||
52 | #define ACPI_BUTTON_SUBCLASS_LID "lid" | |
53 | #define ACPI_BUTTON_HID_LID "PNP0C0D" | |
54 | #define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch" | |
55 | #define ACPI_BUTTON_TYPE_LID 0x05 | |
56 | ||
57 | #define _COMPONENT ACPI_BUTTON_COMPONENT | |
58 | ACPI_MODULE_NAME ("acpi_button") | |
59 | ||
60 | MODULE_AUTHOR("Paul Diefenbaugh"); | |
61 | MODULE_DESCRIPTION(ACPI_BUTTON_DRIVER_NAME); | |
62 | MODULE_LICENSE("GPL"); | |
63 | ||
64 | ||
65 | static int acpi_button_add (struct acpi_device *device); | |
66 | static int acpi_button_remove (struct acpi_device *device, int type); | |
1da177e4 LT |
67 | |
68 | static struct acpi_driver acpi_button_driver = { | |
69 | .name = ACPI_BUTTON_DRIVER_NAME, | |
70 | .class = ACPI_BUTTON_CLASS, | |
71 | .ids = "ACPI_FPB,ACPI_FSB,PNP0C0D,PNP0C0C,PNP0C0E", | |
72 | .ops = { | |
73 | .add = acpi_button_add, | |
74 | .remove = acpi_button_remove, | |
75 | }, | |
76 | }; | |
77 | ||
78 | struct acpi_button { | |
79 | acpi_handle handle; | |
80 | struct acpi_device *device; /* Fixed button kludge */ | |
81 | u8 type; | |
82 | unsigned long pushed; | |
83 | }; | |
84 | ||
1da177e4 LT |
85 | /* -------------------------------------------------------------------------- |
86 | Driver Interface | |
87 | -------------------------------------------------------------------------- */ | |
88 | ||
89 | static void | |
90 | acpi_button_notify ( | |
91 | acpi_handle handle, | |
92 | u32 event, | |
93 | void *data) | |
94 | { | |
95 | struct acpi_button *button = (struct acpi_button *) data; | |
96 | ||
97 | ACPI_FUNCTION_TRACE("acpi_button_notify"); | |
98 | ||
99 | if (!button || !button->device) | |
100 | return_VOID; | |
101 | ||
102 | switch (event) { | |
103 | case ACPI_BUTTON_NOTIFY_STATUS: | |
104 | acpi_bus_generate_event(button->device, event, ++button->pushed); | |
105 | break; | |
106 | default: | |
107 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | |
108 | "Unsupported event [0x%x]\n", event)); | |
109 | break; | |
110 | } | |
111 | ||
112 | return_VOID; | |
113 | } | |
114 | ||
115 | ||
116 | static acpi_status | |
117 | acpi_button_notify_fixed ( | |
118 | void *data) | |
119 | { | |
120 | struct acpi_button *button = (struct acpi_button *) data; | |
121 | ||
122 | ACPI_FUNCTION_TRACE("acpi_button_notify_fixed"); | |
123 | ||
bd4698da | 124 | BUG_ON(!button); |
1da177e4 LT |
125 | |
126 | acpi_button_notify(button->handle, ACPI_BUTTON_NOTIFY_STATUS, button); | |
127 | ||
128 | return_ACPI_STATUS(AE_OK); | |
129 | } | |
130 | ||
131 | ||
132 | static int | |
133 | acpi_button_add ( | |
134 | struct acpi_device *device) | |
135 | { | |
136 | int result = 0; | |
137 | acpi_status status = AE_OK; | |
138 | struct acpi_button *button = NULL; | |
139 | ||
1da177e4 LT |
140 | ACPI_FUNCTION_TRACE("acpi_button_add"); |
141 | ||
142 | if (!device) | |
143 | return_VALUE(-EINVAL); | |
144 | ||
145 | button = kmalloc(sizeof(struct acpi_button), GFP_KERNEL); | |
146 | if (!button) | |
147 | return_VALUE(-ENOMEM); | |
148 | memset(button, 0, sizeof(struct acpi_button)); | |
149 | ||
150 | button->device = device; | |
151 | button->handle = device->handle; | |
152 | acpi_driver_data(device) = button; | |
153 | ||
154 | /* | |
155 | * Determine the button type (via hid), as fixed-feature buttons | |
156 | * need to be handled a bit differently than generic-space. | |
157 | */ | |
158 | if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWER)) { | |
159 | button->type = ACPI_BUTTON_TYPE_POWER; | |
160 | strcpy(acpi_device_name(device), | |
161 | ACPI_BUTTON_DEVICE_NAME_POWER); | |
162 | sprintf(acpi_device_class(device), "%s/%s", | |
163 | ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER); | |
164 | } | |
165 | else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) { | |
166 | button->type = ACPI_BUTTON_TYPE_POWERF; | |
167 | strcpy(acpi_device_name(device), | |
168 | ACPI_BUTTON_DEVICE_NAME_POWERF); | |
169 | sprintf(acpi_device_class(device), "%s/%s", | |
170 | ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER); | |
171 | } | |
172 | else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEP)) { | |
173 | button->type = ACPI_BUTTON_TYPE_SLEEP; | |
174 | strcpy(acpi_device_name(device), | |
175 | ACPI_BUTTON_DEVICE_NAME_SLEEP); | |
176 | sprintf(acpi_device_class(device), "%s/%s", | |
177 | ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP); | |
178 | } | |
179 | else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) { | |
180 | button->type = ACPI_BUTTON_TYPE_SLEEPF; | |
181 | strcpy(acpi_device_name(device), | |
182 | ACPI_BUTTON_DEVICE_NAME_SLEEPF); | |
183 | sprintf(acpi_device_class(device), "%s/%s", | |
184 | ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP); | |
185 | } | |
186 | else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_LID)) { | |
187 | button->type = ACPI_BUTTON_TYPE_LID; | |
188 | strcpy(acpi_device_name(device), | |
189 | ACPI_BUTTON_DEVICE_NAME_LID); | |
190 | sprintf(acpi_device_class(device), "%s/%s", | |
191 | ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID); | |
192 | } | |
193 | else { | |
194 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unsupported hid [%s]\n", | |
195 | acpi_device_hid(device))); | |
196 | result = -ENODEV; | |
197 | goto end; | |
198 | } | |
199 | ||
1da177e4 LT |
200 | switch (button->type) { |
201 | case ACPI_BUTTON_TYPE_POWERF: | |
202 | status = acpi_install_fixed_event_handler ( | |
203 | ACPI_EVENT_POWER_BUTTON, | |
204 | acpi_button_notify_fixed, | |
205 | button); | |
206 | break; | |
207 | case ACPI_BUTTON_TYPE_SLEEPF: | |
208 | status = acpi_install_fixed_event_handler ( | |
209 | ACPI_EVENT_SLEEP_BUTTON, | |
210 | acpi_button_notify_fixed, | |
211 | button); | |
212 | break; | |
213 | default: | |
214 | status = acpi_install_notify_handler ( | |
215 | button->handle, | |
216 | ACPI_DEVICE_NOTIFY, | |
217 | acpi_button_notify, | |
218 | button); | |
219 | break; | |
220 | } | |
221 | ||
222 | if (ACPI_FAILURE(status)) { | |
223 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
224 | "Error installing notify handler\n")); | |
225 | result = -ENODEV; | |
226 | goto end; | |
227 | } | |
228 | ||
229 | if (device->wakeup.flags.valid) { | |
230 | /* Button's GPE is run-wake GPE */ | |
231 | acpi_set_gpe_type(device->wakeup.gpe_device, | |
232 | device->wakeup.gpe_number, ACPI_GPE_TYPE_WAKE_RUN); | |
233 | acpi_enable_gpe(device->wakeup.gpe_device, | |
234 | device->wakeup.gpe_number, ACPI_NOT_ISR); | |
235 | device->wakeup.state.enabled = 1; | |
236 | } | |
237 | ||
238 | printk(KERN_INFO PREFIX "%s [%s]\n", | |
239 | acpi_device_name(device), acpi_device_bid(device)); | |
240 | ||
241 | end: | |
242 | if (result) { | |
1da177e4 LT |
243 | kfree(button); |
244 | } | |
245 | ||
246 | return_VALUE(result); | |
247 | } | |
248 | ||
249 | ||
250 | static int | |
251 | acpi_button_remove (struct acpi_device *device, int type) | |
252 | { | |
253 | acpi_status status = 0; | |
254 | struct acpi_button *button = NULL; | |
255 | ||
256 | ACPI_FUNCTION_TRACE("acpi_button_remove"); | |
257 | ||
258 | if (!device || !acpi_driver_data(device)) | |
259 | return_VALUE(-EINVAL); | |
260 | ||
261 | button = acpi_driver_data(device); | |
262 | ||
263 | /* Unregister for device notifications. */ | |
264 | switch (button->type) { | |
265 | case ACPI_BUTTON_TYPE_POWERF: | |
266 | status = acpi_remove_fixed_event_handler( | |
267 | ACPI_EVENT_POWER_BUTTON, acpi_button_notify_fixed); | |
268 | break; | |
269 | case ACPI_BUTTON_TYPE_SLEEPF: | |
270 | status = acpi_remove_fixed_event_handler( | |
271 | ACPI_EVENT_SLEEP_BUTTON, acpi_button_notify_fixed); | |
272 | break; | |
273 | default: | |
274 | status = acpi_remove_notify_handler(button->handle, | |
275 | ACPI_DEVICE_NOTIFY, acpi_button_notify); | |
276 | break; | |
277 | } | |
278 | ||
279 | if (ACPI_FAILURE(status)) | |
280 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
281 | "Error removing notify handler\n")); | |
282 | ||
1da177e4 LT |
283 | kfree(button); |
284 | ||
285 | return_VALUE(0); | |
286 | } | |
287 | ||
288 | ||
289 | static int __init | |
290 | acpi_button_init (void) | |
291 | { | |
292 | int result = 0; | |
293 | ||
294 | ACPI_FUNCTION_TRACE("acpi_button_init"); | |
295 | ||
1da177e4 LT |
296 | result = acpi_bus_register_driver(&acpi_button_driver); |
297 | if (result < 0) { | |
1da177e4 LT |
298 | return_VALUE(-ENODEV); |
299 | } | |
300 | ||
301 | return_VALUE(0); | |
302 | } | |
303 | ||
1da177e4 LT |
304 | static void __exit |
305 | acpi_button_exit (void) | |
306 | { | |
307 | ACPI_FUNCTION_TRACE("acpi_button_exit"); | |
308 | ||
309 | acpi_bus_unregister_driver(&acpi_button_driver); | |
310 | ||
1da177e4 LT |
311 | return_VOID; |
312 | } | |
313 | ||
1da177e4 LT |
314 | module_init(acpi_button_init); |
315 | module_exit(acpi_button_exit); |