Я люблю брать Nintendo Switch 2 в поездки, и каждый раз упираюсь в одно и то же: официальный док громоздкий и выглядит хрупким, поэтому с собой я его не вожу. Обидно, потому что док работает через DisplayPort Alt Mode по USB-C, а у меня уже есть USB-C хаб, который это умеет и без проблем работает со всеми остальными моими устройствами. Но Nintendo, как это с ней часто бывает, пошла своим путём, и обычный хаб доком не станет.
Недавно друг подарил мне сниффер USB-PD. Я решил посмотреть, что именно делает консоль и почему она соглашается работать только с официальным доком.
Хаб
Хаб у меня — Cable Matters 7-in-1 на три монитора: DP, HDMI и VGA, питание до 60 Вт. Я разобрал его и посмотрел, что внутри:
VL103 — контроллер USB-PD;
VL817 — USB-хаб;
RTD2142 — MST-хаб DisplayPort (здесь он работает конвертером DP→HDMI);
флеш-память XT25F16B с прошивками.



От этого хаба Switch уже заряжается, значит, обычный USB-PD он понимает. Можно предположить, что фирменное «рукопожатие» дока Nintendo идёт поверх USB-PD, в сообщениях, которые определяет сам производитель (vendor-defined messages).
Со стоковой прошивкой Switch заходит на удивление далеко. Она договаривается о питании, входит в DisplayPort Alt Mode и даже настраивает pin assignment D, и хаб всё это подтверждает. А дальше ничего: ни режима телевизора, ни картинки. Одного DisplayPort Alt Mode мало, сначала должно произойти что-то ещё.
Инструменты
Сниффер USB-PD. Это WITRN K2: он включается в разрыв USB-C кабеля и показывает PD-трафик. К нему есть только приложение для Windows, поэтому я попросил Claude написать маленькую консольную утилиту для Linux. Она читает HID-отчёты в отдельном потоке, декодирует их через usbpdparser и пишет JSONL, который потом можно воспроизвести.
Прошивка контроллера PD. В VL103 стоит микроконтроллер 8051 с 32 КиБ прошивки. Плагин vli в fwupd уже умеет снимать и заливать её через хаб VL817, ему не хватало только записи quirk с USB ID этого хаба. В конце образа лежит CRC-16/USB, а свободного стёртого места под новый код много.
sudo fwupdtool --plugins vli firmware-dump OUT.bin <device-id> sudo fwupdtool --plugins vli --allow-reinstall --prepare --cleanup \ install-blob patched.bin <device-id>
Для реверса я взял процессорный модуль 8051 в Ghidra для дизассемблирования, ассемблер из SDCC для патчей и симулятор 8051 uCsim, чтобы гонять настоящую прошивку на записанных дампах трафика.
Опыт программирования под 8051 у меня есть, но Claude и GPT я использовал в этом проекте очень активно: они помогали с дизассемблированием, патчами и разбором того, что делает родная прошивка хаба. Без них довести дело до конца было бы куда тяжелее.
Слушаем официальный док
Подключаю сниффер, запускаю на ПК скрипт и смотрю на трафик USB-PD:
14:19:11.863 SOP Source_Capabilities Source/DFP id=0 [F 5.0V@3.0A, F 9.0V@3.0A, F 15.0V@2.8A] 14:19:11.868 SOP Request Sink/UFP id=0 [[3] F 15.0V@2.8A] 14:19:12.070 SOP Vendor_Defined Sink/DFP id=4 VDM PD SID(0xFF00) Discover Identity REQ 14:19:12.073 SOP Vendor_Defined Source/UFP id=5 VDM PD SID(0xFF00) Discover Identity ACK VDOs: 0x5460057E 0x00000000 0x20620000 0x65000031 0x00000000 14:19:12.080 SOP Vendor_Defined Source/UFP id=6 VDM PD SID(0xFF00) Discover SVIDs ACK SVIDs: DisplayPort(0xFF01), Nintendo(0x057E) 14:19:12.162 SOP Vendor_Defined Sink/DFP id=0 VDM Nintendo(0x057E) Enter Mode REQ mode #1 14:19:12.183 SOP Vendor_Defined Sink/DFP id=1 VDM DisplayPort(0xFF01) Enter Mode REQ mode #1 14:19:12.210 SOP Vendor_Defined Sink/DFP id=3 VDM DisplayPort(0xFF01) 17 REQ VDOs: 0x00000806 14:19:12.228 SOP Vendor_Defined Sink/DFP id=4 VDM/unstructured Nintendo(0x057E) 0x0001 VDOs: 0x00000020 14:19:12.240 SOP Vendor_Defined Source/UFP id=5 VDM/unstructured Nintendo(0x057E) 0x0041 VDOs: 0x00000020 0x00000000 14:19:12.249 SOP Vendor_Defined Sink/DFP id=5 VDM/unstructured Nintendo(0x057E) 0x0001 VDOs: 0x00000001 14:19:12.260 SOP Vendor_Defined Source/UFP id=6 VDM/unstructured Nintendo(0x057E) 0x0041 VDOs: 0x00000001 0x03010503 14:19:12.438 SOP Vendor_Defined Sink/DFP id=6 VDM/unstructured Nintendo(0x057E) 0x0001 VDOs: 0x00000002 14:19:12.450 SOP Vendor_Defined Source/UFP id=7 VDM/unstructured Nintendo(0x057E) 0x0041 VDOs: 0x00000002 0x0003006E 14:19:12.458 SOP Vendor_Defined Sink/DFP id=7 VDM/unstructured Nintendo(0x057E) 0x0001 VDOs: 0x00000006 14:19:12.471 SOP Vendor_Defined Source/UFP id=0 VDM/unstructured Nintendo(0x057E) 0x0041 VDOs: 0x00000006 0x330107DA 0x06016D68 0x330107DA 0x06016D68
Сразу бросается в глаза несколько вещей:
Discover Identity. Док представляется как устройство Nintendo (VID 057E), продукт 2062, тип «периферия».
Discover SVIDs. Кроме DisplayPort (FF01) док объявляет собственный SVID Nintendo, 057E. Switch входит в режим Nintendo раньше, чем в режим DisplayPort.
Сообщения Nintendo. После настройки DP консоль шлёт серию неструктурированных vendor-сообщений. Каждый запрос — это 057E0001 плюс номер команды, а док отвечает 057E0041, той же командой и данными.
Команда | Ответ дока | Примечание |
|---|---|---|
20 | 00000000 | ещё и опрашивается периодически |
01 | 03010503 | |
29, 44, 41, 49, 23, 131, 51 | эхо | |
02 | 0003006E | похоже на номер версии |
06 | 330107DA 06016D68 ×2 | запись о версии, тег hm |
04 | 330107DA 06016D61 ×2 | запись о версии, тег am |
Что значит большая часть этого, я не знаю. Записи о версиях совпадают по формату с версиями прошивок Cypress/Infineon CCGx, а в другом проекте по патчингу доков видели, что ответ на 02 менялся после обновления официального дока.
Учим хаб рукопожатию
Начал я с того, что снял прошивку VL103 и попросил Claude её декомпилировать и разобрать. Когда он попытался запустить Ghidra, сработал классификатор безопасности и работу остановил. Поэтому декомпиляцией занимался GPT.
В прошивку VL103 я добавил обработчик: около 540 байт кода 8051 в стёртой области, встроенных в стоковый PD-стек. Он отвечает на сообщения Nintendo, а всё остальное оставляет родному коду. Дальше я менял по одной вещи за раз:
Версия | Что изменил | Результат |
|---|---|---|
v1/v2 | отвечаю на Discover/Enter Mode от Nintendo и на эхо-команды | на линии подтверждения, DP настроен, картинки нет |
v3 | предлагаю 15 В / 2,8 А, как док (стоковый хаб: 2,53 А) | картинки нет |
v4 | отдаю vendor ID Nintendo вместо VIA | картинки нет |
v5 | отдаю полную идентификацию официального дока | режим телевизора работает! |
v6 | отвечаю на 02 версией дока | теперь Switch застревает на 06 |
v7 | отвечаю на 06 и 04 записями дока | последовательность совпадает целиком, док подключается быстрее |
Самое интересное — разница между v4 и v5: одного vendor ID мало, Switch проверяет идентификацию шире. v7 убрала паузу примерно в 3 секунды: с v5 консоль запрашивала 02, получала «Not supported» и ждала, прежде чем продолжить.
Ответы с версиями скопированы с официального дока. Это не настоящие версии чего-либо в этом хабе, и за этими командами ничего не реализовано. На любую команду, которую я не распознаю, по-прежнему уходит стоковый ответ «Not supported».
HDR выглядит блёклым
Режим телевизора заработал, но с включённым HDR цвета были заметно бледнее. Официальный док на том же мониторе и в той же игре выдавал нормальную картинку. Что я исключил:
Дисплей. То же самое и на мониторе, и на телевизоре, и оба показывают значок HDR.
Диапазон RGB. С ограниченным становится хуже, с полным всё равно блёкло.
Хаб в целом. Мой ПК с Linux выводит HDR через этот же хаб правильно.
Контроллер PD. PD-трафик с HDR и без него одинаковый.
Картинка выглядела так, будто перепутано цветовое пространство. Моя гипотеза была такой: Switch отдаёт цвет в BT.2020, а дисплею сообщают, что это BT.709, и он сжимает цвета. Сторона DisplayPort контроллер PD вообще не задевает, она идёт прямо в RTD2142. Значит, следующий на очереди этот чип.
Внутри RTD2142
Сначала нужно было снять его прошивку. В flashrom есть программатор realtek_mst_i2c_spi, который общается с чипом по I²C поверх DP AUX, через тот же USB-C кабель. Два чтения дали одинаковые образы по 2 МиБ. Прошивка не подписана, так что изменённый образ можно просто залить (запасным планом была клипса SOIC-8).
flashrom -p realtek_mst_i2c_spi:bus=7,allow_brick=yes,reset_mcu=1 -r rtd2142.bin
Анализ. Внутри снова 8051, на этот раз с восемью банками кода по 64 КиБ, которые переключаются через трамплины. Я доработал свой симулятор, чтобы он отслеживал переключения банков.
Больше всего помогла база данных инструмента Realtek для родственного RTD2172, которую я нашёл в публичном репозитории на GitHub: там есть имена и описания аппаратных регистров, и они совпали с тем, что делает эта прошивка.
Выход HDMI описывает цвет картинки в AVI InfoFrame — небольшом пакете, который идёт вместе с видео. Поля колориметрии в нём говорили BT.709. Конвертер берёт BT.2020 из входящего потока DisplayPort, только если он объявлен одним конкретным стандартным способом, а цветовую метку на HDMI обновляет только при запуске выхода. Метаданные яркости HDR идут отдельным пакетом, поэтому телевизор всё равно переключался в режим HDR.
Я посмотрел, как с этим справляются другие сторонние доки. Они выпускали обновления прошивки, которые «исправляют серый или тусклый HDR». В одном из них есть отладочная строка «switch2 color space patch»: увидев Switch 2 и поток, где BT.2020 не помечен стандартным способом, прошивка берёт колориметрию из EDID дисплея. Так что не я один на это наткнулся.
Исправление HDR
Мой патч не пытается распознать Switch, а смотрит на сами метаданные HDR:
если метаданные HDR есть и передаточная функция у них PQ или HLG, каждый AVI InfoFrame помечается как BT.2020, а его контрольная сумма пересчитывается;
InfoFrame пересобирается при каждом включении и выключении HDR;
больше ничего не меняется: те же пиксели, те же пакеты для SDR, ничего в пути DP и при запуске.
Это 108 байт в одном банке: три небольших хука и немного кода в стёртом заполнении. Перед прошивкой я прогнал оригинальную прошивку с патчем в симуляторе: все форматы пикселей, переключения HDR туда и обратно и полную цепочку от входа до выхода.
Результат: HDR выглядит правильно. С пикселями всё было в порядке с самого начала, неправильной была только метка.
Выход DisplayPort у хаба идёт другим путём, который я не патчил, поэтому HDR через порт DP по-прежнему блёклый. Я решил, что мне это не настолько важно, чтобы чинить :)
А VRR?
Раз хаб теперь притворяется официальным доком, Switch предлагает и VRR. Если его включить, картинка через порт DP замирает, а на HDMI частота так и остаётся 60 Гц. Судя по прошивке, поддержки VRR в ней нет вообще: она никогда не сообщает источнику, что умеет переменные тайминги, и никогда не отправляет HDMI-пакет VRR.
Я хотел полноценный док, так что я всё же попробовал. У прошивки RTD2142 нашёлся отладочный интерфейс на I²C-адресе 0x35 через тот же канал AUX: одна команда читает любой регистр, другая записывает. Это позволило читать живое состояние чипа и трогать регистры, ничего не прошивая.
Чтобы получить источник, который действительно использует VRR, я сделал второй патч, с которым хаб объявляет поддержку Adaptive-Sync, и подсунул Linux подменённый EDID. amdgpu радостно включил VRR, и экран погас, как только частота кадров начала реально меняться. Главный цикл чипа переставал отвечать, пока тайминги снова не становились стабильными. Он не перезагружался, и отключение сторожевого таймера FIFO и прерываний не помогло. Лучшая моя догадка — цикл повторного захвата тактовой частоты, который так и не устаканивается.
Чтобы это заработало, пришлось бы переписать то, как чип захватывает входной сигнал, надеяться, что его генератор таймингов справится с переменными кадрами, и добавить HDMI-пакет VRR. На этом я остановился и вернулся к прошивке только с исправлением HDR. При работе через такой хаб я просто выключаю VRR в настройках Switch.
Итог
Теперь хаб работает дорожным доком для того, что мне важно: режим телевизора и HDR по HDMI, и Switch при этом заряжается. USB-порты и Ethernet я толком не проверял. VRR не работает, поэтому я держу его выключенным в настройках Switch. Всего хабу понадобилось около 540 байт в контроллере PD и 108 байт в RTD2142.
Хотя хаб теперь представляется официальным доком Nintendo, с моим ПК и телефоном он по-прежнему работает как обычно.
Отдельное спасибо VIA и Realtek за то, что ставят туда 8051: с ними взлом становится куда проще и доступнее :)
Обновление: код опубликован. Патчер лежит на GitHub: https://github.com/mkopec/switch2-hub-patcher. Пробуйте.
