Commit e04e2b76 authored by Armin Wolf's avatar Armin Wolf Committed by Hans de Goede

platform/x86: wmi: Pass event data directly to legacy notify handlers

The current legacy WMI handlers are susceptible to picking up wrong
WMI event data on systems where different WMI devices share some
notification IDs.

Prevent this by letting the WMI driver core taking care of retrieving
the event data. This also simplifies the legacy WMI handlers and their
implementation inside the WMI driver core.
Reviewed-by: default avatarIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Reviewed-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarArmin Wolf <W_Armin@gmx.de>
Link: https://lore.kernel.org/r/20240901031055.3030-3-W_Armin@gmx.deSigned-off-by: default avatarHans de Goede <hdegoede@redhat.com>
parent 56d8b784
......@@ -1597,15 +1597,13 @@ static void hp_wmi_devm_notify_remove(void *ignored)
}
/* hp_wmi_notify - WMI event notification handler */
static void hp_wmi_notify(u32 value, void *context)
static void hp_wmi_notify(union acpi_object *wobj, void *context)
{
struct hp_wmi_info *temp_info[HP_WMI_MAX_INSTANCES] = {};
struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
struct hp_wmi_sensors *state = context;
struct device *dev = &state->wdev->dev;
struct hp_wmi_event event = {};
struct hp_wmi_info *fan_info;
union acpi_object *wobj;
acpi_status err;
int event_type;
u8 count;
......@@ -1630,20 +1628,15 @@ static void hp_wmi_notify(u32 value, void *context)
* HPBIOS_BIOSEvent instance.
*/
mutex_lock(&state->lock);
err = wmi_get_event_data(value, &out);
if (ACPI_FAILURE(err))
goto out_unlock;
wobj = out.pointer;
if (!wobj)
goto out_unlock;
return;
mutex_lock(&state->lock);
err = populate_event_from_wobj(dev, &event, wobj);
if (err) {
dev_warn(dev, "Bad event data (ACPI type %d)\n", wobj->type);
goto out_free_wobj;
goto out_free;
}
event_type = classify_event(event.name, event.category);
......@@ -1668,13 +1661,10 @@ static void hp_wmi_notify(u32 value, void *context)
break;
}
out_free_wobj:
kfree(wobj);
out_free:
devm_kfree(dev, event.name);
devm_kfree(dev, event.description);
out_unlock:
mutex_unlock(&state->lock);
}
......
......@@ -2223,39 +2223,25 @@ static void acer_rfkill_exit(void)
}
}
static void acer_wmi_notify(u32 value, void *context)
static void acer_wmi_notify(union acpi_object *obj, void *context)
{
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
struct event_return_value return_value;
acpi_status status;
u16 device_state;
const struct key_entry *key;
u32 scancode;
status = wmi_get_event_data(value, &response);
if (status != AE_OK) {
pr_warn("bad event status 0x%x\n", status);
return;
}
obj = (union acpi_object *)response.pointer;
if (!obj)
return;
if (obj->type != ACPI_TYPE_BUFFER) {
pr_warn("Unknown response received %d\n", obj->type);
kfree(obj);
return;
}
if (obj->buffer.length != 8) {
pr_warn("Unknown buffer length %d\n", obj->buffer.length);
kfree(obj);
return;
}
return_value = *((struct event_return_value *)obj->buffer.pointer);
kfree(obj);
switch (return_value.function) {
case WMID_HOTKEY_EVENT:
......
......@@ -4201,28 +4201,15 @@ static void asus_wmi_fnlock_update(struct asus_wmi *asus)
/* WMI events *****************************************************************/
static int asus_wmi_get_event_code(u32 value)
static int asus_wmi_get_event_code(union acpi_object *obj)
{
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
int code;
status = wmi_get_event_data(value, &response);
if (ACPI_FAILURE(status)) {
pr_warn("Failed to get WMI notify code: %s\n",
acpi_format_exception(status));
return -EIO;
}
obj = (union acpi_object *)response.pointer;
if (obj && obj->type == ACPI_TYPE_INTEGER)
code = (int)(obj->integer.value & WMI_EVENT_MASK);
else
code = -EIO;
kfree(obj);
return code;
}
......@@ -4288,10 +4275,10 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
pr_info("Unknown key code 0x%x\n", code);
}
static void asus_wmi_notify(u32 value, void *context)
static void asus_wmi_notify(union acpi_object *obj, void *context)
{
struct asus_wmi *asus = context;
int code = asus_wmi_get_event_code(value);
int code = asus_wmi_get_event_code(obj);
if (code < 0) {
pr_warn("Failed to get notify code: %d\n", code);
......
......@@ -70,20 +70,10 @@ static bool dell_wmi_aio_event_check(u8 *buffer, int length)
return false;
}
static void dell_wmi_aio_notify(u32 value, void *context)
static void dell_wmi_aio_notify(union acpi_object *obj, void *context)
{
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
struct dell_wmi_event *event;
acpi_status status;
status = wmi_get_event_data(value, &response);
if (status != AE_OK) {
pr_info("bad event status 0x%x\n", status);
return;
}
obj = (union acpi_object *)response.pointer;
if (obj) {
unsigned int scancode = 0;
......@@ -114,7 +104,6 @@ static void dell_wmi_aio_notify(u32 value, void *context)
break;
}
}
kfree(obj);
}
static int __init dell_wmi_aio_input_setup(void)
......
......@@ -834,28 +834,16 @@ static struct attribute *hp_wmi_attrs[] = {
};
ATTRIBUTE_GROUPS(hp_wmi);
static void hp_wmi_notify(u32 value, void *context)
static void hp_wmi_notify(union acpi_object *obj, void *context)
{
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
u32 event_id, event_data;
union acpi_object *obj;
acpi_status status;
u32 *location;
int key_code;
status = wmi_get_event_data(value, &response);
if (status != AE_OK) {
pr_info("bad event status 0x%x\n", status);
return;
}
obj = (union acpi_object *)response.pointer;
if (!obj)
return;
if (obj->type != ACPI_TYPE_BUFFER) {
pr_info("Unknown response received %d\n", obj->type);
kfree(obj);
return;
}
......@@ -872,10 +860,8 @@ static void hp_wmi_notify(u32 value, void *context)
event_data = *(location + 2);
} else {
pr_info("Unknown buffer length %d\n", obj->buffer.length);
kfree(obj);
return;
}
kfree(obj);
switch (event_id) {
case HPWMI_DOCK_EVENT:
......
......@@ -734,26 +734,14 @@ static void huawei_wmi_process_key(struct input_dev *idev, int code)
sparse_keymap_report_entry(idev, key, 1, true);
}
static void huawei_wmi_input_notify(u32 value, void *context)
static void huawei_wmi_input_notify(union acpi_object *obj, void *context)
{
struct input_dev *idev = (struct input_dev *)context;
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
status = wmi_get_event_data(value, &response);
if (ACPI_FAILURE(status)) {
dev_err(&idev->dev, "Unable to get event data\n");
return;
}
obj = (union acpi_object *)response.pointer;
if (obj && obj->type == ACPI_TYPE_INTEGER)
huawei_wmi_process_key(idev, obj->integer.value);
else
dev_err(&idev->dev, "Bad response type\n");
kfree(response.pointer);
}
static int huawei_wmi_input_setup(struct device *dev, const char *guid)
......
......@@ -205,21 +205,11 @@ static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u
return (union acpi_object *)buffer.pointer;
}
static void wmi_notify(u32 value, void *context)
static void wmi_notify(union acpi_object *obj, void *context)
{
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
long data = (long)context;
pr_debug("event guid %li\n", data);
status = wmi_get_event_data(value, &response);
if (ACPI_FAILURE(status)) {
pr_err("Bad event status 0x%x\n", status);
return;
}
obj = (union acpi_object *)response.pointer;
if (!obj)
return;
......@@ -241,7 +231,6 @@ static void wmi_notify(u32 value, void *context)
pr_debug("Type: %i Eventcode: 0x%llx\n", obj->type,
obj->integer.value);
kfree(response.pointer);
}
static void wmi_input_setup(void)
......
......@@ -170,20 +170,9 @@ static const struct backlight_ops msi_backlight_ops = {
.update_status = bl_set_status,
};
static void msi_wmi_notify(u32 value, void *context)
static void msi_wmi_notify(union acpi_object *obj, void *context)
{
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
struct key_entry *key;
union acpi_object *obj;
acpi_status status;
status = wmi_get_event_data(value, &response);
if (status != AE_OK) {
pr_info("bad event status 0x%x\n", status);
return;
}
obj = (union acpi_object *)response.pointer;
if (obj && obj->type == ACPI_TYPE_INTEGER) {
int eventcode = obj->integer.value;
......@@ -192,7 +181,7 @@ static void msi_wmi_notify(u32 value, void *context)
eventcode);
if (!key) {
pr_info("Unknown key pressed - %x\n", eventcode);
goto msi_wmi_notify_exit;
return;
}
if (event_wmi->quirk_last_pressed) {
......@@ -204,7 +193,7 @@ static void msi_wmi_notify(u32 value, void *context)
pr_debug("Suppressed key event 0x%X - "
"Last press was %lld us ago\n",
key->code, ktime_to_us(diff));
goto msi_wmi_notify_exit;
return;
}
last_pressed = cur;
}
......@@ -221,9 +210,6 @@ static void msi_wmi_notify(u32 value, void *context)
}
} else
pr_info("Unknown event received\n");
msi_wmi_notify_exit:
kfree(response.pointer);
}
static int __init msi_wmi_backlight_setup(void)
......
......@@ -32,26 +32,13 @@ static const struct key_entry toshiba_wmi_keymap[] __initconst = {
{ KE_END, 0 }
};
static void toshiba_wmi_notify(u32 value, void *context)
static void toshiba_wmi_notify(union acpi_object *obj, void *context)
{
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
status = wmi_get_event_data(value, &response);
if (ACPI_FAILURE(status)) {
pr_err("Bad event status 0x%x\n", status);
return;
}
obj = (union acpi_object *)response.pointer;
if (!obj)
return;
/* TODO: Add proper checks once we have data */
pr_debug("Unknown event received, obj type %x\n", obj->type);
kfree(response.pointer);
}
static const struct dmi_system_id toshiba_wmi_dmi_table[] __initconst = {
......
......@@ -1227,40 +1227,33 @@ static int wmi_notify_device(struct device *dev, void *data)
if (!(wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *event))
return 0;
/* The ACPI WMI specification says that _WED should be
* evaluated every time an notification is received, even
* if no consumers are present.
*
* Some firmware implementations actually depend on this
* by using a queue for events which will fill up if the
* WMI driver core stops evaluating _WED due to missing
* WMI event consumers.
*/
ret = wmi_get_notify_data(wblock, &obj);
if (ret < 0)
return -EIO;
down_read(&wblock->notify_lock);
/* The WMI driver notify handler conflicts with the legacy WMI handler.
* Because of this the WMI driver notify handler takes precedence.
*/
if (wblock->dev.dev.driver && wblock->driver_ready) {
ret = wmi_get_notify_data(wblock, &obj);
if (ret >= 0) {
wmi_notify_driver(wblock, obj);
kfree(obj);
}
wmi_notify_driver(wblock, obj);
} else {
if (wblock->handler) {
wblock->handler(*event, wblock->handler_data);
} else {
/* The ACPI WMI specification says that _WED should be
* evaluated every time an notification is received, even
* if no consumers are present.
*
* Some firmware implementations actually depend on this
* by using a queue for events which will fill up if the
* WMI driver core stops evaluating _WED due to missing
* WMI event consumers.
*
* Because of this we need this seemingly useless call to
* wmi_get_notify_data() which in turn evaluates _WED.
*/
ret = wmi_get_notify_data(wblock, &obj);
if (ret >= 0)
kfree(obj);
}
if (wblock->handler)
wblock->handler(obj, wblock->handler_data);
}
up_read(&wblock->notify_lock);
kfree(obj);
acpi_bus_generate_netlink_event("wmi", acpi_dev_name(wblock->acpi_device), *event, 0);
return -EBUSY;
......
......@@ -386,7 +386,7 @@ extern bool acpi_is_pnp_device(struct acpi_device *);
#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)
typedef void (*wmi_notify_handler) (u32 value, void *context);
typedef void (*wmi_notify_handler) (union acpi_object *data, void *context);
int wmi_instance_count(const char *guid);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment