esp_flasher_worker.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. #include "esp_flasher_worker.h"
  2. FuriStreamBuffer* flash_rx_stream; // TODO make safe
  3. EspFlasherApp* global_app; // TODO make safe
  4. FuriTimer* timer; // TODO make
  5. static uint32_t _remaining_time = 0;
  6. static void _timer_callback(void* context) {
  7. UNUSED(context);
  8. if(_remaining_time > 0) {
  9. _remaining_time--;
  10. }
  11. }
  12. static esp_loader_error_t _flash_file(EspFlasherApp* app, char* filepath, uint32_t addr) {
  13. // TODO cleanup
  14. esp_loader_error_t err;
  15. static uint8_t payload[1024];
  16. File* bin_file = storage_file_alloc(app->storage);
  17. char user_msg[256];
  18. // open file
  19. if(!storage_file_open(bin_file, filepath, FSAM_READ, FSOM_OPEN_EXISTING)) {
  20. storage_file_close(bin_file);
  21. storage_file_free(bin_file);
  22. dialog_message_show_storage_error(app->dialogs, "Cannot open file");
  23. return ESP_LOADER_ERROR_FAIL;
  24. }
  25. uint64_t size = storage_file_size(bin_file);
  26. loader_port_debug_print("Erasing flash...this may take a while\n");
  27. err = esp_loader_flash_start(addr, size, sizeof(payload));
  28. if(err != ESP_LOADER_SUCCESS) {
  29. storage_file_close(bin_file);
  30. storage_file_free(bin_file);
  31. snprintf(user_msg, sizeof(user_msg), "Erasing flash failed with error %d\n", err);
  32. loader_port_debug_print(user_msg);
  33. return err;
  34. }
  35. loader_port_debug_print("Start programming\n");
  36. uint64_t last_updated = size;
  37. while(size > 0) {
  38. if((last_updated - size) > 50000) {
  39. // inform user every 50k bytes
  40. // TODO: draw a progress bar next update
  41. snprintf(user_msg, sizeof(user_msg), "%llu bytes left.\n", size);
  42. loader_port_debug_print(user_msg);
  43. last_updated = size;
  44. }
  45. size_t to_read = MIN(size, sizeof(payload));
  46. uint16_t num_bytes = storage_file_read(bin_file, payload, to_read);
  47. err = esp_loader_flash_write(payload, num_bytes);
  48. if(err != ESP_LOADER_SUCCESS) {
  49. snprintf(user_msg, sizeof(user_msg), "Packet could not be written! Error: %u\n", err);
  50. storage_file_close(bin_file);
  51. storage_file_free(bin_file);
  52. loader_port_debug_print(user_msg);
  53. return err;
  54. }
  55. size -= num_bytes;
  56. }
  57. loader_port_debug_print("Finished programming\n");
  58. // TODO verify
  59. storage_file_close(bin_file);
  60. storage_file_free(bin_file);
  61. return ESP_LOADER_SUCCESS;
  62. }
  63. // This in-app FW switch "exploits" the otadata (boot_app0)
  64. // - the first four bytes of each array are the counter and the last four bytes are just a CRC of that counter
  65. // - the bootloader will just boot whichever app has the highest counter in the otadata partition
  66. // so we'll just pick 1 for A, and then B will use either 0 or 2 depending on whether it's the slot in use
  67. #define MAGIC_PAYLOAD_SIZE (32)
  68. const uint8_t magic_payload_app_a[MAGIC_PAYLOAD_SIZE] = {0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
  69. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  70. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  71. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  72. 0x9a, 0x98, 0x43, 0x47};
  73. const uint8_t magic_payload_app_b_unset[MAGIC_PAYLOAD_SIZE] = {
  74. 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  75. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  76. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
  77. const uint8_t magic_payload_app_b_set[MAGIC_PAYLOAD_SIZE] = {
  78. 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  79. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  80. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x74, 0x37, 0xf6, 0x55};
  81. // return true if "switching" fw selected instead of flashing new fw
  82. // (this does not indicate success)
  83. static bool _switch_fw(EspFlasherApp* app) {
  84. if(app->switch_fw == SwitchNotSet) {
  85. return false;
  86. }
  87. esp_loader_error_t err;
  88. char user_msg[256];
  89. loader_port_debug_print("Preparing to set flags for firmware A\n");
  90. err = esp_loader_flash_start(
  91. ESP_ADDR_BOOT_APP0 + ESP_ADDR_OTADATA_OFFSET_APP_A,
  92. MAGIC_PAYLOAD_SIZE,
  93. MAGIC_PAYLOAD_SIZE);
  94. if(err != ESP_LOADER_SUCCESS) {
  95. snprintf(user_msg, sizeof(user_msg), "Erasing flash failed with error %d\n", err);
  96. loader_port_debug_print(user_msg);
  97. return true;
  98. }
  99. loader_port_debug_print("Setting flags for firmware A\n");
  100. const uint8_t* which_payload_app_a = magic_payload_app_a;
  101. err = esp_loader_flash_write((void*)which_payload_app_a, MAGIC_PAYLOAD_SIZE);
  102. if(err != ESP_LOADER_SUCCESS) {
  103. snprintf(user_msg, sizeof(user_msg), "Packet could not be written! Error: %u\n", err);
  104. loader_port_debug_print(user_msg);
  105. return true;
  106. }
  107. loader_port_debug_print("Preparing to set flags for firmware B\n");
  108. err = esp_loader_flash_start(
  109. ESP_ADDR_BOOT_APP0 + ESP_ADDR_OTADATA_OFFSET_APP_B,
  110. MAGIC_PAYLOAD_SIZE,
  111. MAGIC_PAYLOAD_SIZE);
  112. if(err != ESP_LOADER_SUCCESS) {
  113. snprintf(user_msg, sizeof(user_msg), "Erasing flash failed with error %d\n", err);
  114. loader_port_debug_print(user_msg);
  115. return true;
  116. }
  117. loader_port_debug_print("Setting flags for firmware B\n");
  118. const uint8_t* which_payload_app_b =
  119. (app->switch_fw == SwitchToFirmwareB ? magic_payload_app_b_set :
  120. magic_payload_app_b_unset);
  121. err = esp_loader_flash_write((void*)which_payload_app_b, MAGIC_PAYLOAD_SIZE);
  122. if(err != ESP_LOADER_SUCCESS) {
  123. snprintf(user_msg, sizeof(user_msg), "Packet could not be written! Error: %u\n", err);
  124. loader_port_debug_print(user_msg);
  125. return true;
  126. }
  127. loader_port_debug_print("Finished programming\n");
  128. return true;
  129. }
  130. typedef struct {
  131. SelectedFlashOptions selected;
  132. const char* description;
  133. char* path;
  134. uint32_t addr;
  135. } FlashItem;
  136. static void _flash_all_files(EspFlasherApp* app) {
  137. esp_loader_error_t err;
  138. const int num_steps = app->num_selected_flash_options;
  139. #define NUM_FLASH_ITEMS 7
  140. FlashItem items[NUM_FLASH_ITEMS] = {
  141. {SelectedFlashBoot,
  142. "bootloader",
  143. app->bin_file_path_boot,
  144. app->selected_flash_options[SelectedFlashS3Mode] ? ESP_ADDR_BOOT_S3 : ESP_ADDR_BOOT},
  145. {SelectedFlashPart, "partition table", app->bin_file_path_part, ESP_ADDR_PART},
  146. {SelectedFlashNvs, "NVS", app->bin_file_path_nvs, ESP_ADDR_NVS},
  147. {SelectedFlashBootApp0, "boot_app0", app->bin_file_path_boot_app0, ESP_ADDR_BOOT_APP0},
  148. {SelectedFlashAppA, "firmware A", app->bin_file_path_app_a, ESP_ADDR_APP_A},
  149. {SelectedFlashAppB, "firmware B", app->bin_file_path_app_b, ESP_ADDR_APP_B},
  150. {SelectedFlashCustom, "custom data", app->bin_file_path_custom, 0x0},
  151. /* if you add more entries, update NUM_FLASH_ITEMS above! */
  152. };
  153. char user_msg[256];
  154. int current_step = 1;
  155. for(FlashItem* item = &items[0]; item < &items[NUM_FLASH_ITEMS]; ++item) {
  156. if(app->selected_flash_options[item->selected]) {
  157. snprintf(
  158. user_msg,
  159. sizeof(user_msg),
  160. "Flashing %s (%d/%d) to address 0x%lx\n",
  161. item->description,
  162. current_step++,
  163. num_steps,
  164. item->addr);
  165. loader_port_debug_print(user_msg);
  166. err = _flash_file(app, item->path, item->addr);
  167. if(err) {
  168. break;
  169. }
  170. }
  171. }
  172. }
  173. static int32_t esp_flasher_flash_bin(void* context) {
  174. EspFlasherApp* app = (void*)context;
  175. esp_loader_error_t err;
  176. app->flash_worker_busy = true;
  177. // alloc global objects
  178. flash_rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1);
  179. timer = furi_timer_alloc(_timer_callback, FuriTimerTypePeriodic, app);
  180. // turn on flipper blue LED for duration of flash
  181. notification_message(app->notification, &sequence_set_only_blue_255);
  182. loader_port_debug_print("Connecting\n");
  183. esp_loader_connect_args_t connect_config = ESP_LOADER_CONNECT_DEFAULT();
  184. err = esp_loader_connect(&connect_config);
  185. if(err != ESP_LOADER_SUCCESS) {
  186. char err_msg[256];
  187. snprintf(
  188. err_msg,
  189. sizeof(err_msg),
  190. "Cannot connect to target. Error: %u\nMake sure the device is in bootloader/reflash mode, then try again.\n",
  191. err);
  192. loader_port_debug_print(err_msg);
  193. }
  194. // higher BR
  195. if(!err && app->turbospeed) {
  196. loader_port_debug_print("Increasing speed for faster flash\n");
  197. err = esp_loader_change_transmission_rate(FAST_BAUDRATE);
  198. if(err != ESP_LOADER_SUCCESS) {
  199. char err_msg[256];
  200. snprintf(
  201. err_msg, sizeof(err_msg), "Cannot change transmission rate. Error: %u\n", err);
  202. loader_port_debug_print(err_msg);
  203. }
  204. furi_hal_uart_set_br(UART_CH, FAST_BAUDRATE);
  205. }
  206. if(!err) {
  207. loader_port_debug_print("Connected\n");
  208. uint32_t start_time = furi_get_tick();
  209. if(!_switch_fw(app)) {
  210. _flash_all_files(app);
  211. }
  212. app->switch_fw = SwitchNotSet;
  213. FuriString* flash_time =
  214. furi_string_alloc_printf("Flash took: %lds\n", (furi_get_tick() - start_time) / 1000);
  215. loader_port_debug_print(furi_string_get_cstr(flash_time));
  216. furi_string_free(flash_time);
  217. if(app->turbospeed) {
  218. loader_port_debug_print("Restoring transmission rate\n");
  219. furi_hal_uart_set_br(UART_CH, BAUDRATE);
  220. }
  221. loader_port_debug_print(
  222. "Done flashing. Please reset the board manually if it doesn't auto-reset.\n");
  223. // auto-reset for supported boards
  224. loader_port_reset_target();
  225. // short buzz to alert user
  226. notification_message(app->notification, &sequence_set_vibro_on);
  227. loader_port_delay_ms(50);
  228. notification_message(app->notification, &sequence_reset_vibro);
  229. }
  230. // turn off flipper blue LED
  231. notification_message(app->notification, &sequence_reset_blue);
  232. // done
  233. app->flash_worker_busy = false;
  234. app->quickflash = false;
  235. // cleanup
  236. furi_stream_buffer_free(flash_rx_stream);
  237. flash_rx_stream = NULL;
  238. furi_timer_free(timer);
  239. return 0;
  240. }
  241. static void _initDTR(void) {
  242. furi_hal_gpio_init(&gpio_ext_pc3, GpioModeOutputPushPull, GpioPullDown, GpioSpeedVeryHigh);
  243. }
  244. static void _initRTS(void) {
  245. furi_hal_gpio_init(&gpio_ext_pb2, GpioModeOutputPushPull, GpioPullDown, GpioSpeedVeryHigh);
  246. }
  247. static void _setDTR(bool state) {
  248. furi_hal_gpio_write(&gpio_ext_pc3, state);
  249. }
  250. static void _setRTS(bool state) {
  251. furi_hal_gpio_write(&gpio_ext_pb2, state);
  252. }
  253. static int32_t esp_flasher_reset(void* context) {
  254. EspFlasherApp* app = (void*)context;
  255. app->flash_worker_busy = true;
  256. _setDTR(false);
  257. _initDTR();
  258. _setRTS(false);
  259. _initRTS();
  260. furi_hal_gpio_init_simple(&gpio_swclk, GpioModeOutputPushPull);
  261. furi_hal_gpio_write(&gpio_swclk, true);
  262. if(app->reset) {
  263. loader_port_debug_print("Resetting board\n");
  264. loader_port_reset_target();
  265. } else if(app->boot) {
  266. loader_port_debug_print("Entering bootloader\n");
  267. loader_port_enter_bootloader();
  268. }
  269. // done
  270. app->flash_worker_busy = false;
  271. app->reset = false;
  272. app->boot = false;
  273. if(app->quickflash) {
  274. esp_flasher_flash_bin(app);
  275. }
  276. return 0;
  277. }
  278. void esp_flasher_worker_start_thread(EspFlasherApp* app) {
  279. global_app = app;
  280. app->flash_worker = furi_thread_alloc();
  281. furi_thread_set_name(app->flash_worker, "EspFlasherFlashWorker");
  282. furi_thread_set_stack_size(app->flash_worker, 2048);
  283. furi_thread_set_context(app->flash_worker, app);
  284. if(app->reset || app->boot) {
  285. furi_thread_set_callback(app->flash_worker, esp_flasher_reset);
  286. } else {
  287. furi_thread_set_callback(app->flash_worker, esp_flasher_flash_bin);
  288. }
  289. furi_thread_start(app->flash_worker);
  290. }
  291. void esp_flasher_worker_stop_thread(EspFlasherApp* app) {
  292. furi_thread_join(app->flash_worker);
  293. furi_thread_free(app->flash_worker);
  294. }
  295. esp_loader_error_t loader_port_read(uint8_t* data, uint16_t size, uint32_t timeout) {
  296. size_t read = furi_stream_buffer_receive(flash_rx_stream, data, size, timeout);
  297. if(read < size) {
  298. return ESP_LOADER_ERROR_TIMEOUT;
  299. } else {
  300. return ESP_LOADER_SUCCESS;
  301. }
  302. }
  303. esp_loader_error_t loader_port_write(const uint8_t* data, uint16_t size, uint32_t timeout) {
  304. UNUSED(timeout);
  305. esp_flasher_uart_tx((uint8_t*)data, size);
  306. return ESP_LOADER_SUCCESS;
  307. }
  308. void loader_port_reset_target(void) {
  309. _setDTR(true);
  310. loader_port_delay_ms(SERIAL_FLASHER_RESET_HOLD_TIME_MS);
  311. _setDTR(false);
  312. }
  313. void loader_port_enter_bootloader(void) {
  314. // Also support for the Multi-fucc and Xeon boards
  315. furi_hal_gpio_write(&gpio_swclk, false);
  316. furi_hal_power_disable_otg();
  317. loader_port_delay_ms(100);
  318. furi_hal_power_enable_otg();
  319. furi_hal_gpio_init_simple(&gpio_swclk, GpioModeAnalog);
  320. loader_port_delay_ms(100);
  321. // adapted from custom usb-jtag-serial reset in esptool
  322. // (works on official wifi dev board)
  323. _setDTR(true);
  324. loader_port_delay_ms(SERIAL_FLASHER_RESET_HOLD_TIME_MS);
  325. _setRTS(true);
  326. _setDTR(false);
  327. loader_port_delay_ms(SERIAL_FLASHER_BOOT_HOLD_TIME_MS);
  328. _setRTS(false);
  329. }
  330. void loader_port_delay_ms(uint32_t ms) {
  331. furi_delay_ms(ms);
  332. }
  333. void loader_port_start_timer(uint32_t ms) {
  334. _remaining_time = ms;
  335. furi_timer_start(timer, 1);
  336. }
  337. uint32_t loader_port_remaining_time(void) {
  338. return _remaining_time;
  339. }
  340. extern void esp_flasher_console_output_handle_rx_data_cb(
  341. uint8_t* buf,
  342. size_t len,
  343. void* context); // TODO cleanup
  344. void loader_port_debug_print(const char* str) {
  345. if(global_app)
  346. esp_flasher_console_output_handle_rx_data_cb((uint8_t*)str, strlen(str), global_app);
  347. }
  348. void loader_port_spi_set_cs(uint32_t level) {
  349. UNUSED(level);
  350. // unimplemented
  351. }
  352. void esp_flasher_worker_handle_rx_data_cb(uint8_t* buf, size_t len, void* context) {
  353. UNUSED(context);
  354. if(flash_rx_stream) {
  355. furi_stream_buffer_send(flash_rx_stream, buf, len, 0);
  356. } else {
  357. // done flashing
  358. if(global_app) esp_flasher_console_output_handle_rx_data_cb(buf, len, global_app);
  359. }
  360. }