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

Commit 72e71de1 authored by Lee, Chun-Yi's avatar Lee, Chun-Yi Committed by Matthew Garrett
Browse files

acer-wmi: add ACER_WMID_v2 interface flag to represent new notebooks



There have new acer notebooks' BIOS provide new WMID_GUID3 and
ACERWMID_EVENT_GUID methods.

Some of machines still keep the old WMID_GUID1 method but more and
more machines were already removed old wmi methods from DSDT.

So, this patch add a new ACER_WMID_v2 interface flag to represent
new acer notebooks, the following is definition:

 + ACER_WMID:
        It means this machine only provides WMID_GUID1/2 methods.

 + ACER_WMID_v2:
        It means this machine provide new WMID_GUID3 and WMID_EVENT_GUID
        methods.
        Some ACER_WMID_v2 machines also provide old WMID_GUID1/2 methods,
        but we still query/set communication device's state by new
        WMID_GUID3 method.

Tested on Acer Travelmate 8572
Tested on Acer Aspire 4739Z

Tested-by: default avatarAceLan Kao <acelan.kao@canonical.com>
Cc: Carlos Corbacho <carlos@strangeworlds.co.uk>
Cc: Matthew Garrett <mjg@redhat.com>
Cc: Dmitry Torokhov <dtor@mail.ru>
Cc: Corentin Chary <corentincj@iksaif.net>
Cc: Thomas Renninger <trenn@suse.de>
Signed-off-by: default avatarLee, Chun-Yi <jlee@suse.com>
Signed-off-by: default avatarMatthew Garrett <mjg@redhat.com>
parent 6a0d89c2
Loading
Loading
Loading
Loading
+213 −203
Original line number Diff line number Diff line
@@ -190,6 +190,7 @@ enum interface_flags {
	ACER_AMW0,
	ACER_AMW0_V2,
	ACER_WMID,
	ACER_WMID_v2,
};

#define ACER_DEFAULT_WIRELESS  0
@@ -877,6 +878,177 @@ static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
	return WMI_execute_u32(method_id, (u32)value, NULL);
}

static acpi_status wmid3_get_device_status(u32 *value, u16 device)
{
	struct wmid3_gds_return_value return_value;
	acpi_status status;
	union acpi_object *obj;
	struct wmid3_gds_input_param params = {
		.function_num = 0x1,
		.hotkey_number = 0x01,
		.devices = device,
	};
	struct acpi_buffer input = {
		sizeof(struct wmid3_gds_input_param),
		&params
	};
	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };

	status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
	if (ACPI_FAILURE(status))
		return status;

	obj = output.pointer;

	if (!obj)
		return AE_ERROR;
	else if (obj->type != ACPI_TYPE_BUFFER) {
		kfree(obj);
		return AE_ERROR;
	}
	if (obj->buffer.length != 8) {
		pr_warn("Unknown buffer length %d\n", obj->buffer.length);
		kfree(obj);
		return AE_ERROR;
	}

	return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
	kfree(obj);

	if (return_value.error_code || return_value.ec_return_value)
		pr_warn("Get 0x%x Device Status failed: 0x%x - 0x%x\n",
			device,
			return_value.error_code,
			return_value.ec_return_value);
	else
		*value = !!(return_value.devices & device);

	return status;
}

static acpi_status wmid_v2_get_u32(u32 *value, u32 cap)
{
	u16 device;

	switch (cap) {
	case ACER_CAP_WIRELESS:
		device = ACER_WMID3_GDS_WIRELESS;
		break;
	case ACER_CAP_BLUETOOTH:
		device = ACER_WMID3_GDS_BLUETOOTH;
		break;
	case ACER_CAP_THREEG:
		device = ACER_WMID3_GDS_THREEG;
		break;
	default:
		return AE_ERROR;
	}
	return wmid3_get_device_status(value, device);
}

