wii_i2c.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. //----------------------------------------------------------------------------- ----------------------------------------
  2. // Biblio: [standing on the shoulders of giants]
  3. // https://bootlin.com/labs/doc/nunchuk.pdf
  4. // https://www.hackster.io/infusion/using-a-wii-nunchuk-with-arduino-597254#toc-i2c-protocol-9
  5. // https://web.archive.org/web/20220000000000*/https://www.hackster.io/infusion/using-a-wii-nunchuk-with-arduino-597254
  6. // https://github.com/madhephaestus/WiiChuck/blob/master/src/Accessory.cpp#L14
  7. // https://wiibrew.org/wiki/Wiimote/Extension_Controllers
  8. // https://www.best-microcontroller-projects.com/i2c-tutorial.html
  9. //
  10. // WiiMote Extension Controller:
  11. // Bus Address : 0x52
  12. // Register autoincrements after each (byte is) read
  13. // 0x00..0x05 ( 6 bytes) ... [r] Controller Data
  14. // 0x20..0x2F (16 bytes) ... [r] Calibration Data
  15. // 0x30..0x3F (16 bytes) ... [r] (A copy of the) Calibration Data
  16. // 0x40..0x4F (16 bytes) ... [w] Encryption key(s)
  17. // 0xFA..0xFF ( 6 bytes) ... [r] Perhipheral ID
  18. //----------------------------------------------------------------------------- ----------------------------------------
  19. #include <stdlib.h>
  20. #include <stdint.h>
  21. #include <stdbool.h>
  22. #include <furi.h>
  23. #include <furi_hal.h>
  24. #include <furi_hal_i2c.h>
  25. #include "i2c_workaround.h" //! temporary workaround for a bug in furi i2c [see header]
  26. #include "wii_anal.h"
  27. #include "wii_i2c.h"
  28. #include "wii_ec.h"
  29. #include "bc_logging.h"
  30. //----------------------------------------------------------------------------- ----------------------------------------
  31. // Wii Extension Controller i2c Bus address
  32. static const uint8_t ec_i2cAddr = 0x52;
  33. // Initialise for UNencrypted comms
  34. static const uint8_t regInit1 = 0xF0;
  35. static const uint8_t regInit2 = 0xFB;
  36. static const uint8_t cmdInit1[] = {regInit1, 0x55};
  37. static const uint8_t cmdInit2[] = {regInit2, 0x00};
  38. // Initialise for ENcrypted comms
  39. static const uint8_t regInitEnc = 0x40;
  40. static const uint8_t cmdInitEnc[] = {regInitEnc, 0x00};
  41. // Crypto key (PSK), base register : {0x40..0x4F}[2][8]
  42. static const uint8_t regEnc = 0x40; // ENC_LEN
  43. // Controller State data, base register : {0x00..0x05}[6]
  44. static const uint8_t regJoy = 0x00; // JOY_LEN
  45. // Calibration data, base register : {0x20..0x2F}[16]
  46. static const uint8_t regCal = 0x20; // CAL_LEN
  47. // Controller ID, base register : {0xFA..0xFF}[6]
  48. static const uint8_t regPid = 0xFA; // PID_LEN
  49. //+============================================================================ ========================================
  50. // Hexdump a buffer to the logfile
  51. //
  52. #if LOG_LEVEL >= 4 // INFO
  53. static
  54. void dump (const uint8_t* buf, const unsigned int len, const char* id)
  55. {
  56. // snprintf() would be useful!
  57. char s[128] = {0};
  58. char* p = NULL;
  59. strcpy(s, id);
  60. p = s +strlen(s);
  61. *p++ = ':';
  62. *p++ = ' ';
  63. *p++ = '{';
  64. for (unsigned int i = 0; i < len; i++) {
  65. uint8_t hi = (buf[i] &0xF0) >>4;
  66. uint8_t lo = (buf[i] &0x0F);
  67. hi = hi + ((hi > 9) ? ('A' -10) : '0');
  68. lo = lo + ((lo > 9) ? ('A' -10) : '0');
  69. *p++ = (char)hi;
  70. *p++ = (char)lo;
  71. *p++ = ',';
  72. }
  73. *p = '\0';
  74. *--p = '}';
  75. INFO(s);
  76. }
  77. #else
  78. # define dump(...)
  79. #endif
  80. //+============================================================================ ========================================
  81. //
  82. //! -W-A-R-N-I-N-G- : THIS ENCRYPTION CODE SHOULD NEVER BE REQUIRED ... AS SUCH, I'VE NEVER TESTED IT
  83. //
  84. static
  85. void decrypt (uint8_t* buf, const uint8_t* encKey, const uint8_t reg, unsigned int len)
  86. {
  87. #if 1 // Use standard algorithm
  88. // decrypted_byte = (encrypted_byte XOR encKey[1][address%8]) + encKey[2][address%8]
  89. for (uint8_t* p = buf; p < buf+len; p++)
  90. *p = (*p ^ encKey[(reg +(p -buf)) %8]) + encKey[8 +((reg +(p -buf)) %8)];
  91. #else //! This is (I think) a shortcut for an all-zero key [not tested]
  92. (void)encKey;
  93. (void)reg;
  94. for (uint8_t* p = buf; p < buf+len; p++)
  95. *p = (*p ^ 0x17) + 0x17;
  96. #endif
  97. }
  98. //+============================================================================ ========================================
  99. // Read the Extension Controller state
  100. // ...and decode it in to something sane
  101. //
  102. // Returns: {0:OK, >0:Error}
  103. //
  104. int ecRead (wiiEC_t* pec)
  105. {
  106. ENTER;
  107. int rv = 0; // assume success
  108. if (!pec->init) {
  109. WARN("%s : device not initialised", __func__);
  110. rv = 1;
  111. goto bail;
  112. }
  113. if (!furi_hal_i2c_is_device_ready(i2cBus,i2cAddr, i2cTimeout)) {
  114. INFO("%s : device disconnected", __func__);
  115. pec->init = false;
  116. rv = 2;
  117. goto bail;
  118. }
  119. if (!furi_hal_i2c_trxd(i2cBus,i2cAddr, &regJoy,1, pec->joy,JOY_LEN, i2cTimeout,i2cReadWait)) {
  120. ERROR("%s : trxd fail", __func__);
  121. rv = 3;
  122. goto bail;
  123. }
  124. if (pec->encrypt) decrypt(pec->joy, pec->encKey, regJoy, JOY_LEN) ;
  125. // Decode the readings (according to Controller type)
  126. ecDecode(pec);
  127. bail:
  128. LEAVE;
  129. return rv;
  130. }
  131. //+============================================================================ ========================================
  132. // Initialise an Extension Controller
  133. //
  134. //! To disable encryption, pass a NULL encryption key <-- this is currently ALWAYS the case
  135. //
  136. bool ecInit (wiiEC_t* pec, const uint8_t* encKey)
  137. {
  138. ENTER;
  139. bool rv = false; // assume failure
  140. #if 0 //! i2c workaround
  141. //! I think this is done during OS startup - long before the plugin starts
  142. furi_hal_i2c_init();
  143. #endif
  144. #if 0 //! i2c workaround
  145. // May become relevant when the i2c issues are resolved
  146. // Take control of the i2c bus [which returns void !?]
  147. // --> firmware/targets/f7/furi_hal/furi_hal_i2c.c
  148. furi_hal_i2c_acquire(i2cBus);
  149. #endif
  150. pec->init = false; // assume failure
  151. // === See if the device is alive ===
  152. if (!furi_hal_i2c_is_device_ready(i2cBus,i2cAddr, i2cTimeout)) {
  153. TRACE("%s : waiting for device", __func__);
  154. goto bail;
  155. }
  156. INFO("%s : device connected", __func__);
  157. // === Initialise the device ===
  158. pec->init = false; // This goes true AFTER the (optional) controller-specific init code
  159. // === Start the Extension Controller ===
  160. if (encKey) { //! start in encrypted mode
  161. //! todo - should this happen here, or AFTER we've got the ID ?
  162. } else {
  163. if ( !furi_hal_i2c_tx(i2cBus,i2cAddr, cmdInit1,sizeof(cmdInit1), i2cTimeout) ) {
  164. ERROR("%s : init fail (dec1)", __func__);
  165. goto bail;
  166. }
  167. TRACE("%s : init OK1", __func__);
  168. if ( !furi_hal_i2c_tx(i2cBus,i2cAddr, cmdInit2,sizeof(cmdInit2), i2cTimeout) ) {
  169. ERROR("%s : init fail (dec2)", __func__);
  170. goto bail;
  171. }
  172. TRACE("%s : init OK2", __func__);
  173. }
  174. // === Retrieve the Extension Controller ID ===
  175. if (!furi_hal_i2c_trx(i2cBus,i2cAddr, &regPid,1, pec->pid,PID_LEN, i2cTimeout)) {
  176. ERROR("%s : T(R)x fail (pid)", __func__);
  177. goto bail;
  178. }
  179. if (pec->encrypt) decrypt(pec->joy, pec->encKey, regJoy, JOY_LEN);
  180. dump(pec->pid, PID_LEN, "pid"); // debug INFO
  181. // Find the StringID in the lookup table
  182. for (pec->pidx = PID_FIRST; pec->pidx < PID_ERROR; pec->pidx++)
  183. if (memcmp(pec->pid, ecId[pec->pidx].id, PID_LEN) == 0) break ;
  184. if (pec->pidx == PID_ERROR) pec->pidx = PID_UNKNOWN ;
  185. pec->sid = ecId[pec->pidx].name;
  186. INFO("sid: %s", pec->sid);
  187. // === (optionally) Enable encryption ===
  188. if (!encKey) {
  189. pec->encrypt = false;
  190. } else { // Controller WILL encrypt ALL tranmissions
  191. //! this encryption code fails - should it be done earlier?
  192. //! as it is probably never of any use, I'm kinda loathed to spend time on it
  193. //! https://github.com/madhephaestus/WiiChuck/blob/master/src/Accessory.cpp#L138
  194. uint8_t encTx[1+ENC_LEN] = {0};
  195. uint8_t* ep = encTx;
  196. pec->encrypt = true;
  197. // ** Start the Controller in ENcrytped mode
  198. if ( !furi_hal_i2c_tx(i2cBus,i2cAddr, cmdInitEnc,sizeof(cmdInitEnc), i2cTimeout) ) {
  199. ERROR("%s : init fail (enc)", __func__);
  200. goto bail;
  201. }
  202. // Copy the (symmetric) encryption key to the controller state table
  203. if (pec->encKey != encKey)
  204. memcpy(pec->encKey, encKey, ENC_LEN);
  205. // Build the encryption key packet
  206. *ep++ = regEnc;
  207. memcpy(ep, pec->encKey, ENC_LEN);
  208. // ** Send encryption key (PSK)
  209. if ( !furi_hal_i2c_tx(i2cBus,i2cAddr, encTx,(1+ENC_LEN), i2cTimeout) ) {
  210. ERROR("%s : key fail", __func__);
  211. goto bail;
  212. }
  213. TRACE("%s : init OK (enc)", __func__);
  214. }
  215. // === Some devices [eg. Drawsome/uDraw] require additional init code ===
  216. if ( ecId[pec->init].init && (ecId[pec->init].init(pec) == false) ) goto bail ;
  217. pec->init = true;
  218. // === Read calibration data ===
  219. if (!furi_hal_i2c_trx(i2cBus,i2cAddr, &regCal,1, pec->calF,CAL_LEN, i2cTimeout)) {
  220. ERROR("%s : trx fail (cal)", __func__);
  221. goto bail;
  222. }
  223. if (pec->encrypt) decrypt(pec->joy, pec->encKey, regJoy, JOY_LEN);
  224. dump(pec->calF, CAL_LEN, "cal");
  225. ecCalibrate(pec, CAL_RESET | CAL_FACTORY); // Load factory default calibration
  226. // === Initialise decode buffers ===
  227. pec->decN = 0; // read in to decode[1] (yes, N=0 -> read in to dec[1])
  228. switch (ecRead(pec)) {
  229. case 0: // read OK
  230. memcpy(&pec->dec[0], &pec->dec[1], sizeof(pec->dec[0]));
  231. dump(pec->joy, JOY_LEN, "joy");
  232. break;
  233. default: // bug: unknown
  234. case 1: // bug: not initialised - should never happen
  235. ERROR("%s : read bug", __func__);
  236. break;
  237. case 2: // device gone
  238. case 3: // read fail
  239. // Logging done by ecRead()
  240. pec->init = false;
  241. goto bail;
  242. }
  243. rv = true; // yay :)
  244. bail:
  245. #if 0 //! i2c workaround
  246. furi_hal_i2c_release(i2cBus);
  247. #endif
  248. LEAVE;
  249. return rv;
  250. }