linux/drivers/platform/x86/dell-wmi.c
<<
>>
Prefs
   1/*
   2 * Dell WMI hotkeys
   3 *
   4 * Copyright (C) 2008 Red Hat <mjg@redhat.com>
   5 * Copyright (C) 2014-2015 Pali Rohár <pali.rohar@gmail.com>
   6 *
   7 * Portions based on wistron_btns.c:
   8 * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
   9 * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
  10 * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
  11 *
  12 *  This program is free software; you can redistribute it and/or modify
  13 *  it under the terms of the GNU General Public License as published by
  14 *  the Free Software Foundation; either version 2 of the License, or
  15 *  (at your option) any later version.
  16 *
  17 *  This program is distributed in the hope that it will be useful,
  18 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  19 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20 *  GNU General Public License for more details.
  21 *
  22 *  You should have received a copy of the GNU General Public License
  23 *  along with this program; if not, write to the Free Software
  24 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  25 */
  26
  27#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  28
  29#include <linux/kernel.h>
  30#include <linux/module.h>
  31#include <linux/init.h>
  32#include <linux/slab.h>
  33#include <linux/types.h>
  34#include <linux/input.h>
  35#include <linux/input/sparse-keymap.h>
  36#include <linux/acpi.h>
  37#include <linux/string.h>
  38#include <linux/dmi.h>
  39#include <acpi/video.h>
  40#include "dell-smbios.h"
  41
  42MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
  43MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
  44MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
  45MODULE_LICENSE("GPL");
  46
  47#define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492"
  48#define DELL_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
  49
  50static u32 dell_wmi_interface_version;
  51static bool wmi_requires_smbios_request;
  52
  53MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
  54MODULE_ALIAS("wmi:"DELL_DESCRIPTOR_GUID);
  55
  56static int __init dmi_matched(const struct dmi_system_id *dmi)
  57{
  58        wmi_requires_smbios_request = 1;
  59        return 1;
  60}
  61
  62static const struct dmi_system_id dell_wmi_smbios_list[] __initconst = {
  63        {
  64                .callback = dmi_matched,
  65                .ident = "Dell Inspiron M5110",
  66                .matches = {
  67                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  68                        DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"),
  69                },
  70        },
  71        {
  72                .callback = dmi_matched,
  73                .ident = "Dell Vostro V131",
  74                .matches = {
  75                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  76                        DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
  77                },
  78        },
  79        { }
  80};
  81
  82/*
  83 * Certain keys are flagged as KE_IGNORE. All of these are either
  84 * notifications (rather than requests for change) or are also sent
  85 * via the keyboard controller so should not be sent again.
  86 */
  87
  88static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
  89        { KE_IGNORE, 0x003a, { KEY_CAPSLOCK } },
  90
  91        { KE_KEY, 0xe045, { KEY_PROG1 } },
  92        { KE_KEY, 0xe009, { KEY_EJECTCD } },
  93
  94        /* These also contain the brightness level at offset 6 */
  95        { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
  96        { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
  97
  98        /* Battery health status button */
  99        { KE_KEY, 0xe007, { KEY_BATTERY } },
 100
 101        /* Radio devices state change */
 102        { KE_IGNORE, 0xe008, { KEY_RFKILL } },
 103
 104        /* The next device is at offset 6, the active devices are at
 105           offset 8 and the attached devices at offset 10 */
 106        { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
 107
 108        { KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } },
 109
 110        /* BIOS error detected */
 111        { KE_IGNORE, 0xe00d, { KEY_RESERVED } },
 112
 113        /* Wifi Catcher */
 114        { KE_KEY, 0xe011, {KEY_PROG2 } },
 115
 116        /* Ambient light sensor toggle */
 117        { KE_IGNORE, 0xe013, { KEY_RESERVED } },
 118
 119        { KE_IGNORE, 0xe020, { KEY_MUTE } },
 120
 121        /* Dell Instant Launch key */
 122        { KE_KEY, 0xe025, { KEY_PROG4 } },
 123        { KE_KEY, 0xe029, { KEY_PROG4 } },
 124
 125        /* Audio panel key */
 126        { KE_IGNORE, 0xe026, { KEY_RESERVED } },
 127
 128        { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
 129        { KE_IGNORE, 0xe030, { KEY_VOLUMEUP } },
 130        { KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } },
 131        { KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } },
 132        { KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } },
 133        { KE_IGNORE, 0xe045, { KEY_NUMLOCK } },
 134        { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } },
 135        { KE_IGNORE, 0xe0f7, { KEY_MUTE } },
 136        { KE_IGNORE, 0xe0f8, { KEY_VOLUMEDOWN } },
 137        { KE_IGNORE, 0xe0f9, { KEY_VOLUMEUP } },
 138        { KE_END, 0 }
 139};
 140
 141static bool dell_new_hk_type;
 142
 143struct dell_bios_keymap_entry {
 144        u16 scancode;
 145        u16 keycode;
 146};
 147
 148struct dell_bios_hotkey_table {
 149        struct dmi_header header;
 150        struct dell_bios_keymap_entry keymap[];
 151
 152};
 153
 154struct dell_dmi_results {
 155        int err;
 156        struct key_entry *keymap;
 157};
 158
 159/* Uninitialized entries here are KEY_RESERVED == 0. */
 160static const u16 bios_to_linux_keycode[256] __initconst = {
 161        [0]     = KEY_MEDIA,
 162        [1]     = KEY_NEXTSONG,
 163        [2]     = KEY_PLAYPAUSE,
 164        [3]     = KEY_PREVIOUSSONG,
 165        [4]     = KEY_STOPCD,
 166        [5]     = KEY_UNKNOWN,
 167        [6]     = KEY_UNKNOWN,
 168        [7]     = KEY_UNKNOWN,
 169        [8]     = KEY_WWW,
 170        [9]     = KEY_UNKNOWN,
 171        [10]    = KEY_VOLUMEDOWN,
 172        [11]    = KEY_MUTE,
 173        [12]    = KEY_VOLUMEUP,
 174        [13]    = KEY_UNKNOWN,
 175        [14]    = KEY_BATTERY,
 176        [15]    = KEY_EJECTCD,
 177        [16]    = KEY_UNKNOWN,
 178        [17]    = KEY_SLEEP,
 179        [18]    = KEY_PROG1,
 180        [19]    = KEY_BRIGHTNESSDOWN,
 181        [20]    = KEY_BRIGHTNESSUP,
 182        [21]    = KEY_UNKNOWN,
 183        [22]    = KEY_KBDILLUMTOGGLE,
 184        [23]    = KEY_UNKNOWN,
 185        [24]    = KEY_SWITCHVIDEOMODE,
 186        [25]    = KEY_UNKNOWN,
 187        [26]    = KEY_UNKNOWN,
 188        [27]    = KEY_SWITCHVIDEOMODE,
 189        [28]    = KEY_UNKNOWN,
 190        [29]    = KEY_UNKNOWN,
 191        [30]    = KEY_PROG2,
 192        [31]    = KEY_UNKNOWN,
 193        [32]    = KEY_UNKNOWN,
 194        [33]    = KEY_UNKNOWN,
 195        [34]    = KEY_UNKNOWN,
 196        [35]    = KEY_UNKNOWN,
 197        [36]    = KEY_UNKNOWN,
 198        [37]    = KEY_UNKNOWN,
 199        [38]    = KEY_MICMUTE,
 200        [255]   = KEY_PROG3,
 201};
 202
 203/*
 204 * These are applied if the 0xB2 DMI hotkey table is present and doesn't
 205 * override them.
 206 */
 207static const struct key_entry dell_wmi_extra_keymap[] __initconst = {
 208        /* Fn-lock */
 209        { KE_IGNORE, 0x151, { KEY_RESERVED } },
 210
 211        /* Change keyboard illumination */
 212        { KE_IGNORE, 0x152, { KEY_KBDILLUMTOGGLE } },
 213
 214        /*
 215         * Radio disable (notify only -- there is no model for which the
 216         * WMI event is supposed to trigger an action).
 217         */
 218        { KE_IGNORE, 0x153, { KEY_RFKILL } },
 219
 220        /* RGB keyboard backlight control */
 221        { KE_IGNORE, 0x154, { KEY_RESERVED } },
 222
 223        /* Stealth mode toggle */
 224        { KE_IGNORE, 0x155, { KEY_RESERVED } },
 225};
 226
 227static struct input_dev *dell_wmi_input_dev;
 228
 229static void dell_wmi_process_key(int reported_key)
 230{
 231        const struct key_entry *key;
 232
 233        key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
 234                                                reported_key);
 235        if (!key) {
 236                pr_info("Unknown key with scancode 0x%x pressed\n",
 237                        reported_key);
 238                return;
 239        }
 240
 241        pr_debug("Key %x pressed\n", reported_key);
 242
 243        /* Don't report brightness notifications that will also come via ACPI */
 244        if ((key->keycode == KEY_BRIGHTNESSUP ||
 245             key->keycode == KEY_BRIGHTNESSDOWN) &&
 246            acpi_video_handles_brightness_key_presses())
 247                return;
 248
 249        if (reported_key == 0xe025 && !wmi_requires_smbios_request)
 250                return;
 251
 252        sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
 253}
 254
 255static void dell_wmi_notify(u32 value, void *context)
 256{
 257        struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
 258        union acpi_object *obj;
 259        acpi_status status;
 260        acpi_size buffer_size;
 261        u16 *buffer_entry, *buffer_end;
 262        int len, i;
 263
 264        status = wmi_get_event_data(value, &response);
 265        if (status != AE_OK) {
 266                pr_warn("bad event status 0x%x\n", status);
 267                return;
 268        }
 269
 270        obj = (union acpi_object *)response.pointer;
 271        if (!obj) {
 272                pr_warn("no response\n");
 273                return;
 274        }
 275
 276        if (obj->type != ACPI_TYPE_BUFFER) {
 277                pr_warn("bad response type %x\n", obj->type);
 278                kfree(obj);
 279                return;
 280        }
 281
 282        pr_debug("Received WMI event (%*ph)\n",
 283                obj->buffer.length, obj->buffer.pointer);
 284
 285        buffer_entry = (u16 *)obj->buffer.pointer;
 286        buffer_size = obj->buffer.length/2;
 287
 288        if (!dell_new_hk_type) {
 289                if (buffer_size >= 3 && buffer_entry[1] == 0x0)
 290                        dell_wmi_process_key(buffer_entry[2]);
 291                else if (buffer_size >= 2)
 292                        dell_wmi_process_key(buffer_entry[1]);
 293                else
 294                        pr_info("Received unknown WMI event\n");
 295                kfree(obj);
 296                return;
 297        }
 298
 299        buffer_end = buffer_entry + buffer_size;
 300
 301        /*
 302         * BIOS/ACPI on devices with WMI interface version 0 does not clear
 303         * buffer before filling it. So next time when BIOS/ACPI send WMI event
 304         * which is smaller as previous then it contains garbage in buffer from
 305         * previous event.
 306         *
 307         * BIOS/ACPI on devices with WMI interface version 1 clears buffer and
 308         * sometimes send more events in buffer at one call.
 309         *
 310         * So to prevent reading garbage from buffer we will process only first
 311         * one event on devices with WMI interface version 0.
 312         */
 313        if (dell_wmi_interface_version == 0 && buffer_entry < buffer_end)
 314                if (buffer_end > buffer_entry + buffer_entry[0] + 1)
 315                        buffer_end = buffer_entry + buffer_entry[0] + 1;
 316
 317        while (buffer_entry < buffer_end) {
 318
 319                len = buffer_entry[0];
 320                if (len == 0)
 321                        break;
 322
 323                len++;
 324
 325                if (buffer_entry + len > buffer_end) {
 326                        pr_warn("Invalid length of WMI event\n");
 327                        break;
 328                }
 329
 330                pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry);
 331
 332                switch (buffer_entry[1]) {
 333                case 0x00:
 334                        for (i = 2; i < len; ++i) {
 335                                switch (buffer_entry[i]) {
 336                                case 0xe043:
 337                                        /* NIC Link is Up */
 338                                        pr_debug("NIC Link is Up\n");
 339                                        break;
 340                                case 0xe044:
 341                                        /* NIC Link is Down */
 342                                        pr_debug("NIC Link is Down\n");
 343                                        break;
 344                                case 0xe045:
 345                                        /* Unknown event but defined in DSDT */
 346                                default:
 347                                        /* Unknown event */
 348                                        pr_info("Unknown WMI event type 0x00: "
 349                                                "0x%x\n", (int)buffer_entry[i]);
 350                                        break;
 351                                }
 352                        }
 353                        break;
 354                case 0x10:
 355                        /* Keys pressed */
 356                        for (i = 2; i < len; ++i)
 357                                dell_wmi_process_key(buffer_entry[i]);
 358                        break;
 359                case 0x11:
 360                        for (i = 2; i < len; ++i) {
 361                                switch (buffer_entry[i]) {
 362                                case 0xfff0:
 363                                        /* Battery unplugged */
 364                                        pr_debug("Battery unplugged\n");
 365                                        break;
 366                                case 0xfff1:
 367                                        /* Battery inserted */
 368                                        pr_debug("Battery inserted\n");
 369                                        break;
 370                                case 0x01e1:
 371                                case 0x02ea:
 372                                case 0x02eb:
 373                                case 0x02ec:
 374                                case 0x02f6:
 375                                        /* Keyboard backlight level changed */
 376                                        pr_debug("Keyboard backlight level "
 377                                                 "changed\n");
 378                                        break;
 379                                default:
 380                                        /* Unknown event */
 381                                        pr_info("Unknown WMI event type 0x11: "
 382                                                "0x%x\n", (int)buffer_entry[i]);
 383                                        break;
 384                                }
 385                        }
 386                        break;
 387                default:
 388                        /* Unknown event */
 389                        pr_info("Unknown WMI event type 0x%x\n",
 390                                (int)buffer_entry[1]);
 391                        break;
 392                }
 393
 394                buffer_entry += len;
 395
 396        }
 397
 398        kfree(obj);
 399}
 400
 401static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len)
 402{
 403        int i;
 404
 405        for (i = 0; i < len; i++)
 406                if (keymap[i].code == scancode)
 407                        return true;
 408
 409        return false;
 410}
 411
 412static void __init handle_dmi_entry(const struct dmi_header *dm,
 413
 414                                    void *opaque)
 415
 416{
 417        struct dell_dmi_results *results = opaque;
 418        struct dell_bios_hotkey_table *table;
 419        int hotkey_num, i, pos = 0;
 420        struct key_entry *keymap;
 421        int num_bios_keys;
 422
 423        if (results->err || results->keymap)
 424                return;         /* We already found the hotkey table. */
 425
 426        if (dm->type != 0xb2)
 427                return;
 428
 429        table = container_of(dm, struct dell_bios_hotkey_table, header);
 430
 431        hotkey_num = (table->header.length -
 432                      sizeof(struct dell_bios_hotkey_table)) /
 433                                sizeof(struct dell_bios_keymap_entry);
 434        if (hotkey_num < 1) {
 435                /*
 436                 * Historically, dell-wmi would ignore a DMI entry of
 437                 * fewer than 7 bytes.  Sizes between 4 and 8 bytes are
 438                 * nonsensical (both the header and all entries are 4
 439                 * bytes), so we approximate the old behavior by
 440                 * ignoring tables with fewer than one entry.
 441                 */
 442                return;
 443        }
 444
 445        keymap = kcalloc(hotkey_num + ARRAY_SIZE(dell_wmi_extra_keymap) + 1,
 446                         sizeof(struct key_entry), GFP_KERNEL);
 447        if (!keymap) {
 448                results->err = -ENOMEM;
 449                return;
 450        }
 451
 452        for (i = 0; i < hotkey_num; i++) {
 453                const struct dell_bios_keymap_entry *bios_entry =
 454                                        &table->keymap[i];
 455
 456                /* Uninitialized entries are 0 aka KEY_RESERVED. */
 457                u16 keycode = (bios_entry->keycode <
 458                               ARRAY_SIZE(bios_to_linux_keycode)) ?
 459                        bios_to_linux_keycode[bios_entry->keycode] :
 460                        KEY_RESERVED;
 461
 462                /*
 463                 * Log if we find an entry in the DMI table that we don't
 464                 * understand.  If this happens, we should figure out what
 465                 * the entry means and add it to bios_to_linux_keycode.
 466                 */
 467                if (keycode == KEY_RESERVED) {
 468                        pr_info("firmware scancode 0x%x maps to unrecognized keycode 0x%x\n",
 469                                bios_entry->scancode, bios_entry->keycode);
 470                        continue;
 471                }
 472
 473                if (keycode == KEY_KBDILLUMTOGGLE)
 474                        keymap[pos].type = KE_IGNORE;
 475                else
 476                        keymap[pos].type = KE_KEY;
 477                keymap[pos].code = bios_entry->scancode;
 478                keymap[pos].keycode = keycode;
 479
 480                pos++;
 481        }
 482
 483        num_bios_keys = pos;
 484
 485        for (i = 0; i < ARRAY_SIZE(dell_wmi_extra_keymap); i++) {
 486                const struct key_entry *entry = &dell_wmi_extra_keymap[i];
 487
 488                /*
 489                 * Check if we've already found this scancode.  This takes
 490                 * quadratic time, but it doesn't matter unless the list
 491                 * of extra keys gets very long.
 492                 */
 493                if (!have_scancode(entry->code, keymap, num_bios_keys)) {
 494                        keymap[pos] = *entry;
 495                        pos++;
 496                }
 497        }
 498
 499        keymap[pos].type = KE_END;
 500
 501        results->keymap = keymap;
 502}
 503
 504static int __init dell_wmi_input_setup(void)
 505{
 506        struct dell_dmi_results dmi_results = {};
 507        int err;
 508
 509        dell_wmi_input_dev = input_allocate_device();
 510        if (!dell_wmi_input_dev)
 511                return -ENOMEM;
 512
 513        dell_wmi_input_dev->name = "Dell WMI hotkeys";
 514        dell_wmi_input_dev->phys = "wmi/input0";
 515        dell_wmi_input_dev->id.bustype = BUS_HOST;
 516
 517        if (dmi_walk(handle_dmi_entry, &dmi_results)) {
 518                /*
 519                 * Historically, dell-wmi ignored dmi_walk errors.  A failure
 520                 * is certainly surprising, but it probably just indicates
 521                 * a very old laptop.
 522                 */
 523                pr_warn("no DMI; using the old-style hotkey interface\n");
 524        }
 525
 526        if (dmi_results.err) {
 527                err = dmi_results.err;
 528                goto err_free_dev;
 529        }
 530
 531        if (dmi_results.keymap) {
 532                dell_new_hk_type = true;
 533
 534                err = sparse_keymap_setup(dell_wmi_input_dev,
 535                                          dmi_results.keymap, NULL);
 536
 537                /*
 538                 * Sparse keymap library makes a copy of keymap so we
 539                 * don't need the original one that was allocated.
 540                 */
 541                kfree(dmi_results.keymap);
 542        } else {
 543                err = sparse_keymap_setup(dell_wmi_input_dev,
 544                                          dell_wmi_legacy_keymap, NULL);
 545        }
 546        if (err)
 547                goto err_free_dev;
 548
 549        err = input_register_device(dell_wmi_input_dev);
 550        if (err)
 551                goto err_free_keymap;
 552
 553        return 0;
 554
 555 err_free_keymap:
 556        sparse_keymap_free(dell_wmi_input_dev);
 557 err_free_dev:
 558        input_free_device(dell_wmi_input_dev);
 559        return err;
 560}
 561
 562static void dell_wmi_input_destroy(void)
 563{
 564        sparse_keymap_free(dell_wmi_input_dev);
 565        input_unregister_device(dell_wmi_input_dev);
 566}
 567
 568/*
 569 * Descriptor buffer is 128 byte long and contains:
 570 *
 571 *       Name             Offset  Length  Value
 572 * Vendor Signature          0       4    "DELL"
 573 * Object Signature          4       4    " WMI"
 574 * WMI Interface Version     8       4    <version>
 575 * WMI buffer length        12       4    4096
 576 */
 577static int __init dell_wmi_check_descriptor_buffer(void)
 578{
 579        struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
 580        union acpi_object *obj;
 581        acpi_status status;
 582        u32 *buffer;
 583
 584        status = wmi_query_block(DELL_DESCRIPTOR_GUID, 0, &out);
 585        if (ACPI_FAILURE(status)) {
 586                pr_err("Cannot read Dell descriptor buffer - %d\n", status);
 587                return status;
 588        }
 589
 590        obj = (union acpi_object *)out.pointer;
 591        if (!obj) {
 592                pr_err("Dell descriptor buffer is empty\n");
 593                return -EINVAL;
 594        }
 595
 596        if (obj->type != ACPI_TYPE_BUFFER) {
 597                pr_err("Cannot read Dell descriptor buffer\n");
 598                kfree(obj);
 599                return -EINVAL;
 600        }
 601
 602        if (obj->buffer.length != 128) {
 603                pr_err("Dell descriptor buffer has invalid length (%d)\n",
 604                        obj->buffer.length);
 605                if (obj->buffer.length < 16) {
 606                        kfree(obj);
 607                        return -EINVAL;
 608                }
 609        }
 610
 611        buffer = (u32 *)obj->buffer.pointer;
 612
 613        if (buffer[0] != 0x4C4C4544 && buffer[1] != 0x494D5720)
 614                pr_warn("Dell descriptor buffer has invalid signature (%*ph)\n",
 615                        8, buffer);
 616
 617        if (buffer[2] != 0 && buffer[2] != 1)
 618                pr_warn("Dell descriptor buffer has unknown version (%d)\n",
 619                        buffer[2]);
 620
 621        if (buffer[3] != 4096)
 622                pr_warn("Dell descriptor buffer has invalid buffer length (%d)\n",
 623                        buffer[3]);
 624
 625        dell_wmi_interface_version = buffer[2];
 626
 627        pr_info("Detected Dell WMI interface version %u\n",
 628                dell_wmi_interface_version);
 629
 630        kfree(obj);
 631        return 0;
 632}
 633
 634/*
 635 * According to Dell SMBIOS documentation:
 636 *
 637 * 17  3  Application Program Registration
 638 *
 639 *     cbArg1 Application ID 1 = 0x00010000
 640 *     cbArg2 Application ID 2
 641 *            QUICKSET/DCP = 0x51534554 "QSET"
 642 *            ALS Driver   = 0x416c7353 "AlsS"
 643 *            Latitude ON  = 0x4c6f6e52 "LonR"
 644 *     cbArg3 Application version or revision number
 645 *     cbArg4 0 = Unregister application
 646 *            1 = Register application
 647 *     cbRes1 Standard return codes (0, -1, -2)
 648 */
 649
 650static int dell_wmi_events_set_enabled(bool enable)
 651{
 652        struct calling_interface_buffer *buffer;
 653        int ret;
 654
 655        buffer = dell_smbios_get_buffer();
 656        buffer->input[0] = 0x10000;
 657        buffer->input[1] = 0x51534554;
 658        buffer->input[3] = enable;
 659        dell_smbios_send_request(17, 3);
 660        ret = buffer->output[0];
 661        dell_smbios_release_buffer();
 662
 663        return dell_smbios_error(ret);
 664}
 665
 666static int __init dell_wmi_init(void)
 667{
 668        int err;
 669        acpi_status status;
 670
 671        if (!wmi_has_guid(DELL_EVENT_GUID) ||
 672            !wmi_has_guid(DELL_DESCRIPTOR_GUID)) {
 673                pr_warn("Dell WMI GUID were not found\n");
 674                return -ENODEV;
 675        }
 676
 677        err = dell_wmi_check_descriptor_buffer();
 678        if (err)
 679                return err;
 680
 681        err = dell_wmi_input_setup();
 682        if (err)
 683                return err;
 684
 685        status = wmi_install_notify_handler(DELL_EVENT_GUID,
 686                                         dell_wmi_notify, NULL);
 687        if (ACPI_FAILURE(status)) {
 688                dell_wmi_input_destroy();
 689                pr_err("Unable to register notify handler - %d\n", status);
 690                return -ENODEV;
 691        }
 692
 693        dmi_check_system(dell_wmi_smbios_list);
 694
 695        if (wmi_requires_smbios_request) {
 696                err = dell_wmi_events_set_enabled(true);
 697                if (err) {
 698                        pr_err("Failed to enable WMI events\n");
 699                        wmi_remove_notify_handler(DELL_EVENT_GUID);
 700                        dell_wmi_input_destroy();
 701                        return err;
 702                }
 703        }
 704
 705        return 0;
 706}
 707module_init(dell_wmi_init);
 708
 709static void __exit dell_wmi_exit(void)
 710{
 711        if (wmi_requires_smbios_request)
 712                dell_wmi_events_set_enabled(false);
 713        wmi_remove_notify_handler(DELL_EVENT_GUID);
 714        dell_wmi_input_destroy();
 715}
 716module_exit(dell_wmi_exit);
 717