loader.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. #include "loader_i.h"
  2. static Loader* loader_instance = NULL;
  3. static void loader_menu_callback(void* _ctx) {
  4. const FlipperApplication* flipper_app = _ctx;
  5. furi_assert(flipper_app->app);
  6. furi_assert(flipper_app->name);
  7. if(!loader_lock(loader_instance)) return;
  8. if(furi_thread_get_state(loader_instance->thread) != FuriThreadStateStopped) {
  9. FURI_LOG_E(
  10. LOADER_LOG_TAG, "Can't start app. %s is running", loader_instance->current_app->name);
  11. return;
  12. }
  13. api_hal_power_insomnia_enter();
  14. loader_instance->current_app = flipper_app;
  15. FURI_LOG_I(
  16. LOADER_LOG_TAG, "Starting furi application: %s", loader_instance->current_app->name);
  17. furi_thread_set_name(loader_instance->thread, flipper_app->name);
  18. furi_thread_set_stack_size(loader_instance->thread, flipper_app->stack_size);
  19. furi_thread_set_context(loader_instance->thread, NULL);
  20. furi_thread_set_callback(loader_instance->thread, flipper_app->app);
  21. furi_thread_start(loader_instance->thread);
  22. }
  23. static void loader_cli_callback(Cli* cli, string_t args, void* _ctx) {
  24. furi_assert(_ctx);
  25. const FlipperApplication* flipper_app = (FlipperApplication*)_ctx;
  26. furi_assert(flipper_app->app);
  27. furi_assert(flipper_app->name);
  28. if(furi_thread_get_state(loader_instance->thread) != FuriThreadStateStopped) {
  29. printf("Can't start, furi application is running");
  30. return;
  31. }
  32. loader_instance->lock_semaphore++;
  33. api_hal_power_insomnia_enter();
  34. loader_instance->current_app = flipper_app;
  35. printf("Starting furi application %s", loader_instance->current_app->name);
  36. furi_thread_set_name(loader_instance->thread, flipper_app->name);
  37. furi_thread_set_stack_size(loader_instance->thread, flipper_app->stack_size);
  38. furi_thread_set_callback(loader_instance->thread, flipper_app->app);
  39. furi_thread_start(loader_instance->thread);
  40. }
  41. bool loader_start(Loader* instance, const char* name, const char* args) {
  42. furi_assert(name);
  43. const FlipperApplication* flipper_app = NULL;
  44. // Search for application
  45. for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
  46. if(strcmp(FLIPPER_APPS[i].name, name) == 0) {
  47. flipper_app = &FLIPPER_APPS[i];
  48. break;
  49. }
  50. }
  51. if(!flipper_app) {
  52. FURI_LOG_E(LOADER_LOG_TAG, "Can't find application with name %s", name);
  53. return false;
  54. }
  55. loader_lock(instance);
  56. if(furi_thread_get_state(instance->thread) != FuriThreadStateStopped) {
  57. FURI_LOG_E(LOADER_LOG_TAG, "Can't start app. %s is running", instance->current_app->name);
  58. return false;
  59. }
  60. instance->current_app = flipper_app;
  61. if(args) {
  62. string_set_str(instance->args, args);
  63. string_strim(instance->args);
  64. FURI_LOG_I(LOADER_LOG_TAG, "Start %s app with args: %s", name, args);
  65. } else {
  66. string_clean(instance->args);
  67. FURI_LOG_I(LOADER_LOG_TAG, "Start %s app with no args", name);
  68. }
  69. furi_thread_set_name(instance->thread, flipper_app->name);
  70. furi_thread_set_stack_size(instance->thread, flipper_app->stack_size);
  71. furi_thread_set_context(instance->thread, (void*)string_get_cstr(instance->args));
  72. furi_thread_set_callback(instance->thread, flipper_app->app);
  73. return furi_thread_start(instance->thread);
  74. }
  75. bool loader_lock(Loader* instance) {
  76. bool ret = false;
  77. furi_check(osMutexAcquire(instance->mutex, osWaitForever) == osOK);
  78. if(instance->lock_semaphore == 0) {
  79. instance->lock_semaphore++;
  80. ret = true;
  81. }
  82. furi_check(osMutexRelease(instance->mutex) == osOK);
  83. return ret;
  84. }
  85. void loader_unlock(Loader* instance) {
  86. furi_check(osMutexAcquire(instance->mutex, osWaitForever) == osOK);
  87. furi_check(instance->lock_semaphore > 0);
  88. instance->lock_semaphore--;
  89. furi_check(osMutexRelease(instance->mutex) == osOK);
  90. }
  91. static void loader_thread_state_callback(FuriThreadState thread_state, void* context) {
  92. furi_assert(context);
  93. Loader* instance = context;
  94. if(thread_state == FuriThreadStateRunning) {
  95. instance->free_heap_size = xPortGetFreeHeapSize();
  96. } else if(thread_state == FuriThreadStateStopped) {
  97. /*
  98. * Current Leak Sanitizer assumes that memory is allocated and freed
  99. * inside one thread. Timers are allocated in one task, but freed in
  100. * Timer-Task thread, and xTimerDelete() just put command to queue.
  101. * To avoid some bad cases there are few fixes:
  102. * 1) delay for Timer to process commands
  103. * 2) there are 'heap diff' which shows difference in heap before task
  104. * started and after task completed. In process of leakage monitoring
  105. * both values should be taken into account.
  106. */
  107. delay(20);
  108. int heap_diff = instance->free_heap_size - xPortGetFreeHeapSize();
  109. FURI_LOG_I(
  110. LOADER_LOG_TAG,
  111. "Application thread stopped. Heap allocation balance: %d. Thread allocation balance: %d.",
  112. heap_diff,
  113. furi_thread_get_heap_size(instance->thread));
  114. api_hal_power_insomnia_exit();
  115. loader_unlock(instance);
  116. }
  117. }
  118. static Loader* loader_alloc() {
  119. Loader* instance = furi_alloc(sizeof(Loader));
  120. instance->thread = furi_thread_alloc();
  121. furi_thread_enable_heap_trace(instance->thread);
  122. furi_thread_set_state_context(instance->thread, instance);
  123. furi_thread_set_state_callback(instance->thread, loader_thread_state_callback);
  124. string_init(instance->args);
  125. instance->mutex = osMutexNew(NULL);
  126. instance->menu_vm = furi_record_open("menu");
  127. instance->cli = furi_record_open("cli");
  128. return instance;
  129. }
  130. static void loader_free(Loader* instance) {
  131. furi_assert(instance);
  132. furi_record_close("cli");
  133. furi_record_close("menu");
  134. osMutexDelete(instance->mutex);
  135. string_clear(instance->args);
  136. furi_thread_free(instance->thread);
  137. free(instance);
  138. }
  139. static void loader_build_menu() {
  140. FURI_LOG_I(LOADER_LOG_TAG, "Building main menu");
  141. with_value_mutex(
  142. loader_instance->menu_vm, (Menu * menu) {
  143. for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
  144. // Add menu item
  145. menu_item_add(
  146. menu,
  147. menu_item_alloc_function(
  148. FLIPPER_APPS[i].name,
  149. icon_animation_alloc(FLIPPER_APPS[i].icon),
  150. loader_menu_callback,
  151. (void*)&FLIPPER_APPS[i]));
  152. // Add cli command
  153. string_t cli_name;
  154. string_init_set_str(cli_name, "app_");
  155. string_cat_str(cli_name, FLIPPER_APPS[i].name);
  156. cli_add_command(
  157. loader_instance->cli,
  158. string_get_cstr(cli_name),
  159. CliCommandFlagDefault,
  160. loader_cli_callback,
  161. (void*)&FLIPPER_APPS[i]);
  162. string_clear(cli_name);
  163. }
  164. });
  165. FURI_LOG_I(LOADER_LOG_TAG, "Building plugins menu");
  166. with_value_mutex(
  167. loader_instance->menu_vm, (Menu * menu) {
  168. MenuItem* menu_plugins =
  169. menu_item_alloc_menu("Plugins", icon_animation_alloc(&A_Plugins_14));
  170. for(size_t i = 0; i < FLIPPER_PLUGINS_COUNT; i++) {
  171. // Add menu item
  172. menu_item_subitem_add(
  173. menu_plugins,
  174. menu_item_alloc_function(
  175. FLIPPER_PLUGINS[i].name,
  176. icon_animation_alloc(FLIPPER_PLUGINS[i].icon),
  177. loader_menu_callback,
  178. (void*)&FLIPPER_PLUGINS[i]));
  179. // Add cli command
  180. string_t cli_name;
  181. string_init_set_str(cli_name, "app_");
  182. string_cat_str(cli_name, FLIPPER_PLUGINS[i].name);
  183. cli_add_command(
  184. loader_instance->cli,
  185. string_get_cstr(cli_name),
  186. CliCommandFlagDefault,
  187. loader_cli_callback,
  188. (void*)&FLIPPER_PLUGINS[i]);
  189. string_clear(cli_name);
  190. }
  191. menu_item_add(menu, menu_plugins);
  192. });
  193. FURI_LOG_I(LOADER_LOG_TAG, "Building debug menu");
  194. with_value_mutex(
  195. loader_instance->menu_vm, (Menu * menu) {
  196. MenuItem* menu_debug =
  197. menu_item_alloc_menu("Debug tools", icon_animation_alloc(&A_Settings_14));
  198. for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) {
  199. // Add menu item
  200. menu_item_subitem_add(
  201. menu_debug,
  202. menu_item_alloc_function(
  203. FLIPPER_DEBUG_APPS[i].name,
  204. icon_animation_alloc(FLIPPER_DEBUG_APPS[i].icon),
  205. loader_menu_callback,
  206. (void*)&FLIPPER_DEBUG_APPS[i]));
  207. // Add cli command
  208. string_t cli_name;
  209. string_init_set_str(cli_name, "app_");
  210. string_cat_str(cli_name, FLIPPER_DEBUG_APPS[i].name);
  211. cli_add_command(
  212. loader_instance->cli,
  213. string_get_cstr(cli_name),
  214. CliCommandFlagDefault,
  215. loader_cli_callback,
  216. (void*)&FLIPPER_DEBUG_APPS[i]);
  217. string_clear(cli_name);
  218. }
  219. menu_item_add(menu, menu_debug);
  220. });
  221. FURI_LOG_I(LOADER_LOG_TAG, "Building settings menu");
  222. with_value_mutex(
  223. loader_instance->menu_vm, (Menu * menu) {
  224. MenuItem* menu_debug =
  225. menu_item_alloc_menu("Settings", icon_animation_alloc(&A_Settings_14));
  226. for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
  227. // Add menu item
  228. menu_item_subitem_add(
  229. menu_debug,
  230. menu_item_alloc_function(
  231. FLIPPER_SETTINGS_APPS[i].name,
  232. icon_animation_alloc(FLIPPER_SETTINGS_APPS[i].icon),
  233. loader_menu_callback,
  234. (void*)&FLIPPER_SETTINGS_APPS[i]));
  235. }
  236. menu_item_add(menu, menu_debug);
  237. });
  238. }
  239. int32_t loader(void* p) {
  240. FURI_LOG_I(LOADER_LOG_TAG, "Starting");
  241. loader_instance = loader_alloc();
  242. loader_build_menu();
  243. // Call on start hooks
  244. for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) {
  245. (*FLIPPER_ON_SYSTEM_START[i])();
  246. }
  247. FURI_LOG_I(LOADER_LOG_TAG, "Started");
  248. furi_record_create("loader", loader_instance);
  249. while(1) {
  250. osThreadSuspend(osThreadGetId());
  251. }
  252. loader_free(loader_instance);
  253. return 0;
  254. }