swd_probe_app.c 33 KB

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