flasher.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. #include "flasher.h"
  2. #include <furi.h>
  3. #include <storage/storage.h>
  4. #include "uf2.h"
  5. #include "swd.h"
  6. #include "board.h"
  7. #include "target.h"
  8. #include "rp2040.h"
  9. #define TAG "VgmFlasher"
  10. #define W25Q128_CAPACITY (0x1000000UL)
  11. #define W25Q128_PAGE_SIZE (0x100UL)
  12. #define W25Q128_SECTOR_SIZE (0x1000UL)
  13. #define PROGRESS_VERIFY_WEIGHT (4U)
  14. #define PROGRESS_ERASE_WEIGHT (6U)
  15. #define PROGRESS_PROGRAM_WEIGHT (90U)
  16. #define FLASHER_ATTEMPT_COUNT (10UL)
  17. typedef struct {
  18. FlasherCallback callback;
  19. void* context;
  20. } Flasher;
  21. static Flasher flasher;
  22. bool flasher_init(void) {
  23. FURI_LOG_D(TAG, "Attaching the target");
  24. board_init();
  25. bool success = false;
  26. FURI_CRITICAL_ENTER();
  27. do {
  28. swd_init();
  29. if(!target_attach(RP2040_CORE0_ADDR)) {
  30. FURI_LOG_E(TAG, "Failed to attach target");
  31. break;
  32. }
  33. success = true;
  34. } while(false);
  35. FURI_CRITICAL_EXIT();
  36. if(!success) {
  37. flasher_deinit();
  38. }
  39. return success;
  40. }
  41. void flasher_deinit(void) {
  42. FURI_LOG_D(TAG, "Detaching target and restoring pins");
  43. FURI_CRITICAL_ENTER();
  44. target_detach();
  45. swd_deinit();
  46. FURI_CRITICAL_EXIT();
  47. board_reset();
  48. board_deinit();
  49. }
  50. void flasher_set_callback(FlasherCallback callback, void* context) {
  51. flasher.callback = callback;
  52. flasher.context = context;
  53. }
  54. static inline bool flasher_init_chip(void) {
  55. FURI_CRITICAL_ENTER();
  56. const bool success = rp2040_init();
  57. FURI_CRITICAL_EXIT();
  58. return success;
  59. }
  60. static inline bool flasher_erase_sector(uint32_t address) {
  61. FURI_CRITICAL_ENTER();
  62. const bool success = rp2040_flash_erase_sector(address);
  63. FURI_CRITICAL_EXIT();
  64. return success;
  65. }
  66. static inline bool flasher_program_page(uint32_t address, const void* data, size_t data_size) {
  67. FURI_CRITICAL_ENTER();
  68. const bool success = rp2040_flash_program_page(address, data, data_size);
  69. FURI_CRITICAL_EXIT();
  70. return success;
  71. }
  72. static void flasher_emit_progress(uint8_t start, uint8_t weight, uint8_t progress) {
  73. furi_assert(flasher.callback);
  74. FlasherEvent event = {
  75. .type = FlasherEventTypeProgress,
  76. .progress = start + ((uint32_t)weight * progress) / 100U,
  77. };
  78. flasher.callback(event, flasher.context);
  79. }
  80. static void flasher_emit_error(FlasherError error) {
  81. furi_assert(flasher.callback);
  82. FlasherEvent event = {
  83. .type = FlasherEventTypeError,
  84. .error = error,
  85. };
  86. flasher.callback(event, flasher.context);
  87. }
  88. static void flasher_emit_success(void) {
  89. furi_assert(flasher.callback);
  90. FlasherEvent event = {
  91. .type = FlasherEventTypeSuccess,
  92. };
  93. flasher.callback(event, flasher.context);
  94. }
  95. static bool flasher_prepare_target(void) {
  96. bool success = false;
  97. for(uint32_t i = 0; i < FLASHER_ATTEMPT_COUNT; ++i) {
  98. if(flasher_init()) {
  99. success = true;
  100. break;
  101. }
  102. furi_delay_ms(10);
  103. }
  104. if(!success) {
  105. flasher_emit_error(FlasherErrorDisconnect);
  106. }
  107. return success;
  108. }
  109. static bool flasher_prepare_file(File* file, const char* file_path) {
  110. bool success = false;
  111. do {
  112. if(!flasher_init_chip()) {
  113. FURI_LOG_E(TAG, "Failed to initialise chip");
  114. flasher_emit_error(FlasherErrorDisconnect);
  115. break;
  116. }
  117. if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
  118. FURI_LOG_E(TAG, "Failed to open firmware file: %s", file_path);
  119. flasher_emit_error(FlasherErrorBadFile);
  120. break;
  121. }
  122. success = true;
  123. } while(false);
  124. return success;
  125. }
  126. static bool flasher_verify_file(File* file, size_t* data_size) {
  127. bool success = false;
  128. do {
  129. uint32_t block_count;
  130. if(!uf2_get_block_count(file, &block_count)) {
  131. FURI_LOG_E(TAG, "Failed to get block count");
  132. flasher_emit_error(FlasherErrorBadFile);
  133. break;
  134. }
  135. uint32_t blocks_verified;
  136. uint8_t prev_progress = UINT8_MAX;
  137. for(blocks_verified = 0; blocks_verified < block_count; ++blocks_verified) {
  138. if(!uf2_verify_block(file, RP2040_FAMILY_ID, W25Q128_PAGE_SIZE)) break;
  139. const uint8_t verify_progress = (blocks_verified * 100UL) / block_count;
  140. if(verify_progress != prev_progress) {
  141. prev_progress = verify_progress;
  142. flasher_emit_progress(0, PROGRESS_VERIFY_WEIGHT, verify_progress);
  143. FURI_LOG_D(TAG, "Verifying file: %u%%", verify_progress);
  144. }
  145. }
  146. if(blocks_verified < block_count) {
  147. FURI_LOG_E(TAG, "Failed to verify all blocks");
  148. flasher_emit_error(FlasherErrorBadFile);
  149. break;
  150. }
  151. const size_t size_total = block_count * W25Q128_PAGE_SIZE;
  152. if(size_total > W25Q128_CAPACITY) {
  153. FURI_LOG_E(TAG, "File is too large to fit on the flash");
  154. flasher_emit_error(FlasherErrorBadFile);
  155. break;
  156. }
  157. if(!storage_file_seek(file, 0, true)) {
  158. FURI_LOG_E(TAG, "Failed to rewind the file");
  159. flasher_emit_error(FlasherErrorBadFile);
  160. break;
  161. }
  162. *data_size = size_total;
  163. success = true;
  164. } while(false);
  165. return success;
  166. }
  167. static bool flasher_erase_flash(size_t erase_size) {
  168. uint8_t prev_progress = UINT8_MAX;
  169. size_t size_erased;
  170. for(size_erased = 0; size_erased < erase_size;) {
  171. if(!flasher_erase_sector(size_erased)) {
  172. FURI_LOG_E(TAG, "Failed to erase flash sector at address 0x%zX", size_erased);
  173. flasher_emit_error(FlasherErrorDisconnect);
  174. break;
  175. }
  176. size_erased += MIN(erase_size - size_erased, W25Q128_SECTOR_SIZE);
  177. const uint8_t erase_progress = (size_erased * 100UL) / erase_size;
  178. if(erase_progress != prev_progress) {
  179. prev_progress = erase_progress;
  180. flasher_emit_progress(PROGRESS_VERIFY_WEIGHT, PROGRESS_ERASE_WEIGHT, erase_progress);
  181. FURI_LOG_D(TAG, "Erasing flash: %u%%", erase_progress);
  182. }
  183. }
  184. return size_erased == erase_size;
  185. }
  186. static bool flasher_program_flash(File* file, size_t data_size) {
  187. uint8_t prev_progress = UINT8_MAX;
  188. size_t size_programmed;
  189. for(size_programmed = 0; size_programmed < data_size;) {
  190. uint8_t buf[W25Q128_PAGE_SIZE];
  191. if(!uf2_read_block(file, buf, W25Q128_PAGE_SIZE)) {
  192. FURI_LOG_E(TAG, "Failed to read UF2 block");
  193. flasher_emit_error(FlasherErrorBadFile);
  194. break;
  195. }
  196. if(!flasher_program_page(size_programmed, buf, W25Q128_PAGE_SIZE)) {
  197. FURI_LOG_E(TAG, "Failed to program flash page at address 0x%zX", size_programmed);
  198. flasher_emit_error(FlasherErrorDisconnect);
  199. break;
  200. }
  201. size_programmed += W25Q128_PAGE_SIZE;
  202. const uint8_t program_progress = (size_programmed * 100UL) / data_size;
  203. if(program_progress != prev_progress) {
  204. prev_progress = program_progress;
  205. flasher_emit_progress(
  206. PROGRESS_VERIFY_WEIGHT + PROGRESS_ERASE_WEIGHT,
  207. PROGRESS_PROGRAM_WEIGHT,
  208. program_progress);
  209. FURI_LOG_D(TAG, "Programming flash: %u%%", program_progress);
  210. }
  211. }
  212. return size_programmed == data_size;
  213. }
  214. void flasher_start(const char* file_path) {
  215. FURI_LOG_D(TAG, "Flashing firmware from file: %s", file_path);
  216. Storage* storage = furi_record_open(RECORD_STORAGE);
  217. File* file = storage_file_alloc(storage);
  218. size_t data_size;
  219. do {
  220. if(!flasher_prepare_target()) break;
  221. if(!flasher_prepare_file(file, file_path)) break;
  222. if(!flasher_verify_file(file, &data_size)) break;
  223. if(!flasher_erase_flash(data_size)) break;
  224. if(!flasher_program_flash(file, data_size)) break;
  225. flasher_emit_success();
  226. } while(false);
  227. storage_file_free(file);
  228. furi_record_close(RECORD_STORAGE);
  229. }