1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * lg-laptop.c - LG Gram ACPI features and hotkeys Driver 4 * 5 * Copyright (C) 2018 Matan Ziv-Av <matan@svgalib.org> 6 */ 7 8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 10 #include <linux/acpi.h> 11 #include <linux/dmi.h> 12 #include <linux/input.h> 13 #include <linux/input/sparse-keymap.h> 14 #include <linux/kernel.h> 15 #include <linux/leds.h> 16 #include <linux/module.h> 17 #include <linux/platform_device.h> 18 #include <linux/types.h> 19 20 #include <acpi/battery.h> 21 22 #define LED_DEVICE(_name, max, flag) struct led_classdev _name = { \ 23 .name = __stringify(_name), \ 24 .max_brightness = max, \ 25 .brightness_set = _name##_set, \ 26 .brightness_get = _name##_get, \ 27 .flags = flag, \ 28 } 29 30 MODULE_AUTHOR("Matan Ziv-Av"); 31 MODULE_DESCRIPTION("LG WMI Hotkey Driver"); 32 MODULE_LICENSE("GPL"); 33 34 #define WMI_EVENT_GUID0 "E4FB94F9-7F2B-4173-AD1A-CD1D95086248" 35 #define WMI_EVENT_GUID1 "023B133E-49D1-4E10-B313-698220140DC2" 36 #define WMI_EVENT_GUID2 "37BE1AC0-C3F2-4B1F-BFBE-8FDEAF2814D6" 37 #define WMI_EVENT_GUID3 "911BAD44-7DF8-4FBB-9319-BABA1C4B293B" 38 #define WMI_METHOD_WMAB "C3A72B38-D3EF-42D3-8CBB-D5A57049F66D" 39 #define WMI_METHOD_WMBB "2B4F501A-BD3C-4394-8DCF-00A7D2BC8210" 40 #define WMI_EVENT_GUID WMI_EVENT_GUID0 41 42 #define SB_GGOV_METHOD "\\_SB.GGOV" 43 #define GOV_TLED 0x2020008 44 #define WM_GET 1 45 #define WM_SET 2 46 #define WM_KEY_LIGHT 0x400 47 #define WM_TLED 0x404 48 #define WM_FN_LOCK 0x407 49 #define WM_BATT_LIMIT 0x61 50 #define WM_READER_MODE 0xBF 51 #define WM_FAN_MODE 0x33 52 #define WMBB_USB_CHARGE 0x10B 53 #define WMBB_BATT_LIMIT 0x10C 54 55 #define PLATFORM_NAME "lg-laptop" 56 57 MODULE_ALIAS("wmi:" WMI_EVENT_GUID0); 58 MODULE_ALIAS("wmi:" WMI_EVENT_GUID1); 59 MODULE_ALIAS("wmi:" WMI_EVENT_GUID2); 60 MODULE_ALIAS("wmi:" WMI_EVENT_GUID3); 61 MODULE_ALIAS("wmi:" WMI_METHOD_WMAB); 62 MODULE_ALIAS("wmi:" WMI_METHOD_WMBB); 63 64 static struct platform_device *pf_device; 65 static struct input_dev *wmi_input_dev; 66 67 static u32 inited; 68 #define INIT_INPUT_WMI_0 0x01 69 #define INIT_INPUT_WMI_2 0x02 70 #define INIT_INPUT_ACPI 0x04 71 #define INIT_SPARSE_KEYMAP 0x80 72 73 static int battery_limit_use_wmbb; 74 static struct led_classdev kbd_backlight; 75 static enum led_brightness get_kbd_backlight_level(struct device *dev); 76 77 static const struct key_entry wmi_keymap[] = { 78 {KE_KEY, 0x70, {KEY_F15} }, /* LG control panel (F1) */ 79 {KE_KEY, 0x74, {KEY_F21} }, /* Touchpad toggle (F5) */ 80 {KE_KEY, 0xf020000, {KEY_F14} }, /* Read mode (F9) */ 81 {KE_KEY, 0x10000000, {KEY_F16} },/* Keyboard backlight (F8) - pressing 82 * this key both sends an event and 83 * changes backlight level. 84 */ 85 {KE_END, 0} 86 }; 87 88 static int ggov(u32 arg0) 89 { 90 union acpi_object args[1]; 91 union acpi_object *r; 92 acpi_status status; 93 acpi_handle handle; 94 struct acpi_object_list arg; 95 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 96 int res; 97 98 args[0].type = ACPI_TYPE_INTEGER; 99 args[0].integer.value = arg0; 100 101 status = acpi_get_handle(NULL, (acpi_string) SB_GGOV_METHOD, &handle); 102 if (ACPI_FAILURE(status)) { 103 pr_err("Cannot get handle"); 104 return -ENODEV; 105 } 106 107 arg.count = 1; 108 arg.pointer = args; 109 110 status = acpi_evaluate_object(handle, NULL, &arg, &buffer); 111 if (ACPI_FAILURE(status)) { 112 acpi_handle_err(handle, "GGOV: call failed.\n"); 113 return -EINVAL; 114 } 115 116 r = buffer.pointer; 117 if (r->type != ACPI_TYPE_INTEGER) { 118 kfree(r); 119 return -EINVAL; 120 } 121 122 res = r->integer.value; 123 kfree(r); 124 125 return res; 126 } 127 128 static union acpi_object *lg_wmab(struct device *dev, u32 method, u32 arg1, u32 arg2) 129 { 130 union acpi_object args[3]; 131 acpi_status status; 132 struct acpi_object_list arg; 133 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 134 135 args[0].type = ACPI_TYPE_INTEGER; 136 args[0].integer.value = method; 137 args[1].type = ACPI_TYPE_INTEGER; 138 args[1].integer.value = arg1; 139 args[2].type = ACPI_TYPE_INTEGER; 140 args[2].integer.value = arg2; 141 142 arg.count = 3; 143 arg.pointer = args; 144 145 status = acpi_evaluate_object(ACPI_HANDLE(dev), "WMAB", &arg, &buffer); 146 if (ACPI_FAILURE(status)) { 147 dev_err(dev, "WMAB: call failed.\n"); 148 return NULL; 149 } 150 151 return buffer.pointer; 152 } 153 154 static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u32 arg2) 155 { 156 union acpi_object args[3]; 157 acpi_status status; 158 struct acpi_object_list arg; 159 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 160 u8 buf[32]; 161 162 *(u32 *)buf = method_id; 163 *(u32 *)(buf + 4) = arg1; 164 *(u32 *)(buf + 16) = arg2; 165 args[0].type = ACPI_TYPE_INTEGER; 166 args[0].integer.value = 0; /* ignored */ 167 args[1].type = ACPI_TYPE_INTEGER; 168 args[1].integer.value = 1; /* Must be 1 or 2. Does not matter which */ 169 args[2].type = ACPI_TYPE_BUFFER; 170 args[2].buffer.length = 32; 171 args[2].buffer.pointer = buf; 172 173 arg.count = 3; 174 arg.pointer = args; 175 176 status = acpi_evaluate_object(ACPI_HANDLE(dev), "WMBB", &arg, &buffer); 177 if (ACPI_FAILURE(status)) { 178 dev_err(dev, "WMBB: call failed.\n"); 179 return NULL; 180 } 181 182 return (union acpi_object *)buffer.pointer; 183 } 184 185 static void wmi_notify(u32 value, void *context) 186 { 187 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; 188 union acpi_object *obj; 189 acpi_status status; 190 long data = (long)context; 191 192 pr_debug("event guid %li\n", data); 193 status = wmi_get_event_data(value, &response); 194 if (ACPI_FAILURE(status)) { 195 pr_err("Bad event status 0x%x\n", status); 196 return; 197 } 198 199 obj = (union acpi_object *)response.pointer; 200 if (!obj) 201 return; 202 203 if (obj->type == ACPI_TYPE_INTEGER) { 204 int eventcode = obj->integer.value; 205 struct key_entry *key; 206 207 if (eventcode == 0x10000000) { 208 led_classdev_notify_brightness_hw_changed( 209 &kbd_backlight, get_kbd_backlight_level(kbd_backlight.dev->parent)); 210 } else { 211 key = sparse_keymap_entry_from_scancode( 212 wmi_input_dev, eventcode); 213 if (key && key->type == KE_KEY) 214 sparse_keymap_report_entry(wmi_input_dev, 215 key, 1, true); 216 } 217 } 218 219 pr_debug("Type: %i Eventcode: 0x%llx\n", obj->type, 220 obj->integer.value); 221 kfree(response.pointer); 222 } 223 224 static void wmi_input_setup(void) 225 { 226 acpi_status status; 227 228 wmi_input_dev = input_allocate_device(); 229 if (wmi_input_dev) { 230 wmi_input_dev->name = "LG WMI hotkeys"; 231 wmi_input_dev->phys = "wmi/input0"; 232 wmi_input_dev->id.bustype = BUS_HOST; 233 234 if (sparse_keymap_setup(wmi_input_dev, wmi_keymap, NULL) || 235 input_register_device(wmi_input_dev)) { 236 pr_info("Cannot initialize input device"); 237 input_free_device(wmi_input_dev); 238 return; 239 } 240 241 inited |= INIT_SPARSE_KEYMAP; 242 status = wmi_install_notify_handler(WMI_EVENT_GUID0, wmi_notify, 243 (void *)0); 244 if (ACPI_SUCCESS(status)) 245 inited |= INIT_INPUT_WMI_0; 246 247 status = wmi_install_notify_handler(WMI_EVENT_GUID2, wmi_notify, 248 (void *)2); 249 if (ACPI_SUCCESS(status)) 250 inited |= INIT_INPUT_WMI_2; 251 } else { 252 pr_info("Cannot allocate input device"); 253 } 254 } 255 256 static void acpi_notify(struct acpi_device *device, u32 event) 257 { 258 acpi_handle_debug(device->handle, "notify: %d\n", event); 259 } 260 261 static ssize_t fan_mode_store(struct device *dev, 262 struct device_attribute *attr, 263 const char *buffer, size_t count) 264 { 265 bool value; 266 union acpi_object *r; 267 u32 m; 268 int ret; 269 270 ret = kstrtobool(buffer, &value); 271 if (ret) 272 return ret; 273 274 r = lg_wmab(dev, WM_FAN_MODE, WM_GET, 0); 275 if (!r) 276 return -EIO; 277 278 if (r->type != ACPI_TYPE_INTEGER) { 279 kfree(r); 280 return -EIO; 281 } 282 283 m = r->integer.value; 284 kfree(r); 285 r = lg_wmab(dev, WM_FAN_MODE, WM_SET, (m & 0xffffff0f) | (value << 4)); 286 kfree(r); 287 r = lg_wmab(dev, WM_FAN_MODE, WM_SET, (m & 0xfffffff0) | value); 288 kfree(r); 289 290 return count; 291 } 292 293 static ssize_t fan_mode_show(struct device *dev, 294 struct device_attribute *attr, char *buffer) 295 { 296 unsigned int status; 297 union acpi_object *r; 298 299 r = lg_wmab(dev, WM_FAN_MODE, WM_GET, 0); 300 if (!r) 301 return -EIO; 302 303 if (r->type != ACPI_TYPE_INTEGER) { 304 kfree(r); 305 return -EIO; 306 } 307 308 status = r->integer.value & 0x01; 309 kfree(r); 310 311 return sysfs_emit(buffer, "%d\n", status); 312 } 313 314 static ssize_t usb_charge_store(struct device *dev, 315 struct device_attribute *attr, 316 const char *buffer, size_t count) 317 { 318 bool value; 319 union acpi_object *r; 320 int ret; 321 322 ret = kstrtobool(buffer, &value); 323 if (ret) 324 return ret; 325 326 r = lg_wmbb(dev, WMBB_USB_CHARGE, WM_SET, value); 327 if (!r) 328 return -EIO; 329 330 kfree(r); 331 return count; 332 } 333 334 static ssize_t usb_charge_show(struct device *dev, 335 struct device_attribute *attr, char *buffer) 336 { 337 unsigned int status; 338 union acpi_object *r; 339 340 r = lg_wmbb(dev, WMBB_USB_CHARGE, WM_GET, 0); 341 if (!r) 342 return -EIO; 343 344 if (r->type != ACPI_TYPE_BUFFER) { 345 kfree(r); 346 return -EIO; 347 } 348 349 status = !!r->buffer.pointer[0x10]; 350 351 kfree(r); 352 353 return sysfs_emit(buffer, "%d\n", status); 354 } 355 356 static ssize_t reader_mode_store(struct device *dev, 357 struct device_attribute *attr, 358 const char *buffer, size_t count) 359 { 360 bool value; 361 union acpi_object *r; 362 int ret; 363 364 ret = kstrtobool(buffer, &value); 365 if (ret) 366 return ret; 367 368 r = lg_wmab(dev, WM_READER_MODE, WM_SET, value); 369 if (!r) 370 return -EIO; 371 372 kfree(r); 373 return count; 374 } 375 376 static ssize_t reader_mode_show(struct device *dev, 377 struct device_attribute *attr, char *buffer) 378 { 379 unsigned int status; 380 union acpi_object *r; 381 382 r = lg_wmab(dev, WM_READER_MODE, WM_GET, 0); 383 if (!r) 384 return -EIO; 385 386 if (r->type != ACPI_TYPE_INTEGER) { 387 kfree(r); 388 return -EIO; 389 } 390 391 status = !!r->integer.value; 392 393 kfree(r); 394 395 return sysfs_emit(buffer, "%d\n", status); 396 } 397 398 static ssize_t fn_lock_store(struct device *dev, 399 struct device_attribute *attr, 400 const char *buffer, size_t count) 401 { 402 bool value; 403 union acpi_object *r; 404 int ret; 405 406 ret = kstrtobool(buffer, &value); 407 if (ret) 408 return ret; 409 410 r = lg_wmab(dev, WM_FN_LOCK, WM_SET, value); 411 if (!r) 412 return -EIO; 413 414 kfree(r); 415 return count; 416 } 417 418 static ssize_t fn_lock_show(struct device *dev, 419 struct device_attribute *attr, char *buffer) 420 { 421 unsigned int status; 422 union acpi_object *r; 423 424 r = lg_wmab(dev, WM_FN_LOCK, WM_GET, 0); 425 if (!r) 426 return -EIO; 427 428 if (r->type != ACPI_TYPE_BUFFER) { 429 kfree(r); 430 return -EIO; 431 } 432 433 status = !!r->buffer.pointer[0]; 434 kfree(r); 435 436 return sysfs_emit(buffer, "%d\n", status); 437 } 438 439 static ssize_t charge_control_end_threshold_store(struct device *dev, 440 struct device_attribute *attr, 441 const char *buf, size_t count) 442 { 443 unsigned long value; 444 int ret; 445 446 ret = kstrtoul(buf, 10, &value); 447 if (ret) 448 return ret; 449 450 if (value == 100 || value == 80) { 451 union acpi_object *r; 452 453 if (battery_limit_use_wmbb) 454 r = lg_wmbb(&pf_device->dev, WMBB_BATT_LIMIT, WM_SET, value); 455 else 456 r = lg_wmab(&pf_device->dev, WM_BATT_LIMIT, WM_SET, value); 457 if (!r) 458 return -EIO; 459 460 kfree(r); 461 return count; 462 } 463 464 return -EINVAL; 465 } 466 467 static ssize_t charge_control_end_threshold_show(struct device *device, 468 struct device_attribute *attr, 469 char *buf) 470 { 471 unsigned int status; 472 union acpi_object *r; 473 474 if (battery_limit_use_wmbb) { 475 r = lg_wmbb(&pf_device->dev, WMBB_BATT_LIMIT, WM_GET, 0); 476 if (!r) 477 return -EIO; 478 479 if (r->type != ACPI_TYPE_BUFFER) { 480 kfree(r); 481 return -EIO; 482 } 483 484 status = r->buffer.pointer[0x10]; 485 } else { 486 r = lg_wmab(&pf_device->dev, WM_BATT_LIMIT, WM_GET, 0); 487 if (!r) 488 return -EIO; 489 490 if (r->type != ACPI_TYPE_INTEGER) { 491 kfree(r); 492 return -EIO; 493 } 494 495 status = r->integer.value; 496 } 497 kfree(r); 498 if (status != 80 && status != 100) 499 status = 0; 500 501 return sysfs_emit(buf, "%d\n", status); 502 } 503 504 static ssize_t battery_care_limit_show(struct device *dev, 505 struct device_attribute *attr, 506 char *buffer) 507 { 508 return charge_control_end_threshold_show(dev, attr, buffer); 509 } 510 511 static ssize_t battery_care_limit_store(struct device *dev, 512 struct device_attribute *attr, 513 const char *buffer, size_t count) 514 { 515 return charge_control_end_threshold_store(dev, attr, buffer, count); 516 } 517 518 static DEVICE_ATTR_RW(fan_mode); 519 static DEVICE_ATTR_RW(usb_charge); 520 static DEVICE_ATTR_RW(reader_mode); 521 static DEVICE_ATTR_RW(fn_lock); 522 static DEVICE_ATTR_RW(charge_control_end_threshold); 523 static DEVICE_ATTR_RW(battery_care_limit); 524 525 static int lg_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook) 526 { 527 if (device_create_file(&battery->dev, 528 &dev_attr_charge_control_end_threshold)) 529 return -ENODEV; 530 531 return 0; 532 } 533 534 static int lg_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook) 535 { 536 device_remove_file(&battery->dev, 537 &dev_attr_charge_control_end_threshold); 538 return 0; 539 } 540 541 static struct acpi_battery_hook battery_hook = { 542 .add_battery = lg_battery_add, 543 .remove_battery = lg_battery_remove, 544 .name = "LG Battery Extension", 545 }; 546 547 static struct attribute *dev_attributes[] = { 548 &dev_attr_fan_mode.attr, 549 &dev_attr_usb_charge.attr, 550 &dev_attr_reader_mode.attr, 551 &dev_attr_fn_lock.attr, 552 &dev_attr_battery_care_limit.attr, 553 NULL 554 }; 555 556 static const struct attribute_group dev_attribute_group = { 557 .attrs = dev_attributes, 558 }; 559 560 static void tpad_led_set(struct led_classdev *cdev, 561 enum led_brightness brightness) 562 { 563 union acpi_object *r; 564 565 r = lg_wmab(cdev->dev->parent, WM_TLED, WM_SET, brightness > LED_OFF); 566 kfree(r); 567 } 568 569 static enum led_brightness tpad_led_get(struct led_classdev *cdev) 570 { 571 return ggov(GOV_TLED) > 0 ? LED_ON : LED_OFF; 572 } 573 574 static LED_DEVICE(tpad_led, 1, 0); 575 576 static void kbd_backlight_set(struct led_classdev *cdev, 577 enum led_brightness brightness) 578 { 579 u32 val; 580 union acpi_object *r; 581 582 val = 0x22; 583 if (brightness <= LED_OFF) 584 val = 0; 585 if (brightness >= LED_FULL) 586 val = 0x24; 587 r = lg_wmab(cdev->dev->parent, WM_KEY_LIGHT, WM_SET, val); 588 kfree(r); 589 } 590 591 static enum led_brightness get_kbd_backlight_level(struct device *dev) 592 { 593 union acpi_object *r; 594 int val; 595 596 r = lg_wmab(dev, WM_KEY_LIGHT, WM_GET, 0); 597 598 if (!r) 599 return LED_OFF; 600 601 if (r->type != ACPI_TYPE_BUFFER || r->buffer.pointer[1] != 0x05) { 602 kfree(r); 603 return LED_OFF; 604 } 605 606 switch (r->buffer.pointer[0] & 0x27) { 607 case 0x24: 608 val = LED_FULL; 609 break; 610 case 0x22: 611 val = LED_HALF; 612 break; 613 default: 614 val = LED_OFF; 615 } 616 617 kfree(r); 618 619 return val; 620 } 621 622 static enum led_brightness kbd_backlight_get(struct led_classdev *cdev) 623 { 624 return get_kbd_backlight_level(cdev->dev->parent); 625 } 626 627 static LED_DEVICE(kbd_backlight, 255, LED_BRIGHT_HW_CHANGED); 628 629 static void wmi_input_destroy(void) 630 { 631 if (inited & INIT_INPUT_WMI_2) 632 wmi_remove_notify_handler(WMI_EVENT_GUID2); 633 634 if (inited & INIT_INPUT_WMI_0) 635 wmi_remove_notify_handler(WMI_EVENT_GUID0); 636 637 if (inited & INIT_SPARSE_KEYMAP) 638 input_unregister_device(wmi_input_dev); 639 640 inited &= ~(INIT_INPUT_WMI_0 | INIT_INPUT_WMI_2 | INIT_SPARSE_KEYMAP); 641 } 642 643 static struct platform_driver pf_driver = { 644 .driver = { 645 .name = PLATFORM_NAME, 646 } 647 }; 648 649 static int acpi_add(struct acpi_device *device) 650 { 651 struct platform_device_info pdev_info = { 652 .fwnode = acpi_fwnode_handle(device), 653 .name = PLATFORM_NAME, 654 .id = PLATFORM_DEVID_NONE, 655 }; 656 int ret; 657 const char *product; 658 int year = 2017; 659 660 if (pf_device) 661 return 0; 662 663 ret = platform_driver_register(&pf_driver); 664 if (ret) 665 return ret; 666 667 pf_device = platform_device_register_full(&pdev_info); 668 if (IS_ERR(pf_device)) { 669 ret = PTR_ERR(pf_device); 670 pf_device = NULL; 671 pr_err("unable to register platform device\n"); 672 goto out_platform_registered; 673 } 674 product = dmi_get_system_info(DMI_PRODUCT_NAME); 675 if (product && strlen(product) > 4) 676 switch (product[4]) { 677 case '5': 678 if (strlen(product) > 5) 679 switch (product[5]) { 680 case 'N': 681 year = 2021; 682 break; 683 case '0': 684 year = 2016; 685 break; 686 default: 687 year = 2022; 688 } 689 break; 690 case '6': 691 year = 2016; 692 break; 693 case '7': 694 year = 2017; 695 break; 696 case '8': 697 year = 2018; 698 break; 699 case '9': 700 year = 2019; 701 break; 702 case '0': 703 if (strlen(product) > 5) 704 switch (product[5]) { 705 case 'N': 706 year = 2020; 707 break; 708 case 'P': 709 year = 2021; 710 break; 711 default: 712 year = 2022; 713 } 714 break; 715 default: 716 year = 2019; 717 } 718 pr_info("product: %s year: %d\n", product, year); 719 720 if (year >= 2019) 721 battery_limit_use_wmbb = 1; 722 723 ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group); 724 if (ret) 725 goto out_platform_device; 726 727 /* LEDs are optional */ 728 led_classdev_register(&pf_device->dev, &kbd_backlight); 729 led_classdev_register(&pf_device->dev, &tpad_led); 730 731 wmi_input_setup(); 732 battery_hook_register(&battery_hook); 733 734 return 0; 735 736 out_platform_device: 737 platform_device_unregister(pf_device); 738 out_platform_registered: 739 platform_driver_unregister(&pf_driver); 740 return ret; 741 } 742 743 static void acpi_remove(struct acpi_device *device) 744 { 745 sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group); 746 747 led_classdev_unregister(&tpad_led); 748 led_classdev_unregister(&kbd_backlight); 749 750 battery_hook_unregister(&battery_hook); 751 wmi_input_destroy(); 752 platform_device_unregister(pf_device); 753 pf_device = NULL; 754 platform_driver_unregister(&pf_driver); 755 } 756 757 static const struct acpi_device_id device_ids[] = { 758 {"LGEX0820", 0}, 759 {"", 0} 760 }; 761 MODULE_DEVICE_TABLE(acpi, device_ids); 762 763 static struct acpi_driver acpi_driver = { 764 .name = "LG Gram Laptop Support", 765 .class = "lg-laptop", 766 .ids = device_ids, 767 .ops = { 768 .add = acpi_add, 769 .remove = acpi_remove, 770 .notify = acpi_notify, 771 }, 772 .owner = THIS_MODULE, 773 }; 774 775 static int __init acpi_init(void) 776 { 777 int result; 778 779 result = acpi_bus_register_driver(&acpi_driver); 780 if (result < 0) { 781 pr_debug("Error registering driver\n"); 782 return -ENODEV; 783 } 784 785 return 0; 786 } 787 788 static void __exit acpi_exit(void) 789 { 790 acpi_bus_unregister_driver(&acpi_driver); 791 } 792 793 module_init(acpi_init); 794 module_exit(acpi_exit); 795