2 * acpi_button.c - ACPI Button Driver ($Revision: 30 $)
4 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
5 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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.
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.
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.
23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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>
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
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
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
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
63 #define _COMPONENT ACPI_BUTTON_COMPONENT
64 ACPI_MODULE_NAME ("acpi_button")
66 MODULE_AUTHOR("Paul Diefenbaugh");
67 MODULE_DESCRIPTION(ACPI_BUTTON_DRIVER_NAME
);
68 MODULE_LICENSE("GPL");
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
);
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",
81 .add
= acpi_button_add
,
82 .remove
= acpi_button_remove
,
88 struct acpi_device
*device
; /* Fixed button kludge */
93 static struct file_operations acpi_button_info_fops
= {
94 .open
= acpi_button_info_open_fs
,
97 .release
= single_release
,
100 static struct file_operations acpi_button_state_fops
= {
101 .open
= acpi_button_state_open_fs
,
104 .release
= single_release
,
106 /* --------------------------------------------------------------------------
108 -------------------------------------------------------------------------- */
110 static struct proc_dir_entry
*acpi_button_dir
;
112 static int acpi_button_info_seq_show(struct seq_file
*seq
, void *offset
)
114 struct acpi_button
*button
= (struct acpi_button
*) seq
->private;
116 ACPI_FUNCTION_TRACE("acpi_button_info_seq_show");
118 if (!button
|| !button
->device
)
121 seq_printf(seq
, "type: %s\n",
122 acpi_device_name(button
->device
));
127 static int acpi_button_info_open_fs(struct inode
*inode
, struct file
*file
)
129 return single_open(file
, acpi_button_info_seq_show
, PDE(inode
)->data
);
132 static int acpi_button_state_seq_show(struct seq_file
*seq
, void *offset
)
134 struct acpi_button
*button
= (struct acpi_button
*) seq
->private;
138 ACPI_FUNCTION_TRACE("acpi_button_state_seq_show");
140 if (!button
|| !button
->device
)
143 status
= acpi_evaluate_integer(button
->handle
,"_LID",NULL
,&state
);
144 if (ACPI_FAILURE(status
)) {
145 seq_printf(seq
, "state: unsupported\n");
148 seq_printf(seq
, "state: %s\n", (state
? "open" : "closed"));
154 static int acpi_button_state_open_fs(struct inode
*inode
, struct file
*file
)
156 return single_open(file
, acpi_button_state_seq_show
, PDE(inode
)->data
);
161 struct acpi_device
*device
)
163 struct proc_dir_entry
*entry
= NULL
;
164 struct acpi_button
*button
= NULL
;
166 ACPI_FUNCTION_TRACE("acpi_button_add_fs");
168 if (!device
|| !acpi_driver_data(device
))
169 return_VALUE(-EINVAL
);
171 button
= acpi_driver_data(device
);
173 switch (button
->type
) {
174 case ACPI_BUTTON_TYPE_POWER
:
175 case ACPI_BUTTON_TYPE_POWERF
:
176 entry
= proc_mkdir(ACPI_BUTTON_SUBCLASS_POWER
,
179 case ACPI_BUTTON_TYPE_SLEEP
:
180 case ACPI_BUTTON_TYPE_SLEEPF
:
181 entry
= proc_mkdir(ACPI_BUTTON_SUBCLASS_SLEEP
,
184 case ACPI_BUTTON_TYPE_LID
:
185 entry
= proc_mkdir(ACPI_BUTTON_SUBCLASS_LID
,
191 return_VALUE(-ENODEV
);
192 entry
->owner
= THIS_MODULE
;
194 acpi_device_dir(device
) = proc_mkdir(acpi_device_bid(device
), entry
);
195 if (!acpi_device_dir(device
))
196 return_VALUE(-ENODEV
);
197 acpi_device_dir(device
)->owner
= THIS_MODULE
;
200 entry
= create_proc_entry(ACPI_BUTTON_FILE_INFO
,
201 S_IRUGO
, acpi_device_dir(device
));
203 ACPI_DEBUG_PRINT((ACPI_DB_ERROR
,
204 "Unable to create '%s' fs entry\n",
205 ACPI_BUTTON_FILE_INFO
));
207 entry
->proc_fops
= &acpi_button_info_fops
;
208 entry
->data
= acpi_driver_data(device
);
209 entry
->owner
= THIS_MODULE
;
212 /* show lid state [R] */
213 if (button
->type
== ACPI_BUTTON_TYPE_LID
) {
214 entry
= create_proc_entry(ACPI_BUTTON_FILE_STATE
,
215 S_IRUGO
, acpi_device_dir(device
));
217 ACPI_DEBUG_PRINT((ACPI_DB_ERROR
,
218 "Unable to create '%s' fs entry\n",
219 ACPI_BUTTON_FILE_INFO
));
221 entry
->proc_fops
= &acpi_button_state_fops
;
222 entry
->data
= acpi_driver_data(device
);
223 entry
->owner
= THIS_MODULE
;
232 acpi_button_remove_fs (
233 struct acpi_device
*device
)
235 struct acpi_button
*button
= NULL
;
237 ACPI_FUNCTION_TRACE("acpi_button_remove_fs");
239 button
= acpi_driver_data(device
);
240 if (acpi_device_dir(device
)) {
241 if (button
->type
== ACPI_BUTTON_TYPE_LID
)
242 remove_proc_entry(ACPI_BUTTON_FILE_STATE
,
243 acpi_device_dir(device
));
244 remove_proc_entry(ACPI_BUTTON_FILE_INFO
,
245 acpi_device_dir(device
));
247 remove_proc_entry(acpi_device_bid(device
),
248 acpi_device_dir(device
)->parent
);
251 switch (button
->type
) {
252 case ACPI_BUTTON_TYPE_POWER
:
253 case ACPI_BUTTON_TYPE_POWERF
:
254 remove_proc_entry(ACPI_BUTTON_SUBCLASS_POWER
,
257 case ACPI_BUTTON_TYPE_SLEEP
:
258 case ACPI_BUTTON_TYPE_SLEEPF
:
259 remove_proc_entry(ACPI_BUTTON_SUBCLASS_SLEEP
,
262 case ACPI_BUTTON_TYPE_LID
:
263 remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID
,
267 acpi_device_dir(device
) = NULL
;
274 /* --------------------------------------------------------------------------
276 -------------------------------------------------------------------------- */
284 struct acpi_button
*button
= (struct acpi_button
*) data
;
286 ACPI_FUNCTION_TRACE("acpi_button_notify");
288 if (!button
|| !button
->device
)
292 case ACPI_BUTTON_NOTIFY_STATUS
:
293 acpi_bus_generate_event(button
->device
, event
, ++button
->pushed
);
296 ACPI_DEBUG_PRINT((ACPI_DB_INFO
,
297 "Unsupported event [0x%x]\n", event
));
306 acpi_button_notify_fixed (
309 struct acpi_button
*button
= (struct acpi_button
*) data
;
311 ACPI_FUNCTION_TRACE("acpi_button_notify_fixed");
314 return_ACPI_STATUS(AE_BAD_PARAMETER
);
316 acpi_button_notify(button
->handle
, ACPI_BUTTON_NOTIFY_STATUS
, button
);
318 return_ACPI_STATUS(AE_OK
);
324 struct acpi_device
*device
)
327 acpi_status status
= AE_OK
;
328 struct acpi_button
*button
= NULL
;
330 static struct acpi_device
*power_button
;
331 static struct acpi_device
*sleep_button
;
332 static struct acpi_device
*lid_button
;
334 ACPI_FUNCTION_TRACE("acpi_button_add");
337 return_VALUE(-EINVAL
);
339 button
= kmalloc(sizeof(struct acpi_button
), GFP_KERNEL
);
341 return_VALUE(-ENOMEM
);
342 memset(button
, 0, sizeof(struct acpi_button
));
344 button
->device
= device
;
345 button
->handle
= device
->handle
;
346 acpi_driver_data(device
) = button
;
349 * Determine the button type (via hid), as fixed-feature buttons
350 * need to be handled a bit differently than generic-space.
352 if (!strcmp(acpi_device_hid(device
), ACPI_BUTTON_HID_POWER
)) {
353 button
->type
= ACPI_BUTTON_TYPE_POWER
;
354 strcpy(acpi_device_name(device
),
355 ACPI_BUTTON_DEVICE_NAME_POWER
);
356 sprintf(acpi_device_class(device
), "%s/%s",
357 ACPI_BUTTON_CLASS
, ACPI_BUTTON_SUBCLASS_POWER
);
359 else if (!strcmp(acpi_device_hid(device
), ACPI_BUTTON_HID_POWERF
)) {
360 button
->type
= ACPI_BUTTON_TYPE_POWERF
;
361 strcpy(acpi_device_name(device
),
362 ACPI_BUTTON_DEVICE_NAME_POWERF
);
363 sprintf(acpi_device_class(device
), "%s/%s",
364 ACPI_BUTTON_CLASS
, ACPI_BUTTON_SUBCLASS_POWER
);
366 else if (!strcmp(acpi_device_hid(device
), ACPI_BUTTON_HID_SLEEP
)) {
367 button
->type
= ACPI_BUTTON_TYPE_SLEEP
;
368 strcpy(acpi_device_name(device
),
369 ACPI_BUTTON_DEVICE_NAME_SLEEP
);
370 sprintf(acpi_device_class(device
), "%s/%s",
371 ACPI_BUTTON_CLASS
, ACPI_BUTTON_SUBCLASS_SLEEP
);
373 else if (!strcmp(acpi_device_hid(device
), ACPI_BUTTON_HID_SLEEPF
)) {
374 button
->type
= ACPI_BUTTON_TYPE_SLEEPF
;
375 strcpy(acpi_device_name(device
),
376 ACPI_BUTTON_DEVICE_NAME_SLEEPF
);
377 sprintf(acpi_device_class(device
), "%s/%s",
378 ACPI_BUTTON_CLASS
, ACPI_BUTTON_SUBCLASS_SLEEP
);
380 else if (!strcmp(acpi_device_hid(device
), ACPI_BUTTON_HID_LID
)) {
381 button
->type
= ACPI_BUTTON_TYPE_LID
;
382 strcpy(acpi_device_name(device
),
383 ACPI_BUTTON_DEVICE_NAME_LID
);
384 sprintf(acpi_device_class(device
), "%s/%s",
385 ACPI_BUTTON_CLASS
, ACPI_BUTTON_SUBCLASS_LID
);
388 ACPI_DEBUG_PRINT((ACPI_DB_ERROR
, "Unsupported hid [%s]\n",
389 acpi_device_hid(device
)));
395 * Ensure only one button of each type is used.
397 switch (button
->type
) {
398 case ACPI_BUTTON_TYPE_POWER
:
399 case ACPI_BUTTON_TYPE_POWERF
:
401 power_button
= device
;
404 return_VALUE(-ENODEV
);
407 case ACPI_BUTTON_TYPE_SLEEP
:
408 case ACPI_BUTTON_TYPE_SLEEPF
:
410 sleep_button
= device
;
413 return_VALUE(-ENODEV
);
416 case ACPI_BUTTON_TYPE_LID
:
421 return_VALUE(-ENODEV
);
426 result
= acpi_button_add_fs(device
);
430 switch (button
->type
) {
431 case ACPI_BUTTON_TYPE_POWERF
:
432 status
= acpi_install_fixed_event_handler (
433 ACPI_EVENT_POWER_BUTTON
,
434 acpi_button_notify_fixed
,
437 case ACPI_BUTTON_TYPE_SLEEPF
:
438 status
= acpi_install_fixed_event_handler (
439 ACPI_EVENT_SLEEP_BUTTON
,
440 acpi_button_notify_fixed
,
444 status
= acpi_install_notify_handler (
452 if (ACPI_FAILURE(status
)) {
453 ACPI_DEBUG_PRINT((ACPI_DB_ERROR
,
454 "Error installing notify handler\n"));
459 if (device
->wakeup
.flags
.valid
) {
460 /* Button's GPE is run-wake GPE */
461 acpi_set_gpe_type(device
->wakeup
.gpe_device
,
462 device
->wakeup
.gpe_number
, ACPI_GPE_TYPE_WAKE_RUN
);
463 acpi_enable_gpe(device
->wakeup
.gpe_device
,
464 device
->wakeup
.gpe_number
, ACPI_NOT_ISR
);
465 device
->wakeup
.state
.enabled
= 1;
468 printk(KERN_INFO PREFIX
"%s [%s]\n",
469 acpi_device_name(device
), acpi_device_bid(device
));
473 acpi_button_remove_fs(device
);
477 return_VALUE(result
);
482 acpi_button_remove (struct acpi_device
*device
, int type
)
484 acpi_status status
= 0;
485 struct acpi_button
*button
= NULL
;
487 ACPI_FUNCTION_TRACE("acpi_button_remove");
489 if (!device
|| !acpi_driver_data(device
))
490 return_VALUE(-EINVAL
);
492 button
= acpi_driver_data(device
);
494 /* Unregister for device notifications. */
495 switch (button
->type
) {
496 case ACPI_BUTTON_TYPE_POWERF
:
497 status
= acpi_remove_fixed_event_handler(
498 ACPI_EVENT_POWER_BUTTON
, acpi_button_notify_fixed
);
500 case ACPI_BUTTON_TYPE_SLEEPF
:
501 status
= acpi_remove_fixed_event_handler(
502 ACPI_EVENT_SLEEP_BUTTON
, acpi_button_notify_fixed
);
505 status
= acpi_remove_notify_handler(button
->handle
,
506 ACPI_DEVICE_NOTIFY
, acpi_button_notify
);
510 if (ACPI_FAILURE(status
))
511 ACPI_DEBUG_PRINT((ACPI_DB_ERROR
,
512 "Error removing notify handler\n"));
514 acpi_button_remove_fs(device
);
523 acpi_button_init (void)
527 ACPI_FUNCTION_TRACE("acpi_button_init");
529 acpi_button_dir
= proc_mkdir(ACPI_BUTTON_CLASS
, acpi_root_dir
);
530 if (!acpi_button_dir
)
531 return_VALUE(-ENODEV
);
532 acpi_button_dir
->owner
= THIS_MODULE
;
534 result
= acpi_bus_register_driver(&acpi_button_driver
);
536 remove_proc_entry(ACPI_BUTTON_CLASS
, acpi_root_dir
);
537 return_VALUE(-ENODEV
);
545 acpi_button_exit (void)
547 ACPI_FUNCTION_TRACE("acpi_button_exit");
549 acpi_bus_unregister_driver(&acpi_button_driver
);
551 remove_proc_entry(ACPI_BUTTON_CLASS
, acpi_root_dir
);
557 module_init(acpi_button_init
);
558 module_exit(acpi_button_exit
);