提交 72e71de1 编写于 作者: L Lee, Chun-Yi 提交者: Matthew Garrett

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: NAceLan 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: NLee, Chun-Yi <jlee@suse.com>
Signed-off-by: NMatthew Garrett <mjg@redhat.com>
上级 6a0d89c2
...@@ -190,6 +190,7 @@ enum interface_flags { ...@@ -190,6 +190,7 @@ enum interface_flags {
ACER_AMW0, ACER_AMW0,
ACER_AMW0_V2, ACER_AMW0_V2,
ACER_WMID, ACER_WMID,
ACER_WMID_v2,
}; };
#define ACER_DEFAULT_WIRELESS 0 #define ACER_DEFAULT_WIRELESS 0
...@@ -877,6 +878,177 @@ static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface) ...@@ -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); 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) static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy)
{ {
struct hotkey_function_type_aa *type_aa; struct hotkey_function_type_aa *type_aa;
...@@ -922,17 +1094,11 @@ static acpi_status WMID_set_capabilities(void) ...@@ -922,17 +1094,11 @@ static acpi_status WMID_set_capabilities(void)
return AE_ERROR; return AE_ERROR;
} }
dmi_walk(type_aa_dmi_decode, NULL); interface->capability |= ACER_CAP_WIRELESS;
if (!has_type_aa) { if (devices & 0x40)
interface->capability |= ACER_CAP_WIRELESS; interface->capability |= ACER_CAP_THREEG;
if (devices & 0x40) if (devices & 0x10)
interface->capability |= ACER_CAP_THREEG; interface->capability |= ACER_CAP_BLUETOOTH;
if (devices & 0x10)
interface->capability |= ACER_CAP_BLUETOOTH;
}
/* WMID always provides brightness methods */
interface->capability |= ACER_CAP_BRIGHTNESS;
if (!(devices & 0x20)) if (!(devices & 0x20))
max_brightness = 0x9; max_brightness = 0x9;
...@@ -945,6 +1111,10 @@ static struct wmi_interface wmid_interface = { ...@@ -945,6 +1111,10 @@ static struct wmi_interface wmid_interface = {
.type = ACER_WMID, .type = ACER_WMID,
}; };
static struct wmi_interface wmid_v2_interface = {
.type = ACER_WMID_v2,
};
/* /*
* Generic Device (interface-independent) * Generic Device (interface-independent)
*/ */
...@@ -965,6 +1135,14 @@ static acpi_status get_u32(u32 *value, u32 cap) ...@@ -965,6 +1135,14 @@ static acpi_status get_u32(u32 *value, u32 cap)
case ACER_WMID: case ACER_WMID:
status = WMID_get_u32(value, cap, interface); status = WMID_get_u32(value, cap, interface);
break; 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; return status;
...@@ -998,6 +1176,13 @@ static acpi_status set_u32(u32 value, u32 cap) ...@@ -998,6 +1176,13 @@ static acpi_status set_u32(u32 value, u32 cap)
} }
case ACER_WMID: case ACER_WMID:
return WMID_set_u32(value, cap, interface); 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: default:
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
} }
...@@ -1104,186 +1289,6 @@ static void acer_backlight_exit(void) ...@@ -1104,186 +1289,6 @@ static void acer_backlight_exit(void)
backlight_device_unregister(acer_backlight_device); 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 * Rfkill devices
*/ */
...@@ -1311,8 +1316,7 @@ static void acer_rfkill_update(struct work_struct *ignored) ...@@ -1311,8 +1316,7 @@ static void acer_rfkill_update(struct work_struct *ignored)
} }
if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) { if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) {
status = wmid3_get_device_status(&state, status = get_u32(&state, ACER_WMID3_GDS_THREEG);
ACER_WMID3_GDS_THREEG);
if (ACPI_SUCCESS(status)) if (ACPI_SUCCESS(status))
rfkill_set_sw_state(threeg_rfkill, !state); rfkill_set_sw_state(threeg_rfkill, !state);
} }
...@@ -1326,7 +1330,7 @@ static int acer_rfkill_set(void *data, bool blocked) ...@@ -1326,7 +1330,7 @@ static int acer_rfkill_set(void *data, bool blocked)
u32 cap = (unsigned long)data; u32 cap = (unsigned long)data;
if (rfkill_inited) { if (rfkill_inited) {
status = set_device_status(!blocked, cap); status = set_u32(!blocked, cap);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return -ENODEV; return -ENODEV;
} }
...@@ -1353,7 +1357,7 @@ static struct rfkill *acer_rfkill_register(struct device *dev, ...@@ -1353,7 +1357,7 @@ static struct rfkill *acer_rfkill_register(struct device *dev,
if (!rfkill_dev) if (!rfkill_dev)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
status = get_device_status(&state, cap); status = get_u32(&state, cap);
err = rfkill_register(rfkill_dev); err = rfkill_register(rfkill_dev);
if (err) { if (err) {
...@@ -1457,11 +1461,7 @@ static ssize_t show_bool_threeg(struct device *dev, ...@@ -1457,11 +1461,7 @@ static ssize_t show_bool_threeg(struct device *dev,
pr_info("This threeg sysfs will be removed in 2012" pr_info("This threeg sysfs will be removed in 2012"
" - used by: %s\n", current->comm); " - used by: %s\n", current->comm);
if (wmi_has_guid(WMID_GUID3)) status = get_u32(&result, ACER_CAP_THREEG);
status = wmid3_get_device_status(&result,
ACER_WMID3_GDS_THREEG);
else
status = get_u32(&result, ACER_CAP_THREEG);
if (ACPI_SUCCESS(status)) if (ACPI_SUCCESS(status))
return sprintf(buf, "%u\n", result); return sprintf(buf, "%u\n", result);
return sprintf(buf, "Read error\n"); return sprintf(buf, "Read error\n");
...@@ -1493,6 +1493,8 @@ static ssize_t show_interface(struct device *dev, struct device_attribute *attr, ...@@ -1493,6 +1493,8 @@ static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "AMW0 v2\n"); return sprintf(buf, "AMW0 v2\n");
case ACER_WMID: case ACER_WMID:
return sprintf(buf, "WMID\n"); return sprintf(buf, "WMID\n");
case ACER_WMID_v2:
return sprintf(buf, "WMID v2\n");
default: default:
return sprintf(buf, "Error!\n"); return sprintf(buf, "Error!\n");
} }
...@@ -1912,12 +1914,20 @@ static int __init acer_wmi_init(void) ...@@ -1912,12 +1914,20 @@ static int __init acer_wmi_init(void)
if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1)) if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
interface = &wmid_interface; 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 (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"); pr_err("Unable to detect available WMID devices\n");
return -ENODEV; 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"); pr_err("No WMID device detection method found\n");
return -ENODEV; return -ENODEV;
} }
...@@ -1941,7 +1951,7 @@ static int __init acer_wmi_init(void) ...@@ -1941,7 +1951,7 @@ static int __init acer_wmi_init(void)
set_quirks(); set_quirks();
if (acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) { if (acpi_video_backlight_support()) {
interface->capability &= ~ACER_CAP_BRIGHTNESS; interface->capability &= ~ACER_CAP_BRIGHTNESS;
pr_info("Brightness must be controlled by " pr_info("Brightness must be controlled by "
"generic video driver\n"); "generic video driver\n");
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册