wii_anal.c 18 KB

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