В Surface 2 стоит комбо-чип Marvell 88W8797. Wi-Fi подключён по SDIO, Bluetooth по UART-D, а прошивка на оба радио одна и загружается по SDIO. Из таблиц ACPI видно разводку:
| Линия | Назначение |
|---|---|
UART-D, 0x70006300 | Bluetooth с аппаратным RTS/CTS |
| PU6 (GPIO 166) | Сигнал пробуждения от Bluetooth к хосту |
| PK3 (GPIO 83) | Сигнал пробуждения от хоста к Bluetooth |
| PK4, затем PK2 или PK0 | Питание всего чипа, зависит от ревизии платы |
Важная деталь: PK3 не сброс и не питание, это только пробуждение. Отдельного питания у Bluetooth нет.
Почему не работало
Транспорт Bluetooth вшит в сборку прошивки, конфигурацией он не выбирается. Получался тупик:
| Прошивка | Bluetooth | Wi-Fi с mwifiex |
|---|---|---|
linux-firmware FP66/FP68 | По SDIO, на этой плате не работает | Работает |
Surface 2, FP62_WIN 14.62.28.p58 | По UART, работает | Не работает, другой протокол |
Мы проверили 23 образа: всю историю linux-firmware, ChromiumOS, Steam Link и сборки TI. UART-версии для 8797 там нет.
Шаг 1. Заставляем Bluetooth ответить
Предыдущие попытки заканчивались одинаково: чип поднимает CTS, хост отправляет 12 байт, в ответ 0. Перебор скоростей, UART-C и UART-D ничего не давал. Когда мы проверили реальное состояние линий, причин оказалось сразу две.
Во-первых, драйвер 8250 физически не может дать 3 Мбод на этой тактовой частоте. Нужен serial-tegra, и он включается через device tree:
serial@70006300 {
clocks = <0x02 0x41>;
compatible = "nvidia,tegra30-hsuart";
interrupts = <0x00 0x5a 0x04>;
reg = <0x70006300 0x100>;
reg-shift = <0x02>;
reset-names = "serial";
resets = <0x02 0x41>;
status = "okay";
};Во-вторых, линия RTS со стороны хоста оставалась опущенной. Чип видит это как «хост не готов» и молчит. С аппаратным управлением потоком CRTSCTS чип наконец ответил на HCI_Reset:
Условия ответа:
1. прошивка с BT_UART;
2. UART-D через serial-tegra (nvidia,tegra30-hsuart);
3. поднятая линия RTS (CRTSCTS), 3 000 000 бод.Стек BlueZ поднимается одной командой:
btattach -B /dev/ttyTHS1 -P h4 -S 3000000Но с родной прошивкой Surface 2 пропал Wi-Fi.
Шаг 2. Тупик, который стоит описать
Мы разобрали, на каком языке говорит Wi-Fi-часть этой прошивки. Оказалось, что это протокол Microsoft WDI. Прошивка ответила на GET_ADAPTER_CAPABILITIES блоком в 1764 байта, то есть теоретически можно написать для Linux новый драйвер. Мы дошли до создания порта и первых этапов сканирования, но объём работы оценивался в недели.
Шаг 3. Прошивка от старшего брата
Модель драйвера Wi-Fi в Windows поменялась между 8.0 и 8.1, а первый Surface RT вышел под Windows RT 8.0. Значит, его драйвер должен быть построен по старой модели, близкой к mwifiex. Так и оказалось: в mwls97w8arm.sys есть строки классического стека MLAN, а внутри лежит вот такая прошивка:
$Id: w8797-B0, RF878X, FP62_WIN7, 14.62.17.p215, BT_UART $FP62_WIN7 означает сборку под классическую модель, BT_UART означает то, что нам нужно. Образ извлекается простым поиском цепочки блоков. У каждого блока заголовок из 16 байт: команда, адрес, длина и CRC.
def chain_end(data, start):
pos = start
while pos + 16 <= len(data):
cmd, _addr, length, _crc = struct.unpack_from("<IIII", data, pos)
if cmd not in (1, 4) or length > 1024:
return None
pos += 16 + length
if cmd == 4:
return pos if length == 0 else None
return NoneШаг 4. Патч mwifiex
Первый тест порадовал наполовину: Bluetooth заработал, прошивка загрузилась, но интерфейс mlan0 не поднялся. Прошивка 14.62 старше, чем ожидает драйвер, и отвечает на часть команд кодом 2, «не поддерживается».
Для сканирования переходим на старую команду, если версия ниже 14.66:
/* SD8797 firmware 14.62.x (Windows builds with Bluetooth over UART)
* lacks SCAN_EXT; 14.66+ (linux-firmware) supports it.
*/
if (adapter->ext_scan && adapter->fw_api_ver == 14 &&
((adapter->fw_release_number >> 8) & 0xff) < 66) {
mwifiex_dbg(adapter, MSG,
"firmware 14.%u: SCAN_EXT unsupported, using legacy scan\n",
(adapter->fw_release_number >> 8) & 0xff);
adapter->ext_scan = false;
}Две необязательные команды перестают быть фатальными. Именно CHAN_REGION_CFG останавливала инициализацию:
/* Optional commands that older firmware does not implement: ignore
* "not supported" instead of failing (fatal during initialization).
*/
if (le16_to_cpu(resp->result) == HostCmd_RESULT_NOT_SUPPORT &&
(cmdresp_no == HostCmd_CMD_CHAN_REGION_CFG ||
cmdresp_no == HostCmd_CMD_MGMT_FRAME_REG)) {
mwifiex_dbg(adapter, MSG,
"cmd %#x not supported by firmware, ignored\n",
cmdresp_no);
return 0;
}На новых прошивках эти ветки не срабатывают, поведение драйвера не меняется.
Результат
Во втором тесте Wi-Fi подключился за 8 секунд и показал 65–72 Мбит/с на MCS7, а hci0 работал одновременно с ним. После постоянной установки приём достиг 117 Мбит/с.
mwifiex_sdio mmc2:0001:1: driver_version = mwifiex 1.0 (14.62.17.p215)
mwifiex_sdio mmc2:0001:1: cmd 0x10c not supported by firmware, ignored
mwifiex_sdio mmc2:0001:1: info: associated to bssid 00:00:00:00:00:00 successfullyКак это устроено на планшете
Файлы дистрибутива мы не меняем. Ранний сервис монтирует прошивку и модули поверх штатных через mount --bind, поэтому откат мгновенный: достаточно отключить сервис.
fw=/lib/firmware/mrvl/sd8797_uapsta.bin
cd /opt/surface-bt && sha256sum -c --quiet SHA256SUMS || exit 0
mount --bind /opt/surface-bt/fw-win7.bin $fwBluetooth подключается отдельной службой, которая ждёт появления mlan0, ведь чип оживает только после загрузки прошивки:
[Unit]
After=surface-bt-overlay.service systemd-udev-settle.service
Before=bluetooth.service
[Service]
ExecStartPre=/bin/sh -c 'for i in $(seq 60); do [ -c /dev/ttyTHS1 ] && [ -e /sys/class/net/mlan0 ] && exit 0; sleep 1; done; exit 1'
ExecStart=/usr/bin/btattach -B /dev/ttyTHS1 -P h4 -S 3000000
Restart=on-failureОграничения
- Поиск Bluetooth-устройств давал около 11 % потерь ping по Wi-Fi.
- Команда
0x23fвозвращает ошибку, поэтому ширина канала отображается как HT20. - С включённым энергосбережением прошивка может зависнуть, поэтому оно выключено.
- 5 ГГц не проверялись.
Получить прошивку: инструкция.