# WMI-протокол Xiaomi/Redmi (Bitland MIFS) Восстановлено из: `MI Control Utility v1.2.5` (реверс живого Windows-интерфейса) + драйвер ядра Linux `bitland-mifs-wmi` (v10) + патч в linux-kernel ML. > ⚠️ Значения, помеченные **[MIControl]**, подтверждены реальным реверсом Windows-утилиты и приоритетны. > Значения **[Linux]** взяты из драйвера ядра — иногда расходятся в нумерации (см. режимы производительности). **[?]** — предположение, требует проверки на железе. --- ## 1. Транспорт | Параметр | Значение | |----------|----------| | Namespace | `ROOT\WMI` | | Класс | `MiCommonInterface` (регистр не важен — WMI case-insensitive; пользователь видел `MICommonInterface`) | | Метод | `MiInterface` | | Инстанс | `SELECT InstanceName FROM MiCommonInterface` → напр. `ACPI\PNP0C14\MIFS_0` | | Предикат вызова | `MiCommonInterface.InstanceName='<инстанс>'` | | Входной параметр | `InData` — `SAFEARRAY` из `VT_UI1`, длина **32 байта (0x20)** | | Выходной параметр | `OutData` — `SAFEARRAY` из `VT_UI1` (~30 байт) | | GUID метода | `B60BFB48-3E5B-49E4-A0E9-8CFFE1B3434B` | | GUID событий | `46C93E13-EE9B-4262-8488-563BCA757FEF` | `PNP0C14` — стандартный ACPI→WMI mapper Windows. `MIFS` = Mi Firmware Service (ODM Bitland). --- ## 2. Формат буфера запроса (InData) Первые 8 байт значимы, остальные 24 — нули (буфер добивается до 32): ``` offset 0 : 0x00 reserved offset 1 : operation 0xFA = GET (чтение), 0xFB = SET (запись) ← 250 / 251 offset 2 : 0x00 reserved offset 3 : function код функции (см. таблицу ниже) offset 4 : arg под-функция / значение offset 5 : 0x00 reserved offset 6 : arg2 значение offset 7 : 0x00 reserved offset 8..31 : 0x00 padding ``` Точная сборка из MIControl (`CMiLowLevelCommand::Put/Get`): ``` SET: { 0x00, 0xFB, 0x00, cmd, arg, 0x00, arg2, 0x00, 0x00 … } GET: { 0x00, 0xFA, 0x00, cmd, arg, 0x00, arg2, 0x00, 0x00 … } ``` **Ответ (OutData)** — восстановлено пробами на TM2424 (журнал проб — в локальном `reference/`, в репозиторий не входит): ``` OUT[0] = 0x00 OUT[1] = СТАТУС ПРОШИВКИ: 0x80 = OK/поддерживается, 0xE0 = не поддерживается OUT[2] = 0x00 OUT[3] = эхо cmd (если поддерживается) OUT[4] = значение / эхо arg (для perf mode — текущий режим) OUT[6] = значение для charge-protect — КОД уровня порога, не вкл/выкл (таблица кодов ниже) ``` Перед разбором ответа **проверять `OUT[1] == 0x80`** — иначе функция не поддерживается на этой модели. > ⚠️ Набор поддерживаемых функций **зависит от модели**. На Xiaomi Book Pro 14 (TM2424) > отвечают `0x80` только `0x08` (perf), `0x0A` (mic), `0x0C` (см. ниже), `0x10` (charge). > Полный свип `0x01..0x30` (включая суб-функции 0x00–0x08 у тёмных команд) — всё > остальное `0xE0`, в т.ч. `0x14/0x15` (MAX_FAN из Linux-драйвера) и `0x17` (CPU power — > кандидат на PL/TDP-в-ваттах, но `0xE0`; разбор и план перепроверки — [05](05-open-questions.md) Вопрос 7). > Не хардкодить наличие функций — проверять статус в рантайме. > > **Про `0x0C` (исследовано 2026-07-10):** записываемый бинарный флаг — SET принимается, > значение хранится (sub `0x02`, `OUT[6]`), но наблюдаемого эффекта нет. > > Контекст из tongfang-mifs-control-userspace (тот же GUID и формат буфера!): > существует **вторая таблица функций** — для игровой линейки (Tongfang: Redmi G и т.п.): > 8=perf (значения 0..3!), 9=GPU-mux, 11=FnLock, **12=TPLock**, 13=обороты вентиляторов, > 16-18=RGB-клавиатура, 20/21=макс. кулер, 22=температура, 23=CPU power. У тонкой линейки > (Bitland, наш TM2424) таблица другая: 0x10=заряд (у игровых 16=RGB), Fn-Lock — не WMI-функция > 11, а HID-событие 0x07, perf-значения свои (0x02/0x03/0x04/0x09/0x0A). > > ⚠️ **Переопределено (2026-07-28/29):** прежняя догадка про TPLock **неверна**. Реверс OEM-стека > удалённого включения показал, что `0x0C` — это **канал питания/статуса always-on IoT-модуля** (Xiaomi > remote wake): `getDeviceStatus = 0xFA/0x0C/0x03`, `setDeviceStatus = 0xFB/0x0C/0x03/`, значение в > `OUT[6]`. Подфункции: `0x02` = «модуль присутствует» (статично 1), `0x06` = «привязан/remote-wake > активен», `0x03` = **питание модуля (вкл/выкл)**. `SET 0x0C/0x03` **реально гасит/будит модуль** > (подтверждено), т.е. вкл/выкл driver-free возможно — но в приложении пока не используем (см. > [11-iot-remote-wake.md](11-iot-remote-wake.md) §6–8). Само пробуждение выключенного ПК недостижимо > (облако/токен). --- ## 3. Коды функций (offset 3) | cmd | Функция | Dir | Аргументы | Источник | |-----|---------|-----|-----------|----------| | `0x08` | **Режим производительности** | SET/GET | `arg` = режим (таблица ниже). GET → режим в **`OutData[4]`** (проверено на TM2424) | [MIControl] | | `0x09` | Режим GPU | SET/GET | `0`=Hybrid, `1`=Discrete, `2`=UMA | [Linux] | | `0x0A` | **Микрофон (аппаратный mute)** | SET/GET | on/off; в одном пути `arg=0x05` | [MIControl] | | `0x0D` (13) | Обороты вентиляторов | GET | RPM: CPU `[0..1]`, GPU `[2..3]`, SYS `[6..7]`, little-endian | [Linux] | | `0x10` (16) | **Порог заряда (уровни/100%)** | SET/GET | `arg=0x02`; `arg2` = КОД уровня (не бинарно): на TM2424 `{0:100%, 1:80%, 4:80%, 5:70%, 6:60%, 7:50%, 8:40%}`, невалидный код прошивка отвергает. GET → код в `OutData[6]`. Набор зависит от модели — см. `12-charge-levels.md` | [MIControl] [RE] | | `0x12` (18) | Яркость подсветки клавиатуры | SET/GET | `0…10` уровней, `128` = Auto | [Linux] | | `0x13` (19) | Тип блока питания | GET | `1`=USB-C, `2`=DC-разъём | [Linux] | | `0x16` (22) | Температура CPU | GET | °C | [Linux] [?] | > ⚠️ **Коллизия `0x16`:** в MIControl `0x16` используется ещё и как *внутренний* GUI-код диспетчеризации события смены режима (не путать с WMI-функцией). Как WMI-функция `0x16`=CPU temp взято из Linux-драйвера и требует проверки. ### Режим производительности (cmd `0x08`, `arg` = значение) | arg | Режим | Примечание | |-----|-------|------------| | `0x01` | Balance (Баланс) | нет на Redmibook 2025 | | `0x02` | Quiet / Silence (Тихий) | | | `0x03` | Turbo | | | `0x04` | Full-speed | требует питания **от сети** (на батарее прошивка не примет); на моделях с отдельным DC-разъёмом может требовать именно его | | `0x09` | Auto / Smart | **только Redmibook 2025** (SKU `Win32_ComputerSystem.SystemSKUNumber` начинается с `TM24`) | | `0x0A` | **Eco (скрытый)** | значение из EC-референса (регистр `0x68`, `PERF_ECO_MODE`); официальный софт его не показывает | ✅ **Подтверждено на TM2424:** приняты `0x02/0x03/0x04/0x09` **и `0x0A` (Eco)** — SET → `0x80`, readback держится. `0x01` (Balance) отклонён (остаётся Auto). SET работает из обычного admin, GET → режим в `OUT[4]`. > ✅ По Eco (`0x0A`): **режим настоящий, работа подтверждена на TM2424** — > при включении прошивка гасит подсветку клавиатуры и снижает яркость экрана; > судя по поведению, это самый экономный профиль (агрессивнее Quiet). > Точный лимит мощности CPU не замерялся — есть `reference/probe-eco-load.ps1` > для сравнения производительности под нагрузкой, если захочется цифр. Определение модели: `SELECT SystemSKUNumber FROM Win32_ComputerSystem`, префикс `TM24` → Redmibook 2025 (показываются Тихий/Турбо/Full-speed/Auto, без Balance). > ⚠️ Драйвер Linux заявляет другую нумерацию (`0=Balanced,1=Performance,2=Quiet,3=Full-speed`). Для Windows доверяем **[MIControl]**. ### Защита заряда (cmd `0x10`) — ПОДТВЕРЖДЕНО на TM2424 | arg | Назначение | Прим. | |-----|-----------|-------| | `0x01` | **SOH1 — здоровье батареи, % от исходной ёмкости.** Как setpoint не работает (читается `100`, не пишется, на отсечку не влияет) | ✅ используется (`GetBatteryHealth`) | | `0x02` | **Уровень порога заряда — РАБОТАЕТ (НЕ бинарно!).** Прошивка держит коды `{0,1,4,5,6,7,8}` | ✅ основной | | `0x03` | индикатор зоны заряда (1→0 около порога); смысл не критичен | для UI опц. | | `0x06` | **Мощность подключённого PD-адаптера, Вт** (0 = нет питания или блок не-PD) | ✅ используется (`GetAdapterWatts`) | > Группа `0x10` — не только порог: под-функции `0x01` и `0x06` отдают **сенсоры** (здоровье батареи > и ватты адаптера). Подтверждено на TM2424, см. `reference/probe-sensors.ps1`; в коде — > `Mifs.SensorBatteryHealth` / `Mifs.SensorAdapterWatts`. > ✅ **Механизм подтверждён переключением:** `0x02`→0 возобновляет заряд, `0x02`→ненулевое останавливает (повторяемо). Держит именно WMI, не Windows (уведомление «оптимизированная зарядка» — лишь отражение EC-удержания). > > **★ ПЕРЕОТКРЫТО (2026-07-29): порог НЕ фиксирован — 6 уровней, driver-free.** Раньше считали `0x02` > бинарным (80/100), но реверс лимит-селектора в Xiaomi PC Manager показал, что это **уровень**. > Прошивка **валидирует** набор: принимает `{0,1,4,5,6,7,8}` (статус `0x80`, значение держится), > остальное **отвергает** (SET-статус `0x00`) и зажимает к ближайшему (`2,3`→`1`; `9…`→`8`). Маппинг: > > | код | 0 | 1 | 4 | 5 | 6 | 7 | 8 | > |-----|---|---|---|---|---|---|---| > | % | 100 | 80 (legacy) | 80 (granular) | 70 | 60 | 50 | 40 | > > granular-шкала: **% = 120 − код·10** (коды 4–8). **90% нет** (код `3` отвергается). Подтверждено > живьём: наш `SET`→`readback` честный, поведение = как у OEM (тот для 80% пишет legacy `1`). Доступные > driver-free пороги: **40 / 50 / 60 / 70 / 80 / 100 %.** `0x10/01` реальный % не отдаёт (намертво 100) — > процент считаем по таблице сами. Разбор находки: [12-charge-levels.md](12-charge-levels.md). > Команды: `SET 0x10 arg=0x02 val=<код>`; статус = `GET 0x10 arg=0x02` → `OUT[6]`. ### Защита заряда — последовательность вкл/выкл (из MIControl) Из `CPowerPowerChargeProtectGuard`: ``` Включить лимит (≈80% на TM2424): 00 FB 00 10 02 00 00 00 → (Sleep 50 мс) → 00 FB 00 10 02 00 01 00 ↑ так делает MIControl; у нас пауза 80 мс (`MifsClient`) — на 50 мс EC успевал не всегда Выключить 100%: 00 FB 00 10 02 00 00 00 Прочитать: 00 FA 00 10 02 00 00 00 → статус в OutData[6] ``` Перед включением всегда сначала выключают — это сбрасывает стейт-машину заряда EC (тот же приём, что в CoreCharge с регистром `0xA4`). > ⚠️ **EC теряет защиту на переходах питания** (сон, гибернация, AC↔батарея — проверено вживую), > и ре-арм нужен не только *после* пробуждения, но и *перед* уходом в сон/выключение — иначе > ноутбук весь период «выключено» стоит без лимита и заряжается до 100% (багрепорт v0.7). > `CPowerPowerChargeProtectGuard` в службе MIControl переармливает в suspend-колбэке > (`RegisterSuspendResumeNotification` срабатывает и на входе, и на выходе). У нас — > `ChargeGuard`: Resume/StatusChange с дебаунсом + мгновенный ре-арм на Suspend и SessionEnding. > Учесть: «Выключение» Windows 11 при включённом быстром запуске — это гибернация (шлёт Suspend). --- ## 4. События (горячие клавиши, Mi/AI-кнопка) События прошивки приходят как **WMI event class** `HID_EVENTnn`: | Параметр | Значение | |----------|----------| | Namespace | `ROOT\WMI` | | Подписка (WQL) | `SELECT * FROM HID_EVENT20` — класс зашит константой `Mifs.EventClass`, не настраивается | | Полезные данные | свойство `EventDetail` — массив `VT_UI1` | | Разбор | `EventDetail[1]` = sub-cmd, `EventDetail[2]` = arg | ✅ **Подтверждено на TM2424:** события ловятся из пользовательской сессии (подписка `SELECT * FROM HID_EVENT20`). Существует только класс `HID_EVENT20`. Коды `EventDetail[1]`, пойманные вживую (`[0]=0x01` тип, `[2]`=значение): Карта **закрыта** — все коды идентифицированы вживую (константы: `Mifs.Key*`, разбор: `KeyRouter`; подробности и действия по клавишам — [07-keymap.md](07-keymap.md)): | код `[1]` | знач `[2]` | Событие | |-----------|-----------|---------| | `0x21` | 0/1 | Микрофон: `0` = mute (лампа горит), `1` = unmute (подтв. MIControl `HandleFnKey`) | | `0x05` | `0`/`5`/`10`/`0x80` | Подсветка клавиатуры: выкл / 50% / 100% / Авто | | `0x25`/`0x26` | 1 | **Mi-кнопка**: нажатие / отпускание (из пары рождаются клик, двойной клик, удержание) | | `0x23`/`0x24` | 1 | **Клавиша AI** (нейропомощник): нажатие / отпускание | | `0x1B` | 0 | Клавиша **«Настройки»** (шестерёнка) | | `0x01` | 0 | Клавиша **«Проекция»** экрана | | `0x07` | 0/1 | **Fn-Lock** (Fn+Esc): значение = новое состояние | Прочие коды в этом канале на TM2424 не приходят: **Fn отдельного кода не шлёт**, поэтому комбинации с Fn (Fn+K, Fn+F4 и т. п.) через WMI недоступны — обходим жестами Mi-кнопки. Неизвестный код `KeyRouter` не роняет: он падает в `default` и пишется в лог. > Нужен постоянно живущий слушатель WMI (COM sink). ✅ Проверено: работает **из пользовательской admin-сессии** (`ManagementEventWatcher`), поэтому **отдельная служба НЕ нужна** — подписку держит сам трей-резидент. --- ## 5. Что НЕ идёт через WMI (у референса — обычный Win32) Важно не переусложнять: часть «фич» референса вообще не про MIFS, а стандартный Windows: | Функция | Механизм | API | |---------|----------|-----| | Частота обновления экрана | `ChangeDisplaySettingsEx` + `DEVMODE` по **встроенной панели**, найденной через CCD `QueryDisplayConfig` (с `null` правился бы основной экран — XIC-21) | user32 | | Вкл/выкл тачпада и сенсорного экрана | отключение родительского HID-узла (реестр PTP не трогаем) | SetupAPI / CfgMgr32 | | Мёртвая зона у нижнего края тачпада | `SuperCurtainBottom` в `HKLM\…\PrecisionTouchPad` (himetric), применение — рестарт узла | реестр + SetupAPI | | ICC цветовой профиль | Windows Color System | `WcsSetDefaultColorProfile` (Mscms) | | Синхронизация mute с системным звуком | Core Audio | `CAudioInputControl` (IMMDevice) | Их можно взять «бесплатно», они безопасны и переносимы, но к WMI-прошивке отношения не имеют.