ESP32 udaje pada do PS3 — protokół USB od środka
Dlaczego komuś potrzebny pad od PS3
Konsola z 2006 roku, oryginalny pad DualShock 3 z padającą baterią i jedna drażliwa cecha PS3: konsola chce widzieć pada przy starcie. Bez niej nie przejdziesz przez ekran logowania, jeśli nie sparujesz pada z wyprzedzeniem. Efekt: żeby cokolwiek zrobić, potrzebujesz sprawnego oryginalnego pada — koło się domyka.
Postanowiłem zrobić pada, którego nikt nie musi kupować: ESP32-S2/S3 z natywnym USB OTG, udający DualShock 3.
USB od strony urządzeń
PS3 to nie zwykły HID PC — konsola patrzy nie tylko na deskryptory, ale też na konkretne VID/PID i na kolejność zgłoszeń. Ale fundament jest standardowy: device descriptor, configuration descriptor, interface descriptor, potem punkty końcowe (endpointy).
Pierwszy krok był oczywisty, zanim kupiłem cokolwiek:
lsusb
Bus 001 Device 014: ID 054c:0268 Sony Corp. DualShock 3 [GHUP]
Ten sam wynik zapisany — czyli pół odpowiedzi: konsola pyta urządzenie „kim jesteś", a odpowiedź to 18 bajtów, które trzeba odtworzyć co do znaku. Z tego wygenerowałem deskryptory w TinyUSB (NimBLE-era wypadałoby pominąć — tu liczy się USB, nie radio) i zacząłem iterować: zmiana jednego pola, flash, podłączenie do konsoli, patrzenie w logi.
Co PS3 sprawdza
- VID/PID zgodne z oryginalnym padem — inaczej urządzenie ląduje na liście „nieznane";
- klasa i interfeis — pad to HID z dwoma interrupt IN/OUT, nie wirtualny COM;
- pierwsze zgłoszenie po podłączeniu — konsola czeka na komunikat identyfikacyjny i karze czekać, jeśli nie przyjdzie w oknie czasowym. To najbardziej kapryśna część: iterowałem chyba z tuzin razy, aż komunikat poszedł w dobrym momencie.
Reszta (oś, przyciski, cyfry) to tablica bitów w raporcie: ustawiasz bit, wciskasz krzyżak na padzie, patrzysz na ekran — najstarsza pętla deweloperska świata, tylko że z konsolą z 2006 w roli debuggera.
Kiedy wreszcie zaskoczyło
Ekran konsoli pokazał ikonę podłączonego kontrolera. Radość absolutnie nieproporcjonalna do rozmiaru zmiany: zero linii w infrastrukturze, zero PR-ów, a jednak jakieś pół wieczoru pracy. Firmware na Zero — skompilowany, wgrany, zostawiony w konsoli na stałe.
Projekt do ogarnięcia: kwestia raportu o stanie akumulatora i rumble (konsola wysyła raporty do pada, a ESP32 musi je choćby zignorować w dobrym stylu), ale do gry w jednego gracza — działa.
Źródła: <a href="https://github.com/gwolaniuk" rel="me">github.com/gwolaniuk</a>. Jeśli ktoś bawił się w podobne rzeczy — <a href="mailto:grzegorz@pochmurny.net">grzegorz@pochmurny.net</a>.