Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.20-rc2 1042 lines 28 kB view raw
1/* 2 * hotkey.c - ACPI Hotkey Driver ($Revision: 0.2 $) 3 * 4 * Copyright (C) 2004 Luming Yu <luming.yu@intel.com> 5 * 6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or (at 11 * your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, but 14 * WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License along 19 * with this program; if not, write to the Free Software Foundation, Inc., 20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 21 * 22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 23 */ 24#include <linux/kernel.h> 25#include <linux/module.h> 26#include <linux/init.h> 27#include <linux/types.h> 28#include <linux/proc_fs.h> 29#include <linux/sched.h> 30#include <linux/kmod.h> 31#include <linux/seq_file.h> 32#include <acpi/acpi_drivers.h> 33#include <acpi/acpi_bus.h> 34#include <asm/uaccess.h> 35 36#define HOTKEY_ACPI_VERSION "0.1" 37 38#define HOTKEY_PROC "hotkey" 39#define HOTKEY_EV_CONFIG "event_config" 40#define HOTKEY_PL_CONFIG "poll_config" 41#define HOTKEY_ACTION "action" 42#define HOTKEY_INFO "info" 43 44#define ACPI_HOTK_NAME "Generic Hotkey Driver" 45#define ACPI_HOTK_CLASS "Hotkey" 46#define ACPI_HOTK_DEVICE_NAME "Hotkey" 47#define ACPI_HOTK_HID "Unknown?" 48#define ACPI_HOTKEY_COMPONENT 0x20000000 49 50#define ACPI_HOTKEY_EVENT 0x1 51#define ACPI_HOTKEY_POLLING 0x2 52#define ACPI_UNDEFINED_EVENT 0xf 53 54#define RESULT_STR_LEN 80 55 56#define ACTION_METHOD 0 57#define POLL_METHOD 1 58 59#define IS_EVENT(e) ((e) <= 10000 && (e) >0) 60#define IS_POLL(e) ((e) > 10000) 61#define IS_OTHERS(e) ((e)<=0 || (e)>=20000) 62#define _COMPONENT ACPI_HOTKEY_COMPONENT 63ACPI_MODULE_NAME("acpi_hotkey") 64 65 MODULE_AUTHOR("luming.yu@intel.com"); 66MODULE_DESCRIPTION(ACPI_HOTK_NAME); 67MODULE_LICENSE("GPL"); 68 69/* standardized internal hotkey number/event */ 70enum { 71 /* Video Extension event */ 72 HK_EVENT_CYCLE_OUTPUT_DEVICE = 0x80, 73 HK_EVENT_OUTPUT_DEVICE_STATUS_CHANGE, 74 HK_EVENT_CYCLE_DISPLAY_OUTPUT, 75 HK_EVENT_NEXT_DISPLAY_OUTPUT, 76 HK_EVENT_PREVIOUS_DISPLAY_OUTPUT, 77 HK_EVENT_CYCLE_BRIGHTNESS, 78 HK_EVENT_INCREASE_BRIGHTNESS, 79 HK_EVENT_DECREASE_BRIGHTNESS, 80 HK_EVENT_ZERO_BRIGHTNESS, 81 HK_EVENT_DISPLAY_DEVICE_OFF, 82 83 /* Snd Card event */ 84 HK_EVENT_VOLUME_MUTE, 85 HK_EVENT_VOLUME_INCLREASE, 86 HK_EVENT_VOLUME_DECREASE, 87 88 /* running state control */ 89 HK_EVENT_ENTERRING_S3, 90 HK_EVENT_ENTERRING_S4, 91 HK_EVENT_ENTERRING_S5, 92}; 93 94enum conf_entry_enum { 95 bus_handle = 0, 96 bus_method = 1, 97 action_handle = 2, 98 method = 3, 99 LAST_CONF_ENTRY 100}; 101 102/* procdir we use */ 103static struct proc_dir_entry *hotkey_proc_dir; 104static struct proc_dir_entry *hotkey_config; 105static struct proc_dir_entry *hotkey_poll_config; 106static struct proc_dir_entry *hotkey_action; 107static struct proc_dir_entry *hotkey_info; 108 109/* linkage for all type of hotkey */ 110struct acpi_hotkey_link { 111 struct list_head entries; 112 int hotkey_type; /* event or polling based hotkey */ 113 int hotkey_standard_num; /* standardized hotkey(event) number */ 114}; 115 116/* event based hotkey */ 117struct acpi_event_hotkey { 118 struct acpi_hotkey_link hotkey_link; 119 int flag; 120 acpi_handle bus_handle; /* bus to install notify handler */ 121 int external_hotkey_num; /* external hotkey/event number */ 122 acpi_handle action_handle; /* acpi handle attached aml action method */ 123 char *action_method; /* action method */ 124}; 125 126/* 127 * There are two ways to poll status 128 * 1. directy call read_xxx method, without any arguments passed in 129 * 2. call write_xxx method, with arguments passed in, you need 130 * the result is saved in acpi_polling_hotkey.poll_result. 131 * anthoer read command through polling interface. 132 * 133 */ 134 135/* polling based hotkey */ 136struct acpi_polling_hotkey { 137 struct acpi_hotkey_link hotkey_link; 138 int flag; 139 acpi_handle poll_handle; /* acpi handle attached polling method */ 140 char *poll_method; /* poll method */ 141 acpi_handle action_handle; /* acpi handle attached action method */ 142 char *action_method; /* action method */ 143 union acpi_object *poll_result; /* polling_result */ 144 struct proc_dir_entry *proc; 145}; 146 147/* hotkey object union */ 148union acpi_hotkey { 149 struct list_head entries; 150 struct acpi_hotkey_link link; 151 struct acpi_event_hotkey event_hotkey; 152 struct acpi_polling_hotkey poll_hotkey; 153}; 154 155/* hotkey object list */ 156struct acpi_hotkey_list { 157 struct list_head *entries; 158 int count; 159}; 160 161static int auto_hotkey_add(struct acpi_device *device); 162static int auto_hotkey_remove(struct acpi_device *device, int type); 163 164static struct acpi_driver hotkey_driver = { 165 .name = ACPI_HOTK_NAME, 166 .class = ACPI_HOTK_CLASS, 167 .ids = ACPI_HOTK_HID, 168 .ops = { 169 .add = auto_hotkey_add, 170 .remove = auto_hotkey_remove, 171 }, 172}; 173 174static void free_hotkey_device(union acpi_hotkey *key); 175static void free_hotkey_buffer(union acpi_hotkey *key); 176static void free_poll_hotkey_buffer(union acpi_hotkey *key); 177static int hotkey_open_config(struct inode *inode, struct file *file); 178static int hotkey_poll_open_config(struct inode *inode, struct file *file); 179static ssize_t hotkey_write_config(struct file *file, 180 const char __user * buffer, 181 size_t count, loff_t * data); 182static int hotkey_info_open_fs(struct inode *inode, struct file *file); 183static int hotkey_action_open_fs(struct inode *inode, struct file *file); 184static ssize_t hotkey_execute_aml_method(struct file *file, 185 const char __user * buffer, 186 size_t count, loff_t * data); 187static int hotkey_config_seq_show(struct seq_file *seq, void *offset); 188static int hotkey_poll_config_seq_show(struct seq_file *seq, void *offset); 189static int hotkey_polling_open_fs(struct inode *inode, struct file *file); 190static union acpi_hotkey *get_hotkey_by_event(struct 191 acpi_hotkey_list 192 *hotkey_list, int event); 193 194/* event based config */ 195static const struct file_operations hotkey_config_fops = { 196 .open = hotkey_open_config, 197 .read = seq_read, 198 .write = hotkey_write_config, 199 .llseek = seq_lseek, 200 .release = single_release, 201}; 202 203/* polling based config */ 204static const struct file_operations hotkey_poll_config_fops = { 205 .open = hotkey_poll_open_config, 206 .read = seq_read, 207 .write = hotkey_write_config, 208 .llseek = seq_lseek, 209 .release = single_release, 210}; 211 212/* hotkey driver info */ 213static const struct file_operations hotkey_info_fops = { 214 .open = hotkey_info_open_fs, 215 .read = seq_read, 216 .llseek = seq_lseek, 217 .release = single_release, 218}; 219 220/* action */ 221static const struct file_operations hotkey_action_fops = { 222 .open = hotkey_action_open_fs, 223 .read = seq_read, 224 .write = hotkey_execute_aml_method, 225 .llseek = seq_lseek, 226 .release = single_release, 227}; 228 229/* polling results */ 230static const struct file_operations hotkey_polling_fops = { 231 .open = hotkey_polling_open_fs, 232 .read = seq_read, 233 .llseek = seq_lseek, 234 .release = single_release, 235}; 236 237struct acpi_hotkey_list global_hotkey_list; /* link all ev or pl hotkey */ 238struct list_head hotkey_entries; /* head of the list of hotkey_list */ 239 240static int hotkey_info_seq_show(struct seq_file *seq, void *offset) 241{ 242 243 seq_printf(seq, "Hotkey generic driver ver: %s\n", HOTKEY_ACPI_VERSION); 244 245 return 0; 246} 247 248static int hotkey_info_open_fs(struct inode *inode, struct file *file) 249{ 250 return single_open(file, hotkey_info_seq_show, PDE(inode)->data); 251} 252 253static char *format_result(union acpi_object *object) 254{ 255 char *buf; 256 257 buf = kzalloc(RESULT_STR_LEN, GFP_KERNEL); 258 if (!buf) 259 return NULL; 260 /* Now, just support integer type */ 261 if (object->type == ACPI_TYPE_INTEGER) 262 sprintf(buf, "%d\n", (u32) object->integer.value); 263 return buf; 264} 265 266static int hotkey_polling_seq_show(struct seq_file *seq, void *offset) 267{ 268 struct acpi_polling_hotkey *poll_hotkey = seq->private; 269 char *buf; 270 271 272 if (poll_hotkey->poll_result) { 273 buf = format_result(poll_hotkey->poll_result); 274 if (buf) 275 seq_printf(seq, "%s", buf); 276 kfree(buf); 277 } 278 return 0; 279} 280 281static int hotkey_polling_open_fs(struct inode *inode, struct file *file) 282{ 283 return single_open(file, hotkey_polling_seq_show, PDE(inode)->data); 284} 285 286static int hotkey_action_open_fs(struct inode *inode, struct file *file) 287{ 288 return single_open(file, hotkey_info_seq_show, PDE(inode)->data); 289} 290 291/* Mapping external hotkey number to standardized hotkey event num */ 292static int hotkey_get_internal_event(int event, struct acpi_hotkey_list *list) 293{ 294 struct list_head *entries; 295 int val = -1; 296 297 298 list_for_each(entries, list->entries) { 299 union acpi_hotkey *key = 300 container_of(entries, union acpi_hotkey, entries); 301 if (key->link.hotkey_type == ACPI_HOTKEY_EVENT 302 && key->event_hotkey.external_hotkey_num == event) { 303 val = key->link.hotkey_standard_num; 304 break; 305 } 306 } 307 308 return val; 309} 310 311static void 312acpi_hotkey_notify_handler(acpi_handle handle, u32 event, void *data) 313{ 314 struct acpi_device *device = NULL; 315 u32 internal_event; 316 317 318 if (acpi_bus_get_device(handle, &device)) 319 return; 320 321 internal_event = hotkey_get_internal_event(event, &global_hotkey_list); 322 acpi_bus_generate_event(device, internal_event, 0); 323 324 return; 325} 326 327/* Need to invent automatically hotkey add method */ 328static int auto_hotkey_add(struct acpi_device *device) 329{ 330 /* Implement me */ 331 return 0; 332} 333 334/* Need to invent automatically hotkey remove method */ 335static int auto_hotkey_remove(struct acpi_device *device, int type) 336{ 337 /* Implement me */ 338 return 0; 339} 340 341/* Create a proc file for each polling method */ 342static int create_polling_proc(union acpi_hotkey *device) 343{ 344 struct proc_dir_entry *proc; 345 char proc_name[80]; 346 mode_t mode; 347 348 mode = S_IFREG | S_IRUGO | S_IWUGO; 349 350 sprintf(proc_name, "%d", device->link.hotkey_standard_num); 351 /* 352 strcat(proc_name, device->poll_hotkey.poll_method); 353 */ 354 proc = create_proc_entry(proc_name, mode, hotkey_proc_dir); 355 356 if (!proc) { 357 return -ENODEV; 358 } else { 359 proc->proc_fops = &hotkey_polling_fops; 360 proc->owner = THIS_MODULE; 361 proc->data = device; 362 proc->uid = 0; 363 proc->gid = 0; 364 device->poll_hotkey.proc = proc; 365 } 366 return 0; 367} 368 369static int hotkey_add(union acpi_hotkey *device) 370{ 371 int status = 0; 372 struct acpi_device *dev = NULL; 373 374 375 if (device->link.hotkey_type == ACPI_HOTKEY_EVENT) { 376 acpi_bus_get_device(device->event_hotkey.bus_handle, &dev); 377 status = acpi_install_notify_handler(dev->handle, 378 ACPI_DEVICE_NOTIFY, 379 acpi_hotkey_notify_handler, 380 dev); 381 } else /* Add polling hotkey */ 382 create_polling_proc(device); 383 384 global_hotkey_list.count++; 385 386 list_add_tail(&device->link.entries, global_hotkey_list.entries); 387 388 return status; 389} 390 391static int hotkey_remove(union acpi_hotkey *device) 392{ 393 struct list_head *entries, *next; 394 395 396 list_for_each_safe(entries, next, global_hotkey_list.entries) { 397 union acpi_hotkey *key = 398 container_of(entries, union acpi_hotkey, entries); 399 if (key->link.hotkey_standard_num == 400 device->link.hotkey_standard_num) { 401 list_del(&key->link.entries); 402 free_hotkey_device(key); 403 global_hotkey_list.count--; 404 break; 405 } 406 } 407 kfree(device); 408 return 0; 409} 410 411static int hotkey_update(union acpi_hotkey *key) 412{ 413 struct list_head *entries; 414 415 416 list_for_each(entries, global_hotkey_list.entries) { 417 union acpi_hotkey *tmp = 418 container_of(entries, union acpi_hotkey, entries); 419 if (tmp->link.hotkey_standard_num == 420 key->link.hotkey_standard_num) { 421 if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) { 422 free_hotkey_buffer(tmp); 423 tmp->event_hotkey.bus_handle = 424 key->event_hotkey.bus_handle; 425 tmp->event_hotkey.external_hotkey_num = 426 key->event_hotkey.external_hotkey_num; 427 tmp->event_hotkey.action_handle = 428 key->event_hotkey.action_handle; 429 tmp->event_hotkey.action_method = 430 key->event_hotkey.action_method; 431 kfree(key); 432 } else { 433 /* 434 char proc_name[80]; 435 436 sprintf(proc_name, "%d", tmp->link.hotkey_standard_num); 437 strcat(proc_name, tmp->poll_hotkey.poll_method); 438 remove_proc_entry(proc_name,hotkey_proc_dir); 439 */ 440 free_poll_hotkey_buffer(tmp); 441 tmp->poll_hotkey.poll_handle = 442 key->poll_hotkey.poll_handle; 443 tmp->poll_hotkey.poll_method = 444 key->poll_hotkey.poll_method; 445 tmp->poll_hotkey.action_handle = 446 key->poll_hotkey.action_handle; 447 tmp->poll_hotkey.action_method = 448 key->poll_hotkey.action_method; 449 tmp->poll_hotkey.poll_result = 450 key->poll_hotkey.poll_result; 451 /* 452 create_polling_proc(tmp); 453 */ 454 kfree(key); 455 } 456 return 0; 457 break; 458 } 459 } 460 461 return -ENODEV; 462} 463 464static void free_hotkey_device(union acpi_hotkey *key) 465{ 466 struct acpi_device *dev; 467 468 469 if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) { 470 acpi_bus_get_device(key->event_hotkey.bus_handle, &dev); 471 if (dev->handle) 472 acpi_remove_notify_handler(dev->handle, 473 ACPI_DEVICE_NOTIFY, 474 acpi_hotkey_notify_handler); 475 free_hotkey_buffer(key); 476 } else { 477 char proc_name[80]; 478 479 sprintf(proc_name, "%d", key->link.hotkey_standard_num); 480 /* 481 strcat(proc_name, key->poll_hotkey.poll_method); 482 */ 483 remove_proc_entry(proc_name, hotkey_proc_dir); 484 free_poll_hotkey_buffer(key); 485 } 486 kfree(key); 487 return; 488} 489 490static void free_hotkey_buffer(union acpi_hotkey *key) 491{ 492 /* key would never be null, action method could be */ 493 kfree(key->event_hotkey.action_method); 494} 495 496static void free_poll_hotkey_buffer(union acpi_hotkey *key) 497{ 498 /* key would never be null, others could be*/ 499 kfree(key->poll_hotkey.action_method); 500 kfree(key->poll_hotkey.poll_method); 501 kfree(key->poll_hotkey.poll_result); 502} 503static int 504init_hotkey_device(union acpi_hotkey *key, char **config_entry, 505 int std_num, int external_num) 506{ 507 acpi_handle tmp_handle; 508 acpi_status status = AE_OK; 509 510 if (std_num < 0 || IS_POLL(std_num) || !key) 511 goto do_fail; 512 513 if (!config_entry[bus_handle] || !config_entry[action_handle] 514 || !config_entry[method]) 515 goto do_fail; 516 517 key->link.hotkey_type = ACPI_HOTKEY_EVENT; 518 key->link.hotkey_standard_num = std_num; 519 key->event_hotkey.flag = 0; 520 key->event_hotkey.action_method = config_entry[method]; 521 522 status = acpi_get_handle(NULL, config_entry[bus_handle], 523 &(key->event_hotkey.bus_handle)); 524 if (ACPI_FAILURE(status)) 525 goto do_fail_zero; 526 key->event_hotkey.external_hotkey_num = external_num; 527 status = acpi_get_handle(NULL, config_entry[action_handle], 528 &(key->event_hotkey.action_handle)); 529 if (ACPI_FAILURE(status)) 530 goto do_fail_zero; 531 status = acpi_get_handle(key->event_hotkey.action_handle, 532 config_entry[method], &tmp_handle); 533 if (ACPI_FAILURE(status)) 534 goto do_fail_zero; 535 return AE_OK; 536do_fail_zero: 537 key->event_hotkey.action_method = NULL; 538do_fail: 539 return -ENODEV; 540} 541 542static int 543init_poll_hotkey_device(union acpi_hotkey *key, char **config_entry, 544 int std_num) 545{ 546 acpi_status status = AE_OK; 547 acpi_handle tmp_handle; 548 549 if (std_num < 0 || IS_EVENT(std_num) || !key) 550 goto do_fail; 551 if (!config_entry[bus_handle] ||!config_entry[bus_method] || 552 !config_entry[action_handle] || !config_entry[method]) 553 goto do_fail; 554 555 key->link.hotkey_type = ACPI_HOTKEY_POLLING; 556 key->link.hotkey_standard_num = std_num; 557 key->poll_hotkey.flag = 0; 558 key->poll_hotkey.poll_method = config_entry[bus_method]; 559 key->poll_hotkey.action_method = config_entry[method]; 560 561 status = acpi_get_handle(NULL, config_entry[bus_handle], 562 &(key->poll_hotkey.poll_handle)); 563 if (ACPI_FAILURE(status)) 564 goto do_fail_zero; 565 status = acpi_get_handle(key->poll_hotkey.poll_handle, 566 config_entry[bus_method], &tmp_handle); 567 if (ACPI_FAILURE(status)) 568 goto do_fail_zero; 569 status = 570 acpi_get_handle(NULL, config_entry[action_handle], 571 &(key->poll_hotkey.action_handle)); 572 if (ACPI_FAILURE(status)) 573 goto do_fail_zero; 574 status = acpi_get_handle(key->poll_hotkey.action_handle, 575 config_entry[method], &tmp_handle); 576 if (ACPI_FAILURE(status)) 577 goto do_fail_zero; 578 key->poll_hotkey.poll_result = 579 kmalloc(sizeof(union acpi_object), GFP_KERNEL); 580 if (!key->poll_hotkey.poll_result) 581 goto do_fail_zero; 582 return AE_OK; 583 584do_fail_zero: 585 key->poll_hotkey.poll_method = NULL; 586 key->poll_hotkey.action_method = NULL; 587do_fail: 588 return -ENODEV; 589} 590 591static int hotkey_open_config(struct inode *inode, struct file *file) 592{ 593 return (single_open 594 (file, hotkey_config_seq_show, PDE(inode)->data)); 595} 596 597static int hotkey_poll_open_config(struct inode *inode, struct file *file) 598{ 599 return (single_open 600 (file, hotkey_poll_config_seq_show, PDE(inode)->data)); 601} 602 603static int hotkey_config_seq_show(struct seq_file *seq, void *offset) 604{ 605 struct acpi_hotkey_list *hotkey_list = &global_hotkey_list; 606 struct list_head *entries; 607 char bus_name[ACPI_PATHNAME_MAX] = { 0 }; 608 char action_name[ACPI_PATHNAME_MAX] = { 0 }; 609 struct acpi_buffer bus = { ACPI_PATHNAME_MAX, bus_name }; 610 struct acpi_buffer act = { ACPI_PATHNAME_MAX, action_name }; 611 612 613 list_for_each(entries, hotkey_list->entries) { 614 union acpi_hotkey *key = 615 container_of(entries, union acpi_hotkey, entries); 616 if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) { 617 acpi_get_name(key->event_hotkey.bus_handle, 618 ACPI_NAME_TYPE_MAX, &bus); 619 acpi_get_name(key->event_hotkey.action_handle, 620 ACPI_NAME_TYPE_MAX, &act); 621 seq_printf(seq, "%s:%s:%s:%d:%d\n", bus_name, 622 action_name, 623 key->event_hotkey.action_method, 624 key->link.hotkey_standard_num, 625 key->event_hotkey.external_hotkey_num); 626 } 627 } 628 seq_puts(seq, "\n"); 629 return 0; 630} 631 632static int hotkey_poll_config_seq_show(struct seq_file *seq, void *offset) 633{ 634 struct acpi_hotkey_list *hotkey_list = &global_hotkey_list; 635 struct list_head *entries; 636 char bus_name[ACPI_PATHNAME_MAX] = { 0 }; 637 char action_name[ACPI_PATHNAME_MAX] = { 0 }; 638 struct acpi_buffer bus = { ACPI_PATHNAME_MAX, bus_name }; 639 struct acpi_buffer act = { ACPI_PATHNAME_MAX, action_name }; 640 641 642 list_for_each(entries, hotkey_list->entries) { 643 union acpi_hotkey *key = 644 container_of(entries, union acpi_hotkey, entries); 645 if (key->link.hotkey_type == ACPI_HOTKEY_POLLING) { 646 acpi_get_name(key->poll_hotkey.poll_handle, 647 ACPI_NAME_TYPE_MAX, &bus); 648 acpi_get_name(key->poll_hotkey.action_handle, 649 ACPI_NAME_TYPE_MAX, &act); 650 seq_printf(seq, "%s:%s:%s:%s:%d\n", bus_name, 651 key->poll_hotkey.poll_method, 652 action_name, 653 key->poll_hotkey.action_method, 654 key->link.hotkey_standard_num); 655 } 656 } 657 seq_puts(seq, "\n"); 658 return 0; 659} 660 661static int 662get_parms(char *config_record, int *cmd, char **config_entry, 663 int *internal_event_num, int *external_event_num) 664{ 665/* the format of *config_record = 666 * "1:\d+:*" : "cmd:internal_event_num" 667 * "\d+:\w+:\w+:\w+:\w+:\d+:\d+" : 668 * "cmd:bus_handle:bus_method:action_handle:method:internal_event_num:external_event_num" 669 */ 670 char *tmp, *tmp1, count; 671 int i; 672 673 sscanf(config_record, "%d", cmd); 674 if (*cmd == 1) { 675 if (sscanf(config_record, "%d:%d", cmd, internal_event_num) != 676 2) 677 goto do_fail; 678 else 679 return (6); 680 } 681 tmp = strchr(config_record, ':'); 682 if (!tmp) 683 goto do_fail; 684 tmp++; 685 for (i = 0; i < LAST_CONF_ENTRY; i++) { 686 tmp1 = strchr(tmp, ':'); 687 if (!tmp1) { 688 goto do_fail; 689 } 690 count = tmp1 - tmp; 691 config_entry[i] = kzalloc(count + 1, GFP_KERNEL); 692 if (!config_entry[i]) 693 goto handle_failure; 694 strncpy(config_entry[i], tmp, count); 695 tmp = tmp1 + 1; 696 } 697 if (sscanf(tmp, "%d:%d", internal_event_num, external_event_num) <= 0) 698 goto handle_failure; 699 if (!IS_OTHERS(*internal_event_num)) { 700 return 6; 701 } 702handle_failure: 703 while (i-- > 0) 704 kfree(config_entry[i]); 705do_fail: 706 return -1; 707} 708 709/* count is length for one input record */ 710static ssize_t hotkey_write_config(struct file *file, 711 const char __user * buffer, 712 size_t count, loff_t * data) 713{ 714 char *config_record = NULL; 715 char *config_entry[LAST_CONF_ENTRY]; 716 int cmd, internal_event_num, external_event_num; 717 int ret = 0; 718 union acpi_hotkey *key = kzalloc(sizeof(union acpi_hotkey), GFP_KERNEL); 719 720 if (!key) 721 return -ENOMEM; 722 723 config_record = kzalloc(count + 1, GFP_KERNEL); 724 if (!config_record) { 725 kfree(key); 726 return -ENOMEM; 727 } 728 729 if (copy_from_user(config_record, buffer, count)) { 730 kfree(config_record); 731 kfree(key); 732 printk(KERN_ERR PREFIX "Invalid data\n"); 733 return -EINVAL; 734 } 735 ret = get_parms(config_record, &cmd, config_entry, 736 &internal_event_num, &external_event_num); 737 kfree(config_record); 738 if (ret != 6) { 739 printk(KERN_ERR PREFIX "Invalid data format ret=%d\n", ret); 740 return -EINVAL; 741 } 742 743 if (cmd == 1) { 744 union acpi_hotkey *tmp = NULL; 745 tmp = get_hotkey_by_event(&global_hotkey_list, 746 internal_event_num); 747 if (!tmp) 748 printk(KERN_ERR PREFIX "Invalid key\n"); 749 else 750 memcpy(key, tmp, sizeof(union acpi_hotkey)); 751 goto cont_cmd; 752 } 753 if (IS_EVENT(internal_event_num)) { 754 if (init_hotkey_device(key, config_entry, 755 internal_event_num, external_event_num)) 756 goto init_hotkey_fail; 757 } else { 758 if (init_poll_hotkey_device(key, config_entry, 759 internal_event_num)) 760 goto init_poll_hotkey_fail; 761 } 762cont_cmd: 763 switch (cmd) { 764 case 0: 765 if (get_hotkey_by_event(&global_hotkey_list, 766 key->link.hotkey_standard_num)) 767 goto fail_out; 768 else 769 hotkey_add(key); 770 break; 771 case 1: 772 hotkey_remove(key); 773 break; 774 case 2: 775 /* key is kfree()ed if matched*/ 776 if (hotkey_update(key)) 777 goto fail_out; 778 break; 779 default: 780 goto fail_out; 781 break; 782 } 783 return count; 784 785init_poll_hotkey_fail: /* failed init_poll_hotkey_device */ 786 kfree(config_entry[bus_method]); 787 config_entry[bus_method] = NULL; 788init_hotkey_fail: /* failed init_hotkey_device */ 789 kfree(config_entry[method]); 790fail_out: 791 kfree(config_entry[bus_handle]); 792 kfree(config_entry[action_handle]); 793 /* No double free since elements =NULL for error cases */ 794 if (IS_EVENT(internal_event_num)) { 795 if (config_entry[bus_method]) 796 kfree(config_entry[bus_method]); 797 free_hotkey_buffer(key); /* frees [method] */ 798 } else 799 free_poll_hotkey_buffer(key); /* frees [bus_method]+[method] */ 800 kfree(key); 801 printk(KERN_ERR PREFIX "invalid key\n"); 802 return -EINVAL; 803} 804 805/* 806 * This function evaluates an ACPI method, given an int as parameter, the 807 * method is searched within the scope of the handle, can be NULL. The output 808 * of the method is written is output, which can also be NULL 809 * 810 * returns 1 if write is successful, 0 else. 811 */ 812static int write_acpi_int(acpi_handle handle, const char *method, int val, 813 struct acpi_buffer *output) 814{ 815 struct acpi_object_list params; /* list of input parameters (an int here) */ 816 union acpi_object in_obj; /* the only param we use */ 817 acpi_status status; 818 819 params.count = 1; 820 params.pointer = &in_obj; 821 in_obj.type = ACPI_TYPE_INTEGER; 822 in_obj.integer.value = val; 823 824 status = acpi_evaluate_object(handle, (char *)method, &params, output); 825 826 return (status == AE_OK); 827} 828 829static int read_acpi_int(acpi_handle handle, const char *method, 830 union acpi_object *val) 831{ 832 struct acpi_buffer output; 833 union acpi_object out_obj; 834 acpi_status status; 835 836 output.length = sizeof(out_obj); 837 output.pointer = &out_obj; 838 839 status = acpi_evaluate_object(handle, (char *)method, NULL, &output); 840 if (val) { 841 val->integer.value = out_obj.integer.value; 842 val->type = out_obj.type; 843 } else 844 printk(KERN_ERR PREFIX "null val pointer\n"); 845 return ((status == AE_OK) 846 && (out_obj.type == ACPI_TYPE_INTEGER)); 847} 848 849static union acpi_hotkey *get_hotkey_by_event(struct 850 acpi_hotkey_list 851 *hotkey_list, int event) 852{ 853 struct list_head *entries; 854 855 list_for_each(entries, hotkey_list->entries) { 856 union acpi_hotkey *key = 857 container_of(entries, union acpi_hotkey, entries); 858 if (key->link.hotkey_standard_num == event) { 859 return (key); 860 } 861 } 862 return (NULL); 863} 864 865/* 866 * user call AML method interface: 867 * Call convention: 868 * echo "event_num: arg type : value" 869 * example: echo "1:1:30" > /proc/acpi/action 870 * Just support 1 integer arg passing to AML method 871 */ 872 873static ssize_t hotkey_execute_aml_method(struct file *file, 874 const char __user * buffer, 875 size_t count, loff_t * data) 876{ 877 struct acpi_hotkey_list *hotkey_list = &global_hotkey_list; 878 char *arg; 879 int event, method_type, type, value; 880 union acpi_hotkey *key; 881 882 883 arg = kzalloc(count + 1, GFP_KERNEL); 884 if (!arg) 885 return -ENOMEM; 886 887 if (copy_from_user(arg, buffer, count)) { 888 kfree(arg); 889 printk(KERN_ERR PREFIX "Invalid argument 2\n"); 890 return -EINVAL; 891 } 892 893 if (sscanf(arg, "%d:%d:%d:%d", &event, &method_type, &type, &value) != 894 4) { 895 kfree(arg); 896 printk(KERN_ERR PREFIX "Invalid argument 3\n"); 897 return -EINVAL; 898 } 899 kfree(arg); 900 if (type == ACPI_TYPE_INTEGER) { 901 key = get_hotkey_by_event(hotkey_list, event); 902 if (!key) 903 goto do_fail; 904 if (IS_EVENT(event)) 905 write_acpi_int(key->event_hotkey.action_handle, 906 key->event_hotkey.action_method, value, 907 NULL); 908 else if (IS_POLL(event)) { 909 if (method_type == POLL_METHOD) 910 read_acpi_int(key->poll_hotkey.poll_handle, 911 key->poll_hotkey.poll_method, 912 key->poll_hotkey.poll_result); 913 else if (method_type == ACTION_METHOD) 914 write_acpi_int(key->poll_hotkey.action_handle, 915 key->poll_hotkey.action_method, 916 value, NULL); 917 else 918 goto do_fail; 919 920 } 921 } else { 922 printk(KERN_WARNING "Not supported\n"); 923 return -EINVAL; 924 } 925 return count; 926 do_fail: 927 return -EINVAL; 928 929} 930 931static int __init hotkey_init(void) 932{ 933 int result; 934 mode_t mode = S_IFREG | S_IRUGO | S_IWUGO; 935 936 937 if (acpi_disabled) 938 return -ENODEV; 939 940 if (acpi_specific_hotkey_enabled) { 941 printk("Using specific hotkey driver\n"); 942 return -ENODEV; 943 } 944 945 hotkey_proc_dir = proc_mkdir(HOTKEY_PROC, acpi_root_dir); 946 if (!hotkey_proc_dir) { 947 return (-ENODEV); 948 } 949 hotkey_proc_dir->owner = THIS_MODULE; 950 951 hotkey_config = 952 create_proc_entry(HOTKEY_EV_CONFIG, mode, hotkey_proc_dir); 953 if (!hotkey_config) { 954 goto do_fail1; 955 } else { 956 hotkey_config->proc_fops = &hotkey_config_fops; 957 hotkey_config->data = &global_hotkey_list; 958 hotkey_config->owner = THIS_MODULE; 959 hotkey_config->uid = 0; 960 hotkey_config->gid = 0; 961 } 962 963 hotkey_poll_config = 964 create_proc_entry(HOTKEY_PL_CONFIG, mode, hotkey_proc_dir); 965 if (!hotkey_poll_config) { 966 goto do_fail2; 967 } else { 968 hotkey_poll_config->proc_fops = &hotkey_poll_config_fops; 969 hotkey_poll_config->data = &global_hotkey_list; 970 hotkey_poll_config->owner = THIS_MODULE; 971 hotkey_poll_config->uid = 0; 972 hotkey_poll_config->gid = 0; 973 } 974 975 hotkey_action = create_proc_entry(HOTKEY_ACTION, mode, hotkey_proc_dir); 976 if (!hotkey_action) { 977 goto do_fail3; 978 } else { 979 hotkey_action->proc_fops = &hotkey_action_fops; 980 hotkey_action->owner = THIS_MODULE; 981 hotkey_action->uid = 0; 982 hotkey_action->gid = 0; 983 } 984 985 hotkey_info = create_proc_entry(HOTKEY_INFO, mode, hotkey_proc_dir); 986 if (!hotkey_info) { 987 goto do_fail4; 988 } else { 989 hotkey_info->proc_fops = &hotkey_info_fops; 990 hotkey_info->owner = THIS_MODULE; 991 hotkey_info->uid = 0; 992 hotkey_info->gid = 0; 993 } 994 995 result = acpi_bus_register_driver(&hotkey_driver); 996 if (result < 0) 997 goto do_fail5; 998 global_hotkey_list.count = 0; 999 global_hotkey_list.entries = &hotkey_entries; 1000 1001 INIT_LIST_HEAD(&hotkey_entries); 1002 1003 return (0); 1004 1005 do_fail5: 1006 remove_proc_entry(HOTKEY_INFO, hotkey_proc_dir); 1007 do_fail4: 1008 remove_proc_entry(HOTKEY_ACTION, hotkey_proc_dir); 1009 do_fail3: 1010 remove_proc_entry(HOTKEY_PL_CONFIG, hotkey_proc_dir); 1011 do_fail2: 1012 remove_proc_entry(HOTKEY_EV_CONFIG, hotkey_proc_dir); 1013 do_fail1: 1014 remove_proc_entry(HOTKEY_PROC, acpi_root_dir); 1015 return (-ENODEV); 1016} 1017 1018static void __exit hotkey_exit(void) 1019{ 1020 struct list_head *entries, *next; 1021 1022 1023 list_for_each_safe(entries, next, global_hotkey_list.entries) { 1024 union acpi_hotkey *key = 1025 container_of(entries, union acpi_hotkey, entries); 1026 1027 acpi_os_wait_events_complete(NULL); 1028 list_del(&key->link.entries); 1029 global_hotkey_list.count--; 1030 free_hotkey_device(key); 1031 } 1032 acpi_bus_unregister_driver(&hotkey_driver); 1033 remove_proc_entry(HOTKEY_EV_CONFIG, hotkey_proc_dir); 1034 remove_proc_entry(HOTKEY_PL_CONFIG, hotkey_proc_dir); 1035 remove_proc_entry(HOTKEY_ACTION, hotkey_proc_dir); 1036 remove_proc_entry(HOTKEY_INFO, hotkey_proc_dir); 1037 remove_proc_entry(HOTKEY_PROC, acpi_root_dir); 1038 return; 1039} 1040 1041module_init(hotkey_init); 1042module_exit(hotkey_exit);