.remove = foo_remove, /* optional, devres is preferred */
.shutdown = foo_shutdown, /* optional, called during shutdown */
.notify_new = foo_notify, /* optional, for event handling */
- .no_notify_data = true, /* optional, enables events containing no additional data */
+ .min_event_size = X, /* optional, simplifies event payload size verification */
.no_singleton = true, /* required for new WMI drivers */
};
module_wmi_driver(foo_driver);
However WMI driver developers should be aware that multiple WMI events can be received concurrently,
so any locking (if necessary) needs to be provided by the WMI driver itself.
-In order to be able to receive WMI events containing no additional event data,
-the ``no_notify_data`` flag inside struct wmi_driver should be set to ``true``.
+The WMI driver can furthermore instruct the WMI driver core to automatically reject WMI events
+that contain a undersized event payload by populating the ``min_event_size`` field inside
+struct wmi_driver. Setting this field to 0 will thus enable the WMI driver to receive WMI events
+without any event payload.
Take a look at drivers/platform/x86/xiaomi-wmi.c for an example WMI event driver.
}
if (wdriver->notify || wdriver->notify_new) {
- if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags) && !wdriver->no_notify_data)
+ if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags) && wdriver->min_event_size)
return -ENODEV;
}
static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj)
{
struct wmi_driver *driver = to_wmi_driver(wblock->dev.dev.driver);
+ struct wmi_buffer dummy = {
+ .length = 0,
+ .data = ZERO_SIZE_PTR,
+ };
struct wmi_buffer buffer;
int ret;
- if (!obj && !driver->no_notify_data) {
+ if (!obj && driver->min_event_size) {
dev_warn(&wblock->dev.dev, "Event contains no event data\n");
return;
}
if (driver->notify_new) {
if (!obj) {
- driver->notify_new(&wblock->dev, NULL);
+ driver->notify_new(&wblock->dev, &dummy);
return;
}
- ret = wmi_unmarshal_acpi_object(obj, &buffer, 0);
+ ret = wmi_unmarshal_acpi_object(obj, &buffer, driver->min_event_size);
if (ret < 0) {
dev_warn(&wblock->dev.dev, "Failed to unmarshal event data: %d\n", ret);
return;
const struct wmi_buffer *buffer)
{
struct bitland_mifs_wmi_data *data = dev_get_drvdata(&wdev->dev);
- const struct bitland_mifs_event *event;
+ const struct bitland_mifs_event *event = buffer->data;
struct bitland_fan_notify_data fan_data;
u8 brightness;
- if (buffer->length < sizeof(*event))
- return;
-
- event = buffer->data;
-
/* Validate event type */
if (event->event_type != WMI_EVENT_TYPE_HOTKEY)
return;
.pm = pm_sleep_ptr(&bitland_mifs_wmi_pm_ops),
},
.id_table = bitland_mifs_wmi_id_table,
+ .min_event_size = sizeof(struct bitland_mifs_event),
.probe = bitland_mifs_wmi_probe,
.notify_new = bitland_mifs_wmi_notify,
};
.name = "dell-wmi",
},
.id_table = dell_wmi_id_table,
+ .min_event_size = sizeof(u16),
.probe = dell_wmi_probe,
.remove = dell_wmi_remove,
.notify = dell_wmi_notify,
.name = "ideapad_wmi",
},
.id_table = ideapad_wmi_ids,
+ .min_event_size = sizeof(u32),
.probe = ideapad_wmi_probe,
.notify = ideapad_wmi_notify,
};
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.id_table = lenovo_wmi_id_table,
+ .min_event_size = sizeof(u8),
.no_singleton = true,
.probe = lenovo_wmi_probe,
.notify = lenovo_wmi_notify,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.id_table = lwmi_events_id_table,
+ .min_event_size = sizeof(u32),
.probe = lwmi_events_probe,
.notify = lwmi_events_notify,
.no_singleton = true,
.name = "lenovo-ymc",
},
.id_table = lenovo_ymc_wmi_id_table,
+ .min_event_size = sizeof(u32),
.probe = lenovo_ymc_probe,
.notify = lenovo_ymc_notify,
};
.name = "yogabook-wmi",
.pm = pm_sleep_ptr(&yogabook_pm_ops),
},
- .no_notify_data = true,
.id_table = yogabook_wmi_id_table,
+ .min_event_size = 0,
.probe = yogabook_wmi_probe,
.remove = yogabook_wmi_remove,
.notify = yogabook_wmi_notify,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.id_table = redmi_wmi_id_table,
+ .min_event_size = 32,
.probe = redmi_wmi_probe,
.notify = redmi_wmi_notify,
.no_singleton = true,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.id_table = uniwill_wmi_id_table,
+ .min_event_size = sizeof(u32),
.notify = uniwill_wmi_notify,
.no_singleton = true,
};
.name = "xiaomi-wmi",
},
.id_table = xiaomi_wmi_id_table,
+ .min_event_size = 0,
.probe = xiaomi_wmi_probe,
.notify_new = xiaomi_wmi_notify,
.no_singleton = true,
* struct wmi_driver - WMI driver structure
* @driver: Driver model structure
* @id_table: List of WMI GUIDs supported by this driver
- * @no_notify_data: Driver supports WMI events which provide no event data
+ * @min_event_size: Minimum event payload size supported by this driver
* @no_singleton: Driver can be instantiated multiple times
* @probe: Callback for device binding
* @remove: Callback for device unbinding
*
* This represents WMI drivers which handle WMI devices. The data inside the buffer
* passed to the @notify_new callback is guaranteed to be aligned on a 8-byte boundary.
+ * The minimum supported size for said buffer can be specified using @min_event_size.
+ * WMI drivers that still use the deprecated @notify callback can still set @min_event_size
+ * to 0 in order to signal that they support WMI events which provide no event data.
*/
struct wmi_driver {
struct device_driver driver;
const struct wmi_device_id *id_table;
- bool no_notify_data;
+ size_t min_event_size;
bool no_singleton;
int (*probe)(struct wmi_device *wdev, const void *context);