Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4b7fb9fc authored by João Paulo Rechi Vita's avatar João Paulo Rechi Vita Committed by Darren Hart
Browse files

platform/x86: asus-wireless: Use per-HID HSWC parameters



Some Asus machines use 0x4/0x5 as their LED on/off values, while others
use 0x0/0x1, as shown in the DSDT excerpts below. Luckily it seems this
behavior is tied to different HIDs, after looking at 44 DSDTs from
different Asus models.

Another small difference is that a few of them call GWBL instead of
OWGS, and SWBL instead of OWGD. That does not seem to make a difference
for asus-wireless, and is additional reasoning to not try to call these
methods directly.

Device (ASHS)                       | Device (ASHS)
{                                   | {
    Name (_HID, "ATK4002")          |     Name (_HID, "ATK4001")
    Method (HSWC, 1, Serialized)    |     Method (HSWC, 1, Serialized)
    {                               |     {
        If ((Arg0 < 0x02))          |         If ((Arg0 < 0x02))
        {                           |         {
            OWGD (Arg0)             |             OWGD (Arg0)
            Return (One)            |             Return (One)
        }                           |         }
        If ((Arg0 == 0x02))         |
        {                           |         If ((Arg0 == 0x02))
            Local0 = OWGS ()        |         {
            If (Local0)             |             Return (OWGS ())
            {                       |         }
                    Return (0x05)   |
            }                       |         If ((Arg0 == 0x03))
            Else                    |         {
            {                       |             Return (0xFF)
                    Return (0x04)   |         }
            }                       |
        }                           |         If ((Arg0 == 0x80))
        If ((Arg0 == 0x03))         |         {
        {                           |            Return (One)
            Return (0xFF)           |         }
        }                           |     }
        If ((Arg0 == 0x04))         |     Method (_STA, 0, NotSerialized)
        {                           |     {
            OWGD (Zero)             |         If ((MSOS () >= OSW8))
            Return (One)            |         {
        }                           |             Return (0x0F)
        If ((Arg0 == 0x05))         |         }
        {                           |         Else
            OWGD (One)              |         {
            Return (One)            |             Return (Zero)
        }                           |         }
        If ((Arg0 == 0x80))         |     }
        {                           | }
            Return (One)            |
        }                           |
    }                               |
    Method (_STA, 0, NotSerialized) |
    {                               |
        If ((MSOS () >= OSW8))      |
        {                           |
            Return (0x0F)           |
        }                           |
        Else                        |
        {                           |
            Return (Zero)           |
        }                           |
    }                               |
}                                   |

Signed-off-by: default avatarJoão Paulo Rechi Vita <jprvita@endlessm.com>
Signed-off-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
parent 23e775db
Loading
Loading
Loading
Loading
+42 −15
Original line number Original line Diff line number Diff line
@@ -17,19 +17,41 @@
#include <linux/pci_ids.h>
#include <linux/pci_ids.h>
#include <linux/leds.h>
#include <linux/leds.h>


#define ASUS_WIRELESS_LED_STATUS 0x2
struct hswc_params {
#define ASUS_WIRELESS_LED_OFF 0x4
	u8 on;
#define ASUS_WIRELESS_LED_ON 0x5
	u8 off;
	u8 status;
};


struct asus_wireless_data {
struct asus_wireless_data {
	struct input_dev *idev;
	struct input_dev *idev;
	struct acpi_device *adev;
	struct acpi_device *adev;
	const struct hswc_params *hswc_params;
	struct workqueue_struct *wq;
	struct workqueue_struct *wq;
	struct work_struct led_work;
	struct work_struct led_work;
	struct led_classdev led;
	struct led_classdev led;
	int led_state;
	int led_state;
};
};


static const struct hswc_params atk4001_id_params = {
	.on = 0x0,
	.off = 0x1,
	.status = 0x2,
};

static const struct hswc_params atk4002_id_params = {
	.on = 0x5,
	.off = 0x4,
	.status = 0x2,
};

static const struct acpi_device_id device_ids[] = {
	{"ATK4001", (kernel_ulong_t)&atk4001_id_params},
	{"ATK4002", (kernel_ulong_t)&atk4002_id_params},
	{"", 0},
};
MODULE_DEVICE_TABLE(acpi, device_ids);