static acpi_status wmid3_set_device_status(u32 value, u16 device)
{
	struct wmid3_gds_return_value return_value;
	acpi_status status;
	union acpi_object *obj;
	u16 devices;
	struct wmid3_gds_input_param params = {
		.function_num = 0x1,
		.hotkey_number = 0x01,
		.devices = ACER_WMID3_GDS_WIRELESS |
				ACER_WMID3_GDS_THREEG |
				ACER_WMID3_GDS_WIMAX |
				ACER_WMID3_GDS_BLUETOOTH,
	};
	struct acpi_buffer input = {
		sizeof(struct wmid3_gds_input_param),
		&params
	};
	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
	struct acpi_buffer output2 = { ACPI_ALLOCATE_BUFFER, NULL };

	status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
	if (ACPI_FAILURE(status))
		return status;

	obj = output.pointer;

	if (!obj)
		return AE_ERROR;
	else if (obj->type != ACPI_TYPE_BUFFER) {
		kfree(obj);
		return AE_ERROR;
	}
	if (obj->buffer.length != 8) {
		pr_warning("Unknown buffer length %d\n", obj->buffer.length);
		kfree(obj);
		return AE_ERROR;
	}

	return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
	kfree(obj);

	if (return_value.error_code || return_value.ec_return_value) {
		pr_warning("Get Current Device Status failed: "
			"0x%x - 0x%x\n", return_value.error_code,
			return_value.ec_return_value);
		return status;
	}

	devices = return_value.devices;
	params.function_num = 0x2;
	params.hotkey_number = 0x01;
	params.devices = (value) ? (devices | device) : (devices & ~device);

	status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output2);
	if (ACPI_FAILURE(status))
		return status;

	obj = output2.pointer;

	if (!obj)
		return AE_ERROR;
	else if (obj->type != ACPI_TYPE_BUFFER) {
		kfree(obj);
		return AE_ERROR;
	}
	if (obj->buffer.length != 4) {
		pr_warning("Unknown buffer length %d\n", obj->buffer.length);
		kfree(obj);
		return AE_ERROR;
	}

	return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
	kfree(obj);

	if (return_value.error_code || return_value.ec_return_value)
		pr_warning("Set Device Status failed: "
			"0x%x - 0x%x\n", return_value.error_code,
			return_value.ec_return_value);

	return status;
}

static acpi_status wmid_v2_set_u32(u32 value, u32 cap)
{
	u16 device;

	switch (cap) {
	case ACER_CAP_WIRELESS:
		device = ACER_WMID3_GDS_WIRELESS;
		break;
	case ACER_CAP_BLUETOOTH:
		device = ACER_WMID3_GDS_BLUETOOTH;
		break;
	case ACER_CAP_THREEG:
		device = ACER_WMID3_GDS_THREEG;
		break;
	default:
		return AE_ERROR;
	}
	return wmid3_set_device_status(value, device);
}

static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy)
{
	struct hotkey_function_type_aa *type_aa;
@@ -922,17 +1094,11 @@ static acpi_status WMID_set_capabilities(void)
		return AE_ERROR;
	}

	dmi_walk(type_aa_dmi_decode, NULL);
	if (!has_type_aa) {
	interface->capability |= ACER_CAP_WIRELESS;
	if (devices & 0x40)
		interface->capability |= ACER_CAP_THREEG;
	if (devices & 0x10)
		interface->capability |= ACER_CAP_BLUETOOTH;
	}

	/* WMID always provides brightness methods */
	interface->capability |= ACER_CAP_BRIGHTNESS;

	if (!(devices & 0x20))
		max_brightness = 0x9;
@@ -945,6 +1111,10 @@ static struct wmi_interface wmid_interface = {
	.type = ACER_WMID,
};

static struct wmi_interface wmid_v2_interface = {
	.type = ACER_WMID_v2,
};

/*
 * Generic Device (interface-independent)
 */
@@ -965,6 +1135,14 @@ static acpi_status get_u32(u32 *value, u32 cap)
	case ACER_WMID:
		status = WMID_get_u32(value, cap, interface);
		break;
	case ACER_WMID_v2:
		if (cap & (ACER_CAP_WIRELESS |
			   ACER_CAP_BLUETOOTH |
			   ACER_CAP_THREEG))
			status = wmid_v2_get_u32(value, cap);
		else if (wmi_has_guid(WMID_GUID2))
			status = WMID_get_u32(value, cap, interface);
		break;
	}

	return status;
