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 <linux/input.h> 33 #include <acpi/acpi_bus.h> 34 #include <acpi/acpi_drivers.h> 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 static int acpi_button_add(struct acpi_device *device); 71 static int acpi_button_remove(struct acpi_device *device, int type); 72 static int acpi_button_info_open_fs(struct inode *inode, struct file *file); 73 static int acpi_button_state_open_fs(struct inode *inode, struct file *file); 74 75 static struct acpi_driver acpi_button_driver = { 76 .name = ACPI_BUTTON_DRIVER_NAME, 77 .class = ACPI_BUTTON_CLASS, 78 .ids = "button_power,button_sleep,PNP0C0D,PNP0C0C,PNP0C0E", 79 .ops = { 80 .add = acpi_button_add, 81 .remove = acpi_button_remove, 82 }, 83 }; 84 85 struct acpi_button { 86 struct acpi_device *device; /* Fixed button kludge */ 87 unsigned int type; 88 struct input_dev *input; 89 char phys[32]; /* for input device */ 90 unsigned long pushed; 91 }; 92 93 static const 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 const 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 /* -------------------------------------------------------------------------- 108 FS Interface (/proc) 109 -------------------------------------------------------------------------- */ 110 111 static struct proc_dir_entry *acpi_button_dir; 112 113 static int acpi_button_info_seq_show(struct seq_file *seq, void *offset) 114 { 115 struct acpi_button *button = seq->private; 116 117 if (!button || !button->device) 118 return 0; 119 120 seq_printf(seq, "type: %s\n", 121 acpi_device_name(button->device)); 122 123 return 0; 124 } 125 126 static int acpi_button_info_open_fs(struct inode *inode, struct file *file) 127 { 128 return single_open(file, acpi_button_info_seq_show, PDE(inode)->data); 129 } 130 131 static int acpi_button_state_seq_show(struct seq_file *seq, void *offset) 132 { 133 struct acpi_button *button = seq->private; 134 acpi_status status; 135 unsigned long state; 136 137 if (!button || !button->device) 138 return 0; 139 140 status = acpi_evaluate_integer(button->device->handle, "_LID", NULL, &state); 141 seq_printf(seq, "state: %s\n", 142 ACPI_FAILURE(status) ? "unsupported" : 143 (state ? "open" : "closed")); 144 return 0; 145 } 146 147 static int acpi_button_state_open_fs(struct inode *inode, struct file *file) 148 { 149 return single_open(file, acpi_button_state_seq_show, PDE(inode)->data); 150 } 151 152 static struct proc_dir_entry *acpi_power_dir; 153 static struct proc_dir_entry *acpi_sleep_dir; 154 static struct proc_dir_entry *acpi_lid_dir; 155 156 static int acpi_button_add_fs(struct acpi_device *device) 157 { 158 struct proc_dir_entry *entry = NULL; 159 struct acpi_button *button; 160 161 if (!device || !acpi_driver_data(device)) 162 return -EINVAL; 163 164 button = acpi_driver_data(device); 165 166 switch (button->type) { 167 case ACPI_BUTTON_TYPE_POWER: 168 case ACPI_BUTTON_TYPE_POWERF: 169 if (!acpi_power_dir) 170 acpi_power_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_POWER, 171 acpi_button_dir); 172 entry = acpi_power_dir; 173 break; 174 case ACPI_BUTTON_TYPE_SLEEP: 175 case ACPI_BUTTON_TYPE_SLEEPF: 176 if (!acpi_sleep_dir) 177 acpi_sleep_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_SLEEP, 178 acpi_button_dir); 179 entry = acpi_sleep_dir; 180 break; 181 case ACPI_BUTTON_TYPE_LID: 182 if (!acpi_lid_dir) 183 acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, 184 acpi_button_dir); 185 entry = acpi_lid_dir; 186 break; 187 } 188 189 if (!entry) 190 return -ENODEV; 191 entry->owner = THIS_MODULE; 192 193 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), entry); 194 if (!acpi_device_dir(device)) 195 return -ENODEV; 196 acpi_device_dir(device)->owner = THIS_MODULE; 197 198 /* 'info' [R] */ 199 entry = create_proc_entry(ACPI_BUTTON_FILE_INFO, 200 S_IRUGO, acpi_device_dir(device)); 201 if (!entry) 202 return -ENODEV; 203 else { 204 entry->proc_fops = &acpi_button_info_fops; 205 entry->data = acpi_driver_data(device); 206 entry->owner = THIS_MODULE; 207 } 208 209 /* show lid state [R] */ 210 if (button->type == ACPI_BUTTON_TYPE_LID) { 211 entry = create_proc_entry(ACPI_BUTTON_FILE_STATE, 212 S_IRUGO, acpi_device_dir(device)); 213 if (!entry) 214 return -ENODEV; 215 else { 216 entry->proc_fops = &acpi_button_state_fops; 217 entry->data = acpi_driver_data(device); 218 entry->owner = THIS_MODULE; 219 } 220 } 221 222 return 0; 223 } 224 225 static int acpi_button_remove_fs(struct acpi_device *device) 226 { 227 struct acpi_button *button = acpi_driver_data(device); 228 229 if (acpi_device_dir(device)) { 230 if (button->type == ACPI_BUTTON_TYPE_LID) 231 remove_proc_entry(ACPI_BUTTON_FILE_STATE, 232 acpi_device_dir(device)); 233 remove_proc_entry(ACPI_BUTTON_FILE_INFO, 234 acpi_device_dir(device)); 235 236 remove_proc_entry(acpi_device_bid(device), 237 acpi_device_dir(device)->parent); 238 acpi_device_dir(device) = NULL; 239 } 240 241 return 0; 242 } 243 244 /* -------------------------------------------------------------------------- 245 Driver Interface 246 -------------------------------------------------------------------------- */ 247 248 static void acpi_button_notify(acpi_handle handle, u32 event, void *data) 249 { 250 struct acpi_button *button = data; 251 struct input_dev *input; 252 253 if (!button || !button->device) 254 return; 255 256 switch (event) { 257 case ACPI_BUTTON_NOTIFY_STATUS: 258 input = button->input; 259 260 if (button->type == ACPI_BUTTON_TYPE_LID) { 261 struct acpi_handle *handle = button->device->handle; 262 unsigned long state; 263 264 if (!ACPI_FAILURE(acpi_evaluate_integer(handle, "_LID", 265 NULL, &state))) 266 input_report_switch(input, SW_LID, !state); 267 268 } else { 269 int keycode = test_bit(KEY_SLEEP, input->keybit) ? 270 KEY_SLEEP : KEY_POWER; 271 272 input_report_key(input, keycode, 1); 273 input_sync(input); 274 input_report_key(input, keycode, 0); 275 } 276 input_sync(input); 277 278 acpi_bus_generate_event(button->device, event, 279 ++button->pushed); 280 break; 281 default: 282 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 283 "Unsupported event [0x%x]\n", event)); 284 break; 285 } 286 287 return; 288 } 289 290 static acpi_status acpi_button_notify_fixed(void *data) 291 { 292 struct acpi_button *button = data; 293 294 if (!button) 295 return AE_BAD_PARAMETER; 296 297 acpi_button_notify(button->device->handle, ACPI_BUTTON_NOTIFY_STATUS, button); 298 299 return AE_OK; 300 } 301 302 static int acpi_button_install_notify_handlers(struct acpi_button *button) 303 { 304 acpi_status status; 305 306 switch (button->type) { 307 case ACPI_BUTTON_TYPE_POWERF: 308 status = 309 acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, 310 acpi_button_notify_fixed, 311 button); 312 break; 313 case ACPI_BUTTON_TYPE_SLEEPF: 314 status = 315 acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, 316 acpi_button_notify_fixed, 317 button); 318 break; 319 default: 320 status = acpi_install_notify_handler(button->device->handle, 321 ACPI_DEVICE_NOTIFY, 322 acpi_button_notify, 323 button); 324 break; 325 } 326 327 return ACPI_FAILURE(status) ? -ENODEV : 0; 328 } 329 330 static void acpi_button_remove_notify_handlers(struct acpi_button *button) 331 { 332 switch (button->type) { 333 case ACPI_BUTTON_TYPE_POWERF: 334 acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, 335 acpi_button_notify_fixed); 336 break; 337 case ACPI_BUTTON_TYPE_SLEEPF: 338 acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, 339 acpi_button_notify_fixed); 340 break; 341 default: 342 acpi_remove_notify_handler(button->device->handle, 343 ACPI_DEVICE_NOTIFY, 344 acpi_button_notify); 345 break; 346 } 347 } 348 349 static int acpi_button_add(struct acpi_device *device) 350 { 351 int error; 352 struct acpi_button *button; 353 struct input_dev *input; 354 355 if (!device) 356 return -EINVAL; 357 358 button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL); 359 if (!button) 360 return -ENOMEM; 361 362 button->device = device; 363 acpi_driver_data(device) = button; 364 365 button->input = input = input_allocate_device(); 366 if (!input) { 367 error = -ENOMEM; 368 goto err_free_button; 369 } 370 371 /* 372 * Determine the button type (via hid), as fixed-feature buttons 373 * need to be handled a bit differently than generic-space. 374 */ 375 if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWER)) { 376 button->type = ACPI_BUTTON_TYPE_POWER; 377 strcpy(acpi_device_name(device), ACPI_BUTTON_DEVICE_NAME_POWER); 378 sprintf(acpi_device_class(device), "%s/%s", 379 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER); 380 } else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) { 381 button->type = ACPI_BUTTON_TYPE_POWERF; 382 strcpy(acpi_device_name(device), 383 ACPI_BUTTON_DEVICE_NAME_POWERF); 384 sprintf(acpi_device_class(device), "%s/%s", 385 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER); 386 } else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEP)) { 387 button->type = ACPI_BUTTON_TYPE_SLEEP; 388 strcpy(acpi_device_name(device), ACPI_BUTTON_DEVICE_NAME_SLEEP); 389 sprintf(acpi_device_class(device), "%s/%s", 390 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP); 391 } else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) { 392 button->type = ACPI_BUTTON_TYPE_SLEEPF; 393 strcpy(acpi_device_name(device), 394 ACPI_BUTTON_DEVICE_NAME_SLEEPF); 395 sprintf(acpi_device_class(device), "%s/%s", 396 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP); 397 } else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_LID)) { 398 button->type = ACPI_BUTTON_TYPE_LID; 399 strcpy(acpi_device_name(device), ACPI_BUTTON_DEVICE_NAME_LID); 400 sprintf(acpi_device_class(device), "%s/%s", 401 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID); 402 } else { 403 printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", 404 acpi_device_hid(device)); 405 error = -ENODEV; 406 goto err_free_input; 407 } 408 409 error = acpi_button_add_fs(device); 410 if (error) 411 goto err_free_input; 412 413 error = acpi_button_install_notify_handlers(button); 414 if (error) 415 goto err_remove_fs; 416 417 snprintf(button->phys, sizeof(button->phys), 418 "%s/button/input0", acpi_device_hid(device)); 419 420 input->name = acpi_device_name(device); 421 input->phys = button->phys; 422 input->id.bustype = BUS_HOST; 423 input->id.product = button->type; 424 425 switch (button->type) { 426 case ACPI_BUTTON_TYPE_POWER: 427 case ACPI_BUTTON_TYPE_POWERF: 428 input->evbit[0] = BIT(EV_KEY); 429 set_bit(KEY_POWER, input->keybit); 430 break; 431 432 case ACPI_BUTTON_TYPE_SLEEP: 433 case ACPI_BUTTON_TYPE_SLEEPF: 434 input->evbit[0] = BIT(EV_KEY); 435 set_bit(KEY_SLEEP, input->keybit); 436 break; 437 438 case ACPI_BUTTON_TYPE_LID: 439 input->evbit[0] = BIT(EV_SW); 440 set_bit(SW_LID, input->swbit); 441 break; 442 } 443 444 error = input_register_device(input); 445 if (error) 446 goto err_remove_handlers; 447 448 if (device->wakeup.flags.valid) { 449 /* Button's GPE is run-wake GPE */ 450 acpi_set_gpe_type(device->wakeup.gpe_device, 451 device->wakeup.gpe_number, 452 ACPI_GPE_TYPE_WAKE_RUN); 453 acpi_enable_gpe(device->wakeup.gpe_device, 454 device->wakeup.gpe_number, ACPI_NOT_ISR); 455 device->wakeup.state.enabled = 1; 456 } 457 458 printk(KERN_INFO PREFIX "%s [%s]\n", 459 acpi_device_name(device), acpi_device_bid(device)); 460 461 return 0; 462 463 err_remove_handlers: 464 acpi_button_remove_notify_handlers(button); 465 err_remove_fs: 466 acpi_button_remove_fs(device); 467 err_free_input: 468 input_free_device(input); 469 err_free_button: 470 kfree(button); 471 return error; 472 } 473 474 static int acpi_button_remove(struct acpi_device *device, int type) 475 { 476 struct acpi_button *button; 477 478 if (!device || !acpi_driver_data(device)) 479 return -EINVAL; 480 481 button = acpi_driver_data(device); 482 483 acpi_button_remove_notify_handlers(button); 484 acpi_button_remove_fs(device); 485 input_unregister_device(button->input); 486 kfree(button); 487 488 return 0; 489 } 490 491 static int __init acpi_button_init(void) 492 { 493 int result; 494 495 acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir); 496 if (!acpi_button_dir) 497 return -ENODEV; 498 acpi_button_dir->owner = THIS_MODULE; 499 result = acpi_bus_register_driver(&acpi_button_driver); 500 if (result < 0) { 501 remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); 502 return -ENODEV; 503 } 504 505 return 0; 506 } 507 508 static void __exit acpi_button_exit(void) 509 { 510 acpi_bus_unregister_driver(&acpi_button_driver); 511 512 if (acpi_power_dir) 513 remove_proc_entry(ACPI_BUTTON_SUBCLASS_POWER, acpi_button_dir); 514 if (acpi_sleep_dir) 515 remove_proc_entry(ACPI_BUTTON_SUBCLASS_SLEEP, acpi_button_dir); 516 if (acpi_lid_dir) 517 remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir); 518 remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); 519 } 520 521 module_init(acpi_button_init); 522 module_exit(acpi_button_exit); 523