cli_control.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  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 <FreeRTOS.h>
  8. volatile bool gotCallbackSet = false;
  9. FuriStreamBuffer* tx_stream;
  10. FuriStreamBuffer* rx_stream;
  11. static FuriThread* volatile cliThread = NULL;
  12. static FuriThread* prev_appthread = NULL;
  13. static void tx_handler_stdout(const char* buffer, size_t size) {
  14. furi_stream_buffer_send(tx_stream, buffer, size, FuriWaitForever);
  15. }
  16. static void tx_handler(const uint8_t* buffer, size_t size) {
  17. furi_thread_set_stdout_callback(tx_handler_stdout);
  18. cliThread = furi_thread_get_current();
  19. furi_stream_buffer_send(tx_stream, buffer, size, FuriWaitForever);
  20. }
  21. static size_t real_rx_handler(uint8_t* buffer, size_t size, uint32_t timeout) {
  22. size_t rx_cnt = 0;
  23. while(size > 0) {
  24. size_t batch_size = size;
  25. if(batch_size > 128) batch_size = 128;
  26. size_t len = furi_stream_buffer_receive(rx_stream, buffer, batch_size, timeout);
  27. if(len == 0) break;
  28. size -= len;
  29. buffer += len;
  30. rx_cnt += len;
  31. }
  32. return rx_cnt;
  33. }
  34. static CliCommand* getCliCommand(Cli* cli, const char* name) {
  35. FuriString* target_command = furi_string_alloc();
  36. furi_string_set_str(target_command, name);
  37. CliCommand* command = CliCommandTree_get(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* help_command = getCliCommand(global_cli, "help");
  52. cliThread = help_command->context;
  53. furi_thread_set_stdout_callback(tx_handler_stdout);
  54. if(cliThread != NULL) {
  55. 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. prev_appthread = loader->app.thread;
  70. loader->app.thread = NULL;
  71. furi_record_close(RECORD_LOADER);
  72. furi_record_close(RECORD_CLI);
  73. }
  74. void unlatch_tx_handler(bool persist) {
  75. Cli* global_cli = furi_record_open(RECORD_CLI);
  76. // Stash cliThread if not null
  77. if(cliThread != NULL) {
  78. CliCommand* help_command = getCliCommand(global_cli, "help");
  79. help_command->context = cliThread;
  80. }
  81. // Switch to new session
  82. if(persist) {
  83. // Use dummy debug firmware function as is_connected
  84. cli_vcp.is_connected = &furi_hal_version_do_i_belong_here;
  85. } else {
  86. // Send CTRL-C
  87. char eot = 0x03;
  88. furi_stream_buffer_send(rx_stream, &eot, 1, FuriWaitForever);
  89. }
  90. cli_session_open(global_cli, &cli_vcp);
  91. furi_record_close(RECORD_CLI);
  92. // Unblock waiting rx handler
  93. furi_stream_buffer_send(rx_stream, "_", 1, FuriWaitForever);
  94. // Reconfigure stdout_callback to cli_vcp
  95. if(cliThread != NULL) {
  96. cliThread->output.write_callback = cli_vcp.tx_stdout;
  97. }
  98. // At this point, all cli_vcp functions should be back.
  99. furi_stream_buffer_free(rx_stream);
  100. furi_stream_buffer_free(tx_stream);
  101. // Re-lock loader (to avoid crash on automatic unlock)
  102. Loader* loader = furi_record_open(RECORD_LOADER);
  103. loader->app.thread = prev_appthread;
  104. furi_record_close(RECORD_LOADER);
  105. }