Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  *  Huawei WMI laptop extras driver
0004  *
0005  *  Copyright (C) 2018        Ayman Bagabas <ayman.bagabas@gmail.com>
0006  */
0007 
0008 #include <linux/acpi.h>
0009 #include <linux/debugfs.h>
0010 #include <linux/delay.h>
0011 #include <linux/dmi.h>
0012 #include <linux/input.h>
0013 #include <linux/input/sparse-keymap.h>
0014 #include <linux/leds.h>
0015 #include <linux/module.h>
0016 #include <linux/mutex.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/power_supply.h>
0019 #include <linux/sysfs.h>
0020 #include <linux/wmi.h>
0021 #include <acpi/battery.h>
0022 
0023 /*
0024  * Huawei WMI GUIDs
0025  */
0026 #define HWMI_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C90629100000"
0027 #define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C90629100000"
0028 
0029 /* Legacy GUIDs */
0030 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
0031 #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
0032 
0033 /* HWMI commands */
0034 
0035 enum {
0036     BATTERY_THRESH_GET      = 0x00001103, /* \GBTT */
0037     BATTERY_THRESH_SET      = 0x00001003, /* \SBTT */
0038     FN_LOCK_GET         = 0x00000604, /* \GFRS */
0039     FN_LOCK_SET         = 0x00000704, /* \SFRS */
0040     MICMUTE_LED_SET         = 0x00000b04, /* \SMLS */
0041 };
0042 
0043 union hwmi_arg {
0044     u64 cmd;
0045     u8 args[8];
0046 };
0047 
0048 struct quirk_entry {
0049     bool battery_reset;
0050     bool ec_micmute;
0051     bool report_brightness;
0052 };
0053 
0054 static struct quirk_entry *quirks;
0055 
0056 struct huawei_wmi_debug {
0057     struct dentry *root;
0058     u64 arg;
0059 };
0060 
0061 struct huawei_wmi {
0062     bool battery_available;
0063     bool fn_lock_available;
0064 
0065     struct huawei_wmi_debug debug;
0066     struct input_dev *idev[2];
0067     struct led_classdev cdev;
0068     struct device *dev;
0069 
0070     struct mutex wmi_lock;
0071 };
0072 
0073 static struct huawei_wmi *huawei_wmi;
0074 
0075 static const struct key_entry huawei_wmi_keymap[] = {
0076     { KE_KEY,    0x281, { KEY_BRIGHTNESSDOWN } },
0077     { KE_KEY,    0x282, { KEY_BRIGHTNESSUP } },
0078     { KE_KEY,    0x284, { KEY_MUTE } },
0079     { KE_KEY,    0x285, { KEY_VOLUMEDOWN } },
0080     { KE_KEY,    0x286, { KEY_VOLUMEUP } },
0081     { KE_KEY,    0x287, { KEY_MICMUTE } },
0082     { KE_KEY,    0x289, { KEY_WLAN } },
0083     // Huawei |M| key
0084     { KE_KEY,    0x28a, { KEY_CONFIG } },
0085     // Keyboard backlit
0086     { KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
0087     { KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
0088     { KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
0089     { KE_END,    0 }
0090 };
0091 
0092 static int battery_reset = -1;
0093 static int report_brightness = -1;
0094 
0095 module_param(battery_reset, bint, 0444);
0096 MODULE_PARM_DESC(battery_reset,
0097         "Reset battery charge values to (0-0) before disabling it using (0-100)");
0098 module_param(report_brightness, bint, 0444);
0099 MODULE_PARM_DESC(report_brightness,
0100         "Report brightness keys.");
0101 
0102 /* Quirks */
0103 
0104 static int __init dmi_matched(const struct dmi_system_id *dmi)
0105 {
0106     quirks = dmi->driver_data;
0107     return 1;
0108 }
0109 
0110 static struct quirk_entry quirk_unknown = {
0111 };
0112 
0113 static struct quirk_entry quirk_battery_reset = {
0114     .battery_reset = true,
0115 };
0116 
0117 static struct quirk_entry quirk_matebook_x = {
0118     .ec_micmute = true,
0119     .report_brightness = true,
0120 };
0121 
0122 static const struct dmi_system_id huawei_quirks[] = {
0123     {
0124         .callback = dmi_matched,
0125         .ident = "Huawei MACH-WX9",
0126         .matches = {
0127             DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
0128             DMI_MATCH(DMI_PRODUCT_NAME, "MACH-WX9"),
0129         },
0130         .driver_data = &quirk_battery_reset
0131     },
0132     {
0133         .callback = dmi_matched,
0134         .ident = "Huawei MateBook X",
0135         .matches = {
0136             DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
0137             DMI_MATCH(DMI_PRODUCT_NAME, "HUAWEI MateBook X")
0138         },
0139         .driver_data = &quirk_matebook_x
0140     },
0141     {  }
0142 };
0143 
0144 /* Utils */
0145 
0146 static int huawei_wmi_call(struct huawei_wmi *huawei,
0147                struct acpi_buffer *in, struct acpi_buffer *out)
0148 {
0149     acpi_status status;
0150 
0151     mutex_lock(&huawei->wmi_lock);
0152     status = wmi_evaluate_method(HWMI_METHOD_GUID, 0, 1, in, out);
0153     mutex_unlock(&huawei->wmi_lock);
0154     if (ACPI_FAILURE(status)) {
0155         dev_err(huawei->dev, "Failed to evaluate wmi method\n");
0156         return -ENODEV;
0157     }
0158 
0159     return 0;
0160 }
0161 
0162 /* HWMI takes a 64 bit input and returns either a package with 2 buffers, one of
0163  * 4 bytes and the other of 256 bytes, or one buffer of size 0x104 (260) bytes.
0164  * The first 4 bytes are ignored, we ignore the first 4 bytes buffer if we got a
0165  * package, or skip the first 4 if a buffer of 0x104 is used. The first byte of
0166  * the remaining 0x100 sized buffer has the return status of every call. In case
0167  * the return status is non-zero, we return -ENODEV but still copy the returned
0168  * buffer to the given buffer parameter (buf).
0169  */
0170 static int huawei_wmi_cmd(u64 arg, u8 *buf, size_t buflen)
0171 {
0172     struct huawei_wmi *huawei = huawei_wmi;
0173     struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
0174     struct acpi_buffer in;
0175     union acpi_object *obj;
0176     size_t len;
0177     int err, i;
0178 
0179     in.length = sizeof(arg);
0180     in.pointer = &arg;
0181 
0182     /* Some models require calling HWMI twice to execute a command. We evaluate
0183      * HWMI and if we get a non-zero return status we evaluate it again.
0184      */
0185     for (i = 0; i < 2; i++) {
0186         err = huawei_wmi_call(huawei, &in, &out);
0187         if (err)
0188             goto fail_cmd;
0189 
0190         obj = out.pointer;
0191         if (!obj) {
0192             err = -EIO;
0193             goto fail_cmd;
0194         }
0195 
0196         switch (obj->type) {
0197         /* Models that implement both "legacy" and HWMI tend to return a 0x104
0198          * sized buffer instead of a package of 0x4 and 0x100 buffers.
0199          */
0200         case ACPI_TYPE_BUFFER:
0201             if (obj->buffer.length == 0x104) {
0202                 // Skip the first 4 bytes.
0203                 obj->buffer.pointer += 4;
0204                 len = 0x100;
0205             } else {
0206                 dev_err(huawei->dev, "Bad buffer length, got %d\n", obj->buffer.length);
0207                 err = -EIO;
0208                 goto fail_cmd;
0209             }
0210 
0211             break;
0212         /* HWMI returns a package with 2 buffer elements, one of 4 bytes and the
0213          * other is 256 bytes.
0214          */
0215         case ACPI_TYPE_PACKAGE:
0216             if (obj->package.count != 2) {
0217                 dev_err(huawei->dev, "Bad package count, got %d\n", obj->package.count);
0218                 err = -EIO;
0219                 goto fail_cmd;
0220             }
0221 
0222             obj = &obj->package.elements[1];
0223             if (obj->type != ACPI_TYPE_BUFFER) {
0224                 dev_err(huawei->dev, "Bad package element type, got %d\n", obj->type);
0225                 err = -EIO;
0226                 goto fail_cmd;
0227             }
0228             len = obj->buffer.length;
0229 
0230             break;
0231         /* Shouldn't get here! */
0232         default:
0233             dev_err(huawei->dev, "Unexpected obj type, got: %d\n", obj->type);
0234             err = -EIO;
0235             goto fail_cmd;
0236         }
0237 
0238         if (!*obj->buffer.pointer)
0239             break;
0240     }
0241 
0242     err = (*obj->buffer.pointer) ? -ENODEV : 0;
0243 
0244     if (buf) {
0245         len = min(buflen, len);
0246         memcpy(buf, obj->buffer.pointer, len);
0247     }
0248 
0249 fail_cmd:
0250     kfree(out.pointer);
0251     return err;
0252 }
0253 
0254 /* LEDs */
0255 
0256 static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
0257         enum led_brightness brightness)
0258 {
0259     /* This is a workaround until the "legacy" interface is implemented. */
0260     if (quirks && quirks->ec_micmute) {
0261         char *acpi_method;
0262         acpi_handle handle;
0263         acpi_status status;
0264         union acpi_object args[3];
0265         struct acpi_object_list arg_list = {
0266             .pointer = args,
0267             .count = ARRAY_SIZE(args),
0268         };
0269 
0270         handle = ec_get_handle();
0271         if (!handle)
0272             return -ENODEV;
0273 
0274         args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
0275         args[1].integer.value = 0x04;
0276 
0277         if (acpi_has_method(handle, "SPIN")) {
0278             acpi_method = "SPIN";
0279             args[0].integer.value = 0;
0280             args[2].integer.value = brightness ? 1 : 0;
0281         } else if (acpi_has_method(handle, "WPIN")) {
0282             acpi_method = "WPIN";
0283             args[0].integer.value = 1;
0284             args[2].integer.value = brightness ? 0 : 1;
0285         } else {
0286             return -ENODEV;
0287         }
0288 
0289         status = acpi_evaluate_object(handle, acpi_method, &arg_list, NULL);
0290         if (ACPI_FAILURE(status))
0291             return -ENODEV;
0292 
0293         return 0;
0294     } else {
0295         union hwmi_arg arg;
0296 
0297         arg.cmd = MICMUTE_LED_SET;
0298         arg.args[2] = brightness;
0299 
0300         return huawei_wmi_cmd(arg.cmd, NULL, 0);
0301     }
0302 }
0303 
0304 static void huawei_wmi_leds_setup(struct device *dev)
0305 {
0306     struct huawei_wmi *huawei = dev_get_drvdata(dev);
0307 
0308     huawei->cdev.name = "platform::micmute";
0309     huawei->cdev.max_brightness = 1;
0310     huawei->cdev.brightness_set_blocking = &huawei_wmi_micmute_led_set;
0311     huawei->cdev.default_trigger = "audio-micmute";
0312     huawei->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
0313     huawei->cdev.dev = dev;
0314     huawei->cdev.flags = LED_CORE_SUSPENDRESUME;
0315 
0316     devm_led_classdev_register(dev, &huawei->cdev);
0317 }
0318 
0319 /* Battery protection */
0320 
0321 static int huawei_wmi_battery_get(int *start, int *end)
0322 {
0323     u8 ret[0x100];
0324     int err, i;
0325 
0326     err = huawei_wmi_cmd(BATTERY_THRESH_GET, ret, 0x100);
0327     if (err)
0328         return err;
0329 
0330     /* Find the last two non-zero values. Return status is ignored. */
0331     i = 0xff;
0332     do {
0333         if (start)
0334             *start = ret[i-1];
0335         if (end)
0336             *end = ret[i];
0337     } while (i > 2 && !ret[i--]);
0338 
0339     return 0;
0340 }
0341 
0342 static int huawei_wmi_battery_set(int start, int end)
0343 {
0344     union hwmi_arg arg;
0345     int err;
0346 
0347     if (start < 0 || end < 0 || start > 100 || end > 100)
0348         return -EINVAL;
0349 
0350     arg.cmd = BATTERY_THRESH_SET;
0351     arg.args[2] = start;
0352     arg.args[3] = end;
0353 
0354     /* This is an edge case were some models turn battery protection
0355      * off without changing their thresholds values. We clear the
0356      * values before turning off protection. Sometimes we need a sleep delay to
0357      * make sure these values make their way to EC memory.
0358      */
0359     if (quirks && quirks->battery_reset && start == 0 && end == 100) {
0360         err = huawei_wmi_battery_set(0, 0);
0361         if (err)
0362             return err;
0363 
0364         msleep(1000);
0365     }
0366 
0367     err = huawei_wmi_cmd(arg.cmd, NULL, 0);
0368 
0369     return err;
0370 }
0371 
0372 static ssize_t charge_control_start_threshold_show(struct device *dev,
0373         struct device_attribute *attr,
0374         char *buf)
0375 {
0376     int err, start;
0377 
0378     err = huawei_wmi_battery_get(&start, NULL);
0379     if (err)
0380         return err;
0381 
0382     return sprintf(buf, "%d\n", start);
0383 }
0384 
0385 static ssize_t charge_control_end_threshold_show(struct device *dev,
0386         struct device_attribute *attr,
0387         char *buf)
0388 {
0389     int err, end;
0390 
0391     err = huawei_wmi_battery_get(NULL, &end);
0392     if (err)
0393         return err;
0394 
0395     return sprintf(buf, "%d\n", end);
0396 }
0397 
0398 static ssize_t charge_control_thresholds_show(struct device *dev,
0399         struct device_attribute *attr,
0400         char *buf)
0401 {
0402     int err, start, end;
0403 
0404     err = huawei_wmi_battery_get(&start, &end);
0405     if (err)
0406         return err;
0407 
0408     return sprintf(buf, "%d %d\n", start, end);
0409 }
0410 
0411 static ssize_t charge_control_start_threshold_store(struct device *dev,
0412         struct device_attribute *attr,
0413         const char *buf, size_t size)
0414 {
0415     int err, start, end;
0416 
0417     err = huawei_wmi_battery_get(NULL, &end);
0418     if (err)
0419         return err;
0420 
0421     if (sscanf(buf, "%d", &start) != 1)
0422         return -EINVAL;
0423 
0424     err = huawei_wmi_battery_set(start, end);
0425     if (err)
0426         return err;
0427 
0428     return size;
0429 }
0430 
0431 static ssize_t charge_control_end_threshold_store(struct device *dev,
0432         struct device_attribute *attr,
0433         const char *buf, size_t size)
0434 {
0435     int err, start, end;
0436 
0437     err = huawei_wmi_battery_get(&start, NULL);
0438     if (err)
0439         return err;
0440 
0441     if (sscanf(buf, "%d", &end) != 1)
0442         return -EINVAL;
0443 
0444     err = huawei_wmi_battery_set(start, end);
0445     if (err)
0446         return err;
0447 
0448     return size;
0449 }
0450 
0451 static ssize_t charge_control_thresholds_store(struct device *dev,
0452         struct device_attribute *attr,
0453         const char *buf, size_t size)
0454 {
0455     int err, start, end;
0456 
0457     if (sscanf(buf, "%d %d", &start, &end) != 2)
0458         return -EINVAL;
0459 
0460     err = huawei_wmi_battery_set(start, end);
0461     if (err)
0462         return err;
0463 
0464     return size;
0465 }
0466 
0467 static DEVICE_ATTR_RW(charge_control_start_threshold);
0468 static DEVICE_ATTR_RW(charge_control_end_threshold);
0469 static DEVICE_ATTR_RW(charge_control_thresholds);
0470 
0471 static int huawei_wmi_battery_add(struct power_supply *battery)
0472 {
0473     int err = 0;
0474 
0475     err = device_create_file(&battery->dev, &dev_attr_charge_control_start_threshold);
0476     if (err)
0477         return err;
0478 
0479     err = device_create_file(&battery->dev, &dev_attr_charge_control_end_threshold);
0480     if (err)
0481         device_remove_file(&battery->dev, &dev_attr_charge_control_start_threshold);
0482 
0483     return err;
0484 }
0485 
0486 static int huawei_wmi_battery_remove(struct power_supply *battery)
0487 {
0488     device_remove_file(&battery->dev, &dev_attr_charge_control_start_threshold);
0489     device_remove_file(&battery->dev, &dev_attr_charge_control_end_threshold);
0490 
0491     return 0;
0492 }
0493 
0494 static struct acpi_battery_hook huawei_wmi_battery_hook = {
0495     .add_battery = huawei_wmi_battery_add,
0496     .remove_battery = huawei_wmi_battery_remove,
0497     .name = "Huawei Battery Extension"
0498 };
0499 
0500 static void huawei_wmi_battery_setup(struct device *dev)
0501 {
0502     struct huawei_wmi *huawei = dev_get_drvdata(dev);
0503 
0504     huawei->battery_available = true;
0505     if (huawei_wmi_battery_get(NULL, NULL)) {
0506         huawei->battery_available = false;
0507         return;
0508     }
0509 
0510     battery_hook_register(&huawei_wmi_battery_hook);
0511     device_create_file(dev, &dev_attr_charge_control_thresholds);
0512 }
0513 
0514 static void huawei_wmi_battery_exit(struct device *dev)
0515 {
0516     struct huawei_wmi *huawei = dev_get_drvdata(dev);
0517 
0518     if (huawei->battery_available) {
0519         battery_hook_unregister(&huawei_wmi_battery_hook);
0520         device_remove_file(dev, &dev_attr_charge_control_thresholds);
0521     }
0522 }
0523 
0524 /* Fn lock */
0525 
0526 static int huawei_wmi_fn_lock_get(int *on)
0527 {
0528     u8 ret[0x100] = { 0 };
0529     int err, i;
0530 
0531     err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
0532     if (err)
0533         return err;
0534 
0535     /* Find the first non-zero value. Return status is ignored. */
0536     i = 1;
0537     do {
0538         if (on)
0539             *on = ret[i] - 1; // -1 undefined, 0 off, 1 on.
0540     } while (i < 0xff && !ret[i++]);
0541 
0542     return 0;
0543 }
0544 
0545 static int huawei_wmi_fn_lock_set(int on)
0546 {
0547     union hwmi_arg arg;
0548 
0549     arg.cmd = FN_LOCK_SET;
0550     arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
0551 
0552     return huawei_wmi_cmd(arg.cmd, NULL, 0);
0553 }
0554 
0555 static ssize_t fn_lock_state_show(struct device *dev,
0556         struct device_attribute *attr,
0557         char *buf)
0558 {
0559     int err, on;
0560 
0561     err = huawei_wmi_fn_lock_get(&on);
0562     if (err)
0563         return err;
0564 
0565     return sprintf(buf, "%d\n", on);
0566 }
0567 
0568 static ssize_t fn_lock_state_store(struct device *dev,
0569         struct device_attribute *attr,
0570         const char *buf, size_t size)
0571 {
0572     int on, err;
0573 
0574     if (kstrtoint(buf, 10, &on) ||
0575             on < 0 || on > 1)
0576         return -EINVAL;
0577 
0578     err = huawei_wmi_fn_lock_set(on);
0579     if (err)
0580         return err;
0581 
0582     return size;
0583 }
0584 
0585 static DEVICE_ATTR_RW(fn_lock_state);
0586 
0587 static void huawei_wmi_fn_lock_setup(struct device *dev)
0588 {
0589     struct huawei_wmi *huawei = dev_get_drvdata(dev);
0590 
0591     huawei->fn_lock_available = true;
0592     if (huawei_wmi_fn_lock_get(NULL)) {
0593         huawei->fn_lock_available = false;
0594         return;
0595     }
0596 
0597     device_create_file(dev, &dev_attr_fn_lock_state);
0598 }
0599 
0600 static void huawei_wmi_fn_lock_exit(struct device *dev)
0601 {
0602     struct huawei_wmi *huawei = dev_get_drvdata(dev);
0603 
0604     if (huawei->fn_lock_available)
0605         device_remove_file(dev, &dev_attr_fn_lock_state);
0606 }
0607 
0608 /* debugfs */
0609 
0610 static void huawei_wmi_debugfs_call_dump(struct seq_file *m, void *data,
0611         union acpi_object *obj)
0612 {
0613     struct huawei_wmi *huawei = m->private;
0614     int i;
0615 
0616     switch (obj->type) {
0617     case ACPI_TYPE_INTEGER:
0618         seq_printf(m, "0x%llx", obj->integer.value);
0619         break;
0620     case ACPI_TYPE_STRING:
0621         seq_printf(m, "\"%.*s\"", obj->string.length, obj->string.pointer);
0622         break;
0623     case ACPI_TYPE_BUFFER:
0624         seq_puts(m, "{");
0625         for (i = 0; i < obj->buffer.length; i++) {
0626             seq_printf(m, "0x%02x", obj->buffer.pointer[i]);
0627             if (i < obj->buffer.length - 1)
0628                 seq_puts(m, ",");
0629         }
0630         seq_puts(m, "}");
0631         break;
0632     case ACPI_TYPE_PACKAGE:
0633         seq_puts(m, "[");
0634         for (i = 0; i < obj->package.count; i++) {
0635             huawei_wmi_debugfs_call_dump(m, huawei, &obj->package.elements[i]);
0636             if (i < obj->package.count - 1)
0637                 seq_puts(m, ",");
0638         }
0639         seq_puts(m, "]");
0640         break;
0641     default:
0642         dev_err(huawei->dev, "Unexpected obj type, got %d\n", obj->type);
0643         return;
0644     }
0645 }
0646 
0647 static int huawei_wmi_debugfs_call_show(struct seq_file *m, void *data)
0648 {
0649     struct huawei_wmi *huawei = m->private;
0650     struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
0651     struct acpi_buffer in;
0652     union acpi_object *obj;
0653     int err;
0654 
0655     in.length = sizeof(u64);
0656     in.pointer = &huawei->debug.arg;
0657 
0658     err = huawei_wmi_call(huawei, &in, &out);
0659     if (err)
0660         return err;
0661 
0662     obj = out.pointer;
0663     if (!obj) {
0664         err = -EIO;
0665         goto fail_debugfs_call;
0666     }
0667 
0668     huawei_wmi_debugfs_call_dump(m, huawei, obj);
0669 
0670 fail_debugfs_call:
0671     kfree(out.pointer);
0672     return err;
0673 }
0674 
0675 DEFINE_SHOW_ATTRIBUTE(huawei_wmi_debugfs_call);
0676 
0677 static void huawei_wmi_debugfs_setup(struct device *dev)
0678 {
0679     struct huawei_wmi *huawei = dev_get_drvdata(dev);
0680 
0681     huawei->debug.root = debugfs_create_dir("huawei-wmi", NULL);
0682 
0683     debugfs_create_x64("arg", 0644, huawei->debug.root,
0684         &huawei->debug.arg);
0685     debugfs_create_file("call", 0400,
0686         huawei->debug.root, huawei, &huawei_wmi_debugfs_call_fops);
0687 }
0688 
0689 static void huawei_wmi_debugfs_exit(struct device *dev)
0690 {
0691     struct huawei_wmi *huawei = dev_get_drvdata(dev);
0692 
0693     debugfs_remove_recursive(huawei->debug.root);
0694 }
0695 
0696 /* Input */
0697 
0698 static void huawei_wmi_process_key(struct input_dev *idev, int code)
0699 {
0700     const struct key_entry *key;
0701 
0702     /*
0703      * WMI0 uses code 0x80 to indicate a hotkey event.
0704      * The actual key is fetched from the method WQ00
0705      * using WMI0_EXPENSIVE_GUID.
0706      */
0707     if (code == 0x80) {
0708         struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
0709         union acpi_object *obj;
0710         acpi_status status;
0711 
0712         status = wmi_query_block(WMI0_EXPENSIVE_GUID, 0, &response);
0713         if (ACPI_FAILURE(status))
0714             return;
0715 
0716         obj = (union acpi_object *)response.pointer;
0717         if (obj && obj->type == ACPI_TYPE_INTEGER)
0718             code = obj->integer.value;
0719 
0720         kfree(response.pointer);
0721     }
0722 
0723     key = sparse_keymap_entry_from_scancode(idev, code);
0724     if (!key) {
0725         dev_info(&idev->dev, "Unknown key pressed, code: 0x%04x\n", code);
0726         return;
0727     }
0728 
0729     if (quirks && !quirks->report_brightness &&
0730             (key->sw.code == KEY_BRIGHTNESSDOWN ||
0731             key->sw.code == KEY_BRIGHTNESSUP))
0732         return;
0733 
0734     sparse_keymap_report_entry(idev, key, 1, true);
0735 }
0736 
0737 static void huawei_wmi_input_notify(u32 value, void *context)
0738 {
0739     struct input_dev *idev = (struct input_dev *)context;
0740     struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
0741     union acpi_object *obj;
0742     acpi_status status;
0743 
0744     status = wmi_get_event_data(value, &response);
0745     if (ACPI_FAILURE(status)) {
0746         dev_err(&idev->dev, "Unable to get event data\n");
0747         return;
0748     }
0749 
0750     obj = (union acpi_object *)response.pointer;
0751     if (obj && obj->type == ACPI_TYPE_INTEGER)
0752         huawei_wmi_process_key(idev, obj->integer.value);
0753     else
0754         dev_err(&idev->dev, "Bad response type\n");
0755 
0756     kfree(response.pointer);
0757 }
0758 
0759 static int huawei_wmi_input_setup(struct device *dev,
0760         const char *guid,
0761         struct input_dev **idev)
0762 {
0763     *idev = devm_input_allocate_device(dev);
0764     if (!*idev)
0765         return -ENOMEM;
0766 
0767     (*idev)->name = "Huawei WMI hotkeys";
0768     (*idev)->phys = "wmi/input0";
0769     (*idev)->id.bustype = BUS_HOST;
0770     (*idev)->dev.parent = dev;
0771 
0772     return sparse_keymap_setup(*idev, huawei_wmi_keymap, NULL) ||
0773         input_register_device(*idev) ||
0774         wmi_install_notify_handler(guid, huawei_wmi_input_notify,
0775                 *idev);
0776 }
0777 
0778 static void huawei_wmi_input_exit(struct device *dev, const char *guid)
0779 {
0780     wmi_remove_notify_handler(guid);
0781 }
0782 
0783 /* Huawei driver */
0784 
0785 static const struct wmi_device_id huawei_wmi_events_id_table[] = {
0786     { .guid_string = WMI0_EVENT_GUID },
0787     { .guid_string = HWMI_EVENT_GUID },
0788     {  }
0789 };
0790 
0791 static int huawei_wmi_probe(struct platform_device *pdev)
0792 {
0793     const struct wmi_device_id *guid = huawei_wmi_events_id_table;
0794     int err;
0795 
0796     platform_set_drvdata(pdev, huawei_wmi);
0797     huawei_wmi->dev = &pdev->dev;
0798 
0799     while (*guid->guid_string) {
0800         struct input_dev *idev = *huawei_wmi->idev;
0801 
0802         if (wmi_has_guid(guid->guid_string)) {
0803             err = huawei_wmi_input_setup(&pdev->dev, guid->guid_string, &idev);
0804             if (err) {
0805                 dev_err(&pdev->dev, "Failed to setup input on %s\n", guid->guid_string);
0806                 return err;
0807             }
0808         }
0809 
0810         idev++;
0811         guid++;
0812     }
0813 
0814     if (wmi_has_guid(HWMI_METHOD_GUID)) {
0815         mutex_init(&huawei_wmi->wmi_lock);
0816 
0817         huawei_wmi_leds_setup(&pdev->dev);
0818         huawei_wmi_fn_lock_setup(&pdev->dev);
0819         huawei_wmi_battery_setup(&pdev->dev);
0820         huawei_wmi_debugfs_setup(&pdev->dev);
0821     }
0822 
0823     return 0;
0824 }
0825 
0826 static int huawei_wmi_remove(struct platform_device *pdev)
0827 {
0828     const struct wmi_device_id *guid = huawei_wmi_events_id_table;
0829 
0830     while (*guid->guid_string) {
0831         if (wmi_has_guid(guid->guid_string))
0832             huawei_wmi_input_exit(&pdev->dev, guid->guid_string);
0833 
0834         guid++;
0835     }
0836 
0837     if (wmi_has_guid(HWMI_METHOD_GUID)) {
0838         huawei_wmi_debugfs_exit(&pdev->dev);
0839         huawei_wmi_battery_exit(&pdev->dev);
0840         huawei_wmi_fn_lock_exit(&pdev->dev);
0841     }
0842 
0843     return 0;
0844 }
0845 
0846 static struct platform_driver huawei_wmi_driver = {
0847     .driver = {
0848         .name = "huawei-wmi",
0849     },
0850     .probe = huawei_wmi_probe,
0851     .remove = huawei_wmi_remove,
0852 };
0853 
0854 static __init int huawei_wmi_init(void)
0855 {
0856     struct platform_device *pdev;
0857     int err;
0858 
0859     huawei_wmi = kzalloc(sizeof(struct huawei_wmi), GFP_KERNEL);
0860     if (!huawei_wmi)
0861         return -ENOMEM;
0862 
0863     quirks = &quirk_unknown;
0864     dmi_check_system(huawei_quirks);
0865     if (battery_reset != -1)
0866         quirks->battery_reset = battery_reset;
0867     if (report_brightness != -1)
0868         quirks->report_brightness = report_brightness;
0869 
0870     err = platform_driver_register(&huawei_wmi_driver);
0871     if (err)
0872         goto pdrv_err;
0873 
0874     pdev = platform_device_register_simple("huawei-wmi", -1, NULL, 0);
0875     if (IS_ERR(pdev)) {
0876         err = PTR_ERR(pdev);
0877         goto pdev_err;
0878     }
0879 
0880     return 0;
0881 
0882 pdev_err:
0883     platform_driver_unregister(&huawei_wmi_driver);
0884 pdrv_err:
0885     kfree(huawei_wmi);
0886     return err;
0887 }
0888 
0889 static __exit void huawei_wmi_exit(void)
0890 {
0891     struct platform_device *pdev = to_platform_device(huawei_wmi->dev);
0892 
0893     platform_device_unregister(pdev);
0894     platform_driver_unregister(&huawei_wmi_driver);
0895 
0896     kfree(huawei_wmi);
0897 }
0898 
0899 module_init(huawei_wmi_init);
0900 module_exit(huawei_wmi_exit);
0901 
0902 MODULE_ALIAS("wmi:"HWMI_METHOD_GUID);
0903 MODULE_DEVICE_TABLE(wmi, huawei_wmi_events_id_table);
0904 MODULE_AUTHOR("Ayman Bagabas <ayman.bagabas@gmail.com>");
0905 MODULE_DESCRIPTION("Huawei WMI laptop extras driver");
0906 MODULE_LICENSE("GPL v2");