Merge rsync://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[linux-2.6] / drivers / acpi / button.c
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>
29 #include <linux/types.h>
30 #include <linux/proc_fs.h>
31 #include <linux/seq_file.h>
32 #include <acpi/acpi_bus.h>
33 #include <acpi/acpi_drivers.h>
34
35 #define ACPI_BUTTON_COMPONENT           0x00080000
36 #define ACPI_BUTTON_DRIVER_NAME         "ACPI Button Driver"
37 #define ACPI_BUTTON_CLASS               "button"
38 #define ACPI_BUTTON_FILE_INFO           "info"
39 #define ACPI_BUTTON_FILE_STATE          "state"
40 #define ACPI_BUTTON_TYPE_UNKNOWN        0x00
41 #define ACPI_BUTTON_NOTIFY_STATUS       0x80
42
43 #define ACPI_BUTTON_SUBCLASS_POWER      "power"
44 #define ACPI_BUTTON_HID_POWER           "PNP0C0C"
45 #define ACPI_BUTTON_DEVICE_NAME_POWER   "Power Button (CM)"
46 #define ACPI_BUTTON_DEVICE_NAME_POWERF  "Power Button (FF)"
47 #define ACPI_BUTTON_TYPE_POWER          0x01
48 #define ACPI_BUTTON_TYPE_POWERF         0x02
49
50 #define ACPI_BUTTON_SUBCLASS_SLEEP      "sleep"
51 #define ACPI_BUTTON_HID_SLEEP           "PNP0C0E"
52 #define ACPI_BUTTON_DEVICE_NAME_SLEEP   "Sleep Button (CM)"
53 #define ACPI_BUTTON_DEVICE_NAME_SLEEPF  "Sleep Button (FF)"
54 #define ACPI_BUTTON_TYPE_SLEEP          0x03
55 #define ACPI_BUTTON_TYPE_SLEEPF         0x04
56
57 #define ACPI_BUTTON_SUBCLASS_LID        "lid"
58 #define ACPI_BUTTON_HID_LID             "PNP0C0D"
59 #define ACPI_BUTTON_DEVICE_NAME_LID     "Lid Switch"
60 #define ACPI_BUTTON_TYPE_LID            0x05
61
62 #define _COMPONENT              ACPI_BUTTON_COMPONENT
63 ACPI_MODULE_NAME("acpi_button")
64
65     MODULE_AUTHOR("Paul Diefenbaugh");
66 MODULE_DESCRIPTION(ACPI_BUTTON_DRIVER_NAME);
67 MODULE_LICENSE("GPL");
68
69 static int acpi_button_add(struct acpi_device *device);
70 static int acpi_button_remove(struct acpi_device *device, int type);
71 static int acpi_button_info_open_fs(struct inode *inode, struct file *file);
72 static int acpi_button_state_open_fs(struct inode *inode, struct file *file);
73
74 static struct acpi_driver acpi_button_driver = {
75         .name = ACPI_BUTTON_DRIVER_NAME,
76         .class = ACPI_BUTTON_CLASS,
77         .ids = "ACPI_FPB,ACPI_FSB,PNP0C0D,PNP0C0C,PNP0C0E",
78         .ops = {
79                 .add = acpi_button_add,
80                 .remove = acpi_button_remove,
81                 },
82 };
83
84 struct acpi_button {
85         acpi_handle handle;
86         struct acpi_device *device;     /* Fixed button kludge */
87         u8 type;
88         unsigned long pushed;
89 };
90
91 static struct file_operations acpi_button_info_fops = {
92         .open = acpi_button_info_open_fs,
93         .read = seq_read,
94         .llseek = seq_lseek,
95         .release = single_release,
96 };
97
98 static struct file_operations acpi_button_state_fops = {
99         .open = acpi_button_state_open_fs,
100         .read = seq_read,
101         .llseek = seq_lseek,
102         .release = single_release,
103 };
104
105 /* --------------------------------------------------------------------------
106                               FS Interface (/proc)
107    -------------------------------------------------------------------------- */
108
109 static struct proc_dir_entry *acpi_button_dir;
110
111 static int acpi_button_info_seq_show(struct seq_file *seq, void *offset)
112 {
113         struct acpi_button *button = (struct acpi_button *)seq->private;
114
115
116         if (!button || !button->device)
117                 return 0;
118
119         seq_printf(seq, "type:                    %s\n",
120                    acpi_device_name(button->device));
121
122         return 0;
123 }
124
125 static int acpi_button_info_open_fs(struct inode *inode, struct file *file)
126 {
127         return single_open(file, acpi_button_info_seq_show, PDE(inode)->data);
128 }
129
130 static int acpi_button_state_seq_show(struct seq_file *seq, void *offset)
131 {
132         struct acpi_button *button = (struct acpi_button *)seq->private;
133         acpi_status status;
134         unsigned long state;
135
136
137         if (!button || !button->device)
138                 return 0;
139
140         status = acpi_evaluate_integer(button->handle, "_LID", NULL, &state);
141         if (ACPI_FAILURE(status)) {
142                 seq_printf(seq, "state:      unsupported\n");
143         } else {
144                 seq_printf(seq, "state:      %s\n",
145                            (state ? "open" : "closed"));
146         }
147
148         return 0;
149 }
150
151 static int acpi_button_state_open_fs(struct inode *inode, struct file *file)
152 {
153         return single_open(file, acpi_button_state_seq_show, PDE(inode)->data);
154 }
155
156 static struct proc_dir_entry *acpi_power_dir;
157 static struct proc_dir_entry *acpi_sleep_dir;
158 static struct proc_dir_entry *acpi_lid_dir;
159
160 static int acpi_button_add_fs(struct acpi_device *device)
161 {
162         struct proc_dir_entry *entry = NULL;
163         struct acpi_button *button = NULL;
164
165
166         if (!device || !acpi_driver_data(device))
167                 return -EINVAL;
168
169         button = acpi_driver_data(device);
170
171         switch (button->type) {
172         case ACPI_BUTTON_TYPE_POWER:
173         case ACPI_BUTTON_TYPE_POWERF:
174                 if (!acpi_power_dir)
175                         acpi_power_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_POWER,
176                                                     acpi_button_dir);
177                 entry = acpi_power_dir;
178                 break;
179         case ACPI_BUTTON_TYPE_SLEEP:
180         case ACPI_BUTTON_TYPE_SLEEPF:
181                 if (!acpi_sleep_dir)
182                         acpi_sleep_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_SLEEP,
183                                                     acpi_button_dir);
184                 entry = acpi_sleep_dir;
185                 break;
186         case ACPI_BUTTON_TYPE_LID:
187                 if (!acpi_lid_dir)
188                         acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID,
189                                                   acpi_button_dir);
190                 entry = acpi_lid_dir;
191                 break;
192         }
193
194         if (!entry)
195                 return -ENODEV;
196         entry->owner = THIS_MODULE;
197
198         acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), entry);
199         if (!acpi_device_dir(device))
200                 return -ENODEV;
201         acpi_device_dir(device)->owner = THIS_MODULE;
202
203         /* 'info' [R] */
204         entry = create_proc_entry(ACPI_BUTTON_FILE_INFO,
205                                   S_IRUGO, acpi_device_dir(device));
206         if (!entry)
207                 return -ENODEV;
208         else {
209                 entry->proc_fops = &acpi_button_info_fops;
210                 entry->data = acpi_driver_data(device);
211                 entry->owner = THIS_MODULE;
212         }
213
214         /* show lid state [R] */
215         if (button->type == ACPI_BUTTON_TYPE_LID) {
216                 entry = create_proc_entry(ACPI_BUTTON_FILE_STATE,
217                                           S_IRUGO, acpi_device_dir(device));
218                 if (!entry)
219                         return -ENODEV;
220                 else {
221                         entry->proc_fops = &acpi_button_state_fops;
222                         entry->data = acpi_driver_data(device);
223                         entry->owner = THIS_MODULE;
224                 }
225         }
226
227         return 0;
228 }
229
230 static int acpi_button_remove_fs(struct acpi_device *device)
231 {
232         struct acpi_button *button = NULL;
233
234
235         button = acpi_driver_data(device);
236         if (acpi_device_dir(device)) {
237                 if (button->type == ACPI_BUTTON_TYPE_LID)
238                         remove_proc_entry(ACPI_BUTTON_FILE_STATE,
239                                           acpi_device_dir(device));
240                 remove_proc_entry(ACPI_BUTTON_FILE_INFO,
241                                   acpi_device_dir(device));
242
243                 remove_proc_entry(acpi_device_bid(device),
244                                   acpi_device_dir(device)->parent);
245                 acpi_device_dir(device) = NULL;
246         }
247
248         return 0;
249 }
250
251 /* --------------------------------------------------------------------------
252                                 Driver Interface
253    -------------------------------------------------------------------------- */
254
255 static void acpi_button_notify(acpi_handle handle, u32 event, void *data)
256 {
257         struct acpi_button *button = (struct acpi_button *)data;
258
259
260         if (!button || !button->device)
261                 return;
262
263         switch (event) {
264         case ACPI_BUTTON_NOTIFY_STATUS:
265                 acpi_bus_generate_event(button->device, event,
266                                         ++button->pushed);
267                 break;
268         default:
269                 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
270                                   "Unsupported event [0x%x]\n", event));
271                 break;
272         }
273
274         return;
275 }
276
277 static acpi_status acpi_button_notify_fixed(void *data)
278 {
279         struct acpi_button *button = (struct acpi_button *)data;
280
281
282         if (!button)
283                 return AE_BAD_PARAMETER;
284
285         acpi_button_notify(button->handle, ACPI_BUTTON_NOTIFY_STATUS, button);
286
287         return AE_OK;
288 }
289
290 static int acpi_button_add(struct acpi_device *device)
291 {
292         int result = 0;
293         acpi_status status = AE_OK;
294         struct acpi_button *button = NULL;
295
296
297         if (!device)
298                 return -EINVAL;
299
300         button = kmalloc(sizeof(struct acpi_button), GFP_KERNEL);
301         if (!button)
302                 return -ENOMEM;
303         memset(button, 0, sizeof(struct acpi_button));
304
305         button->device = device;
306         button->handle = device->handle;
307         acpi_driver_data(device) = button;
308
309         /*
310          * Determine the button type (via hid), as fixed-feature buttons
311          * need to be handled a bit differently than generic-space.
312          */
313         if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWER)) {
314                 button->type = ACPI_BUTTON_TYPE_POWER;
315                 strcpy(acpi_device_name(device), ACPI_BUTTON_DEVICE_NAME_POWER);
316                 sprintf(acpi_device_class(device), "%s/%s",
317                         ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
318         } else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) {
319                 button->type = ACPI_BUTTON_TYPE_POWERF;
320                 strcpy(acpi_device_name(device),
321                        ACPI_BUTTON_DEVICE_NAME_POWERF);
322                 sprintf(acpi_device_class(device), "%s/%s",
323                         ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
324         } else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEP)) {
325                 button->type = ACPI_BUTTON_TYPE_SLEEP;
326                 strcpy(acpi_device_name(device), ACPI_BUTTON_DEVICE_NAME_SLEEP);
327                 sprintf(acpi_device_class(device), "%s/%s",
328                         ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
329         } else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) {
330                 button->type = ACPI_BUTTON_TYPE_SLEEPF;
331                 strcpy(acpi_device_name(device),
332                        ACPI_BUTTON_DEVICE_NAME_SLEEPF);
333                 sprintf(acpi_device_class(device), "%s/%s",
334                         ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
335         } else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_LID)) {
336                 button->type = ACPI_BUTTON_TYPE_LID;
337                 strcpy(acpi_device_name(device), ACPI_BUTTON_DEVICE_NAME_LID);
338                 sprintf(acpi_device_class(device), "%s/%s",
339                         ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
340         } else {
341                 printk(KERN_ERR PREFIX "Unsupported hid [%s]\n",
342                             acpi_device_hid(device));
343                 result = -ENODEV;
344                 goto end;
345         }
346
347         result = acpi_button_add_fs(device);
348         if (result)
349                 goto end;
350
351         switch (button->type) {
352         case ACPI_BUTTON_TYPE_POWERF:
353                 status =
354                     acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
355                                                      acpi_button_notify_fixed,
356                                                      button);
357                 break;
358         case ACPI_BUTTON_TYPE_SLEEPF:
359                 status =
360                     acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
361                                                      acpi_button_notify_fixed,
362                                                      button);
363                 break;
364         default:
365                 status = acpi_install_notify_handler(button->handle,
366                                                      ACPI_DEVICE_NOTIFY,
367                                                      acpi_button_notify,
368                                                      button);
369                 break;
370         }
371
372         if (ACPI_FAILURE(status)) {
373                 result = -ENODEV;
374                 goto end;
375         }
376
377         if (device->wakeup.flags.valid) {
378                 /* Button's GPE is run-wake GPE */
379                 acpi_set_gpe_type(device->wakeup.gpe_device,
380                                   device->wakeup.gpe_number,
381                                   ACPI_GPE_TYPE_WAKE_RUN);
382                 acpi_enable_gpe(device->wakeup.gpe_device,
383                                 device->wakeup.gpe_number, ACPI_NOT_ISR);
384                 device->wakeup.state.enabled = 1;
385         }
386
387         printk(KERN_INFO PREFIX "%s [%s]\n",
388                acpi_device_name(device), acpi_device_bid(device));
389
390       end:
391         if (result) {
392                 acpi_button_remove_fs(device);
393                 kfree(button);
394         }
395
396         return result;
397 }
398
399 static int acpi_button_remove(struct acpi_device *device, int type)
400 {
401         acpi_status status = 0;
402         struct acpi_button *button = NULL;
403
404
405         if (!device || !acpi_driver_data(device))
406                 return -EINVAL;
407
408         button = acpi_driver_data(device);
409
410         /* Unregister for device notifications. */
411         switch (button->type) {
412         case ACPI_BUTTON_TYPE_POWERF:
413                 status =
414                     acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
415                                                     acpi_button_notify_fixed);
416                 break;
417         case ACPI_BUTTON_TYPE_SLEEPF:
418                 status =
419                     acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
420                                                     acpi_button_notify_fixed);
421                 break;
422         default:
423                 status = acpi_remove_notify_handler(button->handle,
424                                                     ACPI_DEVICE_NOTIFY,
425                                                     acpi_button_notify);
426                 break;
427         }
428
429         acpi_button_remove_fs(device);
430
431         kfree(button);
432
433         return 0;
434 }
435
436 static int __init acpi_button_init(void)
437 {
438         int result = 0;
439
440
441         acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
442         if (!acpi_button_dir)
443                 return -ENODEV;
444         acpi_button_dir->owner = THIS_MODULE;
445         result = acpi_bus_register_driver(&acpi_button_driver);
446         if (result < 0) {
447                 remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
448                 return -ENODEV;
449         }
450
451         return 0;
452 }
453
454 static void __exit acpi_button_exit(void)
455 {
456
457         acpi_bus_unregister_driver(&acpi_button_driver);
458
459         if (acpi_power_dir)
460                 remove_proc_entry(ACPI_BUTTON_SUBCLASS_POWER, acpi_button_dir);
461         if (acpi_sleep_dir)
462                 remove_proc_entry(ACPI_BUTTON_SUBCLASS_SLEEP, acpi_button_dir);
463         if (acpi_lid_dir)
464                 remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
465         remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
466
467         return;
468 }
469
470 module_init(acpi_button_init);
471 module_exit(acpi_button_exit);