@@ -998,6 +1176,13 @@ static acpi_status set_u32(u32 value, u32 cap)
			}
		case ACER_WMID:
			return WMID_set_u32(value, cap, interface);
		case ACER_WMID_v2:
			if (cap & (ACER_CAP_WIRELESS |
				   ACER_CAP_BLUETOOTH |
				   ACER_CAP_THREEG))
				return wmid_v2_set_u32(value, cap);
			else if (wmi_has_guid(WMID_GUID2))
				return WMID_set_u32(value, cap, interface);
		default:
			return AE_BAD_PARAMETER;
		}
@@ -1104,186 +1289,6 @@ static void acer_backlight_exit(void)
	backlight_device_unregister(acer_backlight_device);
}

static acpi_status wmid3_get_device_status(u32 *value, u16 device)
{
	struct wmid3_gds_return_value return_value;
	acpi_status status;
	union acpi_object *obj;
	struct wmid3_gds_input_param params = {
		.function_num = 0x1,
		.hotkey_number = 0x01,
		.devices = device,
	};
	struct acpi_buffer input = {
		sizeof(struct wmid3_gds_input_param),
		&params
	};
	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };

	status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
	if (ACPI_FAILURE(status))
		return status;

	obj = output.pointer;

	if (!obj)
		return AE_ERROR;
	else if (obj->type != ACPI_TYPE_BUFFER) {
		kfree(obj);
		return AE_ERROR;
	}
	if (obj->buffer.length != 8) {
		pr_warn("Unknown buffer length %d\n", obj->buffer.length);
		kfree(obj);
		return AE_ERROR;
	}

	return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
	kfree(obj);

	if (return_value.error_code || return_value.ec_return_value)
		pr_warn("Get Device Status failed: 0x%x - 0x%x\n",
			return_value.error_code,
			return_value.ec_return_value);
	else
		*value = !!(return_value.devices & device);

	return status;
}

static acpi_status get_device_status(u32 *value, u32 cap)
{
	if (wmi_has_guid(WMID_GUID3)) {
		u16 device;

		switch (cap) {
		case ACER_CAP_WIRELESS:
			device = ACER_WMID3_GDS_WIRELESS;
			break;
		case ACER_CAP_BLUETOOTH:
			device = ACER_WMID3_GDS_BLUETOOTH;
			break;
		case ACER_CAP_THREEG:
			device = ACER_WMID3_GDS_THREEG;
			break;
		default:
			return AE_ERROR;
		}
		return wmid3_get_device_status(value, device);

	} else {
		return get_u32(value, cap);
	}
}

static acpi_status wmid3_set_device_status(u32 value, u16 device)
{
	struct wmid3_gds_return_value return_value;
	acpi_status status;
	union acpi_object *obj;
	u16 devices;
	struct wmid3_gds_input_param params = {
		.function_num = 0x1,
		.hotkey_number = 0x01,
		.devices = ACER_WMID3_GDS_WIRELESS |
				ACER_WMID3_GDS_THREEG |
				ACER_WMID3_GDS_WIMAX |
				ACER_WMID3_GDS_BLUETOOTH,
	};
	struct acpi_buffer input = {
		sizeof(struct wmid3_gds_input_param),
		&params
	};
	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
	struct acpi_buffer output2 = { ACPI_ALLOCATE_BUFFER, NULL };

	status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
	if (ACPI_FAILURE(status))
		return status;

	obj = output.pointer;

	if (!obj)
		return AE_ERROR;
	else if (obj->type != ACPI_TYPE_BUFFER) {
		kfree(obj);
		return AE_ERROR;
	}
	if (obj->buffer.length != 8) {
		pr_warning("Unknown buffer length %d\n", obj->buffer.length);
		kfree(obj);
		return AE_ERROR;
	}

	return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
	kfree(obj);

	if (return_value.error_code || return_value.ec_return_value) {
		pr_warning("Get Current Device Status failed: "
			"0x%x - 0x%x\n", return_value.error_code,
			return_value.ec_return_value);
		return status;
	}

	devices = return_value.devices;
	params.function_num = 0x2;
	params.hotkey_number = 0x01;
	params.devices = (value) ? (devices | device) : (devices & ~device);

	status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output2);
	if (ACPI_FAILURE(status))
		return status;

	obj = output2.pointer;

	if (!obj)
		return AE_ERROR;
	else if (obj->type != ACPI_TYPE_BUFFER) {
		kfree(obj);
		return AE_ERROR;
	}
	if (obj->buffer.length != 4) {
		pr_warning("Unknown buffer length %d\n", obj->buffer.length);
		kfree(obj);
		return AE_ERROR;
	}

	return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
	kfree(obj);

	if (return_value.error_code || return_value.ec_return_value)
		pr_warning("Set Device Status failed: "
			"0x%x - 0x%x\n", return_value.error_code,
			return_value.ec_return_value);

	return status;
}

