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 112 static int acpi_button_info_seq_show(struct seq_file *seq, void *offset) 113 { 114 struct acpi_button *button = (struct acpi_button *) seq->private; 115 116 ACPI_FUNCTION_TRACE("acpi_button_info_seq_show"); 117 118 if (!button || !button->device) 119 return_VALUE(0); 120 121 seq_printf(seq, "type: %s\n", 122 acpi_device_name(button->device)); 123 124 return_VALUE(0); 125 } 126 127 static int acpi_button_info_open_fs(struct inode *inode, struct file *file) 128 { 129 return single_open(file, acpi_button_info_seq_show, PDE(inode)->data); 130 } 131 132 static int acpi_button_state_seq_show(struct seq_file *seq, void *offset) 133 { 134 struct acpi_button *button = (struct acpi_button *) seq->private; 135 acpi_status status; 136 unsigned long state; 137 138 ACPI_FUNCTION_TRACE("acpi_button_state_seq_show"); 139 140 if (!button || !button->device) 141 return_VALUE(0); 142 143 status = acpi_evaluate_integer(button->handle,"_LID",NULL,&state); 144 if (ACPI_FAILURE(status)) { 145 seq_printf(seq, "state: unsupported\n"); 146 } 147 else{ 148 seq_printf(seq, "state: %s\n", (state ? "open" : "closed")); 149 } 150 151 return_VALUE(0); 152 } 153 154 static int acpi_button_state_open_fs(struct inode *inode, struct file *file) 155 { 156 return single_open(file, acpi_button_state_seq_show, PDE(inode)->data); 157 } 158 159 static int 160 acpi_button_add_fs ( 161 struct acpi_device *device) 162 { 163 struct proc_dir_entry *entry = NULL; 164 struct acpi_button *button = NULL; 165 166 ACPI_FUNCTION_TRACE("acpi_button_add_fs"); 167 168 if (!device || !acpi_driver_data(device)) 169 return_VALUE(-EINVAL); 170 171 button = acpi_driver_data(device); 172 173 switch (button->type) { 174 case ACPI_BUTTON_TYPE_POWER: 175 case ACPI_BUTTON_TYPE_POWERF: 176 entry = proc_mkdir(ACPI_BUTTON_SUBCLASS_POWER, 177 acpi_button_dir); 178 break; 179 case ACPI_BUTTON_TYPE_SLEEP: 180 case ACPI_BUTTON_TYPE_SLEEPF: 181 entry = proc_mkdir(ACPI_BUTTON_SUBCLASS_SLEEP, 182 acpi_button_dir); 183 break; 184 case ACPI_BUTTON_TYPE_LID: 185 entry = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, 186 acpi_button_dir); 187 break; 188 } 189 190 if (!entry) 191 return_VALUE(-ENODEV); 192 entry->owner = THIS_MODULE; 193 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; 198 199 /* 'info' [R] */ 200 entry = create_proc_entry(ACPI_BUTTON_FILE_INFO, 201 S_IRUGO, acpi_device_dir(device)); 202 if (!entry) 203 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 204 "Unable to create '%s' fs entry\n", 205 ACPI_BUTTON_FILE_INFO)); 206 else { 207 entry->proc_fops = &acpi_button_info_fops; 208 entry->data = acpi_driver_data(device); 209 entry->owner = THIS_MODULE; 210 } 211 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)); 216 if (!entry) 217 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 218 "Unable to create '%s' fs entry\n", 219 ACPI_BUTTON_FILE_INFO)); 220 else { 221 entry->proc_fops = &acpi_button_state_fops; 222 entry->data = acpi_driver_data(device); 223 entry->owner = THIS_MODULE; 224 } 225 } 226 227 return_VALUE(0); 228 } 229 230 231 static int 232 acpi_button_remove_fs ( 233 struct acpi_device *device) 234 { 235 struct acpi_button *button = NULL; 236 237 ACPI_FUNCTION_TRACE("acpi_button_remove_fs"); 238 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)); 246 247 remove_proc_entry(acpi_device_bid(device), 248 acpi_device_dir(device)->parent); 249 250 251 switch (button->type) { 252 case ACPI_BUTTON_TYPE_POWER: 253 case ACPI_BUTTON_TYPE_POWERF: 254 remove_proc_entry(ACPI_BUTTON_SUBCLASS_POWER, 255 acpi_button_dir); 256 break; 257 case ACPI_BUTTON_TYPE_SLEEP: 258 case ACPI_BUTTON_TYPE_SLEEPF: 259 remove_proc_entry(ACPI_BUTTON_SUBCLASS_SLEEP, 260 acpi_button_dir); 261 break; 262 case ACPI_BUTTON_TYPE_LID: 263 remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, 264 acpi_button_dir); 265 break; 266 } 267 acpi_device_dir(device) = NULL; 268 } 269 270 return_VALUE(0); 271 } 272 273 274 /* -------------------------------------------------------------------------- 275 Driver Interface 276 -------------------------------------------------------------------------- */ 277 278 static void 279 acpi_button_notify ( 280 acpi_handle handle, 281 u32 event, 282 void *data) 283 { 284 struct acpi_button *button = (struct acpi_button *) data; 285 286 ACPI_FUNCTION_TRACE("acpi_button_notify"); 287 288 if (!button || !button->device) 289 return_VOID; 290 291 switch (event) { 292 case ACPI_BUTTON_NOTIFY_STATUS: 293 acpi_bus_generate_event(button->device, event, ++button->pushed); 294 break; 295 default: 296 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 297 "Unsupported event [0x%x]\n", event)); 298 break; 299 } 300 301 return_VOID; 302 } 303 304 305 static acpi_status 306 acpi_button_notify_fixed ( 307 void *data) 308 { 309 struct acpi_button *button = (struct acpi_button *) data; 310 311 ACPI_FUNCTION_TRACE("acpi_button_notify_fixed"); 312 313 if (!button) 314 return_ACPI_STATUS(AE_BAD_PARAMETER); 315 316 acpi_button_notify(button->handle, ACPI_BUTTON_NOTIFY_STATUS, button); 317 318 return_ACPI_STATUS(AE_OK); 319 } 320 321 322 static int 323 acpi_button_add ( 324 struct acpi_device *device) 325 { 326 int result = 0; 327 acpi_status status = AE_OK; 328 struct acpi_button *button = NULL; 329 330 static struct acpi_device *power_button; 331 static struct acpi_device *sleep_button; 332 static struct acpi_device *lid_button; 333 334 ACPI_FUNCTION_TRACE("acpi_button_add"); 335 336 if (!device) 337 return_VALUE(-EINVAL); 338 339 button = kmalloc(sizeof(struct acpi_button), GFP_KERNEL); 340 if (!button) 341 return_VALUE(-ENOMEM); 342 memset(button, 0, sizeof(struct acpi_button)); 343 344 button->device = device; 345 button->handle = device->handle; 346 acpi_driver_data(device) = button; 347 348 /* 349 * Determine the button type (via hid), as fixed-feature buttons 350 * need to be handled a bit differently than generic-space. 351 */ 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); 358 } 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); 365 } 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); 372 } 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); 379 } 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); 386 } 387 else { 388 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unsupported hid [%s]\n", 389 acpi_device_hid(device))); 390 result = -ENODEV; 391 goto end; 392 } 393 394 /* 395 * Ensure only one button of each type is used. 396 */ 397 switch (button->type) { 398 case ACPI_BUTTON_TYPE_POWER: 399 case ACPI_BUTTON_TYPE_POWERF: 400 if (!power_button) 401 power_button = device; 402 else { 403 kfree(button); 404 return_VALUE(-ENODEV); 405 } 406 break; 407 case ACPI_BUTTON_TYPE_SLEEP: 408 case ACPI_BUTTON_TYPE_SLEEPF: 409 if (!sleep_button) 410 sleep_button = device; 411 else { 412 kfree(button); 413 return_VALUE(-ENODEV); 414 } 415 break; 416 case ACPI_BUTTON_TYPE_LID: 417 if (!lid_button) 418 lid_button = device; 419 else { 420 kfree(button); 421 return_VALUE(-ENODEV); 422 } 423 break; 424 } 425 426 result = acpi_button_add_fs(device); 427 if (result) 428 goto end; 429 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, 435 button); 436 break; 437 case ACPI_BUTTON_TYPE_SLEEPF: 438 status = acpi_install_fixed_event_handler ( 439 ACPI_EVENT_SLEEP_BUTTON, 440 acpi_button_notify_fixed, 441 button); 442 break; 443 default: 444 status = acpi_install_notify_handler ( 445 button->handle, 446 ACPI_DEVICE_NOTIFY, 447 acpi_button_notify, 448 button); 449 break; 450 } 451 452 if (ACPI_FAILURE(status)) { 453 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 454 "Error installing notify handler\n")); 455 result = -ENODEV; 456 goto end; 457 } 458 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; 466 } 467 468 printk(KERN_INFO PREFIX "%s [%s]\n", 469 acpi_device_name(device), acpi_device_bid(device)); 470 471 end: 472 if (result) { 473 acpi_button_remove_fs(device); 474 kfree(button); 475 } 476 477 return_VALUE(result); 478 } 479 480 481 static int 482 acpi_button_remove (struct acpi_device *device, int type) 483 { 484 acpi_status status = 0; 485 struct acpi_button *button = NULL; 486 487 ACPI_FUNCTION_TRACE("acpi_button_remove"); 488 489 if (!device || !acpi_driver_data(device)) 490 return_VALUE(-EINVAL); 491 492 button = acpi_driver_data(device); 493 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); 499 break; 500 case ACPI_BUTTON_TYPE_SLEEPF: 501 status = acpi_remove_fixed_event_handler( 502 ACPI_EVENT_SLEEP_BUTTON, acpi_button_notify_fixed); 503 break; 504 default: 505 status = acpi_remove_notify_handler(button->handle, 506 ACPI_DEVICE_NOTIFY, acpi_button_notify); 507 break; 508 } 509 510 if (ACPI_FAILURE(status)) 511 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 512 "Error removing notify handler\n")); 513 514 acpi_button_remove_fs(device); 515 516 kfree(button); 517 518 return_VALUE(0); 519 } 520 521 522 static int __init 523 acpi_button_init (void) 524 { 525 int result = 0; 526 527 ACPI_FUNCTION_TRACE("acpi_button_init"); 528 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; 533 534 result = acpi_bus_register_driver(&acpi_button_driver); 535 if (result < 0) { 536 remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); 537 return_VALUE(-ENODEV); 538 } 539 540 return_VALUE(0); 541 } 542 543 544 static void __exit 545 acpi_button_exit (void) 546 { 547 ACPI_FUNCTION_TRACE("acpi_button_exit"); 548 549 acpi_bus_unregister_driver(&acpi_button_driver); 550 551 remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); 552 553 return_VOID; 554 } 555 556 557 module_init(acpi_button_init); 558 module_exit(acpi_button_exit); 559