swd_probe_app.c 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120
  1. #include <furi.h>
  2. #include <gui/gui.h>
  3. #include <gui/elements.h>
  4. #include <input/input.h>
  5. #include <stdlib.h>
  6. #include <dolphin/dolphin.h>
  7. #include <notification/notification.h>
  8. #include <notification/notification_messages.h>
  9. #include "swd_probe_app.h"
  10. #include "jep106.h"
  11. #define COUNT(x) (sizeof(x) / sizeof((x)[0]))
  12. static const GpioPin* gpios[] = {
  13. &gpio_ext_pc0,
  14. &gpio_ext_pc1,
  15. &gpio_ext_pc3,
  16. &gpio_ext_pb2,
  17. &gpio_ext_pb3,
  18. &gpio_ext_pa4,
  19. &gpio_ext_pa6,
  20. &gpio_ext_pa7};
  21. static const char* gpio_names[] = {"PC0", "PC1", "PC3", "PB2", "PB3", "PA4", "PA6", "PA7"};
  22. /* bit set: clock, else data */
  23. static const uint8_t gpio_direction_mask[6] =
  24. {0b10101010, 0b01010101, 0b11001100, 0b00110011, 0b11110000, 0b00001111};
  25. static const uint8_t gpio_direction_ind[6] = "-\\||/-";
  26. static bool has_multiple_bits(uint8_t x) {
  27. return (x & (x - 1)) != 0;
  28. }
  29. static int get_bit_num(uint8_t x) {
  30. return __builtin_ctz(x);
  31. }
  32. static const char* gpio_name(uint8_t mask) {
  33. if(has_multiple_bits(mask)) {
  34. return "Pxx";
  35. }
  36. int io = get_bit_num(mask);
  37. if(io >= COUNT(gpio_names)) {
  38. return "Pxx";
  39. }
  40. return gpio_names[io];
  41. }
  42. static void swd_configure_pins(AppFSM* const ctx, bool output) {
  43. if(ctx->mode_page != ModePageScan && ctx->io_num_swc < 8 && ctx->io_num_swd < 8) {
  44. furi_hal_gpio_init(
  45. gpios[ctx->io_num_swc], GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  46. if(!output) {
  47. furi_hal_gpio_init(
  48. gpios[ctx->io_num_swd], GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
  49. } else {
  50. furi_hal_gpio_init(
  51. gpios[ctx->io_num_swd], GpioModeOutputOpenDrain, GpioPullUp, GpioSpeedVeryHigh);
  52. }
  53. return;
  54. }
  55. for(int io = 0; io < 8; io++) {
  56. uint8_t bitmask = 1 << io;
  57. /* if neither candidate for SWC nor SWD then skip */
  58. if(!(ctx->io_swc & bitmask) && !(ctx->io_swd & bitmask)) {
  59. furi_hal_gpio_init(gpios[io], GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
  60. continue;
  61. }
  62. if(ctx->current_mask & bitmask) {
  63. /* set for clock */
  64. furi_hal_gpio_init(gpios[io], GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  65. } else {
  66. /* set for data */
  67. if(!output) {
  68. furi_hal_gpio_init(gpios[io], GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
  69. } else {
  70. furi_hal_gpio_init(
  71. gpios[io], GpioModeOutputOpenDrain, GpioPullUp, GpioSpeedVeryHigh);
  72. }
  73. }
  74. }
  75. }
  76. static void swd_set_clock(AppFSM* const ctx, const uint8_t level) {
  77. if(ctx->mode_page != ModePageScan && ctx->io_num_swc < 8) {
  78. furi_hal_gpio_write(gpios[ctx->io_num_swc], level);
  79. return;
  80. }
  81. for(int io = 0; io < 8; io++) {
  82. uint8_t bitmask = 1 << io;
  83. /* if no candidate for SWC then skip */
  84. if(!(ctx->io_swc & bitmask)) {
  85. continue;
  86. }
  87. if(ctx->current_mask & bitmask) {
  88. furi_hal_gpio_write(gpios[io], level);
  89. }
  90. }
  91. }
  92. static void swd_set_data(AppFSM* const ctx, const uint8_t level) {
  93. if(ctx->mode_page != ModePageScan && ctx->io_num_swd < 8) {
  94. furi_hal_gpio_write(gpios[ctx->io_num_swd], level);
  95. return;
  96. }
  97. for(int io = 0; io < 8; io++) {
  98. uint8_t bitmask = 1 << io;
  99. /* if no candidate for SWD then skip */
  100. if(!(ctx->io_swd & bitmask)) {
  101. continue;
  102. }
  103. if(!(ctx->current_mask & bitmask)) {
  104. furi_hal_gpio_write(gpios[io], level);
  105. }
  106. }
  107. }
  108. static uint8_t swd_get_data(AppFSM* const ctx) {
  109. if(ctx->mode_page != ModePageScan && ctx->io_num_swd < 8) {
  110. return furi_hal_gpio_read(gpios[ctx->io_num_swd]);
  111. }
  112. uint8_t bits = 0;
  113. for(int io = 0; io < 8; io++) {
  114. uint8_t bitmask = 1 << io;
  115. /* if no candidate for SWD then skip */
  116. if(!(ctx->io_swd & bitmask)) {
  117. continue;
  118. }
  119. bits |= furi_hal_gpio_read(gpios[io]) ? bitmask : 0;
  120. }
  121. return bits;
  122. }
  123. static void swd_write_bit(AppFSM* const ctx, bool level) {
  124. swd_set_clock(ctx, 0);
  125. swd_set_data(ctx, level);
  126. furi_delay_us(SWD_DELAY_US);
  127. swd_set_clock(ctx, 1);
  128. furi_delay_us(SWD_DELAY_US);
  129. swd_set_clock(ctx, 0);
  130. }
  131. static uint8_t swd_read_bit(AppFSM* const ctx) {
  132. swd_set_clock(ctx, 1);
  133. furi_delay_us(SWD_DELAY_US);
  134. swd_set_clock(ctx, 0);
  135. uint8_t bits = swd_get_data(ctx);
  136. furi_delay_us(SWD_DELAY_US);
  137. swd_set_clock(ctx, 1);
  138. return bits;
  139. }
  140. /* send a byte or less LSB-first */
  141. static void swd_write_byte(AppFSM* const ctx, const uint8_t data, size_t bits) {
  142. for(size_t pos = 0; pos < bits; pos++) {
  143. swd_write_bit(ctx, data & (1 << pos));
  144. }
  145. }
  146. /* send a sequence of bytes LSB-first */
  147. static void swd_write(AppFSM* const ctx, const uint8_t* data, size_t bits) {
  148. size_t byte_pos = 0;
  149. while(bits > 0) {
  150. size_t remain = (bits > 8) ? 8 : bits;
  151. swd_write_byte(ctx, data[byte_pos++], remain);
  152. bits -= remain;
  153. }
  154. }
  155. static uint8_t swd_transfer(AppFSM* const ctx, bool ap, bool write, uint8_t a23, uint32_t* data) {
  156. swd_set_data(ctx, false);
  157. swd_configure_pins(ctx, true);
  158. swd_write_byte(ctx, 0, 8);
  159. uint8_t request[] = {0};
  160. request[0] |= 0x01; /* start bit*/
  161. request[0] |= ap ? 0x02 : 0; /* APnDP */
  162. request[0] |= write ? 0 : 0x04; /* operation */
  163. request[0] |= (a23 & 0x01) ? 0x08 : 0; /* A[2:3] */
  164. request[0] |= (a23 & 0x02) ? 0x10 : 0; /* A[2:3] */
  165. request[0] |= 0x80; /* park bit */
  166. request[0] |= __builtin_parity(request[0]) ? 0x20 : 0; /* parity */
  167. swd_write(ctx, request, sizeof(request) * 8);
  168. /* turnaround cycle */
  169. swd_configure_pins(ctx, false);
  170. uint8_t ack = 0;
  171. /* receive 3 ACK bits */
  172. for(int pos = 0; pos < 3; pos++) {
  173. ack >>= 1;
  174. ack |= swd_read_bit(ctx) ? 0x04 : 0;
  175. }
  176. /* force ABORT/CTRL to always work */
  177. if(!ap && a23 == 0) {
  178. ack = 1;
  179. }
  180. if(ack != 0x01) {
  181. return ack;
  182. }
  183. if(write) {
  184. swd_write_bit(ctx, 0);
  185. swd_configure_pins(ctx, true);
  186. /* send 32 WDATA bits */
  187. for(int pos = 0; pos < 32; pos++) {
  188. swd_write_bit(ctx, *data & (1 << pos));
  189. }
  190. /* send parity bit */
  191. swd_write_bit(ctx, __builtin_parity(*data));
  192. } else {
  193. *data = 0;
  194. /* receive 32 RDATA bits */
  195. for(int pos = 0; pos < 32; pos++) {
  196. *data >>= 1;
  197. *data |= swd_read_bit(ctx) ? 0x80000000 : 0;
  198. }
  199. /* receive parity bit */
  200. bool parity = swd_read_bit(ctx);
  201. if(parity != __builtin_parity(*data)) {
  202. return 8;
  203. }
  204. }
  205. swd_set_data(ctx, false);
  206. swd_configure_pins(ctx, true);
  207. return ack;
  208. }
  209. /* A line reset is achieved by holding the data signal HIGH for at least 50 clock cycles, followed by at least two idle cycles. */
  210. static void swd_line_reset(AppFSM* const ctx) {
  211. for(int bitcount = 0; bitcount < 50; bitcount += 8) {
  212. swd_write_byte(ctx, 0xFF, 8);
  213. }
  214. swd_write_byte(ctx, 0, 8);
  215. }
  216. static void swd_abort(AppFSM* const ctx) {
  217. uint32_t dpidr;
  218. /* first reset the line */
  219. swd_line_reset(ctx);
  220. swd_transfer(ctx, false, false, 0, &dpidr);
  221. uint32_t abort = 0x0E;
  222. swd_transfer(ctx, false, true, 0, &abort);
  223. }
  224. static void swd_abort_simple(AppFSM* const ctx) {
  225. uint32_t abort = 0x0E;
  226. swd_transfer(ctx, false, true, 0, &abort);
  227. uint32_t dpidr;
  228. if(swd_transfer(ctx, false, false, 0, &dpidr) != 1) {
  229. swd_abort(ctx);
  230. }
  231. }
  232. static uint8_t swd_select(AppFSM* const ctx, uint8_t ap_sel, uint8_t ap_bank, uint8_t dp_bank) {
  233. uint32_t bank_reg = (ap_sel << 24) | ((ap_bank & 0x0F) << 4) | (dp_bank & 0x0F);
  234. uint8_t ret = swd_transfer(ctx, false, true, 2, &bank_reg);
  235. if(ret != 1) {
  236. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_select: failed: %d", ret);
  237. }
  238. return ret;
  239. }
  240. static uint8_t
  241. swd_read_dpbank(AppFSM* const ctx, uint8_t dp_off, uint8_t dp_bank, uint32_t* data) {
  242. /* select target bank */
  243. uint8_t ret = swd_select(ctx, 0, 0, dp_bank);
  244. if(ret != 1) {
  245. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_read_dpbank: swd_select failed");
  246. return ret;
  247. }
  248. /* read data from it */
  249. *data = 0;
  250. ret = swd_transfer(ctx, false, false, dp_off, data);
  251. if(ret != 1) {
  252. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_read_dpbank: failed: %d", ret);
  253. return ret;
  254. }
  255. /* reset bank to zero again */
  256. ret = swd_select(ctx, 0, 0, 0);
  257. return ret;
  258. }
  259. static uint8_t swd_read_ap(AppFSM* const ctx, uint8_t ap, uint8_t ap_off, uint32_t* data) {
  260. uint8_t ret = swd_select(ctx, ap, (ap_off >> 4) & 0x0F, 0);
  261. if(ret != 1) {
  262. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_read_ap: swd_select failed");
  263. return ret;
  264. }
  265. ret = swd_transfer(ctx, true, false, (ap_off >> 2) & 3, data);
  266. *data = 0;
  267. ret = swd_transfer(ctx, true, false, (ap_off >> 2) & 3, data);
  268. if(ret != 1) {
  269. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_read_ap: failed: %d", ret);
  270. return ret;
  271. }
  272. return ret;
  273. }
  274. static uint8_t swd_read_ap_single(AppFSM* const ctx, uint8_t ap, uint8_t ap_off, uint32_t* data) {
  275. uint8_t ret = swd_select(ctx, ap, (ap_off >> 4) & 0x0F, 0);
  276. if(ret != 1) {
  277. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_read_ap_single: swd_select failed");
  278. return ret;
  279. }
  280. *data = 0;
  281. ret = swd_transfer(ctx, true, false, (ap_off >> 2) & 3, data);
  282. if(ret != 1) {
  283. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_read_ap_single: failed: %d", ret);
  284. return ret;
  285. }
  286. return ret;
  287. }
  288. static uint8_t swd_write_ap(AppFSM* const ctx, uint8_t ap, uint8_t ap_off, uint32_t data) {
  289. uint8_t ret = swd_select(ctx, ap, (ap_off >> 4) & 0x0F, 0);
  290. if(ret != 1) {
  291. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_write_ap: swd_select failed");
  292. return ret;
  293. }
  294. ret = swd_transfer(ctx, true, true, (ap_off >> 2) & 3, &data);
  295. if(ret != 1) {
  296. furi_log_print_format(FuriLogLevelDefault, TAG, "swd_write_ap: failed: %d", ret);
  297. return ret;
  298. }
  299. return ret;
  300. }
  301. static uint8_t swd_read_memory(AppFSM* const ctx, uint8_t ap, uint32_t address, uint32_t* data) {
  302. uint8_t ret = 0;
  303. ret |= swd_write_ap(ctx, ap, MEMAP_CSW, 0x03000012);
  304. ret |= swd_write_ap(ctx, ap, MEMAP_TAR, address);
  305. ret |= swd_read_ap(ctx, ap, MEMAP_DRW, data);
  306. if(ret != 1) {
  307. swd_abort(ctx);
  308. }
  309. return ret;
  310. }
  311. static uint8_t swd_read_memory_cont(AppFSM* const ctx, uint8_t ap, uint32_t* data) {
  312. uint8_t ret = 0;
  313. ret |= swd_read_ap_single(ctx, ap, MEMAP_DRW, data);
  314. if(ret != 1) {
  315. swd_abort(ctx);
  316. }
  317. return ret;
  318. }
  319. static uint32_t swd_detect(AppFSM* const ctx) {
  320. swd_set_data(ctx, false);
  321. swd_configure_pins(ctx, true);
  322. uint8_t data[] = {0xA5};
  323. swd_write(ctx, data, sizeof(data) * 8);
  324. /* turnaround cycle */
  325. swd_configure_pins(ctx, false);
  326. uint8_t ack_bits[3];
  327. uint8_t rdata[32];
  328. /* receive 3 ACK bits */
  329. for(int pos = 0; pos < 3; pos++) {
  330. ack_bits[pos] = swd_read_bit(ctx);
  331. }
  332. /* receive 32 RDATA bits */
  333. for(int pos = 0; pos < 32; pos++) {
  334. rdata[pos] = swd_read_bit(ctx);
  335. }
  336. /* receive parity bit */
  337. uint8_t parity = swd_read_bit(ctx);
  338. for(int io = 0; io < 8; io++) {
  339. uint8_t bitmask = 1 << io;
  340. /* skip if it's a clock */
  341. if(ctx->current_mask & bitmask) {
  342. continue;
  343. }
  344. uint8_t ack = 0;
  345. for(int pos = 0; pos < 3; pos++) {
  346. ack >>= 1;
  347. ack |= (ack_bits[pos] & bitmask) ? 4 : 0;
  348. }
  349. uint32_t dpidr = 0;
  350. for(int pos = 0; pos < 32; pos++) {
  351. dpidr >>= 1;
  352. dpidr |= (rdata[pos] & bitmask) ? 0x80000000 : 0;
  353. }
  354. if(ack == 1 && dpidr != 0 && dpidr != 0xFFFFFFFF) {
  355. bool received_parity = (parity & bitmask);
  356. if(__builtin_parity(dpidr) == received_parity) {
  357. ctx->dp_regs.dpidr = dpidr;
  358. ctx->dp_regs.dpidr_ok = true;
  359. ctx->detected = true;
  360. ctx->io_swd = bitmask;
  361. ctx->io_swc &= ctx->current_mask;
  362. furi_log_print_format(
  363. FuriLogLevelDefault,
  364. TAG,
  365. "swd_detect: data: %08lX, io_swd %02X, io_swc %02X",
  366. dpidr,
  367. ctx->io_swd,
  368. ctx->io_swc);
  369. if(!has_multiple_bits(ctx->io_swc)) {
  370. ctx->io_num_swd = get_bit_num(ctx->io_swd);
  371. ctx->io_num_swc = get_bit_num(ctx->io_swc);
  372. }
  373. }
  374. }
  375. }
  376. swd_set_data(ctx, false);
  377. swd_configure_pins(ctx, true);
  378. return 0;
  379. }
  380. static void swd_scan(AppFSM* const ctx) {
  381. /* To switch SWJ-DP from JTAG to SWD operation:
  382. 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.
  383. 2. Send the 16-bit JTAG-to-SWD select sequence 0x79e7 on SWDIOTMS.
  384. 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.
  385. */
  386. swd_configure_pins(ctx, true);
  387. /* reset JTAG interface */
  388. for(int bitcount = 0; bitcount < 50; bitcount += 8) {
  389. swd_write_byte(ctx, 0xFF, 8);
  390. }
  391. /* Send the 16-bit JTAG-to-SWD select sequence */
  392. swd_write_byte(ctx, 0x9E, 8);
  393. swd_write_byte(ctx, 0xE7, 8);
  394. /* resynchronize SWD */
  395. swd_line_reset(ctx);
  396. swd_detect(ctx);
  397. }
  398. static bool swd_ensure_powerup(AppFSM* const ctx) {
  399. if(!(ctx->dp_regs.ctrlstat & (CSYSPWRUPREQ | CDBGPWRUPREQ))) {
  400. /* fetch current CTRL/STAT */
  401. swd_transfer(ctx, false, false, 1, &ctx->dp_regs.ctrlstat);
  402. /* enable requests */
  403. ctx->dp_regs.ctrlstat |= CDBGPWRUPREQ;
  404. ctx->dp_regs.ctrlstat |= CSYSPWRUPREQ;
  405. furi_log_print_format(FuriLogLevelDefault, TAG, "no (CSYSPWRUPREQ | CDBGPWRUPREQ)");
  406. swd_transfer(ctx, false, true, 1, &ctx->dp_regs.ctrlstat);
  407. return false;
  408. }
  409. if(!(ctx->dp_regs.ctrlstat & CDBGPWRUPACK)) {
  410. /* fetch current CTRL/STAT */
  411. swd_transfer(ctx, false, false, 1, &ctx->dp_regs.ctrlstat);
  412. furi_log_print_format(FuriLogLevelDefault, TAG, "no CDBGPWRUPACK");
  413. return false;
  414. }
  415. return true;
  416. }
  417. static void swd_apscan_reset(AppFSM* const ctx) {
  418. for(int reset_ap = 0; reset_ap < COUNT(ctx->apidr_info); reset_ap++) {
  419. ctx->apidr_info[reset_ap].tested = false;
  420. }
  421. }
  422. static bool swd_apscan_test(AppFSM* const ctx, uint32_t ap) {
  423. ctx->apidr_info[ap].tested = true;
  424. uint32_t data = 0;
  425. uint32_t base = 0;
  426. if(swd_read_ap(ctx, ap, AP_IDR, &data) != 1) {
  427. swd_abort(ctx);
  428. return false;
  429. }
  430. if(data == 0) {
  431. return false;
  432. }
  433. furi_log_print_format(FuriLogLevelDefault, TAG, "AP%lu detected", ap);
  434. ctx->apidr_info[ap].ok = true;
  435. ctx->apidr_info[ap].revision = (data >> 24) & 0x0F;
  436. ctx->apidr_info[ap].designer = (data >> 17) & 0x3FF;
  437. ctx->apidr_info[ap].class = (data >> 13) & 0x0F;
  438. ctx->apidr_info[ap].variant = (data >> 4) & 0x0F;
  439. ctx->apidr_info[ap].type = (data >> 0) & 0x0F;
  440. if(swd_read_ap(ctx, ap, AP_BASE, &ctx->apidr_info[ap].base) != 1) {
  441. swd_abort(ctx);
  442. return false;
  443. }
  444. return true;
  445. }
  446. static void render_callback(Canvas* const canvas, void* cb_ctx) {
  447. AppFSM* ctx = acquire_mutex((ValueMutex*)cb_ctx, 25);
  448. if(ctx == NULL) {
  449. return;
  450. }
  451. char buffer[64];
  452. int y = 10;
  453. canvas_draw_frame(canvas, 0, 0, 128, 64);
  454. canvas_set_font(canvas, FontPrimary);
  455. if(ctx->detected_device) {
  456. /* if seen less than a quarter second ago */
  457. switch(ctx->mode_page) {
  458. case ModePageScan: {
  459. if((ctx->detected_timeout + TIMER_HZ / 4) >= TIMER_HZ * TIMEOUT) {
  460. snprintf(buffer, sizeof(buffer), "FOUND!");
  461. } else {
  462. /* if it was seen more than a quarter second ago, show countdown */
  463. snprintf(
  464. buffer,
  465. sizeof(buffer),
  466. "FOUND! (%lus)",
  467. (ctx->detected_timeout / TIMER_HZ) + 1);
  468. }
  469. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, buffer);
  470. y += 10;
  471. canvas_set_font(canvas, FontKeyboard);
  472. snprintf(
  473. buffer,
  474. sizeof(buffer),
  475. "SWC/SWD: %s/%s",
  476. gpio_name(ctx->io_swc),
  477. gpio_name(ctx->io_swd));
  478. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  479. y += 10;
  480. snprintf(buffer, sizeof(buffer), "DPIDR 0x%08lX", ctx->dp_regs.dpidr);
  481. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  482. y += 10;
  483. snprintf(
  484. buffer,
  485. sizeof(buffer),
  486. "Part %02X Rev %X DAPv%d",
  487. ctx->dpidr_info.partno,
  488. ctx->dpidr_info.revision,
  489. ctx->dpidr_info.version);
  490. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  491. y += 10;
  492. canvas_set_font(canvas, FontSecondary);
  493. snprintf(buffer, sizeof(buffer), "%s", jep106_manufacturer(ctx->dpidr_info.designer));
  494. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  495. y += 10;
  496. canvas_set_font(canvas, FontSecondary);
  497. elements_button_right(canvas, "DP Regs");
  498. break;
  499. }
  500. case ModePageDPRegs: {
  501. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "DP Registers");
  502. y += 10;
  503. canvas_set_font(canvas, FontKeyboard);
  504. if(ctx->dp_regs.dpidr_ok) {
  505. snprintf(buffer, sizeof(buffer), "DPIDR %08lX", ctx->dp_regs.dpidr);
  506. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  507. }
  508. y += 10;
  509. if(ctx->dp_regs.ctrlstat_ok) {
  510. snprintf(buffer, sizeof(buffer), "CTRL %08lX", ctx->dp_regs.ctrlstat);
  511. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  512. }
  513. y += 10;
  514. if(ctx->dp_regs.targetid_ok) {
  515. snprintf(buffer, sizeof(buffer), "TGTID %08lX", ctx->dp_regs.targetid);
  516. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  517. }
  518. y += 10;
  519. if(ctx->dp_regs.eventstat_ok) {
  520. snprintf(buffer, sizeof(buffer), "EVTST %08lX", ctx->dp_regs.eventstat);
  521. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  522. }
  523. y += 10;
  524. canvas_set_font(canvas, FontSecondary);
  525. elements_button_left(canvas, "Scan");
  526. elements_button_right(canvas, "DPID");
  527. break;
  528. }
  529. case ModePageDPID: {
  530. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "DP ID Register");
  531. y += 10;
  532. canvas_set_font(canvas, FontKeyboard);
  533. if(ctx->dpidr_info.version != 2) {
  534. snprintf(buffer, sizeof(buffer), "TARGETID not supported");
  535. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  536. y += 10;
  537. } else {
  538. if(ctx->dp_regs.targetid_ok) {
  539. snprintf(buffer, sizeof(buffer), "TGTID %08lX", ctx->dp_regs.targetid);
  540. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  541. y += 10;
  542. snprintf(buffer, sizeof(buffer), "Part No. %04X", ctx->targetid_info.partno);
  543. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  544. y += 10;
  545. snprintf(
  546. buffer,
  547. sizeof(buffer),
  548. "%s",
  549. jep106_manufacturer(ctx->targetid_info.designer));
  550. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  551. y += 10;
  552. }
  553. }
  554. canvas_set_font(canvas, FontSecondary);
  555. elements_button_left(canvas, "DP Regs");
  556. elements_button_right(canvas, "APs");
  557. break;
  558. }
  559. case ModePageAPID: {
  560. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "AP Menu");
  561. y += 10;
  562. canvas_set_font(canvas, FontKeyboard);
  563. char state = ' ';
  564. if(ctx->ap_pos >= ctx->ap_scanned && ctx->ap_pos <= ctx->ap_scanned + 10) {
  565. state = '*';
  566. }
  567. if(!ctx->apidr_info[ctx->ap_pos].ok) {
  568. snprintf(buffer, sizeof(buffer), "[%d]%c<none>", ctx->ap_pos, state);
  569. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  570. y += 10;
  571. if(ctx->ap_pos == 0) {
  572. for(int pos = 0; pos < COUNT(ctx->apidr_info); pos++) {
  573. if(ctx->apidr_info[pos].ok) {
  574. ctx->ap_pos = pos;
  575. }
  576. }
  577. }
  578. } else {
  579. const char* class = "";
  580. switch(ctx->apidr_info[ctx->ap_pos].class) {
  581. case 0:
  582. class = "und";
  583. break;
  584. case 1:
  585. class = "COM";
  586. break;
  587. case 8:
  588. class = "MEM";
  589. break;
  590. default:
  591. class = "unk";
  592. break;
  593. }
  594. const char* types[] = {
  595. "COM-AP",
  596. "AHB3",
  597. "APB2 or APB3",
  598. "Type unknown",
  599. "AXI3 or AXI4",
  600. "AHB5",
  601. "APB4 and APB5",
  602. "AXI5",
  603. "AHB5 enh.",
  604. };
  605. const char* type = "Type unk";
  606. if(ctx->apidr_info[ctx->ap_pos].type < COUNT(types)) {
  607. type = types[ctx->apidr_info[ctx->ap_pos].type];
  608. }
  609. snprintf(buffer, sizeof(buffer), "[%d]%c%s, %s", ctx->ap_pos, state, class, type);
  610. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  611. y += 10;
  612. snprintf(
  613. buffer, sizeof(buffer), "Base 0x%08lX", ctx->apidr_info[ctx->ap_pos].base);
  614. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  615. y += 10;
  616. snprintf(
  617. buffer,
  618. sizeof(buffer),
  619. "Rev %d Var %d",
  620. ctx->apidr_info[ctx->ap_pos].revision,
  621. ctx->apidr_info[ctx->ap_pos].variant);
  622. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  623. y += 10;
  624. snprintf(
  625. buffer,
  626. sizeof(buffer),
  627. "%s",
  628. jep106_manufacturer(ctx->apidr_info[ctx->ap_pos].designer));
  629. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  630. y += 10;
  631. elements_button_center(canvas, "Show");
  632. }
  633. canvas_set_font(canvas, FontSecondary);
  634. elements_button_left(canvas, "DPID");
  635. elements_scrollbar_pos(
  636. canvas, 4, 10, 40, ctx->ap_pos / 32, COUNT(ctx->apidr_info) / 32);
  637. } break;
  638. /* hex dump view */
  639. case ModePageHexDump: {
  640. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "Hex dump");
  641. y += 10;
  642. canvas_set_font(canvas, FontKeyboard);
  643. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, "Addr:");
  644. snprintf(buffer, sizeof(buffer), "%08lX", ctx->hex_addr);
  645. canvas_draw_str_aligned(canvas, 38, y, AlignLeft, AlignBottom, buffer);
  646. uint32_t font_width = canvas_glyph_width(canvas, '0');
  647. uint32_t x = 37 + (7 - ctx->hex_select) * font_width;
  648. /* draw selection */
  649. canvas_draw_line(canvas, x, y + 1, x + font_width, y + 1);
  650. y += 10;
  651. uint32_t byte_num = 0;
  652. for(int line = 0; line < 4; line++) {
  653. uint32_t x_pos = 5;
  654. for(int byte_pos = 0; byte_pos < 8; byte_pos++) {
  655. if(ctx->hex_buffer_valid[byte_num / 4]) {
  656. snprintf(buffer, sizeof(buffer), "%02X", ctx->hex_buffer[byte_num]);
  657. } else {
  658. snprintf(buffer, sizeof(buffer), "--");
  659. }
  660. byte_num++;
  661. canvas_draw_str_aligned(canvas, x_pos, y, AlignLeft, AlignBottom, buffer);
  662. x_pos += font_width * 2 + font_width / 2;
  663. }
  664. y += 10;
  665. }
  666. break;
  667. }
  668. }
  669. } else {
  670. snprintf(
  671. buffer, sizeof(buffer), "Searching... %c", gpio_direction_ind[ctx->current_mask_id]);
  672. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, buffer);
  673. y += 10;
  674. }
  675. release_mutex((ValueMutex*)cb_ctx, ctx);
  676. }
  677. static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
  678. furi_assert(event_queue);
  679. AppEvent event = {.type = EventKeyPress, .input = *input_event};
  680. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  681. }
  682. static void timer_tick_callback(FuriMessageQueue* event_queue) {
  683. furi_assert(event_queue);
  684. AppEvent event = {.type = EventTimerTick};
  685. furi_message_queue_put(event_queue, &event, 0);
  686. }
  687. static void app_init(AppFSM* const ctx, FuriMessageQueue* event_queue) {
  688. ctx->_event_queue = event_queue;
  689. FuriTimer* timer =
  690. furi_timer_alloc(timer_tick_callback, FuriTimerTypePeriodic, ctx->_event_queue);
  691. furi_timer_start(timer, furi_kernel_get_tick_frequency() / TIMER_HZ);
  692. ctx->_timer = timer;
  693. ctx->current_mask_id = 0;
  694. ctx->current_mask = gpio_direction_mask[ctx->current_mask_id];
  695. ctx->io_swd = 0xFF;
  696. ctx->io_swc = 0xFF;
  697. ctx->hex_addr = 0x40002800;
  698. strcpy(ctx->state_string, "none");
  699. }
  700. static void app_deinit(AppFSM* const ctx) {
  701. furi_timer_free(ctx->_timer);
  702. }
  703. static void on_timer_tick(AppFSM* ctx) {
  704. switch(ctx->mode_page) {
  705. case ModePageScan: {
  706. /* reset after timeout */
  707. if(ctx->detected_timeout == 0) {
  708. ctx->detected_device = false;
  709. ctx->io_swd = 0xFF;
  710. ctx->io_swc = 0xFF;
  711. ctx->io_num_swd = 0xFF;
  712. ctx->io_num_swc = 0xFF;
  713. ctx->ap_scanned = 0;
  714. memset(&ctx->dp_regs, 0x00, sizeof(ctx->dp_regs));
  715. memset(&ctx->targetid_info, 0x00, sizeof(ctx->targetid_info));
  716. memset(&ctx->apidr_info, 0x00, sizeof(ctx->apidr_info));
  717. } else {
  718. ctx->detected_timeout--;
  719. }
  720. ctx->detected = false;
  721. ctx->current_mask = gpio_direction_mask[ctx->current_mask_id];
  722. /* when SWD was already detected, set it to data pin regardless of the mask */
  723. if(ctx->detected_device) {
  724. ctx->current_mask &= ~ctx->io_swd;
  725. }
  726. /* do the scan */
  727. swd_scan(ctx);
  728. /* now when detected a device, set the timeout */
  729. if(ctx->detected) {
  730. ctx->detected_device = true;
  731. ctx->detected_timeout = TIMER_HZ * TIMEOUT;
  732. /* update DPIDR fields */
  733. ctx->dpidr_info.revision = (ctx->dp_regs.dpidr >> 28) & 0x0F;
  734. ctx->dpidr_info.partno = (ctx->dp_regs.dpidr >> 20) & 0xFF;
  735. ctx->dpidr_info.version = (ctx->dp_regs.dpidr >> 12) & 0x0F;
  736. ctx->dpidr_info.designer = (ctx->dp_regs.dpidr >> 1) & 0x3FF;
  737. if(!has_multiple_bits(ctx->io_swc)) {
  738. /* reset error */
  739. uint8_t ack = swd_transfer(ctx, false, false, 1, &ctx->dp_regs.ctrlstat);
  740. furi_log_print_format(
  741. FuriLogLevelDefault, TAG, "stat %02lX %d", ctx->dp_regs.ctrlstat, ack);
  742. if(ack != 1 || (ctx->dp_regs.ctrlstat & STAT_ERROR_FLAGS)) {
  743. furi_log_print_format(FuriLogLevelDefault, TAG, "send ABORT");
  744. swd_abort(ctx);
  745. }
  746. ctx->dp_regs.ctrlstat_ok = swd_read_dpbank(ctx, 1, 0, &ctx->dp_regs.ctrlstat) == 1;
  747. if(ctx->dpidr_info.version >= 1) {
  748. ctx->dp_regs.dlcr_ok = swd_read_dpbank(ctx, 1, 1, &ctx->dp_regs.dlcr) == 1;
  749. }
  750. if(ctx->dpidr_info.version >= 2) {
  751. ctx->dp_regs.targetid_ok =
  752. swd_read_dpbank(ctx, 1, 2, &ctx->dp_regs.targetid) == 1;
  753. ctx->dp_regs.eventstat_ok =
  754. swd_read_dpbank(ctx, 1, 4, &ctx->dp_regs.eventstat) == 1;
  755. ctx->dp_regs.dlpidr_ok = swd_read_dpbank(ctx, 1, 3, &ctx->dp_regs.dlpidr) == 1;
  756. }
  757. if(ctx->dp_regs.targetid_ok) {
  758. ctx->targetid_info.revision = (ctx->dp_regs.targetid >> 28) & 0x0F;
  759. ctx->targetid_info.partno = (ctx->dp_regs.targetid >> 12) & 0xFFFF;
  760. ctx->targetid_info.designer = (ctx->dp_regs.targetid >> 1) & 0x3FF;
  761. }
  762. }
  763. }
  764. ctx->current_mask_id = (ctx->current_mask_id + 1) % COUNT(gpio_direction_mask);
  765. break;
  766. }
  767. case ModePageDPRegs:
  768. case ModePageDPID:
  769. case ModePageAPID: {
  770. /* set debug enable request */
  771. if(!swd_ensure_powerup(ctx)) {
  772. break;
  773. }
  774. /* only scan a few APs at once to stay responsive */
  775. for(int pos = 0; pos < 5; pos++) {
  776. if(ctx->ap_scanned == 0) {
  777. swd_apscan_reset(ctx);
  778. }
  779. uint8_t ap = ctx->ap_scanned++;
  780. if(ctx->apidr_info[ap].tested) {
  781. continue;
  782. }
  783. if(swd_apscan_test(ctx, ap)) {
  784. break;
  785. }
  786. }
  787. break;
  788. }
  789. case ModePageHexDump: {
  790. if(ctx->hex_read_delay++ < 10) {
  791. break;
  792. }
  793. ctx->hex_read_delay = 0;
  794. memset(ctx->hex_buffer, 0xEE, sizeof(ctx->hex_buffer));
  795. uint32_t addr = ctx->hex_addr;
  796. uint32_t data = 0;
  797. for(int pos = 0; pos < sizeof(ctx->hex_buffer) / 4; pos++) {
  798. ctx->hex_buffer_valid[pos] = swd_read_memory(ctx, ctx->ap_pos, addr, &data) == 1;
  799. if(ctx->hex_buffer_valid[pos]) {
  800. memcpy(&ctx->hex_buffer[pos * 4], &data, 4);
  801. } else {
  802. swd_abort_simple(ctx);
  803. }
  804. addr += 4;
  805. }
  806. }
  807. }
  808. }
  809. int32_t swd_probe_app_main(void* p) {
  810. UNUSED(p);
  811. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent));
  812. AppFSM* ctx = malloc(sizeof(AppFSM));
  813. app_init(ctx, event_queue);
  814. ValueMutex state_mutex;
  815. if(!init_mutex(&state_mutex, ctx, sizeof(AppFSM))) {
  816. FURI_LOG_E(TAG, "cannot create mutex\r\n");
  817. free(ctx);
  818. return 255;
  819. }
  820. ViewPort* view_port = view_port_alloc();
  821. view_port_draw_callback_set(view_port, render_callback, &state_mutex);
  822. view_port_input_callback_set(view_port, input_callback, event_queue);
  823. Gui* gui = furi_record_open(RECORD_GUI);
  824. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  825. ctx->notification = furi_record_open(RECORD_NOTIFICATION);
  826. notification_message_block(ctx->notification, &sequence_display_backlight_enforce_on);
  827. DOLPHIN_DEED(DolphinDeedPluginGameStart);
  828. AppEvent event;
  829. for(bool processing = true; processing;) {
  830. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  831. AppFSM* ctx = (AppFSM*)acquire_mutex_block(&state_mutex);
  832. if(event_status == FuriStatusOk) {
  833. if(event.type == EventKeyPress) {
  834. if(event.input.type == InputTypePress) {
  835. switch(event.input.key) {
  836. case InputKeyUp:
  837. ctx->last_key = KeyUp;
  838. switch(ctx->mode_page) {
  839. default:
  840. break;
  841. case 3:
  842. if(ctx->ap_pos > 0) {
  843. ctx->ap_pos--;
  844. }
  845. break;
  846. case 4: {
  847. ctx->hex_addr +=
  848. ((ctx->hex_select) ? 1 : 8) * (1 << (4 * ctx->hex_select));
  849. break;
  850. }
  851. }
  852. break;
  853. case InputKeyDown:
  854. ctx->last_key = KeyDown;
  855. switch(ctx->mode_page) {
  856. default:
  857. break;
  858. case 3:
  859. if(ctx->ap_pos + 1 < COUNT(ctx->apidr_info)) {
  860. ctx->ap_pos++;
  861. }
  862. break;
  863. case 4: {
  864. ctx->hex_addr -=
  865. ((ctx->hex_select) ? 1 : 8) * (1 << (4 * ctx->hex_select));
  866. break;
  867. }
  868. }
  869. break;
  870. case InputKeyRight:
  871. ctx->last_key = KeyRight;
  872. if(ctx->mode_page == ModePageHexDump) {
  873. if(ctx->hex_select > 0) {
  874. ctx->hex_select--;
  875. }
  876. } else {
  877. if(ctx->mode_page + 1 < ModePageCount) {
  878. ctx->mode_page++;
  879. }
  880. }
  881. break;
  882. case InputKeyLeft:
  883. ctx->last_key = KeyLeft;
  884. if(ctx->mode_page == ModePageHexDump) {
  885. if(ctx->hex_select < 7) {
  886. ctx->hex_select++;
  887. }
  888. } else {
  889. if(ctx->mode_page > 0) {
  890. ctx->mode_page--;
  891. }
  892. }
  893. break;
  894. case InputKeyOk:
  895. ctx->last_key = KeyOK;
  896. if(ctx->mode_page == ModePageAPID && ctx->apidr_info[ctx->ap_pos].ok) {
  897. ctx->mode_page = ModePageHexDump;
  898. }
  899. break;
  900. case InputKeyBack:
  901. if(ctx->mode_page == ModePageHexDump) {
  902. ctx->mode_page = ModePageAPID;
  903. } else if(ctx->mode_page != ModePageScan) {
  904. ctx->mode_page = ModePageScan;
  905. } else {
  906. processing = false;
  907. }
  908. break;
  909. default:
  910. break;
  911. }
  912. }
  913. } else if(event.type == EventTimerTick) {
  914. FURI_CRITICAL_ENTER();
  915. on_timer_tick(ctx);
  916. FURI_CRITICAL_EXIT();
  917. }
  918. } else {
  919. /* timeout */
  920. }
  921. view_port_update(view_port);
  922. bool beep = false;
  923. if(ctx->detected_device && !ctx->detected_notified) {
  924. ctx->detected_notified = true;
  925. beep = true;
  926. }
  927. if(!ctx->detected_device && ctx->detected_notified) {
  928. ctx->detected_notified = false;
  929. }
  930. release_mutex(&state_mutex, ctx);
  931. if(beep) {
  932. notification_message_block(ctx->notification, &seq_c_minor);
  933. }
  934. }
  935. // Wait for all notifications to be played and return backlight to normal state
  936. app_deinit(ctx);
  937. notification_message_block(ctx->notification, &sequence_display_backlight_enforce_auto);
  938. view_port_enabled_set(view_port, false);
  939. gui_remove_view_port(gui, view_port);
  940. furi_record_close(RECORD_GUI);
  941. furi_record_close(RECORD_NOTIFICATION);
  942. view_port_free(view_port);
  943. furi_message_queue_free(event_queue);
  944. delete_mutex(&state_mutex);
  945. free(ctx);
  946. return 0;
  947. }