linux/drivers/acpi/button.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  button.c - ACPI Button Driver
   4 *
   5 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
   6 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
   7 */
   8
   9#define pr_fmt(fmt) "ACPI: button: " fmt
  10
  11#include <linux/compiler.h>
  12#include <linux/kernel.h>
  13#include <linux/module.h>
  14#include <linux/init.h>
  15#include <linux/types.h>
  16#include <linux/proc_fs.h>
  17#include <linux/seq_file.h>
  18#include <linux/input.h>
  19#include <linux/slab.h>
  20#include <linux/acpi.h>
  21#include <linux/dmi.h>
  22#include <acpi/button.h>
  23
  24#define PREFIX "ACPI: "
  25
  26#define ACPI_BUTTON_CLASS               "button"
  27#define ACPI_BUTTON_FILE_STATE          "state"
  28#define ACPI_BUTTON_TYPE_UNKNOWN        0x00
  29#define ACPI_BUTTON_NOTIFY_STATUS       0x80
  30
  31#define ACPI_BUTTON_SUBCLASS_POWER      "power"
  32#define ACPI_BUTTON_DEVICE_NAME_POWER   "Power Button"
  33#define ACPI_BUTTON_TYPE_POWER          0x01
  34
  35#define ACPI_BUTTON_SUBCLASS_SLEEP      "sleep"
  36#define ACPI_BUTTON_DEVICE_NAME_SLEEP   "Sleep Button"
  37#define ACPI_BUTTON_TYPE_SLEEP          0x03
  38
  39#define ACPI_BUTTON_SUBCLASS_LID        "lid"
  40#define ACPI_BUTTON_DEVICE_NAME_LID     "Lid Switch"
  41#define ACPI_BUTTON_TYPE_LID            0x05
  42
  43enum {
  44        ACPI_BUTTON_LID_INIT_IGNORE,
  45        ACPI_BUTTON_LID_INIT_OPEN,
  46        ACPI_BUTTON_LID_INIT_METHOD,
  47        ACPI_BUTTON_LID_INIT_DISABLED,
  48};
  49
  50static const char * const lid_init_state_str[] = {
  51        [ACPI_BUTTON_LID_INIT_IGNORE]           = "ignore",
  52        [ACPI_BUTTON_LID_INIT_OPEN]             = "open",
  53        [ACPI_BUTTON_LID_INIT_METHOD]           = "method",
  54        [ACPI_BUTTON_LID_INIT_DISABLED]         = "disabled",
  55};
  56
  57#define _COMPONENT              ACPI_BUTTON_COMPONENT
  58ACPI_MODULE_NAME("button");
  59
  60MODULE_AUTHOR("Paul Diefenbaugh");
  61MODULE_DESCRIPTION("ACPI Button Driver");
  62MODULE_LICENSE("GPL");
  63
  64static const struct acpi_device_id button_device_ids[] = {
  65        {ACPI_BUTTON_HID_LID,    0},
  66        {ACPI_BUTTON_HID_SLEEP,  0},
  67        {ACPI_BUTTON_HID_SLEEPF, 0},
  68        {ACPI_BUTTON_HID_POWER,  0},
  69        {ACPI_BUTTON_HID_POWERF, 0},
  70        {"", 0},
  71};
  72MODULE_DEVICE_TABLE(acpi, button_device_ids);
  73
  74/* Please keep this list sorted alphabetically by vendor and model */
  75static const struct dmi_system_id dmi_lid_quirks[] = {
  76        {
  77                /* GP-electronic T701, _LID method points to a floating GPIO */
  78                .matches = {
  79                        DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
  80                        DMI_MATCH(DMI_PRODUCT_NAME, "T701"),
  81                        DMI_MATCH(DMI_BIOS_VERSION, "BYT70A.YNCHENG.WIN.007"),
  82                },
  83                .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED,
  84        },
  85        {
  86                /*
  87                 * Medion Akoya E2215T, notification of the LID device only
  88                 * happens on close, not on open and _LID always returns closed.
  89                 */
  90                .matches = {
  91                        DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
  92                        DMI_MATCH(DMI_PRODUCT_NAME, "E2215T"),
  93                },
  94                .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
  95        },
  96        {
  97                /*
  98                 * Medion Akoya E2228T, notification of the LID device only
  99                 * happens on close, not on open and _LID always returns closed.
 100                 */
 101                .matches = {
 102                        DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
 103                        DMI_MATCH(DMI_PRODUCT_NAME, "E2228T"),
 104                },
 105                .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
 106        },
 107        {
 108                /*
 109                 * Razer Blade Stealth 13 late 2019, notification of the LID device
 110                 * only happens on close, not on open and _LID always returns closed.
 111                 */
 112                .matches = {
 113                        DMI_MATCH(DMI_SYS_VENDOR, "Razer"),
 114                        DMI_MATCH(DMI_PRODUCT_NAME, "Razer Blade Stealth 13 Late 2019"),
 115                },
 116                .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
 117        },
 118        {}
 119};
 120
 121static int acpi_button_add(struct acpi_device *device);
 122static int acpi_button_remove(struct acpi_device *device);
 123static void acpi_button_notify(struct acpi_device *device, u32 event);
 124
 125#ifdef CONFIG_PM_SLEEP
 126static int acpi_button_suspend(struct device *dev);
 127static int acpi_button_resume(struct device *dev);
 128#else
 129#define acpi_button_suspend NULL
 130#define acpi_button_resume NULL
 131#endif
 132static SIMPLE_DEV_PM_OPS(acpi_button_pm, acpi_button_suspend, acpi_button_resume);
 133
 134static struct acpi_driver acpi_button_driver = {
 135        .name = "button",
 136        .class = ACPI_BUTTON_CLASS,
 137        .ids = button_device_ids,
 138        .ops = {
 139                .add = acpi_button_add,
 140                .remove = acpi_button_remove,
 141                .notify = acpi_button_notify,
 142        },
 143        .drv.pm = &acpi_button_pm,
 144};
 145
 146struct acpi_button {
 147        unsigned int type;
 148        struct input_dev *input;
 149        char phys[32];                  /* for input device */
 150        unsigned long pushed;
 151        int last_state;
 152        ktime_t last_time;
 153        bool suspended;
 154        bool lid_state_initialized;
 155};
 156
 157static struct acpi_device *lid_device;
 158static long lid_init_state = -1;
 159
 160static unsigned long lid_report_interval __read_mostly = 500;
 161module_param(lid_report_interval, ulong, 0644);
 162MODULE_PARM_DESC(lid_report_interval, "Interval (ms) between lid key events");
 163
 164/* --------------------------------------------------------------------------
 165                              FS Interface (/proc)
 166   -------------------------------------------------------------------------- */
 167
 168static struct proc_dir_entry *acpi_button_dir;
 169static struct proc_dir_entry *acpi_lid_dir;
 170
 171static int acpi_lid_evaluate_state(struct acpi_device *device)
 172{
 173        unsigned long long lid_state;
 174        acpi_status status;
 175
 176        status = acpi_evaluate_integer(device->handle, "_LID", NULL, &lid_state);
 177        if (ACPI_FAILURE(status))
 178                return -ENODEV;
 179
 180        return lid_state ? 1 : 0;
 181}
 182
 183static int acpi_lid_notify_state(struct acpi_device *device, int state)
 184{
 185        struct acpi_button *button = acpi_driver_data(device);
 186        ktime_t next_report;
 187        bool do_update;
 188
 189        /*
 190         * In lid_init_state=ignore mode, if user opens/closes lid
 191         * frequently with "open" missing, and "last_time" is also updated
 192         * frequently, "close" cannot be delivered to the userspace.
 193         * So "last_time" is only updated after a timeout or an actual
 194         * switch.
 195         */
 196        if (lid_init_state != ACPI_BUTTON_LID_INIT_IGNORE ||
 197            button->last_state != !!state)
 198                do_update = true;
 199        else
 200                do_update = false;
 201
 202        next_report = ktime_add(button->last_time,
 203                                ms_to_ktime(lid_report_interval));
 204        if (button->last_state == !!state &&
 205            ktime_after(ktime_get(), next_report)) {
 206                /* Complain the buggy firmware */
 207                pr_warn_once("The lid device is not compliant to SW_LID.\n");
 208
 209                /*
 210                 * Send the unreliable complement switch event:
 211                 *
 212                 * On most platforms, the lid device is reliable. However
 213                 * there are exceptions:
 214                 * 1. Platforms returning initial lid state as "close" by
 215                 *    default after booting/resuming:
 216                 *     https://bugzilla.kernel.org/show_bug.cgi?id=89211
 217                 *     https://bugzilla.kernel.org/show_bug.cgi?id=106151
 218                 * 2. Platforms never reporting "open" events:
 219                 *     https://bugzilla.kernel.org/show_bug.cgi?id=106941
 220                 * On these buggy platforms, the usage model of the ACPI
 221                 * lid device actually is:
 222                 * 1. The initial returning value of _LID may not be
 223                 *    reliable.
 224                 * 2. The open event may not be reliable.
 225                 * 3. The close event is reliable.
 226                 *
 227                 * But SW_LID is typed as input switch event, the input
 228                 * layer checks if the event is redundant. Hence if the
 229                 * state is not switched, the userspace cannot see this
 230                 * platform triggered reliable event. By inserting a
 231                 * complement switch event, it then is guaranteed that the
 232                 * platform triggered reliable one can always be seen by
 233                 * the userspace.
 234                 */
 235                if (lid_init_state == ACPI_BUTTON_LID_INIT_IGNORE) {
 236                        do_update = true;
 237                        /*
 238                         * Do generate complement switch event for "close"
 239                         * as "close" is reliable and wrong "open" won't
 240                         * trigger unexpected behaviors.
 241                         * Do not generate complement switch event for
 242                         * "open" as "open" is not reliable and wrong
 243                         * "close" will trigger unexpected behaviors.
 244                         */
 245                        if (!state) {
 246                                input_report_switch(button->input,
 247                                                    SW_LID, state);
 248                                input_sync(button->input);
 249                        }
 250                }
 251        }
 252        /* Send the platform triggered reliable event */
 253        if (do_update) {
 254                acpi_handle_debug(device->handle, "ACPI LID %s\n",
 255                                  state ? "open" : "closed");
 256                input_report_switch(button->input, SW_LID, !state);
 257                input_sync(button->input);
 258                button->last_state = !!state;
 259                button->last_time = ktime_get();
 260        }
 261
 262        return 0;
 263}
 264
 265static int __maybe_unused acpi_button_state_seq_show(struct seq_file *seq,
 266                                                     void *offset)
 267{
 268        struct acpi_device *device = seq->private;
 269        int state;
 270
 271        state = acpi_lid_evaluate_state(device);
 272        seq_printf(seq, "state:      %s\n",
 273                   state < 0 ? "unsupported" : (state ? "open" : "closed"));
 274        return 0;
 275}
 276
 277static int acpi_button_add_fs(struct acpi_device *device)
 278{
 279        struct acpi_button *button = acpi_driver_data(device);
 280        struct proc_dir_entry *entry = NULL;
 281        int ret = 0;
 282
 283        /* procfs I/F for ACPI lid device only */
 284        if (button->type != ACPI_BUTTON_TYPE_LID)
 285                return 0;
 286
 287        if (acpi_button_dir || acpi_lid_dir) {
 288                printk(KERN_ERR PREFIX "More than one Lid device found!\n");
 289                return -EEXIST;
 290        }
 291
 292        /* create /proc/acpi/button */
 293        acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
 294        if (!acpi_button_dir)
 295                return -ENODEV;
 296
 297        /* create /proc/acpi/button/lid */
 298        acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
 299        if (!acpi_lid_dir) {
 300                ret = -ENODEV;
 301                goto remove_button_dir;
 302        }
 303
 304        /* create /proc/acpi/button/lid/LID/ */
 305        acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_lid_dir);
 306        if (!acpi_device_dir(device)) {
 307                ret = -ENODEV;
 308                goto remove_lid_dir;
 309        }
 310
 311        /* create /proc/acpi/button/lid/LID/state */
 312        entry = proc_create_single_data(ACPI_BUTTON_FILE_STATE, S_IRUGO,
 313                        acpi_device_dir(device), acpi_button_state_seq_show,
 314                        device);
 315        if (!entry) {
 316                ret = -ENODEV;
 317                goto remove_dev_dir;
 318        }
 319
 320done:
 321        return ret;
 322
 323remove_dev_dir:
 324        remove_proc_entry(acpi_device_bid(device),
 325                          acpi_lid_dir);
 326        acpi_device_dir(device) = NULL;
 327remove_lid_dir:
 328        remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
 329        acpi_lid_dir = NULL;
 330remove_button_dir:
 331        remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
 332        acpi_button_dir = NULL;
 333        goto done;
 334}
 335
 336static int acpi_button_remove_fs(struct acpi_device *device)
 337{
 338        struct acpi_button *button = acpi_driver_data(device);
 339
 340        if (button->type != ACPI_BUTTON_TYPE_LID)
 341                return 0;
 342
 343        remove_proc_entry(ACPI_BUTTON_FILE_STATE,
 344                          acpi_device_dir(device));
 345        remove_proc_entry(acpi_device_bid(device),
 346                          acpi_lid_dir);
 347        acpi_device_dir(device) = NULL;
 348        remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
 349        acpi_lid_dir = NULL;
 350        remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
 351        acpi_button_dir = NULL;
 352
 353        return 0;
 354}
 355
 356/* --------------------------------------------------------------------------
 357                                Driver Interface
 358   -------------------------------------------------------------------------- */
 359int acpi_lid_open(void)
 360{
 361        if (!lid_device)
 362                return -ENODEV;
 363
 364        return acpi_lid_evaluate_state(lid_device);
 365}
 366EXPORT_SYMBOL(acpi_lid_open);
 367
 368static int acpi_lid_update_state(struct acpi_device *device,
 369                                 bool signal_wakeup)
 370{
 371        int state;
 372
 373        state = acpi_lid_evaluate_state(device);
 374        if (state < 0)
 375                return state;
 376
 377        if (state && signal_wakeup)
 378                acpi_pm_wakeup_event(&device->dev);
 379
 380        return acpi_lid_notify_state(device, state);
 381}
 382
 383static void acpi_lid_initialize_state(struct acpi_device *device)
 384{
 385        struct acpi_button *button = acpi_driver_data(device);
 386
 387        switch (lid_init_state) {
 388        case ACPI_BUTTON_LID_INIT_OPEN:
 389                (void)acpi_lid_notify_state(device, 1);
 390                break;
 391        case ACPI_BUTTON_LID_INIT_METHOD:
 392                (void)acpi_lid_update_state(device, false);
 393                break;
 394        case ACPI_BUTTON_LID_INIT_IGNORE:
 395        default:
 396                break;
 397        }
 398
 399        button->lid_state_initialized = true;
 400}
 401
 402static void acpi_button_notify(struct acpi_device *device, u32 event)
 403{
 404        struct acpi_button *button = acpi_driver_data(device);
 405        struct input_dev *input;
 406
 407        switch (event) {
 408        case ACPI_FIXED_HARDWARE_EVENT:
 409                event = ACPI_BUTTON_NOTIFY_STATUS;
 410                fallthrough;
 411        case ACPI_BUTTON_NOTIFY_STATUS:
 412                input = button->input;
 413                if (button->type == ACPI_BUTTON_TYPE_LID) {
 414                        if (button->lid_state_initialized)
 415                                acpi_lid_update_state(device, true);
 416                } else {
 417                        int keycode;
 418
 419                        acpi_pm_wakeup_event(&device->dev);
 420                        if (button->suspended)
 421                                break;
 422
 423                        keycode = test_bit(KEY_SLEEP, input->keybit) ?
 424                                                KEY_SLEEP : KEY_POWER;
 425                        input_report_key(input, keycode, 1);
 426                        input_sync(input);
 427                        input_report_key(input, keycode, 0);
 428                        input_sync(input);
 429
 430                        acpi_bus_generate_netlink_event(
 431                                        device->pnp.device_class,
 432                                        dev_name(&device->dev),
 433                                        event, ++button->pushed);
 434                }
 435                break;
 436        default:
 437                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 438                                  "Unsupported event [0x%x]\n", event));
 439                break;
 440        }
 441}
 442
 443#ifdef CONFIG_PM_SLEEP
 444static int acpi_button_suspend(struct device *dev)
 445{
 446        struct acpi_device *device = to_acpi_device(dev);
 447        struct acpi_button *button = acpi_driver_data(device);
 448
 449        button->suspended = true;
 450        return 0;
 451}
 452
 453static int acpi_button_resume(struct device *dev)
 454{
 455        struct acpi_device *device = to_acpi_device(dev);
 456        struct acpi_button *button = acpi_driver_data(device);
 457
 458        button->suspended = false;
 459        if (button->type == ACPI_BUTTON_TYPE_LID) {
 460                button->last_state = !!acpi_lid_evaluate_state(device);
 461                button->last_time = ktime_get();
 462                acpi_lid_initialize_state(device);
 463        }
 464        return 0;
 465}
 466#endif
 467
 468static int acpi_lid_input_open(struct input_dev *input)
 469{
 470        struct acpi_device *device = input_get_drvdata(input);
 471        struct acpi_button *button = acpi_driver_data(device);
 472
 473        button->last_state = !!acpi_lid_evaluate_state(device);
 474        button->last_time = ktime_get();
 475        acpi_lid_initialize_state(device);
 476
 477        return 0;
 478}
 479
 480static int acpi_button_add(struct acpi_device *device)
 481{
 482        struct acpi_button *button;
 483        struct input_dev *input;
 484        const char *hid = acpi_device_hid(device);
 485        char *name, *class;
 486        int error;
 487
 488        if (!strcmp(hid, ACPI_BUTTON_HID_LID) &&
 489             lid_init_state == ACPI_BUTTON_LID_INIT_DISABLED)
 490                return -ENODEV;
 491
 492        button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL);
 493        if (!button)
 494                return -ENOMEM;
 495
 496        device->driver_data = button;
 497
 498        button->input = input = input_allocate_device();
 499        if (!input) {
 500                error = -ENOMEM;
 501                goto err_free_button;
 502        }
 503
 504        name = acpi_device_name(device);
 505        class = acpi_device_class(device);
 506
 507        if (!strcmp(hid, ACPI_BUTTON_HID_POWER) ||
 508            !strcmp(hid, ACPI_BUTTON_HID_POWERF)) {
 509                button->type = ACPI_BUTTON_TYPE_POWER;
 510                strcpy(name, ACPI_BUTTON_DEVICE_NAME_POWER);
 511                sprintf(class, "%s/%s",
 512                        ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
 513        } else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEP) ||
 514                   !strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) {
 515                button->type = ACPI_BUTTON_TYPE_SLEEP;
 516                strcpy(name, ACPI_BUTTON_DEVICE_NAME_SLEEP);
 517                sprintf(class, "%s/%s",
 518                        ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
 519        } else if (!strcmp(hid, ACPI_BUTTON_HID_LID)) {
 520                button->type = ACPI_BUTTON_TYPE_LID;
 521                strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID);
 522                sprintf(class, "%s/%s",
 523                        ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
 524                input->open = acpi_lid_input_open;
 525        } else {
 526                printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid);
 527                error = -ENODEV;
 528                goto err_free_input;
 529        }
 530
 531        error = acpi_button_add_fs(device);
 532        if (error)
 533                goto err_free_input;
 534
 535        snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid);
 536
 537        input->name = name;
 538        input->phys = button->phys;
 539        input->id.bustype = BUS_HOST;
 540        input->id.product = button->type;
 541        input->dev.parent = &device->dev;
 542
 543        switch (button->type) {
 544        case ACPI_BUTTON_TYPE_POWER:
 545                input_set_capability(input, EV_KEY, KEY_POWER);
 546                break;
 547
 548        case ACPI_BUTTON_TYPE_SLEEP:
 549                input_set_capability(input, EV_KEY, KEY_SLEEP);
 550                break;
 551
 552        case ACPI_BUTTON_TYPE_LID:
 553                input_set_capability(input, EV_SW, SW_LID);
 554                break;
 555        }
 556
 557        input_set_drvdata(input, device);
 558        error = input_register_device(input);
 559        if (error)
 560                goto err_remove_fs;
 561        if (button->type == ACPI_BUTTON_TYPE_LID) {
 562                /*
 563                 * This assumes there's only one lid device, or if there are
 564                 * more we only care about the last one...
 565                 */
 566                lid_device = device;
 567        }
 568
 569        device_init_wakeup(&device->dev, true);
 570        printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device));
 571        return 0;
 572
 573 err_remove_fs:
 574        acpi_button_remove_fs(device);
 575 err_free_input:
 576        input_free_device(input);
 577 err_free_button:
 578        kfree(button);
 579        return error;
 580}
 581
 582static int acpi_button_remove(struct acpi_device *device)
 583{
 584        struct acpi_button *button = acpi_driver_data(device);
 585
 586        acpi_button_remove_fs(device);
 587        input_unregister_device(button->input);
 588        kfree(button);
 589        return 0;
 590}
 591
 592static int param_set_lid_init_state(const char *val,
 593                                    const struct kernel_param *kp)
 594{
 595        int i;
 596
 597        i = sysfs_match_string(lid_init_state_str, val);
 598        if (i < 0)
 599                return i;
 600
 601        lid_init_state = i;
 602        pr_info("Initial lid state set to '%s'\n", lid_init_state_str[i]);
 603        return 0;
 604}
 605
 606static int param_get_lid_init_state(char *buf, const struct kernel_param *kp)
 607{
 608        int i, c = 0;
 609
 610        for (i = 0; i < ARRAY_SIZE(lid_init_state_str); i++)
 611                if (i == lid_init_state)
 612                        c += sprintf(buf + c, "[%s] ", lid_init_state_str[i]);
 613                else
 614                        c += sprintf(buf + c, "%s ", lid_init_state_str[i]);
 615
 616        buf[c - 1] = '\n'; /* Replace the final space with a newline */
 617
 618        return c;
 619}
 620
 621module_param_call(lid_init_state,
 622                  param_set_lid_init_state, param_get_lid_init_state,
 623                  NULL, 0644);
 624MODULE_PARM_DESC(lid_init_state, "Behavior for reporting LID initial state");
 625
 626static int acpi_button_register_driver(struct acpi_driver *driver)
 627{
 628        const struct dmi_system_id *dmi_id;
 629
 630        if (lid_init_state == -1) {
 631                dmi_id = dmi_first_match(dmi_lid_quirks);
 632                if (dmi_id)
 633                        lid_init_state = (long)dmi_id->driver_data;
 634                else
 635                        lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
 636        }
 637
 638        /*
 639         * Modules such as nouveau.ko and i915.ko have a link time dependency
 640         * on acpi_lid_open(), and would therefore not be loadable on ACPI
 641         * capable kernels booted in non-ACPI mode if the return value of
 642         * acpi_bus_register_driver() is returned from here with ACPI disabled
 643         * when this driver is built as a module.
 644         */
 645        if (acpi_disabled)
 646                return 0;
 647
 648        return acpi_bus_register_driver(driver);
 649}
 650
 651static void acpi_button_unregister_driver(struct acpi_driver *driver)
 652{
 653        if (!acpi_disabled)
 654                acpi_bus_unregister_driver(driver);
 655}
 656
 657module_driver(acpi_button_driver, acpi_button_register_driver,
 658               acpi_button_unregister_driver);
 659