lg-laptop.c (17704B)
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 30MODULE_AUTHOR("Matan Ziv-Av"); 31MODULE_DESCRIPTION("LG WMI Hotkey Driver"); 32MODULE_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 59MODULE_ALIAS("wmi:" WMI_EVENT_GUID0); 60MODULE_ALIAS("wmi:" WMI_EVENT_GUID1); 61MODULE_ALIAS("wmi:" WMI_EVENT_GUID2); 62MODULE_ALIAS("wmi:" WMI_EVENT_GUID3); 63MODULE_ALIAS("wmi:" WMI_METHOD_WMAB); 64MODULE_ALIAS("wmi:" WMI_METHOD_WMBB); 65 66static struct platform_device *pf_device; 67static struct input_dev *wmi_input_dev; 68 69static 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 75static int battery_limit_use_wmbb; 76static struct led_classdev kbd_backlight; 77static enum led_brightness get_kbd_backlight_level(void); 78 79static 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 91static 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 131static 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 164static 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 202static 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 241static 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 273static 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 285static 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 317static 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 338static 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 358static 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 380static 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 400static 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 422static 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 442static 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 463static 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 491static 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 528static 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 535static 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 542static DEVICE_ATTR_RW(fan_mode); 543static DEVICE_ATTR_RW(usb_charge); 544static DEVICE_ATTR_RW(reader_mode); 545static DEVICE_ATTR_RW(fn_lock); 546static DEVICE_ATTR_RW(charge_control_end_threshold); 547static DEVICE_ATTR_RW(battery_care_limit); 548 549static int lg_battery_add(struct power_supply *battery) 550{ 551 if (device_create_file(&battery->dev, 552 &dev_attr_charge_control_end_threshold)) 553 return -ENODEV; 554 555 return 0; 556} 557 558static int lg_battery_remove(struct power_supply *battery) 559{ 560 device_remove_file(&battery->dev, 561 &dev_attr_charge_control_end_threshold); 562 return 0; 563} 564 565static 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 571static 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 580static const struct attribute_group dev_attribute_group = { 581 .attrs = dev_attributes, 582}; 583 584static 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 593static enum led_brightness tpad_led_get(struct led_classdev *cdev) 594{ 595 return ggov(GOV_TLED) > 0 ? LED_ON : LED_OFF; 596} 597 598static LED_DEVICE(tpad_led, 1, 0); 599 600static 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 615static 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 646static enum led_brightness kbd_backlight_get(struct led_classdev *cdev) 647{ 648 return get_kbd_backlight_level(); 649} 650 651static LED_DEVICE(kbd_backlight, 255, LED_BRIGHT_HW_CHANGED); 652 653static 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 667static struct platform_driver pf_driver = { 668 .driver = { 669 .name = PLATFORM_NAME, 670 } 671}; 672 673static 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, 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 757out_platform_device: 758 platform_device_unregister(pf_device); 759out_platform_registered: 760 platform_driver_unregister(&pf_driver); 761 return ret; 762} 763 764static int 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 return 0; 778} 779 780static const struct acpi_device_id device_ids[] = { 781 {"LGEX0815", 0}, 782 {"", 0} 783}; 784MODULE_DEVICE_TABLE(acpi, device_ids); 785 786static struct acpi_driver acpi_driver = { 787 .name = "LG Gram Laptop Support", 788 .class = "lg-laptop", 789 .ids = device_ids, 790 .ops = { 791 .add = acpi_add, 792 .remove = acpi_remove, 793 .notify = acpi_notify, 794 }, 795 .owner = THIS_MODULE, 796}; 797 798static int __init acpi_init(void) 799{ 800 int result; 801 802 result = acpi_bus_register_driver(&acpi_driver); 803 if (result < 0) { 804 pr_debug("Error registering driver\n"); 805 return -ENODEV; 806 } 807 808 return 0; 809} 810 811static void __exit acpi_exit(void) 812{ 813 acpi_bus_unregister_driver(&acpi_driver); 814} 815 816module_init(acpi_init); 817module_exit(acpi_exit);