swd_probe_app.c 61 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931
  1. #include <furi.h>
  2. #include <gui/gui.h>
  3. #include <gui/elements.h>
  4. #include <dialogs/dialogs.h>
  5. #include <input/input.h>
  6. #include <storage/storage.h>
  7. #include <stdlib.h>
  8. #include <dolphin/dolphin.h>
  9. #include <notification/notification.h>
  10. #include <notification/notification_messages.h>
  11. #include "swd_probe_app.h"
  12. #include "swd_probe_icons.h"
  13. #include "jep106.h"
  14. #define COUNT(x) (sizeof(x) / sizeof((x)[0]))
  15. static void render_callback(Canvas* const canvas, void* cb_ctx);
  16. static bool swd_message_process(AppFSM* ctx);
  17. static const GpioPin* gpios[] = {
  18. &gpio_ext_pc0,
  19. &gpio_ext_pc1,
  20. &gpio_ext_pc3,
  21. &gpio_ext_pb2,
  22. &gpio_ext_pb3,
  23. &gpio_ext_pa4,
  24. &gpio_ext_pa6,
  25. &gpio_ext_pa7};
  26. static const char* gpio_names[] = {"PC0", "PC1", "PC3", "PB2", "PB3", "PA4", "PA6", "PA7"};
  27. /* bit set: clock, else data */
  28. static const uint8_t gpio_direction_mask[6] =
  29. {0b10101010, 0b01010101, 0b11001100, 0b00110011, 0b11110000, 0b00001111};
  30. static const uint8_t gpio_direction_ind[6] = "-\\||/-";
  31. static bool has_multiple_bits(uint8_t x) {
  32. return (x & (x - 1)) != 0;
  33. }
  34. static int get_bit_num(uint8_t x) {
  35. return __builtin_ctz(x);
  36. }
  37. static const char* gpio_name(uint8_t mask) {
  38. if(has_multiple_bits(mask)) {
  39. return "Pxx";
  40. }
  41. int io = get_bit_num(mask);
  42. if(io >= COUNT(gpio_names)) {
  43. return "Pxx";
  44. }
  45. return gpio_names[io];
  46. }
  47. static void swd_configure_pins(AppFSM* const ctx, bool output) {
  48. if(ctx->mode_page != ModePageScan && ctx->io_num_swc < 8 && ctx->io_num_swd < 8) {
  49. furi_hal_gpio_init(
  50. gpios[ctx->io_num_swc], GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  51. if(!output) {
  52. furi_hal_gpio_init(
  53. gpios[ctx->io_num_swd], GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
  54. } else {
  55. furi_hal_gpio_init(
  56. gpios[ctx->io_num_swd], GpioModeOutputOpenDrain, GpioPullUp, GpioSpeedVeryHigh);
  57. }
  58. return;
  59. }
  60. for(int io = 0; io < 8; io++) {
  61. uint8_t bitmask = 1 << io;
  62. /* if neither candidate for SWC nor SWD then skip */
  63. if(!(ctx->io_swc & bitmask) && !(ctx->io_swd & bitmask)) {
  64. furi_hal_gpio_init(gpios[io], GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
  65. continue;
  66. }
  67. if(ctx->current_mask & bitmask) {
  68. /* set for clock */
  69. furi_hal_gpio_init(gpios[io], GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  70. } else {
  71. /* set for data */
  72. if(!output) {
  73. furi_hal_gpio_init(gpios[io], GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
  74. } else {
  75. furi_hal_gpio_init(
  76. gpios[io], GpioModeOutputOpenDrain, GpioPullUp, GpioSpeedVeryHigh);
  77. }
  78. }
  79. }
  80. }
  81. static void swd_set_clock(AppFSM* const ctx, const uint8_t level) {
  82. if(ctx->mode_page != ModePageScan && ctx->io_num_swc < 8) {
  83. furi_hal_gpio_write(gpios[ctx->io_num_swc], level);
  84. return;
  85. }
  86. for(int io = 0; io < 8; io++) {
  87. uint8_t bitmask = 1 << io;
  88. /* if no candidate for SWC then skip */
  89. if(!(ctx->io_swc & bitmask)) {
  90. continue;
  91. }
  92. if(ctx->current_mask & bitmask) {
  93. furi_hal_gpio_write(gpios[io], level);
  94. }
  95. }
  96. }
  97. static void swd_set_data(AppFSM* const ctx, const uint8_t level) {
  98. if(ctx->mode_page != ModePageScan && ctx->io_num_swd < 8) {
  99. furi_hal_gpio_write(gpios[ctx->io_num_swd], level);
  100. return;
  101. }
  102. for(int io = 0; io < 8; io++) {
  103. uint8_t bitmask = 1 << io;
  104. /* if no candidate for SWD then skip */
  105. if(!(ctx->io_swd & bitmask)) {
  106. continue;
  107. }
  108. if(!(ctx->current_mask & bitmask)) {
  109. furi_hal_gpio_write(gpios[io], level);
  110. }
  111. }
  112. }
  113. static uint8_t swd_get_data(AppFSM* const ctx) {
  114. if(ctx->mode_page != ModePageScan && ctx->io_num_swd < 8) {
  115. return furi_hal_gpio_read(gpios[ctx->io_num_swd]);
  116. }
  117. uint8_t bits = 0;
  118. for(int io = 0; io < 8; io++) {
  119. uint8_t bitmask = 1 << io;
  120. /* if no candidate for SWD then skip */
  121. if(!(ctx->io_swd & bitmask)) {
  122. continue;
  123. }
  124. bits |= furi_hal_gpio_read(gpios[io]) ? bitmask : 0;
  125. }
  126. return bits;
  127. }
  128. static void swd_clock_delay(AppFSM* const ctx) {
  129. if(ctx->swd_clock_delay) {
  130. furi_delay_us(ctx->swd_clock_delay);
  131. }
  132. }
  133. static void swd_write_bit(AppFSM* const ctx, bool level) {
  134. swd_set_clock(ctx, 0);
  135. swd_set_data(ctx, level);
  136. swd_clock_delay(ctx);
  137. swd_set_clock(ctx, 1);
  138. swd_clock_delay(ctx);
  139. swd_set_clock(ctx, 0);
  140. }
  141. static uint8_t swd_read_bit(AppFSM* const ctx) {
  142. swd_set_clock(ctx, 1);
  143. swd_clock_delay(ctx);
  144. swd_set_clock(ctx, 0);
  145. uint8_t bits = swd_get_data(ctx);
  146. swd_clock_delay(ctx);
  147. swd_set_clock(ctx, 1);
  148. return bits;
  149. }
  150. /* send a byte or less LSB-first */
  151. static void swd_write_byte(AppFSM* const ctx, const uint8_t data, size_t bits) {
  152. for(size_t pos = 0; pos < bits; pos++) {
  153. swd_write_bit(ctx, data & (1 << pos));
  154. }
  155. }
  156. /* send a sequence of bytes LSB-first */
  157. static void swd_write(AppFSM* const ctx, const uint8_t* data, size_t bits) {
  158. size_t byte_pos = 0;
  159. while(bits > 0) {
  160. size_t remain = (bits > 8) ? 8 : bits;
  161. swd_write_byte(ctx, data[byte_pos++], remain);
  162. bits -= remain;
  163. }
  164. }
  165. static uint8_t swd_transfer(AppFSM* const ctx, bool ap, bool write, uint8_t a23, uint32_t* data) {
  166. swd_set_data(ctx, false);
  167. swd_configure_pins(ctx, true);
  168. swd_write_byte(ctx, 0, 8);
  169. uint8_t request[] = {0};
  170. request[0] |= 0x01; /* start bit*/
  171. request[0] |= ap ? 0x02 : 0; /* APnDP */
  172. request[0] |= write ? 0 : 0x04; /* operation */
  173. request[0] |= (a23 & 0x01) ? 0x08 : 0; /* A[2:3] */
  174. request[0] |= (a23 & 0x02) ? 0x10 : 0; /* A[2:3] */
  175. request[0] |= 0x80; /* park bit */
  176. request[0] |= __builtin_parity(request[0]) ? 0x20 : 0; /* parity */
  177. swd_write(ctx, request, sizeof(request) * 8);
  178. /* turnaround cycle */
  179. swd_configure_pins(ctx, false);
  180. uint8_t ack = 0;
  181. /* receive 3 ACK bits */
  182. for(int pos = 0; pos < 3; pos++) {
  183. ack >>= 1;
  184. ack |= swd_read_bit(ctx) ? 0x04 : 0;
  185. }
  186. /* force ABORT/CTRL to always work */
  187. if(!ap && a23 == 0) {
  188. ack = 1;
  189. }
  190. if(ack != 0x01) {
  191. return ack;
  192. }
  193. if(write) {
  194. swd_write_bit(ctx, 0);
  195. swd_configure_pins(ctx, true);
  196. /* send 32 WDATA bits */
  197. for(int pos = 0; pos < 32; pos++) {
  198. swd_write_bit(ctx, *data & (1 << pos));
  199. }
  200. /* send parity bit */
  201. swd_write_bit(ctx, __builtin_parity(*data));
  202. } else {
  203. *data = 0;
  204. /* receive 32 RDATA bits */
  205. for(int pos = 0; pos < 32; pos++) {
  206. *data >>= 1;
  207. *data |= swd_read_bit(ctx) ? 0x80000000 : 0;
  208. }
  209. /* receive parity bit */
  210. bool parity = swd_read_bit(ctx);
  211. if(parity != __builtin_parity(*data)) {
  212. return 8;
  213. }
  214. }
  215. swd_set_data(ctx, false);
  216. swd_configure_pins(ctx, true);
  217. return ack;
  218. }
  219. /* A line reset is achieved by holding the data signal HIGH for at least 50 clock cycles, followed by at least two idle cycles. */
  220. static void swd_line_reset(AppFSM* const ctx) {
  221. for(int bitcount = 0; bitcount < 50; bitcount += 8) {
  222. swd_write_byte(ctx, 0xFF, 8);
  223. }
  224. swd_write_byte(ctx, 0, 8);
  225. }
  226. static void swd_abort(AppFSM* const ctx) {
  227. uint32_t dpidr;
  228. /* first reset the line */
  229. swd_line_reset(ctx);
  230. swd_transfer(ctx, false, false, 0, &dpidr);
  231. uint32_t abort = 0x0E;
  232. swd_transfer(ctx, false, true, 0, &abort);
  233. }
  234. static void swd_abort_simple(AppFSM* const ctx) {
  235. uint32_t abort = 0x0E;
  236. swd_transfer(ctx, false, true, 0, &abort);
  237. uint32_t dpidr;
  238. if(swd_transfer(ctx, false, false, 0, &dpidr) != 1) {
  239. swd_abort(ctx);
  240. }
  241. }
  242. static uint8_t swd_select(AppFSM* const ctx, uint8_t ap_sel, uint8_t ap_bank, uint8_t dp_bank) {
  243. uint32_t bank_reg = (ap_sel << 24) | ((ap_bank & 0x0F) << 4) | (dp_bank & 0x0F);
  244. uint8_t ret = swd_transfer(ctx, false, true, 2, &bank_reg);
  245. if(ret != 1) {
  246. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_select: failed: %d", ret);
  247. }
  248. return ret;
  249. }
  250. static uint8_t
  251. swd_read_dpbank(AppFSM* const ctx, uint8_t dp_off, uint8_t dp_bank, uint32_t* data) {
  252. /* select target bank */
  253. uint8_t ret = swd_select(ctx, 0, 0, dp_bank);
  254. if(ret != 1) {
  255. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_read_dpbank: swd_select failed");
  256. return ret;
  257. }
  258. /* read data from it */
  259. *data = 0;
  260. ret = swd_transfer(ctx, false, false, dp_off, data);
  261. if(ret != 1) {
  262. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_read_dpbank: failed: %d", ret);
  263. return ret;
  264. }
  265. /* reset bank to zero again */
  266. ret = swd_select(ctx, 0, 0, 0);
  267. return ret;
  268. }
  269. static uint8_t swd_read_ap(AppFSM* const ctx, uint8_t ap, uint8_t ap_off, uint32_t* data) {
  270. uint8_t ret = swd_select(ctx, ap, (ap_off >> 4) & 0x0F, 0);
  271. if(ret != 1) {
  272. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_read_ap: swd_select failed");
  273. return ret;
  274. }
  275. ret = swd_transfer(ctx, true, false, (ap_off >> 2) & 3, data);
  276. *data = 0;
  277. ret = swd_transfer(ctx, true, false, (ap_off >> 2) & 3, data);
  278. if(ret != 1) {
  279. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_read_ap: failed: %d", ret);
  280. return ret;
  281. }
  282. return ret;
  283. }
  284. static uint8_t swd_read_ap_single(AppFSM* const ctx, uint8_t ap, uint8_t ap_off, uint32_t* data) {
  285. uint8_t ret = swd_select(ctx, ap, (ap_off >> 4) & 0x0F, 0);
  286. if(ret != 1) {
  287. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_read_ap_single: swd_select failed");
  288. return ret;
  289. }
  290. *data = 0;
  291. ret = swd_transfer(ctx, true, false, (ap_off >> 2) & 3, data);
  292. if(ret != 1) {
  293. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_read_ap_single: failed: %d", ret);
  294. return ret;
  295. }
  296. return ret;
  297. }
  298. static uint8_t swd_write_ap(AppFSM* const ctx, uint8_t ap, uint8_t ap_off, uint32_t data) {
  299. uint8_t ret = swd_select(ctx, ap, (ap_off >> 4) & 0x0F, 0);
  300. if(ret != 1) {
  301. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_write_ap: swd_select failed");
  302. return ret;
  303. }
  304. ret = swd_transfer(ctx, true, true, (ap_off >> 2) & 3, &data);
  305. if(ret != 1) {
  306. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_write_ap: failed: %d", ret);
  307. return ret;
  308. }
  309. return ret;
  310. }
  311. static uint8_t swd_write_memory(AppFSM* const ctx, uint8_t ap, uint32_t address, uint32_t data) {
  312. uint8_t ret = 0;
  313. ret |= swd_write_ap(ctx, ap, MEMAP_CSW, 0x03000012);
  314. ret |= swd_write_ap(ctx, ap, MEMAP_TAR, address);
  315. ret |= swd_write_ap(ctx, ap, MEMAP_DRW, data);
  316. if(ret != 1) {
  317. swd_abort(ctx);
  318. }
  319. return ret;
  320. }
  321. static uint8_t swd_read_memory(AppFSM* const ctx, uint8_t ap, uint32_t address, uint32_t* data) {
  322. uint8_t ret = 0;
  323. ret |= swd_write_ap(ctx, ap, MEMAP_CSW, 0x03000012);
  324. ret |= swd_write_ap(ctx, ap, MEMAP_TAR, address);
  325. ret |= swd_read_ap(ctx, ap, MEMAP_DRW, data);
  326. if(ret != 1) {
  327. swd_abort(ctx);
  328. }
  329. return ret;
  330. }
  331. static uint8_t swd_read_memory_block(
  332. AppFSM* const ctx,
  333. uint8_t ap,
  334. uint32_t address,
  335. uint8_t* buf,
  336. uint32_t len) {
  337. uint8_t ret = 0;
  338. uint32_t data = 0;
  339. bool first = true;
  340. ret |= swd_write_ap(ctx, ap, MEMAP_CSW, 0x03000012);
  341. ret |= swd_write_ap(ctx, ap, MEMAP_TAR, address);
  342. ret |= swd_read_ap_single(ctx, ap, MEMAP_DRW, &data);
  343. for(size_t pos = 0; pos < len; pos += 4) {
  344. ret |= swd_read_ap_single(ctx, ap, MEMAP_DRW, &data);
  345. memcpy(&buf[pos], &data, 4);
  346. if(ret != 1) {
  347. swd_abort(ctx);
  348. return ret;
  349. }
  350. }
  351. return ret;
  352. }
  353. static uint32_t swd_detect(AppFSM* const ctx) {
  354. swd_set_data(ctx, false);
  355. swd_configure_pins(ctx, true);
  356. uint8_t data[] = {0xA5};
  357. swd_write(ctx, data, sizeof(data) * 8);
  358. /* turnaround cycle */
  359. swd_configure_pins(ctx, false);
  360. uint8_t ack_bits[3];
  361. uint8_t rdata[32];
  362. /* receive 3 ACK bits */
  363. for(int pos = 0; pos < 3; pos++) {
  364. ack_bits[pos] = swd_read_bit(ctx);
  365. }
  366. /* receive 32 RDATA bits */
  367. for(int pos = 0; pos < 32; pos++) {
  368. rdata[pos] = swd_read_bit(ctx);
  369. }
  370. /* receive parity bit */
  371. uint8_t parity = swd_read_bit(ctx);
  372. for(int io = 0; io < 8; io++) {
  373. uint8_t bitmask = 1 << io;
  374. /* skip if it's a clock */
  375. if(ctx->current_mask & bitmask) {
  376. continue;
  377. }
  378. uint8_t ack = 0;
  379. for(int pos = 0; pos < 3; pos++) {
  380. ack >>= 1;
  381. ack |= (ack_bits[pos] & bitmask) ? 4 : 0;
  382. }
  383. uint32_t dpidr = 0;
  384. for(int pos = 0; pos < 32; pos++) {
  385. dpidr >>= 1;
  386. dpidr |= (rdata[pos] & bitmask) ? 0x80000000 : 0;
  387. }
  388. if(ack == 1 && dpidr != 0 && dpidr != 0xFFFFFFFF) {
  389. bool received_parity = (parity & bitmask);
  390. if(__builtin_parity(dpidr) == received_parity) {
  391. ctx->dp_regs.dpidr = dpidr;
  392. ctx->dp_regs.dpidr_ok = true;
  393. ctx->detected = true;
  394. ctx->io_swd = bitmask;
  395. ctx->io_swc &= ctx->current_mask;
  396. furi_log_print_format(
  397. FuriLogLevelDefault,
  398. TAG,
  399. "swd_detect: data: %08lX, io_swd %02X, io_swc %02X",
  400. dpidr,
  401. ctx->io_swd,
  402. ctx->io_swc);
  403. if(!has_multiple_bits(ctx->io_swc)) {
  404. ctx->io_num_swd = get_bit_num(ctx->io_swd);
  405. ctx->io_num_swc = get_bit_num(ctx->io_swc);
  406. }
  407. }
  408. }
  409. }
  410. swd_set_data(ctx, false);
  411. swd_configure_pins(ctx, true);
  412. return 0;
  413. }
  414. static void swd_scan(AppFSM* const ctx) {
  415. /* To switch SWJ-DP from JTAG to SWD operation:
  416. 1. Send at least 50 SWCLKTCK cycles with SWDIOTMS HIGH. This ensures that the current interface is in its reset state. The JTAG interface only detects the 16-bit JTAG-to-SWD sequence starting from the Test-Logic-Reset state.
  417. 2. Send the 16-bit JTAG-to-SWD select sequence 0x79e7 on SWDIOTMS.
  418. 3. Send at least 50 SWCLKTCK cycles with SWDIOTMS HIGH. This ensures that if SWJ-DP was already in SWD operation before sending the select sequence, the SWD interface enters line reset state.
  419. */
  420. swd_configure_pins(ctx, true);
  421. /* reset JTAG interface */
  422. for(int bitcount = 0; bitcount < 50; bitcount += 8) {
  423. swd_write_byte(ctx, 0xFF, 8);
  424. }
  425. /* Send the 16-bit JTAG-to-SWD select sequence */
  426. swd_write_byte(ctx, 0x9E, 8);
  427. swd_write_byte(ctx, 0xE7, 8);
  428. /* resynchronize SWD */
  429. swd_line_reset(ctx);
  430. swd_detect(ctx);
  431. }
  432. static bool swd_ensure_powerup(AppFSM* const ctx) {
  433. if(!(ctx->dp_regs.ctrlstat & (CSYSPWRUPREQ | CDBGPWRUPREQ))) {
  434. /* fetch current CTRL/STAT */
  435. swd_transfer(ctx, false, false, 1, &ctx->dp_regs.ctrlstat);
  436. /* enable requests */
  437. ctx->dp_regs.ctrlstat |= CDBGPWRUPREQ;
  438. ctx->dp_regs.ctrlstat |= CSYSPWRUPREQ;
  439. furi_log_print_format(FuriLogLevelDefault, TAG, "no (CSYSPWRUPREQ | CDBGPWRUPREQ)");
  440. swd_transfer(ctx, false, true, 1, &ctx->dp_regs.ctrlstat);
  441. return false;
  442. }
  443. if(!(ctx->dp_regs.ctrlstat & CDBGPWRUPACK)) {
  444. /* fetch current CTRL/STAT */
  445. swd_transfer(ctx, false, false, 1, &ctx->dp_regs.ctrlstat);
  446. furi_log_print_format(FuriLogLevelDefault, TAG, "no CDBGPWRUPACK");
  447. return false;
  448. }
  449. return true;
  450. }
  451. static void swd_apscan_reset(AppFSM* const ctx) {
  452. for(int reset_ap = 0; reset_ap < COUNT(ctx->apidr_info); reset_ap++) {
  453. ctx->apidr_info[reset_ap].tested = false;
  454. }
  455. }
  456. static bool swd_apscan_test(AppFSM* const ctx, uint32_t ap) {
  457. furi_assert(ctx);
  458. furi_assert(ap < sizeof(ctx->apidr_info));
  459. ctx->apidr_info[ap].tested = true;
  460. uint32_t data = 0;
  461. uint32_t base = 0;
  462. if(swd_read_ap(ctx, ap, AP_IDR, &data) != 1) {
  463. swd_abort(ctx);
  464. return false;
  465. }
  466. if(data == 0) {
  467. return false;
  468. }
  469. furi_log_print_format(FuriLogLevelDefault, TAG, "AP%lu detected", ap);
  470. ctx->apidr_info[ap].ok = true;
  471. ctx->apidr_info[ap].revision = (data >> 24) & 0x0F;
  472. ctx->apidr_info[ap].designer = (data >> 17) & 0x3FF;
  473. ctx->apidr_info[ap].class = (data >> 13) & 0x0F;
  474. ctx->apidr_info[ap].variant = (data >> 4) & 0x0F;
  475. ctx->apidr_info[ap].type = (data >> 0) & 0x0F;
  476. if(swd_read_ap(ctx, ap, AP_BASE, &ctx->apidr_info[ap].base) != 1) {
  477. swd_abort(ctx);
  478. return false;
  479. }
  480. return true;
  481. }
  482. /************************** script helpers **************************/
  483. static bool swd_script_seek_newline(ScriptContext* ctx) {
  484. while(true) {
  485. uint8_t ch = 0;
  486. uint16_t ret = storage_file_read(ctx->script_file, &ch, 1);
  487. if(ret != 1) {
  488. return false;
  489. }
  490. if(ch == '\n') {
  491. return true;
  492. }
  493. }
  494. }
  495. static bool swd_script_skip_whitespace(ScriptContext* ctx) {
  496. while(true) {
  497. uint8_t ch = 0;
  498. uint64_t start_pos = storage_file_tell(ctx->script_file);
  499. uint16_t ret = storage_file_read(ctx->script_file, &ch, 1);
  500. if(ret != 1) {
  501. return false;
  502. }
  503. if(ch == '\n') {
  504. return false;
  505. }
  506. if(ch != ' ') {
  507. storage_file_seek(ctx->script_file, start_pos, true);
  508. return true;
  509. }
  510. }
  511. }
  512. static bool swd_script_get_string(ScriptContext* ctx, char* str, size_t max_length) {
  513. bool quot = false;
  514. size_t pos = 0;
  515. str[pos] = '\000';
  516. while(true) {
  517. char ch = 0;
  518. uint64_t start_pos = storage_file_tell(ctx->script_file);
  519. uint16_t ret = storage_file_read(ctx->script_file, &ch, 1);
  520. if(ret != 1) {
  521. return false;
  522. }
  523. if(ch == '"') {
  524. quot = !quot;
  525. continue;
  526. }
  527. if(!quot) {
  528. if(ch == ' ') {
  529. return true;
  530. }
  531. if(ch == '\r' || ch == '\n') {
  532. storage_file_seek(ctx->script_file, start_pos, true);
  533. return true;
  534. }
  535. }
  536. if(pos + 2 > max_length) {
  537. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_script_get_string: too long");
  538. return false;
  539. }
  540. str[pos++] = ch;
  541. str[pos] = '\000';
  542. }
  543. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_script_get_string: got '%s'", str);
  544. return true;
  545. }
  546. static bool swd_script_get_number(ScriptContext* ctx, uint32_t* number) {
  547. char str[16];
  548. if(!swd_script_get_string(ctx, str, sizeof(str))) {
  549. furi_log_print_format(
  550. FuriLogLevelDefault, TAG, "swd_script_get_number: could not get string");
  551. return false;
  552. }
  553. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_script_get_number: got '%s'", str);
  554. size_t pos = 0;
  555. *number = 0;
  556. /* hex number? */
  557. if(!strncmp(str, "0x", 2)) {
  558. pos += 2;
  559. while(str[pos]) {
  560. uint8_t ch = str[pos++];
  561. uint8_t ch_num = ch - '0';
  562. uint8_t ch_hex = (ch & ~0x20) - 'A';
  563. *number <<= 4;
  564. if(ch_num <= 10) {
  565. *number += ch_num;
  566. } else if(ch_hex <= 5) {
  567. *number += 10 + ch_hex;
  568. } else {
  569. return false;
  570. }
  571. }
  572. } else {
  573. while(str[pos]) {
  574. uint8_t ch = str[pos++];
  575. uint8_t ch_num = ch - '0';
  576. *number *= 10;
  577. if(ch_num < 10) {
  578. *number += ch_num;
  579. } else {
  580. return false;
  581. }
  582. }
  583. }
  584. return true;
  585. }
  586. static void swd_script_gui_refresh(ScriptContext* ctx) {
  587. //Canvas* canvas = gui_direct_draw_acquire(ctx->app->gui);
  588. furi_log_print_format(FuriLogLevelDefault, TAG, "Status: %s", ctx->app->state_string);
  589. //render_callback(canvas, &ctx->app->state_mutex);
  590. if(furi_message_queue_get_count(ctx->app->event_queue) > 0) {
  591. swd_message_process(ctx->app);
  592. }
  593. view_port_update(ctx->app->view_port);
  594. //gui_direct_draw_release(ctx->app->gui);
  595. }
  596. /************************** script functions **************************/
  597. static bool swd_scriptfunc_comment(ScriptContext* ctx) {
  598. furi_log_print_format(FuriLogLevelDefault, TAG, "comment");
  599. swd_script_seek_newline(ctx);
  600. return true;
  601. }
  602. static bool swd_scriptfunc_beep(ScriptContext* ctx) {
  603. uint32_t sound = 0;
  604. furi_log_print_format(FuriLogLevelDefault, TAG, "beep");
  605. swd_script_skip_whitespace(ctx);
  606. swd_script_get_number(ctx, &sound);
  607. notification_message_block(ctx->app->notification, seq_sounds[sound]);
  608. swd_script_seek_newline(ctx);
  609. return true;
  610. }
  611. static bool swd_scriptfunc_message(ScriptContext* ctx) {
  612. uint32_t wait_time = 0;
  613. char message[256];
  614. if(!swd_script_skip_whitespace(ctx)) {
  615. furi_log_print_format(FuriLogLevelDefault, TAG, "message: missing whitespace");
  616. return false;
  617. }
  618. if(!swd_script_get_number(ctx, &wait_time)) {
  619. furi_log_print_format(FuriLogLevelDefault, TAG, "message: failed to parse wait_time");
  620. return false;
  621. }
  622. if(!swd_script_get_string(ctx, message, sizeof(message))) {
  623. furi_log_print_format(FuriLogLevelDefault, TAG, "message: failed to parse message");
  624. return false;
  625. }
  626. if(wait_time <= 60 * 1000) {
  627. strcpy(ctx->app->state_string, message);
  628. swd_script_gui_refresh(ctx);
  629. furi_delay_ms(wait_time);
  630. }
  631. swd_script_seek_newline(ctx);
  632. return true;
  633. }
  634. static bool swd_scriptfunc_swd_clock_delay(ScriptContext* ctx) {
  635. uint32_t swd_clock_delay = 0;
  636. if(!swd_script_skip_whitespace(ctx)) {
  637. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_clock_delay: missing whitespace");
  638. return false;
  639. }
  640. if(!swd_script_get_number(ctx, &swd_clock_delay)) {
  641. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_clock_delay: failed to parse");
  642. return false;
  643. }
  644. if(swd_clock_delay <= 1000000) {
  645. ctx->app->swd_clock_delay = swd_clock_delay;
  646. } else {
  647. furi_log_print_format(
  648. FuriLogLevelDefault, TAG, "swd_clock_delay: value must be between 1 and 1000000");
  649. }
  650. swd_script_seek_newline(ctx);
  651. return true;
  652. }
  653. static bool swd_scriptfunc_maxtries(ScriptContext* ctx) {
  654. uint32_t max_tries = 0;
  655. if(!swd_script_skip_whitespace(ctx)) {
  656. furi_log_print_format(FuriLogLevelDefault, TAG, "max_tries: missing whitespace");
  657. return false;
  658. }
  659. if(!swd_script_get_number(ctx, &max_tries)) {
  660. furi_log_print_format(FuriLogLevelDefault, TAG, "max_tries: failed to parse");
  661. return false;
  662. }
  663. if(max_tries >= 1 && max_tries <= 1024) {
  664. ctx->max_tries = max_tries;
  665. } else {
  666. furi_log_print_format(
  667. FuriLogLevelDefault, TAG, "max_tries: value must be between 1 and 1024");
  668. }
  669. swd_script_seek_newline(ctx);
  670. return true;
  671. }
  672. static bool swd_scriptfunc_blocksize(ScriptContext* ctx) {
  673. uint32_t block_size = 0;
  674. if(!swd_script_skip_whitespace(ctx)) {
  675. furi_log_print_format(FuriLogLevelDefault, TAG, "block_size: missing whitespace");
  676. return false;
  677. }
  678. if(!swd_script_get_number(ctx, &block_size)) {
  679. furi_log_print_format(FuriLogLevelDefault, TAG, "block_size: failed to parse");
  680. return false;
  681. }
  682. if(block_size >= 4 && block_size <= 0x1000) {
  683. ctx->block_size = block_size;
  684. } else {
  685. furi_log_print_format(
  686. FuriLogLevelDefault, TAG, "block_size: value must be between 4 and 4096");
  687. }
  688. swd_script_seek_newline(ctx);
  689. return true;
  690. }
  691. static bool swd_scriptfunc_apselect(ScriptContext* ctx) {
  692. uint32_t ap = 0;
  693. if(!swd_script_skip_whitespace(ctx)) {
  694. furi_log_print_format(FuriLogLevelDefault, TAG, "apselect: missing whitespace");
  695. return false;
  696. }
  697. if(!swd_script_get_number(ctx, &ap)) {
  698. furi_log_print_format(FuriLogLevelDefault, TAG, "apselect: failed to parse");
  699. return false;
  700. }
  701. if(!swd_apscan_test(ctx->app, ap)) {
  702. furi_log_print_format(FuriLogLevelDefault, TAG, "apselect: no selected AP");
  703. return false;
  704. }
  705. ctx->selected_ap = ap;
  706. swd_script_seek_newline(ctx);
  707. return true;
  708. }
  709. static bool swd_scriptfunc_apscan(ScriptContext* ctx) {
  710. furi_log_print_format(FuriLogLevelDefault, TAG, "apscan: Scanning APs");
  711. for(uint32_t ap = 0; ap < 255; ap++) {
  712. snprintf(ctx->app->state_string, sizeof(ctx->app->state_string), "Scan AP %lu", ap);
  713. swd_script_gui_refresh(ctx);
  714. if(swd_apscan_test(ctx->app, ap)) {
  715. furi_log_print_format(FuriLogLevelDefault, TAG, "apscan: AP%lu detected", ap);
  716. }
  717. }
  718. swd_script_seek_newline(ctx);
  719. return true;
  720. }
  721. static bool swd_scriptfunc_abort(ScriptContext* ctx) {
  722. furi_log_print_format(FuriLogLevelDefault, TAG, "abort: Aborting");
  723. swd_abort(ctx->app);
  724. swd_script_seek_newline(ctx);
  725. return true;
  726. }
  727. static bool swd_scriptfunc_mem_dump(ScriptContext* ctx) {
  728. char filename[MAX_FILE_LENGTH];
  729. uint32_t address = 0;
  730. uint32_t length = 0;
  731. uint32_t flags = 0;
  732. bool success = true;
  733. /* get file */
  734. if(!swd_script_skip_whitespace(ctx)) {
  735. furi_log_print_format(FuriLogLevelDefault, TAG, "mem_dump: missing whitespace");
  736. return false;
  737. }
  738. if(!swd_script_get_string(ctx, filename, sizeof(filename))) {
  739. furi_log_print_format(FuriLogLevelDefault, TAG, "mem_dump: failed to parse filename");
  740. return false;
  741. }
  742. /* get address */
  743. if(!swd_script_get_number(ctx, &address)) {
  744. furi_log_print_format(FuriLogLevelDefault, TAG, "mem_dump: failed to parse address");
  745. return false;
  746. }
  747. /* get length */
  748. if(!swd_script_get_number(ctx, &length)) {
  749. furi_log_print_format(FuriLogLevelDefault, TAG, "mem_dump: failed to parse length");
  750. return false;
  751. }
  752. /* get flags */
  753. if(swd_script_get_number(ctx, &flags)) {
  754. furi_log_print_format(FuriLogLevelDefault, TAG, "mem_dump: found extra flags");
  755. }
  756. furi_log_print_format(
  757. FuriLogLevelDefault,
  758. TAG,
  759. "mem_dump: would dump %08lX, len %08lX into %s",
  760. address,
  761. length,
  762. filename);
  763. File* dump = storage_file_alloc(ctx->app->storage);
  764. if(!storage_file_open(dump, filename, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
  765. storage_file_free(dump);
  766. snprintf(ctx->app->state_string, sizeof(ctx->app->state_string), "Failed to create file");
  767. swd_script_gui_refresh(ctx);
  768. notification_message_block(ctx->app->notification, &seq_error);
  769. return false;
  770. }
  771. if(ctx->block_size == 0) {
  772. ctx->block_size = 0x100;
  773. }
  774. if(ctx->block_size > 0x1000) {
  775. ctx->block_size = 0x1000;
  776. }
  777. uint8_t* buffer = malloc(ctx->block_size);
  778. for(uint32_t pos = 0; pos < length; pos += ctx->block_size) {
  779. if((pos & 0xFF) == 0) {
  780. int pct = pos * 100 / length;
  781. snprintf(
  782. ctx->app->state_string,
  783. sizeof(ctx->app->state_string),
  784. "Dump %08lX (%d%%)",
  785. pos,
  786. pct);
  787. swd_script_gui_refresh(ctx);
  788. }
  789. bool read_ok = false;
  790. uint32_t data = 0;
  791. for(uint32_t tries = 0; tries < ctx->max_tries; tries++) {
  792. if(ctx->abort) {
  793. furi_log_print_format(FuriLogLevelDefault, TAG, "mem_dump: aborting read");
  794. break;
  795. }
  796. uint32_t ret = swd_read_memory_block(
  797. ctx->app, ctx->selected_ap, address + pos, buffer, ctx->block_size);
  798. read_ok = (ret == 1);
  799. if(!read_ok) {
  800. snprintf(
  801. ctx->app->state_string,
  802. sizeof(ctx->app->state_string),
  803. "Failed at 0x%08lX",
  804. address + pos);
  805. swd_script_gui_refresh(ctx);
  806. furi_delay_ms(100);
  807. } else {
  808. break;
  809. }
  810. }
  811. if(ctx->abort) {
  812. furi_log_print_format(FuriLogLevelDefault, TAG, "mem_dump: aborting");
  813. break;
  814. }
  815. if(!read_ok) {
  816. /* flags == 1: "continue reading even if it fails" */
  817. /* flags == 2: "its okay if cannot dump fully" */
  818. if(flags & 1) {
  819. /* set all content to a known value as indication */
  820. for(size_t fill_pos = 0; fill_pos < ctx->block_size; fill_pos += 4) {
  821. *((uint32_t*)&buffer[fill_pos]) = 0xDEADFACE;
  822. }
  823. } else if(flags & 2) {
  824. success = (pos > 0);
  825. break;
  826. } else {
  827. notification_message_block(ctx->app->notification, &seq_error);
  828. success = false;
  829. break;
  830. }
  831. }
  832. storage_file_write(dump, buffer, ctx->block_size);
  833. }
  834. storage_file_close(dump);
  835. swd_script_seek_newline(ctx);
  836. free(buffer);
  837. return success;
  838. }
  839. static bool swd_scriptfunc_mem_write(ScriptContext* ctx) {
  840. uint32_t address = 0;
  841. uint32_t data = 0;
  842. bool success = true;
  843. /* get file */
  844. if(!swd_script_skip_whitespace(ctx)) {
  845. furi_log_print_format(FuriLogLevelDefault, TAG, "mem_write: missing whitespace");
  846. return false;
  847. }
  848. /* get address */
  849. if(!swd_script_get_number(ctx, &address)) {
  850. furi_log_print_format(FuriLogLevelDefault, TAG, "mem_write: failed to parse 1");
  851. return false;
  852. }
  853. /* get data */
  854. if(!swd_script_get_number(ctx, &data)) {
  855. furi_log_print_format(FuriLogLevelDefault, TAG, "mem_write: failed to parse 2");
  856. return false;
  857. }
  858. furi_log_print_format(
  859. FuriLogLevelDefault, TAG, "mem_write: write %08lX to %08lX", data, address);
  860. bool access_ok = false;
  861. for(uint32_t tries = 0; tries < ctx->max_tries; tries++) {
  862. if(ctx->abort) {
  863. furi_log_print_format(FuriLogLevelDefault, TAG, "mem_write: aborting");
  864. break;
  865. }
  866. access_ok = swd_write_memory(ctx->app, ctx->selected_ap, address, data) == 1;
  867. if(!access_ok) {
  868. snprintf(
  869. ctx->app->state_string,
  870. sizeof(ctx->app->state_string),
  871. "Failed write 0x%08lX",
  872. address);
  873. swd_script_gui_refresh(ctx);
  874. }
  875. }
  876. if(!access_ok) {
  877. notification_message_block(ctx->app->notification, &seq_error);
  878. success = false;
  879. }
  880. swd_script_seek_newline(ctx);
  881. return success;
  882. }
  883. static bool swd_scriptfunc_mem_ldmst(ScriptContext* ctx) {
  884. uint32_t address = 0;
  885. uint32_t data = 0;
  886. uint32_t mask = 0;
  887. bool success = true;
  888. /* get file */
  889. if(!swd_script_skip_whitespace(ctx)) {
  890. furi_log_print_format(FuriLogLevelDefault, TAG, "mem_ldmst: missing whitespace");
  891. return false;
  892. }
  893. /* get address */
  894. if(!swd_script_get_number(ctx, &address)) {
  895. furi_log_print_format(FuriLogLevelDefault, TAG, "mem_ldmst: failed to parse 1");
  896. return false;
  897. }
  898. /* get data */
  899. if(!swd_script_get_number(ctx, &data)) {
  900. furi_log_print_format(FuriLogLevelDefault, TAG, "mem_ldmst: failed to parse 2");
  901. return false;
  902. }
  903. /* get mask */
  904. if(!swd_script_get_number(ctx, &mask)) {
  905. furi_log_print_format(FuriLogLevelDefault, TAG, "mem_ldmst: failed to parse 2");
  906. return false;
  907. }
  908. furi_log_print_format(
  909. FuriLogLevelDefault,
  910. TAG,
  911. "mem_ldmst: write %08lX to %08lX, mask %08lX",
  912. data,
  913. address,
  914. mask);
  915. bool access_ok = false;
  916. uint32_t modified = 0;
  917. for(uint32_t tries = 0; tries < ctx->max_tries; tries++) {
  918. if(ctx->abort) {
  919. furi_log_print_format(FuriLogLevelDefault, TAG, "mem_ldmst: aborting");
  920. break;
  921. }
  922. access_ok = swd_read_memory(ctx->app, ctx->selected_ap, address, &modified) == 1;
  923. modified = (modified & mask) | data;
  924. access_ok &= swd_write_memory(ctx->app, ctx->selected_ap, address, modified) == 1;
  925. if(!access_ok) {
  926. snprintf(
  927. ctx->app->state_string,
  928. sizeof(ctx->app->state_string),
  929. "Failed access 0x%08lX",
  930. address);
  931. swd_script_gui_refresh(ctx);
  932. }
  933. }
  934. if(!access_ok) {
  935. notification_message_block(ctx->app->notification, &seq_error);
  936. success = false;
  937. }
  938. swd_script_seek_newline(ctx);
  939. return success;
  940. }
  941. static const ScriptFunctionInfo script_funcs[] = {
  942. {"#", &swd_scriptfunc_comment},
  943. {"message", &swd_scriptfunc_message},
  944. {"beep", &swd_scriptfunc_beep},
  945. {"apscan", &swd_scriptfunc_apscan},
  946. {"apselect", &swd_scriptfunc_apselect},
  947. {"max_tries", &swd_scriptfunc_maxtries},
  948. {"swd_clock_delay", &swd_scriptfunc_swd_clock_delay},
  949. {"block_size", &swd_scriptfunc_blocksize},
  950. {"abort", &swd_scriptfunc_abort},
  951. {"mem_dump", &swd_scriptfunc_mem_dump},
  952. {"mem_ldmst", &swd_scriptfunc_mem_ldmst},
  953. {"mem_write", &swd_scriptfunc_mem_write}};
  954. /************************** script main code **************************/
  955. static bool swd_execute_script_line(ScriptContext* const ctx, File* file) {
  956. char buffer[64];
  957. uint64_t start_pos = storage_file_tell(ctx->script_file);
  958. uint16_t ret = storage_file_read(ctx->script_file, buffer, 2);
  959. storage_file_seek(ctx->script_file, start_pos, true);
  960. if(ret < 2) {
  961. return true;
  962. }
  963. if(buffer[0] == '\n' || (buffer[0] == '\r' && buffer[1] == '\n')) {
  964. return true;
  965. }
  966. for(size_t entry = 0; entry < COUNT(script_funcs); entry++) {
  967. if(ctx->abort) {
  968. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_execute_script_line: aborting");
  969. break;
  970. }
  971. char buffer[64];
  972. storage_file_seek(ctx->script_file, start_pos, true);
  973. size_t expected = strlen(script_funcs[entry].prefix);
  974. uint16_t ret = storage_file_read(ctx->script_file, buffer, expected);
  975. if(ret != expected) {
  976. continue;
  977. }
  978. buffer[expected] = '\000';
  979. if(strncmp(buffer, script_funcs[entry].prefix, expected)) {
  980. continue;
  981. }
  982. furi_log_print_format(
  983. FuriLogLevelDefault, TAG, "command: '%s'", script_funcs[entry].prefix);
  984. snprintf(
  985. ctx->app->state_string,
  986. sizeof(ctx->app->state_string),
  987. "CMD: %s",
  988. script_funcs[entry].prefix);
  989. swd_script_gui_refresh(ctx);
  990. /* func function, execute */
  991. return script_funcs[entry].func(ctx);
  992. }
  993. furi_log_print_format(FuriLogLevelDefault, TAG, "unknown command");
  994. return false;
  995. }
  996. static bool swd_execute_script(AppFSM* const ctx, const char* filename) {
  997. bool success = true;
  998. ctx->script = malloc(sizeof(ScriptContext));
  999. ctx->script->app = ctx;
  1000. ctx->script->max_tries = 1;
  1001. if(!storage_file_exists(ctx->storage, filename)) {
  1002. furi_log_print_format(FuriLogLevelDefault, TAG, "Does not exist '%s'", filename);
  1003. free(ctx->script);
  1004. ctx->script = NULL;
  1005. return false;
  1006. }
  1007. /* first allocate a file object */
  1008. ctx->script->script_file = storage_file_alloc(ctx->storage);
  1009. /* then get our script opened */
  1010. if(!storage_file_open(ctx->script->script_file, filename, FSAM_READ, FSOM_OPEN_EXISTING)) {
  1011. FURI_LOG_E(TAG, "open, %s", storage_file_get_error_desc(ctx->script->script_file));
  1012. furi_log_print_format(FuriLogLevelDefault, TAG, "Failed to open '%s'", filename);
  1013. storage_file_free(ctx->script->script_file);
  1014. free(ctx->script);
  1015. ctx->script = NULL;
  1016. return false;
  1017. }
  1018. uint32_t line = 1;
  1019. while(line < SCRIPT_MAX_LINES) {
  1020. if(ctx->script->abort) {
  1021. furi_log_print_format(FuriLogLevelDefault, TAG, "Abort requested");
  1022. break;
  1023. }
  1024. if(storage_file_eof(ctx->script->script_file)) {
  1025. break;
  1026. }
  1027. furi_log_print_format(FuriLogLevelDefault, TAG, "line %lu", line);
  1028. if(!swd_execute_script_line(ctx->script, ctx->script->script_file)) {
  1029. furi_log_print_format(FuriLogLevelDefault, TAG, "Failed to execute line %lu", line);
  1030. success = false;
  1031. break;
  1032. }
  1033. line++;
  1034. }
  1035. furi_log_print_format(FuriLogLevelDefault, TAG, "Finished");
  1036. storage_file_close(ctx->script->script_file);
  1037. storage_file_free(ctx->script->script_file);
  1038. free(ctx->script);
  1039. ctx->script = NULL;
  1040. return success;
  1041. }
  1042. /************************** UI functions **************************/
  1043. static void render_callback(Canvas* const canvas, void* cb_ctx) {
  1044. AppFSM* ctx = acquire_mutex((ValueMutex*)cb_ctx, 25);
  1045. if(ctx == NULL) {
  1046. return;
  1047. }
  1048. char buffer[64];
  1049. int y = 10;
  1050. canvas_draw_frame(canvas, 0, 0, 128, 64);
  1051. canvas_set_font(canvas, FontPrimary);
  1052. if(ctx->detected_device) {
  1053. /* if seen less than a quarter second ago */
  1054. switch(ctx->mode_page) {
  1055. case ModePageScan: {
  1056. if((ctx->detected_timeout + TIMER_HZ / 4) >= TIMER_HZ * TIMEOUT) {
  1057. snprintf(buffer, sizeof(buffer), "FOUND!");
  1058. } else {
  1059. /* if it was seen more than a quarter second ago, show countdown */
  1060. snprintf(
  1061. buffer,
  1062. sizeof(buffer),
  1063. "FOUND! (%lus)",
  1064. (ctx->detected_timeout / TIMER_HZ) + 1);
  1065. }
  1066. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, buffer);
  1067. y += 10;
  1068. canvas_set_font(canvas, FontKeyboard);
  1069. snprintf(
  1070. buffer,
  1071. sizeof(buffer),
  1072. "SWC/SWD: %s/%s",
  1073. gpio_name(ctx->io_swc),
  1074. gpio_name(ctx->io_swd));
  1075. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  1076. y += 10;
  1077. snprintf(buffer, sizeof(buffer), "DPIDR 0x%08lX", ctx->dp_regs.dpidr);
  1078. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  1079. y += 10;
  1080. snprintf(
  1081. buffer,
  1082. sizeof(buffer),
  1083. "Part %02X Rev %X DAPv%d",
  1084. ctx->dpidr_info.partno,
  1085. ctx->dpidr_info.revision,
  1086. ctx->dpidr_info.version);
  1087. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  1088. y += 10;
  1089. canvas_set_font(canvas, FontSecondary);
  1090. snprintf(buffer, sizeof(buffer), "%s", jep106_manufacturer(ctx->dpidr_info.designer));
  1091. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  1092. y += 10;
  1093. canvas_set_font(canvas, FontSecondary);
  1094. elements_button_right(canvas, "DP Regs");
  1095. break;
  1096. }
  1097. case ModePageDPRegs: {
  1098. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "DP Registers");
  1099. y += 10;
  1100. canvas_set_font(canvas, FontKeyboard);
  1101. if(ctx->dp_regs.dpidr_ok) {
  1102. snprintf(buffer, sizeof(buffer), "DPIDR %08lX", ctx->dp_regs.dpidr);
  1103. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  1104. }
  1105. y += 10;
  1106. if(ctx->dp_regs.ctrlstat_ok) {
  1107. snprintf(buffer, sizeof(buffer), "CTRL %08lX", ctx->dp_regs.ctrlstat);
  1108. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  1109. }
  1110. y += 10;
  1111. if(ctx->dp_regs.targetid_ok) {
  1112. snprintf(buffer, sizeof(buffer), "TGTID %08lX", ctx->dp_regs.targetid);
  1113. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  1114. }
  1115. y += 10;
  1116. if(ctx->dp_regs.eventstat_ok) {
  1117. snprintf(buffer, sizeof(buffer), "EVTST %08lX", ctx->dp_regs.eventstat);
  1118. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  1119. }
  1120. y += 10;
  1121. canvas_set_font(canvas, FontSecondary);
  1122. elements_button_left(canvas, "Scan");
  1123. elements_button_right(canvas, "DPID");
  1124. break;
  1125. }
  1126. case ModePageDPID: {
  1127. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "DP ID Register");
  1128. y += 10;
  1129. canvas_set_font(canvas, FontKeyboard);
  1130. if(ctx->dpidr_info.version != 2) {
  1131. snprintf(buffer, sizeof(buffer), "TARGETID not supported");
  1132. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  1133. y += 10;
  1134. } else {
  1135. if(ctx->dp_regs.targetid_ok) {
  1136. snprintf(buffer, sizeof(buffer), "TGTID %08lX", ctx->dp_regs.targetid);
  1137. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  1138. y += 10;
  1139. snprintf(buffer, sizeof(buffer), "Part No. %04X", ctx->targetid_info.partno);
  1140. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  1141. y += 10;
  1142. snprintf(
  1143. buffer,
  1144. sizeof(buffer),
  1145. "%s",
  1146. jep106_manufacturer(ctx->targetid_info.designer));
  1147. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  1148. y += 10;
  1149. }
  1150. }
  1151. canvas_set_font(canvas, FontSecondary);
  1152. elements_button_left(canvas, "DP Regs");
  1153. elements_button_right(canvas, "APs");
  1154. break;
  1155. }
  1156. case ModePageAPID: {
  1157. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "AP Menu");
  1158. y += 10;
  1159. canvas_set_font(canvas, FontKeyboard);
  1160. char state = ' ';
  1161. if(ctx->ap_pos >= ctx->ap_scanned && ctx->ap_pos <= ctx->ap_scanned + 10) {
  1162. state = '*';
  1163. }
  1164. if(!ctx->apidr_info[ctx->ap_pos].ok) {
  1165. snprintf(buffer, sizeof(buffer), "[%d]%c<none>", ctx->ap_pos, state);
  1166. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  1167. y += 10;
  1168. if(ctx->ap_pos == 0) {
  1169. for(int pos = 0; pos < COUNT(ctx->apidr_info); pos++) {
  1170. if(ctx->apidr_info[pos].ok) {
  1171. ctx->ap_pos = pos;
  1172. }
  1173. }
  1174. }
  1175. } else {
  1176. const char* class = "";
  1177. switch(ctx->apidr_info[ctx->ap_pos].class) {
  1178. case 0:
  1179. class = "und";
  1180. break;
  1181. case 1:
  1182. class = "COM";
  1183. break;
  1184. case 8:
  1185. class = "MEM";
  1186. break;
  1187. default:
  1188. class = "unk";
  1189. break;
  1190. }
  1191. const char* types[] = {
  1192. "COM-AP",
  1193. "AHB3",
  1194. "APB2 or APB3",
  1195. "Type unknown",
  1196. "AXI3 or AXI4",
  1197. "AHB5",
  1198. "APB4 and APB5",
  1199. "AXI5",
  1200. "AHB5 enh.",
  1201. };
  1202. const char* type = "Type unk";
  1203. if(ctx->apidr_info[ctx->ap_pos].type < COUNT(types)) {
  1204. type = types[ctx->apidr_info[ctx->ap_pos].type];
  1205. }
  1206. snprintf(buffer, sizeof(buffer), "[%d]%c%s, %s", ctx->ap_pos, state, class, type);
  1207. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  1208. y += 10;
  1209. snprintf(
  1210. buffer, sizeof(buffer), "Base 0x%08lX", ctx->apidr_info[ctx->ap_pos].base);
  1211. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  1212. y += 10;
  1213. snprintf(
  1214. buffer,
  1215. sizeof(buffer),
  1216. "Rev %d Var %d",
  1217. ctx->apidr_info[ctx->ap_pos].revision,
  1218. ctx->apidr_info[ctx->ap_pos].variant);
  1219. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  1220. y += 10;
  1221. snprintf(
  1222. buffer,
  1223. sizeof(buffer),
  1224. "%s",
  1225. jep106_manufacturer(ctx->apidr_info[ctx->ap_pos].designer));
  1226. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  1227. y += 10;
  1228. elements_button_center(canvas, "Show");
  1229. }
  1230. canvas_set_font(canvas, FontSecondary);
  1231. elements_button_left(canvas, "DPID");
  1232. elements_scrollbar_pos(
  1233. canvas, 4, 10, 40, ctx->ap_pos / 32, COUNT(ctx->apidr_info) / 32);
  1234. } break;
  1235. /* hex dump view */
  1236. case ModePageHexDump: {
  1237. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "Hex dump");
  1238. y += 10;
  1239. canvas_set_font(canvas, FontKeyboard);
  1240. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, "Addr:");
  1241. snprintf(buffer, sizeof(buffer), "%08lX", ctx->hex_addr);
  1242. canvas_draw_str_aligned(canvas, 38, y, AlignLeft, AlignBottom, buffer);
  1243. uint32_t font_width = canvas_glyph_width(canvas, '0');
  1244. uint32_t x = 37 + (7 - ctx->hex_select) * font_width;
  1245. /* draw selection */
  1246. canvas_draw_line(canvas, x, y + 1, x + font_width, y + 1);
  1247. y += 10;
  1248. uint32_t byte_num = 0;
  1249. for(int line = 0; line < 4; line++) {
  1250. uint32_t x_pos = 5;
  1251. for(int byte_pos = 0; byte_pos < 8; byte_pos++) {
  1252. if(ctx->hex_buffer_valid[byte_num / 4]) {
  1253. snprintf(buffer, sizeof(buffer), "%02X", ctx->hex_buffer[byte_num]);
  1254. } else {
  1255. snprintf(buffer, sizeof(buffer), "--");
  1256. }
  1257. byte_num++;
  1258. canvas_draw_str_aligned(canvas, x_pos, y, AlignLeft, AlignBottom, buffer);
  1259. x_pos += font_width * 2 + font_width / 2;
  1260. }
  1261. y += 10;
  1262. }
  1263. break;
  1264. }
  1265. /* hex dump view */
  1266. case ModePageScript: {
  1267. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "Script");
  1268. y += 10;
  1269. y += 10;
  1270. canvas_draw_str_aligned(canvas, 10, y, AlignLeft, AlignBottom, "Status:");
  1271. y += 10;
  1272. canvas_set_font(canvas, FontKeyboard);
  1273. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, ctx->state_string);
  1274. y += 10;
  1275. } break;
  1276. }
  1277. } else {
  1278. snprintf(
  1279. buffer, sizeof(buffer), "Searching... %c", gpio_direction_ind[ctx->current_mask_id]);
  1280. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, buffer);
  1281. y += 10;
  1282. y += 10;
  1283. canvas_draw_str_aligned(canvas, 10, y, AlignLeft, AlignBottom, "Script:");
  1284. y += 10;
  1285. if(strlen(ctx->script_detected) > 0) {
  1286. canvas_set_font(canvas, FontKeyboard);
  1287. const char* filename = strrchr(ctx->script_detected, '/');
  1288. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, &filename[1]);
  1289. y += 10;
  1290. canvas_set_font(canvas, FontSecondary);
  1291. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "Up to clear");
  1292. y += 10;
  1293. } else {
  1294. canvas_set_font(canvas, FontKeyboard);
  1295. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "<none>");
  1296. y += 10;
  1297. canvas_set_font(canvas, FontSecondary);
  1298. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "Down to select");
  1299. y += 10;
  1300. }
  1301. }
  1302. release_mutex((ValueMutex*)cb_ctx, ctx);
  1303. }
  1304. static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
  1305. furi_assert(event_queue);
  1306. /* better skip than sorry */
  1307. if(furi_message_queue_get_count(event_queue) < QUEUE_SIZE) {
  1308. AppEvent event = {.type = EventKeyPress, .input = *input_event};
  1309. furi_message_queue_put(event_queue, &event, 100);
  1310. }
  1311. }
  1312. static void timer_tick_callback(FuriMessageQueue* event_queue) {
  1313. furi_assert(event_queue);
  1314. /* filling buffer makes no sense, as we lost timing anyway */
  1315. if(furi_message_queue_get_count(event_queue) < 1) {
  1316. AppEvent event = {.type = EventTimerTick};
  1317. furi_message_queue_put(event_queue, &event, 100);
  1318. }
  1319. }
  1320. static void app_init(AppFSM* const app) {
  1321. app->current_mask_id = 0;
  1322. app->current_mask = gpio_direction_mask[app->current_mask_id];
  1323. app->io_swd = 0xFF;
  1324. app->io_swc = 0xFF;
  1325. app->hex_addr = 0x40002800;
  1326. strcpy(app->state_string, "none");
  1327. strcpy(app->script_detected, "");
  1328. }
  1329. static void app_deinit(AppFSM* const ctx) {
  1330. furi_timer_free(ctx->timer);
  1331. }
  1332. static void on_timer_tick(AppFSM* ctx) {
  1333. switch(ctx->mode_page) {
  1334. case ModePageScan: {
  1335. /* reset after timeout */
  1336. if(ctx->detected_timeout == 0) {
  1337. ctx->detected_device = false;
  1338. ctx->io_swd = 0xFF;
  1339. ctx->io_swc = 0xFF;
  1340. ctx->io_num_swd = 0xFF;
  1341. ctx->io_num_swc = 0xFF;
  1342. ctx->ap_scanned = 0;
  1343. memset(&ctx->dp_regs, 0x00, sizeof(ctx->dp_regs));
  1344. memset(&ctx->targetid_info, 0x00, sizeof(ctx->targetid_info));
  1345. memset(&ctx->apidr_info, 0x00, sizeof(ctx->apidr_info));
  1346. ctx->script_detected_executed = false;
  1347. } else {
  1348. ctx->detected_timeout--;
  1349. }
  1350. ctx->detected = false;
  1351. ctx->current_mask = gpio_direction_mask[ctx->current_mask_id];
  1352. /* when SWD was already detected, set it to data pin regardless of the mask */
  1353. if(ctx->detected_device) {
  1354. ctx->current_mask &= ~ctx->io_swd;
  1355. }
  1356. /* do the scan */
  1357. swd_scan(ctx);
  1358. /* now when detected a device, set the timeout */
  1359. if(ctx->detected) {
  1360. ctx->detected_device = true;
  1361. ctx->detected_timeout = TIMER_HZ * TIMEOUT;
  1362. /* update DPIDR fields */
  1363. ctx->dpidr_info.revision = (ctx->dp_regs.dpidr >> 28) & 0x0F;
  1364. ctx->dpidr_info.partno = (ctx->dp_regs.dpidr >> 20) & 0xFF;
  1365. ctx->dpidr_info.version = (ctx->dp_regs.dpidr >> 12) & 0x0F;
  1366. ctx->dpidr_info.designer = (ctx->dp_regs.dpidr >> 1) & 0x3FF;
  1367. if(!has_multiple_bits(ctx->io_swc)) {
  1368. /* reset error */
  1369. uint8_t ack = swd_transfer(ctx, false, false, 1, &ctx->dp_regs.ctrlstat);
  1370. furi_log_print_format(
  1371. FuriLogLevelDefault, TAG, "stat %02lX %d", ctx->dp_regs.ctrlstat, ack);
  1372. if(ack != 1 || (ctx->dp_regs.ctrlstat & STAT_ERROR_FLAGS)) {
  1373. furi_log_print_format(FuriLogLevelDefault, TAG, "send ABORT");
  1374. swd_abort(ctx);
  1375. }
  1376. ctx->dp_regs.ctrlstat_ok = swd_read_dpbank(ctx, 1, 0, &ctx->dp_regs.ctrlstat) == 1;
  1377. if(ctx->dpidr_info.version >= 1) {
  1378. ctx->dp_regs.dlcr_ok = swd_read_dpbank(ctx, 1, 1, &ctx->dp_regs.dlcr) == 1;
  1379. }
  1380. if(ctx->dpidr_info.version >= 2) {
  1381. ctx->dp_regs.targetid_ok =
  1382. swd_read_dpbank(ctx, 1, 2, &ctx->dp_regs.targetid) == 1;
  1383. ctx->dp_regs.eventstat_ok =
  1384. swd_read_dpbank(ctx, 1, 4, &ctx->dp_regs.eventstat) == 1;
  1385. ctx->dp_regs.dlpidr_ok = swd_read_dpbank(ctx, 1, 3, &ctx->dp_regs.dlpidr) == 1;
  1386. }
  1387. if(ctx->dp_regs.targetid_ok) {
  1388. ctx->targetid_info.revision = (ctx->dp_regs.targetid >> 28) & 0x0F;
  1389. ctx->targetid_info.partno = (ctx->dp_regs.targetid >> 12) & 0xFFFF;
  1390. ctx->targetid_info.designer = (ctx->dp_regs.targetid >> 1) & 0x3FF;
  1391. }
  1392. if(!ctx->script_detected_executed && strlen(ctx->script_detected) > 0) {
  1393. furi_log_print_format(
  1394. FuriLogLevelDefault, TAG, "Run detected script '%s'", ctx->script_detected);
  1395. ctx->script_detected_executed = true;
  1396. ctx->mode_page = ModePageScript;
  1397. swd_execute_script(ctx, ctx->script_detected);
  1398. ctx->mode_page = ModePageScan;
  1399. }
  1400. }
  1401. }
  1402. ctx->current_mask_id = (ctx->current_mask_id + 1) % COUNT(gpio_direction_mask);
  1403. break;
  1404. }
  1405. case ModePageDPRegs:
  1406. case ModePageDPID:
  1407. case ModePageAPID: {
  1408. /* set debug enable request */
  1409. if(!swd_ensure_powerup(ctx)) {
  1410. break;
  1411. }
  1412. /* only scan a few APs at once to stay responsive */
  1413. for(int pos = 0; pos < 5; pos++) {
  1414. if(ctx->ap_scanned == 0) {
  1415. swd_apscan_reset(ctx);
  1416. }
  1417. uint8_t ap = ctx->ap_scanned++;
  1418. if(ctx->apidr_info[ap].tested) {
  1419. continue;
  1420. }
  1421. if(swd_apscan_test(ctx, ap)) {
  1422. break;
  1423. }
  1424. }
  1425. break;
  1426. }
  1427. case ModePageHexDump: {
  1428. if(ctx->hex_read_delay++ < 10) {
  1429. break;
  1430. }
  1431. ctx->hex_read_delay = 0;
  1432. memset(ctx->hex_buffer, 0xEE, sizeof(ctx->hex_buffer));
  1433. uint32_t addr = ctx->hex_addr;
  1434. uint32_t data = 0;
  1435. for(int pos = 0; pos < sizeof(ctx->hex_buffer) / 4; pos++) {
  1436. ctx->hex_buffer_valid[pos] = swd_read_memory(ctx, ctx->ap_pos, addr, &data) == 1;
  1437. if(ctx->hex_buffer_valid[pos]) {
  1438. memcpy(&ctx->hex_buffer[pos * 4], &data, 4);
  1439. } else {
  1440. swd_abort_simple(ctx);
  1441. }
  1442. addr += 4;
  1443. }
  1444. }
  1445. }
  1446. }
  1447. static bool swd_message_process(AppFSM* ctx) {
  1448. bool processing = true;
  1449. AppEvent event;
  1450. FuriStatus event_status = furi_message_queue_get(ctx->event_queue, &event, 100);
  1451. if(event_status == FuriStatusOk) {
  1452. if(event.type == EventKeyPress) {
  1453. if(event.input.type == InputTypePress) {
  1454. switch(event.input.key) {
  1455. case InputKeyUp:
  1456. switch(ctx->mode_page) {
  1457. default:
  1458. break;
  1459. case ModePageScan: {
  1460. strcpy(ctx->script_detected, "");
  1461. break;
  1462. }
  1463. case ModePageAPID:
  1464. if(ctx->ap_pos > 0) {
  1465. ctx->ap_pos--;
  1466. }
  1467. break;
  1468. case ModePageHexDump: {
  1469. ctx->hex_addr +=
  1470. ((ctx->hex_select) ? 1 : 8) * (1 << (4 * ctx->hex_select));
  1471. break;
  1472. }
  1473. }
  1474. break;
  1475. case InputKeyDown:
  1476. switch(ctx->mode_page) {
  1477. default:
  1478. break;
  1479. case ModePageScan: {
  1480. FuriString* result_path = furi_string_alloc_printf(ANY_PATH("swd"));
  1481. FuriString* preselected = furi_string_alloc_printf(
  1482. (strlen(ctx->script_detected) > 0) ? ctx->script_detected :
  1483. ANY_PATH("swd"));
  1484. DialogsFileBrowserOptions options;
  1485. dialog_file_browser_set_basic_options(&options, "swd", &I_swd);
  1486. if(dialog_file_browser_show(
  1487. ctx->dialogs, result_path, preselected, &options)) {
  1488. const char* path = furi_string_get_cstr(result_path);
  1489. strcpy(ctx->script_detected, path);
  1490. }
  1491. furi_string_free(result_path);
  1492. furi_string_free(preselected);
  1493. break;
  1494. }
  1495. case ModePageAPID:
  1496. if(ctx->ap_pos + 1 < COUNT(ctx->apidr_info)) {
  1497. ctx->ap_pos++;
  1498. }
  1499. break;
  1500. case ModePageHexDump: {
  1501. ctx->hex_addr -=
  1502. ((ctx->hex_select) ? 1 : 8) * (1 << (4 * ctx->hex_select));
  1503. break;
  1504. }
  1505. }
  1506. break;
  1507. case InputKeyRight:
  1508. if(ctx->mode_page == ModePageHexDump) {
  1509. if(ctx->hex_select > 0) {
  1510. ctx->hex_select--;
  1511. }
  1512. } else {
  1513. if(ctx->mode_page + 1 < ModePageCount) {
  1514. ctx->mode_page++;
  1515. }
  1516. }
  1517. break;
  1518. case InputKeyLeft:
  1519. if(ctx->mode_page == ModePageHexDump) {
  1520. if(ctx->hex_select < 7) {
  1521. ctx->hex_select++;
  1522. }
  1523. } else {
  1524. if(ctx->mode_page > 0) {
  1525. ctx->mode_page--;
  1526. }
  1527. }
  1528. break;
  1529. case InputKeyOk:
  1530. if(ctx->mode_page == ModePageAPID && ctx->apidr_info[ctx->ap_pos].ok) {
  1531. ctx->mode_page = ModePageHexDump;
  1532. }
  1533. break;
  1534. case InputKeyBack:
  1535. if(ctx->mode_page == ModePageHexDump) {
  1536. ctx->mode_page = ModePageAPID;
  1537. } else if(ctx->mode_page == ModePageScript) {
  1538. ctx->script->abort = true;
  1539. } else if(ctx->mode_page != ModePageScan) {
  1540. ctx->mode_page = ModePageScan;
  1541. } else {
  1542. processing = false;
  1543. }
  1544. break;
  1545. default:
  1546. break;
  1547. }
  1548. }
  1549. } else if(event.type == EventTimerTick) {
  1550. on_timer_tick(ctx);
  1551. }
  1552. } else {
  1553. /* timeout */
  1554. }
  1555. return processing;
  1556. }
  1557. int32_t swd_probe_app_main(void* p) {
  1558. UNUSED(p);
  1559. AppFSM* app = malloc(sizeof(AppFSM));
  1560. app_init(app);
  1561. if(!init_mutex(&app->state_mutex, app, sizeof(AppFSM))) {
  1562. FURI_LOG_E(TAG, "cannot create mutex\r\n");
  1563. free(app);
  1564. return 255;
  1565. }
  1566. app->notification = furi_record_open(RECORD_NOTIFICATION);
  1567. app->gui = furi_record_open(RECORD_GUI);
  1568. app->dialogs = furi_record_open(RECORD_DIALOGS);
  1569. app->storage = furi_record_open(RECORD_STORAGE);
  1570. app->view_port = view_port_alloc();
  1571. app->event_queue = furi_message_queue_alloc(QUEUE_SIZE, sizeof(AppEvent));
  1572. app->timer = furi_timer_alloc(timer_tick_callback, FuriTimerTypePeriodic, app->event_queue);
  1573. view_port_draw_callback_set(app->view_port, render_callback, &app->state_mutex);
  1574. view_port_input_callback_set(app->view_port, input_callback, app->event_queue);
  1575. gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
  1576. notification_message_block(app->notification, &sequence_display_backlight_enforce_on);
  1577. DOLPHIN_DEED(DolphinDeedPluginGameStart);
  1578. furi_timer_start(app->timer, furi_kernel_get_tick_frequency() / TIMER_HZ);
  1579. for(bool processing = true; processing;) {
  1580. //AppFSM* ctx = (AppFSM*)acquire_mutex_block(&app->state_mutex);
  1581. processing = swd_message_process(app);
  1582. view_port_update(app->view_port);
  1583. //release_mutex(&ctx->state_mutex, ctx);
  1584. bool beep = false;
  1585. if(app->detected_device && !app->detected_notified) {
  1586. app->detected_notified = true;
  1587. beep = true;
  1588. }
  1589. if(!app->detected_device && app->detected_notified) {
  1590. app->detected_notified = false;
  1591. }
  1592. if(beep) {
  1593. notification_message_block(app->notification, &seq_c_minor);
  1594. }
  1595. }
  1596. app_deinit(app);
  1597. // Wait for all notifications to be played and return backlight to normal state
  1598. notification_message_block(app->notification, &sequence_display_backlight_enforce_auto);
  1599. view_port_enabled_set(app->view_port, false);
  1600. gui_remove_view_port(app->gui, app->view_port);
  1601. furi_record_close(RECORD_GUI);
  1602. furi_record_close(RECORD_NOTIFICATION);
  1603. view_port_free(app->view_port);
  1604. furi_message_queue_free(app->event_queue);
  1605. delete_mutex(&app->state_mutex);
  1606. free(app);
  1607. return 0;
  1608. }