| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546 |
- //----------------------------------------------------------------------------- ----------------------------------------
- // Includes
- //
- // System libs
- #include <stdlib.h> // malloc
- #include <stdint.h> // uint32_t
- #include <stdarg.h> // __VA_ARGS__
- #include <stdio.h>
- #include <ctype.h>
- // FlipperZero libs
- #include <furi.h> // Core API
- #include <gui/gui.h> // GUI (screen/keyboard) API
- #include <input/input.h> // GUI Input extensions
- #include <notification/notification_messages.h>
- // Do this first!
- #define ERR_C_ // Do this in precisely ONE file
- #include "err.h" // Error numbers & messages
- #include "bc_logging.h"
- // Local headers
- #include "wii_anal.h" // Various enums and struct declarations
- #include "wii_i2c.h" // Wii i2c functions
- #include "wii_ec.h" // Wii Extension Controller functions (eg. draw)
- #include "wii_anal_keys.h" // key mappings
- #include "gfx/images.h" // Images
- #include "wii_anal_lcd.h" // Drawing functions
- #include "wii_anal_ec.h" // Wii controller events
- #include "wii_anal_ver.h" // Version number
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // OOOOO // SSSSS CCCCC AAA L L BBBB AAA CCCC K K SSSSS
- // O O /// S C A A L L B B A A C K K S
- // O O /// SSSSS C AAAAA L L BBBB AAAAA C KKK SSSSS
- // O O /// S C A A L L B B A A C K K S
- // OOOOO // SSSSS CCCCC A A LLLLL LLLLL BBBB A A CCCC K K SSSSS
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- //+============================================================================ ========================================
- // OS Callback : Timer tick
- // We register this function to be called when the OS signals a timer 'tick' event
- //
- static void cbTimer(void* ctx) {
- ENTER;
- FuriMessageQueue* queue = ctx;
- furi_assert(queue);
- eventMsg_t message = {.id = EVID_TICK};
- furi_message_queue_put(queue, &message, 0);
- LEAVE;
- return;
- }
- //+============================================================================ ========================================
- // OS Callback : Keypress
- // We register this function to be called when the OS detects a keypress
- //
- static void cbInput(InputEvent* event, void* ctx) {
- ENTER;
- FuriMessageQueue* queue = ctx;
- furi_assert(queue);
- furi_assert(event);
- // Put an "input" event message on the message queue
- eventMsg_t message = {.id = EVID_KEY, .input = *event};
- furi_message_queue_put(queue, &message, FuriWaitForever);
- LEAVE;
- return;
- }
- //+============================================================================
- // Show version number
- //
- static void showVer(Canvas* const canvas) {
- show(canvas, 0, 59, &img_3x5_v, SHOW_SET_BLK);
- show(canvas, 4, 59, VER_MAJ, SHOW_SET_BLK);
- canvas_draw_frame(canvas, 8, 62, 2, 2);
- show(canvas, 11, 59, VER_MIN, SHOW_SET_BLK);
- canvas_draw_frame(canvas, 15, 62, 2, 2);
- show(canvas, 18, 59, VER_SUB, SHOW_SET_BLK);
- }
- //+============================================================================
- // OS Callback : Draw request
- // We register this function to be called when the OS requests that the screen is redrawn
- //
- // We actually instruct the OS to perform this request, after we update the interface
- // I guess it's possible that this instruction may able be issued by other threads !?
- //
- static void cbDraw(Canvas* const canvas, void* ctx) {
- ENTER;
- furi_assert(canvas);
- furi_assert(ctx);
- state_t* state = ctx;
- // Try to acquire the mutex for the plugin state variables, timeout = 25mS
- if(furi_mutex_acquire(state->mutex, 25) != FuriStatusOk) return;
- switch(state->scene) {
- //---------------------------------------------------------------------
- case SCENE_SPLASH:
- show(canvas, 2, 0, &img_csLogo_FULL, SHOW_SET_BLK);
- canvas_set_font(canvas, FontSecondary);
- canvas_draw_str_aligned(canvas, 64, 43, AlignCenter, AlignTop, "Wii Extension Controller");
- canvas_draw_str_aligned(canvas, 64, 55, AlignCenter, AlignTop, "Protocol Analyser");
- showVer(canvas);
- break;
- //---------------------------------------------------------------------
- case SCENE_RIP:
- show(canvas, 0, 0, &img_RIP, SHOW_SET_BLK);
- break;
- //---------------------------------------------------------------------
- case SCENE_WAIT:
- #define xo 2
- show(canvas, 3 + xo, 10, &img_ecp_port, SHOW_SET_BLK);
- BOX_TL(22 + xo, 6, 82 + xo, 23); // 3v3
- BOX_TL(48 + xo, 21, 82 + xo, 23); // C1
- BOX_BL(22 + xo, 41, 82 + xo, 58); // C0
- BOX_BL(48 + xo, 41, 82 + xo, 44); // Gnd
- show(canvas, 90 + xo, 3, &img_6x8_3, SHOW_SET_BLK); // 3v3
- show(canvas, 97 + xo, 3, &img_6x8_v, SHOW_SET_BLK);
- show(canvas, 104 + xo, 3, &img_6x8_3, SHOW_SET_BLK);
- show(canvas, 90 + xo, 18, &img_6x8_C, SHOW_SET_BLK); // C1 <->
- show(canvas, 98 + xo, 18, &img_6x8_1, SHOW_SET_BLK);
- show(canvas, 107 + xo, 16, &img_ecp_SDA, SHOW_SET_BLK);
- show(canvas, 90 + xo, 40, &img_6x8_G, SHOW_SET_BLK); // Gnd
- show(canvas, 97 + xo, 40, &img_6x8_n, SHOW_SET_BLK);
- show(canvas, 104 + xo, 40, &img_6x8_d, SHOW_SET_BLK);
- show(canvas, 90 + xo, 54, &img_6x8_C, SHOW_SET_BLK); // C0 _-_-
- show(canvas, 98 + xo, 54, &img_6x8_0, SHOW_SET_BLK);
- show(canvas, 108 + xo, 54, &img_ecp_SCL, SHOW_SET_BLK);
- show(canvas, 0, 0, &img_csLogo_Small, SHOW_SET_BLK);
- showVer(canvas);
- #undef xo
- break;
- //---------------------------------------------------------------------
- case SCENE_DEBUG:
- canvas_set_font(canvas, FontSecondary);
- show(canvas, 0, 0, &img_key_U, SHOW_SET_BLK);
- canvas_draw_str_aligned(canvas, 11, 0, AlignLeft, AlignTop, "Initialise Perhipheral");
- show(canvas, 0, 11, &img_key_OK, SHOW_SET_BLK);
- canvas_draw_str_aligned(canvas, 11, 11, AlignLeft, AlignTop, "Read values [see log]");
- show(canvas, 0, 23, &img_key_D, SHOW_SET_BLK);
- canvas_draw_str_aligned(canvas, 11, 22, AlignLeft, AlignTop, "Restart Scanner");
- show(canvas, 0, 33, &img_key_Back, SHOW_SET_BLK);
- canvas_draw_str_aligned(canvas, 11, 33, AlignLeft, AlignTop, "Exit");
- break;
- //---------------------------------------------------------------------
- default:
- if(state->ec.pidx >= PID_ERROR) {
- ERROR("%s : bad PID = %d", __func__, state->ec.pidx);
- } else {
- if((state->scene == SCENE_DUMP) || !ecId[state->ec.pidx].show)
- ecId[PID_UNKNOWN].show(canvas, state);
- else
- ecId[state->ec.pidx].show(canvas, state);
- }
- break;
- }
- // Release the mutex
- furi_mutex_release(state->mutex);
- LEAVE;
- return;
- }
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // SSSSS TTTTT AAA TTTTT EEEEE V V AAA RRRR IIIII AAA BBBB L EEEEE SSSSS
- // S T A A T E V V A A R R I A A B B L E S
- // SSSSS T AAAAA T EEE V V AAAAA RRRR I AAAAA BBBB L EEE SSSSS
- // S T A A T E V V A A R R I A A B B L E S
- // SSSSS T A A T EEEEE V A A R R IIIII A A BBBB LLLLL EEEEE SSSSS
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- //+============================================================================ ========================================
- // Initialise plugin state variables
- //
- static inline bool stateInit(state_t* const state) {
- ENTER;
- furi_assert(state);
- bool rv = true; // assume success
- // Enable the main loop
- state->run = true;
- // Timer
- state->timerEn = false;
- state->timer = NULL;
- state->timerHz = furi_kernel_get_tick_frequency();
- state->fps = 30;
- // Scene
- state->scene = SCENE_SPLASH;
- state->scenePrev = SCENE_NONE;
- state->scenePegg = SCENE_NONE;
- state->hold = 0; // show hold meters (-1=lowest, 0=current, +1=highest}
- state->calib = CAL_TRACK;
- state->pause = false; // animation running
- state->apause = false; // auto-pause animation
- // Notifications
- state->notify = NULL;
- // Perhipheral
- state->ec.init = false;
- state->ec.pidx = PID_UNKNOWN;
- state->ec.sid = ecId[state->ec.pidx].name;
- // Controller data
- memset(state->ec.pid, 0xC5, PID_LEN); // Cyborg 5ystems
- memset(state->ec.calF, 0xC5, CAL_LEN);
- memset(state->ec.joy, 0xC5, JOY_LEN);
- // Encryption details
- state->ec.encrypt = false;
- memset(state->ec.encKey, 0x00, ENC_LEN);
- // Seed the PRNG
- // CYCCNT --> lib/STM32CubeWB/Drivers/CMSIS/Include/core_cm7.h
- // srand(DWT->CYCCNT);
- LEAVE;
- return rv;
- }
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // MM MM AAA IIIII N N
- // M M M A A I NN N
- // M M M AAAAA I N N N
- // M M A A I N NN
- // M M A A IIIII N N
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- //+============================================================================ ========================================
- // Enable/Disable scanning
- //
- void timerEn(state_t* state, bool on) {
- ENTER;
- furi_assert(state);
- // ENable scanning
- if(on) {
- if(state->timerEn) {
- WARN(wii_errs[WARN_SCAN_START]);
- } else {
- // Set the timer to fire at 'fps' times/second
- if(furi_timer_start(state->timer, state->timerHz / state->fps) == FuriStatusOk) {
- state->timerEn = true;
- INFO("%s : monitor started", __func__);
- } else {
- ERROR(wii_errs[ERR_TIMER_START]);
- }
- }
- // DISable scanning
- } else {
- if(!state->timerEn) {
- WARN(wii_errs[WARN_SCAN_STOP]);
- } else {
- // Stop the timer
- if(furi_timer_stop(state->timer) == FuriStatusOk) {
- state->timerEn = false;
- INFO("%s : monitor stopped", __func__);
- } else {
- ERROR(wii_errs[ERR_TIMER_STOP]);
- }
- }
- }
- LEAVE;
- return;
- }
- //+============================================================================ ========================================
- // Plugin entry point
- //
- int32_t wii_ec_anal(void) {
- ENTER;
- // ===== Variables =====
- err_t error = 0; // assume success
- Gui* gui = NULL;
- ViewPort* vpp = NULL;
- state_t* state = NULL;
- FuriMessageQueue* queue = NULL;
- const uint32_t queueSz = 20; // maximum messages in queue
- uint32_t tmo = (3.5f * 1000); // timeout splash screen after N seconds
- // The queue will contain plugin event-messages
- // --> local
- eventMsg_t msg = {0};
- INFO("BEGIN");
- // ===== Message queue =====
- // 1. Create a message queue (for up to 8 (keyboard) event messages)
- if(!(queue = furi_message_queue_alloc(queueSz, sizeof(msg)))) {
- ERROR(wii_errs[(error = ERR_MALLOC_QUEUE)]);
- goto bail;
- }
- // ===== Create GUI Interface =====
- // 2. Create a GUI interface
- if(!(gui = furi_record_open("gui"))) {
- ERROR(wii_errs[(error = ERR_NO_GUI)]);
- goto bail;
- }
- // ===== Plugin state variables =====
- // 3. Allocate space on the heap for the plugin state variables
- if(!(state = malloc(sizeof(state_t)))) {
- ERROR(wii_errs[(error = ERR_MALLOC_STATE)]);
- goto bail;
- }
- // 4. Initialise the plugin state variables
- if(!stateInit(state)) {
- // error message(s) is/are output by stateInit()
- error = 15;
- goto bail;
- }
- // 5. Create a mutex for (reading/writing) the plugin state variables
- if(!(state->mutex = furi_mutex_alloc(FuriMutexTypeNormal))) {
- ERROR(wii_errs[(error = ERR_NO_MUTEX)]);
- goto bail;
- }
- // ===== Viewport =====
- // 6. Allocate space on the heap for the viewport
- if(!(vpp = view_port_alloc())) {
- ERROR(wii_errs[(error = ERR_MALLOC_VIEW)]);
- goto bail;
- }
- // 7a. Register a callback for input events
- view_port_input_callback_set(vpp, cbInput, queue);
- // 7b. Register a callback for draw events
- view_port_draw_callback_set(vpp, cbDraw, state);
- // ===== Start GUI Interface =====
- // 8. Attach the viewport to the GUI
- gui_add_view_port(gui, vpp, GuiLayerFullscreen);
- // ===== Timer =====
- // 9. Allocate a timer
- if(!(state->timer = furi_timer_alloc(cbTimer, FuriTimerTypePeriodic, queue))) {
- ERROR(wii_errs[(error = ERR_NO_TIMER)]);
- goto bail;
- }
- // === System Notifications ===
- // 10. Acquire a handle for the system notification queue
- if(!(state->notify = furi_record_open(RECORD_NOTIFICATION))) {
- ERROR(wii_errs[(error = ERR_NO_NOTIFY)]);
- goto bail;
- }
- patBacklight(state); // Turn on the backlight [qv. remote FAP launch]
- INFO("INITIALISED");
- // ==================== Main event loop ====================
- if(state->run) do {
- //bool redraw = false;
- FuriStatus status = FuriStatusErrorTimeout;
- // Wait for a message
- // while ((status = furi_message_queue_get(queue, &msg, tmo)) == FuriStatusErrorTimeout) ;
- status = furi_message_queue_get(queue, &msg, tmo);
- // Clear splash screen
- if((state->scene == SCENE_SPLASH) &&
- (state->scenePrev == SCENE_NONE) && // Initial splash
- ((status == FuriStatusErrorTimeout) || // timeout
- ((msg.id == EVID_KEY) && (msg.input.type == InputTypeShort))) // or <any> key-short
- ) {
- tmo = 60 * 1000; // increase message-wait timeout to 60secs
- timerEn(state, true); // start scanning the i2c bus
- status = FuriStatusOk; // pass status check
- msg.id = EVID_NONE; // valid msg ID
- sceneSet(state, SCENE_WAIT); // move to wait screen
- }
- // Check for queue errors
- if(status != FuriStatusOk) {
- switch(status) {
- case FuriStatusErrorTimeout:
- DEBUG(wii_errs[DEBUG_QUEUE_TIMEOUT]);
- continue;
- case FuriStatusError:
- ERROR(wii_errs[(error = ERR_QUEUE_RTOS)]);
- goto bail;
- case FuriStatusErrorResource:
- ERROR(wii_errs[(error = ERR_QUEUE_RESOURCE)]);
- goto bail;
- case FuriStatusErrorParameter:
- ERROR(wii_errs[(error = ERR_QUEUE_BADPRM)]);
- goto bail;
- case FuriStatusErrorNoMemory:
- ERROR(wii_errs[(error = ERR_QUEUE_NOMEM)]);
- goto bail;
- case FuriStatusErrorISR:
- ERROR(wii_errs[(error = ERR_QUEUE_ISR)]);
- goto bail;
- default:
- ERROR(wii_errs[(error = ERR_QUEUE_UNK)]);
- goto bail;
- }
- }
- // Read successful
- // *** Try to lock the plugin state variables ***
- if(furi_mutex_acquire(state->mutex, FuriWaitForever) != FuriStatusOk) {
- ERROR(wii_errs[(error = ERR_MUTEX_BLOCK)]);
- goto bail;
- }
- // *** Handle events ***
- switch(msg.id) {
- //---------------------------------------------
- case EVID_TICK: // Timer events
- //! I would prefer to have ecPoll() called by cbTimer()
- //! ...but how does cbTimer() get the required access to the state variables? Namely: 'state->ec'
- //! So, for now, the timer pushes a message to call ecPoll()
- //! which, in turn, will push WIIEC event meesages! <facepalm>
- ecPoll(&state->ec, queue);
- break;
- //---------------------------------------------
- case EVID_WIIEC: // WiiMote Perhipheral
- evWiiEC(&msg, state);
- break;
- //---------------------------------------------
- case EVID_KEY: // Key events
- patBacklight(state);
- evKey(&msg, state);
- break;
- //---------------------------------------------
- case EVID_NONE:
- break;
- //---------------------------------------------
- default: // Unknown event
- WARN("Unknown message.ID [%d]", msg.id);
- break;
- }
- // *** Try to release the plugin state variables ***
- if(furi_mutex_release(state->mutex) != FuriStatusOk) {
- ERROR(wii_errs[(error = ERR_MUTEX_RELEASE)]);
- goto bail;
- }
- // *** Update the GUI screen via the viewport ***
- view_port_update(vpp);
- } while(state->run);
- // ===== Game Over =====
- INFO("USER EXIT");
- bail:
- // 10. Release system notification queue
- if(state && state->notify) {
- furi_record_close(RECORD_NOTIFICATION);
- state->notify = NULL;
- }
- // 9. Stop the timer
- if(state && state->timer) {
- (void)furi_timer_stop(state->timer);
- furi_timer_free(state->timer);
- state->timer = NULL;
- state->timerEn = false;
- }
- // 8. Detach the viewport
- gui_remove_view_port(gui, vpp);
- // 7. No need to unreqgister the callbacks
- // ...they will go when the viewport is destroyed
- // 6. Destroy the viewport
- if(vpp) {
- view_port_enabled_set(vpp, false);
- view_port_free(vpp);
- vpp = NULL;
- }
- // 5. Free the mutex
- if(state && state->mutex) {
- furi_mutex_free(state->mutex);
- state->mutex = NULL;
- }
- // 4. Free up state pointer(s)
- // none
- // 3. Free the plugin state variables
- if(state) {
- free(state);
- state = NULL;
- }
- // 2. Close the GUI
- furi_record_close("gui");
- // 1. Destroy the message queue
- if(queue) {
- furi_message_queue_free(queue);
- queue = NULL;
- }
- INFO("CLEAN EXIT ... Exit code: %d", error);
- LEAVE;
- return (int32_t)error;
- }
|