platform/x86: wmi: Always evaluate _WED when receiving an event
The ACPI WMI specification states: "The _WED control method is evaluated by the mapper in response to receiving a notification from a control method." This means that _WED should be evaluated unconditionally even if no WMI event consumers are present. Some firmware implementations actually depend on this behavior by storing the event data inside a queue which will fill up if the WMI core stops retrieving event data items due to no consumers being present Fix this by always evaluating _WED even if no WMI event consumers are present. Signed-off-by: Armin Wolf <W_Armin@gmx.de> Link: https://lore.kernel.org/r/20240219115919.16526-4-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
This commit is contained in:
parent
125619112d
commit
56230bd733
|
@ -1206,37 +1206,46 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wmi_notify_driver(struct wmi_block *wblock)
|
static int wmi_get_notify_data(struct wmi_block *wblock, union acpi_object **obj)
|
||||||
{
|
{
|
||||||
struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver);
|
|
||||||
struct acpi_buffer data = { ACPI_ALLOCATE_BUFFER, NULL };
|
struct acpi_buffer data = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||||
union acpi_object *obj = NULL;
|
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
|
|
||||||
if (!driver->no_notify_data) {
|
if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags)) {
|
||||||
status = get_event_data(wblock, &data);
|
*obj = NULL;
|
||||||
if (ACPI_FAILURE(status)) {
|
return 0;
|
||||||
dev_warn(&wblock->dev.dev, "Failed to get event data\n");
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
obj = data.pointer;
|
status = get_event_data(wblock, &data);
|
||||||
if (!obj) {
|
if (ACPI_FAILURE(status)) {
|
||||||
dev_warn(&wblock->dev.dev, "Event contains no event data\n");
|
dev_warn(&wblock->dev.dev, "Failed to get event data\n");
|
||||||
return;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*obj = data.pointer;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj)
|
||||||
|
{
|
||||||
|
struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver);
|
||||||
|
|
||||||
|
if (!obj && !driver->no_notify_data) {
|
||||||
|
dev_warn(&wblock->dev.dev, "Event contains no event data\n");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (driver->notify)
|
if (driver->notify)
|
||||||
driver->notify(&wblock->dev, obj);
|
driver->notify(&wblock->dev, obj);
|
||||||
|
|
||||||
kfree(obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wmi_notify_device(struct device *dev, void *data)
|
static int wmi_notify_device(struct device *dev, void *data)
|
||||||
{
|
{
|
||||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||||
|
union acpi_object *obj;
|
||||||
u32 *event = data;
|
u32 *event = data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!(wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *event))
|
if (!(wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *event))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1246,10 +1255,32 @@ static int wmi_notify_device(struct device *dev, void *data)
|
||||||
* Because of this the WMI driver notify handler takes precedence.
|
* Because of this the WMI driver notify handler takes precedence.
|
||||||
*/
|
*/
|
||||||
if (wblock->dev.dev.driver && wblock->driver_ready) {
|
if (wblock->dev.dev.driver && wblock->driver_ready) {
|
||||||
wmi_notify_driver(wblock);
|
ret = wmi_get_notify_data(wblock, &obj);
|
||||||
|
if (ret >= 0) {
|
||||||
|
wmi_notify_driver(wblock, obj);
|
||||||
|
kfree(obj);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (wblock->handler)
|
if (wblock->handler) {
|
||||||
wblock->handler(*event, wblock->handler_data);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
up_read(&wblock->notify_lock);
|
up_read(&wblock->notify_lock);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue