Merge master.kernel.org:/pub/scm/linux/kernel/git/dtor/input
[pandora-kernel.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 <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"
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);
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
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
124         BUG_ON(!button);
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
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
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) {
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
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
296         result = acpi_bus_register_driver(&acpi_button_driver);
297         if (result < 0) {
298                 return_VALUE(-ENODEV);
299         }
300
301         return_VALUE(0);
302 }
303
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
311         return_VOID;
312 }
313
314 module_init(acpi_button_init);
315 module_exit(acpi_button_exit);