Просмотр исходного кода

Revert "Merge pokemon_trading/lib/flipper-gblink from https://github.com/kbembedded/flipper-gblink"

This reverts commit 43e212ac0250e3d58922f6d0d6a4199dcd4f51e4, reversing
changes made to 3146d7a17f548ea5d1b7ff90e231a04d065b0b33.
Willy-JL 1 год назад
Родитель
Сommit
4398ca10d8

+ 2 - 2
pokemon_trading/lib/flipper-gblink/README.md

@@ -1,7 +1,7 @@
 # Flipper Game Boy Game Link Cable API
 Simple API that can be included in projects to provide a flexible and easy way to handle data exchange over a Game Link Cable.
 
-Current Version: 0.62
+Current Version: 0.6
 
 Available from: https://github.com/kbembedded/flipper-gblink
 
@@ -23,7 +23,7 @@ Available from: https://github.com/kbembedded/flipper-gblink
 - [ ] Proper documentation  
 
 ## Use example
-See https://github.com/kbembedded/Flipper-Zero-Game-Boy-Pokemon-Trading
+See https://github.com/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading
 
 To include this in a Flipper Zero application, add this repo as a submodule in the `lib/` directory of the application source. Then add the following to `application.fam`:
 ```

+ 466 - 0
pokemon_trading/lib/flipper-gblink/gblink.c

@@ -0,0 +1,466 @@
+// SPDX-License-Identifier: BSD-2-Clause
+// Copyright (c) 2023 KBEmbedded
+
+#include <furi.h>
+#include <furi_hal.h>
+#include <stm32wbxx_ll_exti.h>
+#include <stm32wbxx_ll_system.h>
+
+#include <stdint.h>
+
+#include "gblink.h"
+
+const struct gblink_pins common_pinouts[PINOUT_COUNT] = {
+	/* Original */
+	{
+		&gpio_ext_pc3,
+		&gpio_ext_pb3,
+		&gpio_ext_pb2,
+		&gpio_ext_pa4,
+	},
+	/* MALVEKE EXT1 */
+	{
+		&gpio_ext_pa6,
+		&gpio_ext_pa7,
+		&gpio_ext_pb3,
+		&gpio_ext_pa4,
+	},
+};
+
+struct gblink {
+	const GpioPin *serin;
+	const GpioPin *serout;
+	const GpioPin *clk;
+	const GpioPin *sd;
+
+	uint8_t in;
+	uint8_t out;
+	uint8_t out_buf;
+	bool out_buf_valid;
+	uint8_t shift;
+	uint8_t nobyte;
+	gblink_clk_source source;
+	gblink_mode mode;
+	gblink_speed speed;
+
+	uint32_t time;
+
+	uint32_t bitclk_timeout_us;
+	/* Clocks idle between bytes is nominally 430 us long for burst data,
+	 * 15 ms for idle polling (e.g. waiting for menu selection), some oddball
+	 * 2 ms gaps that appears between one 0xFE byte from the Game Boy every trade;
+	 * clock period is nominally 122 us.
+	 * Therefore, if we haven't seen a clock in 500 us, reset our bit counter.
+	 * Note that, this should never actually be a concern, but it is an additional
+	 * safeguard against desyncing.
+	 */
+
+	void (*callback)(void* cb_context, uint8_t in);
+	void *cb_context;
+
+	uint32_t* ivt_mirror;
+	uint32_t ivt_mirror_offs;
+	bool exti3_rise_enable;
+	bool exti3_fall_enable;
+	bool exti3_event_enable;
+};
+
+static void gblink_shift_in(struct gblink *gblink)
+{
+	const uint32_t time_ticks = furi_hal_cortex_instructions_per_microsecond() * gblink->bitclk_timeout_us;
+
+	/* If we exceeded the bit clock timeout, reset all counters */
+	if ((DWT->CYCCNT - gblink->time) > time_ticks) {
+		gblink->in = 0;
+		gblink->shift = 0;
+	}
+	gblink->time = DWT->CYCCNT;
+
+	gblink->in <<= 1;
+	gblink->in |= furi_hal_gpio_read(gblink->serin);
+	gblink->shift++;
+	/* If 8 bits transfered, reset shift counter, call registered
+	 * callback, re-set nobyte in output buffer.
+	 */
+	if (gblink->shift == 8) {
+		gblink->shift = 0;
+
+		/* Set up next out byte before calling the callback.
+		 * This is in case the callback itself sets a new out
+		 * byte which it will in most cases. It is up to the
+		 * main application at this time to ensure that
+		 * gblink_transfer() isn't called multiple times before
+		 * a byte has a chance to be sent out.
+		 */
+		if (gblink->out_buf_valid) {
+			gblink->out = gblink->out_buf;
+			gblink->out_buf_valid = false;
+		} else {
+			gblink->out = gblink->nobyte;
+		}
+		gblink->callback(gblink->cb_context, gblink->in);
+	}
+}
+
+static void gblink_shift_out(struct gblink *gblink)
+{
+	furi_hal_gpio_write(gblink->serout, !!(gblink->out & 0x80));
+	gblink->out <<= 1;
+}
+
+static void gblink_clk_isr(void *context)
+{
+	furi_assert(context);
+	struct gblink *gblink = context;
+
+	if (furi_hal_gpio_read(gblink->clk)) {
+		/* Posedge Shift in data */
+		gblink_shift_in(gblink);
+	} else {
+		/* Negedge shift out data */
+		gblink_shift_out(gblink);
+	}
+}
+
+/* NOTE WELL! This function is absurdly hacky and a stupid workaround to a
+ * stupid issue that doesn't really have any other solution in the current
+ * Flipper/FURI API. I'm over-commenting this so we know exactly what is going
+ * on if we ever have to re-visit this mess.
+ *
+ * This block of text below describes the overall idea, more specific comments
+ * in the function body.
+ *
+ * TODO: make this more generic for any other GPIOs that might conflict with
+ * exti interrupts. PA6, PB3, PC3, PB2? (NFC), PA13, PB6
+ * NOTE: This is only set up at the moment for PB3, hardcoded
+ *
+ * There are multiple problems that this workaround is handling. EXTI interrupts
+ * are shared among multiple pins. The FURI core maintains per-pin ISRs in a
+ * private struct that has no way to read, save, or otherwise be able to put
+ * back the ISR that would service a conflicting EXTI. e.g. PB3 and PH3
+ * (the OK button) both share EXTI3. Setting an interrupt on PB3 will clobber
+ * the FURI ISR callback/context pair as well as change EXTI3 to use PB3 as
+ * the interrupt source.
+ *
+ * To make an interrupt work correctly on PB3 and not break the OK button
+ * we need a way to set an interrupt for PB3 in a way that doesn't clobber the
+ * private FURI GPIO ISR handles and can let the interrupt for the OK button
+ * work again when we're done.
+ *
+ * The general concept of this workaround is to modify the IVT to create our
+ * own handler for EXTI3 interrupts. Doing this leaves the aforementioned private
+ * GPIO struct unmodified and disables the OK button from triggering an interrupt.
+ * The IVT is normally located at the lowest addresses of flash (which is located
+ * at 0x08000000 and mapped at runtime to 0x00000000); this means the IVT cannot
+ * be changed at runtime.
+ *
+ * To make this work, we use the Vector Table Offset Register (VTOR) in the
+ * System Control Block (SCB). The VTOR allows for changing the location of the
+ * IVT. We copy the IVT to a location in memory, and then do a dance to safely
+ * set up the GPIO interrupt to PB3, and swap in our IVT with the modified EXTI3
+ * handler.
+ *
+ * When undoing this, the process is not quite in reverse as we have to put back
+ * specific interrupt settings that we very likely would have clobbered but have
+ * the ability to save beforehand.
+ *
+ * Wrapping the steps in disabling the EXTI3 interrupt is probably not needed,
+ * but is a precaution since we are changing the interrupt sources in weird ways.
+ */
+/* Used to map our callback context in a way the handler can access */
+static void *exti3_cb_context;
+static void gblink_exti3_IRQHandler(void) {
+	if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_3)) {
+		gblink_clk_isr(exti3_cb_context);
+		LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_3);
+	}
+}
+
+static void gblink_gross_exti_workaround(struct gblink *gblink)
+{
+	/* This process makes a number of assumptions, including that the IVT
+	 * is located at 0x00000000, that the lowest flash page is mapped to
+	 * that base address, and that the VTOR points to 0x00000000.
+	 * There are runtime protections in place to prevent reading from the
+	 * first 1 MB of addresses. So we have to always assume that the lowest
+	 * page of flash is mapped to 0x00000000 and read the IVT from the that
+	 * page in flash directly.
+	 * The only check we can really do here is ensuring VTOR is 0 and that
+	 * Main memory is mapped to 0x00000000. If either of those are not true,
+	 * then we can't continue.
+	 */
+	furi_check(SCB->VTOR == 0x0);
+	furi_check(LL_SYSCFG_GetRemapMemory() == LL_SYSCFG_REMAP_FLASH);
+
+	/* Create a mirror of the existing IVT from CPU 1
+	 * The IVT on this platform has 79 entries; 63 maskable, 10 non-maskable,
+	 * 6 reserved. The maskable interrupts start at offset 16.
+	 * CMSIS documentation says that the boundary for IVT must be aligned to
+	 * the number of interrupts, rounded up to the nearest power of two, and
+	 * then multiplied by the word width of the CPU. 79 rounds up to 128
+	 * with a word width of 4, this is 512/0x200 bytes.
+	 * As there is no good way with FreeRTOS to request an alloc at an
+	 * aligned boundary, allocate the amount of data we need, plus 0x200
+	 * bytes, to guarantee that we can put the table in a location that is
+	 * properly aligned. Once we find a suitable base address, this offset
+	 * is saved for later.
+	 */
+	gblink->ivt_mirror = malloc((79 * sizeof(uint32_t)) + 0x200);
+	gblink->ivt_mirror_offs = (uint32_t)gblink->ivt_mirror;
+	while (gblink->ivt_mirror_offs & 0x1FF)
+		gblink->ivt_mirror_offs++;
+	/* 0x08000000 is used instead of 0x00000000 because everything complains
+	 * using a NULL pointer.
+	 */
+	memcpy((uint32_t *)gblink->ivt_mirror_offs, ((uint32_t *)0x08000000), 79 * sizeof(uint32_t));
+
+	/* Point our IVT's EXTI3 interrupt to our desired interrupt handler.
+	 * Also copy the gblink struct to the global var that the interrupt
+	 * handler will use to make further calls.
+	 */
+	((uint32_t *)gblink->ivt_mirror_offs)[25] = (uint32_t)gblink_exti3_IRQHandler; // 16 NMI + offset of 9 for EXTI3
+	exti3_cb_context = gblink;
+
+	/* Disable the EXTI3 interrupt. This lets us do bad things without
+	 * fear of an IRQ hitting in the middle.
+	 */
+	LL_EXTI_DisableIT_0_31(LL_EXTI_LINE_3);
+
+	/* Save the existing rise/fall trigger settings. In theory, these should
+	 * really never change through the life of the flipper OS. But for safety
+	 * we always save them rather than just blindly restoring the same settings
+	 * back when we undo this later.
+	 */
+	gblink->exti3_rise_enable = LL_EXTI_IsEnabledRisingTrig_0_31(LL_EXTI_LINE_3);
+	gblink->exti3_fall_enable = LL_EXTI_IsEnabledFallingTrig_0_31(LL_EXTI_LINE_3);
+	gblink->exti3_event_enable = LL_EXTI_IsEnabledEvent_0_31(LL_EXTI_LINE_3);
+
+	/* Now, set up our desired pin settings. This will only clobber exti3
+	 * settings and will not affect the actual interrupt vector address.
+	 * Settings include the rising/falling/event triggers which we just
+	 * saved.
+	 */
+	furi_hal_gpio_init(gblink->clk, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedVeryHigh);
+
+	/* Update the NVIC table to point at our desired table.
+	 * Out of safety, stop the world around changing the VTOR reg.
+	 */
+	FURI_CRITICAL_ENTER();
+	SCB->VTOR = gblink->ivt_mirror_offs;
+	FURI_CRITICAL_EXIT();
+
+	/* Last, enable the interrupts and hope everything works. */
+	LL_EXTI_EnableIT_0_31(LL_EXTI_LINE_3);
+}
+
+static void gblink_gross_exti_workaround_undo(struct gblink *gblink)
+{
+	/* First, disable the EXTI3 interrupt. This lets us do bad things without
+	 * fear of an IRQ hitting in the middle.
+	 */
+	LL_EXTI_DisableIT_0_31(LL_EXTI_LINE_3);
+
+	/* Set the correct input source, PH3/OK button, to EXTI3. It is important
+	 * to do this before calling furi_hal_gpio_init() on PB3. When that func
+	 * is called with no interrupt settings enabled, if the EXTI source
+	 * matches the pin, and the interrupt is enabled, interrupts will be
+	 * disabled. By manually setting the EXTI3 source here, it no longer
+	 * matches the PB3 pin, and our changing of IO settings on our GPIO pin
+	 * to no longer have interrupts will not affect the shared IRQ.
+	 */
+	LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTH, LL_SYSCFG_EXTI_LINE3);
+
+	/* Set the correct rise/fall/event settings back */
+	if (gblink->exti3_rise_enable)
+		LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_3);
+	else
+		LL_EXTI_DisableRisingTrig_0_31(LL_EXTI_LINE_3);
+
+	if (gblink->exti3_fall_enable)
+		LL_EXTI_EnableFallingTrig_0_31(LL_EXTI_LINE_3);
+	else
+		LL_EXTI_DisableFallingTrig_0_31(LL_EXTI_LINE_3);
+
+	if (gblink->exti3_event_enable)
+		LL_EXTI_EnableEvent_0_31(LL_EXTI_LINE_3);
+	else
+		LL_EXTI_DisableEvent_0_31(LL_EXTI_LINE_3);
+
+	/* "Release" the GPIO by putting it back in a known idle state. */
+	furi_hal_gpio_init_simple(gblink->clk, GpioModeAnalog);
+
+	/* Set the IVT back to the normal, in-flash table. Stopping the world
+	 * while we do so.
+	 * NOTE: This just assumes the VTOR is always at 0x0 by default, if this
+	 * ever changes in the Flipper OS, then that will be a problem.
+	 */
+	FURI_CRITICAL_ENTER();
+	SCB->VTOR = 0x0;
+	FURI_CRITICAL_EXIT();
+
+	/* Re-enable the interrupt, OK button should work again. */
+	LL_EXTI_EnableIT_0_31(LL_EXTI_LINE_3);
+
+	/* Free the alloc()ed mirror space */
+	free(gblink->ivt_mirror);
+}
+
+void gblink_clk_source_set(void *handle, int source)
+{
+	furi_assert(handle);
+	struct gblink *gblink = handle;
+
+	gblink->source = source;
+	gblink->shift = 0;
+}
+
+void gblink_speed_set(void *handle, gblink_speed speed)
+{
+	furi_assert(handle);
+	struct gblink *gblink = handle;
+
+	gblink->speed = speed;
+}
+
+/* default is set to 500 us */
+void gblink_timeout_set(void *handle, uint32_t us)
+{
+	furi_assert(handle);
+	struct gblink *gblink = handle;
+
+	gblink->bitclk_timeout_us = us;
+}
+
+void gblink_transfer(void *handle, uint8_t val)
+{
+	furi_assert(handle);
+	struct gblink *gblink = handle;
+
+	/* This checks the value of gblink->shift which can change in the ISR.
+	 * Because of that, disable interrupts when checking gblink->shift and
+	 * setting gblink->out_buf_valid
+	 * If shift is 0, we're between bytes and can safely set the out byte.
+	 * If shift is nonzero, a byte is currently being transmitted. Set the
+	 * out_buf and set out_buf_valid. When the ISR is finished writing the
+	 * next byte it will check out_buf_valid and copy in out_buf.
+	 *
+	 * The correct/smart way of doing this would be a mutex rather than
+	 * stopping the world.
+	 *
+	 * Realistically, this should only ever be called from the transfer
+	 * complete callback. There are few situations outside of that which
+	 * would make sense.
+	 *
+	 * Note that, this currently has no checks for if there is data already
+	 * pending to be transmitted. Calling this back to back can cause data
+	 * loss!
+	 */
+	FURI_CRITICAL_ENTER();
+	if (gblink->shift == 0) {
+		gblink->out = val;
+		gblink->out_buf_valid = false;
+	} else {
+		gblink->out_buf = val;
+		gblink->out_buf_valid = true;
+	}
+	FURI_CRITICAL_EXIT();
+}
+
+void gblink_nobyte_set(void *handle, uint8_t val)
+{
+	struct gblink *gblink = handle;
+	gblink->nobyte = val;
+}
+
+void gblink_int_enable(void *handle)
+{
+	furi_assert(handle);
+	struct gblink *gblink = handle;
+
+	furi_hal_gpio_enable_int_callback(gblink->clk);
+}
+
+void gblink_int_disable(void *handle)
+{
+	furi_assert(handle);
+	struct gblink *gblink = handle;
+
+	furi_hal_gpio_disable_int_callback(gblink->clk);
+}
+
+void *gblink_alloc(struct gblink_def *gblink_def)
+{
+	struct gblink *gblink;
+
+	/* Allocate and zero struct */
+	gblink = malloc(sizeof(struct gblink));
+
+	/* Set struct values from function args */
+	gblink->serin = gblink_def->pins->serin;
+	gblink->serout = gblink_def->pins->serout;
+	gblink->clk = gblink_def->pins->clk;
+	gblink->sd = gblink_def->pins->sd;
+	gblink->source = gblink_def->source;
+	gblink->speed = GBLINK_SPD_8192HZ;
+
+	/* Set up timeout variables */
+	gblink->bitclk_timeout_us = 500;
+	gblink->time = DWT->CYCCNT;
+
+	/* Set up secondary callback */
+	gblink->callback = gblink_def->callback;
+	gblink->cb_context = gblink_def->cb_context;
+
+	/* Set up pins */
+	/* TODO: Set up a list of pins that are not safe to use with interrupts.
+	 * I do believe the main FURI GPIO struct has this data baked in so that
+	 * could be used. For now though, we're only checking for the MALVEKE
+	 * pinout which uses a clk pin that has its IRQ shared with the Okay
+	 * button.
+	 * See the work done in pokemon trade tool custom pinout selection for
+	 * an idea of how to check all that.
+	 */
+	/* TODO: Currently assumes external clock source only */
+	/* XXX: This might actually be open-drain on real GB hardware */
+	furi_hal_gpio_write(gblink->serout, false);
+	furi_hal_gpio_init(gblink->serout, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
+	furi_hal_gpio_write(gblink->serin, false);
+	furi_hal_gpio_init(gblink->serin, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
+
+	/* Set up interrupt on clock */
+	if (gblink->clk == &gpio_ext_pb3) {
+		/* The clock pin is on a pin that is not safe to set an interrupt
+		 * on, so we do a gross workaround to get an interrupt enabled
+		 * on that pin in a way that can be undone safely later with
+		 * no impact to the shared IRQ.
+		 */
+		gblink_gross_exti_workaround(gblink);
+	} else {
+		furi_hal_gpio_init(gblink->clk, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedVeryHigh);
+		/* This may not be needed after NFC refactor */
+		furi_hal_gpio_remove_int_callback(gblink->clk);
+		furi_hal_gpio_add_int_callback(gblink->clk, gblink_clk_isr, gblink);
+	}
+
+	return gblink;
+}
+
+void gblink_free(void *handle)
+{
+	furi_assert(handle);
+	struct gblink *gblink = handle;
+
+	if (gblink->clk == &gpio_ext_pb3) {
+		/* This handles switching the IVT back and putting the EXTI
+		 * regs and pin regs in a valid state for normal use.
+		 */
+		gblink_gross_exti_workaround_undo(gblink);
+	} else {
+		/* Remove interrupt, set IO to sane state */
+		furi_hal_gpio_remove_int_callback(gblink->clk);
+	}
+	furi_hal_gpio_init_simple(gblink->serin, GpioModeAnalog);
+	furi_hal_gpio_init_simple(gblink->serout, GpioModeAnalog);
+	furi_hal_gpio_init_simple(gblink->clk, GpioModeAnalog);
+	free(gblink);
+}

+ 86 - 0
pokemon_trading/lib/flipper-gblink/gblink.h

@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: BSD-2-Clause
+// Copyright (c) 2023 KBEmbedded
+
+#ifndef __GBLINK_H__
+#define __GBLINK_H__
+
+#pragma once
+
+#include <furi.h>
+#include <furi_hal.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+	/* Flipper drives the clock line */
+	/* Unsupported at this time */
+	GBLINK_INTERNAL_CLK,
+	/* Game Boy drives the clock line */
+	GBLINK_EXTERNAL_CLK,
+} gblink_clk_source;
+
+/* Currently unused */
+typedef enum {
+	GBLINK_MODE_GBC,
+	GBLINK_MODE_GBA,
+} gblink_mode;
+
+/* Should this just be a macro? */
+/* This pretty much only applies to GBC, OG GB is 8192 Hz only */
+/* This is only for TX */
+typedef enum {
+	GBLINK_SPD_8192HZ,
+	GBLINK_SPD_16384HZ,
+	GBLINK_SPD_262144HZ,
+	GBLINK_SPD_524288HZ,
+} gblink_speed;
+
+struct gblink_pins {
+        const GpioPin *serin;
+        const GpioPin *serout;
+        const GpioPin *clk;
+        const GpioPin *sd;
+};
+
+typedef enum {
+	PINOUT_ORIGINAL,
+	PINOUT_MALVEKE_EXT1,
+	PINOUT_COUNT,
+} gblink_pinout;
+
+extern const struct gblink_pins common_pinouts[PINOUT_COUNT];
+
+struct gblink_def {
+	struct gblink_pins *pins;
+	gblink_clk_source source;
+	gblink_mode mode;
+	void (*callback)(void* cb_context, uint8_t in);
+	void *cb_context;
+};
+
+void gblink_clk_source_set(void *handle, int clk_source);
+
+void gblink_speed_set(void *handle, gblink_speed speed);
+
+void gblink_timeout_set(void *handle, uint32_t us);
+
+void gblink_transfer(void *handle, uint8_t val);
+
+void gblink_nobyte_set(void *handle, uint8_t val);
+
+void gblink_int_enable(void *handle);
+
+void gblink_int_disable(void *handle);
+
+void *gblink_alloc(struct gblink_def *gblink_def);
+
+void gblink_free(void *handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __GBLINK_H__

+ 0 - 68
pokemon_trading/lib/flipper-gblink/gblink/clock_timer.c

@@ -1,68 +0,0 @@
-/*
- * NOTE TO ANYONE USING THIS CODE
- *
- * The following is verbatim from the flipperzero-game-engine codebase and
- * exists with a separate license, GPL-3.0 
- *
- * While the flipper-gblink is BSD-2, this project is always open-source.
- *
- * If you are using the flipper-gblink library in another project that is not
- * open-source, then you should review the GPL-3.0 text to ensure you abide by
- * the terms of the license before releasing a binary made with this code and
- * not including the source alongside it.
- *
- */
-
-#include "clock_timer_i.h"
-#include <stdlib.h>
-
-#include <furi_hal_interrupt.h>
-#include <furi_hal_bus.h>
-#include <stm32wbxx_ll_tim.h>
-
-#define FURI_HAL_CLOCK_TIMER TIM2
-#define FURI_HAL_CLOCK_TIMER_BUS FuriHalBusTIM2
-#define FURI_HAL_CLOCK_TIMER_IRQ FuriHalInterruptIdTIM2
-
-typedef struct {
-    ClockTimerCallback callback;
-    void* context;
-} ClockTimer;
-
-static ClockTimer clock_timer = {
-    .callback = NULL,
-    .context = NULL,
-};
-
-static void clock_timer_isr(void* context) {
-    if(clock_timer.callback) {
-        clock_timer.callback(context);
-    }
-
-    LL_TIM_ClearFlag_UPDATE(FURI_HAL_CLOCK_TIMER);
-}
-
-void clock_timer_start(ClockTimerCallback callback, void* context, float period) {
-    clock_timer.callback = callback;
-    clock_timer.context = context;
-
-    furi_hal_bus_enable(FURI_HAL_CLOCK_TIMER_BUS);
-
-    // init timer to produce interrupts
-    LL_TIM_InitTypeDef TIM_InitStruct = {0};
-    TIM_InitStruct.Autoreload = (SystemCoreClock / period) - 1;
-    LL_TIM_Init(FURI_HAL_CLOCK_TIMER, &TIM_InitStruct);
-
-    furi_hal_interrupt_set_isr(FURI_HAL_CLOCK_TIMER_IRQ, clock_timer_isr, clock_timer.context);
-
-    LL_TIM_EnableIT_UPDATE(FURI_HAL_CLOCK_TIMER);
-    LL_TIM_EnableCounter(FURI_HAL_CLOCK_TIMER);
-}
-
-void clock_timer_stop(void) {
-    LL_TIM_DisableIT_UPDATE(FURI_HAL_CLOCK_TIMER);
-    LL_TIM_DisableCounter(FURI_HAL_CLOCK_TIMER);
-
-    furi_hal_bus_disable(FURI_HAL_CLOCK_TIMER_BUS);
-    furi_hal_interrupt_set_isr(FURI_HAL_CLOCK_TIMER_IRQ, NULL, NULL);
-}

+ 0 - 15
pokemon_trading/lib/flipper-gblink/gblink/clock_timer_i.h

@@ -1,15 +0,0 @@
-#pragma once
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef void (*ClockTimerCallback)(void* context);
-
-void clock_timer_start(ClockTimerCallback callback, void* context, float period);
-
-void clock_timer_stop(void);
-
-#ifdef __cplusplus
-}
-#endif

+ 0 - 202
pokemon_trading/lib/flipper-gblink/gblink/exti_workaround.c

@@ -1,202 +0,0 @@
-// SPDX-License-Identifier: BSD-2-Clause
-// Copyright (c) 2023 KBEmbedded
-
-#include <furi.h>
-#include <furi_hal.h>
-#include <stm32wbxx_ll_exti.h>
-#include <stm32wbxx_ll_system.h>
-
-#include <stdint.h>
-
-struct exti_workaround {
-	uint32_t* ivt_mirror;
-	uint32_t ivt_mirror_offs;
-	bool exti3_rise_enable;
-	bool exti3_fall_enable;
-	bool exti3_event_enable;
-	const GpioPin *clk;
-};
-
-/* NOTE WELL! This function is absurdly hacky and a stupid workaround to a
- * stupid issue that doesn't really have any other solution in the current
- * Flipper/FURI API. I'm over-commenting this so we know exactly what is going
- * on if we ever have to re-visit this mess.
- *
- * This block of text below describes the overall idea, more specific comments
- * in the function body.
- *
- * TODO: make this more generic for any other GPIOs that might conflict with
- * exti interrupts. PA6, PB3, PC3, PB2? (NFC), PA13, PB6
- * NOTE: This is only set up at the moment for PB3, hardcoded
- *
- * There are multiple problems that this workaround is handling. EXTI interrupts
- * are shared among multiple pins. The FURI core maintains per-pin ISRs in a
- * private struct that has no way to read, save, or otherwise be able to put
- * back the ISR that would service a conflicting EXTI. e.g. PB3 and PH3
- * (the OK button) both share EXTI3. Setting an interrupt on PB3 will clobber
- * the FURI ISR callback/context pair as well as change EXTI3 to use PB3 as
- * the interrupt source.
- *
- * To make an interrupt work correctly on PB3 and not break the OK button
- * we need a way to set an interrupt for PB3 in a way that doesn't clobber the
- * private FURI GPIO ISR handles and can let the interrupt for the OK button
- * work again when we're done.
- *
- * The general concept of this workaround is to modify the IVT to create our
- * own handler for EXTI3 interrupts. Doing this leaves the aforementioned private
- * GPIO struct unmodified and disables the OK button from triggering an interrupt.
- * The IVT is normally located at the lowest addresses of flash (which is located
- * at 0x08000000 and mapped at runtime to 0x00000000); this means the IVT cannot
- * be changed at runtime.
- *
- * To make this work, we use the Vector Table Offset Register (VTOR) in the
- * System Control Block (SCB). The VTOR allows for changing the location of the
- * IVT. We copy the IVT to a location in memory, and then do a dance to safely
- * set up the GPIO interrupt to PB3, and swap in our IVT with the modified EXTI3
- * handler.
- *
- * When undoing this, the process is not quite in reverse as we have to put back
- * specific interrupt settings that we very likely would have clobbered but have
- * the ability to save beforehand.
- *
- * Wrapping the steps in disabling the EXTI3 interrupt is probably not needed,
- * but is a precaution since we are changing the interrupt sources in weird ways.
- */
-/* Used to map our callback context in a way the handler can access */
-static void *exti3_cb_context;
-static void (*callback)(void *context);
-static void gblink_exti3_IRQHandler(void) {
-	if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_3)) {
-		callback(exti3_cb_context);
-		LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_3);
-	}
-}
-
-void *exti_workaround(const GpioPin *clk, void (*isr_callback)(void *context), void *context)
-{
-	struct exti_workaround *work = NULL;
-
-	/* This process makes a number of assumptions, including that the IVT
-	 * is located at 0x00000000, that the lowest flash page is mapped to
-	 * that base address, and that the VTOR points to 0x00000000.
-	 * There are runtime protections in place to prevent reading from the
-	 * first 1 MB of addresses. So we have to always assume that the lowest
-	 * page of flash is mapped to 0x00000000 and read the IVT from the that
-	 * page in flash directly.
-	 * The only check we can really do here is ensuring VTOR is 0 and that
-	 * Main memory is mapped to 0x00000000. If either of those are not true,
-	 * then we can't continue.
-	 */
-	furi_check(SCB->VTOR == 0x0);
-	furi_check(LL_SYSCFG_GetRemapMemory() == LL_SYSCFG_REMAP_FLASH);
-
-	/* Create a mirror of the existing IVT from CPU 1
-	 * The IVT on this platform has 79 entries; 63 maskable, 10 non-maskable,
-	 * 6 reserved. The maskable interrupts start at offset 16.
-	 * CMSIS documentation says that the boundary for IVT must be aligned to
-	 * the number of interrupts, rounded up to the nearest power of two, and
-	 * then multiplied by the word width of the CPU. 79 rounds up to 128
-	 * with a word width of 4, this is 512/0x200 bytes.
-	 * As there is no good way with FreeRTOS to request an alloc at an
-	 * aligned boundary, allocate the amount of data we need, plus 0x200
-	 * bytes, to guarantee that we can put the table in a location that is
-	 * properly aligned. Once we find a suitable base address, this offset
-	 * is saved for later.
-	 */
-	work = malloc(sizeof(struct exti_workaround));
-	work->ivt_mirror = malloc((79 * sizeof(uint32_t)) + 0x200);
-	work->ivt_mirror_offs = (uint32_t)work->ivt_mirror;
-	while (work->ivt_mirror_offs & 0x1FF)
-		work->ivt_mirror_offs++;
-	/* 0x08000000 is used instead of 0x00000000 because everything complains
-	 * using a NULL pointer.
-	 */
-	memcpy((uint32_t *)work->ivt_mirror_offs, ((uint32_t *)0x08000000), 79 * sizeof(uint32_t));
-
-	/* Point our IVT's EXTI3 interrupt to our desired interrupt handler.
-	 * Also copy the gblink struct to the global var that the interrupt
-	 * handler will use to make further calls.
-	 */
-	((uint32_t *)work->ivt_mirror_offs)[25] = (uint32_t)gblink_exti3_IRQHandler; // 16 NMI + offset of 9 for EXTI3
-	callback = isr_callback;
-	exti3_cb_context = context;
-
-	/* Disable the EXTI3 interrupt. This lets us do bad things without
-	 * fear of an IRQ hitting in the middle.
-	 */
-	LL_EXTI_DisableIT_0_31(LL_EXTI_LINE_3);
-
-	/* Save the existing rise/fall trigger settings. In theory, these should
-	 * really never change through the life of the flipper OS. But for safety
-	 * we always save them rather than just blindly restoring the same settings
-	 * back when we undo this later.
-	 */
-	work->exti3_rise_enable = LL_EXTI_IsEnabledRisingTrig_0_31(LL_EXTI_LINE_3);
-	work->exti3_fall_enable = LL_EXTI_IsEnabledFallingTrig_0_31(LL_EXTI_LINE_3);
-	work->exti3_event_enable = LL_EXTI_IsEnabledEvent_0_31(LL_EXTI_LINE_3);
-	work->clk = clk;
-
-	/* Update the NVIC table to point at our desired table.
-	 * Out of safety, stop the world around changing the VTOR reg.
-	 */
-	FURI_CRITICAL_ENTER();
-	SCB->VTOR = work->ivt_mirror_offs;
-	FURI_CRITICAL_EXIT();
-
-	/* Last, enable the interrupts and hope everything works. */
-	LL_EXTI_EnableIT_0_31(LL_EXTI_LINE_3);
-
-	return work;
-}
-
-void exti_workaround_undo(void *handle)
-{
-	struct exti_workaround *work = handle;
-	/* First, disable the EXTI3 interrupt. This lets us do bad things without
-	 * fear of an IRQ hitting in the middle.
-	 */
-	LL_EXTI_DisableIT_0_31(LL_EXTI_LINE_3);
-
-	/* Set the correct input source, PH3/OK button, to EXTI3. It is important
-	 * to do this before calling furi_hal_gpio_init() on PB3. When that func
-	 * is called with no interrupt settings enabled, if the EXTI source
-	 * matches the pin, and the interrupt is enabled, interrupts will be
-	 * disabled. By manually setting the EXTI3 source here, it no longer
-	 * matches the PB3 pin, and our changing of IO settings on our GPIO pin
-	 * to no longer have interrupts will not affect the shared IRQ.
-	 */
-	LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTH, LL_SYSCFG_EXTI_LINE3);
-
-	/* Set the correct rise/fall/event settings back */
-	if (work->exti3_rise_enable)
-		LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_3);
-	else
-		LL_EXTI_DisableRisingTrig_0_31(LL_EXTI_LINE_3);
-
-	if (work->exti3_fall_enable)
-		LL_EXTI_EnableFallingTrig_0_31(LL_EXTI_LINE_3);
-	else
-		LL_EXTI_DisableFallingTrig_0_31(LL_EXTI_LINE_3);
-
-	if (work->exti3_event_enable)
-		LL_EXTI_EnableEvent_0_31(LL_EXTI_LINE_3);
-	else
-		LL_EXTI_DisableEvent_0_31(LL_EXTI_LINE_3);
-
-
-	/* Set the IVT back to the normal, in-flash table. Stopping the world
-	 * while we do so.
-	 * NOTE: This just assumes the VTOR is always at 0x0 by default, if this
-	 * ever changes in the Flipper OS, then that will be a problem.
-	 */
-	FURI_CRITICAL_ENTER();
-	SCB->VTOR = 0x0;
-	FURI_CRITICAL_EXIT();
-
-	/* Re-enable the interrupt, OK button should work again. */
-	LL_EXTI_EnableIT_0_31(LL_EXTI_LINE_3);
-
-	/* Free the alloc()ed mirror space */
-	free(work->ivt_mirror);
-	free(work);
-}

+ 0 - 22
pokemon_trading/lib/flipper-gblink/gblink/exti_workaround_i.h

@@ -1,22 +0,0 @@
-// SPDX-License-Identifier: BSD-2-Clause
-// Copyright (c) 2023 KBEmbedded
-
-#ifndef __EXTI_WORKAROUND_H__
-#define __EXTI_WORKAROUND_H__
-
-#include <furi.h>
-#include <furi_hal.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void *exti_workaround(const GpioPin *clk, void (*isr_callback)(void *context), void *context);
-
-void exti_workaround_undo(void *handle);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // __EXTI_WORKAROUND_H__

+ 0 - 593
pokemon_trading/lib/flipper-gblink/gblink/gblink.c

@@ -1,593 +0,0 @@
-// SPDX-License-Identifier: BSD-2-Clause
-// Copyright (c) 2023 KBEmbedded
-
-#include <furi.h>
-#include <furi_hal.h>
-#include <stm32wbxx_ll_exti.h>
-#include <stm32wbxx_ll_system.h>
-
-#include <stdint.h>
-
-#include <gblink/include/gblink.h>
-#include "exti_workaround_i.h"
-#include "clock_timer_i.h"
-
-const struct gblink_pins common_pinouts[PINOUT_COUNT] = {
-	/* Original */
-	{
-		&gpio_ext_pc3,
-		&gpio_ext_pb3,
-		&gpio_ext_pb2,
-		&gpio_ext_pa4,
-	},
-	/* MALVEKE EXT1 */
-	{
-		&gpio_ext_pa6,
-		&gpio_ext_pa7,
-		&gpio_ext_pb3,
-		&gpio_ext_pa4,
-	},
-};
-
-struct gblink {
-	const GpioPin *serin;
-	const GpioPin *serout;
-	const GpioPin *clk;
-	const GpioPin *sd;
-	gblink_mode mode;
-	void (*callback)(void* cb_context, uint8_t in);
-	void *cb_context;
-
-	/* These two semaphores serve similar but distinct purposes. */
-	/* The transfer semaphore is taken as soon as a transfer() request
-	 * has been started. This is used in the function to wait until the
-	 * transfer has been completed.
-	 */
-	FuriSemaphore *transfer_sem;
-	/* The out byte semaphore is used to indicate that a byte transfer
-	 * is in progress. This is used in the transfer function to not allow
-	 * a transfer request if we're in the middle of sending a byte.
-	 * The transfer semaphore is not used for that purpose since if the
-	 * Flipper is in EXT clk mode, once a transfer() is started, there
-	 * would be no way to both prevent transfer() from being called again
-	 * as well as cancelling/changing what we're wanting to send. Using
-	 * out byte semaphore means a transfer() can be called at any time,
-	 * waited on synchronously for a timeout, and then re-called at a
-	 * later time; while blocking that update if a byte is actually
-	 * in the middle of being transmitted.
-	 */
-	FuriSemaphore *out_byte_sem;
-
-	/* Used to lock out changing things after a certain point. Pinout,
-	 * mode, etc.
-	 * XXX: Might make more sense to use the mutex to protect a flag?
-	 * Maybe a semaphore? Though I think that is the wrong use.
-	 */
-	FuriMutex *start_mutex;
-
-	/* 
-	 * The following should probably have the world stopped around them
-	 * if not modified in an interrupt context.
-	 */
-	uint8_t in;
-	uint8_t out;
-	uint8_t shift;
-	uint8_t nobyte;
-
-	/* Should only be changed when not in middle of tx, will affect a lot */
-	gblink_clk_source source;
-
-	/* Can be changed at any time, will only take effect on the next
-	 * transfer.
-	 */
-	gblink_speed speed;
-
-
-	/* 
-	 * The following is based on observing Pokemon trade data
-	 *
-	 * Clocks idle between bytes is nominally 430 us long for burst data,
-	 * 15 ms for idle polling (e.g. waiting for menu selection), some oddball
-	 * 2 ms gaps that appears between one 0xFE byte from the Game Boy every trade;
-	 * clock period is nominally 122 us.
-	 *
-	 * Therefore, if we haven't seen a clock in 500 us, reset our bit counter.
-	 * Note that, this should never actually be a concern, but it is an additional
-	 * safeguard against desyncing.
-	 */
-	uint32_t time;
-	uint32_t bitclk_timeout_us;
-
-	void *exti_workaround_handle;
-};
-
-static inline bool gblink_transfer_in_progress(struct gblink *gblink)
-{
-	return !(furi_semaphore_get_count(gblink->out_byte_sem));
-}
-
-static void gblink_shift_in_isr(struct gblink *gblink)
-{
-	const uint32_t time_ticks = furi_hal_cortex_instructions_per_microsecond() * gblink->bitclk_timeout_us;
-
-	if (gblink->source == GBLINK_CLK_INT)
-		furi_hal_gpio_write(gblink->clk, 1);
-
-	/* If we exceeded the bit clock timeout, reset all counters */
-	if ((DWT->CYCCNT - gblink->time) > time_ticks) {
-		gblink->in = 0;
-		gblink->shift = 0;
-	}
-	gblink->time = DWT->CYCCNT;
-
-	gblink->in <<= 1;
-	gblink->in |= furi_hal_gpio_read(gblink->serin);
-	gblink->shift++;
-	/* If 8 bits transfered, reset shift counter, call registered
-	 * callback, re-set nobyte in output buffer.
-	 */
-	if (gblink->shift == 8) {
-	 	if (gblink->source == GBLINK_CLK_INT)
-			clock_timer_stop();
-
-		gblink->shift = 0;
-
-		/* 
-		 * Set up next out byte before calling the callback.
-		 * This is in case the callback itself sets a new out
-		 * byte which it will in most cases.
-		 *
-		 * The nobyte value is set in place as the next output byte,
-		 * in case the flipper does not set a real byte before the next
-		 * transfer starts.
-		 */
-		gblink->out = gblink->nobyte;
-		furi_semaphore_release(gblink->out_byte_sem);
-
-		/* 
-		 * Call the callback, if set, and then release the semaphore
-		 * in case a thread is waiting on TX to complete.
-		 */
-		if (gblink->callback)
-			gblink->callback(gblink->cb_context, gblink->in);
-
-		furi_semaphore_release(gblink->transfer_sem);
-	}
-}
-
-static void gblink_shift_out_isr(struct gblink *gblink)
-{
-	furi_semaphore_acquire(gblink->out_byte_sem, 0);
-	furi_hal_gpio_write(gblink->serout, !!(gblink->out & 0x80));
-	gblink->out <<= 1;
-
-	/* XXX: TODO: Check that this is the correct thing with open drain.
-	 * does 0 value actually drive the line low, or high?
-	 */
-	if (gblink->source == GBLINK_CLK_INT)
-		furi_hal_gpio_write(gblink->clk, 0);
-}
-
-static void gblink_clk_isr(void *context)
-{
-	furi_assert(context);
-	struct gblink *gblink = context;
-	bool out = false;
-
-	/* 
-	 * Whether we're shifting in or out is dependent on the clock source.
-	 * If external, and the clock line is high, that means a posedge just
-	 * occurred and we need to shift data in.
-	 *
-	 * If internal, and the clock line is high, that means we're about
-	 * to drive a negedge and need to shift data out.
-	 *
-	 * The actual in/out functions drive the clock state at the right times
-	 * if the clock is internal source.
-	 */
-	out = (furi_hal_gpio_read(gblink->clk) ==
-	      (gblink->source == GBLINK_CLK_INT));
-
-	if (out)
-		gblink_shift_out_isr(gblink);
-	else
-		gblink_shift_in_isr(gblink);
-}
-
-/* 
- * Call to set up the clk pin modes to do the right thing based on if INT or
- * EXT clock source is configured.
- */
-static void gblink_clk_configure(struct gblink *gblink)
-{
-	if (gblink->source == GBLINK_CLK_EXT) {
-		furi_hal_gpio_init(gblink->clk, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedVeryHigh);
-		/* furi_hal_gpio_init, while it sets interrupt settings on the GPIO,
-		 * does not actually enable the EXTI interrupt.
-		 */
-		gblink_int_enable(gblink);
-	} else {
-		/* This will disable the EXTI interrupt for us */
-		furi_hal_gpio_init(gblink->clk, GpioModeOutputOpenDrain, GpioPullUp, GpioSpeedVeryHigh);
-	};
-}
-
-void gblink_clk_source_set(void *handle, gblink_clk_source source)
-{
-	furi_assert(handle);
-	struct gblink *gblink = handle;
-
-	if (source == gblink->source)
-		return;
-	
-	/*
-	 * NOTE:
-	 * I'm not sure the best way to handle this at the moment. In theory,
-	 * it should be safe to check that we're just not in the middle of a
-	 * transfer and not worry about getting stuck.
-	 * However, I'm not really sure how true that is, so for now this will
-	 * always change the source and reset the current byte transfer.
-	 * It is up to the callee to ensure that they are between bytes.
-	 *
-	 * One idea would be to get the semaphore, but wait the set timeout.
-	 * if that is exceeded or the semaphore is acquired, then its probably
-	 * safe to change the source and reset shift register.
-	 */
-
-	gblink->source = source;
-	gblink->shift = 0;
-
-	gblink_clk_configure(gblink);
-}
-
-void gblink_speed_set(void *handle, gblink_speed speed)
-{
-	furi_assert(handle);
-	struct gblink *gblink = handle;
-
-	/*
-	 * This does not need any protection, it will take effect at the start
-	 * of the next byte.
-	 */
-	gblink->speed = speed;
-}
-
-/* default is set to 500 us */
-void gblink_timeout_set(void *handle, uint32_t us)
-{
-	furi_assert(handle);
-	struct gblink *gblink = handle;
-
-	gblink->bitclk_timeout_us = us;
-}
-
-int gblink_pin_set(void *handle, gblink_bus_pins pin, const GpioPin *gpio)
-{
-	furi_assert(handle);
-	struct gblink *gblink = handle;
-
-	if (furi_mutex_acquire(gblink->start_mutex, 0) != FuriStatusOk)
-		return 1;
-
-	switch (pin) {
-	case PIN_SERIN:
-		gblink->serin = gpio;
-		break;
-	case PIN_SEROUT:
-		gblink->serout = gpio;
-		break;
-	case PIN_CLK:
-		gblink->clk = gpio;
-		break;
-	case PIN_SD:
-		gblink->sd = gpio;
-		break;
-	default:
-		furi_crash();
-		break;
-	}
-
-	furi_mutex_release(gblink->start_mutex);
-
-	return 0;
-}
-
-int gblink_pin_set_default(void *handle, gblink_pinouts pinout)
-{
-	furi_assert(handle);
-	struct gblink *gblink = handle;
-
-	if (furi_mutex_acquire(gblink->start_mutex, 0) != FuriStatusOk)
-		return 1;
-
-	gblink->serin = common_pinouts[pinout].serin;
-	gblink->serout = common_pinouts[pinout].serout;
-	gblink->clk = common_pinouts[pinout].clk;
-	gblink->sd = common_pinouts[pinout].sd;
-
-	furi_mutex_release(gblink->start_mutex);
-
-	return 0;
-}
-
-
-const GpioPin *gblink_pin_get(void *handle, gblink_bus_pins pin)
-{
-	furi_assert(handle);
-	struct gblink *gblink = handle;
-
-	switch (pin) {
-	case PIN_SERIN:
-		return gblink->serin;
-	case PIN_SEROUT:
-		return gblink->serout;
-	case PIN_CLK:
-		return gblink->clk;
-	case PIN_SD:
-		return gblink->sd;
-	default:
-		furi_crash();
-		break;
-	}
-
-	return NULL;
-}
-
-int gblink_callback_set(void *handle, void (*callback)(void* cb_context, uint8_t in), void *cb_context)
-{
-	furi_assert(handle);
-	struct gblink *gblink = handle;
-
-	if (furi_mutex_acquire(gblink->start_mutex, 0) != FuriStatusOk)
-		return 1;
-
-	gblink->callback = callback;
-	gblink->cb_context = cb_context;
-	furi_mutex_release(gblink->start_mutex);
-
-	return 0;
-}
-
-int gblink_mode_set(void *handle, gblink_mode mode)
-{
-	furi_assert(handle);
-	struct gblink *gblink = handle;
-
-	if (furi_mutex_acquire(gblink->start_mutex, 0) != FuriStatusOk)
-		return 1;
-
-	gblink->mode = mode;
-	furi_mutex_release(gblink->start_mutex);
-
-	return 0;
-}
-
-
-bool gblink_transfer(void *handle, uint8_t val)
-{
-	furi_assert(handle);
-	struct gblink *gblink = handle;
-	bool ret = false;
-
-
-	/* Stop the world, this is to ensure we can safely set the next out byte */
-	/*
-	 * The reason for and therefore issue of setting the next byte has a few
-	 * points to keep in mind.
-	 *
-	 * First, with EXT clock source, the first hint of the external device
-	 * clocking in data is a negative edge where it would set data. This
-	 * means that the next out byte needs to be set before that.
-	 *
-	 * Second, since the interrupt on the neg clock edge loads the next
-	 * byte in to serout after grabbing the semaphore; we can stop the
-	 * world right now, and set the byte if there is no transfer in
-	 * progress. As soon as the world is resumed, the IRQ will fire, and
-	 * the correct, new, data byte will start to be shifted out.
-	 */
-	FURI_CRITICAL_ENTER();
-
-	/* If we're in the middle of a tranfer, don't let the byte be set. */
-	if (!gblink_transfer_in_progress(gblink)) {
-		gblink->out = val;
-		ret = true;
-
-		/*
-		 * Now that we're this far, this means the byte we set will be
-		 * transferred one way or another. Because of that, take the
-		 * transfer semaphore. This gets released once a full byte has
-		 * been transferred. This is for the TX wait function. We cannot
-		 * use the out_byte_sem as if the wait is called immediately
-		 * after the transfer, and no data has yet been shifted out,
-		 * the TX wait function would incorrectly return immediately.
-		 */
-		furi_semaphore_acquire(gblink->transfer_sem, 0);
-	}
-
-	FURI_CRITICAL_EXIT();
-
-	/* 
-	 * If the out byte was successfully set, and we're driving the clock,
-	 * turn on our timer for byte transfer.
-	 */
-	if (ret && gblink->source == GBLINK_CLK_INT)
-		clock_timer_start(gblink_clk_isr, gblink, gblink->speed);
-
-	return ret;
-
-}
-
-uint8_t gblink_transfer_tx_wait_complete(void *handle)
-{
-	struct gblink *gblink = handle;
-
-	/* XXX: TODO: Think about how to implement this in a way that we can
-	 * use the semaphore to see if there is a transfer waiting to happen,
-	 * but not in a way that would incorrectly show a transfer waiting. e.g.
-	 * if this takes the semaphore, then the semaphore is in the same state
-	 * as if a transfer was in progress. Should this put back the semaphore
-	 * after acquiring it? Is there a better way of handling it?
-	 */
-
-	furi_semaphore_acquire(gblink->transfer_sem, FuriWaitForever);
-
-	return gblink->in;
-}
-
-void gblink_nobyte_set(void *handle, uint8_t val)
-{
-	struct gblink *gblink = handle;
-
-	/*
-	 * This is safe to run at any time. It is only copied in after a byte
-	 * transfer is completed.
-	 */
-	gblink->nobyte = val;
-}
-
-void gblink_int_enable(void *handle)
-{
-	furi_assert(handle);
-	struct gblink *gblink = handle;
-
-	/*
-	 * NOTE: This is currently safe to run even with the exti workaround
-	 * in effect. It just enables the root EXTI interrupt source of the
-	 * given pin.
-	 */
-	furi_hal_gpio_enable_int_callback(gblink->clk);
-}
-
-void gblink_int_disable(void *handle)
-{
-	furi_assert(handle);
-	struct gblink *gblink = handle;
-
-	/*
-	 * NOTE: This is currently safe to run even with the exti workaround
-	 * in effect. It just disables the root EXTI interrupt source of the
-	 * given pin.
-	 */
-	furi_hal_gpio_disable_int_callback(gblink->clk);
-}
-
-void *gblink_alloc(void)
-{
-	struct gblink *gblink;
-
-	/* Allocate and zero struct */
-	gblink = malloc(sizeof(struct gblink));
-	//gblink->spec = malloc(sizeof(struct gblink_spec));
-
-	gblink->transfer_sem = furi_semaphore_alloc(1, 1);
-	gblink->out_byte_sem = furi_semaphore_alloc(1, 1);
-	gblink->start_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
-
-	/* Set defaults */
-	gblink_pin_set_default(gblink, PINOUT_ORIGINAL);
-	gblink_mode_set(gblink, GBLINK_MODE_GBC);
-	gblink_clk_source_set(gblink, GBLINK_CLK_EXT);
-	gblink_speed_set(gblink, GBLINK_SPD_8192HZ);
-	gblink_timeout_set(gblink, 500);
-
-	/* Set current time to start timeout calculations */
-	gblink->time = DWT->CYCCNT;
-
-	return gblink;
-}
-
-void gblink_start(void *handle)
-{
-	furi_assert(handle);
-	struct gblink *gblink = handle;
-
-	/* XXX: Check callback is valid */
-
-	furi_mutex_acquire(gblink->start_mutex, FuriWaitForever);
-
-	/* Set up pins */
-	/* TODO: Set up a list of pins that are not safe to use with interrupts.
-	 * I do believe the main FURI GPIO struct has this data baked in so that
-	 * could be used. For now though, we're only checking for the MALVEKE
-	 * pinout which uses a clk pin that has its IRQ shared with the Okay
-	 * button.
-	 * See the work done in pokemon trade tool custom pinout selection for
-	 * an idea of how to check all that.
-	 */
-	furi_hal_gpio_write(gblink->serout, false);
-	furi_hal_gpio_init(gblink->serout, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
-	furi_hal_gpio_write(gblink->serin, false);
-	furi_hal_gpio_init(gblink->serin, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
-
-	/* Set up interrupt on clock pin */
-	if (gblink->clk == &gpio_ext_pb3) {
-		/* The clock pin is on a pin that is not safe to set an interrupt
-		 * on, so we do a gross workaround to get an interrupt enabled
-		 * on that pin in a way that can be undone safely later with
-		 * no impact to the shared IRQ.
-		 */
-		gblink->exti_workaround_handle = exti_workaround(gblink->clk, gblink_clk_isr, gblink);
-	} else {
-		/* This may not be needed after NFC refactor */
-		furi_hal_gpio_remove_int_callback(gblink->clk);
-		furi_hal_gpio_add_int_callback(gblink->clk, gblink_clk_isr, gblink);
-	}
-
-	/* The above immediately enables the interrupt, we don't want
-	 * that just yet and we want configure to handle it.
-	 */
-	gblink_int_disable(gblink);
-
-	gblink_clk_configure(gblink);
-}
-
-void gblink_stop(void *handle)
-{
-	furi_assert(handle);
-	struct gblink *gblink = handle;
-
-	/* If we can acquire the mutex, that means start was never actually
-	 * called. Crash.
-	 * XXX: Probably a bit harsh to just crash, can it gracefully recover
-	 * without too much effort?
-	 */
-	if (furi_mutex_acquire(gblink->start_mutex, 0) == FuriStatusOk) {
-		furi_crash();
-		return;
-	}
-
-	if (gblink->clk == &gpio_ext_pb3) {
-		/* This handles switching the IVT back and putting the EXTI
-		 * regs and pin regs in a valid state for normal use.
-		 */
-		exti_workaround_undo(gblink->exti_workaround_handle);
-	} else {
-		/* Remove interrupt, set IO to sane state */
-		furi_hal_gpio_remove_int_callback(gblink->clk);
-	}
-	furi_hal_gpio_init_simple(gblink->serin, GpioModeAnalog);
-	furi_hal_gpio_init_simple(gblink->serout, GpioModeAnalog);
-	furi_hal_gpio_init_simple(gblink->clk, GpioModeAnalog);
-
-	furi_mutex_release(gblink->start_mutex);
-}
-
-void gblink_free(void *handle)
-{
-	furi_assert(handle);
-	struct gblink *gblink = handle;
-
-	/* If we cannot acquire the mutex, that means the link was never properly
-	 * stopped. Crash.
-	 * XXX: Can this be gracefully handled?
-	 */
-	if (furi_mutex_acquire(gblink->start_mutex, 0) != FuriStatusOk) {
-		furi_crash();
-		return;
-	}
-	furi_mutex_release(gblink->start_mutex);
-	furi_mutex_free(gblink->start_mutex);
-	furi_semaphore_free(gblink->transfer_sem);
-	furi_semaphore_free(gblink->out_byte_sem);
-	free(gblink);
-}

+ 0 - 115
pokemon_trading/lib/flipper-gblink/gblink/include/gblink.h

@@ -1,115 +0,0 @@
-// SPDX-License-Identifier: BSD-2-Clause
-// Copyright (c) 2023 KBEmbedded
-
-#ifndef __GBLINK_H__
-#define __GBLINK_H__
-
-#pragma once
-
-#include <furi.h>
-#include <furi_hal.h>
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef enum {
-	/* Flipper drives the clock line */
-	/* Unsupported at this time */
-	GBLINK_CLK_INT,
-	/* Game Boy drives the clock line */
-	GBLINK_CLK_EXT,
-} gblink_clk_source;
-
-/* Currently unused */
-typedef enum {
-	GBLINK_MODE_GBC,
-	GBLINK_MODE_GBA,
-} gblink_mode;
-
-/* Should this just be a macro? */
-/* This pretty much only applies to GBC, OG GB is 8192 Hz only */
-/* This is only for TX */
-typedef enum {
-	GBLINK_SPD_8192HZ = 4096,
-	GBLINK_SPD_16384HZ = 8192,
-	GBLINK_SPD_262144HZ = 16384,
-	GBLINK_SPD_524288HZ = 262144,
-} gblink_speed;
-
-struct gblink_pins {
-        const GpioPin *serin;
-        const GpioPin *serout;
-        const GpioPin *clk;
-        const GpioPin *sd;
-};
-
-typedef enum {
-	PINOUT_ORIGINAL,
-	PINOUT_MALVEKE_EXT1,
-	PINOUT_COUNT,
-} gblink_pinouts;
-
-typedef enum {
-	PIN_SERIN,
-	PIN_SEROUT,
-	PIN_CLK,
-	PIN_SD,
-	PIN_COUNT,
-} gblink_bus_pins;
-
-/*
- * NOTE:
- * This can be called at any time, it resets the current byte transfer information
- */
-void gblink_clk_source_set(void *handle, gblink_clk_source clk_source);
-
-void gblink_speed_set(void *handle, gblink_speed speed);
-
-void gblink_timeout_set(void *handle, uint32_t us);
-
-bool gblink_transfer(void *handle, uint8_t val);
-
-/* Can only be run after alloc, before start */
-int gblink_pin_set_default(void *handle, gblink_pinouts pinout);
-
-int gblink_pin_set(void *handle, gblink_bus_pins pin, const GpioPin *gpio);
-
-const GpioPin *gblink_pin_get(void *handle, gblink_bus_pins pin);
-
-int gblink_callback_set(void *handle, void (*callback)(void* cb_context, uint8_t in), void *cb_context);
-
-int gblink_mode_set(void *handle, gblink_mode mode);
-
-/* 
- * This can be used for INT or EXT clock modes. After a call to
- * gblink_transfer this can be called at any time and will return only after
- * a full byte is transferred and it will return the byte that was last shifted
- * in from the link partner.
- */
-uint8_t gblink_transfer_tx_wait_complete(void *handle);
-
-void gblink_nobyte_set(void *handle, uint8_t val);
-
-void gblink_int_enable(void *handle);
-
-void gblink_int_disable(void *handle);
-
-/* Sets up some defaults */
-void *gblink_alloc(void);
-
-void gblink_free(void *handle);
-
-void gblink_start(void *handle);
-
-void gblink_stop(void *handle);
-
-// void gblink_blink_led_on_byte(handle, color?)
-// get blink?
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // __GBLINK_H__

+ 0 - 56
pokemon_trading/lib/flipper-gblink/protocols/printer/include/printer_proto.h

@@ -1,56 +0,0 @@
-#ifndef PRINTER_PROTO_H
-#define PRINTER_PROTO_H
-
-#include <gblink/include/gblink.h>
-
-#pragma once
-
-enum cb_reason {
-	reason_data,
-	reason_print,
-	reason_complete,
-};
-
-/* Dual purpose struct used for both receiving image data from game boy, and
- * sending it to printer.
- */
-struct gb_image {
-	/* TODO: Need to understand this more */
-	uint8_t num_sheets;
-	uint8_t margins;
-	/* TODO: Does this actually matter? */
-	uint8_t palette;
-	/* TODO: Need to play with this more */
-	uint8_t exposure;
-
-	/* Always expected to be 160 px wide */
-	size_t data_sz;
-	uint8_t data[];
-};
-
-void *printer_alloc(void);
-
-void printer_free(void *printer_handle);
-
-void printer_callback_context_set(void *printer_handle, void *context);
-
-void printer_callback_set(void *printer_handle, void (*callback)(void *context, struct gb_image *image, enum cb_reason reason));
-
-/* Can only be run after alloc, before start */
-int printer_pin_set_default(void *printer_handle, gblink_pinouts pinout);
-
-int printer_pin_set(void *printer_handle, gblink_bus_pins pin, const GpioPin *gpio);
-
-const GpioPin *printer_pin_get(void *printer_handle, gblink_bus_pins pin);
-
-void printer_stop(void *printer_handle);
-
-/* Allocates a buffer of the maximum size that the printer can handle, must be
- * freed manually. Provided as a convenience function so the application doesn't
- * need to know how big of a buffer to create.
- */
-struct gb_image *printer_image_buffer_alloc(void);
-
-void printer_image_buffer_free(struct gb_image *image);
-
-#endif // PRINTER_PROTO_H

+ 0 - 10
pokemon_trading/lib/flipper-gblink/protocols/printer/include/printer_receive.h

@@ -1,10 +0,0 @@
-#ifndef PRINTER_RECEIVE_H
-#define PRINTER_RECEIVE_H
-
-#pragma once
-
-void printer_receive_start(void *printer_handle);
-
-void printer_receive_print_complete(void *printer_handle);
-
-#endif // PRINTER_RECEIVE_H

+ 0 - 100
pokemon_trading/lib/flipper-gblink/protocols/printer/printer_i.h

@@ -1,100 +0,0 @@
-#ifndef PRINTER_I_H
-#define PRINTER_I_H
-
-#include <protocols/printer/include/printer_proto.h>
-
-#define PKT_START_L		0x88
-#define PKT_START_H		0x33
-
-#define PRINTER_ID		0x81
-#define CMD_INIT		0x01
-#define CMD_PRINT		0x02
-#define CMD_TRANSFER		0x10
-#define CMD_DATA		0x04
-#define CMD_BREAK		0x08 // ??
-#define CMD_STATUS		0x0f
-
-#define STATUS_LOWBATT		(1 << 7)
-#define STATUS_ERR		(1 << 6)
-#define STATUS_JAM		(1 << 5)
-#define STATUS_PKT_ERR		(1 << 4)
-#define STATUS_READY		(1 << 3)
-#define STATUS_FULL		(1 << 2)
-#define STATUS_PRINTING		(1 << 1)
-#define STATUS_CKSUM		(1 << 0)
-
-// Extra status bits used not used by the printer
-#define STATUS_PRINT_CMD	(1 << 8)
-#define STATUS_PRINTED		(1 << 9)
-
-/* emulate printer's internal print receive buffer */
-#define TILE_SIZE		16 // 8x8 tile, 2bpp color
-#define WIDTH			20 // 20 tiles wide
-#define HEIGHT			18 // 18 tiles tall
-#define PRINT_RECV_SZ		640 // (TILE_SIZE * WIDTH * 2)
-#define PRINT_FULL_SZ		5760 // (PRINT_RECV_SZ * HEIGHT / 2)
-#define TRANSFER_RECV_SZ	3584 // (16*16*14) // Image minus frame
-
-//Note that TRANSFER uses a locked size, 16x14 tiles, 16*16*14
-
-//GB seems to use 2 second busy timeout? I think that is a go to busy/printing within 2 seconds?
-//20 second print timeout
-
-
-enum packet_state {
-	START_L,
-	START_H,
-	COMMAND,
-	COMPRESS,
-	LEN_L,
-	LEN_H,
-	COMMAND_DAT,
-	CKSUM_L,
-	CKSUM_H,
-	ALIVE,
-	STATUS,
-};
-
-enum printer_method {
-	PROTO_RECV,
-	PROTO_SEND,
-};
-
-/* Does not need to care about start bytes */
-struct packet {
-	uint8_t cmd;
-	uint8_t compress;
-	uint16_t len; // This is stored in the flipper endianness, arrives LSB first from GB, unmodified in code
-	uint8_t recv_data[PRINT_RECV_SZ]; // 640 bytes, enough for two lines of tiles
-	uint16_t cksum; // This is stored in the flipper endianness, arrives LSB first from GB
-	
-	/* These are not part of the packet, but used by us */
-	uint16_t cksum_calc;
-	size_t recv_data_sz;
-	uint16_t status;
-	uint8_t zero_counter;
-	enum packet_state state;
-	uint32_t time;
-};
-
-#define THREAD_FLAGS_EXIT	(1 << 0)
-#define THREAD_FLAGS_DATA	(1 << 1)
-#define THREAD_FLAGS_PRINT	(1 << 2)
-#define THREAD_FLAGS_COMPLETE	(1 << 3)
-#define THREAD_FLAGS_ALL	(THREAD_FLAGS_EXIT | THREAD_FLAGS_DATA | THREAD_FLAGS_PRINT | THREAD_FLAGS_COMPLETE)
-
-struct printer_proto {
-	void *gblink_handle;
-
-	void (*callback)(void *cb_context, struct gb_image *image, enum cb_reason reason);
-	void *cb_context;
-
-	struct packet *packet; //packet data used by send()/receive() for tracking
-
-	struct gb_image *image; // Details of the current image being sent/received
-
-
-	FuriThread *thread;
-};
-
-#endif // PRINTER_I_H

+ 0 - 146
pokemon_trading/lib/flipper-gblink/protocols/printer/printer_proto.c

@@ -1,146 +0,0 @@
-#include <furi.h>
-
-#include <gblink/include/gblink.h>
-#include <protocols/printer/include/printer_proto.h>
-#include "printer_i.h"
-
-/* XXX: Does this make sense to be a message dispatcher rather than calling callbacks?
- * In order to keep the stack small for the thread, need to be weary of all calls made from here. */
-/* XXX TODO Test using a timer pending callback instead of this */
-/* XXX: TODO: Create a more streamlined callback that can simply pass a struct that has
- * pointers to data, sz, reason, margins (aka is there more data coming), etc., could even place
- * the callback context in there which would allow using the timer pending callback function
- */
-static int32_t printer_callback_thread(void *context)
-{
-	struct printer_proto *printer = context;
-	uint32_t flags;
-
-	while (1) {
-		/* XXX: TODO: align flags and enum cb_reason to share them */
-		flags = furi_thread_flags_wait(THREAD_FLAGS_ALL, FuriFlagWaitAny, FuriWaitForever);
-		furi_check(!(flags & FuriFlagError));
-		if (flags & THREAD_FLAGS_EXIT)
-			break;
-		if (flags & THREAD_FLAGS_DATA)
-			printer->callback(printer->cb_context, printer->image, reason_data);
-		if (flags & THREAD_FLAGS_PRINT)
-			printer->callback(printer->cb_context, printer->image, reason_print);
-	}
-
-	return 0;
-}
-
-void *printer_alloc(void)
-{
-	struct printer_proto *printer = NULL;
-
-	printer = malloc(sizeof(struct printer_proto));
-
-	/* Allocate and start callback handling thread */
-	/* XXX: TODO: The stack can decrease if FURI_LOG calls are removed in callbacks! */
-	printer->thread = furi_thread_alloc_ex("GBLinkPrinterProtoCB",
-						1024,
-						printer_callback_thread,
-						printer);
-	/* Highest priority to ensure it runs ASAP */
-	furi_thread_set_priority(printer->thread, FuriThreadPriorityHighest);
-	furi_thread_start(printer->thread);
-
-	printer->packet = malloc(sizeof(struct packet));
-	printer->image = printer_image_buffer_alloc();
-
-	printer->gblink_handle = gblink_alloc();
-
-	/* Set up some settings for the print protocol. The final send/receive() calls
-	 * may clobber some of these, but that is intentional and they don't need to
-	 * care about some of the other details that are specified here.
-	 */
-	/* Reported 1.49 ms timeout between bytes, need confirmation */
-	gblink_timeout_set(printer->gblink_handle, 1490);
-	gblink_nobyte_set(printer->gblink_handle, 0x00);
-
-	return printer;
-}
-
-/* TODO: Allow free() without stop, add a way to check if printer_stop has not
- * yet been called.
- */
-void printer_free(void *printer_handle)
-{
-	struct printer_proto *printer = printer_handle;
-
-	furi_thread_flags_set(printer->thread, THREAD_FLAGS_EXIT);
-	furi_thread_join(printer->thread);
-	furi_thread_free(printer->thread);
-	gblink_free(printer->gblink_handle);
-	free(printer->packet);
-	free(printer->image);
-	free(printer);
-}
-
-void printer_callback_context_set(void *printer_handle, void *context)
-{
-	struct printer_proto *printer = printer_handle;
-
-	printer->cb_context = context;
-}
-
-void printer_callback_set(void *printer_handle, void (*callback)(void *context, struct gb_image *image, enum cb_reason reason))
-{
-	struct printer_proto *printer = printer_handle;
-
-	printer->callback = callback;
-}
-
-int printer_pin_set_default(void *printer_handle, gblink_pinouts pinout)
-{
-	struct printer_proto *printer = printer_handle;
-	return gblink_pin_set_default(printer->gblink_handle, pinout);
-}
-
-int printer_pin_set(void *printer_handle, gblink_bus_pins pin, const GpioPin *gpio)
-{
-	struct printer_proto *printer = printer_handle;
-	return gblink_pin_set(printer->gblink_handle, pin, gpio);
-}
-
-const GpioPin *printer_pin_get(void *printer_handle, gblink_bus_pins pin)
-{
-	struct printer_proto *printer = printer_handle;
-	return gblink_pin_get(printer->gblink_handle, pin);
-}
-
-
-void printer_stop(void *printer_handle)
-{
-	struct printer_proto *printer = printer_handle;
-
-	gblink_stop(printer->gblink_handle);
-	/* TODO: Call the callback one last time with a flag to indicate that the transfer has completely
-	 * ended.
-	 * Receive/send should also have a separate timeout, doesn't need to call stop, but, will
-	 * also retrigger the complete callback. This allows for both the actual process to signal
-	 * there was a gap (I think the gameboy print normally has a "I'm done" marker as well),
-	 * and then the actual application that started the send/receive, can catch a back or other
-	 * nav event, call stop itself, which will then call the callback again with a "we're done here"
-	 * message as well.
-	 */
-	 
-	/* TODO: Figure out what mode we're in, and run stop. Though, it might
-	 * not be necessary to actually to know the mode. We should be able to
-	 * just stop?
-	 */
-}
-
-
-struct gb_image *printer_image_buffer_alloc(void)
-{
-	struct gb_image *image = malloc(sizeof(struct gb_image) + PRINT_FULL_SZ);
-	return image;
-}
-
-void printer_image_buffer_free(struct gb_image *image)
-{
-	free(image);
-}

+ 0 - 224
pokemon_trading/lib/flipper-gblink/protocols/printer/printer_receive.c

@@ -1,224 +0,0 @@
-#include <stdint.h>
-
-#include <furi.h>
-
-#include <gblink/include/gblink.h>
-#include "printer_i.h"
-
-#define TAG "printer_receive"
-
-/* XXX: TODO: Double check this */
-#define TIMEOUT_US 1000000
-
-static void printer_reset(struct printer_proto *printer)
-{
-	/* Clear out the current packet data */
-	memset(printer->packet, '\0', sizeof(struct packet));
-
-	printer->image->data_sz = 0;
-	/* This is technically redundant, done for completeness */
-	printer->packet->state = START_L;
-
-	/* Packet timeout start */
-	printer->packet->time = DWT->CYCCNT;
-}
-
-static void byte_callback(void *context, uint8_t val)
-{
-	struct printer_proto *printer = context;
-	struct packet *packet = printer->packet;
-	const uint32_t time_ticks = furi_hal_cortex_instructions_per_microsecond() * TIMEOUT_US;
-	uint8_t data_out = 0x00;
-
-	if ((DWT->CYCCNT - printer->packet->time) > time_ticks)
-		printer_reset(printer);
-
-	/* TODO: flash led? */
-
-	switch (packet->state) {
-	case START_L:
-		if (val == PKT_START_L) {
-			packet->state = START_H;
-			/* Packet timeout restart */
-			packet->time = DWT->CYCCNT;
-			packet->zero_counter = 0;
-		}
-		if (val == 0x00) {
-			packet->zero_counter++;
-			if (packet->zero_counter == 16)
-				printer_reset(printer);
-		}
-		break;
-	case START_H:
-		if (val == PKT_START_H)
-			packet->state = COMMAND;
-		else
-			packet->state = START_L;
-		break;
-	case COMMAND:
-		packet->cmd = val;
-		packet->state = COMPRESS;
-		packet->cksum_calc += val;
-
-		/* We only do a real reset after the packet is completed, however
-		 * we need to clear the status flags at this point.
-		 */
-		if (val == CMD_INIT)
-			packet->status = 0;
-
-		break;
-	case COMPRESS:
-		packet->cksum_calc += val;
-		packet->state = LEN_L;
-		if (val) {
-			FURI_LOG_E(TAG, "Compression not supported!");
-			packet->status |= STATUS_PKT_ERR;
-		}
-		break;
-	case LEN_L:
-		packet->cksum_calc += val;
-		packet->state = LEN_H;
-		packet->len = (val & 0xff);
-		break;
-	case LEN_H:
-		packet->cksum_calc += val;
-		packet->len |= ((val & 0xff) << 8);
-		/* Override length for a TRANSFER */
-		if (packet->cmd == CMD_TRANSFER)
-			packet->len = TRANSFER_RECV_SZ;
-
-		if (packet->len) {
-			packet->state = COMMAND_DAT;
-		} else {
-			packet->state = CKSUM_L;
-		}
-		break;
-	case COMMAND_DAT:
-		packet->cksum_calc += val;
-		packet->recv_data[packet->recv_data_sz] = val;
-		packet->recv_data_sz++;
-		if (packet->recv_data_sz == packet->len) 
-			packet->state = CKSUM_L;
-		break;
-	case CKSUM_L:
-		packet->state = CKSUM_H;
-		packet->cksum = (val & 0xff);
-		break;
-	case CKSUM_H:
-		packet->state = ALIVE;
-		packet->cksum |= ((val & 0xff) << 8);
-		if (packet->cksum != packet->cksum_calc)
-			packet->status |= STATUS_CKSUM;
-		// TRANSFER does not set checksum bytes
-		if (packet->cmd == CMD_TRANSFER)
-			packet->status &= ~STATUS_CKSUM;
-		data_out = PRINTER_ID;
-		break;
-	case ALIVE:
-		packet->state = STATUS;
-		data_out = packet->status;
-		break;
-	case STATUS:
-		packet->state = START_L;
-		switch (packet->cmd) {
-		case CMD_INIT:
-			printer_reset(printer);
-			break;
-		case CMD_DATA:
-			if (printer->image->data_sz < PRINT_FULL_SZ) {
-				if ((printer->image->data_sz + packet->len) <= PRINT_FULL_SZ) {
-					memcpy((printer->image->data)+printer->image->data_sz, packet->recv_data, packet->len);
-					printer->image->data_sz += packet->len;
-				} else {
-					memcpy((printer->image->data)+printer->image->data_sz, packet->recv_data, ((printer->image->data_sz + packet->len)) - PRINT_FULL_SZ);
-					printer->image->data_sz += (PRINT_FULL_SZ - (printer->image->data_sz + packet->len));
-					furi_assert(printer->image->data_sz <= PRINT_FULL_SZ);
-				}
-			}
-			if (printer->image->data_sz == PRINT_FULL_SZ)
-				packet->status |= STATUS_READY;
-
-			furi_thread_flags_set(printer->thread, THREAD_FLAGS_DATA);
-			break;
-		case CMD_TRANSFER:
-			/* XXX: TODO: Check to see if we're still printing when getting
-			 * a transfer command. If so, then we have failed to beat the clock.
-			 */
-		case CMD_PRINT:
-			packet->status &= ~STATUS_READY;
-			packet->status |= (STATUS_PRINTING | STATUS_FULL);
-			furi_thread_flags_set(printer->thread, THREAD_FLAGS_PRINT);
-			break;
-		case CMD_STATUS:
-			/* READY cleared on status request */
-			packet->status &= ~STATUS_READY;
-			if ((packet->status & STATUS_PRINTING) &&
-			    (packet->status & STATUS_PRINTED)) {
-				packet->status &= ~(STATUS_PRINTING | STATUS_PRINTED);
-				furi_thread_flags_set(printer->thread, THREAD_FLAGS_COMPLETE);
-			}
-		}
-
-		packet->recv_data_sz = 0;
-		packet->cksum_calc = 0;
-
-
-		/* XXX: TODO: if the command had something we need to do, do it here. */
-		/* done! flush our buffers, deal with any status changes like
-		 * not printing -> printing -> not printing, etc.
-		 */
-		/* Do a callback here?
-		 * if so, I guess we should wait for callback completion before accepting more recv_data?
-		 * but that means the callback is in an interrupt context, which, is probably okay?
-		 */
-		/* XXX: TODO: NOTE: FIXME:
-		 * all of the notes..
-		 * This module needs to maintain the whole buffer, but it can be safely assumed that the buffer
-		 * will never exceed 20x18 tiles (no clue how many bytes) as that is the max the printer can
-		 * take on in a single print. Printing mulitples needs a print, and then a second print with
-		 * no margin. So the margins are important and need to be passed to the final application,
-		 * SOMEHOW.
-		 *
-		 * More imporatntly, is the completed callback NEEDS to have a return value. This allows
-		 * the end application to take that whole panel, however its laid out, and do whatever
-		 * it wants to do with it. Write it to a file, convert, etc., etc., so that this module
-		 * will forever return that it is printing until the callback returns true.
-		 *
-		 * Once we call the callback and it shows a true, then we can be sure the higher module
-		 * is done with the buffer, and we can tell the host that yes, its done, you can continue
-		 * if you want.
-		 */
-		/* XXX: On TRANSFER, there is no checking of status, it is only two packets in total.
-		 * I can assume that if we delay a bit in moving the buffer around that should be okay
-		 * but we probably don't want to wait too long.
-		 * Upon testing, transfer seems to doesn't 
-		 */
-		break;
-	default:
-		FURI_LOG_E(TAG, "unknown status!");
-		break;
-	}
-
-	/* transfer next byte */
-	gblink_transfer(printer->gblink_handle, data_out);
-}
-
-void printer_receive_start(void *printer_handle)
-{
-	struct printer_proto *printer = printer_handle;
-
-	/* Set up defaults the receive path needs */
-	gblink_callback_set(printer->gblink_handle, byte_callback, printer);
-	gblink_clk_source_set(printer->gblink_handle, GBLINK_CLK_EXT);
-
-	printer_reset(printer);
-
-	gblink_start(printer->gblink_handle);
-}
-
-void printer_receive_print_complete(void *printer_handle)
-{
-	struct printer_proto *printer = printer_handle;
-
-	printer->packet->status &= ~STATUS_PRINTING;
-}