stdglue.c 4.1 KB

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