diff options
| -rw-r--r-- | drivers/platform/x86/asus-wmi.c | 73 | 
1 files changed, 68 insertions, 5 deletions
| diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index fc4ccdedb626..83bf145e9ca5 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -84,6 +84,13 @@ MODULE_LICENSE("GPL");  #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI	0x9c31  #define ASUS_ACPI_UID_ASUSWMI		"ASUSWMI" +#define ASUS_ACPI_UID_ATK		"ATK" + +#define WMI_EVENT_QUEUE_SIZE		0x10 +#define WMI_EVENT_QUEUE_END		0x1 +#define WMI_EVENT_MASK			0xFFFF +/* The WMI hotkey event value is always the same. */ +#define WMI_EVENT_VALUE_ATK		0xFF  #define WMI_EVENT_MASK			0xFFFF @@ -150,6 +157,7 @@ struct asus_wmi {  	int dsts_id;  	int spec;  	int sfun; +	bool wmi_event_queue;  	struct input_dev *inputdev;  	struct backlight_device *backlight_device; @@ -1738,14 +1746,52 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)  static void asus_wmi_notify(u32 value, void *context)  {  	struct asus_wmi *asus = context; -	int code = asus_wmi_get_event_code(value); +	int code; +	int i; -	if (code < 0) { -		pr_warn("Failed to get notify code: %d\n", code); -		return; +	for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) { +		code = asus_wmi_get_event_code(value); + +		if (code < 0) { +			pr_warn("Failed to get notify code: %d\n", code); +			return; +		} + +		if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK) +			return; + +		asus_wmi_handle_event_code(code, asus); + +		/* +		 * Double check that queue is present: +		 * ATK (with queue) uses 0xff, ASUSWMI (without) 0xd2. +		 */ +		if (!asus->wmi_event_queue || value != WMI_EVENT_VALUE_ATK) +			return;  	} -	asus_wmi_handle_event_code(code, asus); +	pr_warn("Failed to process event queue, last code: 0x%x\n", code); +} + +static int asus_wmi_notify_queue_flush(struct asus_wmi *asus) +{ +	int code; +	int i; + +	for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) { +		code = asus_wmi_get_event_code(WMI_EVENT_VALUE_ATK); + +		if (code < 0) { +			pr_warn("Failed to get event during flush: %d\n", code); +			return code; +		} + +		if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK) +			return 0; +	} + +	pr_warn("Failed to flush event queue\n"); +	return -EIO;  }  /* @@ -1944,6 +1990,23 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)  		asus->dsts_id = ASUS_WMI_METHODID_DSTS;  	} +	/* +	 * Some devices can have multiple event codes stored in a queue before +	 * the module load if it was unloaded intermittently after calling +	 * the INIT method (enables event handling). The WMI notify handler is +	 * expected to retrieve all event codes until a retrieved code equals +	 * queue end marker (One or Ones). Old codes are flushed from the queue +	 * upon module load. Not enabling this when it should be has minimal +	 * visible impact so fall back if anything goes wrong. +	 */ +	wmi_uid = wmi_get_acpi_device_uid(asus->driver->event_guid); +	if (wmi_uid && !strcmp(wmi_uid, ASUS_ACPI_UID_ATK)) { +		dev_info(dev, "Detected ATK, enable event queue\n"); + +		if (!asus_wmi_notify_queue_flush(asus)) +			asus->wmi_event_queue = true; +	} +  	/* CWAP allow to define the behavior of the Fn+F2 key,  	 * this method doesn't seems to be present on Eee PCs */  	if (asus->driver->quirks->wapf >= 0) | 
