linux/drivers/platform/x86/huawei-wmi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 *  Huawei WMI laptop extras driver
   4 *
   5 *  Copyright (C) 2018        Ayman Bagabas <ayman.bagabas@gmail.com>
   6 */
   7
   8#include <linux/acpi.h>
   9#include <linux/debugfs.h>
  10#include <linux/delay.h>
  11#include <linux/dmi.h>
  12#include <linux/input.h>
  13#include <linux/input/sparse-keymap.h>
  14#include <linux/leds.h>
  15#include <linux/module.h>
  16#include <linux/mutex.h>
  17#include <linux/platform_device.h>
  18#include <linux/power_supply.h>
  19#include <linux/sysfs.h>
  20#include <linux/wmi.h>
  21#include <acpi/battery.h>
  22
  23/*
  24 * Huawei WMI GUIDs
  25 */
  26#define HWMI_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C90629100000"
  27#define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C90629100000"
  28
  29/* Legacy GUIDs */
  30#define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
  31#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
  32
  33/* HWMI commands */
  34
  35enum {
  36        BATTERY_THRESH_GET              = 0x00001103, /* \GBTT */
  37        BATTERY_THRESH_SET              = 0x00001003, /* \SBTT */
  38        FN_LOCK_GET                     = 0x00000604, /* \GFRS */
  39        FN_LOCK_SET                     = 0x00000704, /* \SFRS */
  40        MICMUTE_LED_SET                 = 0x00000b04, /* \SMLS */
  41};
  42
  43union hwmi_arg {
  44        u64 cmd;
  45        u8 args[8];
  46};
  47
  48struct quirk_entry {
  49        bool battery_reset;
  50        bool ec_micmute;
  51        bool report_brightness;
  52};
  53
  54static struct quirk_entry *quirks;
  55
  56struct huawei_wmi_debug {
  57        struct dentry *root;
  58        u64 arg;
  59};
  60
  61struct huawei_wmi {
  62        bool battery_available;
  63        bool fn_lock_available;
  64
  65        struct huawei_wmi_debug debug;
  66        struct input_dev *idev[2];
  67        struct led_classdev cdev;
  68        struct device *dev;
  69
  70        struct mutex wmi_lock;
  71};
  72
  73static struct huawei_wmi *huawei_wmi;
  74
  75static const struct key_entry huawei_wmi_keymap[] = {
  76        { KE_KEY,    0x281, { KEY_BRIGHTNESSDOWN } },
  77        { KE_KEY,    0x282, { KEY_BRIGHTNESSUP } },
  78        { KE_KEY,    0x284, { KEY_MUTE } },
  79        { KE_KEY,    0x285, { KEY_VOLUMEDOWN } },
  80        { KE_KEY,    0x286, { KEY_VOLUMEUP } },
  81        { KE_KEY,    0x287, { KEY_MICMUTE } },
  82        { KE_KEY,    0x289, { KEY_WLAN } },
  83        // Huawei |M| key
  84        { KE_KEY,    0x28a, { KEY_CONFIG } },
  85        // Keyboard backlit
  86        { KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
  87        { KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
  88        { KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
  89        { KE_END,        0 }
  90};
  91
  92static int battery_reset = -1;
  93static int report_brightness = -1;
  94
  95module_param(battery_reset, bint, 0444);
  96MODULE_PARM_DESC(battery_reset,
  97                "Reset battery charge values to (0-0) before disabling it using (0-100)");
  98module_param(report_brightness, bint, 0444);
  99MODULE_PARM_DESC(report_brightness,
 100                "Report brightness keys.");
 101
 102/* Quirks */
 103
 104static int __init dmi_matched(const struct dmi_system_id *dmi)
 105{
 106        quirks = dmi->driver_data;
 107        return 1;
 108}
 109
 110static struct quirk_entry quirk_unknown = {
 111};
 112
 113static struct quirk_entry quirk_battery_reset = {
 114        .battery_reset = true,
 115};
 116
 117static struct quirk_entry quirk_matebook_x = {
 118        .ec_micmute = true,
 119        .report_brightness = true,
 120};
 121
 122static const struct dmi_system_id huawei_quirks[] = {
 123        {
 124                .callback = dmi_matched,
 125                .ident = "Huawei MACH-WX9",
 126                .matches = {
 127                        DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
 128                        DMI_MATCH(DMI_PRODUCT_NAME, "MACH-WX9"),
 129                },
 130                .driver_data = &quirk_battery_reset
 131        },
 132        {
 133                .callback = dmi_matched,
 134                .ident = "Huawei MateBook X",
 135                .matches = {
 136                        DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
 137                        DMI_MATCH(DMI_PRODUCT_NAME, "HUAWEI MateBook X")
 138                },
 139                .driver_data = &quirk_matebook_x
 140        },
 141        {  }
 142};
 143
 144/* Utils */
 145
 146static int huawei_wmi_call(struct huawei_wmi *huawei,
 147                           struct acpi_buffer *in, struct acpi_buffer *out)
 148{
 149        acpi_status status;
 150
 151        mutex_lock(&huawei->wmi_lock);
 152        status = wmi_evaluate_method(HWMI_METHOD_GUID, 0, 1, in, out);
 153        mutex_unlock(&huawei->wmi_lock);
 154        if (ACPI_FAILURE(status)) {
 155                dev_err(huawei->dev, "Failed to evaluate wmi method\n");
 156                return -ENODEV;
 157        }
 158
 159        return 0;
 160}
 161
 162/* HWMI takes a 64 bit input and returns either a package with 2 buffers, one of
 163 * 4 bytes and the other of 256 bytes, or one buffer of size 0x104 (260) bytes.
 164 * The first 4 bytes are ignored, we ignore the first 4 bytes buffer if we got a
 165 * package, or skip the first 4 if a buffer of 0x104 is used. The first byte of
 166 * the remaining 0x100 sized buffer has the return status of every call. In case
 167 * the return status is non-zero, we return -ENODEV but still copy the returned
 168 * buffer to the given buffer parameter (buf).
 169 */
 170static int huawei_wmi_cmd(u64 arg, u8 *buf, size_t buflen)
 171{
 172        struct huawei_wmi *huawei = huawei_wmi;
 173        struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
 174        struct acpi_buffer in;
 175        union acpi_object *obj;
 176        size_t len;
 177        int err, i;
 178
 179        in.length = sizeof(arg);
 180        in.pointer = &arg;
 181
 182        /* Some models require calling HWMI twice to execute a command. We evaluate
 183         * HWMI and if we get a non-zero return status we evaluate it again.
 184         */
 185        for (i = 0; i < 2; i++) {
 186                err = huawei_wmi_call(huawei, &in, &out);
 187                if (err)
 188                        goto fail_cmd;
 189
 190                obj = out.pointer;
 191                if (!obj) {
 192                        err = -EIO;
 193                        goto fail_cmd;
 194                }
 195
 196                switch (obj->type) {
 197                /* Models that implement both "legacy" and HWMI tend to return a 0x104
 198                 * sized buffer instead of a package of 0x4 and 0x100 buffers.
 199                 */
 200                case ACPI_TYPE_BUFFER:
 201                        if (obj->buffer.length == 0x104) {
 202                                // Skip the first 4 bytes.
 203                                obj->buffer.pointer += 4;
 204                                len = 0x100;
 205                        } else {
 206                                dev_err(huawei->dev, "Bad buffer length, got %d\n", obj->buffer.length);
 207                                err = -EIO;
 208                                goto fail_cmd;
 209                        }
 210
 211                        break;
 212                /* HWMI returns a package with 2 buffer elements, one of 4 bytes and the
 213                 * other is 256 bytes.
 214                 */
 215                case ACPI_TYPE_PACKAGE:
 216                        if (obj->package.count != 2) {
 217                                dev_err(huawei->dev, "Bad package count, got %d\n", obj->package.count);
 218                                err = -EIO;
 219                                goto fail_cmd;
 220                        }
 221
 222                        obj = &obj->package.elements[1];
 223                        if (obj->type != ACPI_TYPE_BUFFER) {
 224                                dev_err(huawei->dev, "Bad package element type, got %d\n", obj->type);
 225                                err = -EIO;
 226                                goto fail_cmd;
 227                        }
 228                        len = obj->buffer.length;
 229
 230                        break;
 231                /* Shouldn't get here! */
 232                default:
 233                        dev_err(huawei->dev, "Unexpected obj type, got: %d\n", obj->type);
 234                        err = -EIO;
 235                        goto fail_cmd;
 236                }
 237
 238                if (!*obj->buffer.pointer)
 239                        break;
 240        }
 241
 242        err = (*obj->buffer.pointer) ? -ENODEV : 0;
 243
 244        if (buf) {
 245                len = min(buflen, len);
 246                memcpy(buf, obj->buffer.pointer, len);
 247        }
 248
 249fail_cmd:
 250        kfree(out.pointer);
 251        return err;
 252}
 253
 254/* LEDs */
 255
 256static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
 257                enum led_brightness brightness)
 258{
 259        /* This is a workaround until the "legacy" interface is implemented. */
 260        if (quirks && quirks->ec_micmute) {
 261                char *acpi_method;
 262                acpi_handle handle;
 263                acpi_status status;
 264                union acpi_object args[3];
 265                struct acpi_object_list arg_list = {
 266                        .pointer = args,
 267                        .count = ARRAY_SIZE(args),
 268                };
 269
 270                handle = ec_get_handle();
 271                if (!handle)
 272                        return -ENODEV;
 273
 274                args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
 275                args[1].integer.value = 0x04;
 276
 277                if (acpi_has_method(handle, "SPIN")) {
 278                        acpi_method = "SPIN";
 279                        args[0].integer.value = 0;
 280                        args[2].integer.value = brightness ? 1 : 0;
 281                } else if (acpi_has_method(handle, "WPIN")) {
 282                        acpi_method = "WPIN";
 283                        args[0].integer.value = 1;
 284                        args[2].integer.value = brightness ? 0 : 1;
 285                } else {
 286                        return -ENODEV;
 287                }
 288
 289                status = acpi_evaluate_object(handle, acpi_method, &arg_list, NULL);
 290                if (ACPI_FAILURE(status))
 291                        return -ENODEV;
 292
 293                return 0;
 294        } else {
 295                union hwmi_arg arg;
 296
 297                arg.cmd = MICMUTE_LED_SET;
 298                arg.args[2] = brightness;
 299
 300                return huawei_wmi_cmd(arg.cmd, NULL, 0);
 301        }
 302}
 303
 304static void huawei_wmi_leds_setup(struct device *dev)
 305{
 306        struct huawei_wmi *huawei = dev_get_drvdata(dev);
 307
 308        huawei->cdev.name = "platform::micmute";
 309        huawei->cdev.max_brightness = 1;
 310        huawei->cdev.brightness_set_blocking = &huawei_wmi_micmute_led_set;
 311        huawei->cdev.default_trigger = "audio-micmute";
 312        huawei->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
 313        huawei->cdev.dev = dev;
 314        huawei->cdev.flags = LED_CORE_SUSPENDRESUME;
 315
 316        devm_led_classdev_register(dev, &huawei->cdev);
 317}
 318
 319/* Battery protection */
 320
 321static int huawei_wmi_battery_get(int *start, int *end)
 322{
 323        u8 ret[0x100];
 324        int err, i;
 325
 326        err = huawei_wmi_cmd(BATTERY_THRESH_GET, ret, 0x100);
 327        if (err)
 328                return err;
 329
 330        /* Find the last two non-zero values. Return status is ignored. */
 331        i = 0xff;
 332        do {
 333                if (start)
 334                        *start = ret[i-1];
 335                if (end)
 336                        *end = ret[i];
 337        } while (i > 2 && !ret[i--]);
 338
 339        return 0;
 340}
 341
 342static int huawei_wmi_battery_set(int start, int end)
 343{
 344        union hwmi_arg arg;
 345        int err;
 346
 347        if (start < 0 || end < 0 || start > 100 || end > 100)
 348                return -EINVAL;
 349
 350        arg.cmd = BATTERY_THRESH_SET;
 351        arg.args[2] = start;
 352        arg.args[3] = end;
 353
 354        /* This is an edge case were some models turn battery protection
 355         * off without changing their thresholds values. We clear the
 356         * values before turning off protection. Sometimes we need a sleep delay to
 357         * make sure these values make their way to EC memory.
 358         */
 359        if (quirks && quirks->battery_reset && start == 0 && end == 100) {
 360                err = huawei_wmi_battery_set(0, 0);
 361                if (err)
 362                        return err;
 363
 364                msleep(1000);
 365        }
 366
 367        err = huawei_wmi_cmd(arg.cmd, NULL, 0);
 368
 369        return err;
 370}
 371
 372static ssize_t charge_control_start_threshold_show(struct device *dev,
 373                struct device_attribute *attr,
 374                char *buf)
 375{
 376        int err, start;
 377
 378        err = huawei_wmi_battery_get(&start, NULL);
 379        if (err)
 380                return err;
 381
 382        return sprintf(buf, "%d\n", start);
 383}
 384
 385static ssize_t charge_control_end_threshold_show(struct device *dev,
 386                struct device_attribute *attr,
 387                char *buf)
 388{
 389        int err, end;
 390
 391        err = huawei_wmi_battery_get(NULL, &end);
 392        if (err)
 393                return err;
 394
 395        return sprintf(buf, "%d\n", end);
 396}
 397
 398static ssize_t charge_control_thresholds_show(struct device *dev,
 399                struct device_attribute *attr,
 400                char *buf)
 401{
 402        int err, start, end;
 403
 404        err = huawei_wmi_battery_get(&start, &end);
 405        if (err)
 406                return err;
 407
 408        return sprintf(buf, "%d %d\n", start, end);
 409}
 410
 411static ssize_t charge_control_start_threshold_store(struct device *dev,
 412                struct device_attribute *attr,
 413                const char *buf, size_t size)
 414{
 415        int err, start, end;
 416
 417        err = huawei_wmi_battery_get(NULL, &end);
 418        if (err)
 419                return err;
 420
 421        if (sscanf(buf, "%d", &start) != 1)
 422                return -EINVAL;
 423
 424        err = huawei_wmi_battery_set(start, end);
 425        if (err)
 426                return err;
 427
 428        return size;
 429}
 430
 431static ssize_t charge_control_end_threshold_store(struct device *dev,
 432                struct device_attribute *attr,
 433                const char *buf, size_t size)
 434{
 435        int err, start, end;
 436
 437        err = huawei_wmi_battery_get(&start, NULL);
 438        if (err)
 439                return err;
 440
 441        if (sscanf(buf, "%d", &end) != 1)
 442                return -EINVAL;
 443
 444        err = huawei_wmi_battery_set(start, end);
 445        if (err)
 446                return err;
 447
 448        return size;
 449}
 450
 451static ssize_t charge_control_thresholds_store(struct device *dev,
 452                struct device_attribute *attr,
 453                const char *buf, size_t size)
 454{
 455        int err, start, end;
 456
 457        if (sscanf(buf, "%d %d", &start, &end) != 2)
 458                return -EINVAL;
 459
 460        err = huawei_wmi_battery_set(start, end);
 461        if (err)
 462                return err;
 463
 464        return size;
 465}
 466
 467static DEVICE_ATTR_RW(charge_control_start_threshold);
 468static DEVICE_ATTR_RW(charge_control_end_threshold);
 469static DEVICE_ATTR_RW(charge_control_thresholds);
 470
 471static int huawei_wmi_battery_add(struct power_supply *battery)
 472{
 473        device_create_file(&battery->dev, &dev_attr_charge_control_start_threshold);
 474        device_create_file(&battery->dev, &dev_attr_charge_control_end_threshold);
 475
 476        return 0;
 477}
 478
 479static int huawei_wmi_battery_remove(struct power_supply *battery)
 480{
 481        device_remove_file(&battery->dev, &dev_attr_charge_control_start_threshold);
 482        device_remove_file(&battery->dev, &dev_attr_charge_control_end_threshold);
 483
 484        return 0;
 485}
 486
 487static struct acpi_battery_hook huawei_wmi_battery_hook = {
 488        .add_battery = huawei_wmi_battery_add,
 489        .remove_battery = huawei_wmi_battery_remove,
 490        .name = "Huawei Battery Extension"
 491};
 492
 493static void huawei_wmi_battery_setup(struct device *dev)
 494{
 495        struct huawei_wmi *huawei = dev_get_drvdata(dev);
 496
 497        huawei->battery_available = true;
 498        if (huawei_wmi_battery_get(NULL, NULL)) {
 499                huawei->battery_available = false;
 500                return;
 501        }
 502
 503        battery_hook_register(&huawei_wmi_battery_hook);
 504        device_create_file(dev, &dev_attr_charge_control_thresholds);
 505}
 506
 507static void huawei_wmi_battery_exit(struct device *dev)
 508{
 509        struct huawei_wmi *huawei = dev_get_drvdata(dev);
 510
 511        if (huawei->battery_available) {
 512                battery_hook_unregister(&huawei_wmi_battery_hook);
 513                device_remove_file(dev, &dev_attr_charge_control_thresholds);
 514        }
 515}
 516
 517/* Fn lock */
 518
 519static int huawei_wmi_fn_lock_get(int *on)
 520{
 521        u8 ret[0x100] = { 0 };
 522        int err, i;
 523
 524        err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
 525        if (err)
 526                return err;
 527
 528        /* Find the first non-zero value. Return status is ignored. */
 529        i = 1;
 530        do {
 531                if (on)
 532                        *on = ret[i] - 1; // -1 undefined, 0 off, 1 on.
 533        } while (i < 0xff && !ret[i++]);
 534
 535        return 0;
 536}
 537
 538static int huawei_wmi_fn_lock_set(int on)
 539{
 540        union hwmi_arg arg;
 541
 542        arg.cmd = FN_LOCK_SET;
 543        arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
 544
 545        return huawei_wmi_cmd(arg.cmd, NULL, 0);
 546}
 547
 548static ssize_t fn_lock_state_show(struct device *dev,
 549                struct device_attribute *attr,
 550                char *buf)
 551{
 552        int err, on;
 553
 554        err = huawei_wmi_fn_lock_get(&on);
 555        if (err)
 556                return err;
 557
 558        return sprintf(buf, "%d\n", on);
 559}
 560
 561static ssize_t fn_lock_state_store(struct device *dev,
 562                struct device_attribute *attr,
 563                const char *buf, size_t size)
 564{
 565        int on, err;
 566
 567        if (kstrtoint(buf, 10, &on) ||
 568                        on < 0 || on > 1)
 569                return -EINVAL;
 570
 571        err = huawei_wmi_fn_lock_set(on);
 572        if (err)
 573                return err;
 574
 575        return size;
 576}
 577
 578static DEVICE_ATTR_RW(fn_lock_state);
 579
 580static void huawei_wmi_fn_lock_setup(struct device *dev)
 581{
 582        struct huawei_wmi *huawei = dev_get_drvdata(dev);
 583
 584        huawei->fn_lock_available = true;
 585        if (huawei_wmi_fn_lock_get(NULL)) {
 586                huawei->fn_lock_available = false;
 587                return;
 588        }
 589
 590        device_create_file(dev, &dev_attr_fn_lock_state);
 591}
 592
 593static void huawei_wmi_fn_lock_exit(struct device *dev)
 594{
 595        struct huawei_wmi *huawei = dev_get_drvdata(dev);
 596
 597        if (huawei->fn_lock_available)
 598                device_remove_file(dev, &dev_attr_fn_lock_state);
 599}
 600
 601/* debugfs */
 602
 603static void huawei_wmi_debugfs_call_dump(struct seq_file *m, void *data,
 604                union acpi_object *obj)
 605{
 606        struct huawei_wmi *huawei = m->private;
 607        int i;
 608
 609        switch (obj->type) {
 610        case ACPI_TYPE_INTEGER:
 611                seq_printf(m, "0x%llx", obj->integer.value);
 612                break;
 613        case ACPI_TYPE_STRING:
 614                seq_printf(m, "\"%.*s\"", obj->string.length, obj->string.pointer);
 615                break;
 616        case ACPI_TYPE_BUFFER:
 617                seq_puts(m, "{");
 618                for (i = 0; i < obj->buffer.length; i++) {
 619                        seq_printf(m, "0x%02x", obj->buffer.pointer[i]);
 620                        if (i < obj->buffer.length - 1)
 621                                seq_puts(m, ",");
 622                }
 623                seq_puts(m, "}");
 624                break;
 625        case ACPI_TYPE_PACKAGE:
 626                seq_puts(m, "[");
 627                for (i = 0; i < obj->package.count; i++) {
 628                        huawei_wmi_debugfs_call_dump(m, huawei, &obj->package.elements[i]);
 629                        if (i < obj->package.count - 1)
 630                                seq_puts(m, ",");
 631                }
 632                seq_puts(m, "]");
 633                break;
 634        default:
 635                dev_err(huawei->dev, "Unexpected obj type, got %d\n", obj->type);
 636                return;
 637        }
 638}
 639
 640static int huawei_wmi_debugfs_call_show(struct seq_file *m, void *data)
 641{
 642        struct huawei_wmi *huawei = m->private;
 643        struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
 644        struct acpi_buffer in;
 645        union acpi_object *obj;
 646        int err;
 647
 648        in.length = sizeof(u64);
 649        in.pointer = &huawei->debug.arg;
 650
 651        err = huawei_wmi_call(huawei, &in, &out);
 652        if (err)
 653                return err;
 654
 655        obj = out.pointer;
 656        if (!obj) {
 657                err = -EIO;
 658                goto fail_debugfs_call;
 659        }
 660
 661        huawei_wmi_debugfs_call_dump(m, huawei, obj);
 662
 663fail_debugfs_call:
 664        kfree(out.pointer);
 665        return err;
 666}
 667
 668DEFINE_SHOW_ATTRIBUTE(huawei_wmi_debugfs_call);
 669
 670static void huawei_wmi_debugfs_setup(struct device *dev)
 671{
 672        struct huawei_wmi *huawei = dev_get_drvdata(dev);
 673
 674        huawei->debug.root = debugfs_create_dir("huawei-wmi", NULL);
 675
 676        debugfs_create_x64("arg", 0644, huawei->debug.root,
 677                &huawei->debug.arg);
 678        debugfs_create_file("call", 0400,
 679                huawei->debug.root, huawei, &huawei_wmi_debugfs_call_fops);
 680}
 681
 682static void huawei_wmi_debugfs_exit(struct device *dev)
 683{
 684        struct huawei_wmi *huawei = dev_get_drvdata(dev);
 685
 686        debugfs_remove_recursive(huawei->debug.root);
 687}
 688
 689/* Input */
 690
 691static void huawei_wmi_process_key(struct input_dev *idev, int code)
 692{
 693        const struct key_entry *key;
 694
 695        /*
 696         * WMI0 uses code 0x80 to indicate a hotkey event.
 697         * The actual key is fetched from the method WQ00
 698         * using WMI0_EXPENSIVE_GUID.
 699         */
 700        if (code == 0x80) {
 701                struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
 702                union acpi_object *obj;
 703                acpi_status status;
 704
 705                status = wmi_query_block(WMI0_EXPENSIVE_GUID, 0, &response);
 706                if (ACPI_FAILURE(status))
 707                        return;
 708
 709                obj = (union acpi_object *)response.pointer;
 710                if (obj && obj->type == ACPI_TYPE_INTEGER)
 711                        code = obj->integer.value;
 712
 713                kfree(response.pointer);
 714        }
 715
 716        key = sparse_keymap_entry_from_scancode(idev, code);
 717        if (!key) {
 718                dev_info(&idev->dev, "Unknown key pressed, code: 0x%04x\n", code);
 719                return;
 720        }
 721
 722        if (quirks && !quirks->report_brightness &&
 723                        (key->sw.code == KEY_BRIGHTNESSDOWN ||
 724                        key->sw.code == KEY_BRIGHTNESSUP))
 725                return;
 726
 727        sparse_keymap_report_entry(idev, key, 1, true);
 728}
 729
 730static void huawei_wmi_input_notify(u32 value, void *context)
 731{
 732        struct input_dev *idev = (struct input_dev *)context;
 733        struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
 734        union acpi_object *obj;
 735        acpi_status status;
 736
 737        status = wmi_get_event_data(value, &response);
 738        if (ACPI_FAILURE(status)) {
 739                dev_err(&idev->dev, "Unable to get event data\n");
 740                return;
 741        }
 742
 743        obj = (union acpi_object *)response.pointer;
 744        if (obj && obj->type == ACPI_TYPE_INTEGER)
 745                huawei_wmi_process_key(idev, obj->integer.value);
 746        else
 747                dev_err(&idev->dev, "Bad response type\n");
 748
 749        kfree(response.pointer);
 750}
 751
 752static int huawei_wmi_input_setup(struct device *dev,
 753                const char *guid,
 754                struct input_dev **idev)
 755{
 756        *idev = devm_input_allocate_device(dev);
 757        if (!*idev)
 758                return -ENOMEM;
 759
 760        (*idev)->name = "Huawei WMI hotkeys";
 761        (*idev)->phys = "wmi/input0";
 762        (*idev)->id.bustype = BUS_HOST;
 763        (*idev)->dev.parent = dev;
 764
 765        return sparse_keymap_setup(*idev, huawei_wmi_keymap, NULL) ||
 766                input_register_device(*idev) ||
 767                wmi_install_notify_handler(guid, huawei_wmi_input_notify,
 768                                *idev);
 769}
 770
 771static void huawei_wmi_input_exit(struct device *dev, const char *guid)
 772{
 773        wmi_remove_notify_handler(guid);
 774}
 775
 776/* Huawei driver */
 777
 778static const struct wmi_device_id huawei_wmi_events_id_table[] = {
 779        { .guid_string = WMI0_EVENT_GUID },
 780        { .guid_string = HWMI_EVENT_GUID },
 781        {  }
 782};
 783
 784static int huawei_wmi_probe(struct platform_device *pdev)
 785{
 786        const struct wmi_device_id *guid = huawei_wmi_events_id_table;
 787        int err;
 788
 789        platform_set_drvdata(pdev, huawei_wmi);
 790        huawei_wmi->dev = &pdev->dev;
 791
 792        while (*guid->guid_string) {
 793                struct input_dev *idev = *huawei_wmi->idev;
 794
 795                if (wmi_has_guid(guid->guid_string)) {
 796                        err = huawei_wmi_input_setup(&pdev->dev, guid->guid_string, &idev);
 797                        if (err) {
 798                                dev_err(&pdev->dev, "Failed to setup input on %s\n", guid->guid_string);
 799                                return err;
 800                        }
 801                }
 802
 803                idev++;
 804                guid++;
 805        }
 806
 807        if (wmi_has_guid(HWMI_METHOD_GUID)) {
 808                mutex_init(&huawei_wmi->wmi_lock);
 809
 810                huawei_wmi_leds_setup(&pdev->dev);
 811                huawei_wmi_fn_lock_setup(&pdev->dev);
 812                huawei_wmi_battery_setup(&pdev->dev);
 813                huawei_wmi_debugfs_setup(&pdev->dev);
 814        }
 815
 816        return 0;
 817}
 818
 819static int huawei_wmi_remove(struct platform_device *pdev)
 820{
 821        const struct wmi_device_id *guid = huawei_wmi_events_id_table;
 822
 823        while (*guid->guid_string) {
 824                if (wmi_has_guid(guid->guid_string))
 825                        huawei_wmi_input_exit(&pdev->dev, guid->guid_string);
 826
 827                guid++;
 828        }
 829
 830        if (wmi_has_guid(HWMI_METHOD_GUID)) {
 831                huawei_wmi_debugfs_exit(&pdev->dev);
 832                huawei_wmi_battery_exit(&pdev->dev);
 833                huawei_wmi_fn_lock_exit(&pdev->dev);
 834        }
 835
 836        return 0;
 837}
 838
 839static struct platform_driver huawei_wmi_driver = {
 840        .driver = {
 841                .name = "huawei-wmi",
 842        },
 843        .probe = huawei_wmi_probe,
 844        .remove = huawei_wmi_remove,
 845};
 846
 847static __init int huawei_wmi_init(void)
 848{
 849        struct platform_device *pdev;
 850        int err;
 851
 852        huawei_wmi = kzalloc(sizeof(struct huawei_wmi), GFP_KERNEL);
 853        if (!huawei_wmi)
 854                return -ENOMEM;
 855
 856        quirks = &quirk_unknown;
 857        dmi_check_system(huawei_quirks);
 858        if (battery_reset != -1)
 859                quirks->battery_reset = battery_reset;
 860        if (report_brightness != -1)
 861                quirks->report_brightness = report_brightness;
 862
 863        err = platform_driver_register(&huawei_wmi_driver);
 864        if (err)
 865                goto pdrv_err;
 866
 867        pdev = platform_device_register_simple("huawei-wmi", -1, NULL, 0);
 868        if (IS_ERR(pdev)) {
 869                err = PTR_ERR(pdev);
 870                goto pdev_err;
 871        }
 872
 873        return 0;
 874
 875pdev_err:
 876        platform_driver_unregister(&huawei_wmi_driver);
 877pdrv_err:
 878        kfree(huawei_wmi);
 879        return err;
 880}
 881
 882static __exit void huawei_wmi_exit(void)
 883{
 884        struct platform_device *pdev = to_platform_device(huawei_wmi->dev);
 885
 886        platform_device_unregister(pdev);
 887        platform_driver_unregister(&huawei_wmi_driver);
 888
 889        kfree(huawei_wmi);
 890}
 891
 892module_init(huawei_wmi_init);
 893module_exit(huawei_wmi_exit);
 894
 895MODULE_ALIAS("wmi:"HWMI_METHOD_GUID);
 896MODULE_DEVICE_TABLE(wmi, huawei_wmi_events_id_table);
 897MODULE_AUTHOR("Ayman Bagabas <ayman.bagabas@gmail.com>");
 898MODULE_DESCRIPTION("Huawei WMI laptop extras driver");
 899MODULE_LICENSE("GPL v2");
 900