cli_control.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. #include "cli_control.h"
  2. #include <cli/cli.h>
  3. #include <cli/cli_i.h>
  4. #include <cli/cli_vcp.h>
  5. #include "cligui_main_i.h"
  6. #include <FreeRTOS.h>
  7. volatile bool gotCallbackSet = false;
  8. FuriStreamBuffer* tx_stream;
  9. FuriStreamBuffer* rx_stream;
  10. static FuriThread* volatile cliThread = NULL;
  11. static FuriThread* prev_appthread = NULL;
  12. static void tx_handler_stdout(const char* buffer, size_t size) {
  13. furi_stream_buffer_send(tx_stream, buffer, size, FuriWaitForever);
  14. }
  15. static void tx_handler(const uint8_t* buffer, size_t size) {
  16. furi_thread_set_stdout_callback(tx_handler_stdout);
  17. cliThread = furi_thread_get_current();
  18. furi_stream_buffer_send(tx_stream, buffer, size, FuriWaitForever);
  19. }
  20. static size_t real_rx_handler(uint8_t* buffer, size_t size, uint32_t timeout) {
  21. size_t rx_cnt = 0;
  22. while(size > 0) {
  23. size_t batch_size = size;
  24. if(batch_size > 128) batch_size = 128;
  25. size_t len = furi_stream_buffer_receive(rx_stream, buffer, batch_size, timeout);
  26. if(len == 0) break;
  27. size -= len;
  28. buffer += len;
  29. rx_cnt += len;
  30. }
  31. return rx_cnt;
  32. }
  33. static CliCommand_internal* getInternalCliCommand(Cli* cli, const char* name) {
  34. FuriString* target_command = furi_string_alloc();
  35. furi_string_set_str(target_command, name);
  36. CliCommand_internal* command =
  37. CliCommandTree_internal_get(((Cli_internal*)cli)->commands, target_command);
  38. furi_string_free(target_command);
  39. return command;
  40. }
  41. static void session_init(void) {
  42. }
  43. static void session_deinit(void) {
  44. }
  45. static bool session_connected(void) {
  46. return true;
  47. }
  48. static CliSession session;
  49. void latch_tx_handler() {
  50. Cli* global_cli = furi_record_open(RECORD_CLI);
  51. CliCommand_internal* help_command = getInternalCliCommand(global_cli, "help");
  52. cliThread = help_command->context;
  53. furi_thread_set_stdout_callback(tx_handler_stdout);
  54. if(cliThread != NULL) {
  55. ((FuriThread_internal*)cliThread)->output.write_callback = &tx_handler_stdout;
  56. }
  57. rx_stream = furi_stream_buffer_alloc(128, 1);
  58. tx_stream = furi_stream_buffer_alloc(128, 1);
  59. session.tx = &tx_handler;
  60. session.rx = &real_rx_handler;
  61. session.tx_stdout = &tx_handler_stdout;
  62. session.init = &session_init;
  63. session.deinit = &session_deinit;
  64. session.is_connected = &session_connected;
  65. cli_session_close(global_cli);
  66. cli_session_open(global_cli, &session);
  67. // Unlock loader-lock
  68. Loader* loader = furi_record_open(RECORD_LOADER);
  69. Loader_internal* loader_i = (Loader_internal*)loader;
  70. prev_appthread = loader_i->app.thread;
  71. loader_i->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_internal* help_command = getInternalCliCommand(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. ((FuriThread_internal*)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_internal* loader_i = (Loader_internal*)loader;
  105. loader_i->app.thread = prev_appthread;
  106. furi_record_close(RECORD_LOADER);
  107. }