swd_probe_app.c 35 KB

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