| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- #include "mode_ps3.h"
- #include "triton.h"
- #include "gamepad_util.h"
- #include "config.h"
- #include "haptics.h"
- #include "bonds.h"
- #include "usb_tx.h"
- #include <Adafruit_TinyUSB.h>
- #include <Arduino.h>
- #include <string.h>
- Ps3Controller g_ps3Ctl;
- // ---------------------------------------------------------------------------------------------------------
- // Genuine Sony Sixaxis / DualShock 3 (054C:0268) HID report descriptor -- 148 bytes, captured verbatim from
- // real hardware (Nefarius "SIXAXIS native HID Report Descriptor"). The PS3 console does NOT parse this to
- // drive input -- it recognises the pad by VID/PID and the control-transfer handshake below -- but a faithful
- // descriptor (declaring input 0x01, output 0x01, and feature reports 0x01/0x02/0xEE/0xEF) keeps both the
- // console and PC HID stacks happy. Do not "tidy" it: this is the real firmware's (famously odd) descriptor.
- // ---------------------------------------------------------------------------------------------------------
- static const uint8_t DS3_HID_DESC[148] = {
- 0x05, 0x01, 0x09, 0x04, 0xA1, 0x01, 0xA1, 0x02, 0x85, 0x01, 0x75, 0x08,
- 0x95, 0x01, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x81, 0x03, 0x75, 0x01, 0x95,
- 0x13, 0x15, 0x00, 0x25, 0x01, 0x35, 0x00, 0x45, 0x01, 0x05, 0x09, 0x19,
- 0x01, 0x29, 0x13, 0x81, 0x02, 0x75, 0x01, 0x95, 0x0D, 0x06, 0x00, 0xFF,
- 0x81, 0x03, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x05, 0x01, 0x09, 0x01, 0xA1,
- 0x00, 0x75, 0x08, 0x95, 0x04, 0x35, 0x00, 0x46, 0xFF, 0x00, 0x09, 0x30,
- 0x09, 0x31, 0x09, 0x32, 0x09, 0x35, 0x81, 0x02, 0xC0, 0x05, 0x01, 0x75,
- 0x08, 0x95, 0x27, 0x09, 0x01, 0x81, 0x02, 0x75, 0x08, 0x95, 0x30, 0x09,
- 0x01, 0x91, 0x02, 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, 0xB1, 0x02, 0xC0,
- 0xA1, 0x02, 0x85, 0x02, 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, 0xB1, 0x02,
- 0xC0, 0xA1, 0x02, 0x85, 0xEE, 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, 0xB1,
- 0x02, 0xC0, 0xA1, 0x02, 0x85, 0xEF, 0x75, 0x08, 0x95, 0x30, 0x09, 0x01,
- 0xB1, 0x02, 0xC0, 0xC0
- };
- // ---------------------------------------------------------------------------------------------------------
- // Magic GET_REPORT(Feature) responses. Lifted verbatim from GIMX-firmwares EMUPS3 (matlo) -- a LUFA-based DS3
- // emulator proven to enumerate on a real PS3. GIMX sends each 64-byte array RAW (Endpoint_Write_Control_-
- // Stream_LE), so the genuine wire byte 0 is the array's first byte -- which for a Sixaxis is NOT the report
- // id (e.g. GET 0xF5 returns 01 00.., GET 0xEF returns 00 EF..). See emitFeature() for how we reproduce that
- // despite TinyUSB force-prepending the report id. The PS3 enable handshake is:
- // GET 0x01 -> GET 0xF2 -> {SET 0xEF / GET 0xEF}x2 -> GET 0xF8 -> SET output 0x01 -> SET 0xF4(enable)
- // then input report 0x01 streams. (GIMX does not even handle 0xF4 and streams unconditionally; so do we.)
- // ---------------------------------------------------------------------------------------------------------
- static const uint8_t report_01[64] = {
- 0x00, 0x01, 0x04, 0x00, 0x07, 0x0c, 0x01, 0x02, 0x18, 0x18, 0x18,
- 0x18, 0x09, 0x0a, 0x10, 0x11, 0x12, 0x13, 0x00, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x04, 0x04,
- 0x04, 0x04, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0x07, 0x00, 0x17,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
- // 0xF2 device info; bytes [4..9] = the controller's Bluetooth MAC (overlaid from g_ds3Mac at runtime).
- static const uint8_t report_f2[64] = {
- 0xf2, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00,
- 0x03, 0x50, 0x81, 0xd8, 0x01, 0x8a, 0x13, 0x00, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x04, 0x04,
- 0x04, 0x04, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0x07, 0x00, 0x17,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
- // 0xF5 host-pairing info; bytes [2..7] = master (host) BT MAC, overlaid once learned from a SET 0xF5.
- static const uint8_t report_f5[64] = {
- 0x01, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xff, 0xf7, 0x00,
- 0x03, 0x50, 0x81, 0xd8, 0x01, 0x8a, 0x13, 0x00, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x04, 0x04,
- 0x04, 0x04, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0x07, 0x00, 0x17,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
- // 0xEF / 0xF8 calibration; byte [7] echoes whatever the PS3 last wrote in SET 0xEF byte[6] (the "do you
- // remember" check the console performs during the 0xEF dance).
- static const uint8_t report_ef[64] = {
- 0x00, 0xef, 0x04, 0x00, 0x07, 0x03, 0x01, 0xb0, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x6b, 0x02, 0x68, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
- static const uint8_t report_f8[64] = {
- 0x00, 0x01, 0x00, 0x00, 0x07, 0x03, 0x01, 0xb0, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x6b, 0x02, 0x68, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
- static const uint8_t report_f7[64] = {
- 0x01, 0x04, 0xc4, 0x02, 0xd6, 0x01, 0xee, 0xff, 0x14, 0x13, 0x01,
- 0x02, 0xc4, 0x01, 0xd6, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x03,
- 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x62, 0x01, 0x02, 0x01, 0x5e,
- 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
- // One HID slot -- a PS3 expects exactly one Sixaxis per USB port.
- static Adafruit_USBD_HID g_ds3;
- static unsigned long g_ds3LastMs = 0;
- // A slot counts as the live controller while it has replied within this window (matches usb_mount's CONN_UP_MS).
- #define DS3_CONN_MS 1200u
- // First bonded slot with a recent RF reply (the controller we present), or -1 if none is connected. Used for
- // both input streaming and rumble routing (this mode is static, so the dynamic g_usbToBond map isn't built).
- static int ds3ActiveSlot()
- {
- unsigned long now = millis();
- for (int s = 0; s < NSLOT; s++)
- if (g_slot[s].used && g_connReplyMs[s] &&
- (now - g_connReplyMs[s]) < DS3_CONN_MS)
- return s;
- return -1;
- }
- static uint8_t g_byte6ef =
- 0xb0; // last SET-0xEF byte[6]; echoed back in GET 0xEF/0xF8 byte[7]
- static uint8_t g_masterBd[6] = {
- 0, 0, 0, 0, 0, 0
- }; // host MAC learned via SET 0xF5
- static bool g_haveMaster = false;
- // Controller BT MAC. OUI 00:1B:DC matches the other PS modes here; last byte 0x80 keeps it distinct. Used
- // only for BT pairing identity -- irrelevant over USB, but the PS3 reads it via GET 0xF2.
- static uint8_t g_ds3Mac[6] = { 0x00, 0x1B, 0xDC, 0x4F, 0x55, 0x80 };
- // Reproduce a genuine 64-byte Sixaxis feature report on the wire. TinyUSB's HID GET_REPORT path force-writes
- // the requested report id as wire byte 0 and hands us the buffer PAST it (`buf` = ctrl[1]); a real Sixaxis
- // (and GIMX) instead puts the array's own first byte there. So we overwrite that prepended id via buf[-1]
- // (== ctrl[0], a valid writable byte for this control transfer) and copy bytes [1..] into buf. Returns the
- // payload length; the stack adds the 1 byte we placed -> total = min(64, wLength), exactly like GIMX's
- // Endpoint_Write_Control_Stream_LE(arr, wLength). reqlen is post-prepend (host wLength - 1); the PS3 always
- // reads these with wLength >= 8, so the prepend always happened and buf[-1] is in bounds.
- static uint16_t emitFeature(uint8_t *buf, uint16_t reqlen,
- const uint8_t arr[64])
- {
- // buf[-1] is only the prepended id (and writable) when the stack actually prepended -- which it does iff
- // the host's wLength was > 1. reqlen here is already post-decrement, so reqlen < 2 means either no prepend
- // (wLength <= 1) or a 2-byte request; bail in both cases so we never write out of bounds. The PS3 reads
- // these reports with wLength >= 8, so real requests are unaffected.
- if (reqlen < 2)
- return 0;
- buf[-1] = arr[0];
- uint16_t n = 63; // 64-byte report minus byte 0 (placed above)
- if (n > reqlen)
- n = reqlen;
- memcpy(buf, arr + 1, n);
- return n;
- }
- static uint16_t ds3GetReport(uint8_t rid, hid_report_type_t type, uint8_t *buf,
- uint16_t reqlen)
- {
- if (type != HID_REPORT_TYPE_FEATURE || !buf || reqlen == 0)
- return 0;
- uint8_t r[64];
- switch (rid) {
- case 0x01:
- return emitFeature(buf, reqlen, report_01);
- case 0xf2: // overlay our controller MAC at [4..9]
- memcpy(r, report_f2, 64);
- memcpy(r + 4, g_ds3Mac, 6);
- return emitFeature(buf, reqlen, r);
- case 0xf5: // overlay learned host MAC at [2..7] (else the dummy aa.. from the template)
- memcpy(r, report_f5, 64);
- if (g_haveMaster)
- memcpy(r + 2, g_masterBd, 6);
- return emitFeature(buf, reqlen, r);
- case 0xef: // echo the PS3's remembered byte at [7]
- memcpy(r, report_ef, 64);
- r[7] = g_byte6ef;
- return emitFeature(buf, reqlen, r);
- case 0xf8:
- memcpy(r, report_f8, 64);
- r[7] = g_byte6ef;
- return emitFeature(buf, reqlen, r);
- case 0xf7:
- return emitFeature(buf, reqlen, report_f7);
- default:
- return 0;
- }
- }
- static void ds3SetReport(uint8_t rid, hid_report_type_t type, uint8_t const *b,
- uint16_t n)
- {
- if (type == HID_REPORT_TYPE_FEATURE) {
- // 0xEF: remember byte[6] so the next GET 0xEF/0xF8 echoes it. 0xF5: learn the host's BT MAC.
- // 0xF4: the PS3's "set operational"/power command -- ACK silently (no gating needed; we stream
- // unconditionally like GIMX). b[1]==0x08 would be a power-off request; ignored here.
- if (rid == 0xef && n >= 7)
- g_byte6ef = b[6];
- else if (rid == 0xf5 && n >= 8) {
- memcpy(g_masterBd, b + 2, 6);
- g_haveMaster = true;
- }
- return;
- }
- if (type != HID_REPORT_TYPE_OUTPUT || n < 1)
- return;
- // DS3 output report 0x01 (rumble + LED). It reaches us two ways: on the OUT interrupt endpoint (rid=0,
- // report id 0x01 included as b[0]) or via control SET_REPORT(Output,0x01) (rid=0x01, id not in the data).
- // Normalise to `p` = the bytes AFTER the report id, where the full report is
- // [0]=id [1]=0x00 [2]=small(right/HFR) dur [3]=small power(on/off) [4]=large(left/LFR) dur [5]=large power.
- // So in p: p[2]=small power, p[4]=large power. Map large->lowFreq, small->highFreq;
- // 257 = 65535/255 scales 8-bit power to the 16-bit haptic range.
- const uint8_t *p;
- uint16_t pn;
- if (rid == 0) { // OUT endpoint: report id leads the transfer
- if (b[0] != 0x01)
- return;
- p = b + 1;
- pn = (uint16_t)(n - 1);
- } else if (rid ==
- 0x01) { // control SET_REPORT: id may or may not be echoed in the data
- if (b[0] == 0x01) {
- p = b + 1;
- pn = (uint16_t)(n - 1);
- } else {
- p = b;
- pn = n;
- }
- } else {
- return;
- }
- if (pn < 5)
- return;
- int slot = ds3ActiveSlot();
- if (slot < 0)
- return;
- hapticSteamRumble((uint16_t)p[4] * 257u, p[2] ? 0xFFFFu : 0u,
- (uint8_t)slot);
- }
- // SC2 IMU int16 (center 0) -> DS3 10-bit unsigned (center 511), little-endian on the wire (low byte first).
- // >>6 maps the full int16 swing onto roughly +/-512 around center; the PS3 applies its own calibration so
- // exact scale is not critical. Writes 2 bytes at out[0..1].
- static void ds3Imu(uint8_t *out, int16_t v)
- {
- int32_t e = 511 + ((int32_t)v >> 6);
- if (e < 0)
- e = 0;
- if (e > 1023)
- e = 1023;
- out[0] = (uint8_t)(e & 0xFF);
- out[1] = (uint8_t)((e >> 8) & 0xFF);
- }
- // Build the 48-byte input-report PAYLOAD (the stack prepends report id 0x01 -> 49-byte Sixaxis report).
- // Offsets are the genuine report's rd[] minus one (rd[0] is the prepended id).
- static void ds3Build(uint8_t slot, uint8_t out[48])
- {
- uint32_t b = psButtonsFromSteam(g_in[slot].buttons);
- bool l2 = (g_in[slot].lt > SW_TRIG_ON) || (b & TB_L2);
- bool r2 = (g_in[slot].rt > SW_TRIG_ON) || (b & TB_R2);
- memset(out, 0, 48);
- // out[0] reserved (rd[1]); if it were 0xFF the kernel/console would drop the report -- keep 0.
- // out[1] (rd[2]): Select(bit0) L3 R3 Start(bit3) Up Right Down Left.
- // SC2 View -> Start, Menu -> Select: matches the physical button positions on the puck/controller (the
- // opposite of the PS5/DS4 Create/Options convention, which read reversed on the DS3 in testing).
- out[1] = ((b & TB_MENU) ? 0x01 : 0) | ((b & TB_L3) ? 0x02 : 0) |
- ((b & TB_R3) ? 0x04 : 0) | ((b & TB_VIEW) ? 0x08 : 0) |
- ((b & TB_DUP) ? 0x10 : 0) | ((b & TB_DRT) ? 0x20 : 0) |
- ((b & TB_DDN) ? 0x40 : 0) | ((b & TB_DLF) ? 0x80 : 0);
- // out[2] (rd[3]): L2 R2 L1 R1 Triangle Circle Cross Square. g_abSwap swaps the A/B + X/Y face pair.
- uint8_t tri, cir, crs, sqr;
- if (g_abSwap) {
- tri = (b & TB_A) ? 0x10 : 0;
- cir = (b & TB_B) ? 0x20 : 0;
- crs = (b & TB_X) ? 0x40 : 0;
- sqr = (b & TB_Y) ? 0x80 : 0;
- } else {
- tri = (b & TB_Y) ? 0x10 : 0;
- cir = (b & TB_B) ? 0x20 : 0;
- crs = (b & TB_A) ? 0x40 : 0;
- sqr = (b & TB_X) ? 0x80 : 0;
- }
- out[2] = (l2 ? 0x01 : 0) | (r2 ? 0x02 : 0) | ((b & TB_LB) ? 0x04 : 0) |
- ((b & TB_RB) ? 0x08 : 0) | tri | cir | crs | sqr;
- // out[3] (rd[4]): PS button
- out[3] = (b & TB_STEAM) ? 0x01 : 0;
- // out[5..8] (rd[6..9]): LX LY RX RY, center 0x80
- int16_t lx, ly, rx, ry;
- slotSticks(slot, &lx, &ly, &rx, &ry);
- out[5] = swStick(lx, false);
- out[6] = swStick(ly, true);
- out[7] = swStick(rx, false);
- out[8] = swStick(ry, true);
- // out[13..24] (rd[14..25]): analog pressures, order Up Right Down Left L2 R2 L1 R1 Tri Cir Cross Square
- out[13] = (b & TB_DUP) ? 0xFF : 0;
- out[14] = (b & TB_DRT) ? 0xFF : 0;
- out[15] = (b & TB_DDN) ? 0xFF : 0;
- out[16] = (b & TB_DLF) ? 0xFF : 0;
- out[17] = g_in[slot].lt;
- out[18] = g_in[slot].rt;
- out[19] = (b & TB_LB) ? 0xFF : 0;
- out[20] = (b & TB_RB) ? 0xFF : 0;
- out[21] = tri ? 0xFF : 0;
- out[22] = cir ? 0xFF : 0;
- out[23] = crs ? 0xFF : 0;
- out[24] = sqr ? 0xFF : 0;
- // out[28..30] (rd[29..31]): genuine DS3 link/power state.
- // 0x02 = plugged, 0x05 = full power, 0x10 = wired + rumble capable.
- out[28] = 0x02;
- out[29] = 0x05;
- out[30] = 0x10;
- // out[40..47] (rd[41..48]): accel X, accel Z, accel Y, gyro Z -- each 10-bit LE, center 511.
- ds3Imu(out + 40, g_in[slot].ax);
- ds3Imu(out + 42, g_in[slot].az);
- ds3Imu(out + 44, g_in[slot].ay);
- ds3Imu(out + 46, g_in[slot].gz);
- }
- // Neutral input report: centered sticks + IMU, no buttons. Streamed when no controller is linked so the PS3
- // keeps the Sixaxis "alive" (and a games/XMB controller slot) instead of seeing a silent endpoint.
- static void ds3Neutral(uint8_t out[48])
- {
- memset(out, 0, 48);
- out[5] = out[6] = out[7] = out[8] = 0x80; // sticks centered
- // Keep neutral reports semantically identical to an attached wired DS3.
- out[28] = 0x02; // plugged
- out[29] = 0x05; // full power
- out[30] = 0x10; // wired + rumble capable
- ds3Imu(out + 40, 0);
- ds3Imu(out + 42, 0);
- ds3Imu(out + 44, 0);
- ds3Imu(out + 46, 0);
- }
- void Ps3Controller::usbIdentity()
- {
- // Genuine Sixaxis / DualShock 3 identity. The PS3 recognises the pad by exactly this VID/PID.
- USBDevice.setID(0x054C, 0x0268);
- USBDevice.setVersion(0x0200);
- USBDevice.setDeviceVersion(0x0100);
- USBDevice.setManufacturerDescriptor("Sony");
- USBDevice.setProductDescriptor("PLAYSTATION(R)3 Controller");
- }
- // Static mount: present the single DS3 interface permanently (the setup() static path calls begin() then
- // attaches; it does NOT call usbIdentity() or build a slot pool, so we set the identity and register the HID
- // here). g_ds3.begin() both adds the interface to the descriptor and locks its TinyUSB instance.
- void Ps3Controller::begin()
- {
- usbIdentity();
- g_ds3.enableOutEndpoint(
- true); // OUT endpoint for rumble/LED output reports
- g_ds3.setReportCallback(ds3GetReport, ds3SetReport);
- g_ds3.setReportDescriptor(DS3_HID_DESC, sizeof DS3_HID_DESC);
- g_ds3.setPollInterval(1); // 1 ms interrupt IN, like the genuine pad
- g_ds3.begin();
- }
- void Ps3Controller::task()
- {
- if (!g_ds3.ready())
- return;
- if (millis() - g_ds3LastMs < USB_STREAM_MS)
- return;
- g_ds3LastMs = millis();
- uint8_t p[48];
- int slot = ds3ActiveSlot();
- if (slot >= 0)
- ds3Build((uint8_t)slot, p);
- else
- ds3Neutral(p);
- usbTxHid(&g_ds3, 0x01, p, sizeof p);
- }
|