aboutsummaryrefslogtreecommitdiffstats
path: root/Documentation/wmi
diff options
context:
space:
mode:
Diffstat (limited to 'Documentation/wmi')
-rw-r--r--Documentation/wmi/acpi-interface.rst99
-rw-r--r--Documentation/wmi/devices/dell-wmi-ddv.rst297
-rw-r--r--Documentation/wmi/devices/index.rst22
-rw-r--r--Documentation/wmi/devices/msi-wmi-platform.rst194
-rw-r--r--Documentation/wmi/devices/wmi-bmof.rst25
-rw-r--r--Documentation/wmi/driver-development-guide.rst178
-rw-r--r--Documentation/wmi/index.rst20
7 files changed, 835 insertions, 0 deletions
diff --git a/Documentation/wmi/acpi-interface.rst b/Documentation/wmi/acpi-interface.rst
new file mode 100644
index 000000000000..06fb7fcf4413
--- /dev/null
+++ b/Documentation/wmi/acpi-interface.rst
@@ -0,0 +1,99 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+==================
+ACPI WMI interface
+==================
+
+The ACPI WMI interface is a proprietary extension of the ACPI specification made
+by Microsoft to allow hardware vendors to embed WMI (Windows Management Instrumentation)
+objects inside their ACPI firmware. Typical functions implemented over ACPI WMI
+are hotkey events on modern notebooks and configuration of BIOS options.
+
+PNP0C14 ACPI device
+-------------------
+
+Discovery of WMI objects is handled by defining ACPI devices with a PNP ID
+of ``PNP0C14``. These devices will contain a set of ACPI buffers and methods
+used for mapping and execution of WMI methods and/or queries. If there exist
+multiple of such devices, then each device is required to have a
+unique ACPI UID.
+
+_WDG buffer
+-----------
+
+The ``_WDG`` buffer is used to discover WMI objects and is required to be
+static. Its internal structure consists of data blocks with a size of 20 bytes,
+containing the following data:
+
+======= =============== =====================================================
+Offset Size (in bytes) Content
+======= =============== =====================================================
+0x00 16 128 bit Variant 2 object GUID.
+0x10 2 2 character method ID or single byte notification ID.
+0x12 1 Object instance count.
+0x13 1 Object flags.
+======= =============== =====================================================
+
+The WMI object flags control whether the method or notification ID is used:
+
+- 0x1: Data block usage is expensive and must be explicitly enabled/disabled.
+- 0x2: Data block contains WMI methods.
+- 0x4: Data block contains ASCIZ string.
+- 0x8: Data block describes a WMI event, use notification ID instead
+ of method ID.
+
+Each WMI object GUID can appear multiple times inside a system.
+The method/notification ID is used to construct the ACPI method names used for
+interacting with the WMI object.
+
+WQxx ACPI methods
+-----------------
+
+If a data block does not contain WMI methods, then its content can be retrieved
+by this required ACPI method. The last two characters of the ACPI method name
+are the method ID of the data block to query. Their single parameter is an
+integer describing the instance which should be queried. This parameter can be
+omitted if the data block contains only a single instance.
+
+WSxx ACPI methods
+-----------------
+
+Similar to the ``WQxx`` ACPI methods, except that it is optional and takes an
+additional buffer as its second argument. The instance argument also cannot
+be omitted.
+
+WMxx ACPI methods
+-----------------
+
+Used for executing WMI methods associated with a data block. The last two
+characters of the ACPI method name are the method ID of the data block
+containing the WMI methods. Their first parameter is a integer describing the
+instance which methods should be executed. The second parameter is an integer
+describing the WMI method ID to execute, and the third parameter is a buffer
+containing the WMI method parameters. If the data block is marked as containing
+an ASCIZ string, then this buffer should contain an ASCIZ string. The ACPI
+method will return the result of the executed WMI method.
+
+WExx ACPI methods
+-----------------
+
+Used for optionally enabling/disabling WMI events, the last two characters of
+the ACPI method are the notification ID of the data block describing the WMI
+event as hexadecimal value. Their first parameter is an integer with a value
+of 0 if the WMI event should be disabled, other values will enable
+the WMI event.
+
+WCxx ACPI methods
+-----------------
+Similar to the ``WExx`` ACPI methods, except that it controls data collection
+instead of events and thus the last two characters of the ACPI method name are
+the method ID of the data block to enable/disable.
+
+_WED ACPI method
+----------------
+
+Used to retrieve additional WMI event data, its single parameter is a integer
+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.
diff --git a/Documentation/wmi/devices/dell-wmi-ddv.rst b/Documentation/wmi/devices/dell-wmi-ddv.rst
new file mode 100644
index 000000000000..2fcdfcf03327
--- /dev/null
+++ b/Documentation/wmi/devices/dell-wmi-ddv.rst
@@ -0,0 +1,297 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+============================================
+Dell DDV WMI interface driver (dell-wmi-ddv)
+============================================
+
+Introduction
+============
+
+Many Dell notebooks made after ~2020 support a WMI-based interface for
+retrieving various system data like battery temperature, ePPID, diagostic data
+and fan/thermal sensor data.
+
+This interface is likely used by the `Dell Data Vault` software on Windows,
+so it was called `DDV`. Currently the ``dell-wmi-ddv`` driver supports
+version 2 and 3 of the interface, with support for new interface versions
+easily added.
+
+.. warning:: The interface is regarded as internal by Dell, so no vendor
+ documentation is available. All knowledge was thus obtained by
+ trial-and-error, please keep that in mind.
+
+Dell ePPID (electronic Piece Part Identification)
+=================================================
+
+The Dell ePPID is used to uniquely identify components in Dell machines,
+including batteries. It has a form similar to `CC-PPPPPP-MMMMM-YMD-SSSS-FFF`
+and contains the following information:
+
+* Country code of origin (CC).
+* Part number with the first character being a filling number (PPPPPP).
+* Manufacture Identification (MMMMM).
+* Manufacturing Year/Month/Date (YMD) in base 36, with Y being the last digit
+ of the year.
+* Manufacture Sequence Number (SSSS).
+* Optional Firmware Version/Revision (FFF).
+
+The `eppidtool <https://pypi.org/project/eppidtool>`_ python utility can be used
+to decode and display this information.
+
+All information regarding the Dell ePPID was gathered using Dell support
+documentation and `this website <https://telcontar.net/KBK/Dell/date_codes>`_.
+
+WMI interface description
+=========================
+
+The WMI interface description can be decoded from the embedded binary MOF (bmof)
+data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
+
+::
+
+ [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("WMI Function"), guid("{8A42EA14-4F2A-FD45-6422-0087F7A7E608}")]
+ class DDVWmiMethodFunction {
+ [key, read] string InstanceName;
+ [read] boolean Active;
+
+ [WmiMethodId(1), Implemented, read, write, Description("Return Battery Design Capacity.")] void BatteryDesignCapacity([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(2), Implemented, read, write, Description("Return Battery Full Charge Capacity.")] void BatteryFullChargeCapacity([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(3), Implemented, read, write, Description("Return Battery Manufacture Name.")] void BatteryManufactureName([in] uint32 arg2, [out] string argr);
+ [WmiMethodId(4), Implemented, read, write, Description("Return Battery Manufacture Date.")] void BatteryManufactureDate([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(5), Implemented, read, write, Description("Return Battery Serial Number.")] void BatterySerialNumber([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(6), Implemented, read, write, Description("Return Battery Chemistry Value.")] void BatteryChemistryValue([in] uint32 arg2, [out] string argr);
+ [WmiMethodId(7), Implemented, read, write, Description("Return Battery Temperature.")] void BatteryTemperature([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(8), Implemented, read, write, Description("Return Battery Current.")] void BatteryCurrent([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(9), Implemented, read, write, Description("Return Battery Voltage.")] void BatteryVoltage([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(10), Implemented, read, write, Description("Return Battery Manufacture Access(MA code).")] void BatteryManufactureAceess([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(11), Implemented, read, write, Description("Return Battery Relative State-Of-Charge.")] void BatteryRelativeStateOfCharge([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(12), Implemented, read, write, Description("Return Battery Cycle Count")] void BatteryCycleCount([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(13), Implemented, read, write, Description("Return Battery ePPID")] void BatteryePPID([in] uint32 arg2, [out] string argr);
+ [WmiMethodId(14), Implemented, read, write, Description("Return Battery Raw Analytics Start")] void BatteryeRawAnalyticsStart([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(15), Implemented, read, write, Description("Return Battery Raw Analytics")] void BatteryeRawAnalytics([in] uint32 arg2, [out] uint32 RawSize, [out, WmiSizeIs("RawSize") : ToInstance] uint8 RawData[]);
+ [WmiMethodId(16), Implemented, read, write, Description("Return Battery Design Voltage.")] void BatteryDesignVoltage([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(17), Implemented, read, write, Description("Return Battery Raw Analytics A Block")] void BatteryeRawAnalyticsABlock([in] uint32 arg2, [out] uint32 RawSize, [out, WmiSizeIs("RawSize") : ToInstance] uint8 RawData[]);
+ [WmiMethodId(18), Implemented, read, write, Description("Return Version.")] void ReturnVersion([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(32), Implemented, read, write, Description("Return Fan Sensor Information")] void FanSensorInformation([in] uint32 arg2, [out] uint32 RawSize, [out, WmiSizeIs("RawSize") : ToInstance] uint8 RawData[]);
+ [WmiMethodId(34), Implemented, read, write, Description("Return Thermal Sensor Information")] void ThermalSensorInformation([in] uint32 arg2, [out] uint32 RawSize, [out, WmiSizeIs("RawSize") : ToInstance] uint8 RawData[]);
+ };
+
+Each WMI method takes an ACPI buffer containing a 32-bit index as input argument,
+with the first 8 bit being used to specify the battery when using battery-related
+WMI methods. Other WMI methods may ignore this argument or interpret it
+differently. The WMI method output format varies:
+
+* if the function has only a single output, then an ACPI object
+ of the corresponding type is returned
+* if the function has multiple outputs, when an ACPI package
+ containing the outputs in the same order is returned
+
+The format of the output should be thoroughly checked, since many methods can
+return malformed data in case of an error.
+
+The data format of many battery-related methods seems to be based on the
+`Smart Battery Data Specification`, so unknown battery-related methods are
+likely to follow this standard in some way.
+
+WMI method GetBatteryDesignCapacity()
+-------------------------------------
+
+Returns the design capacity of the battery in mAh as an u16.
+
+WMI method BatteryFullCharge()
+------------------------------
+
+Returns the full charge capacity of the battery in mAh as an u16.
+
+WMI method BatteryManufactureName()
+-----------------------------------
+
+Returns the manufacture name of the battery as an ASCII string.
+
+WMI method BatteryManufactureDate()
+-----------------------------------
+
+Returns the manufacture date of the battery as an u16.
+The date is encoded in the following manner:
+
+- bits 0 to 4 contain the manufacture day.
+- bits 5 to 8 contain the manufacture month.
+- bits 9 to 15 contain the manufacture year biased by 1980.
+
+.. note::
+ The data format needs to be verified on more machines.
+
+WMI method BatterySerialNumber()
+--------------------------------
+
+Returns the serial number of the battery as an u16.
+
+WMI method BatteryChemistryValue()
+----------------------------------
+
+Returns the chemistry of the battery as an ASCII string.
+Known values are:
+
+- "Li-I" for Li-Ion
+
+WMI method BatteryTemperature()
+-------------------------------
+
+Returns the temperature of the battery in tenth degree kelvin as an u16.
+
+WMI method BatteryCurrent()
+---------------------------
+
+Returns the current flow of the battery in mA as an s16.
+Negative values indicate discharging.
+
+WMI method BatteryVoltage()
+---------------------------
+
+Returns the voltage flow of the battery in mV as an u16.
+
+WMI method BatteryManufactureAccess()
+-------------------------------------
+
+Returns a manufacture-defined value as an u16.
+
+WMI method BatteryRelativeStateOfCharge()
+-----------------------------------------
+
+Returns the capacity of the battery in percent as an u16.
+
+WMI method BatteryCycleCount()
+------------------------------
+
+Returns the cycle count of the battery as an u16.
+
+WMI method BatteryePPID()
+-------------------------
+
+Returns the ePPID of the battery as an ASCII string.
+
+WMI method BatteryeRawAnalyticsStart()
+--------------------------------------
+
+Performs an analysis of the battery and returns a status code:
+
+- ``0x0``: Success
+- ``0x1``: Interface not supported
+- ``0xfffffffe``: Error/Timeout
+
+.. note::
+ The meaning of this method is still largely unknown.
+
+WMI method BatteryeRawAnalytics()
+---------------------------------
+
+Returns a buffer usually containing 12 blocks of analytics data.
+Those blocks contain:
+
+- a block number starting with 0 (u8)
+- 31 bytes of unknown data
+
+.. note::
+ The meaning of this method is still largely unknown.
+
+WMI method BatteryDesignVoltage()
+---------------------------------
+
+Returns the design voltage of the battery in mV as an u16.
+
+WMI method BatteryeRawAnalyticsABlock()
+---------------------------------------
+
+Returns a single block of analytics data, with the second byte
+of the index being used for selecting the block number.
+
+*Supported since WMI interface version 3!*
+
+.. note::
+ The meaning of this method is still largely unknown.
+
+WMI method ReturnVersion()
+--------------------------
+
+Returns the WMI interface version as an u32.
+
+WMI method FanSensorInformation()
+---------------------------------
+
+Returns a buffer containing fan sensor entries, terminated
+with a single ``0xff``.
+Those entries contain:
+
+- fan type (u8)
+- fan speed in RPM (little endian u16)
+
+WMI method ThermalSensorInformation()
+-------------------------------------
+
+Returns a buffer containing thermal sensor entries, terminated
+with a single ``0xff``.
+Those entries contain:
+
+- thermal type (u8)
+- current temperature (s8)
+- min. temperature (s8)
+- max. temperature (s8)
+- unknown field (u8)
+
+.. note::
+ TODO: Find out what the meaning of the last byte is.
+
+ACPI battery matching algorithm
+===============================
+
+The algorithm used to match ACPI batteries to indices is based on information
+which was found inside the logging messages of the OEM software.
+
+Basically for each new ACPI battery, the serial numbers of the batteries behind
+indices 1 till 3 are compared with the serial number of the ACPI battery.
+Since the serial number of the ACPI battery can either be encoded as a normal
+integer or as a hexadecimal value, both cases need to be checked. The first
+index with a matching serial number is then selected.
+
+A serial number of 0 indicates that the corresponding index is not associated
+with an actual battery, or that the associated battery is not present.
+
+Some machines like the Dell Inspiron 3505 only support a single battery and thus
+ignore the battery index. Because of this the driver depends on the ACPI battery
+hook mechanism to discover batteries.
+
+.. note::
+ The ACPI battery matching algorithm currently used inside the driver is
+ outdated and does not match the algorithm described above. The reasons for
+ this are differences in the handling of the ToHexString() ACPI opcode between
+ Linux and Windows, which distorts the serial number of ACPI batteries on many
+ machines. Until this issue is resolved, the driver cannot use the above
+ algorithm.
+
+Reverse-Engineering the DDV WMI interface
+=========================================
+
+1. Find a supported Dell notebook, usually made after ~2020.
+2. Dump the ACPI tables and search for the WMI device (usually called "ADDV").
+3. Decode the corresponding bmof data and look at the ASL code.
+4. Try to deduce the meaning of a certain WMI method by comparing the control
+ flow with other ACPI methods (_BIX or _BIF for battery related methods
+ for example).
+5. Use the built-in UEFI diagostics to view sensor types/values for fan/thermal
+ related methods (sometimes overwriting static ACPI data fields can be used
+ to test different sensor type values, since on some machines this data is
+ not reinitialized upon a warm reset).
+
+Alternatively:
+
+1. Load the ``dell-wmi-ddv`` driver, use the ``force`` module param
+ if necessary.
+2. Use the debugfs interface to access the raw fan/thermal sensor buffer data.
+3. Compare the data with the built-in UEFI diagnostics.
+
+In case the DDV WMI interface version available on your Dell notebook is not
+supported or you are seeing unknown fan/thermal sensors, please submit a
+bugreport on `bugzilla <https://bugzilla.kernel.org>`_ so they can be added
+to the ``dell-wmi-ddv`` driver.
+
+See Documentation/admin-guide/reporting-issues.rst for further information.
diff --git a/Documentation/wmi/devices/index.rst b/Documentation/wmi/devices/index.rst
new file mode 100644
index 000000000000..c08735a9d7df
--- /dev/null
+++ b/Documentation/wmi/devices/index.rst
@@ -0,0 +1,22 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+=============================
+Driver-specific Documentation
+=============================
+
+This section provides information about various devices supported by
+the Linux kernel, their protocols and driver details.
+
+.. toctree::
+ :maxdepth: 1
+ :numbered:
+ :glob:
+
+ *
+
+.. only:: subproject and html
+
+ Indices
+ =======
+
+ * :ref:`genindex`
diff --git a/Documentation/wmi/devices/msi-wmi-platform.rst b/Documentation/wmi/devices/msi-wmi-platform.rst
new file mode 100644
index 000000000000..29b1b2e6d42c
--- /dev/null
+++ b/Documentation/wmi/devices/msi-wmi-platform.rst
@@ -0,0 +1,194 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+===================================================
+MSI WMI Platform Features driver (msi-wmi-platform)
+===================================================
+
+Introduction
+============
+
+Many MSI notebooks support various features like reading fan sensors. This features are controlled
+by the embedded controller, with the ACPI firmware exposing a standard ACPI WMI interface on top
+of the embedded controller interface.
+
+WMI interface description
+=========================
+
+The WMI interface description can be decoded from the embedded binary MOF (bmof)
+data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
+
+::
+
+ [WMI, Locale("MS\0x409"),
+ Description("This class contains the definition of the package used in other classes"),
+ guid("{ABBC0F60-8EA1-11d1-00A0-C90629100000}")]
+ class Package {
+ [WmiDataId(1), read, write, Description("16 bytes of data")] uint8 Bytes[16];
+ };
+
+ [WMI, Locale("MS\0x409"),
+ Description("This class contains the definition of the package used in other classes"),
+ guid("{ABBC0F63-8EA1-11d1-00A0-C90629100000}")]
+ class Package_32 {
+ [WmiDataId(1), read, write, Description("32 bytes of data")] uint8 Bytes[32];
+ };
+
+ [WMI, Dynamic, Provider("WmiProv"), Locale("MS\0x409"),
+ Description("Class used to operate methods on a package"),
+ guid("{ABBC0F6E-8EA1-11d1-00A0-C90629100000}")]
+ class MSI_ACPI {
+ [key, read] string InstanceName;
+ [read] boolean Active;
+
+ [WmiMethodId(1), Implemented, read, write, Description("Return the contents of a package")]
+ void GetPackage([out, id(0)] Package Data);
+
+ [WmiMethodId(2), Implemented, read, write, Description("Set the contents of a package")]
+ void SetPackage([in, id(0)] Package Data);
+
+ [WmiMethodId(3), Implemented, read, write, Description("Return the contents of a package")]
+ void Get_EC([out, id(0)] Package_32 Data);
+
+ [WmiMethodId(4), Implemented, read, write, Description("Set the contents of a package")]
+ void Set_EC([in, id(0)] Package_32 Data);
+
+ [WmiMethodId(5), Implemented, read, write, Description("Return the contents of a package")]
+ void Get_BIOS([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(6), Implemented, read, write, Description("Set the contents of a package")]
+ void Set_BIOS([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(7), Implemented, read, write, Description("Return the contents of a package")]
+ void Get_SMBUS([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(8), Implemented, read, write, Description("Set the contents of a package")]
+ void Set_SMBUS([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(9), Implemented, read, write, Description("Return the contents of a package")]
+ void Get_MasterBattery([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(10), Implemented, read, write, Description("Set the contents of a package")]
+ void Set_MasterBattery([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(11), Implemented, read, write, Description("Return the contents of a package")]
+ void Get_SlaveBattery([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(12), Implemented, read, write, Description("Set the contents of a package")]
+ void Set_SlaveBattery([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(13), Implemented, read, write, Description("Return the contents of a package")]
+ void Get_Temperature([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(14), Implemented, read, write, Description("Set the contents of a package")]
+ void Set_Temperature([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(15), Implemented, read, write, Description("Return the contents of a package")]
+ void Get_Thermal([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(16), Implemented, read, write, Description("Set the contents of a package")]
+ void Set_Thermal([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(17), Implemented, read, write, Description("Return the contents of a package")]
+ void Get_Fan([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(18), Implemented, read, write, Description("Set the contents of a package")]
+ void Set_Fan([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(19), Implemented, read, write, Description("Return the contents of a package")]
+ void Get_Device([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(20), Implemented, read, write, Description("Set the contents of a package")]
+ void Set_Device([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(21), Implemented, read, write, Description("Return the contents of a package")]
+ void Get_Power([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(22), Implemented, read, write, Description("Set the contents of a package")]
+ void Set_Power([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(23), Implemented, read, write, Description("Return the contents of a package")]
+ void Get_Debug([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(24), Implemented, read, write, Description("Set the contents of a package")]
+ void Set_Debug([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(25), Implemented, read, write, Description("Return the contents of a package")]
+ void Get_AP([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(26), Implemented, read, write, Description("Set the contents of a package")]
+ void Set_AP([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(27), Implemented, read, write, Description("Return the contents of a package")]
+ void Get_Data([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(28), Implemented, read, write, Description("Set the contents of a package")]
+ void Set_Data([in, out, id(0)] Package_32 Data);
+
+ [WmiMethodId(29), Implemented, read, write, Description("Return the contents of a package")]
+ void Get_WMI([out, id(0)] Package_32 Data);
+ };
+
+Due to a peculiarity in how Windows handles the ``CreateByteField()`` ACPI operator (errors only
+happen when a invalid byte field is ultimately accessed), all methods require a 32 byte input
+buffer, even if the Binay MOF says otherwise.
+
+The input buffer contains a single byte to select the subfeature to be accessed and 31 bytes of
+input data, the meaning of which depends on the subfeature being accessed.
+
+The output buffer contains a singe byte which signals success or failure (``0x00`` on failure)
+and 31 bytes of output data, the meaning if which depends on the subfeature being accessed.
+
+WMI method Get_EC()
+-------------------
+
+Returns embedded controller information, the selected subfeature does not matter. The output
+data contains a flag byte and a 28 byte controller firmware version string.
+
+The first 4 bits of the flag byte contain the minor version of the embedded controller interface,
+with the next 2 bits containing the major version of the embedded controller interface.
+
+The 7th bit signals if the embedded controller page chaged (exact meaning is unknown), and the
+last bit signals if the platform is a Tigerlake platform.
+
+The MSI software seems to only use this interface when the last bit is set.
+
+WMI method Get_Fan()
+--------------------
+
+Fan speed sensors can be accessed by selecting subfeature ``0x00``. The output data contains
+up to four 16-bit fan speed readings in big-endian format. Most machines do not support all
+four fan speed sensors, so the remaining reading are hardcoded to ``0x0000``.
+
+The fan RPM readings can be calculated with the following formula:
+
+ RPM = 480000 / <fan speed reading>
+
+If the fan speed reading is zero, then the fan RPM is zero too.
+
+WMI method Get_WMI()
+--------------------
+
+Returns the version of the ACPI WMI interface, the selected subfeature does not matter.
+The output data contains two bytes, the first one contains the major version and the last one
+contains the minor revision of the ACPI WMI interface.
+
+The MSI software seems to only use this interface when the major version is greater than two.
+
+Reverse-Engineering the MSI WMI Platform interface
+==================================================
+
+.. warning:: Randomly poking the embedded controller interface can potentially cause damage
+ to the machine and other unwanted side effects, please be careful.
+
+The underlying embedded controller interface is used by the ``msi-ec`` driver, and it seems
+that many methods just copy a part of the embedded controller memory into the output buffer.
+
+This means that the remaining WMI methods can be reverse-engineered by looking which part of
+the embedded controller memory is accessed by the ACPI AML code. The driver also supports a
+debugfs interface for directly executing WMI methods. Additionally, any safety checks regarding
+unsupported hardware can be disabled by loading the module with ``force=true``.
+
+More information about the MSI embedded controller interface can be found at the
+`msi-ec project <https://github.com/BeardOverflow/msi-ec>`_.
+
+Special thanks go to github user `glpnk` for showing how to decode the fan speed readings.
diff --git a/Documentation/wmi/devices/wmi-bmof.rst b/Documentation/wmi/devices/wmi-bmof.rst
new file mode 100644
index 000000000000..ca1ee9a29be3
--- /dev/null
+++ b/Documentation/wmi/devices/wmi-bmof.rst
@@ -0,0 +1,25 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+==============================
+WMI embedded Binary MOF driver
+==============================
+
+Introduction
+============
+
+Many machines embed WMI Binary MOF (Managed Object Format) metadata used to
+describe the details of their ACPI WMI interfaces. The data can be decoded
+with tools like `bmfdec <https://github.com/pali/bmfdec>`_ to obtain a
+human readable WMI interface description, which is useful for developing
+new WMI drivers.
+
+The Binary MOF data can be retrieved from the ``bmof`` sysfs attribute of the
+associated WMI device. Please note that multiple WMI devices containing Binary
+MOF data can exist on a given system.
+
+WMI interface
+=============
+
+The Binary MOF WMI device is identified by the WMI GUID ``05901221-D566-11D1-B2F0-00A0C9062910``.
+The Binary MOF can be obtained by doing a WMI data block query. The result is
+then returned as an ACPI buffer with a variable size.
diff --git a/Documentation/wmi/driver-development-guide.rst b/Documentation/wmi/driver-development-guide.rst
new file mode 100644
index 000000000000..429137b2f632
--- /dev/null
+++ b/Documentation/wmi/driver-development-guide.rst
@@ -0,0 +1,178 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+============================
+WMI driver development guide
+============================
+
+The WMI subsystem provides a rich driver API for implementing WMI drivers,
+documented at Documentation/driver-api/wmi.rst. This document will serve
+as an introductory guide for WMI driver writers using this API. It is supposed
+to be a successor to the original LWN article [1]_ which deals with WMI drivers
+using the deprecated GUID-based WMI interface.
+
+Obtaining WMI device information
+--------------------------------
+
+Before developing an WMI driver, information about the WMI device in question
+must be obtained. The `lswmi <https://pypi.org/project/lswmi>`_ utility can be
+used to extract detailed WMI device information using the following command:
+
+::
+
+ lswmi -V
+
+The resulting output will contain information about all WMI devices available on
+a given machine, plus some extra information.
+
+In order to find out more about the interface used to communicate with a WMI device,
+the `bmfdec <https://github.com/pali/bmfdec>`_ utilities can be used to decode
+the Binary MOF (Managed Object Format) information used to describe WMI devices.
+The ``wmi-bmof`` driver exposes this information to userspace, see
+Documentation/wmi/devices/wmi-bmof.rst.
+
+In order to retrieve the decoded Binary MOF information, use the following command (requires root):
+
+::
+
+ ./bmf2mof /sys/bus/wmi/devices/05901221-D566-11D1-B2F0-00A0C9062910[-X]/bmof
+
+Sometimes, looking at the disassembled ACPI tables used to describe the WMI device
+helps in understanding how the WMI device is supposed to work. The path of the ACPI
+method associated with a given WMI device can be retrieved using the ``lswmi`` utility
+as mentioned above.
+
+Basic WMI driver structure
+--------------------------
+
+The basic WMI driver is build around the struct wmi_driver, which is then bound
+to matching WMI devices using a struct wmi_device_id table:
+
+::
+
+ static const struct wmi_device_id foo_id_table[] = {
+ { "936DA01F-9ABD-4D9D-80C7-02AF85C822A8", NULL },
+ { }
+ };
+ MODULE_DEVICE_TABLE(wmi, foo_id_table);
+
+ static struct wmi_driver foo_driver = {
+ .driver = {
+ .name = "foo",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS, /* recommended */
+ .pm = pm_sleep_ptr(&foo_dev_pm_ops), /* optional */
+ },
+ .id_table = foo_id_table,
+ .probe = foo_probe,
+ .remove = foo_remove, /* optional, devres is preferred */
+ .notify = 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 */
+ };
+ module_wmi_driver(foo_driver);
+
+The probe() callback is called when the WMI driver is bound to a matching WMI device. Allocating
+driver-specific data structures and initialising interfaces to other kernel subsystems should
+normally be done in this function.
+
+The remove() callback is then called when the WMI driver is unbound from a WMI device. In order
+to unregister interfaces to other kernel subsystems and release resources, devres should be used.
+This simplifies error handling during probe and often allows to omit this callback entirely, see
+Documentation/driver-api/driver-model/devres.rst for details.
+
+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.
+
+Because of this, WMI drivers should use the state container design pattern as described in
+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.
+
+The maximum instance number can be retrieved during runtime using wmidev_instance_count().
+
+Take a look at drivers/platform/x86/inspur_platform_profile.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().
+
+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.
+
+WMI event drivers
+-----------------
+
+WMI drivers can receive WMI events via the notify() 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 WMI driver core will take care that the notify() 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() callback.
+
+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``.
+
+Take a look at drivers/platform/x86/xiaomi-wmi.c for an example WMI event driver.
+
+Handling multiple WMI devices at once
+-------------------------------------
+
+There are many cases of firmware vendors using multiple WMI devices to control different aspects
+of a single physical device. This can make developing WMI drivers complicated, as those drivers
+might need to communicate with each other to present a unified interface to userspace.
+
+On such case involves a WMI event device which needs to talk to a WMI data block device or WMI
+method device upon receiving an WMI event. In such a case, two WMI drivers should be developed,
+one for the WMI event device and one for the other WMI device.
+
+The WMI event device driver has only one purpose: to receive WMI events, validate any additional
+event data and invoke a notifier chain. The other WMI driver adds itself to this notifier chain
+during probing and thus gets notified every time a WMI event is received. This WMI driver might
+then process the event further for example by using an input device.
+
+For other WMI device constellations, similar mechanisms can be used.
+
+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
+- bypassing of the WMI subsystem when talking to WMI devices
+- WMI drivers which cannot be instantiated multiple times.
+
+Many older WMI drivers violate one or more points from this list. The reason for
+this is that the WMI subsystem evolved significantly over the last two decades,
+so there is a lot of legacy cruft inside older WMI drivers.
+
+New WMI drivers are also required to conform to the linux kernel coding style as specified in
+Documentation/process/coding-style.rst. The checkpatch utility can catch many common coding style
+violations, you can invoke it with the following command:
+
+::
+
+ ./scripts/checkpatch.pl --strict <path to driver file>
+
+References
+==========
+
+.. [1] https://lwn.net/Articles/391230/
diff --git a/Documentation/wmi/index.rst b/Documentation/wmi/index.rst
new file mode 100644
index 000000000000..fec4b6ae97b3
--- /dev/null
+++ b/Documentation/wmi/index.rst
@@ -0,0 +1,20 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+=============
+WMI Subsystem
+=============
+
+.. toctree::
+ :maxdepth: 1
+
+ acpi-interface
+ driver-development-guide
+ devices/index
+
+.. only:: subproject and html
+
+
+ Indices
+ =======
+
+ * :ref:`genindex`