[PATCH 1/2] platform/x86: bitland-mifs-wmi: Detect failed function calls
From: Armin Wolf
Date: Fri Sep 04 2026 - 18:46:52 EST
The first 16-bit field inside the output data of a Mifs method call
contains a return code that specifies if the method call succeeded
(0x8000) or failed due to missing support (0xE000). The second 16-bit
field should then contain the previously executed function id.
Rework the layout of both input and output data to match the format
used by the Mifs firmware and check the return code.
Tested-by: Martiya Aryan Mehr <martiya.ar@xxxxxxxxx>
Signed-off-by: Armin Wolf <W_Armin@xxxxxx>
---
.../wmi/devices/bitland-mifs-wmi.rst | 61 +++++---
drivers/platform/x86/bitland-mifs-wmi.c | 140 +++++++++---------
2 files changed, 110 insertions(+), 91 deletions(-)
diff --git a/Documentation/wmi/devices/bitland-mifs-wmi.rst b/Documentation/wmi/devices/bitland-mifs-wmi.rst
index 9e86ecc2993c..314b3fbdda45 100644
--- a/Documentation/wmi/devices/bitland-mifs-wmi.rst
+++ b/Documentation/wmi/devices/bitland-mifs-wmi.rst
@@ -67,7 +67,7 @@ WMI Methods (MICommonInterface)
The ``MICommonInterface`` class (GUID: ``{b60bfb48-3e5b-49e4-a0e9-8cffe1b3434b}``)
is the primary control interface. It uses a 32-byte buffer for both input
-(``InData``) and output (``OutData``).
+(``InData``) and output (``OutData`` + ``Reserved``).
Method Structure
----------------
@@ -77,53 +77,78 @@ The data packet follows a standardized format:
+----------+------------------------------------------------------------------+
| Byte | Description |
+==========+==================================================================+
-| 1 | Method Type: Get (0xFA / 250) or Set (0xFB / 251) |
+| 1 and 2 | Method Type or Return Code |
+----------+------------------------------------------------------------------+
-| 3 | Command ID (Method Name) |
+| 3 and 4 | Command ID (Method Name) |
+----------+------------------------------------------------------------------+
-| 4 - 31 | Arguments (for Set) or Return Data (for Get) |
+| 5 - 32 | Arguments (for Set) or Return Data (for Get) |
+----------+------------------------------------------------------------------+
+Method Types
+------------
+
+The following Method types are understood by the underlying firmware:
+
++--------+---------+
+| Type | Meaning |
++========+=========+
+| 0xFA00 | Read |
++--------+---------+
+| 0xFB00 | Write |
++--------+---------+
+
+Return Codes
+------------
+
+The following Return Codes are know to be returned in response to a WMI method invocation:
+
++--------+--------------------+
+| Code | Meaning |
++========+====================+
+| 0x8000 | Success |
++--------+--------------------+
+| 0xE000 | Invalid Command ID |
++--------+--------------------+
Command IDs
-----------
-The following Command IDs are used in the third byte of the buffer:
+The following Command IDs know to be used on some models:
+----------+-----------------------+------------------------------------------+
| ID | Name | Values / Description |
+==========+=======================+==========================================+
-| 8 | SystemPerMode | 0: Balance, 1: Performance, 2: Quiet, |
+| 0x0800 | SystemPerMode | 0: Balance, 1: Performance, 2: Quiet, |
| | | 3: Full-speed |
+----------+-----------------------+------------------------------------------+
-| 9 | GPUMode | 0: Hybrid, 1: Discrete, 2: UMA |
+| 0x0900 | GPUMode | 0: Hybrid, 1: Discrete, 2: UMA |
+----------+-----------------------+------------------------------------------+
-| 10 | KeyboardType | 0: White, 1: Single RGB, 2: Zone RGB |
+| 0x0A00 | KeyboardType | 0: White, 1: Single RGB, 2: Zone RGB |
+----------+-----------------------+------------------------------------------+
-| 11 | FnLock | 0: Off, 1: On |
+| 0x0B00 | FnLock | 0: Off, 1: On |
+----------+-----------------------+------------------------------------------+
-| 12 | TPLock | 0: Unlock, 1: Lock (Touchpad) |
+| 0x0C00 | TPLock | 0: Unlock, 1: Lock (Touchpad) |
+----------+-----------------------+------------------------------------------+
-| 13 | CPUGPUSYSFanSpeed | Returns 12 bytes of fan data: |
+| 0x0D00 | CPUGPUSYSFanSpeed | Returns 12 bytes of fan data: |
| | | Bytes 4-5: CPU Fan RPM (Little Endian) |
| | | Bytes 6-7: GPU Fan RPM (Little Endian) |
| | | Bytes 10-11: SYS Fan RPM (Little Endian) |
+----------+-----------------------+------------------------------------------+
-| 16 | RGBKeyboardMode | 0: Off, 1: Auto Cyclic, 2: Fixed, |
+| 0x1000 | RGBKeyboardMode | 0: Off, 1: Auto Cyclic, 2: Fixed, |
| | | 3: Custom |
+----------+-----------------------+------------------------------------------+
-| 17 | RGBKeyboardColor | Bytes 4, 5, 6: Red, Green, Blue values |
+| 0x1100 | RGBKeyboardColor | Bytes 4, 5, 6: Red, Green, Blue values |
+----------+-----------------------+------------------------------------------+
-| 18 | RGBKeyboardBrightness | 0-10: Brightness Levels, 128: Auto |
+| 0x1200 | RGBKeyboardBrightness | 0-10: Brightness Levels, 128: Auto |
+----------+-----------------------+------------------------------------------+
-| 19 | SystemAcType | 1: Type-C, 2: Circular Hole (DC) |
+| 0x1300 | SystemAcType | 1: Type-C, 2: Circular Hole (DC) |
+----------+-----------------------+------------------------------------------+
-| 20 | MaxFanSpeedSwitch | Byte 4: Fan Type (0: CPU/GPU, 1: SYS) |
+| 0x1400 | MaxFanSpeedSwitch | Byte 4: Fan Type (0: CPU/GPU, 1: SYS) |
| | | Byte 5: State (0: Off, 1: On) |
+----------+-----------------------+------------------------------------------+
-| 21 | MaxFanSpeed | Sets manual fan speed duty cycle |
+| 0x1500 | MaxFanSpeed | Sets manual fan speed duty cycle |
+----------+-----------------------+------------------------------------------+
-| 22 | CPUThermometer | Returns CPU Temperature |
+| 0x1600 | CPUThermometer | Returns CPU Temperature |
+----------+-----------------------+------------------------------------------+
WMI Events (HID_EVENT20)
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index 3a373184519d..cdb930eb678d 100644
--- a/drivers/platform/x86/bitland-mifs-wmi.c
+++ b/drivers/platform/x86/bitland-mifs-wmi.c
@@ -30,6 +30,7 @@
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/sysfs.h>
+#include <linux/types.h>
#include <linux/unaligned.h>
#include <linux/units.h>
#include <linux/wmi.h>
@@ -39,25 +40,30 @@
#define BITLAND_EVENT_GUID "46C93E13-EE9B-4262-8488-563BCA757FEF"
enum bitland_mifs_operation {
- WMI_METHOD_GET = 250,
- WMI_METHOD_SET = 251,
+ WMI_METHOD_GET = 0xFA00,
+ WMI_METHOD_SET = 0xFB00,
};
enum bitland_mifs_function {
- WMI_FN_SYSTEM_PER_MODE = 8,
- WMI_FN_GPU_MODE = 9,
- WMI_FN_KBD_TYPE = 10,
- WMI_FN_FN_LOCK = 11,
- WMI_FN_TP_LOCK = 12,
- WMI_FN_FAN_SPEEDS = 13,
- WMI_FN_RGB_KB_MODE = 16,
- WMI_FN_RGB_KB_COLOR = 17,
- WMI_FN_RGB_KB_BRIGHTNESS = 18,
- WMI_FN_SYSTEM_AC_TYPE = 19,
- WMI_FN_MAX_FAN_SWITCH = 20,
- WMI_FN_MAX_FAN_SPEED = 21,
- WMI_FN_CPU_THERMOMETER = 22,
- WMI_FN_CPU_POWER = 23,
+ WMI_FN_SYSTEM_PER_MODE = 0x0800,
+ WMI_FN_GPU_MODE = 0x0900,
+ WMI_FN_KBD_TYPE = 0x0A00,
+ WMI_FN_FN_LOCK = 0x0B00,
+ WMI_FN_TP_LOCK = 0x0C00,
+ WMI_FN_FAN_SPEEDS = 0x0D00,
+ WMI_FN_RGB_KB_MODE = 0x1000,
+ WMI_FN_RGB_KB_COLOR = 0x1100,
+ WMI_FN_RGB_KB_BRIGHTNESS = 0x1200,
+ WMI_FN_SYSTEM_AC_TYPE = 0x1300,
+ WMI_FN_MAX_FAN_SWITCH = 0x1400,
+ WMI_FN_MAX_FAN_SPEED = 0x1500,
+ WMI_FN_CPU_THERMOMETER = 0x1600,
+ WMI_FN_CPU_POWER = 0x1700,
+};
+
+enum bitland_mifs_return_code {
+ WMI_RETURN_CODE_SUCCESS = 0x8000,
+ WMI_RETURN_CODE_UNKNOWN_FUNCTION = 0xE000,
};
enum bitland_system_ac_mode {
@@ -114,18 +120,14 @@ enum bitland_wmi_device_type {
};
struct bitland_mifs_input {
- u8 reserved1;
- u8 operation;
- u8 reserved2;
- u8 function;
+ __le16 operation;
+ __le16 function;
u8 payload[28];
} __packed;
struct bitland_mifs_output {
- u8 reserved1;
- u8 operation;
- u8 reserved2;
- u8 function;
+ __le16 return_code;
+ __le16 function;
u8 data[28];
} __packed;
@@ -171,15 +173,27 @@ static int bitland_mifs_wmi_call(struct bitland_mifs_wmi_data *data,
guard(mutex)(&data->lock);
- if (!output)
- return wmidev_invoke_procedure(data->wdev, 0, 1, &in_buf);
-
- ret = wmidev_invoke_method(data->wdev, 0, 1, &in_buf, &out_buf, sizeof(*output));
+ ret = wmidev_invoke_method(data->wdev, 0, 1, &in_buf, &out_buf,
+ sizeof(struct bitland_mifs_output));
if (ret)
return ret;
- memcpy(output, out_buf.data, sizeof(*output));
- kfree(out_buf.data);
+ struct bitland_mifs_output *result __free(kfree) = out_buf.data;
+
+ switch (le16_to_cpu(result->return_code)) {
+ case WMI_RETURN_CODE_SUCCESS:
+ break;
+ case WMI_RETURN_CODE_UNKNOWN_FUNCTION:
+ return -EOPNOTSUPP;
+ default:
+ return -EIO;
+ }
+
+ if (result->function != input->function)
+ return -ENOMSG;
+
+ if (output)
+ memcpy(output, result, sizeof(*output));
return 0;
}
@@ -189,10 +203,8 @@ static int laptop_profile_get(struct device *dev,
{
struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
struct bitland_mifs_input input = {
- .reserved1 = 0,
- .operation = WMI_METHOD_GET,
- .reserved2 = 0,
- .function = WMI_FN_SYSTEM_PER_MODE,
+ .operation = cpu_to_le16(WMI_METHOD_GET),
+ .function = cpu_to_le16(WMI_FN_SYSTEM_PER_MODE),
};
struct bitland_mifs_output result;
int ret;
@@ -223,8 +235,8 @@ static int laptop_profile_get(struct device *dev,
static int bitland_check_performance_capability(struct bitland_mifs_wmi_data *data)
{
struct bitland_mifs_input input = {
- .operation = WMI_METHOD_GET,
- .function = WMI_FN_SYSTEM_AC_TYPE,
+ .operation = cpu_to_le16(WMI_METHOD_GET),
+ .function = cpu_to_le16(WMI_FN_SYSTEM_AC_TYPE),
};
struct bitland_mifs_output output;
int ret;
@@ -248,10 +260,8 @@ static int laptop_profile_set(struct device *dev,
{
struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
struct bitland_mifs_input input = {
- .reserved1 = 0,
- .operation = WMI_METHOD_SET,
- .reserved2 = 0,
- .function = WMI_FN_SYSTEM_PER_MODE,
+ .operation = cpu_to_le16(WMI_METHOD_SET),
+ .function = cpu_to_le16(WMI_FN_SYSTEM_PER_MODE),
};
int ret;
u8 val;
@@ -344,22 +354,20 @@ static int laptop_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
{
struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
struct bitland_mifs_input input = {
- .reserved1 = 0,
- .operation = WMI_METHOD_GET,
- .reserved2 = 0,
+ .operation = cpu_to_le16(WMI_METHOD_GET),
};
struct bitland_mifs_output res;
int ret;
switch (type) {
case hwmon_temp:
- input.function = WMI_FN_CPU_THERMOMETER;
+ input.function = cpu_to_le16(WMI_FN_CPU_THERMOMETER);
ret = bitland_mifs_wmi_call(data, &input, &res);
if (!ret)
*val = res.data[0] * MILLIDEGREE_PER_DEGREE;
return ret;
case hwmon_fan:
- input.function = WMI_FN_FAN_SPEEDS;
+ input.function = cpu_to_le16(WMI_FN_FAN_SPEEDS);
ret = bitland_mifs_wmi_call(data, &input, &res);
if (ret)
return ret;
@@ -420,10 +428,8 @@ static int laptop_kbd_led_set(struct led_classdev *led_cdev,
struct bitland_mifs_wmi_data *data =
container_of(led_cdev, struct bitland_mifs_wmi_data, kbd_led);
struct bitland_mifs_input input = {
- .reserved1 = 0,
- .operation = WMI_METHOD_SET,
- .reserved2 = 0,
- .function = WMI_FN_RGB_KB_BRIGHTNESS,
+ .operation = cpu_to_le16(WMI_METHOD_SET),
+ .function = cpu_to_le16(WMI_FN_RGB_KB_BRIGHTNESS),
};
input.payload[0] = (u8)value;
@@ -436,10 +442,8 @@ static enum led_brightness laptop_kbd_led_get(struct led_classdev *led_cdev)
struct bitland_mifs_wmi_data *data =
container_of(led_cdev, struct bitland_mifs_wmi_data, kbd_led);
struct bitland_mifs_input input = {
- .reserved1 = 0,
- .operation = WMI_METHOD_GET,
- .reserved2 = 0,
- .function = WMI_FN_RGB_KB_BRIGHTNESS,
+ .operation = cpu_to_le16(WMI_METHOD_GET),
+ .function = cpu_to_le16(WMI_FN_RGB_KB_BRIGHTNESS),
};
struct bitland_mifs_output res;
int ret;
@@ -463,10 +467,8 @@ static ssize_t gpu_mode_show(struct device *dev, struct device_attribute *attr,
{
struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
struct bitland_mifs_input input = {
- .reserved1 = 0,
- .operation = WMI_METHOD_GET,
- .reserved2 = 0,
- .function = WMI_FN_GPU_MODE,
+ .operation = cpu_to_le16(WMI_METHOD_GET),
+ .function = cpu_to_le16(WMI_FN_GPU_MODE),
};
struct bitland_mifs_output res;
u8 mode_val;
@@ -488,10 +490,8 @@ static ssize_t gpu_mode_store(struct device *dev, struct device_attribute *attr,
{
struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
struct bitland_mifs_input input = {
- .reserved1 = 0,
- .operation = WMI_METHOD_SET,
- .reserved2 = 0,
- .function = WMI_FN_GPU_MODE,
+ .operation = cpu_to_le16(WMI_METHOD_SET),
+ .function = cpu_to_le16(WMI_FN_GPU_MODE),
};
int val;
int ret;
@@ -521,10 +521,8 @@ static ssize_t kb_mode_show(struct device *dev, struct device_attribute *attr,
{
struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
struct bitland_mifs_input input = {
- .reserved1 = 0,
- .operation = WMI_METHOD_GET,
- .reserved2 = 0,
- .function = WMI_FN_RGB_KB_MODE,
+ .operation = cpu_to_le16(WMI_METHOD_GET),
+ .function = cpu_to_le16(WMI_FN_RGB_KB_MODE),
};
struct bitland_mifs_output res;
u8 mode_val;
@@ -546,10 +544,8 @@ static ssize_t kb_mode_store(struct device *dev, struct device_attribute *attr,
{
struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
struct bitland_mifs_input input = {
- .reserved1 = 0,
- .operation = WMI_METHOD_SET,
- .reserved2 = 0,
- .function = WMI_FN_RGB_KB_MODE,
+ .operation = cpu_to_le16(WMI_METHOD_SET),
+ .function = cpu_to_le16(WMI_FN_RGB_KB_MODE),
};
// the wmi value (0, 1, 2 or 3)
int val;
@@ -575,10 +571,8 @@ static ssize_t fan_boost_store(struct device *dev,
{
struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
struct bitland_mifs_input input = {
- .reserved1 = 0,
- .operation = WMI_METHOD_SET,
- .reserved2 = 0,
- .function = WMI_FN_MAX_FAN_SWITCH,
+ .operation = cpu_to_le16(WMI_METHOD_SET),
+ .function = cpu_to_le16(WMI_FN_MAX_FAN_SWITCH),
};
bool val;
int ret;
--
2.39.5