printer_receive.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. // SPDX-License-Identifier: BSD-2-Clause
  2. // Copyright (c) 2024 KBEmbedded
  3. #include <stdint.h>
  4. #include <furi.h>
  5. #include <gblink/include/gblink.h>
  6. #include "printer_i.h"
  7. #define TAG "printer_receive"
  8. /* NOTE:
  9. * These numbers are empirically gathered from a few different games thus far.
  10. * There are many notes floating around the internet of the GB Printer having
  11. * a 100 ms limit between packets where it will reset. However, I've seen
  12. * Pokemon Pinball wait 99.5 ms between packets after a print command which is
  13. * a bit too close for comfort. As this code tracks timestamps _after_ each byte,
  14. * that ends up just over 110 ms which trips the hard timeout and resets state.
  15. * This often cofuses the hell out of games.
  16. *
  17. * Additionally, on the other end of the spectrum, Pokemon Gold absolutely uses
  18. * the hard timeout to reset the printer between packets. It waits ~278 ms, when
  19. * it could just send an init command.
  20. *
  21. * Even more silly, Pokemon Pinball has a fun quirk where if the print completes
  22. * immediately (usually the Flipper will mark a print complete with a single
  23. * packet turnaround), it asks for status a couple of times, then starts (presumably)
  24. * another status packet, but the second byte in the transfer is stopped mid-byte.
  25. * Between that point and the start of the next, real packet, is 30 ms, 32.6 ms
  26. * if you go from end of last byte received to start of next, real packet.
  27. *
  28. * This means there is some "soft" timeout that the printer uses to reset a packet
  29. * transfer in progress, but don't reset the whole printer state.
  30. *
  31. * There are wisps of some "unknown" bit timeout of 1.49 ms. But I've not yet
  32. * seen that in action.
  33. *
  34. * As far as I know, no one has dumped and reverse engineered the ROM of the
  35. * Game Boy Printer directly. I think all of the existing documentation was from
  36. * reverse engineering the communication channel. Maybe someday I'll dump the
  37. * GB Printer ROM and try to better understand all of it.
  38. */
  39. #define HARD_TIMEOUT_US 125000
  40. #define SOFT_TIMEOUT_US 20000
  41. static void printer_reset(struct printer_proto *printer)
  42. {
  43. /* Clear out the current packet data */
  44. memset(printer->packet, '\0', sizeof(struct packet));
  45. printer->image->data_sz = 0;
  46. /* This is technically redundant, done for completeness */
  47. printer->packet->state = START_L;
  48. /* Packet timeout start */
  49. printer->packet->time = DWT->CYCCNT;
  50. }
  51. static void byte_callback(void *context, uint8_t val)
  52. {
  53. struct printer_proto *printer = context;
  54. struct packet *packet = printer->packet;
  55. const uint32_t time_ticks = furi_hal_cortex_instructions_per_microsecond() * HARD_TIMEOUT_US;
  56. uint8_t data_out = 0x00;
  57. if ((DWT->CYCCNT - packet->time) > time_ticks)
  58. printer_reset(printer);
  59. if ((DWT->CYCCNT - packet->time) > furi_hal_cortex_instructions_per_microsecond() * SOFT_TIMEOUT_US)
  60. packet->state = START_L;
  61. /* Packet timeout restart */
  62. packet->time = DWT->CYCCNT;
  63. /* TODO: flash led? */
  64. switch (packet->state) {
  65. case START_L:
  66. if (val == PKT_START_L) {
  67. packet->state = START_H;
  68. packet->zero_counter = 0;
  69. }
  70. if (val == 0x00) {
  71. packet->zero_counter++;
  72. if (packet->zero_counter == 16)
  73. printer_reset(printer);
  74. }
  75. break;
  76. case START_H:
  77. if (val == PKT_START_H)
  78. packet->state = COMMAND;
  79. else
  80. packet->state = START_L;
  81. break;
  82. case COMMAND:
  83. packet->cmd = val;
  84. packet->state = COMPRESS;
  85. packet->cksum_calc += val;
  86. break;
  87. case COMPRESS:
  88. packet->cksum_calc += val;
  89. packet->state = LEN_L;
  90. if (val) {
  91. FURI_LOG_E(TAG, "Compression not supported!");
  92. packet->status |= STATUS_PKT_ERR;
  93. }
  94. break;
  95. case LEN_L:
  96. packet->cksum_calc += val;
  97. packet->state = LEN_H;
  98. packet->len = (val & 0xff);
  99. break;
  100. case LEN_H:
  101. packet->cksum_calc += val;
  102. packet->len |= ((val & 0xff) << 8);
  103. /* Override length for a TRANSFER */
  104. if (packet->cmd == CMD_TRANSFER)
  105. packet->len = TRANSFER_RECV_SZ;
  106. if (packet->len) {
  107. packet->state = COMMAND_DAT;
  108. } else {
  109. packet->state = CKSUM_L;
  110. }
  111. break;
  112. case COMMAND_DAT:
  113. packet->cksum_calc += val;
  114. packet->recv_data[packet->recv_data_sz] = val;
  115. packet->recv_data_sz++;
  116. if (packet->recv_data_sz == packet->len)
  117. packet->state = CKSUM_L;
  118. break;
  119. case CKSUM_L:
  120. packet->state = CKSUM_H;
  121. packet->cksum = (val & 0xff);
  122. break;
  123. case CKSUM_H:
  124. packet->state = ALIVE;
  125. packet->cksum |= ((val & 0xff) << 8);
  126. if (packet->cksum != packet->cksum_calc)
  127. packet->status |= STATUS_CKSUM;
  128. // TRANSFER does not set checksum bytes
  129. if (packet->cmd == CMD_TRANSFER)
  130. packet->status &= ~STATUS_CKSUM;
  131. data_out = PRINTER_ID;
  132. break;
  133. case ALIVE:
  134. packet->state = STATUS;
  135. data_out = packet->status;
  136. break;
  137. case STATUS:
  138. packet->state = START_L;
  139. switch (packet->cmd) {
  140. case CMD_INIT:
  141. printer_reset(printer);
  142. break;
  143. case CMD_DATA:
  144. if (printer->image->data_sz < PRINT_FULL_SZ) {
  145. if ((printer->image->data_sz + packet->len) <= PRINT_FULL_SZ) {
  146. memcpy((printer->image->data)+printer->image->data_sz, packet->recv_data, packet->len);
  147. printer->image->data_sz += packet->len;
  148. } else {
  149. memcpy((printer->image->data)+printer->image->data_sz, packet->recv_data, ((printer->image->data_sz + packet->len)) - PRINT_FULL_SZ);
  150. printer->image->data_sz += (PRINT_FULL_SZ - (printer->image->data_sz + packet->len));
  151. furi_assert(printer->image->data_sz <= PRINT_FULL_SZ);
  152. }
  153. }
  154. /* Any time data is written to the buffer, READY is set */
  155. packet->status |= STATUS_READY;
  156. furi_thread_flags_set(printer->thread, THREAD_FLAGS_DATA);
  157. break;
  158. case CMD_TRANSFER:
  159. /* XXX: TODO: Check to see if we're still printing when getting
  160. * a transfer command. If so, then we have failed to beat the clock.
  161. */
  162. case CMD_PRINT:
  163. /* TODO: Be able to memcpy these */
  164. printer->image->num_sheets = packet->recv_data[0];
  165. printer->image->margins = packet->recv_data[1];
  166. printer->image->palette = packet->recv_data[2];
  167. printer->image->exposure = packet->recv_data[3];
  168. packet->status &= ~STATUS_READY;
  169. packet->status |= (STATUS_PRINTING | STATUS_FULL);
  170. furi_thread_flags_set(printer->thread, THREAD_FLAGS_PRINT);
  171. break;
  172. case CMD_STATUS:
  173. /* READY cleared on status request */
  174. packet->status &= ~STATUS_READY;
  175. if ((packet->status & STATUS_PRINTING) &&
  176. (packet->status & STATUS_PRINTED)) {
  177. packet->status &= ~(STATUS_PRINTING | STATUS_PRINTED);
  178. furi_thread_flags_set(printer->thread, THREAD_FLAGS_COMPLETE);
  179. }
  180. }
  181. packet->recv_data_sz = 0;
  182. packet->cksum_calc = 0;
  183. /* XXX: TODO: if the command had something we need to do, do it here. */
  184. /* done! flush our buffers, deal with any status changes like
  185. * not printing -> printing -> not printing, etc.
  186. */
  187. /* Do a callback here?
  188. * if so, I guess we should wait for callback completion before accepting more recv_data?
  189. * but that means the callback is in an interrupt context, which, is probably okay?
  190. */
  191. /* XXX: TODO: NOTE: FIXME:
  192. * all of the notes..
  193. * This module needs to maintain the whole buffer, but it can be safely assumed that the buffer
  194. * will never exceed 20x18 tiles (no clue how many bytes) as that is the max the printer can
  195. * take on in a single print. Printing mulitples needs a print, and then a second print with
  196. * no margin. So the margins are important and need to be passed to the final application,
  197. * SOMEHOW.
  198. *
  199. * More imporatntly, is the completed callback NEEDS to have a return value. This allows
  200. * the end application to take that whole panel, however its laid out, and do whatever
  201. * it wants to do with it. Write it to a file, convert, etc., etc., so that this module
  202. * will forever return that it is printing until the callback returns true.
  203. *
  204. * Once we call the callback and it shows a true, then we can be sure the higher module
  205. * is done with the buffer, and we can tell the host that yes, its done, you can continue
  206. * if you want.
  207. */
  208. /* XXX: On TRANSFER, there is no checking of status, it is only two packets in total.
  209. * I can assume that if we delay a bit in moving the buffer around that should be okay
  210. * but we probably don't want to wait too long.
  211. * Upon testing, transfer seems to doesn't
  212. */
  213. break;
  214. default:
  215. FURI_LOG_E(TAG, "unknown status!");
  216. break;
  217. }
  218. /* transfer next byte */
  219. gblink_transfer(printer->gblink_handle, data_out);
  220. }
  221. void printer_receive_start(void *printer_handle)
  222. {
  223. struct printer_proto *printer = printer_handle;
  224. /* Set up defaults the receive path needs */
  225. gblink_callback_set(printer->gblink_handle, byte_callback, printer);
  226. gblink_clk_source_set(printer->gblink_handle, GBLINK_CLK_EXT);
  227. printer_reset(printer);
  228. gblink_start(printer->gblink_handle);
  229. }
  230. void printer_receive_print_complete(void *printer_handle)
  231. {
  232. struct printer_proto *printer = printer_handle;
  233. printer->packet->status |= STATUS_PRINTED;
  234. }