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