gblink.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. // SPDX-License-Identifier: BSD-2-Clause
  2. // Copyright (c) 2023 KBEmbedded
  3. #include <furi.h>
  4. #include <furi_hal.h>
  5. #include <stm32wbxx_ll_exti.h>
  6. #include <stm32wbxx_ll_system.h>
  7. #include <stdint.h>
  8. #include "gblink.h"
  9. const struct gblink_pins common_pinouts[PINOUT_COUNT] = {
  10. /* Original */
  11. {
  12. &gpio_ext_pc3,
  13. &gpio_ext_pb3,
  14. &gpio_ext_pb2,
  15. &gpio_ext_pa4,
  16. },
  17. /* MALVEKE EXT1 */
  18. {
  19. &gpio_ext_pa6,
  20. &gpio_ext_pa7,
  21. &gpio_ext_pb3,
  22. &gpio_ext_pa4,
  23. },
  24. };
  25. struct gblink {
  26. const GpioPin *serin;
  27. const GpioPin *serout;
  28. const GpioPin *clk;
  29. const GpioPin *sd;
  30. uint8_t in;
  31. uint8_t out;
  32. uint8_t out_buf;
  33. bool out_buf_valid;
  34. uint8_t shift;
  35. uint8_t nobyte;
  36. gblink_clk_source source;
  37. gblink_mode mode;
  38. gblink_speed speed;
  39. uint32_t time;
  40. uint32_t bitclk_timeout_us;
  41. /* Clocks idle between bytes is nominally 430 us long for burst data,
  42. * 15 ms for idle polling (e.g. waiting for menu selection), some oddball
  43. * 2 ms gaps that appears between one 0xFE byte from the Game Boy every trade;
  44. * clock period is nominally 122 us.
  45. * Therefore, if we haven't seen a clock in 500 us, reset our bit counter.
  46. * Note that, this should never actually be a concern, but it is an additional
  47. * safeguard against desyncing.
  48. */
  49. void (*callback)(void* cb_context, uint8_t in);
  50. void *cb_context;
  51. uint32_t* ivt_mirror;
  52. uint32_t ivt_mirror_offs;
  53. bool exti3_rise_enable;
  54. bool exti3_fall_enable;
  55. bool exti3_event_enable;
  56. };
  57. static void gblink_shift_in(struct gblink *gblink)
  58. {
  59. const uint32_t time_ticks = furi_hal_cortex_instructions_per_microsecond() * gblink->bitclk_timeout_us;
  60. /* If we exceeded the bit clock timeout, reset all counters */
  61. if ((DWT->CYCCNT - gblink->time) > time_ticks) {
  62. gblink->in = 0;
  63. gblink->shift = 0;
  64. }
  65. gblink->time = DWT->CYCCNT;
  66. gblink->in <<= 1;
  67. gblink->in |= furi_hal_gpio_read(gblink->serin);
  68. gblink->shift++;
  69. /* If 8 bits transfered, reset shift counter, call registered
  70. * callback, re-set nobyte in output buffer.
  71. */
  72. if (gblink->shift == 8) {
  73. gblink->shift = 0;
  74. /* Set up next out byte before calling the callback.
  75. * This is in case the callback itself sets a new out
  76. * byte which it will in most cases. It is up to the
  77. * main application at this time to ensure that
  78. * gblink_transfer() isn't called multiple times before
  79. * a byte has a chance to be sent out.
  80. */
  81. if (gblink->out_buf_valid) {
  82. gblink->out = gblink->out_buf;
  83. gblink->out_buf_valid = false;
  84. } else {
  85. gblink->out = gblink->nobyte;
  86. }
  87. gblink->callback(gblink->cb_context, gblink->in);
  88. }
  89. }
  90. static void gblink_shift_out(struct gblink *gblink)
  91. {
  92. furi_hal_gpio_write(gblink->serout, !!(gblink->out & 0x80));
  93. gblink->out <<= 1;
  94. }
  95. static void gblink_clk_isr(void *context)
  96. {
  97. furi_assert(context);
  98. struct gblink *gblink = context;
  99. if (furi_hal_gpio_read(gblink->clk)) {
  100. /* Posedge Shift in data */
  101. gblink_shift_in(gblink);
  102. } else {
  103. /* Negedge shift out data */
  104. gblink_shift_out(gblink);
  105. }
  106. }
  107. /* NOTE WELL! This function is absurdly hacky and a stupid workaround to a
  108. * stupid issue that doesn't really have any other solution in the current
  109. * Flipper/FURI API. I'm over-commenting this so we know exactly what is going
  110. * on if we ever have to re-visit this mess.
  111. *
  112. * This block of text below describes the overall idea, more specific comments
  113. * in the function body.
  114. *
  115. * TODO: make this more generic for any other GPIOs that might conflict with
  116. * exti interrupts. PA6, PB3, PC3, PB2? (NFC), PA13, PB6
  117. * NOTE: This is only set up at the moment for PB3, hardcoded
  118. *
  119. * There are multiple problems that this workaround is handling. EXTI interrupts
  120. * are shared among multiple pins. The FURI core maintains per-pin ISRs in a
  121. * private struct that has no way to read, save, or otherwise be able to put
  122. * back the ISR that would service a conflicting EXTI. e.g. PB3 and PH3
  123. * (the OK button) both share EXTI3. Setting an interrupt on PB3 will clobber
  124. * the FURI ISR callback/context pair as well as change EXTI3 to use PB3 as
  125. * the interrupt source.
  126. *
  127. * To make an interrupt work correctly on PB3 and not break the OK button
  128. * we need a way to set an interrupt for PB3 in a way that doesn't clobber the
  129. * private FURI GPIO ISR handles and can let the interrupt for the OK button
  130. * work again when we're done.
  131. *
  132. * The general concept of this workaround is to modify the IVT to create our
  133. * own handler for EXTI3 interrupts. Doing this leaves the aforementioned private
  134. * GPIO struct unmodified and disables the OK button from triggering an interrupt.
  135. * The IVT is normally located at the lowest addresses of flash (which is located
  136. * at 0x08000000 and mapped at runtime to 0x00000000); this means the IVT cannot
  137. * be changed at runtime.
  138. *
  139. * To make this work, we use the Vector Table Offset Register (VTOR) in the
  140. * System Control Block (SCB). The VTOR allows for changing the location of the
  141. * IVT. We copy the IVT to a location in memory, and then do a dance to safely
  142. * set up the GPIO interrupt to PB3, and swap in our IVT with the modified EXTI3
  143. * handler.
  144. *
  145. * When undoing this, the process is not quite in reverse as we have to put back
  146. * specific interrupt settings that we very likely would have clobbered but have
  147. * the ability to save beforehand.
  148. *
  149. * Wrapping the steps in disabling the EXTI3 interrupt is probably not needed,
  150. * but is a precaution since we are changing the interrupt sources in weird ways.
  151. */
  152. /* Used to map our callback context in a way the handler can access */
  153. static void *exti3_cb_context;
  154. static void gblink_exti3_IRQHandler(void) {
  155. if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_3)) {
  156. gblink_clk_isr(exti3_cb_context);
  157. LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_3);
  158. }
  159. }
  160. static void gblink_gross_exti_workaround(struct gblink *gblink)
  161. {
  162. /* This process makes a number of assumptions, including that the IVT
  163. * is located at 0x00000000, that the lowest flash page is mapped to
  164. * that base address, and that the VTOR points to 0x00000000.
  165. * There are runtime protections in place to prevent reading from the
  166. * first 1 MB of addresses. So we have to always assume that the lowest
  167. * page of flash is mapped to 0x00000000 and read the IVT from the that
  168. * page in flash directly.
  169. * The only check we can really do here is ensuring VTOR is 0 and that
  170. * Main memory is mapped to 0x00000000. If either of those are not true,
  171. * then we can't continue.
  172. */
  173. furi_check(SCB->VTOR == 0x0);
  174. furi_check(LL_SYSCFG_GetRemapMemory() == LL_SYSCFG_REMAP_FLASH);
  175. /* Create a mirror of the existing IVT from CPU 1
  176. * The IVT on this platform has 79 entries; 63 maskable, 10 non-maskable,
  177. * 6 reserved. The maskable interrupts start at offset 16.
  178. * CMSIS documentation says that the boundary for IVT must be aligned to
  179. * the number of interrupts, rounded up to the nearest power of two, and
  180. * then multiplied by the word width of the CPU. 79 rounds up to 128
  181. * with a word width of 4, this is 512/0x200 bytes.
  182. * As there is no good way with FreeRTOS to request an alloc at an
  183. * aligned boundary, allocate the amount of data we need, plus 0x200
  184. * bytes, to guarantee that we can put the table in a location that is
  185. * properly aligned. Once we find a suitable base address, this offset
  186. * is saved for later.
  187. */
  188. gblink->ivt_mirror = malloc((79 * sizeof(uint32_t)) + 0x200);
  189. gblink->ivt_mirror_offs = (uint32_t)gblink->ivt_mirror;
  190. while (gblink->ivt_mirror_offs & 0x1FF)
  191. gblink->ivt_mirror_offs++;
  192. /* 0x08000000 is used instead of 0x00000000 because everything complains
  193. * using a NULL pointer.
  194. */
  195. memcpy((uint32_t *)gblink->ivt_mirror_offs, ((uint32_t *)0x08000000), 79 * sizeof(uint32_t));
  196. /* Point our IVT's EXTI3 interrupt to our desired interrupt handler.
  197. * Also copy the gblink struct to the global var that the interrupt
  198. * handler will use to make further calls.
  199. */
  200. ((uint32_t *)gblink->ivt_mirror_offs)[25] = (uint32_t)gblink_exti3_IRQHandler; // 16 NMI + offset of 9 for EXTI3
  201. exti3_cb_context = gblink;
  202. /* Disable the EXTI3 interrupt. This lets us do bad things without
  203. * fear of an IRQ hitting in the middle.
  204. */
  205. LL_EXTI_DisableIT_0_31(LL_EXTI_LINE_3);
  206. /* Save the existing rise/fall trigger settings. In theory, these should
  207. * really never change through the life of the flipper OS. But for safety
  208. * we always save them rather than just blindly restoring the same settings
  209. * back when we undo this later.
  210. */
  211. gblink->exti3_rise_enable = LL_EXTI_IsEnabledRisingTrig_0_31(LL_EXTI_LINE_3);
  212. gblink->exti3_fall_enable = LL_EXTI_IsEnabledFallingTrig_0_31(LL_EXTI_LINE_3);
  213. gblink->exti3_event_enable = LL_EXTI_IsEnabledEvent_0_31(LL_EXTI_LINE_3);
  214. /* Now, set up our desired pin settings. This will only clobber exti3
  215. * settings and will not affect the actual interrupt vector address.
  216. * Settings include the rising/falling/event triggers which we just
  217. * saved.
  218. */
  219. furi_hal_gpio_init(gblink->clk, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedVeryHigh);
  220. /* Update the NVIC table to point at our desired table.
  221. * Out of safety, stop the world around changing the VTOR reg.
  222. */
  223. FURI_CRITICAL_ENTER();
  224. SCB->VTOR = gblink->ivt_mirror_offs;
  225. FURI_CRITICAL_EXIT();
  226. /* Last, enable the interrupts and hope everything works. */
  227. LL_EXTI_EnableIT_0_31(LL_EXTI_LINE_3);
  228. }
  229. static void gblink_gross_exti_workaround_undo(struct gblink *gblink)
  230. {
  231. /* First, disable the EXTI3 interrupt. This lets us do bad things without
  232. * fear of an IRQ hitting in the middle.
  233. */
  234. LL_EXTI_DisableIT_0_31(LL_EXTI_LINE_3);
  235. /* Set the correct input source, PH3/OK button, to EXTI3. It is important
  236. * to do this before calling furi_hal_gpio_init() on PB3. When that func
  237. * is called with no interrupt settings enabled, if the EXTI source
  238. * matches the pin, and the interrupt is enabled, interrupts will be
  239. * disabled. By manually setting the EXTI3 source here, it no longer
  240. * matches the PB3 pin, and our changing of IO settings on our GPIO pin
  241. * to no longer have interrupts will not affect the shared IRQ.
  242. */
  243. LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTH, LL_SYSCFG_EXTI_LINE3);
  244. /* Set the correct rise/fall/event settings back */
  245. if (gblink->exti3_rise_enable)
  246. LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_3);
  247. else
  248. LL_EXTI_DisableRisingTrig_0_31(LL_EXTI_LINE_3);
  249. if (gblink->exti3_fall_enable)
  250. LL_EXTI_EnableFallingTrig_0_31(LL_EXTI_LINE_3);
  251. else
  252. LL_EXTI_DisableFallingTrig_0_31(LL_EXTI_LINE_3);
  253. if (gblink->exti3_event_enable)
  254. LL_EXTI_EnableEvent_0_31(LL_EXTI_LINE_3);
  255. else
  256. LL_EXTI_DisableEvent_0_31(LL_EXTI_LINE_3);
  257. /* "Release" the GPIO by putting it back in a known idle state. */
  258. furi_hal_gpio_init_simple(gblink->clk, GpioModeAnalog);
  259. /* Set the IVT back to the normal, in-flash table. Stopping the world
  260. * while we do so.
  261. * NOTE: This just assumes the VTOR is always at 0x0 by default, if this
  262. * ever changes in the Flipper OS, then that will be a problem.
  263. */
  264. FURI_CRITICAL_ENTER();
  265. SCB->VTOR = 0x0;
  266. FURI_CRITICAL_EXIT();
  267. /* Re-enable the interrupt, OK button should work again. */
  268. LL_EXTI_EnableIT_0_31(LL_EXTI_LINE_3);
  269. /* Free the alloc()ed mirror space */
  270. free(gblink->ivt_mirror);
  271. }
  272. void gblink_clk_source_set(void *handle, int source)
  273. {
  274. furi_assert(handle);
  275. struct gblink *gblink = handle;
  276. gblink->source = source;
  277. gblink->shift = 0;
  278. }
  279. void gblink_speed_set(void *handle, gblink_speed speed)
  280. {
  281. furi_assert(handle);
  282. struct gblink *gblink = handle;
  283. gblink->speed = speed;
  284. }
  285. /* default is set to 500 us */
  286. void gblink_timeout_set(void *handle, uint32_t us)
  287. {
  288. furi_assert(handle);
  289. struct gblink *gblink = handle;
  290. gblink->bitclk_timeout_us = us;
  291. }
  292. void gblink_transfer(void *handle, uint8_t val)
  293. {
  294. furi_assert(handle);
  295. struct gblink *gblink = handle;
  296. /* This checks the value of gblink->shift which can change in the ISR.
  297. * Because of that, disable interrupts when checking gblink->shift and
  298. * setting gblink->out_buf_valid
  299. * If shift is 0, we're between bytes and can safely set the out byte.
  300. * If shift is nonzero, a byte is currently being transmitted. Set the
  301. * out_buf and set out_buf_valid. When the ISR is finished writing the
  302. * next byte it will check out_buf_valid and copy in out_buf.
  303. *
  304. * The correct/smart way of doing this would be a mutex rather than
  305. * stopping the world.
  306. *
  307. * Realistically, this should only ever be called from the transfer
  308. * complete callback. There are few situations outside of that which
  309. * would make sense.
  310. *
  311. * Note that, this currently has no checks for if there is data already
  312. * pending to be transmitted. Calling this back to back can cause data
  313. * loss!
  314. */
  315. FURI_CRITICAL_ENTER();
  316. if (gblink->shift == 0) {
  317. gblink->out = val;
  318. gblink->out_buf_valid = false;
  319. } else {
  320. gblink->out_buf = val;
  321. gblink->out_buf_valid = true;
  322. }
  323. FURI_CRITICAL_EXIT();
  324. }
  325. void gblink_nobyte_set(void *handle, uint8_t val)
  326. {
  327. struct gblink *gblink = handle;
  328. gblink->nobyte = val;
  329. }
  330. void gblink_int_enable(void *handle)
  331. {
  332. furi_assert(handle);
  333. struct gblink *gblink = handle;
  334. furi_hal_gpio_enable_int_callback(gblink->clk);
  335. }
  336. void gblink_int_disable(void *handle)
  337. {
  338. furi_assert(handle);
  339. struct gblink *gblink = handle;
  340. furi_hal_gpio_disable_int_callback(gblink->clk);
  341. }
  342. void *gblink_alloc(struct gblink_def *gblink_def)
  343. {
  344. struct gblink *gblink;
  345. /* Allocate and zero struct */
  346. gblink = malloc(sizeof(struct gblink));
  347. /* Set struct values from function args */
  348. gblink->serin = gblink_def->pins->serin;
  349. gblink->serout = gblink_def->pins->serout;
  350. gblink->clk = gblink_def->pins->clk;
  351. gblink->sd = gblink_def->pins->sd;
  352. gblink->source = gblink_def->source;
  353. gblink->speed = GBLINK_SPD_8192HZ;
  354. /* Set up timeout variables */
  355. gblink->bitclk_timeout_us = 500;
  356. gblink->time = DWT->CYCCNT;
  357. /* Set up secondary callback */
  358. gblink->callback = gblink_def->callback;
  359. gblink->cb_context = gblink_def->cb_context;
  360. /* Set up pins */
  361. /* TODO: Set up a list of pins that are not safe to use with interrupts.
  362. * I do believe the main FURI GPIO struct has this data baked in so that
  363. * could be used. For now though, we're only checking for the MALVEKE
  364. * pinout which uses a clk pin that has its IRQ shared with the Okay
  365. * button.
  366. * See the work done in pokemon trade tool custom pinout selection for
  367. * an idea of how to check all that.
  368. */
  369. /* TODO: Currently assumes external clock source only */
  370. /* XXX: This might actually be open-drain on real GB hardware */
  371. furi_hal_gpio_write(gblink->serout, false);
  372. furi_hal_gpio_init(gblink->serout, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  373. furi_hal_gpio_write(gblink->serin, false);
  374. furi_hal_gpio_init(gblink->serin, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
  375. /* Set up interrupt on clock */
  376. if (gblink->clk == &gpio_ext_pb3) {
  377. /* The clock pin is on a pin that is not safe to set an interrupt
  378. * on, so we do a gross workaround to get an interrupt enabled
  379. * on that pin in a way that can be undone safely later with
  380. * no impact to the shared IRQ.
  381. */
  382. gblink_gross_exti_workaround(gblink);
  383. } else {
  384. furi_hal_gpio_init(gblink->clk, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedVeryHigh);
  385. /* This may not be needed after NFC refactor */
  386. furi_hal_gpio_remove_int_callback(gblink->clk);
  387. furi_hal_gpio_add_int_callback(gblink->clk, gblink_clk_isr, gblink);
  388. }
  389. return gblink;
  390. }
  391. void gblink_free(void *handle)
  392. {
  393. furi_assert(handle);
  394. struct gblink *gblink = handle;
  395. if (gblink->clk == &gpio_ext_pb3) {
  396. /* This handles switching the IVT back and putting the EXTI
  397. * regs and pin regs in a valid state for normal use.
  398. */
  399. gblink_gross_exti_workaround_undo(gblink);
  400. } else {
  401. /* Remove interrupt, set IO to sane state */
  402. furi_hal_gpio_remove_int_callback(gblink->clk);
  403. }
  404. furi_hal_gpio_init_simple(gblink->serin, GpioModeAnalog);
  405. furi_hal_gpio_init_simple(gblink->serout, GpioModeAnalog);
  406. furi_hal_gpio_init_simple(gblink->clk, GpioModeAnalog);
  407. free(gblink);
  408. }