1 /*
2 * acpi_button.c - ACPI Button Driver ($Revision: 29 $)
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>
29 #include <linux/types.h>
30 #include <linux/compatmac.h>
31 #include <linux/proc_fs.h>
32 #include <acpi/acpi_bus.h>
33 #include <acpi/acpi_drivers.h>
34
35
36 #define _COMPONENT ACPI_BUTTON_COMPONENT
37 ACPI_MODULE_NAME ("acpi_button")
38
39 MODULE_AUTHOR("Paul Diefenbaugh");
40 MODULE_DESCRIPTION(ACPI_BUTTON_DRIVER_NAME);
41 MODULE_LICENSE("GPL");
42
43 #define PREFIX "ACPI: "
44
45
46 static int acpi_button_add (struct acpi_device *device);
47 static int acpi_button_remove (struct acpi_device *device, int type);
48
49 static struct acpi_driver acpi_button_driver = {
50 .name = ACPI_BUTTON_DRIVER_NAME,
51 .class = ACPI_BUTTON_CLASS,
52 .ids = "ACPI_FPB,ACPI_FSB,PNP0C0D,PNP0C0C,PNP0C0E",
53 .ops = {
54 .add = acpi_button_add,
55 .remove = acpi_button_remove,
56 },
57 };
58
59 struct acpi_button {
60 acpi_handle handle;
61 struct acpi_device *device; /* Fixed button kludge */
62 u8 type;
63 unsigned long pushed;
64 };
65
66
67 /* --------------------------------------------------------------------------
68 FS Interface (/proc)
69 -------------------------------------------------------------------------- */
70
71 static struct proc_dir_entry *acpi_button_dir;
72
73 static int
acpi_button_read_info(char * page,char ** start,off_t off,int count,int * eof,void * data)74 acpi_button_read_info (
75 char *page,
76 char **start,
77 off_t off,
78 int count,
79 int *eof,
80 void *data)
81 {
82 struct acpi_button *button = (struct acpi_button *) data;
83 char *p = page;
84 int len = 0;
85
86 ACPI_FUNCTION_TRACE("acpi_button_read_info");
87
88 if (!button || !button->device || (off != 0))
89 goto end;
90
91 p += sprintf(p, "type: %s\n",
92 acpi_device_name(button->device));
93
94 end:
95 len = (p - page);
96 if (len <= off+count) *eof = 1;
97 *start = page + off;
98 len -= off;
99 if (len>count) len = count;
100 if (len<0) len = 0;
101
102 return_VALUE(len);
103 }
104
105 static int
acpi_button_lid_read_state(char * page,char ** start,off_t off,int count,int * eof,void * data)106 acpi_button_lid_read_state(
107 char *page,
108 char **start,
109 off_t off,
110 int count,
111 int *eof,
112 void *data)
113 {
114 struct acpi_button *button = (struct acpi_button *) data;
115 char *p = page;
116 int len = 0;
117 acpi_status status=AE_OK;
118 unsigned long state;
119
120 ACPI_FUNCTION_TRACE("acpi_button_lid_read_state");
121
122 if (!button || !button->device || (off != 0))
123 goto end;
124
125 status=acpi_evaluate_integer(button->handle,"_LID",NULL,&state);
126 if (ACPI_FAILURE(status)){
127 p += sprintf(p, "state: unsupported\n");
128 }
129 else{
130 p += sprintf(p, "state: %s\n", (state ? "open" : "closed"));
131 }
132
133 end:
134 len = (p - page);
135 if (len <= off+count) *eof = 1;
136 *start = page + off;
137 len -= off;
138 if (len>count) len = count;
139 if (len<0) len = 0;
140
141 return_VALUE(len);
142 }
143
144 static int
acpi_button_add_fs(struct acpi_device * device)145 acpi_button_add_fs (
146 struct acpi_device *device)
147 {
148 struct proc_dir_entry *entry = NULL;
149 struct acpi_button *button = NULL;
150
151 ACPI_FUNCTION_TRACE("acpi_button_add_fs");
152
153 if (!device || !acpi_driver_data(device))
154 return_VALUE(-EINVAL);
155
156 button = acpi_driver_data(device);
157
158 switch (button->type) {
159 case ACPI_BUTTON_TYPE_POWER:
160 case ACPI_BUTTON_TYPE_POWERF:
161 entry = proc_mkdir(ACPI_BUTTON_SUBCLASS_POWER,
162 acpi_button_dir);
163 break;
164 case ACPI_BUTTON_TYPE_SLEEP:
165 case ACPI_BUTTON_TYPE_SLEEPF:
166 entry = proc_mkdir(ACPI_BUTTON_SUBCLASS_SLEEP,
167 acpi_button_dir);
168 break;
169 case ACPI_BUTTON_TYPE_LID:
170 entry = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID,
171 acpi_button_dir);
172 break;
173 }
174
175 if (!entry)
176 return_VALUE(-ENODEV);
177 entry->owner = THIS_MODULE;
178
179 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), entry);
180 if (!acpi_device_dir(device))
181 return_VALUE(-ENODEV);
182 acpi_device_dir(device)->owner = THIS_MODULE;
183
184 /* 'info' [R] */
185 entry = create_proc_entry(ACPI_BUTTON_FILE_INFO,
186 S_IRUGO, acpi_device_dir(device));
187 if (!entry)
188 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
189 "Unable to create '%s' fs entry\n",
190 ACPI_BUTTON_FILE_INFO));
191 else {
192 entry->read_proc = acpi_button_read_info;
193 entry->data = acpi_driver_data(device);
194 entry->owner = THIS_MODULE;
195 }
196
197 if (button->type==ACPI_BUTTON_TYPE_LID){
198 /* 'state' [R] */
199 entry = create_proc_entry(ACPI_BUTTON_FILE_STATE,
200 S_IRUGO, acpi_device_dir(device));
201 if (!entry)
202 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
203 "Unable to create '%s' fs entry\n",
204 ACPI_BUTTON_FILE_STATE));
205 else {
206 entry->read_proc = acpi_button_lid_read_state;
207 entry->data = acpi_driver_data(device);
208 entry->owner = THIS_MODULE;
209 }
210 }
211
212 return_VALUE(0);
213 }
214
215
216 static int
acpi_button_remove_fs(struct acpi_device * device)217 acpi_button_remove_fs (
218 struct acpi_device *device)
219 {
220 struct acpi_button *button = NULL;
221
222 ACPI_FUNCTION_TRACE("acpi_button_remove_fs");
223
224 button = acpi_driver_data(device);
225 if (acpi_device_dir(device)) {
226 if (button->type == ACPI_BUTTON_TYPE_LID)
227 remove_proc_entry(ACPI_BUTTON_FILE_STATE,
228 acpi_device_dir(device));
229 remove_proc_entry(ACPI_BUTTON_FILE_INFO,
230 acpi_device_dir(device));
231
232 remove_proc_entry(acpi_device_bid(device),
233 acpi_device_dir(device)->parent);
234
235 switch (button->type) {
236 case ACPI_BUTTON_TYPE_POWER:
237 case ACPI_BUTTON_TYPE_POWERF:
238 remove_proc_entry(ACPI_BUTTON_SUBCLASS_POWER,
239 acpi_button_dir);
240 break;
241 case ACPI_BUTTON_TYPE_SLEEP:
242 case ACPI_BUTTON_TYPE_SLEEPF:
243 remove_proc_entry(ACPI_BUTTON_SUBCLASS_SLEEP,
244 acpi_button_dir);
245 break;
246 case ACPI_BUTTON_TYPE_LID:
247 remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID,
248 acpi_button_dir);
249 break;
250 }
251 acpi_device_dir(device) = NULL;
252 }
253
254 return_VALUE(0);
255 }
256
257
258 /* --------------------------------------------------------------------------
259 Driver Interface
260 -------------------------------------------------------------------------- */
261
262 void
acpi_button_notify(acpi_handle handle,u32 event,void * data)263 acpi_button_notify (
264 acpi_handle handle,
265 u32 event,
266 void *data)
267 {
268 struct acpi_button *button = (struct acpi_button *) data;
269
270 ACPI_FUNCTION_TRACE("acpi_button_notify");
271
272 if (!button || !button->device)
273 return_VOID;
274
275 switch (event) {
276 case ACPI_BUTTON_NOTIFY_STATUS:
277 acpi_bus_generate_event(button->device, event, ++button->pushed);
278 break;
279 default:
280 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
281 "Unsupported event [0x%x]\n", event));
282 break;
283 }
284
285 return_VOID;
286 }
287
288
289 acpi_status
acpi_button_notify_fixed(void * data)290 acpi_button_notify_fixed (
291 void *data)
292 {
293 struct acpi_button *button = (struct acpi_button *) data;
294
295 ACPI_FUNCTION_TRACE("acpi_button_notify_fixed");
296
297 if (!button)
298 return_ACPI_STATUS(AE_BAD_PARAMETER);
299
300 acpi_button_notify(button->handle, ACPI_BUTTON_NOTIFY_STATUS, button);
301
302 return_ACPI_STATUS(AE_OK);
303 }
304
305
306 static int
acpi_button_add(struct acpi_device * device)307 acpi_button_add (
308 struct acpi_device *device)
309 {
310 int result = 0;
311 acpi_status status = AE_OK;
312 struct acpi_button *button = NULL;
313
314 static struct acpi_device *power_button;
315 static struct acpi_device *sleep_button;
316 static struct acpi_device *lid_button;
317
318 ACPI_FUNCTION_TRACE("acpi_button_add");
319
320 if (!device)
321 return_VALUE(-EINVAL);
322
323 button = kmalloc(sizeof(struct acpi_button), GFP_KERNEL);
324 if (!button)
325 return_VALUE(-ENOMEM);
326 memset(button, 0, sizeof(struct acpi_button));
327
328 button->device = device;
329 button->handle = device->handle;
330 acpi_driver_data(device) = button;
331
332 /*
333 * Determine the button type (via hid), as fixed-feature buttons
334 * need to be handled a bit differently than generic-space.
335 */
336 if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWER)) {
337 button->type = ACPI_BUTTON_TYPE_POWER;
338 sprintf(acpi_device_name(device), "%s",
339 ACPI_BUTTON_DEVICE_NAME_POWER);
340 sprintf(acpi_device_class(device), "%s/%s",
341 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
342 }
343 else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) {
344 button->type = ACPI_BUTTON_TYPE_POWERF;
345 sprintf(acpi_device_name(device), "%s",
346 ACPI_BUTTON_DEVICE_NAME_POWERF);
347 sprintf(acpi_device_class(device), "%s/%s",
348 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
349 }
350 else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEP)) {
351 button->type = ACPI_BUTTON_TYPE_SLEEP;
352 sprintf(acpi_device_name(device), "%s",
353 ACPI_BUTTON_DEVICE_NAME_SLEEP);
354 sprintf(acpi_device_class(device), "%s/%s",
355 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
356 }
357 else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) {
358 button->type = ACPI_BUTTON_TYPE_SLEEPF;
359 sprintf(acpi_device_name(device), "%s",
360 ACPI_BUTTON_DEVICE_NAME_SLEEPF);
361 sprintf(acpi_device_class(device), "%s/%s",
362 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
363 }
364 else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_LID)) {
365 button->type = ACPI_BUTTON_TYPE_LID;
366 sprintf(acpi_device_name(device), "%s",
367 ACPI_BUTTON_DEVICE_NAME_LID);
368 sprintf(acpi_device_class(device), "%s/%s",
369 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
370 }
371 else {
372 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unsupported hid [%s]\n",
373 acpi_device_hid(device)));
374 result = -ENODEV;
375 goto end;
376 }
377
378 /*
379 * Ensure only one button of each type is used.
380 */
381 switch (button->type) {
382 case ACPI_BUTTON_TYPE_POWER:
383 case ACPI_BUTTON_TYPE_POWERF:
384 if (!power_button)
385 power_button = device;
386 else {
387 kfree(button);
388 return_VALUE(-ENODEV);
389 }
390 break;
391 case ACPI_BUTTON_TYPE_SLEEP:
392 case ACPI_BUTTON_TYPE_SLEEPF:
393 if (!sleep_button)
394 sleep_button = device;
395 else {
396 kfree(button);
397 return_VALUE(-ENODEV);
398 }
399 break;
400 case ACPI_BUTTON_TYPE_LID:
401 if (!lid_button)
402 lid_button = device;
403 else {
404 kfree(button);
405 return_VALUE(-ENODEV);
406 }
407 break;
408 }
409
410 result = acpi_button_add_fs(device);
411 if (result)
412 goto end;
413
414 switch (button->type) {
415 case ACPI_BUTTON_TYPE_POWERF:
416 status = acpi_install_fixed_event_handler (
417 ACPI_EVENT_POWER_BUTTON,
418 acpi_button_notify_fixed,
419 button);
420 break;
421 case ACPI_BUTTON_TYPE_SLEEPF:
422 status = acpi_install_fixed_event_handler (
423 ACPI_EVENT_SLEEP_BUTTON,
424 acpi_button_notify_fixed,
425 button);
426 break;
427 default:
428 status = acpi_install_notify_handler (
429 button->handle,
430 ACPI_DEVICE_NOTIFY,
431 acpi_button_notify,
432 button);
433 break;
434 }
435
436 if (ACPI_FAILURE(status)) {
437 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
438 "Error installing notify handler\n"));
439 result = -ENODEV;
440 goto end;
441 }
442
443 printk(KERN_INFO PREFIX "%s [%s]\n",
444 acpi_device_name(device), acpi_device_bid(device));
445
446 end:
447 if (result) {
448 acpi_button_remove_fs(device);
449 kfree(button);
450 }
451
452 return_VALUE(result);
453 }
454
455
456 static int
acpi_button_remove(struct acpi_device * device,int type)457 acpi_button_remove (struct acpi_device *device, int type)
458 {
459 acpi_status status = 0;
460 struct acpi_button *button = NULL;
461
462 ACPI_FUNCTION_TRACE("acpi_button_remove");
463
464 if (!device || !acpi_driver_data(device))
465 return_VALUE(-EINVAL);
466
467 button = acpi_driver_data(device);
468
469 /* Unregister for device notifications. */
470 switch (button->type) {
471 case ACPI_BUTTON_TYPE_POWERF:
472 status = acpi_remove_fixed_event_handler(
473 ACPI_EVENT_POWER_BUTTON, acpi_button_notify_fixed);
474 break;
475 case ACPI_BUTTON_TYPE_SLEEPF:
476 status = acpi_remove_fixed_event_handler(
477 ACPI_EVENT_SLEEP_BUTTON, acpi_button_notify_fixed);
478 break;
479 default:
480 status = acpi_remove_notify_handler(button->handle,
481 ACPI_DEVICE_NOTIFY, acpi_button_notify);
482 break;
483 }
484
485 if (ACPI_FAILURE(status))
486 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
487 "Error removing notify handler\n"));
488
489 acpi_button_remove_fs(device);
490
491 kfree(button);
492
493 return_VALUE(0);
494 }
495
496
497 static int __init
acpi_button_init(void)498 acpi_button_init (void)
499 {
500 int result = 0;
501
502 ACPI_FUNCTION_TRACE("acpi_button_init");
503
504 acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
505 if (!acpi_button_dir)
506 return_VALUE(-ENODEV);
507 acpi_button_dir->owner = THIS_MODULE;
508
509 result = acpi_bus_register_driver(&acpi_button_driver);
510 if (result < 0) {
511 remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
512 return_VALUE(-ENODEV);
513 }
514
515 return_VALUE(0);
516 }
517
518
519 static void __exit
acpi_button_exit(void)520 acpi_button_exit (void)
521 {
522 ACPI_FUNCTION_TRACE("acpi_button_exit");
523
524 acpi_bus_unregister_driver(&acpi_button_driver);
525
526 remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
527
528 return_VOID;
529 }
530
531
532 module_init(acpi_button_init);
533 module_exit(acpi_button_exit);
534