summaryrefslogtreecommitdiff
path: root/Documentation/wmi
diff options
context:
space:
mode:
Diffstat (limited to 'Documentation/wmi')
-rw-r--r--Documentation/wmi/acpi-interface.rst5
-rw-r--r--Documentation/wmi/devices/alienware-wmi.rst397
-rw-r--r--Documentation/wmi/devices/dell-wmi-ddv.rst4
-rw-r--r--Documentation/wmi/devices/msi-wmi-platform.rst194
-rw-r--r--Documentation/wmi/driver-development-guide.rst187
-rw-r--r--Documentation/wmi/index.rst1
6 files changed, 785 insertions, 3 deletions
diff --git a/Documentation/wmi/acpi-interface.rst b/Documentation/wmi/acpi-interface.rst
index d31af0ed9c08..06fb7fcf4413 100644
--- a/Documentation/wmi/acpi-interface.rst
+++ b/Documentation/wmi/acpi-interface.rst
@@ -93,4 +93,7 @@ _WED ACPI method
----------------
Used to retrieve additional WMI event data, its single parameter is a integer
-holding the notification ID of the event.
+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/alienware-wmi.rst b/Documentation/wmi/devices/alienware-wmi.rst
new file mode 100644
index 000000000000..ddc5e561960e
--- /dev/null
+++ b/Documentation/wmi/devices/alienware-wmi.rst
@@ -0,0 +1,397 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+==============================================
+Dell AWCC WMI interface driver (alienware-wmi)
+==============================================
+
+Introduction
+============
+
+The WMI device WMAX has been implemented for many Alienware and Dell's G-Series
+models. Throughout these models, two implementations have been identified. The
+first one, used by older systems, deals with HDMI, brightness, RGB, amplifier
+and deep sleep control. The second one used by newer systems deals primarily
+with thermal, overclocking, and GPIO control.
+
+It is suspected that the latter is used by Alienware Command Center (AWCC) to
+manage manufacturer predefined thermal profiles. The alienware-wmi driver
+exposes Thermal_Information and Thermal_Control methods through the Platform
+Profile API to mimic AWCC's behavior.
+
+This newer interface, named AWCCMethodFunction has been reverse engineered, as
+Dell has not provided any official documentation. We will try to describe to the
+best of our ability its discovered inner workings.
+
+.. note::
+ The following method description may be incomplete and some operations have
+ different implementations between devices.
+
+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("{A70591CE-A997-11DA-B012-B622A1EF5492}")]
+ class AWCCWmiMethodFunction {
+ [key, read] string InstanceName;
+ [read] boolean Active;
+
+ [WmiMethodId(13), Implemented, read, write, Description("Return Overclocking Report.")] void Return_OverclockingReport([out] uint32 argr);
+ [WmiMethodId(14), Implemented, read, write, Description("Set OCUIBIOS Control.")] void Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(15), Implemented, read, write, Description("Clear OC FailSafe Flag.")] void Clear_OCFailSafeFlag([out] uint32 argr);
+ [WmiMethodId(19), Implemented, read, write, Description("Get Fan Sensors.")] void GetFanSensors([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(20), Implemented, read, write, Description("Thermal Information.")] void Thermal_Information([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(21), Implemented, read, write, Description("Thermal Control.")] void Thermal_Control([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(23), Implemented, read, write, Description("MemoryOCControl.")] void MemoryOCControl([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(26), Implemented, read, write, Description("System Information.")] void SystemInformation([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(28), Implemented, read, write, Description("Power Information.")] void PowerInformation([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(32), Implemented, read, write, Description("FW Update GPIO toggle.")] void FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(33), Implemented, read, write, Description("Read Total of GPIOs.")] void ReadTotalofGPIOs([out] uint32 argr);
+ [WmiMethodId(34), Implemented, read, write, Description("Read GPIO pin Status.")] void ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(35), Implemented, read, write, Description("Read Chassis Color.")] void ReadChassisColor([out] uint32 argr);
+ [WmiMethodId(36), Implemented, read, write, Description("Read Platform Properties.")] void ReadPlatformProperties([out] uint32 argr);
+ [WmiMethodId(37), Implemented, read, write, Description("Game Shift Status.")] void GameShiftStatus([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(128), Implemented, read, write, Description("Caldera SW installation.")] void CalderaSWInstallation([out] uint32 argr);
+ [WmiMethodId(129), Implemented, read, write, Description("Caldera SW is released.")] void CalderaSWReleased([out] uint32 argr);
+ [WmiMethodId(130), Implemented, read, write, Description("Caldera Connection Status.")] void CalderaConnectionStatus([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(131), Implemented, read, write, Description("Surprise Unplugged Flag Status.")] void SurpriseUnpluggedFlagStatus([out] uint32 argr);
+ [WmiMethodId(132), Implemented, read, write, Description("Clear Surprise Unplugged Flag.")] void ClearSurpriseUnpluggedFlag([out] uint32 argr);
+ [WmiMethodId(133), Implemented, read, write, Description("Cancel Undock Request.")] void CancelUndockRequest([out] uint32 argr);
+ [WmiMethodId(135), Implemented, read, write, Description("Devices in Caldera.")] void DevicesInCaldera([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(136), Implemented, read, write, Description("Notify BIOS for SW ready to disconnect Caldera.")] void NotifyBIOSForSWReadyToDisconnectCaldera([out] uint32 argr);
+ [WmiMethodId(160), Implemented, read, write, Description("Tobii SW installation.")] void TobiiSWinstallation([out] uint32 argr);
+ [WmiMethodId(161), Implemented, read, write, Description("Tobii SW Released.")] void TobiiSWReleased([out] uint32 argr);
+ [WmiMethodId(162), Implemented, read, write, Description("Tobii Camera Power Reset.")] void TobiiCameraPowerReset([out] uint32 argr);
+ [WmiMethodId(163), Implemented, read, write, Description("Tobii Camera Power On.")] void TobiiCameraPowerOn([out] uint32 argr);
+ [WmiMethodId(164), Implemented, read, write, Description("Tobii Camera Power Off.")] void TobiiCameraPowerOff([out] uint32 argr);
+ };
+
+Some of these methods get quite intricate so we will describe them using
+pseudo-code that vaguely resembles the original ASL code.
+
+Methods not described in the following document have unknown behavior.
+
+Argument Structure
+------------------
+
+All input arguments have type **uint32** and their structure is very similar
+between methods. Usually, the first byte corresponds to a specific *operation*
+the method performs, and the subsequent bytes correspond to *arguments* passed
+to this *operation*. For example, if an operation has code 0x01 and requires an
+ID 0xA0, the argument you would pass to the method is 0xA001.
+
+
+Thermal Methods
+===============
+
+WMI method Thermal_Information([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0x01:
+ argr = 1
+
+ if BYTE_0(arg2) == 0x02:
+ argr = SYSTEM_DESCRIPTION
+
+ if BYTE_0(arg2) == 0x03:
+ if BYTE_1(arg2) == 0x00:
+ argr = FAN_ID_0
+
+ if BYTE_1(arg2) == 0x01:
+ argr = FAN_ID_1
+
+ if BYTE_1(arg2) == 0x02:
+ argr = FAN_ID_2
+
+ if BYTE_1(arg2) == 0x03:
+ argr = FAN_ID_3
+
+ if BYTE_1(arg2) == 0x04:
+ argr = SENSOR_ID_CPU | 0x0100
+
+ if BYTE_1(arg2) == 0x05:
+ argr = SENSOR_ID_GPU | 0x0100
+
+ if BYTE_1(arg2) == 0x06:
+ argr = THERMAL_MODE_QUIET_ID
+
+ if BYTE_1(arg2) == 0x07:
+ argr = THERMAL_MODE_BALANCED_ID
+
+ if BYTE_1(arg2) == 0x08:
+ argr = THERMAL_MODE_BALANCED_PERFORMANCE_ID
+
+ if BYTE_1(arg2) == 0x09:
+ argr = THERMAL_MODE_PERFORMANCE_ID
+
+ if BYTE_1(arg2) == 0x0A:
+ argr = THERMAL_MODE_LOW_POWER_ID
+
+ if BYTE_1(arg2) == 0x0B:
+ argr = THERMAL_MODE_GMODE_ID
+
+ else:
+ argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x04:
+ if is_valid_sensor(BYTE_1(arg2)):
+ argr = SENSOR_TEMP_C
+ else:
+ argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x05:
+ if is_valid_fan(BYTE_1(arg2)):
+ argr = FAN_RPM()
+
+ if BYTE_0(arg2) == 0x06:
+ skip
+
+ if BYTE_0(arg2) == 0x07:
+ argr = 0
+
+ If BYTE_0(arg2) == 0x08:
+ if is_valid_fan(BYTE_1(arg2)):
+ argr = 0
+ else:
+ argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x09:
+ if is_valid_fan(BYTE_1(arg2)):
+ argr = FAN_UNKNOWN_STAT_0()
+
+ else:
+ argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x0A:
+ argr = THERMAL_MODE_BALANCED_ID
+
+ if BYTE_0(arg2) == 0x0B:
+ argr = CURRENT_THERMAL_MODE()
+
+ if BYTE_0(arg2) == 0x0C:
+ if is_valid_fan(BYTE_1(arg2)):
+ argr = FAN_UNKNOWN_STAT_1()
+ else:
+ argr = 0xFFFFFFFF
+
+Operation 0x02 returns a *system description* buffer with the following
+structure:
+
+::
+
+ out[0] -> Number of fans
+ out[1] -> Number of sensors
+ out[2] -> 0x00
+ out[3] -> Number of thermal modes
+
+Operation 0x03 list all available fan IDs, sensor IDs and thermal profile
+codes in order, but different models may have different number of fans and
+thermal profiles. These are the known ranges:
+
+* Fan IDs: from 2 up to 4
+* Sensor IDs: 2
+* Thermal profile codes: from 1 up to 7
+
+In total BYTE_1(ARG2) may range from 0x5 up to 0xD depending on the model.
+
+WMI method Thermal_Control([in] uint32 arg2, [out] uint32 argr)
+---------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0x01:
+ if is_valid_thermal_profile(BYTE_1(arg2)):
+ SET_THERMAL_PROFILE(BYTE_1(arg2))
+ argr = 0
+
+ if BYTE_0(arg2) == 0x02:
+ if is_valid_fan(BYTE_1(arg2)):
+ SET_FAN_SPEED_MULTIPLIER(BYTE_2(arg2))
+ argr = 0
+ else:
+ argr = 0xFFFFFFFF
+
+.. note::
+ While you can manually change the fan speed multiplier with this method,
+ Dell's BIOS tends to overwrite this changes anyway.
+
+These are the known thermal profile codes:
+
+::
+
+ CUSTOM 0x00
+
+ BALANCED_USTT 0xA0
+ BALANCED_PERFORMANCE_USTT 0xA1
+ COOL_USTT 0xA2
+ QUIET_USTT 0xA3
+ PERFORMANCE_USTT 0xA4
+ LOW_POWER_USTT 0xA5
+
+ QUIET 0x96
+ BALANCED 0x97
+ BALANCED_PERFORMANCE 0x98
+ PERFORMANCE 0x99
+
+ GMODE 0xAB
+
+Usually if a model doesn't support the first four profiles they will support
+the User Selectable Thermal Tables (USTT) profiles and vice-versa.
+
+GMODE replaces PERFORMANCE in G-Series laptops.
+
+WMI method GameShiftStatus([in] uint32 arg2, [out] uint32 argr)
+---------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0x1:
+ TOGGLE_GAME_SHIFT()
+ argr = GET_GAME_SHIFT_STATUS()
+
+ if BYTE_0(arg2) == 0x2:
+ argr = GET_GAME_SHIFT_STATUS()
+
+Game Shift Status does not change the fan speed profile but it could be some
+sort of CPU/GPU power profile. Benchmarks have not been done.
+
+This method is only present on Dell's G-Series laptops and it's implementation
+implies GMODE thermal profile is available, even if operation 0x03 of
+Thermal_Information does not list it.
+
+G-key on Dell's G-Series laptops also changes Game Shift status, so both are
+directly related.
+
+WMI method GetFanSensors([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0x1:
+ if is_valid_fan(BYTE_1(arg2)):
+ argr = 1
+ else:
+ argr = 0
+
+ if BYTE_0(arg2) == 0x2:
+ if is_valid_fan(BYTE_1(arg2)):
+ if BYTE_2(arg2) == 0:
+ argr == SENSOR_ID
+ else
+ argr == 0xFFFFFFFF
+ else:
+ argr = 0
+
+Overclocking Methods
+====================
+
+.. warning::
+ These methods have not been tested and are only partially reverse
+ engineered.
+
+WMI method Return_OverclockingReport([out] uint32 argr)
+-------------------------------------------------------
+
+::
+
+ CSMI (0xE3, 0x99)
+ argr = 0
+
+CSMI is an unknown operation.
+
+WMI method Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------------
+
+::
+
+ CSMI (0xE3, 0x99)
+ argr = 0
+
+CSMI is an unknown operation.
+
+WMI method Clear_OCFailSafeFlag([out] uint32 argr)
+--------------------------------------------------
+
+::
+
+ CSMI (0xE3, 0x99)
+ argr = 0
+
+CSMI is an unknown operation.
+
+
+WMI method MemoryOCControl([in] uint32 arg2, [out] uint32 argr)
+---------------------------------------------------------------
+
+AWCC supports memory overclocking, but this method is very intricate and has
+not been deciphered yet.
+
+GPIO methods
+============
+
+These methods are probably related to some kind of firmware update system,
+through a GPIO device.
+
+.. warning::
+ These methods have not been tested and are only partially reverse
+ engineered.
+
+WMI method FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr)
+------------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0:
+ if BYTE_1(arg2) == 1:
+ SET_PIN_A_HIGH()
+ else:
+ SET_PIN_A_LOW()
+
+ if BYTE_0(arg2) == 1:
+ if BYTE_1(arg2) == 1:
+ SET_PIN_B_HIGH()
+
+ else:
+ SET_PIN_B_LOW()
+
+ else:
+ argr = 1
+
+WMI method ReadTotalofGPIOs([out] uint32 argr)
+----------------------------------------------
+
+::
+
+ argr = 0x02
+
+WMI method ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr)
+------------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0:
+ argr = PIN_A_STATUS
+
+ if BYTE_0(arg2) == 1:
+ argr = PIN_B_STATUS
+
+Other information Methods
+=========================
+
+WMI method ReadChassisColor([out] uint32 argr)
+----------------------------------------------
+
+::
+
+ argr = CHASSIS_COLOR_ID
+
+Acknowledgements
+================
+
+Kudos to `AlexIII <https://github.com/AlexIII/tcc-g15>`_ for documenting
+and testing available thermal profile codes.
diff --git a/Documentation/wmi/devices/dell-wmi-ddv.rst b/Documentation/wmi/devices/dell-wmi-ddv.rst
index 2fcdfcf03327..e0c20af30948 100644
--- a/Documentation/wmi/devices/dell-wmi-ddv.rst
+++ b/Documentation/wmi/devices/dell-wmi-ddv.rst
@@ -8,7 +8,7 @@ Introduction
============
Many Dell notebooks made after ~2020 support a WMI-based interface for
-retrieving various system data like battery temperature, ePPID, diagostic data
+retrieving various system data like battery temperature, ePPID, diagnostic data
and fan/thermal sensor data.
This interface is likely used by the `Dell Data Vault` software on Windows,
@@ -277,7 +277,7 @@ Reverse-Engineering the DDV WMI interface
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
+5. Use the built-in UEFI diagnostics 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).
diff --git a/Documentation/wmi/devices/msi-wmi-platform.rst b/Documentation/wmi/devices/msi-wmi-platform.rst
new file mode 100644
index 000000000000..31a136942892
--- /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 Binary 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 single 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 changed (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/driver-development-guide.rst b/Documentation/wmi/driver-development-guide.rst
new file mode 100644
index 000000000000..f7e1089a0559
--- /dev/null
+++ b/Documentation/wmi/driver-development-guide.rst
@@ -0,0 +1,187 @@
+.. 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.
+
+If you are attempting to port a driver to Linux and are working on a Windows
+system, `WMIExplorer <https://github.com/vinaypamnani/wmie2>`_ can be useful
+for inspecting available WMI methods and invoking them directly.
+
+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 */
+ .shutdown = foo_shutdown, /* optional, called during shutdown */
+ .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.
+
+The shutdown() callback is called during shutdown, reboot or kexec. Its sole purpose is to disable
+the WMI device and put it in a well-known state for the WMI driver to pick up later after reboot
+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.
+
+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() or shutdown() 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
index 537cff188e14..fec4b6ae97b3 100644
--- a/Documentation/wmi/index.rst
+++ b/Documentation/wmi/index.rst
@@ -8,6 +8,7 @@ WMI Subsystem
:maxdepth: 1
acpi-interface
+ driver-development-guide
devices/index
.. only:: subproject and html