root/drivers/platform/x86/huawei-wmi.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. huawei_wmi_micmute_led_set
  2. huawei_wmi_leds_setup
  3. huawei_wmi_process_key
  4. huawei_wmi_notify
  5. huawei_wmi_input_setup
  6. huawei_wmi_probe

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  *  Huawei WMI hotkeys
   4  *
   5  *  Copyright (C) 2018        Ayman Bagabas <ayman.bagabas@gmail.com>
   6  */
   7 
   8 #include <linux/acpi.h>
   9 #include <linux/input.h>
  10 #include <linux/input/sparse-keymap.h>
  11 #include <linux/leds.h>
  12 #include <linux/module.h>
  13 #include <linux/wmi.h>
  14 
  15 /*
  16  * Huawei WMI GUIDs
  17  */
  18 #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
  19 #define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C90629100000"
  20 
  21 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
  22 
  23 struct huawei_wmi_priv {
  24         struct input_dev *idev;
  25         struct led_classdev cdev;
  26         acpi_handle handle;
  27         char *acpi_method;
  28 };
  29 
  30 static const struct key_entry huawei_wmi_keymap[] = {
  31         { KE_KEY,    0x281, { KEY_BRIGHTNESSDOWN } },
  32         { KE_KEY,    0x282, { KEY_BRIGHTNESSUP } },
  33         { KE_KEY,    0x284, { KEY_MUTE } },
  34         { KE_KEY,    0x285, { KEY_VOLUMEDOWN } },
  35         { KE_KEY,    0x286, { KEY_VOLUMEUP } },
  36         { KE_KEY,    0x287, { KEY_MICMUTE } },
  37         { KE_KEY,    0x289, { KEY_WLAN } },
  38         // Huawei |M| key
  39         { KE_KEY,    0x28a, { KEY_CONFIG } },
  40         // Keyboard backlight
  41         { KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
  42         { KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
  43         { KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
  44         { KE_END,        0 }
  45 };
  46 
  47 static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
  48                 enum led_brightness brightness)
  49 {
  50         struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent);
  51         acpi_status status;
  52         union acpi_object args[3];
  53         struct acpi_object_list arg_list = {
  54                 .pointer = args,
  55                 .count = ARRAY_SIZE(args),
  56         };
  57 
  58         args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
  59         args[1].integer.value = 0x04;
  60 
  61         if (strcmp(priv->acpi_method, "SPIN") == 0) {
  62                 args[0].integer.value = 0;
  63                 args[2].integer.value = brightness ? 1 : 0;
  64         } else if (strcmp(priv->acpi_method, "WPIN") == 0) {
  65                 args[0].integer.value = 1;
  66                 args[2].integer.value = brightness ? 0 : 1;
  67         } else {
  68                 return -EINVAL;
  69         }
  70 
  71         status = acpi_evaluate_object(priv->handle, priv->acpi_method, &arg_list, NULL);
  72         if (ACPI_FAILURE(status))
  73                 return -ENXIO;
  74 
  75         return 0;
  76 }
  77 
  78 static int huawei_wmi_leds_setup(struct wmi_device *wdev)
  79 {
  80         struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
  81 
  82         priv->handle = ec_get_handle();
  83         if (!priv->handle)
  84                 return 0;
  85 
  86         if (acpi_has_method(priv->handle, "SPIN"))
  87                 priv->acpi_method = "SPIN";
  88         else if (acpi_has_method(priv->handle, "WPIN"))
  89                 priv->acpi_method = "WPIN";
  90         else
  91                 return 0;
  92 
  93         priv->cdev.name = "platform::micmute";
  94         priv->cdev.max_brightness = 1;
  95         priv->cdev.brightness_set_blocking = huawei_wmi_micmute_led_set;
  96         priv->cdev.default_trigger = "audio-micmute";
  97         priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
  98         priv->cdev.dev = &wdev->dev;
  99         priv->cdev.flags = LED_CORE_SUSPENDRESUME;
 100 
 101         return devm_led_classdev_register(&wdev->dev, &priv->cdev);
 102 }
 103 
 104 static void huawei_wmi_process_key(struct wmi_device *wdev, int code)
 105 {
 106         struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
 107         const struct key_entry *key;
 108 
 109         /*
 110          * WMI0 uses code 0x80 to indicate a hotkey event.
 111          * The actual key is fetched from the method WQ00
 112          * using WMI0_EXPENSIVE_GUID.
 113          */
 114         if (code == 0x80) {
 115                 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
 116                 union acpi_object *obj;
 117                 acpi_status status;
 118 
 119                 status = wmi_query_block(WMI0_EXPENSIVE_GUID, 0, &response);
 120                 if (ACPI_FAILURE(status))
 121                         return;
 122 
 123                 obj = (union acpi_object *)response.pointer;
 124                 if (obj && obj->type == ACPI_TYPE_INTEGER)
 125                         code = obj->integer.value;
 126 
 127                 kfree(response.pointer);
 128         }
 129 
 130         key = sparse_keymap_entry_from_scancode(priv->idev, code);
 131         if (!key) {
 132                 dev_info(&wdev->dev, "Unknown key pressed, code: 0x%04x\n", code);
 133                 return;
 134         }
 135 
 136         sparse_keymap_report_entry(priv->idev, key, 1, true);
 137 }
 138 
 139 static void huawei_wmi_notify(struct wmi_device *wdev,
 140                 union acpi_object *obj)
 141 {
 142         if (obj->type == ACPI_TYPE_INTEGER)
 143                 huawei_wmi_process_key(wdev, obj->integer.value);
 144         else
 145                 dev_info(&wdev->dev, "Bad response type %d\n", obj->type);
 146 }
 147 
 148 static int huawei_wmi_input_setup(struct wmi_device *wdev)
 149 {
 150         struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
 151         int err;
 152 
 153         priv->idev = devm_input_allocate_device(&wdev->dev);
 154         if (!priv->idev)
 155                 return -ENOMEM;
 156 
 157         priv->idev->name = "Huawei WMI hotkeys";
 158         priv->idev->phys = "wmi/input0";
 159         priv->idev->id.bustype = BUS_HOST;
 160         priv->idev->dev.parent = &wdev->dev;
 161 
 162         err = sparse_keymap_setup(priv->idev, huawei_wmi_keymap, NULL);
 163         if (err)
 164                 return err;
 165 
 166         return input_register_device(priv->idev);
 167 }
 168 
 169 static int huawei_wmi_probe(struct wmi_device *wdev, const void *context)
 170 {
 171         struct huawei_wmi_priv *priv;
 172         int err;
 173 
 174         priv = devm_kzalloc(&wdev->dev, sizeof(struct huawei_wmi_priv), GFP_KERNEL);
 175         if (!priv)
 176                 return -ENOMEM;
 177 
 178         dev_set_drvdata(&wdev->dev, priv);
 179 
 180         err = huawei_wmi_input_setup(wdev);
 181         if (err)
 182                 return err;
 183 
 184         return huawei_wmi_leds_setup(wdev);
 185 }
 186 
 187 static const struct wmi_device_id huawei_wmi_id_table[] = {
 188         { .guid_string = WMI0_EVENT_GUID },
 189         { .guid_string = AMW0_EVENT_GUID },
 190         {  }
 191 };
 192 
 193 static struct wmi_driver huawei_wmi_driver = {
 194         .driver = {
 195                 .name = "huawei-wmi",
 196         },
 197         .id_table = huawei_wmi_id_table,
 198         .probe = huawei_wmi_probe,
 199         .notify = huawei_wmi_notify,
 200 };
 201 
 202 module_wmi_driver(huawei_wmi_driver);
 203 
 204 MODULE_DEVICE_TABLE(wmi, huawei_wmi_id_table);
 205 MODULE_AUTHOR("Ayman Bagabas <ayman.bagabas@gmail.com>");
 206 MODULE_DESCRIPTION("Huawei WMI hotkeys");
 207 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */