wii_anal.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. //----------------------------------------------------------------------------- ----------------------------------------
  2. // Includes
  3. //
  4. // System libs
  5. #include <stdlib.h> // malloc
  6. #include <stdint.h> // uint32_t
  7. #include <stdarg.h> // __VA_ARGS__
  8. #include <stdio.h>
  9. #include <ctype.h>
  10. // FlipperZero libs
  11. #include <furi.h> // Core API
  12. #include <gui/gui.h> // GUI (screen/keyboard) API
  13. #include <input/input.h> // GUI Input extensions
  14. #include <notification/notification_messages.h>
  15. // Do this first!
  16. #define ERR_C_ // Do this in precisely ONE file
  17. #include "err.h" // Error numbers & messages
  18. #include "bc_logging.h"
  19. // Local headers
  20. #include "wii_anal.h" // Various enums and struct declarations
  21. #include "wii_i2c.h" // Wii i2c functions
  22. #include "wii_ec.h" // Wii Extension Controller functions (eg. draw)
  23. #include "wii_anal_keys.h" // key mappings
  24. #include "gfx/images.h" // Images
  25. #include "wii_anal_lcd.h" // Drawing functions
  26. #include "wii_anal_ec.h" // Wii controller events
  27. #include "wii_anal_ver.h" // Version number
  28. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  29. // OOOOO // SSSSS CCCCC AAA L L BBBB AAA CCCC K K SSSSS
  30. // O O /// S C A A L L B B A A C K K S
  31. // O O /// SSSSS C AAAAA L L BBBB AAAAA C KKK SSSSS
  32. // O O /// S C A A L L B B A A C K K S
  33. // OOOOO // SSSSS CCCCC A A LLLLL LLLLL BBBB A A CCCC K K SSSSS
  34. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  35. //+============================================================================ ========================================
  36. // OS Callback : Timer tick
  37. // We register this function to be called when the OS signals a timer 'tick' event
  38. //
  39. static
  40. void cbTimer (FuriMessageQueue* queue)
  41. {
  42. ENTER;
  43. furi_assert(queue);
  44. eventMsg_t message = {.id = EVID_TICK};
  45. furi_message_queue_put(queue, &message, 0);
  46. LEAVE;
  47. return;
  48. }
  49. //+============================================================================ ========================================
  50. // OS Callback : Keypress
  51. // We register this function to be called when the OS detects a keypress
  52. //
  53. static
  54. void cbInput (InputEvent* event, FuriMessageQueue* queue)
  55. {
  56. ENTER;
  57. furi_assert(queue);
  58. furi_assert(event);
  59. // Put an "input" event message on the message queue
  60. eventMsg_t message = {.id = EVID_KEY, .input = *event};
  61. furi_message_queue_put(queue, &message, FuriWaitForever);
  62. LEAVE;
  63. return;
  64. }
  65. //+============================================================================
  66. // Show version number
  67. //
  68. static
  69. void showVer (Canvas* const canvas)
  70. {
  71. show(canvas, 0,59, &img_3x5_v, SHOW_SET_BLK);
  72. show(canvas, 4,59, VER_MAJ, SHOW_SET_BLK);
  73. canvas_draw_frame(canvas, 8,62, 2,2);
  74. show(canvas, 11,59, VER_MIN, SHOW_SET_BLK);
  75. canvas_draw_frame(canvas, 15,62, 2,2);
  76. show(canvas, 18,59, VER_SUB, SHOW_SET_BLK);
  77. }
  78. //+============================================================================
  79. // OS Callback : Draw request
  80. // We register this function to be called when the OS requests that the screen is redrawn
  81. //
  82. // We actually instruct the OS to perform this request, after we update the interface
  83. // I guess it's possible that this instruction may able be issued by other threads !?
  84. //
  85. static
  86. void cbDraw (Canvas* const canvas, void* ctx)
  87. {
  88. ENTER;
  89. furi_assert(canvas);
  90. furi_assert(ctx);
  91. state_t* state = ctx;
  92. // Try to acquire the mutex for the plugin state variables, timeout = 25mS
  93. if (furi_mutex_acquire(state->mutex, 25) != FuriStatusOk) return ;
  94. switch (state->scene) {
  95. //---------------------------------------------------------------------
  96. case SCENE_SPLASH:
  97. show(canvas, 2,0, &img_csLogo_FULL, SHOW_SET_BLK);
  98. canvas_set_font(canvas, FontSecondary);
  99. canvas_draw_str_aligned(canvas, 64,43, AlignCenter, AlignTop, "Wii Extension Controller");
  100. canvas_draw_str_aligned(canvas, 64,55, AlignCenter, AlignTop, "Protocol Analyser");
  101. showVer(canvas);
  102. break;
  103. //---------------------------------------------------------------------
  104. case SCENE_RIP:
  105. show(canvas, 0,0, &img_RIP, SHOW_SET_BLK);
  106. break;
  107. //---------------------------------------------------------------------
  108. case SCENE_WAIT:
  109. # define xo 2
  110. show(canvas, 3+xo,10, &img_ecp_port, SHOW_SET_BLK);
  111. BOX_TL(22+xo, 6, 82+xo,23); // 3v3
  112. BOX_TL(48+xo,21, 82+xo,23); // C1
  113. BOX_BL(22+xo,41, 82+xo,58); // C0
  114. BOX_BL(48+xo,41, 82+xo,44); // Gnd
  115. show(canvas, 90+xo, 3, &img_6x8_3, SHOW_SET_BLK); // 3v3
  116. show(canvas, 97+xo, 3, &img_6x8_v, SHOW_SET_BLK);
  117. show(canvas, 104+xo, 3, &img_6x8_3, SHOW_SET_BLK);
  118. show(canvas, 90+xo,18, &img_6x8_C, SHOW_SET_BLK); // C1 <->
  119. show(canvas, 98+xo,18, &img_6x8_1, SHOW_SET_BLK);
  120. show(canvas, 107+xo,16, &img_ecp_SDA, SHOW_SET_BLK);
  121. show(canvas, 90+xo,40, &img_6x8_G, SHOW_SET_BLK); // Gnd
  122. show(canvas, 97+xo,40, &img_6x8_n, SHOW_SET_BLK);
  123. show(canvas, 104+xo,40, &img_6x8_d, SHOW_SET_BLK);
  124. show(canvas, 90+xo,54, &img_6x8_C, SHOW_SET_BLK); // C0 _-_-
  125. show(canvas, 98+xo,54, &img_6x8_0, SHOW_SET_BLK);
  126. show(canvas, 108+xo,54, &img_ecp_SCL, SHOW_SET_BLK);
  127. show(canvas, 0,0, &img_csLogo_Small, SHOW_SET_BLK);
  128. showVer(canvas);
  129. # undef xo
  130. break;
  131. //---------------------------------------------------------------------
  132. case SCENE_DEBUG:
  133. canvas_set_font(canvas, FontSecondary);
  134. show(canvas, 0,0, &img_key_U, SHOW_SET_BLK);
  135. canvas_draw_str_aligned(canvas, 11, 0, AlignLeft, AlignTop, "Initialise Perhipheral");
  136. show(canvas, 0,11, &img_key_OK, SHOW_SET_BLK);
  137. canvas_draw_str_aligned(canvas, 11,11, AlignLeft, AlignTop, "Read values [see log]");
  138. show(canvas, 0,23, &img_key_D, SHOW_SET_BLK);
  139. canvas_draw_str_aligned(canvas, 11,22, AlignLeft, AlignTop, "Restart Scanner");
  140. show(canvas, 0,33, &img_key_Back, SHOW_SET_BLK);
  141. canvas_draw_str_aligned(canvas, 11,33, AlignLeft, AlignTop, "Exit");
  142. break ;
  143. //---------------------------------------------------------------------
  144. default:
  145. if (state->ec.pidx >= PID_ERROR) {
  146. ERROR("%s : bad PID = %d", __func__, state->ec.pidx);
  147. } else {
  148. if ((state->scene == SCENE_DUMP) || !ecId[state->ec.pidx].show)
  149. ecId[PID_UNKNOWN].show(canvas, state);
  150. else
  151. ecId[state->ec.pidx].show(canvas, state);
  152. }
  153. break;
  154. }
  155. // Release the mutex
  156. furi_mutex_release(state->mutex);
  157. LEAVE;
  158. return;
  159. }
  160. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  161. // SSSSS TTTTT AAA TTTTT EEEEE V V AAA RRRR IIIII AAA BBBB L EEEEE SSSSS
  162. // S T A A T E V V A A R R I A A B B L E S
  163. // SSSSS T AAAAA T EEE V V AAAAA RRRR I AAAAA BBBB L EEE SSSSS
  164. // S T A A T E V V A A R R I A A B B L E S
  165. // SSSSS T A A T EEEEE V A A R R IIIII A A BBBB LLLLL EEEEE SSSSS
  166. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  167. //+============================================================================ ========================================
  168. // Initialise plugin state variables
  169. //
  170. static inline
  171. bool stateInit (state_t* const state)
  172. {
  173. ENTER;
  174. furi_assert(state);
  175. bool rv = true; // assume success
  176. // Enable the main loop
  177. state->run = true;
  178. // Timer
  179. state->timerEn = false;
  180. state->timer = NULL;
  181. state->timerHz = furi_kernel_get_tick_frequency();
  182. state->fps = 30;
  183. // Scene
  184. state->scene = SCENE_SPLASH;
  185. state->scenePrev = SCENE_NONE;
  186. state->scenePegg = SCENE_NONE;
  187. state->hold = 0; // show hold meters (-1=lowest, 0=current, +1=highest}
  188. state->calib = CAL_TRACK;
  189. state->pause = false; // animation running
  190. state->apause = false; // auto-pause animation
  191. // Notifications
  192. state->notify = NULL;
  193. // Perhipheral
  194. state->ec.init = false;
  195. state->ec.pidx = PID_UNKNOWN;
  196. state->ec.sid = ecId[state->ec.pidx].name;
  197. // Controller data
  198. memset(state->ec.pid, 0xC5, PID_LEN); // Cyborg 5ystems
  199. memset(state->ec.calF, 0xC5, CAL_LEN);
  200. memset(state->ec.joy, 0xC5, JOY_LEN);
  201. // Encryption details
  202. state->ec.encrypt = false;
  203. memset(state->ec.encKey, 0x00, ENC_LEN);
  204. // Seed the PRNG
  205. // CYCCNT --> lib/STM32CubeWB/Drivers/CMSIS/Include/core_cm7.h
  206. // srand(DWT->CYCCNT);
  207. LEAVE;
  208. return rv;
  209. }
  210. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  211. // MM MM AAA IIIII N N
  212. // M M M A A I NN N
  213. // M M M AAAAA I N N N
  214. // M M A A I N NN
  215. // M M A A IIIII N N
  216. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  217. //+============================================================================ ========================================
  218. // Enable/Disable scanning
  219. //
  220. void timerEn (state_t* state, bool on)
  221. {
  222. ENTER;
  223. furi_assert(state);
  224. // ENable scanning
  225. if (on) {
  226. if (state->timerEn) {
  227. WARN(wii_errs[WARN_SCAN_START]);
  228. } else {
  229. // Set the timer to fire at 'fps' times/second
  230. if (furi_timer_start(state->timer, state->timerHz/state->fps) == FuriStatusOk) {
  231. state->timerEn = true;
  232. INFO("%s : monitor started", __func__);
  233. } else {
  234. ERROR(wii_errs[ERR_TIMER_START]);
  235. }
  236. }
  237. // DISable scanning
  238. } else {
  239. if (!state->timerEn) {
  240. WARN(wii_errs[WARN_SCAN_STOP]);
  241. } else {
  242. // Stop the timer
  243. if (furi_timer_stop(state->timer) == FuriStatusOk) {
  244. state->timerEn = false;
  245. INFO("%s : monitor stopped", __func__);
  246. } else {
  247. ERROR(wii_errs[ERR_TIMER_STOP]);
  248. }
  249. }
  250. }
  251. LEAVE;
  252. return;
  253. }
  254. //+============================================================================ ========================================
  255. // Plugin entry point
  256. //
  257. int32_t wii_ec_anal (void)
  258. {
  259. ENTER;
  260. // ===== Variables =====
  261. err_t error = 0; // assume success
  262. Gui* gui = NULL;
  263. ViewPort* vpp = NULL;
  264. state_t* state = NULL;
  265. FuriMessageQueue* queue = NULL;
  266. const uint32_t queueSz = 20; // maximum messages in queue
  267. uint32_t tmo = (3.5f *1000); // timeout splash screen after N seconds
  268. // The queue will contain plugin event-messages
  269. // --> local
  270. eventMsg_t msg = {0};
  271. INFO("BEGIN");
  272. // ===== Message queue =====
  273. // 1. Create a message queue (for up to 8 (keyboard) event messages)
  274. if ( !(queue = furi_message_queue_alloc(queueSz, sizeof(msg))) ) {
  275. ERROR(wii_errs[(error = ERR_MALLOC_QUEUE)]);
  276. goto bail;
  277. }
  278. // ===== Create GUI Interface =====
  279. // 2. Create a GUI interface
  280. if ( !(gui = furi_record_open("gui")) ) {
  281. ERROR(wii_errs[(error = ERR_NO_GUI)]);
  282. goto bail;
  283. }
  284. // ===== Plugin state variables =====
  285. // 3. Allocate space on the heap for the plugin state variables
  286. if ( !(state = malloc(sizeof(state_t))) ) {
  287. ERROR(wii_errs[(error = ERR_MALLOC_STATE)]);
  288. goto bail;
  289. }
  290. // 4. Initialise the plugin state variables
  291. if (!stateInit(state)) {
  292. // error message(s) is/are output by stateInit()
  293. error = 15;
  294. goto bail;
  295. }
  296. // 5. Create a mutex for (reading/writing) the plugin state variables
  297. if ( !(state->mutex = furi_mutex_alloc(FuriMutexTypeNormal)) ) {
  298. ERROR(wii_errs[(error = ERR_NO_MUTEX)]);
  299. goto bail;
  300. }
  301. // ===== Viewport =====
  302. // 6. Allocate space on the heap for the viewport
  303. if ( !(vpp = view_port_alloc()) ) {
  304. ERROR(wii_errs[(error = ERR_MALLOC_VIEW)]);
  305. goto bail;
  306. }
  307. // 7a. Register a callback for input events
  308. view_port_input_callback_set(vpp, cbInput, queue);
  309. // 7b. Register a callback for draw events
  310. view_port_draw_callback_set(vpp, cbDraw, state);
  311. // ===== Start GUI Interface =====
  312. // 8. Attach the viewport to the GUI
  313. gui_add_view_port(gui, vpp, GuiLayerFullscreen);
  314. // ===== Timer =====
  315. // 9. Allocate a timer
  316. if ( !(state->timer = furi_timer_alloc(cbTimer, FuriTimerTypePeriodic, queue)) ) {
  317. ERROR(wii_errs[(error = ERR_NO_TIMER)]);
  318. goto bail;
  319. }
  320. // === System Notifications ===
  321. // 10. Acquire a handle for the system notification queue
  322. if ( !(state->notify = furi_record_open(RECORD_NOTIFICATION)) ) {
  323. ERROR(wii_errs[(error = ERR_NO_NOTIFY)]);
  324. goto bail;
  325. }
  326. patBacklight(state); // Turn on the backlight [qv. remote FAP launch]
  327. INFO("INITIALISED");
  328. // ==================== Main event loop ====================
  329. if (state->run) do {
  330. // bool redraw = false;
  331. FuriStatus status = FuriStatusErrorTimeout;
  332. // Wait for a message
  333. // while ((status = furi_message_queue_get(queue, &msg, tmo)) == FuriStatusErrorTimeout) ;
  334. status = furi_message_queue_get(queue, &msg, tmo);
  335. // Clear splash screen
  336. if ( (state->scene == SCENE_SPLASH) && (state->scenePrev == SCENE_NONE) && // Initial splash
  337. ( (status == FuriStatusErrorTimeout) || // timeout
  338. ((msg.id == EVID_KEY) && (msg.input.type == InputTypeShort)) ) // or <any> key-short
  339. ) {
  340. tmo = 60 *1000; // increase message-wait timeout to 60secs
  341. timerEn(state, true); // start scanning the i2c bus
  342. status = FuriStatusOk; // pass status check
  343. msg.id = EVID_NONE; // valid msg ID
  344. sceneSet(state, SCENE_WAIT); // move to wait screen
  345. }
  346. // Check for queue errors
  347. if (status != FuriStatusOk) {
  348. switch (status) {
  349. case FuriStatusErrorTimeout: DEBUG(wii_errs[DEBUG_QUEUE_TIMEOUT]); continue ;
  350. case FuriStatusError: ERROR(wii_errs[(error = ERR_QUEUE_RTOS)]); goto bail ;
  351. case FuriStatusErrorResource: ERROR(wii_errs[(error = ERR_QUEUE_RESOURCE)]); goto bail ;
  352. case FuriStatusErrorParameter: ERROR(wii_errs[(error = ERR_QUEUE_BADPRM)]); goto bail ;
  353. case FuriStatusErrorNoMemory: ERROR(wii_errs[(error = ERR_QUEUE_NOMEM)]); goto bail ;
  354. case FuriStatusErrorISR: ERROR(wii_errs[(error = ERR_QUEUE_ISR)]); goto bail ;
  355. default: ERROR(wii_errs[(error = ERR_QUEUE_UNK)]); goto bail ;
  356. }
  357. }
  358. // Read successful
  359. // *** Try to lock the plugin state variables ***
  360. if (furi_mutex_acquire(state->mutex, FuriWaitForever) != FuriStatusOk) {
  361. ERROR(wii_errs[(error = ERR_MUTEX_BLOCK)]);
  362. goto bail;
  363. }
  364. // *** Handle events ***
  365. switch (msg.id) {
  366. //---------------------------------------------
  367. case EVID_TICK: // Timer events
  368. //! I would prefer to have ecPoll() called by cbTimer()
  369. //! ...but how does cbTimer() get the required access to the state variables? Namely: 'state->ec'
  370. //! So, for now, the timer pushes a message to call ecPoll()
  371. //! which, in turn, will push WIIEC event meesages! <facepalm>
  372. ecPoll(&state->ec, queue);
  373. break;
  374. //---------------------------------------------
  375. case EVID_WIIEC: // WiiMote Perhipheral
  376. evWiiEC(&msg, state);
  377. break;
  378. //---------------------------------------------
  379. case EVID_KEY: // Key events
  380. patBacklight(state);
  381. evKey(&msg, state);
  382. break;
  383. //---------------------------------------------
  384. case EVID_NONE:
  385. break;
  386. //---------------------------------------------
  387. default: // Unknown event
  388. WARN("Unknown message.ID [%d]", msg.id);
  389. break;
  390. }
  391. // *** Update the GUI screen via the viewport ***
  392. view_port_update(vpp) ;
  393. // *** Try to release the plugin state variables ***
  394. if (furi_mutex_release(state->mutex) != FuriStatusOk) {
  395. ERROR(wii_errs[(error = ERR_MUTEX_RELEASE)]);
  396. goto bail;
  397. }
  398. } while (state->run);
  399. // ===== Game Over =====
  400. INFO("USER EXIT");
  401. bail:
  402. // 10. Release system notification queue
  403. if (state && state->notify) {
  404. furi_record_close(RECORD_NOTIFICATION);
  405. state->notify = NULL;
  406. }
  407. // 9. Stop the timer
  408. if (state && state->timer) {
  409. (void)furi_timer_stop(state->timer);
  410. furi_timer_free(state->timer);
  411. state->timer = NULL;
  412. state->timerEn = false;
  413. }
  414. // 8. Detach the viewport
  415. gui_remove_view_port(gui, vpp);
  416. // 7. No need to unreqgister the callbacks
  417. // ...they will go when the viewport is destroyed
  418. // 6. Destroy the viewport
  419. if (vpp) {
  420. view_port_enabled_set(vpp, false);
  421. view_port_free(vpp);
  422. vpp = NULL;
  423. }
  424. // 5. Free the mutex
  425. if (state && state->mutex) {
  426. furi_mutex_free(state->mutex);
  427. state->mutex = NULL;
  428. }
  429. // 4. Free up state pointer(s)
  430. // none
  431. // 3. Free the plugin state variables
  432. if (state) {
  433. free(state);
  434. state = NULL;
  435. }
  436. // 2. Close the GUI
  437. furi_record_close("gui");
  438. // 1. Destroy the message queue
  439. if (queue) {
  440. furi_message_queue_free(queue);
  441. queue = NULL;
  442. }
  443. INFO("CLEAN EXIT ... Exit code: %d", error);
  444. LEAVE;
  445. return (int32_t)error;
  446. }