swd_probe_app.c 35 KB

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