1 /* 2 * Copyright (C) 2009 Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License along 15 * with this program; if not, write to the Free Software Foundation, Inc., 16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 */ 18 19 20 #include <linux/init.h> 21 #include <linux/module.h> 22 #include <linux/slab.h> 23 #include <linux/workqueue.h> 24 #include <acpi/acpi_drivers.h> 25 #include <linux/backlight.h> 26 #include <linux/input.h> 27 #include <linux/rfkill.h> 28 29 MODULE_LICENSE("GPL"); 30 31 32 struct cmpc_accel { 33 int sensitivity; 34 }; 35 36 #define CMPC_ACCEL_SENSITIVITY_DEFAULT 5 37 38 39 #define CMPC_ACCEL_HID "ACCE0000" 40 #define CMPC_TABLET_HID "TBLT0000" 41 #define CMPC_IPML_HID "IPML200" 42 #define CMPC_KEYS_HID "FnBT0000" 43 44 /* 45 * Generic input device code. 46 */ 47 48 typedef void (*input_device_init)(struct input_dev *dev); 49 50 static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name, 51 input_device_init idev_init) 52 { 53 struct input_dev *inputdev; 54 int error; 55 56 inputdev = input_allocate_device(); 57 if (!inputdev) 58 return -ENOMEM; 59 inputdev->name = name; 60 inputdev->dev.parent = &acpi->dev; 61 idev_init(inputdev); 62 error = input_register_device(inputdev); 63 if (error) { 64 input_free_device(inputdev); 65 return error; 66 } 67 dev_set_drvdata(&acpi->dev, inputdev); 68 return 0; 69 } 70 71 static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi) 72 { 73 struct input_dev *inputdev = dev_get_drvdata(&acpi->dev); 74 input_unregister_device(inputdev); 75 return 0; 76 } 77 78 /* 79 * Accelerometer code. 80 */ 81 static acpi_status cmpc_start_accel(acpi_handle handle) 82 { 83 union acpi_object param[2]; 84 struct acpi_object_list input; 85 acpi_status status; 86 87 param[0].type = ACPI_TYPE_INTEGER; 88 param[0].integer.value = 0x3; 89 param[1].type = ACPI_TYPE_INTEGER; 90 input.count = 2; 91 input.pointer = param; 92 status = acpi_evaluate_object(handle, "ACMD", &input, NULL); 93 return status; 94 } 95 96 static acpi_status cmpc_stop_accel(acpi_handle handle) 97 { 98 union acpi_object param[2]; 99 struct acpi_object_list input; 100 acpi_status status; 101 102 param[0].type = ACPI_TYPE_INTEGER; 103 param[0].integer.value = 0x4; 104 param[1].type = ACPI_TYPE_INTEGER; 105 input.count = 2; 106 input.pointer = param; 107 status = acpi_evaluate_object(handle, "ACMD", &input, NULL); 108 return status; 109 } 110 111 static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val) 112 { 113 union acpi_object param[2]; 114 struct acpi_object_list input; 115 116 param[0].type = ACPI_TYPE_INTEGER; 117 param[0].integer.value = 0x02; 118 param[1].type = ACPI_TYPE_INTEGER; 119 param[1].integer.value = val; 120 input.count = 2; 121 input.pointer = param; 122 return acpi_evaluate_object(handle, "ACMD", &input, NULL); 123 } 124 125 static acpi_status cmpc_get_accel(acpi_handle handle, 126 unsigned char *x, 127 unsigned char *y, 128 unsigned char *z) 129 { 130 union acpi_object param[2]; 131 struct acpi_object_list input; 132 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, 0 }; 133 unsigned char *locs; 134 acpi_status status; 135 136 param[0].type = ACPI_TYPE_INTEGER; 137 param[0].integer.value = 0x01; 138 param[1].type = ACPI_TYPE_INTEGER; 139 input.count = 2; 140 input.pointer = param; 141 status = acpi_evaluate_object(handle, "ACMD", &input, &output); 142 if (ACPI_SUCCESS(status)) { 143 union acpi_object *obj; 144 obj = output.pointer; 145 locs = obj->buffer.pointer; 146 *x = locs[0]; 147 *y = locs[1]; 148 *z = locs[2]; 149 kfree(output.pointer); 150 } 151 return status; 152 } 153 154 static void cmpc_accel_handler(struct acpi_device *dev, u32 event) 155 { 156 if (event == 0x81) { 157 unsigned char x, y, z; 158 acpi_status status; 159 160 status = cmpc_get_accel(dev->handle, &x, &y, &z); 161 if (ACPI_SUCCESS(status)) { 162 struct input_dev *inputdev = dev_get_drvdata(&dev->dev); 163 164 input_report_abs(inputdev, ABS_X, x); 165 input_report_abs(inputdev, ABS_Y, y); 166 input_report_abs(inputdev, ABS_Z, z); 167 input_sync(inputdev); 168 } 169 } 170 } 171 172 static ssize_t cmpc_accel_sensitivity_show(struct device *dev, 173 struct device_attribute *attr, 174 char *buf) 175 { 176 struct acpi_device *acpi; 177 struct input_dev *inputdev; 178 struct cmpc_accel *accel; 179 180 acpi = to_acpi_device(dev); 181 inputdev = dev_get_drvdata(&acpi->dev); 182 accel = dev_get_drvdata(&inputdev->dev); 183 184 return sprintf(buf, "%d\n", accel->sensitivity); 185 } 186 187 static ssize_t cmpc_accel_sensitivity_store(struct device *dev, 188 struct device_attribute *attr, 189 const char *buf, size_t count) 190 { 191 struct acpi_device *acpi; 192 struct input_dev *inputdev; 193 struct cmpc_accel *accel; 194 unsigned long sensitivity; 195 int r; 196 197 acpi = to_acpi_device(dev); 198 inputdev = dev_get_drvdata(&acpi->dev); 199 accel = dev_get_drvdata(&inputdev->dev); 200 201 r = strict_strtoul(buf, 0, &sensitivity); 202 if (r) 203 return r; 204 205 accel->sensitivity = sensitivity; 206 cmpc_accel_set_sensitivity(acpi->handle, sensitivity); 207 208 return strnlen(buf, count); 209 } 210 211 static struct device_attribute cmpc_accel_sensitivity_attr = { 212 .attr = { .name = "sensitivity", .mode = 0660 }, 213 .show = cmpc_accel_sensitivity_show, 214 .store = cmpc_accel_sensitivity_store 215 }; 216 217 static int cmpc_accel_open(struct input_dev *input) 218 { 219 struct acpi_device *acpi; 220 221 acpi = to_acpi_device(input->dev.parent); 222 if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle))) 223 return 0; 224 return -EIO; 225 } 226 227 static void cmpc_accel_close(struct input_dev *input) 228 { 229 struct acpi_device *acpi; 230 231 acpi = to_acpi_device(input->dev.parent); 232 cmpc_stop_accel(acpi->handle); 233 } 234 235 static void cmpc_accel_idev_init(struct input_dev *inputdev) 236 { 237 set_bit(EV_ABS, inputdev->evbit); 238 input_set_abs_params(inputdev, ABS_X, 0, 255, 8, 0); 239 input_set_abs_params(inputdev, ABS_Y, 0, 255, 8, 0); 240 input_set_abs_params(inputdev, ABS_Z, 0, 255, 8, 0); 241 inputdev->open = cmpc_accel_open; 242 inputdev->close = cmpc_accel_close; 243 } 244 245 static int cmpc_accel_add(struct acpi_device *acpi) 246 { 247 int error; 248 struct input_dev *inputdev; 249 struct cmpc_accel *accel; 250 251 accel = kmalloc(sizeof(*accel), GFP_KERNEL); 252 if (!accel) 253 return -ENOMEM; 254 255 accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT; 256 cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity); 257 258 error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr); 259 if (error) 260 goto failed_file; 261 262 error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel", 263 cmpc_accel_idev_init); 264 if (error) 265 goto failed_input; 266 267 inputdev = dev_get_drvdata(&acpi->dev); 268 dev_set_drvdata(&inputdev->dev, accel); 269 270 return 0; 271 272 failed_input: 273 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); 274 failed_file: 275 kfree(accel); 276 return error; 277 } 278 279 static int cmpc_accel_remove(struct acpi_device *acpi, int type) 280 { 281 struct input_dev *inputdev; 282 struct cmpc_accel *accel; 283 284 inputdev = dev_get_drvdata(&acpi->dev); 285 accel = dev_get_drvdata(&inputdev->dev); 286 287 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); 288 return cmpc_remove_acpi_notify_device(acpi); 289 } 290 291 static const struct acpi_device_id cmpc_accel_device_ids[] = { 292 {CMPC_ACCEL_HID, 0}, 293 {"", 0} 294 }; 295 296 static struct acpi_driver cmpc_accel_acpi_driver = { 297 .owner = THIS_MODULE, 298 .name = "cmpc_accel", 299 .class = "cmpc_accel", 300 .ids = cmpc_accel_device_ids, 301 .ops = { 302 .add = cmpc_accel_add, 303 .remove = cmpc_accel_remove, 304 .notify = cmpc_accel_handler, 305 } 306 }; 307 308 309 /* 310 * Tablet mode code. 311 */ 312 static acpi_status cmpc_get_tablet(acpi_handle handle, 313 unsigned long long *value) 314 { 315 union acpi_object param; 316 struct acpi_object_list input; 317 unsigned long long output; 318 acpi_status status; 319 320 param.type = ACPI_TYPE_INTEGER; 321 param.integer.value = 0x01; 322 input.count = 1; 323 input.pointer = ¶m; 324 status = acpi_evaluate_integer(handle, "TCMD", &input, &output); 325 if (ACPI_SUCCESS(status)) 326 *value = output; 327 return status; 328 } 329 330 static void cmpc_tablet_handler(struct acpi_device *dev, u32 event) 331 { 332 unsigned long long val = 0; 333 struct input_dev *inputdev = dev_get_drvdata(&dev->dev); 334 335 if (event == 0x81) { 336 if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) 337 input_report_switch(inputdev, SW_TABLET_MODE, !val); 338 } 339 } 340 341 static void cmpc_tablet_idev_init(struct input_dev *inputdev) 342 { 343 unsigned long long val = 0; 344 struct acpi_device *acpi; 345 346 set_bit(EV_SW, inputdev->evbit); 347 set_bit(SW_TABLET_MODE, inputdev->swbit); 348 349 acpi = to_acpi_device(inputdev->dev.parent); 350 if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) 351 input_report_switch(inputdev, SW_TABLET_MODE, !val); 352 } 353 354 static int cmpc_tablet_add(struct acpi_device *acpi) 355 { 356 return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet", 357 cmpc_tablet_idev_init); 358 } 359 360 static int cmpc_tablet_remove(struct acpi_device *acpi, int type) 361 { 362 return cmpc_remove_acpi_notify_device(acpi); 363 } 364 365 static int cmpc_tablet_resume(struct device *dev) 366 { 367 struct input_dev *inputdev = dev_get_drvdata(dev); 368 369 unsigned long long val = 0; 370 if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val))) 371 input_report_switch(inputdev, SW_TABLET_MODE, !val); 372 return 0; 373 } 374 375 static SIMPLE_DEV_PM_OPS(cmpc_tablet_pm, NULL, cmpc_tablet_resume); 376 377 static const struct acpi_device_id cmpc_tablet_device_ids[] = { 378 {CMPC_TABLET_HID, 0}, 379 {"", 0} 380 }; 381 382 static struct acpi_driver cmpc_tablet_acpi_driver = { 383 .owner = THIS_MODULE, 384 .name = "cmpc_tablet", 385 .class = "cmpc_tablet", 386 .ids = cmpc_tablet_device_ids, 387 .ops = { 388 .add = cmpc_tablet_add, 389 .remove = cmpc_tablet_remove, 390 .notify = cmpc_tablet_handler, 391 }, 392 .drv.pm = &cmpc_tablet_pm, 393 }; 394 395 396 /* 397 * Backlight code. 398 */ 399 400 static acpi_status cmpc_get_brightness(acpi_handle handle, 401 unsigned long long *value) 402 { 403 union acpi_object param; 404 struct acpi_object_list input; 405 unsigned long long output; 406 acpi_status status; 407 408 param.type = ACPI_TYPE_INTEGER; 409 param.integer.value = 0xC0; 410 input.count = 1; 411 input.pointer = ¶m; 412 status = acpi_evaluate_integer(handle, "GRDI", &input, &output); 413 if (ACPI_SUCCESS(status)) 414 *value = output; 415 return status; 416 } 417 418 static acpi_status cmpc_set_brightness(acpi_handle handle, 419 unsigned long long value) 420 { 421 union acpi_object param[2]; 422 struct acpi_object_list input; 423 acpi_status status; 424 unsigned long long output; 425 426 param[0].type = ACPI_TYPE_INTEGER; 427 param[0].integer.value = 0xC0; 428 param[1].type = ACPI_TYPE_INTEGER; 429 param[1].integer.value = value; 430 input.count = 2; 431 input.pointer = param; 432 status = acpi_evaluate_integer(handle, "GWRI", &input, &output); 433 return status; 434 } 435 436 static int cmpc_bl_get_brightness(struct backlight_device *bd) 437 { 438 acpi_status status; 439 acpi_handle handle; 440 unsigned long long brightness; 441 442 handle = bl_get_data(bd); 443 status = cmpc_get_brightness(handle, &brightness); 444 if (ACPI_SUCCESS(status)) 445 return brightness; 446 else 447 return -1; 448 } 449 450 static int cmpc_bl_update_status(struct backlight_device *bd) 451 { 452 acpi_status status; 453 acpi_handle handle; 454 455 handle = bl_get_data(bd); 456 status = cmpc_set_brightness(handle, bd->props.brightness); 457 if (ACPI_SUCCESS(status)) 458 return 0; 459 else 460 return -1; 461 } 462 463 static const struct backlight_ops cmpc_bl_ops = { 464 .get_brightness = cmpc_bl_get_brightness, 465 .update_status = cmpc_bl_update_status 466 }; 467 468 /* 469 * RFKILL code. 470 */ 471 472 static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle, 473 unsigned long long *value) 474 { 475 union acpi_object param; 476 struct acpi_object_list input; 477 unsigned long long output; 478 acpi_status status; 479 480 param.type = ACPI_TYPE_INTEGER; 481 param.integer.value = 0xC1; 482 input.count = 1; 483 input.pointer = ¶m; 484 status = acpi_evaluate_integer(handle, "GRDI", &input, &output); 485 if (ACPI_SUCCESS(status)) 486 *value = output; 487 return status; 488 } 489 490 static acpi_status cmpc_set_rfkill_wlan(acpi_handle handle, 491 unsigned long long value) 492 { 493 union acpi_object param[2]; 494 struct acpi_object_list input; 495 acpi_status status; 496 unsigned long long output; 497 498 param[0].type = ACPI_TYPE_INTEGER; 499 param[0].integer.value = 0xC1; 500 param[1].type = ACPI_TYPE_INTEGER; 501 param[1].integer.value = value; 502 input.count = 2; 503 input.pointer = param; 504 status = acpi_evaluate_integer(handle, "GWRI", &input, &output); 505 return status; 506 } 507 508 static void cmpc_rfkill_query(struct rfkill *rfkill, void *data) 509 { 510 acpi_status status; 511 acpi_handle handle; 512 unsigned long long state; 513 bool blocked; 514 515 handle = data; 516 status = cmpc_get_rfkill_wlan(handle, &state); 517 if (ACPI_SUCCESS(status)) { 518 blocked = state & 1 ? false : true; 519 rfkill_set_sw_state(rfkill, blocked); 520 } 521 } 522 523 static int cmpc_rfkill_block(void *data, bool blocked) 524 { 525 acpi_status status; 526 acpi_handle handle; 527 unsigned long long state; 528 bool is_blocked; 529 530 handle = data; 531 status = cmpc_get_rfkill_wlan(handle, &state); 532 if (ACPI_FAILURE(status)) 533 return -ENODEV; 534 /* Check if we really need to call cmpc_set_rfkill_wlan */ 535 is_blocked = state & 1 ? false : true; 536 if (is_blocked != blocked) { 537 state = blocked ? 0 : 1; 538 status = cmpc_set_rfkill_wlan(handle, state); 539 if (ACPI_FAILURE(status)) 540 return -ENODEV; 541 } 542 return 0; 543 } 544 545 static const struct rfkill_ops cmpc_rfkill_ops = { 546 .query = cmpc_rfkill_query, 547 .set_block = cmpc_rfkill_block, 548 }; 549 550 /* 551 * Common backlight and rfkill code. 552 */ 553 554 struct ipml200_dev { 555 struct backlight_device *bd; 556 struct rfkill *rf; 557 }; 558 559 static int cmpc_ipml_add(struct acpi_device *acpi) 560 { 561 int retval; 562 struct ipml200_dev *ipml; 563 struct backlight_properties props; 564 565 ipml = kmalloc(sizeof(*ipml), GFP_KERNEL); 566 if (ipml == NULL) 567 return -ENOMEM; 568 569 memset(&props, 0, sizeof(struct backlight_properties)); 570 props.type = BACKLIGHT_PLATFORM; 571 props.max_brightness = 7; 572 ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev, 573 acpi->handle, &cmpc_bl_ops, 574 &props); 575 if (IS_ERR(ipml->bd)) { 576 retval = PTR_ERR(ipml->bd); 577 goto out_bd; 578 } 579 580 ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN, 581 &cmpc_rfkill_ops, acpi->handle); 582 /* 583 * If RFKILL is disabled, rfkill_alloc will return ERR_PTR(-ENODEV). 584 * This is OK, however, since all other uses of the device will not 585 * derefence it. 586 */ 587 if (ipml->rf) { 588 retval = rfkill_register(ipml->rf); 589 if (retval) { 590 rfkill_destroy(ipml->rf); 591 ipml->rf = NULL; 592 } 593 } 594 595 dev_set_drvdata(&acpi->dev, ipml); 596 return 0; 597 598 out_bd: 599 kfree(ipml); 600 return retval; 601 } 602 603 static int cmpc_ipml_remove(struct acpi_device *acpi, int type) 604 { 605 struct ipml200_dev *ipml; 606 607 ipml = dev_get_drvdata(&acpi->dev); 608 609 backlight_device_unregister(ipml->bd); 610 611 if (ipml->rf) { 612 rfkill_unregister(ipml->rf); 613 rfkill_destroy(ipml->rf); 614 } 615 616 kfree(ipml); 617 618 return 0; 619 } 620 621 static const struct acpi_device_id cmpc_ipml_device_ids[] = { 622 {CMPC_IPML_HID, 0}, 623 {"", 0} 624 }; 625 626 static struct acpi_driver cmpc_ipml_acpi_driver = { 627 .owner = THIS_MODULE, 628 .name = "cmpc", 629 .class = "cmpc", 630 .ids = cmpc_ipml_device_ids, 631 .ops = { 632 .add = cmpc_ipml_add, 633 .remove = cmpc_ipml_remove 634 } 635 }; 636 637 638 /* 639 * Extra keys code. 640 */ 641 static int cmpc_keys_codes[] = { 642 KEY_UNKNOWN, 643 KEY_WLAN, 644 KEY_SWITCHVIDEOMODE, 645 KEY_BRIGHTNESSDOWN, 646 KEY_BRIGHTNESSUP, 647 KEY_VENDOR, 648 KEY_UNKNOWN, 649 KEY_CAMERA, 650 KEY_BACK, 651 KEY_FORWARD, 652 KEY_MAX 653 }; 654 655 static void cmpc_keys_handler(struct acpi_device *dev, u32 event) 656 { 657 struct input_dev *inputdev; 658 int code = KEY_MAX; 659 660 if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes)) 661 code = cmpc_keys_codes[event & 0x0F]; 662 inputdev = dev_get_drvdata(&dev->dev); 663 input_report_key(inputdev, code, !(event & 0x10)); 664 input_sync(inputdev); 665 } 666 667 static void cmpc_keys_idev_init(struct input_dev *inputdev) 668 { 669 int i; 670 671 set_bit(EV_KEY, inputdev->evbit); 672 for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++) 673 set_bit(cmpc_keys_codes[i], inputdev->keybit); 674 } 675 676 static int cmpc_keys_add(struct acpi_device *acpi) 677 { 678 return cmpc_add_acpi_notify_device(acpi, "cmpc_keys", 679 cmpc_keys_idev_init); 680 } 681 682 static int cmpc_keys_remove(struct acpi_device *acpi, int type) 683 { 684 return cmpc_remove_acpi_notify_device(acpi); 685 } 686 687 static const struct acpi_device_id cmpc_keys_device_ids[] = { 688 {CMPC_KEYS_HID, 0}, 689 {"", 0} 690 }; 691 692 static struct acpi_driver cmpc_keys_acpi_driver = { 693 .owner = THIS_MODULE, 694 .name = "cmpc_keys", 695 .class = "cmpc_keys", 696 .ids = cmpc_keys_device_ids, 697 .ops = { 698 .add = cmpc_keys_add, 699 .remove = cmpc_keys_remove, 700 .notify = cmpc_keys_handler, 701 } 702 }; 703 704 705 /* 706 * General init/exit code. 707 */ 708 709 static int cmpc_init(void) 710 { 711 int r; 712 713 r = acpi_bus_register_driver(&cmpc_keys_acpi_driver); 714 if (r) 715 goto failed_keys; 716 717 r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver); 718 if (r) 719 goto failed_bl; 720 721 r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver); 722 if (r) 723 goto failed_tablet; 724 725 r = acpi_bus_register_driver(&cmpc_accel_acpi_driver); 726 if (r) 727 goto failed_accel; 728 729 return r; 730 731 failed_accel: 732 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); 733 734 failed_tablet: 735 acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); 736 737 failed_bl: 738 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); 739 740 failed_keys: 741 return r; 742 } 743 744 static void cmpc_exit(void) 745 { 746 acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); 747 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); 748 acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); 749 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); 750 } 751 752 module_init(cmpc_init); 753 module_exit(cmpc_exit); 754 755 static const struct acpi_device_id cmpc_device_ids[] = { 756 {CMPC_ACCEL_HID, 0}, 757 {CMPC_TABLET_HID, 0}, 758 {CMPC_IPML_HID, 0}, 759 {CMPC_KEYS_HID, 0}, 760 {"", 0} 761 }; 762 763 MODULE_DEVICE_TABLE(acpi, cmpc_device_ids); 764