root/drivers/platform/x86/asus-wireless.c

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

DEFINITIONS

This source file includes following definitions.
  1. asus_wireless_method
  2. led_state_get
  3. led_state_update
  4. led_state_set
  5. asus_wireless_notify
  6. asus_wireless_add
  7. asus_wireless_remove

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Asus Wireless Radio Control Driver
   4  *
   5  * Copyright (C) 2015-2016 Endless Mobile, Inc.
   6  */
   7 
   8 #include <linux/kernel.h>
   9 #include <linux/module.h>
  10 #include <linux/init.h>
  11 #include <linux/types.h>
  12 #include <linux/acpi.h>
  13 #include <linux/input.h>
  14 #include <linux/pci_ids.h>
  15 #include <linux/leds.h>
  16 
  17 struct hswc_params {
  18         u8 on;
  19         u8 off;
  20         u8 status;
  21 };
  22 
  23 struct asus_wireless_data {
  24         struct input_dev *idev;
  25         struct acpi_device *adev;
  26         const struct hswc_params *hswc_params;
  27         struct workqueue_struct *wq;
  28         struct work_struct led_work;
  29         struct led_classdev led;
  30         int led_state;
  31 };
  32 
  33 static const struct hswc_params atk4001_id_params = {
  34         .on = 0x0,
  35         .off = 0x1,
  36         .status = 0x2,
  37 };
  38 
  39 static const struct hswc_params atk4002_id_params = {
  40         .on = 0x5,
  41         .off = 0x4,
  42         .status = 0x2,
  43 };
  44 
  45 static const struct acpi_device_id device_ids[] = {
  46         {"ATK4001", (kernel_ulong_t)&atk4001_id_params},
  47         {"ATK4002", (kernel_ulong_t)&atk4002_id_params},
  48         {"", 0},
  49 };
  50 MODULE_DEVICE_TABLE(acpi, device_ids);
  51 
  52 static acpi_status asus_wireless_method(acpi_handle handle, const char *method,
  53                                         int param, u64 *ret)
  54 {
  55         struct acpi_object_list p;
  56         union acpi_object obj;
  57         acpi_status s;
  58 
  59         acpi_handle_debug(handle, "Evaluating method %s, parameter %#x\n",
  60                           method, param);
  61         obj.type = ACPI_TYPE_INTEGER;
  62         obj.integer.value = param;
  63         p.count = 1;
  64         p.pointer = &obj;
  65 
  66         s = acpi_evaluate_integer(handle, (acpi_string) method, &p, ret);
  67         if (ACPI_FAILURE(s))
  68                 acpi_handle_err(handle,
  69                                 "Failed to eval method %s, param %#x (%d)\n",
  70                                 method, param, s);
  71         else
  72                 acpi_handle_debug(handle, "%s returned %#llx\n", method, *ret);
  73 
  74         return s;
  75 }
  76 
  77 static enum led_brightness led_state_get(struct led_classdev *led)
  78 {
  79         struct asus_wireless_data *data;
  80         acpi_status s;
  81         u64 ret;
  82 
  83         data = container_of(led, struct asus_wireless_data, led);
  84         s = asus_wireless_method(acpi_device_handle(data->adev), "HSWC",
  85                                  data->hswc_params->status, &ret);
  86         if (ACPI_SUCCESS(s) && ret == data->hswc_params->on)
  87                 return LED_FULL;
  88         return LED_OFF;
  89 }
  90 
  91 static void led_state_update(struct work_struct *work)
  92 {
  93         struct asus_wireless_data *data;
  94         u64 ret;
  95 
  96         data = container_of(work, struct asus_wireless_data, led_work);
  97         asus_wireless_method(acpi_device_handle(data->adev), "HSWC",
  98                              data->led_state, &ret);
  99 }
 100 
 101 static void led_state_set(struct led_classdev *led, enum led_brightness value)
 102 {
 103         struct asus_wireless_data *data;
 104 
 105         data = container_of(led, struct asus_wireless_data, led);
 106         data->led_state = value == LED_OFF ? data->hswc_params->off :
 107                                              data->hswc_params->on;
 108         queue_work(data->wq, &data->led_work);
 109 }
 110 
 111 static void asus_wireless_notify(struct acpi_device *adev, u32 event)
 112 {
 113         struct asus_wireless_data *data = acpi_driver_data(adev);
 114 
 115         dev_dbg(&adev->dev, "event=%#x\n", event);
 116         if (event != 0x88) {
 117                 dev_notice(&adev->dev, "Unknown ASHS event: %#x\n", event);
 118                 return;
 119         }
 120         input_report_key(data->idev, KEY_RFKILL, 1);
 121         input_sync(data->idev);
 122         input_report_key(data->idev, KEY_RFKILL, 0);
 123         input_sync(data->idev);
 124 }
 125 
 126 static int asus_wireless_add(struct acpi_device *adev)
 127 {
 128         struct asus_wireless_data *data;
 129         const struct acpi_device_id *id;
 130         int err;
 131 
 132         data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL);
 133         if (!data)
 134                 return -ENOMEM;
 135         adev->driver_data = data;
 136         data->adev = adev;
 137 
 138         data->idev = devm_input_allocate_device(&adev->dev);
 139         if (!data->idev)
 140                 return -ENOMEM;
 141         data->idev->name = "Asus Wireless Radio Control";
 142         data->idev->phys = "asus-wireless/input0";
 143         data->idev->id.bustype = BUS_HOST;
 144         data->idev->id.vendor = PCI_VENDOR_ID_ASUSTEK;
 145         set_bit(EV_KEY, data->idev->evbit);
 146         set_bit(KEY_RFKILL, data->idev->keybit);
 147         err = input_register_device(data->idev);
 148         if (err)
 149                 return err;
 150 
 151         for (id = device_ids; id->id[0]; id++) {
 152                 if (!strcmp((char *) id->id, acpi_device_hid(adev))) {
 153                         data->hswc_params =
 154                                 (const struct hswc_params *)id->driver_data;
 155                         break;
 156                 }
 157         }
 158         if (!data->hswc_params)
 159                 return 0;
 160 
 161         data->wq = create_singlethread_workqueue("asus_wireless_workqueue");
 162         if (!data->wq)
 163                 return -ENOMEM;
 164         INIT_WORK(&data->led_work, led_state_update);
 165         data->led.name = "asus-wireless::airplane";
 166         data->led.brightness_set = led_state_set;
 167         data->led.brightness_get = led_state_get;
 168         data->led.flags = LED_CORE_SUSPENDRESUME;
 169         data->led.max_brightness = 1;
 170         data->led.default_trigger = "rfkill-none";
 171         err = devm_led_classdev_register(&adev->dev, &data->led);
 172         if (err)
 173                 destroy_workqueue(data->wq);
 174 
 175         return err;
 176 }
 177 
 178 static int asus_wireless_remove(struct acpi_device *adev)
 179 {
 180         struct asus_wireless_data *data = acpi_driver_data(adev);
 181 
 182         if (data->wq) {
 183                 devm_led_classdev_unregister(&adev->dev, &data->led);
 184                 destroy_workqueue(data->wq);
 185         }
 186         return 0;
 187 }
 188 
 189 static struct acpi_driver asus_wireless_driver = {
 190         .name = "Asus Wireless Radio Control Driver",
 191         .class = "hotkey",
 192         .ids = device_ids,
 193         .ops = {
 194                 .add = asus_wireless_add,
 195                 .remove = asus_wireless_remove,
 196                 .notify = asus_wireless_notify,
 197         },
 198 };
 199 module_acpi_driver(asus_wireless_driver);
 200 
 201 MODULE_DESCRIPTION("Asus Wireless Radio Control Driver");
 202 MODULE_AUTHOR("João Paulo Rechi Vita <jprvita@gmail.com>");
 203 MODULE_LICENSE("GPL");

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