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