radio.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. #include "radio.h"
  2. // Pairing rendezvous bytes from IBEX rodata. The 0x91A2A793 key that precedes the "ibex" string is a
  3. // DIFFERENT key, NOT the discovery address (confirmed via PTR_s_ibexesb_common=0x56f99 -> "ibex...").
  4. const uint8_t PAIR_BASE[4] = { 0x69, 0x62, 0x65, 0x78 }; // "ibex"
  5. uint8_t g_rfPrefix = 0x10;
  6. uint8_t g_rfCh = 2;
  7. uint8_t g_rfBase[4] = { 0x69, 0x62, 0x65, 0x78 }; // "ibex"
  8. // ch18 measured clean (RSSI 95/90, PER 0) on the REAL PUCK's own channel scan and is in its active hop set
  9. // {18,2,80}; trackpad data bursts collide on bad channels -> reply-rate crash. Tunable 'C'.
  10. uint8_t g_sessCh = 18;
  11. // pre-init to "ibex"/0x10 as a safe default; rfGenSessionAddr() overwrites with the per-device value at boot.
  12. uint8_t g_sessBase[4] = { 0x69, 0x62, 0x65, 0x78 };
  13. uint8_t g_sessPrefix = 0x10;
  14. void rfGenSessionAddr()
  15. {
  16. // Mix the 64-bit FICR DEVICEID into a 4-byte base + a prefix. Deterministic per chip (stable across reboots,
  17. // debuggable) and unique across chips (DEVICEID is unique) -> two OpenPucks never collide on the connected
  18. // session. Avoid degenerate address bytes (0x00/0xFF) for clean preamble correlation, and never reproduce
  19. // the shared discovery base "ibex" (else the isolation is lost).
  20. uint32_t h = NRF_FICR->DEVICEID[0] * 0x9E3779B1u ^
  21. NRF_FICR->DEVICEID[1];
  22. uint32_t h2 = NRF_FICR->DEVICEID[1] * 0x85EBCA6Bu ^
  23. NRF_FICR->DEVICEID[0];
  24. for (int i = 0; i < 4; i++) {
  25. uint8_t b = (uint8_t)(h >> (i * 8));
  26. if (b == 0x00 || b == 0xFF)
  27. b ^= 0x5A;
  28. g_sessBase[i] = b;
  29. }
  30. uint8_t p = (uint8_t)(h2 >> 16);
  31. if (p == 0x00 || p == 0xFF)
  32. p = 0x5C;
  33. g_sessPrefix = p;
  34. if (g_sessBase[0] == g_rfBase[0] && g_sessBase[1] == g_rfBase[1] &&
  35. g_sessBase[2] == g_rfBase[2] && g_sessBase[3] == g_rfBase[3])
  36. g_sessBase[0] ^= 0x80; // collided with "ibex"
  37. }
  38. uint8_t rfrx[100], rftx[100];
  39. uint32_t g_rfRxCount = 0;
  40. // HYBRID puck-link config (firmware-derived, definitive). DECODED from real-puck capture: puck TX = Ble_2Mbit,
  41. // ENDIAN=Big (MSB-first), NO whitening, addr "ibex", CRC16 0x11021/0xFFFF; frame = FUN_00027e9a (static length).
  42. uint32_t g_crcinit = 0xFFFF;
  43. uint8_t g_whiteiv = 37;
  44. // <-- was the bug (1Mbit). Real puck = 2Mbit.
  45. uint8_t g_mode = RADIO_MODE_MODE_Ble_2Mbit;
  46. uint32_t g_pcnf0 =
  47. 0x00030008UL; // S0LEN0, LFLEN8, S1LEN3 (ESB DPL) - CRC-VALIDATED
  48. uint8_t g_statlen = 0x20;
  49. // ENDIAN=Big, BALEN4, MAXLEN=96 (was 64; longer F1 frames e.g. 0x43-augmented)
  50. uint32_t g_pcnf1 = 0x01040060;
  51. uint32_t g_crcpoly = 0x11021UL;
  52. uint16_t g_crccnf = 0x2; // CRC16, address included
  53. uint8_t g_pid = 0;
  54. uint8_t g_balen = 4; // ESB 5-byte addr
  55. bool g_prefixRaw = false;
  56. uint8_t rfBitrev8(uint8_t x)
  57. {
  58. uint8_t y = 0;
  59. for (int i = 0; i < 8; i++) {
  60. y = (y << 1) | (x & 1);
  61. x >>= 1;
  62. }
  63. return y;
  64. }
  65. void rfSetAddr(const uint8_t b4[4], uint8_t prefix)
  66. {
  67. uint8_t b[4];
  68. for (int i = 0; i < 4; i++)
  69. b[i] = rfBitrev8(b4[i]);
  70. NRF_RADIO->BASE0 = ((uint32_t)b[0] << 24) | ((uint32_t)b[1] << 16) |
  71. ((uint32_t)b[2] << 8) | b[3];
  72. NRF_RADIO->PREFIX0 = g_prefixRaw ? prefix : rfBitrev8(prefix);
  73. NRF_RADIO->TXADDRESS = 0;
  74. NRF_RADIO->RXADDRESSES = 1u << 0;
  75. }
  76. // Listen on ONE base across ALL 8 ESB logical addresses (pipes): the real prefix on pipe 0, plus a spread of
  77. // neighbour prefixes on pipes 1..7 (BASE1 mirrors BASE0). Catches a controller whose reply stream rides the
  78. // same base on a DIFFERENT prefix -- a real possibility per the RE notes' "separate stream" model -- without
  79. // us having to know that prefix in advance. RXMATCH (reported per packet) then tells us which pipe it used.
  80. // Extra pipes only ADD matches; they never drop the pipe-0 (puck) frames. base/prefix are stored (pre-transform)
  81. // bytes; the on-air values get the same bitrev8 transform as rfSetAddr (g_prefixRaw=false).
  82. void rfSetAddrMulti(const uint8_t b4[4], uint8_t prefix)
  83. {
  84. uint8_t b[4];
  85. for (int i = 0; i < 4; i++)
  86. b[i] = rfBitrev8(b4[i]);
  87. uint32_t base = ((uint32_t)b[0] << 24) | ((uint32_t)b[1] << 16) |
  88. ((uint32_t)b[2] << 8) | b[3];
  89. NRF_RADIO->BASE0 = base;
  90. NRF_RADIO->BASE1 = base; // pipe 0 uses BASE0; pipes 1..7 use BASE1
  91. // 8 candidate prefixes: the learned one first, then a spread. De-dup isn't needed (a repeat just matches twice).
  92. uint8_t cand[8] = { prefix,
  93. (uint8_t)(prefix ^ 0x01),
  94. (uint8_t)(prefix + 1),
  95. (uint8_t)(prefix - 1),
  96. 0x10,
  97. (uint8_t)(prefix ^ 0x80),
  98. (uint8_t)(prefix + 0x10),
  99. (uint8_t)(prefix - 0x10) };
  100. uint8_t ap[8];
  101. for (int i = 0; i < 8; i++)
  102. ap[i] = g_prefixRaw ? cand[i] : rfBitrev8(cand[i]);
  103. NRF_RADIO->PREFIX0 = (uint32_t)ap[0] | ((uint32_t)ap[1] << 8) |
  104. ((uint32_t)ap[2] << 16) | ((uint32_t)ap[3] << 24);
  105. NRF_RADIO->PREFIX1 = (uint32_t)ap[4] | ((uint32_t)ap[5] << 8) |
  106. ((uint32_t)ap[6] << 16) | ((uint32_t)ap[7] << 24);
  107. NRF_RADIO->TXADDRESS = 0;
  108. NRF_RADIO->RXADDRESSES = 0xFF; // enable all 8 pipes
  109. }
  110. void rfConfig(uint8_t ch)
  111. {
  112. NRF_RADIO->TASKS_DISABLE = 1;
  113. RWAIT_DISABLED();
  114. NRF_RADIO->EVENTS_DISABLED = 0;
  115. NRF_RADIO->MODE = ((uint32_t)g_mode
  116. << RADIO_MODE_MODE_Pos); // Ble_2Mbit (tunable 'M')
  117. NRF_RADIO->FREQUENCY = ch;
  118. NRF_RADIO->TXPOWER =
  119. (RADIO_TXPOWER_TXPOWER_0dBm << RADIO_TXPOWER_TXPOWER_Pos);
  120. #if defined(RADIO_MODECNF0_RU_Fast)
  121. NRF_RADIO->MODECNF0 = (RADIO_MODECNF0_RU_Fast << RADIO_MODECNF0_RU_Pos);
  122. #endif
  123. // static length (tunable '0'); g_pcnf1=0 -> build from g_statlen/g_balen
  124. NRF_RADIO->PCNF0 = g_pcnf0;
  125. // ENDIAN=Big (puck transmits MSB-first)
  126. NRF_RADIO->PCNF1 =
  127. g_pcnf1 ? g_pcnf1 :
  128. ((1u << RADIO_PCNF1_ENDIAN_Pos) |
  129. ((uint32_t)g_balen << RADIO_PCNF1_BALEN_Pos) |
  130. ((uint32_t)g_statlen << RADIO_PCNF1_STATLEN_Pos) |
  131. ((uint32_t)g_statlen
  132. << RADIO_PCNF1_MAXLEN_Pos)); // WHITEEN=0
  133. NRF_RADIO->CRCCNF = g_crccnf; // CRC16, address included
  134. NRF_RADIO->CRCPOLY = g_crcpoly;
  135. NRF_RADIO->CRCINIT = g_crcinit;
  136. NRF_RADIO->DATAWHITEIV = g_whiteiv;
  137. NRF_RADIO->PACKETPTR = (uint32_t)rfrx;
  138. }