printer_proto.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. // SPDX-License-Identifier: BSD-2-Clause
  2. // Copyright (c) 2024 KBEmbedded
  3. #include <furi.h>
  4. #include <gblink/include/gblink.h>
  5. #include <gblink/include/gblink_pinconf.h>
  6. #include <protocols/printer/include/printer_proto.h>
  7. #include "printer_i.h"
  8. /* XXX: Does this make sense to be a message dispatcher rather than calling callbacks?
  9. * In order to keep the stack small for the thread, need to be weary of all calls made from here. */
  10. /* XXX TODO Test using a timer pending callback instead of this */
  11. /* XXX: TODO: Create a more streamlined callback that can simply pass a struct that has
  12. * pointers to data, sz, reason, margins (aka is there more data coming), etc., could even place
  13. * the callback context in there which would allow using the timer pending callback function
  14. */
  15. static int32_t printer_callback_thread(void *context)
  16. {
  17. struct printer_proto *printer = context;
  18. uint32_t flags;
  19. while (1) {
  20. /* XXX: TODO: align flags and enum cb_reason to share them */
  21. flags = furi_thread_flags_wait(THREAD_FLAGS_ALL, FuriFlagWaitAny, FuriWaitForever);
  22. furi_check(!(flags & FuriFlagError));
  23. if (flags & THREAD_FLAGS_EXIT)
  24. break;
  25. if (flags & THREAD_FLAGS_DATA)
  26. printer->callback(printer->cb_context, printer->image, reason_line_xfer);
  27. if (flags & THREAD_FLAGS_PRINT)
  28. printer->callback(printer->cb_context, printer->image, reason_print);
  29. }
  30. return 0;
  31. }
  32. void *printer_alloc(void)
  33. {
  34. struct printer_proto *printer = NULL;
  35. printer = malloc(sizeof(struct printer_proto));
  36. /* Allocate and start callback handling thread */
  37. /* XXX: TODO: The stack can decrease if FURI_LOG calls are removed in callbacks! */
  38. printer->thread = furi_thread_alloc_ex("GBLinkPrinterProtoCB",
  39. 1024,
  40. printer_callback_thread,
  41. printer);
  42. /* Highest priority to ensure it runs ASAP */
  43. furi_thread_set_priority(printer->thread, FuriThreadPriorityHighest);
  44. furi_thread_start(printer->thread);
  45. printer->packet = malloc(sizeof(struct packet));
  46. printer->image = malloc(sizeof(struct gb_image));
  47. printer->gblink_handle = gblink_alloc();
  48. gblink_pinconf_load(printer->gblink_handle);
  49. /* Set up some settings for the print protocol. The final send/receive() calls
  50. * may clobber some of these, but that is intentional and they don't need to
  51. * care about some of the other details that are specified here.
  52. */
  53. /* Reported 1.49 ms timeout between bytes, need confirmation */
  54. gblink_timeout_set(printer->gblink_handle, 1490);
  55. gblink_nobyte_set(printer->gblink_handle, 0x00);
  56. return printer;
  57. }
  58. /* TODO: Allow free() without stop, add a way to check if printer_stop has not
  59. * yet been called.
  60. */
  61. void printer_free(void *printer_handle)
  62. {
  63. struct printer_proto *printer = printer_handle;
  64. furi_thread_flags_set(printer->thread, THREAD_FLAGS_EXIT);
  65. furi_thread_join(printer->thread);
  66. furi_thread_free(printer->thread);
  67. gblink_free(printer->gblink_handle);
  68. free(printer->packet);
  69. free(printer->image);
  70. free(printer);
  71. }
  72. void printer_callback_context_set(void *printer_handle, void *context)
  73. {
  74. struct printer_proto *printer = printer_handle;
  75. printer->cb_context = context;
  76. }
  77. void printer_callback_set(void *printer_handle, void (*callback)(void *context, struct gb_image *image, enum cb_reason reason))
  78. {
  79. struct printer_proto *printer = printer_handle;
  80. printer->callback = callback;
  81. }
  82. void *printer_gblink_handle_get(void *printer_handle)
  83. {
  84. struct printer_proto *printer = printer_handle;
  85. return printer->gblink_handle;
  86. }
  87. void printer_stop(void *printer_handle)
  88. {
  89. struct printer_proto *printer = printer_handle;
  90. gblink_stop(printer->gblink_handle);
  91. /* TODO: Call the callback one last time with a flag to indicate that the transfer has completely
  92. * ended.
  93. * Receive/send should also have a separate timeout, doesn't need to call stop, but, will
  94. * also retrigger the complete callback. This allows for both the actual process to signal
  95. * there was a gap (I think the gameboy print normally has a "I'm done" marker as well),
  96. * and then the actual application that started the send/receive, can catch a back or other
  97. * nav event, call stop itself, which will then call the callback again with a "we're done here"
  98. * message as well.
  99. */
  100. /* TODO: Figure out what mode we're in, and run stop. Though, it might
  101. * not be necessary to actually to know the mode. We should be able to
  102. * just stop?
  103. */
  104. }