cli.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. #include "cli_i.h"
  2. #include "cli_commands.h"
  3. #include <version.h>
  4. #include <api-hal-version.h>
  5. Cli* cli_alloc() {
  6. Cli* cli = furi_alloc(sizeof(Cli));
  7. CliCommandDict_init(cli->commands);
  8. cli->mutex = osMutexNew(NULL);
  9. furi_check(cli->mutex);
  10. cli_reset_state(cli);
  11. return cli;
  12. }
  13. void cli_free(Cli* cli) {
  14. free(cli);
  15. }
  16. void cli_reset_state(Cli* cli) {
  17. // Release allocated buffer, reset state
  18. string_clear(cli->line);
  19. string_init(cli->line);
  20. }
  21. void cli_putc(char c) {
  22. api_hal_vcp_tx((uint8_t*)&c, 1);
  23. }
  24. char cli_getc(Cli* cli) {
  25. furi_assert(cli);
  26. char c;
  27. if(api_hal_vcp_rx((uint8_t*)&c, 1) == 0) {
  28. cli_reset_state(cli);
  29. }
  30. return c;
  31. }
  32. void cli_stdout_callback(void* _cookie, const char* data, size_t size) {
  33. api_hal_vcp_tx((const uint8_t*)data, size);
  34. }
  35. void cli_write(Cli* cli, uint8_t* buffer, size_t size) {
  36. return api_hal_vcp_tx(buffer, size);
  37. }
  38. size_t cli_read(Cli* cli, uint8_t* buffer, size_t size) {
  39. return api_hal_vcp_rx(buffer, size);
  40. }
  41. void cli_print_version(const Version* version) {
  42. if(version) {
  43. printf("\tVersion:\t%s\r\n", version_get_version(version));
  44. printf("\tBuild date:\t%s\r\n", version_get_builddate(version));
  45. printf(
  46. "\tGit Commit:\t%s (%s)\r\n",
  47. version_get_githash(version),
  48. version_get_gitbranchnum(version));
  49. printf("\tGit Branch:\t%s\r\n", version_get_gitbranch(version));
  50. } else {
  51. printf("\tNo build info\r\n");
  52. }
  53. }
  54. void cli_motd() {
  55. const Version* version;
  56. printf("Flipper cli.\r\n");
  57. version = (const Version*)api_hal_version_get_boot_version();
  58. printf("Bootloader\r\n");
  59. cli_print_version(version);
  60. version = (const Version*)api_hal_version_get_fw_version();
  61. printf("Firmware\r\n");
  62. cli_print_version(version);
  63. }
  64. void cli_nl() {
  65. printf("\r\n");
  66. }
  67. void cli_prompt() {
  68. printf("\r\n>: ");
  69. fflush(stdout);
  70. }
  71. void cli_backspace(Cli* cli) {
  72. size_t s = string_size(cli->line);
  73. if(s > 0) {
  74. s--;
  75. string_left(cli->line, s);
  76. cli_putc(CliSymbolAsciiBackspace);
  77. cli_putc(CliSymbolAsciiSpace);
  78. cli_putc(CliSymbolAsciiBackspace);
  79. } else {
  80. cli_putc(CliSymbolAsciiBell);
  81. }
  82. }
  83. void cli_enter(Cli* cli) {
  84. // Normalize input
  85. string_strim(cli->line);
  86. if(string_size(cli->line) == 0) {
  87. cli_prompt();
  88. return;
  89. }
  90. // Get first word as command name
  91. string_t command;
  92. string_init(command);
  93. size_t ws = string_search_char(cli->line, ' ');
  94. if(ws == STRING_FAILURE) {
  95. string_set(command, cli->line);
  96. string_clear(cli->line);
  97. string_init(cli->line);
  98. } else {
  99. string_set_n(command, cli->line, 0, ws);
  100. string_right(cli->line, ws);
  101. string_strim(cli->line);
  102. }
  103. // Search for command
  104. furi_check(osMutexAcquire(cli->mutex, osWaitForever) == osOK);
  105. CliCommand* cli_command = CliCommandDict_get(cli->commands, command);
  106. furi_check(osMutexRelease(cli->mutex) == osOK);
  107. if(cli_command) {
  108. cli_nl();
  109. cli_command->callback(cli->line, cli_command->context);
  110. cli_prompt();
  111. } else {
  112. cli_nl();
  113. printf("Command not found: ");
  114. printf(string_get_cstr(command));
  115. cli_prompt();
  116. cli_putc(CliSymbolAsciiBell);
  117. }
  118. string_clear(command);
  119. // Always finish with clean state
  120. cli_reset_state(cli);
  121. }
  122. void cli_process_input(Cli* cli) {
  123. char c = cli_getc(cli);
  124. size_t r;
  125. if(c == CliSymbolAsciiTab) {
  126. cli_putc(CliSymbolAsciiBell);
  127. } else if(c == CliSymbolAsciiSOH) {
  128. cli_motd();
  129. cli_prompt();
  130. } else if(c == CliSymbolAsciiEOT) {
  131. cli_reset_state(cli);
  132. } else if(c == CliSymbolAsciiEsc) {
  133. r = api_hal_vcp_rx((uint8_t*)&c, 1);
  134. if(r && c == '[') {
  135. api_hal_vcp_rx((uint8_t*)&c, 1);
  136. } else {
  137. cli_putc(CliSymbolAsciiBell);
  138. }
  139. } else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) {
  140. cli_backspace(cli);
  141. } else if(c == CliSymbolAsciiCR) {
  142. cli_enter(cli);
  143. } else if(c >= 0x20 && c < 0x7F) {
  144. string_push_back(cli->line, c);
  145. cli_putc(c);
  146. } else {
  147. cli_putc(CliSymbolAsciiBell);
  148. }
  149. }
  150. void cli_add_command(Cli* cli, const char* name, CliCallback callback, void* context) {
  151. string_t name_str;
  152. string_init_set_str(name_str, name);
  153. string_strim(name_str);
  154. size_t name_replace;
  155. do {
  156. name_replace = string_replace_str(name_str, " ", "_");
  157. } while(name_replace != STRING_FAILURE);
  158. CliCommand c;
  159. c.callback = callback;
  160. c.context = context;
  161. furi_check(osMutexAcquire(cli->mutex, osWaitForever) == osOK);
  162. CliCommandDict_set_at(cli->commands, name_str, c);
  163. furi_check(osMutexRelease(cli->mutex) == osOK);
  164. string_clear(name_str);
  165. }
  166. void cli_delete_command(Cli* cli, const char* name) {
  167. string_t name_str;
  168. string_init_set_str(name_str, name);
  169. string_strim(name_str);
  170. size_t name_replace;
  171. do {
  172. name_replace = string_replace_str(name_str, " ", "_");
  173. } while(name_replace != STRING_FAILURE);
  174. furi_check(osMutexAcquire(cli->mutex, osWaitForever) == osOK);
  175. CliCommandDict_erase(cli->commands, name_str);
  176. furi_check(osMutexRelease(cli->mutex) == osOK);
  177. string_clear(name_str);
  178. }
  179. int32_t cli_task(void* p) {
  180. Cli* cli = cli_alloc();
  181. // Init basic cli commands
  182. cli_commands_init(cli);
  183. furi_record_create("cli", cli);
  184. furi_stdglue_set_thread_stdout_callback(cli_stdout_callback);
  185. while(1) {
  186. cli_process_input(cli);
  187. }
  188. return 0;
  189. }