static u64 asus_wireless_method(acpi_handle handle, const char *method,
static u64 asus_wireless_method(acpi_handle handle, const char *method,
				int param)
				int param)
{
{
@@ -61,8 +83,8 @@ static enum led_brightness led_state_get(struct led_classdev *led)


	data = container_of(led, struct asus_wireless_data, led);
	data = container_of(led, struct asus_wireless_data, led);
	s = asus_wireless_method(acpi_device_handle(data->adev), "HSWC",
	s = asus_wireless_method(acpi_device_handle(data->adev), "HSWC",
				 ASUS_WIRELESS_LED_STATUS);
				 data->hswc_params->status);
	if (s == ASUS_WIRELESS_LED_ON)
	if (s == data->hswc_params->on)
		return LED_FULL;
		return LED_FULL;
	return LED_OFF;
	return LED_OFF;
}
}
@@ -82,8 +104,8 @@ static void led_state_set(struct led_classdev *led,
	struct asus_wireless_data *data;
	struct asus_wireless_data *data;


	data = container_of(led, struct asus_wireless_data, led);
	data = container_of(led, struct asus_wireless_data, led);
	data->led_state = value == LED_OFF ? ASUS_WIRELESS_LED_OFF :
	data->led_state = value == LED_OFF ? data->hswc_params->off :
					     ASUS_WIRELESS_LED_ON;
					     data->hswc_params->on;
	queue_work(data->wq, &data->led_work);
	queue_work(data->wq, &data->led_work);
}
}


@@ -104,12 +126,14 @@ static void asus_wireless_notify(struct acpi_device *adev, u32 event)
static int asus_wireless_add(struct acpi_device *adev)
static int asus_wireless_add(struct acpi_device *adev)
{
{
	struct asus_wireless_data *data;
	struct asus_wireless_data *data;
	const struct acpi_device_id *id;
	int err;
	int err;


	data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL);
	data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL);
	if (!data)
	if (!data)
		return -ENOMEM;
		return -ENOMEM;
	adev->driver_data = data;
	adev->driver_data = data;
	data->adev = adev;


	data->idev = devm_input_allocate_device(&adev->dev);
	data->idev = devm_input_allocate_device(&adev->dev);
	if (!data->idev)
	if (!data->idev)
@@ -124,7 +148,16 @@ static int asus_wireless_add(struct acpi_device *adev)
	if (err)
	if (err)
		return err;
		return err;


	data->adev = adev;
	for (id = device_ids; id->id[0]; id++) {
		if (!strcmp((char *) id->id, acpi_device_hid(adev))) {
			data->hswc_params =
				(const struct hswc_params *)id->driver_data;
			break;
		}
	}
	if (!data->hswc_params)
		return 0;

	data->wq = create_singlethread_workqueue("asus_wireless_workqueue");
	data->wq = create_singlethread_workqueue("asus_wireless_workqueue");
	if (!data->wq)
	if (!data->wq)
		return -ENOMEM;
		return -ENOMEM;
@@ -137,6 +170,7 @@ static int asus_wireless_add(struct acpi_device *adev)
	err = devm_led_classdev_register(&adev->dev, &data->led);
	err = devm_led_classdev_register(&adev->dev, &data->led);
	if (err)
	if (err)
		destroy_workqueue(data->wq);
		destroy_workqueue(data->wq);

	return err;
	return err;
}
}


@@ -149,13 +183,6 @@ static int asus_wireless_remove(struct acpi_device *adev)
	return 0;
	return 0;
}
}


static const struct acpi_device_id device_ids[] = {
	{"ATK4001", 0},
	{"ATK4002", 0},
	{"", 0},
};
MODULE_DEVICE_TABLE(acpi, device_ids);

static struct acpi_driver asus_wireless_driver = {
static struct acpi_driver asus_wireless_driver = {
	.name = "Asus Wireless Radio Control Driver",
	.name = "Asus Wireless Radio Control Driver",
	.class = "hotkey",
	.class = "hotkey",