cli.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #include "cli_i.h"
  2. #include "cli_commands.h"
  3. #include <api-hal-vcp.h>
  4. Cli* cli_alloc() {
  5. Cli* cli = furi_alloc(sizeof(Cli));
  6. CliCommandDict_init(cli->commands);
  7. cli->mutex = osMutexNew(NULL);
  8. furi_check(cli->mutex);
  9. cli_reset_state(cli);
  10. return cli;
  11. }
  12. void cli_free(Cli* cli) {
  13. free(cli);
  14. }
  15. void cli_reset_state(Cli* cli) {
  16. string_clear(cli->line);
  17. string_init(cli->line);
  18. }
  19. void cli_putc(char c) {
  20. api_hal_vcp_tx((uint8_t*)&c, 1);
  21. }
  22. void cli_print(const char* str) {
  23. api_hal_vcp_tx((uint8_t*)str, strlen(str));
  24. }
  25. void cli_print_version() {
  26. cli_print("Build date:" BUILD_DATE ". "
  27. "Git Commit:" GIT_COMMIT ". "
  28. "Git Branch:" GIT_BRANCH ". "
  29. "Commit Number:" GIT_BRANCH_NUM ".");
  30. }
  31. void cli_motd() {
  32. cli_print("Flipper cli.\r\n");
  33. cli_print_version();
  34. }
  35. void cli_nl() {
  36. cli_print("\r\n");
  37. }
  38. void cli_prompt() {
  39. cli_print("\r\n>: ");
  40. }
  41. void cli_backspace(Cli* cli) {
  42. size_t s = string_size(cli->line);
  43. if(s > 0) {
  44. s--;
  45. string_left(cli->line, s);
  46. cli_putc(CliSymbolAsciiBackspace);
  47. cli_putc(CliSymbolAsciiSpace);
  48. cli_putc(CliSymbolAsciiBackspace);
  49. } else {
  50. cli_putc(CliSymbolAsciiBell);
  51. }
  52. }
  53. void cli_enter(Cli* cli) {
  54. // Normalize input
  55. string_strim(cli->line);
  56. if(string_size(cli->line) == 0) {
  57. cli_prompt();
  58. return;
  59. }
  60. // Get first word as command name
  61. string_t command;
  62. string_init(command);
  63. size_t ws = string_search_char(cli->line, ' ');
  64. if(ws == STRING_FAILURE) {
  65. string_set(command, cli->line);
  66. string_clear(cli->line);
  67. string_init(cli->line);
  68. } else {
  69. string_set_n(command, cli->line, 0, ws);
  70. string_right(cli->line, ws);
  71. string_strim(cli->line);
  72. }
  73. // Search for command
  74. furi_check(osMutexAcquire(cli->mutex, osWaitForever) == osOK);
  75. CliCommand* cli_command = CliCommandDict_get(cli->commands, command);
  76. furi_check(osMutexRelease(cli->mutex) == osOK);
  77. if(cli_command) {
  78. cli_nl();
  79. cli_command->callback(cli->line, cli_command->context);
  80. cli_prompt();
  81. } else {
  82. cli_nl();
  83. cli_print("Command not found: ");
  84. cli_print(string_get_cstr(command));
  85. cli_prompt();
  86. cli_putc(CliSymbolAsciiBell);
  87. }
  88. // Always finish with clean state
  89. cli_reset_state(cli);
  90. }
  91. void cli_process_input(Cli* cli) {
  92. char c;
  93. size_t r;
  94. r = api_hal_vcp_rx((uint8_t*)&c, 1);
  95. if(r == 0) {
  96. cli_reset_state(cli);
  97. }
  98. if(c == CliSymbolAsciiTab) {
  99. cli_putc(CliSymbolAsciiBell);
  100. } else if(c == CliSymbolAsciiSOH) {
  101. cli_motd();
  102. cli_prompt();
  103. } else if(c == CliSymbolAsciiEOT) {
  104. cli_reset_state(cli);
  105. } else if(c == CliSymbolAsciiEsc) {
  106. r = api_hal_vcp_rx((uint8_t*)&c, 1);
  107. if(r && c == '[') {
  108. api_hal_vcp_rx((uint8_t*)&c, 1);
  109. } else {
  110. cli_putc(CliSymbolAsciiBell);
  111. }
  112. } else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) {
  113. cli_backspace(cli);
  114. } else if(c == CliSymbolAsciiCR) {
  115. cli_enter(cli);
  116. } else if(c >= 0x20 && c < 0x7F) {
  117. string_push_back(cli->line, c);
  118. cli_putc(c);
  119. } else {
  120. cli_putc(CliSymbolAsciiBell);
  121. }
  122. }
  123. void cli_add_command(Cli* cli, const char* name, CliCallback callback, void* context) {
  124. string_t name_str;
  125. string_init_set_str(name_str, name);
  126. CliCommand c;
  127. c.callback = callback;
  128. c.context = context;
  129. furi_check(osMutexAcquire(cli->mutex, osWaitForever) == osOK);
  130. CliCommandDict_set_at(cli->commands, name_str, c);
  131. furi_check(osMutexRelease(cli->mutex) == osOK);
  132. }
  133. void cli_task(void* p) {
  134. Cli* cli = cli_alloc();
  135. // Init basic cli commands
  136. cli_commands_init(cli);
  137. if(!furi_create("cli", cli)) {
  138. printf("[cli_task] cannot create the cli record\n");
  139. furiac_exit(NULL);
  140. }
  141. furiac_ready();
  142. while(1) {
  143. cli_process_input(cli);
  144. }
  145. }