mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 04:04:43 +01:00
platform/wmi: Update driver development guide
New WMI drivers should use the new buffer-based WMI API instead of the deprecated ACPI-based API. Update the driver development guide to recommend the buffer-based API to driver developers and explain the purpose of struct wmi_buffer. Also update the ACPI interface documentation to describe the conversion rules for converting ACPI objects into WMI buffers. Reviewed-by: Randy Dunlap <rdunlap@infradead.org> Signed-off-by: Armin Wolf <W_Armin@gmx.de> Link: https://patch.msgid.link/20260116204116.4030-10-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
926a266575
commit
2177a02246
2 changed files with 121 additions and 23 deletions
|
|
@ -104,3 +104,71 @@ holding the notification ID of the event. This method should be evaluated every
|
|||
time an ACPI notification is received, since some ACPI implementations use a
|
||||
queue to store WMI event data items. This queue will overflow after a couple
|
||||
of WMI events are received without retrieving the associated WMI event data.
|
||||
|
||||
Conversion rules for ACPI data types
|
||||
------------------------------------
|
||||
|
||||
Consumers of the ACPI-WMI interface use binary buffers to exchange data with the WMI driver core,
|
||||
with the internal structure of the buffer being only know to the consumers. The WMI driver core is
|
||||
thus responsible for converting the data inside the buffer into an appropriate ACPI data type for
|
||||
consumption by the ACPI firmware. Additionally, any data returned by the various ACPI methods needs
|
||||
to be converted back into a binary buffer.
|
||||
|
||||
The layout of said buffers is defined by the MOF description of the WMI method or data block in
|
||||
question [1]_:
|
||||
|
||||
=============== ======================================================================= =========
|
||||
Data Type Layout Alignment
|
||||
=============== ======================================================================= =========
|
||||
``string`` Starts with an unsigned 16-bit little endian integer specifying 2 bytes
|
||||
the length of the string data in bytes, followed by the string data
|
||||
encoded as UTF-16LE with **optional** NULL termination and padding.
|
||||
Keep in mind that some firmware implementations might depend on the
|
||||
terminating NULL character to be present. Also the padding should
|
||||
always be performed with NULL characters.
|
||||
``boolean`` Single byte where 0 means ``false`` and nonzero means ``true``. 1 byte
|
||||
``sint8`` Signed 8-bit integer. 1 byte
|
||||
``uint8`` Unsigned 8-bit integer. 1 byte
|
||||
``sint16`` Signed 16-bit little endian integer. 2 bytes
|
||||
``uint16`` Unsigned 16-bit little endian integer. 2 bytes
|
||||
``sint32`` Signed 32-bit little endian integer. 4 bytes
|
||||
``uint32`` Unsigned 32-bit little endian integer. 4 bytes
|
||||
``sint64`` Signed 64-bit little endian integer. 8 bytes
|
||||
``uint64`` Unsigned 64-bit little endian integer. 8 bytes
|
||||
``datetime`` A fixed-length 25-character UTF-16LE string with the format 2 bytes
|
||||
*yyyymmddhhmmss.mmmmmmsutc* where *yyyy* is the 4-digit year, *mm* is
|
||||
the 2-digit month, *dd* is the 2-digit day, *hh* is the 2-digit hour
|
||||
based on a 24-hour clock, *mm* is the 2-digit minute, *ss* is the
|
||||
2-digit second, *mmmmmm* is the 6-digit microsecond, *s* is a plus or
|
||||
minus character depending on whether *utc* is a positive or negative
|
||||
offset from UTC (or a colon if the date is an interval). Unpopulated
|
||||
fields should be filled with asterisks.
|
||||
=============== ======================================================================= =========
|
||||
|
||||
Arrays should be aligned based on the alignment of their base type, while objects should be
|
||||
aligned based on the largest alignment of an element inside them.
|
||||
|
||||
All buffers returned by the WMI driver core are 8-byte aligned. When converting ACPI data types
|
||||
into such buffers the following conversion rules apply:
|
||||
|
||||
=============== ============================================================
|
||||
ACPI Data Type Converted into
|
||||
=============== ============================================================
|
||||
Buffer Copied as-is.
|
||||
Integer Converted into a ``uint32``.
|
||||
String Converted into a ``string`` with a terminating NULL character
|
||||
to match the behavior the of the Windows driver.
|
||||
Package Each element inside the package is converted with alignment
|
||||
of the resulting data types being respected. Nested packages
|
||||
are not allowed.
|
||||
=============== ============================================================
|
||||
|
||||
The Windows driver does attempt to handle nested packages, but this results in internal data
|
||||
structures (``_ACPI_METHOD_ARGUMENT_V1``) erroneously being copied into the resulting buffer.
|
||||
ACPI firmware implementations should thus not return nested packages from ACPI methods
|
||||
associated with the ACPI-WMI interface.
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/driver-defined-wmi-data-items
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ to matching WMI devices using a struct wmi_device_id table:
|
|||
.probe = foo_probe,
|
||||
.remove = foo_remove, /* optional, devres is preferred */
|
||||
.shutdown = foo_shutdown, /* optional, called during shutdown */
|
||||
.notify = foo_notify, /* optional, for event handling */
|
||||
.notify_new = foo_notify, /* optional, for event handling */
|
||||
.no_notify_data = true, /* optional, enables events containing no additional data */
|
||||
.no_singleton = true, /* required for new WMI drivers */
|
||||
};
|
||||
|
|
@ -90,9 +90,9 @@ the WMI device and put it in a well-known state for the WMI driver to pick up la
|
|||
or kexec. Most WMI drivers need no special shutdown handling and can thus omit this callback.
|
||||
|
||||
Please note that new WMI drivers are required to be able to be instantiated multiple times,
|
||||
and are forbidden from using any deprecated GUID-based WMI functions. This means that the
|
||||
WMI driver should be prepared for the scenario that multiple matching WMI devices are present
|
||||
on a given machine.
|
||||
and are forbidden from using any deprecated GUID-based or ACPI-based WMI functions. This means
|
||||
that the WMI driver should be prepared for the scenario that multiple matching WMI devices are
|
||||
present on a given machine.
|
||||
|
||||
Because of this, WMI drivers should use the state container design pattern as described in
|
||||
Documentation/driver-api/driver-model/design-patterns.rst.
|
||||
|
|
@ -104,38 +104,37 @@ Documentation/driver-api/driver-model/design-patterns.rst.
|
|||
WMI method drivers
|
||||
------------------
|
||||
|
||||
WMI drivers can call WMI device methods using wmidev_evaluate_method(), the
|
||||
structure of the ACPI buffer passed to this function is device-specific and usually
|
||||
needs some tinkering to get right. Looking at the ACPI tables containing the WMI
|
||||
device usually helps here. The method id and instance number passed to this function
|
||||
are also device-specific, looking at the decoded Binary MOF is usually enough to
|
||||
find the right values.
|
||||
WMI drivers can call WMI device methods using wmidev_invoke_method(). For each WMI method
|
||||
invocation the WMI driver needs to provide the instance number and the method ID, as well as
|
||||
a buffer with the method arguments and optionally a buffer for the results.
|
||||
|
||||
The maximum instance number can be retrieved during runtime using wmidev_instance_count().
|
||||
The layout of said buffers is device-specific and described by the Binary MOF data associated
|
||||
with a given WMI device. Said Binary MOF data also describes the method ID of a given WMI method
|
||||
with the ``WmiMethodId`` qualifier. WMI devices exposing WMI methods usually expose only a single
|
||||
instance (instance number 0), but in theory can expose multiple instances as well. In such a case
|
||||
the number of instances can be retrieved using wmidev_instance_count().
|
||||
|
||||
Take a look at drivers/platform/x86/inspur_platform_profile.c for an example WMI method driver.
|
||||
Take a look at drivers/platform/x86/intel/wmi/thunderbolt.c for an example WMI method driver.
|
||||
|
||||
WMI data block drivers
|
||||
----------------------
|
||||
|
||||
WMI drivers can query WMI device data blocks using wmidev_block_query(), the
|
||||
structure of the returned ACPI object is again device-specific. Some WMI devices
|
||||
also allow for setting data blocks using wmidev_block_set().
|
||||
WMI drivers can query WMI data blocks using wmidev_query_block(), the layout of the returned
|
||||
buffer is again device-specific and described by the Binary MOF data. Some WMI data blocks are
|
||||
also writeable and can be set using wmidev_set_block(). The number of data block instances can
|
||||
again be retrieved using wmidev_instance_count().
|
||||
|
||||
The maximum instance number can also be retrieved using wmidev_instance_count().
|
||||
|
||||
Take a look at drivers/platform/x86/intel/wmi/sbl-fw-update.c for an example
|
||||
WMI data block driver.
|
||||
Take a look at drivers/platform/x86/intel/wmi/sbl-fw-update.c for an example WMI data block driver.
|
||||
|
||||
WMI event drivers
|
||||
-----------------
|
||||
|
||||
WMI drivers can receive WMI events via the notify() callback inside the struct wmi_driver.
|
||||
WMI drivers can receive WMI events via the notify_new() callback inside the struct wmi_driver.
|
||||
The WMI subsystem will then take care of setting up the WMI event accordingly. Please note that
|
||||
the structure of the ACPI object passed to this callback is device-specific, and freeing the
|
||||
ACPI object is being done by the WMI subsystem, not the driver.
|
||||
the layout of the buffer passed to this callback is device-specific, and freeing of the buffer
|
||||
is done by the WMI subsystem itself, not the driver.
|
||||
|
||||
The WMI driver core will take care that the notify() callback will only be called after
|
||||
The WMI driver core will take care that the notify_new() callback will only be called after
|
||||
the probe() callback has been called, and that no events are being received by the driver
|
||||
right before and after calling its remove() or shutdown() callback.
|
||||
|
||||
|
|
@ -147,6 +146,36 @@ the ``no_notify_data`` flag inside struct wmi_driver should be set to ``true``.
|
|||
|
||||
Take a look at drivers/platform/x86/xiaomi-wmi.c for an example WMI event driver.
|
||||
|
||||
Exchanging data with the WMI driver core
|
||||
----------------------------------------
|
||||
|
||||
WMI drivers can exchange data with the WMI driver core using struct wmi_buffer. The internal
|
||||
structure of those buffers is device-specific and only known by the WMI driver. Because of this
|
||||
the WMI driver itself is responsible for parsing and validating the data received from its
|
||||
WMI device.
|
||||
|
||||
The structure of said buffers is described by the MOF data associated with the WMI device in
|
||||
question. When such a buffer contains multiple data items it usually makes sense to define a
|
||||
C structure and use it during parsing. Since the WMI driver core guarantees that all buffers
|
||||
received from a WMI device are aligned on an 8-byte boundary, WMI drivers can simply perform
|
||||
a cast between the WMI buffer data and this C structure.
|
||||
|
||||
This however should only be done after the size of the buffer was verified to be large enough
|
||||
to hold the whole C structure. WMI drivers should reject undersized buffers as they are usually
|
||||
sent by the WMI device to signal an internal error. Oversized buffers however should be accepted
|
||||
to emulate the behavior of the Windows WMI implementation.
|
||||
|
||||
When defining a C structure for parsing WMI buffers the alignment of the data items should be
|
||||
respected. This is especially important for 64-bit integers as those have different alignments
|
||||
on 64-bit (8-byte alignment) and 32-bit (4-byte alignment) architectures. It is thus a good idea
|
||||
to manually specify the alignment of such data items or mark the whole structure as packed when
|
||||
appropriate. Integer data items in general are little-endian integers and should be marked as
|
||||
such using ``__le64`` and friends. When parsing WMI string data items the struct wmi_string should
|
||||
be used as WMI strings have a different layout than C strings.
|
||||
|
||||
See Documentation/wmi/acpi-interface.rst for more information regarding the binary format
|
||||
of WMI data items.
|
||||
|
||||
Handling multiple WMI devices at once
|
||||
-------------------------------------
|
||||
|
||||
|
|
@ -171,6 +200,7 @@ Things to avoid
|
|||
When developing WMI drivers, there are a couple of things which should be avoided:
|
||||
|
||||
- usage of the deprecated GUID-based WMI interface which uses GUIDs instead of WMI device structs
|
||||
- usage of the deprecated ACPI-based WMI interface which uses ACPI objects instead of plain buffers
|
||||
- bypassing of the WMI subsystem when talking to WMI devices
|
||||
- WMI drivers which cannot be instantiated multiple times.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue