flasher.c 7.4 KB

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