stdglue.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. #include "stdglue.h"
  2. #include "check.h"
  3. #include "memmgr.h"
  4. #include <FreeRTOS.h>
  5. #include <task.h>
  6. #include <furi-hal.h>
  7. #include <m-dict.h>
  8. DICT_DEF2(
  9. FuriStdglueCallbackDict,
  10. uint32_t,
  11. M_DEFAULT_OPLIST,
  12. FuriStdglueWriteCallback,
  13. M_PTR_OPLIST)
  14. typedef struct {
  15. osMutexId_t mutex;
  16. FuriStdglueCallbackDict_t global_outputs;
  17. FuriStdglueCallbackDict_t thread_outputs;
  18. } FuriStdglue;
  19. static FuriStdglue* furi_stdglue = NULL;
  20. static ssize_t stdout_write(void* _cookie, const char* data, size_t size) {
  21. furi_assert(furi_stdglue);
  22. bool consumed = false;
  23. osKernelState_t state = osKernelGetState();
  24. osThreadId_t thread_id = osThreadGetId();
  25. if(state == osKernelRunning && thread_id &&
  26. osMutexAcquire(furi_stdglue->mutex, osWaitForever) == osOK) {
  27. // We are in the thread context
  28. // Handle global callbacks
  29. FuriStdglueCallbackDict_it_t it;
  30. for(FuriStdglueCallbackDict_it(it, furi_stdglue->global_outputs);
  31. !FuriStdglueCallbackDict_end_p(it);
  32. FuriStdglueCallbackDict_next(it)) {
  33. osThreadId_t it_thread = (osThreadId_t)FuriStdglueCallbackDict_ref(it)->key;
  34. FuriStdglueWriteCallback it_callback = FuriStdglueCallbackDict_ref(it)->value;
  35. if(thread_id != it_thread) {
  36. it_callback(_cookie, data, size);
  37. }
  38. }
  39. // Handle thread callbacks
  40. FuriStdglueWriteCallback* callback_ptr =
  41. FuriStdglueCallbackDict_get(furi_stdglue->thread_outputs, (uint32_t)thread_id);
  42. if(callback_ptr) {
  43. (*callback_ptr)(_cookie, data, size);
  44. consumed = true;
  45. }
  46. furi_check(osMutexRelease(furi_stdglue->mutex) == osOK);
  47. }
  48. // Flush
  49. if(data == 0) {
  50. /*
  51. * This means that we should flush internal buffers. Since we
  52. * don't we just return. (Remember, "handle" == -1 means that all
  53. * handles should be flushed.)
  54. */
  55. return 0;
  56. }
  57. // Debug uart
  58. if(!consumed) furi_hal_console_tx((const uint8_t*)data, size);
  59. // All data consumed
  60. return size;
  61. }
  62. void furi_stdglue_init() {
  63. furi_stdglue = furi_alloc(sizeof(FuriStdglue));
  64. // Init outputs structures
  65. furi_stdglue->mutex = osMutexNew(NULL);
  66. furi_check(furi_stdglue->mutex);
  67. FuriStdglueCallbackDict_init(furi_stdglue->global_outputs);
  68. FuriStdglueCallbackDict_init(furi_stdglue->thread_outputs);
  69. // Prepare and set stdout descriptor
  70. FILE* fp = fopencookie(
  71. NULL,
  72. "w",
  73. (cookie_io_functions_t){
  74. .read = NULL,
  75. .write = stdout_write,
  76. .seek = NULL,
  77. .close = NULL,
  78. });
  79. setvbuf(fp, NULL, _IOLBF, 0);
  80. stdout = fp;
  81. }
  82. bool furi_stdglue_set_global_stdout_callback(FuriStdglueWriteCallback callback) {
  83. furi_assert(furi_stdglue);
  84. osThreadId_t thread_id = osThreadGetId();
  85. if(thread_id) {
  86. furi_check(osMutexAcquire(furi_stdglue->mutex, osWaitForever) == osOK);
  87. if(callback) {
  88. FuriStdglueCallbackDict_set_at(
  89. furi_stdglue->global_outputs, (uint32_t)thread_id, callback);
  90. } else {
  91. FuriStdglueCallbackDict_erase(furi_stdglue->global_outputs, (uint32_t)thread_id);
  92. }
  93. furi_check(osMutexRelease(furi_stdglue->mutex) == osOK);
  94. return true;
  95. } else {
  96. return false;
  97. }
  98. }
  99. bool furi_stdglue_set_thread_stdout_callback(FuriStdglueWriteCallback callback) {
  100. furi_assert(furi_stdglue);
  101. osThreadId_t thread_id = osThreadGetId();
  102. if(thread_id) {
  103. furi_check(osMutexAcquire(furi_stdglue->mutex, osWaitForever) == osOK);
  104. if(callback) {
  105. FuriStdglueCallbackDict_set_at(
  106. furi_stdglue->thread_outputs, (uint32_t)thread_id, callback);
  107. } else {
  108. FuriStdglueCallbackDict_erase(furi_stdglue->thread_outputs, (uint32_t)thread_id);
  109. }
  110. furi_check(osMutexRelease(furi_stdglue->mutex) == osOK);
  111. return true;
  112. } else {
  113. return false;
  114. }
  115. }
  116. void __malloc_lock(struct _reent* REENT) {
  117. vTaskSuspendAll();
  118. }
  119. void __malloc_unlock(struct _reent* REENT) {
  120. xTaskResumeAll();
  121. }