static acpi_status set_device_status(u32 value, u32 cap)
{
	if (wmi_has_guid(WMID_GUID3)) {
		u16 device;

		switch (cap) {
		case ACER_CAP_WIRELESS:
			device = ACER_WMID3_GDS_WIRELESS;
			break;
		case ACER_CAP_BLUETOOTH:
			device = ACER_WMID3_GDS_BLUETOOTH;
			break;
		case ACER_CAP_THREEG:
			device = ACER_WMID3_GDS_THREEG;
			break;
		default:
			return AE_ERROR;
		}
		return wmid3_set_device_status(value, device);

	} else {
		return set_u32(value, cap);
	}
}

/*
 * Rfkill devices
 */
@@ -1311,8 +1316,7 @@ static void acer_rfkill_update(struct work_struct *ignored)
	}

	if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) {
		status = wmid3_get_device_status(&state,
				ACER_WMID3_GDS_THREEG);
		status = get_u32(&state, ACER_WMID3_GDS_THREEG);
		if (ACPI_SUCCESS(status))
			rfkill_set_sw_state(threeg_rfkill, !state);
	}
@@ -1326,7 +1330,7 @@ static int acer_rfkill_set(void *data, bool blocked)
	u32 cap = (unsigned long)data;

	if (rfkill_inited) {
		status = set_device_status(!blocked, cap);
		status = set_u32(!blocked, cap);
		if (ACPI_FAILURE(status))
			return -ENODEV;
	}
@@ -1353,7 +1357,7 @@ static struct rfkill *acer_rfkill_register(struct device *dev,
	if (!rfkill_dev)
		return ERR_PTR(-ENOMEM);

	status = get_device_status(&state, cap);
	status = get_u32(&state, cap);

	err = rfkill_register(rfkill_dev);
	if (err) {
@@ -1457,10 +1461,6 @@ static ssize_t show_bool_threeg(struct device *dev,

	pr_info("This threeg sysfs will be removed in 2012"
		" - used by: %s\n", current->comm);
	if (wmi_has_guid(WMID_GUID3))
		status = wmid3_get_device_status(&result,
				ACER_WMID3_GDS_THREEG);
	else
	status = get_u32(&result, ACER_CAP_THREEG);
	if (ACPI_SUCCESS(status))
		return sprintf(buf, "%u\n", result);
@@ -1493,6 +1493,8 @@ static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
		return sprintf(buf, "AMW0 v2\n");
	case ACER_WMID:
		return sprintf(buf, "WMID\n");
	case ACER_WMID_v2:
		return sprintf(buf, "WMID v2\n");
	default:
		return sprintf(buf, "Error!\n");
	}
@@ -1912,12 +1914,20 @@ static int __init acer_wmi_init(void)
	if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
		interface = &wmid_interface;

	if (wmi_has_guid(WMID_GUID3))
		interface = &wmid_v2_interface;

	if (interface)
		dmi_walk(type_aa_dmi_decode, NULL);

	if (wmi_has_guid(WMID_GUID2) && interface) {
		if (ACPI_FAILURE(WMID_set_capabilities())) {
		if (!has_type_aa && ACPI_FAILURE(WMID_set_capabilities())) {
			pr_err("Unable to detect available WMID devices\n");
			return -ENODEV;
		}
	} else if (!wmi_has_guid(WMID_GUID2) && interface) {
		/* WMID always provides brightness methods */
		interface->capability |= ACER_CAP_BRIGHTNESS;
	} else if (!wmi_has_guid(WMID_GUID2) && interface && !has_type_aa) {
		pr_err("No WMID device detection method found\n");
		return -ENODEV;
	}
@@ -1941,7 +1951,7 @@ static int __init acer_wmi_init(void)

	set_quirks();

	if (acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) {
	if (acpi_video_backlight_support()) {
		interface->capability &= ~ACER_CAP_BRIGHTNESS;
		pr_info("Brightness must be controlled by "
		       "generic video driver\n");