brute-forced more changes from MontaVista's tree. SCSI partition table read still...
[linux-2.4.git] / drivers / acpi / button.c
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
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
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
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
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
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
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
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
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
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
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);