cli_control.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. #include "cli_control.h"
  2. #include <cli/cli.h>
  3. #include <cli/cli_i.h>
  4. #include <cli/cli_vcp.h>
  5. #include <furi/core/thread_i.h>
  6. #include "cligui_main_i.h"
  7. #include <loader/loader_i.h>
  8. #include <FreeRTOS.h>
  9. volatile bool gotCallbackSet = false;
  10. FuriStreamBuffer* tx_stream;
  11. FuriStreamBuffer* rx_stream;
  12. static FuriThread* volatile cliThread = NULL;
  13. static FuriThread* prev_appthread = NULL;
  14. static void tx_handler_stdout(const char* buffer, size_t size) {
  15. furi_stream_buffer_send(tx_stream, buffer, size, FuriWaitForever);
  16. }
  17. static void tx_handler(const uint8_t* buffer, size_t size) {
  18. furi_thread_set_stdout_callback(tx_handler_stdout);
  19. cliThread = furi_thread_get_current();
  20. furi_stream_buffer_send(tx_stream, buffer, size, FuriWaitForever);
  21. }
  22. static size_t real_rx_handler(uint8_t* buffer, size_t size, uint32_t timeout) {
  23. size_t rx_cnt = 0;
  24. while(size > 0) {
  25. size_t batch_size = size;
  26. if(batch_size > 128) batch_size = 128;
  27. size_t len = furi_stream_buffer_receive(rx_stream, buffer, batch_size, timeout);
  28. if(len == 0) break;
  29. size -= len;
  30. buffer += len;
  31. rx_cnt += len;
  32. }
  33. return rx_cnt;
  34. }
  35. static CliCommand* getCliCommand(Cli* cli, const char* name) {
  36. FuriString* target_command = furi_string_alloc();
  37. furi_string_set_str(target_command, name);
  38. CliCommand* command = CliCommandTree_get(cli->commands, target_command);
  39. furi_string_free(target_command);
  40. return command;
  41. }
  42. static void session_init(void) {
  43. }
  44. static void session_deinit(void) {
  45. }
  46. static bool session_connected(void) {
  47. return true;
  48. }
  49. static CliSession session;
  50. void latch_tx_handler() {
  51. Cli* global_cli = furi_record_open(RECORD_CLI);
  52. CliCommand* help_command = getCliCommand(global_cli, "help");
  53. cliThread = help_command->context;
  54. furi_thread_set_stdout_callback(tx_handler_stdout);
  55. if(cliThread != NULL) {
  56. cliThread->output.write_callback = &tx_handler_stdout;
  57. }
  58. rx_stream = furi_stream_buffer_alloc(128, 1);
  59. tx_stream = furi_stream_buffer_alloc(128, 1);
  60. session.tx = &tx_handler;
  61. session.rx = &real_rx_handler;
  62. session.tx_stdout = &tx_handler_stdout;
  63. session.init = &session_init;
  64. session.deinit = &session_deinit;
  65. session.is_connected = &session_connected;
  66. cli_session_close(global_cli);
  67. cli_session_open(global_cli, &session);
  68. // Unlock loader-lock
  69. Loader* loader = furi_record_open(RECORD_LOADER);
  70. prev_appthread = loader->app.thread;
  71. loader->app.thread = NULL;
  72. furi_record_close(RECORD_LOADER);
  73. furi_record_close(RECORD_CLI);
  74. }
  75. void unlatch_tx_handler(bool persist) {
  76. Cli* global_cli = furi_record_open(RECORD_CLI);
  77. // Stash cliThread if not null
  78. if(cliThread != NULL) {
  79. CliCommand* help_command = getCliCommand(global_cli, "help");
  80. help_command->context = cliThread;
  81. }
  82. // Switch to new session
  83. if(persist) {
  84. // Use dummy debug firmware function as is_connected
  85. cli_vcp.is_connected = &furi_hal_version_do_i_belong_here;
  86. } else {
  87. // Send CTRL-C
  88. char eot = 0x03;
  89. furi_stream_buffer_send(rx_stream, &eot, 1, FuriWaitForever);
  90. }
  91. cli_session_open(global_cli, &cli_vcp);
  92. furi_record_close(RECORD_CLI);
  93. // Unblock waiting rx handler
  94. furi_stream_buffer_send(rx_stream, "_", 1, FuriWaitForever);
  95. // Reconfigure stdout_callback to cli_vcp
  96. if(cliThread != NULL) {
  97. cliThread->output.write_callback = cli_vcp.tx_stdout;
  98. }
  99. // At this point, all cli_vcp functions should be back.
  100. furi_stream_buffer_free(rx_stream);
  101. furi_stream_buffer_free(tx_stream);
  102. // Re-lock loader (to avoid crash on automatic unlock)
  103. Loader* loader = furi_record_open(RECORD_LOADER);
  104. loader->app.thread = prev_appthread;
  105. furi_record_close(RECORD_LOADER);
  106. }