swd_probe_app.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  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(ctx->current_mask & bitmask) {
  45. /* set for clock */
  46. furi_hal_gpio_init(gpios[io], GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  47. } else {
  48. /* set for data */
  49. if(!output) {
  50. furi_hal_gpio_init(gpios[io], GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
  51. } else {
  52. furi_hal_gpio_init(
  53. gpios[io], GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedVeryHigh);
  54. }
  55. }
  56. }
  57. }
  58. static void swd_set_clock(AppFSM* const ctx, const uint8_t level) {
  59. for(int io = 0; io < 8; io++) {
  60. uint8_t bitmask = 1 << io;
  61. if(ctx->current_mask & bitmask) {
  62. furi_hal_gpio_write(gpios[io], level);
  63. }
  64. }
  65. }
  66. static void swd_set_data(AppFSM* const ctx, const uint8_t level) {
  67. for(int io = 0; io < 8; io++) {
  68. uint8_t bitmask = 1 << io;
  69. if(!(ctx->current_mask & bitmask)) {
  70. furi_hal_gpio_write(gpios[io], level);
  71. }
  72. }
  73. }
  74. static uint8_t swd_get_data(AppFSM* const ctx) {
  75. uint8_t bits = 0;
  76. for(int io = 0; io < 8; io++) {
  77. bits >>= 1;
  78. bits |= furi_hal_gpio_read(gpios[io]) ? 0x80 : 0;
  79. }
  80. return bits;
  81. }
  82. static void swd_write_bit(AppFSM* const ctx, bool level) {
  83. swd_set_clock(ctx, 0);
  84. swd_set_data(ctx, level);
  85. furi_delay_us(5);
  86. swd_set_clock(ctx, 1);
  87. furi_delay_us(5);
  88. swd_set_clock(ctx, 0);
  89. }
  90. static uint8_t swd_read_bit(AppFSM* const ctx) {
  91. swd_set_clock(ctx, 0);
  92. furi_delay_us(5);
  93. swd_set_clock(ctx, 1);
  94. furi_delay_us(5);
  95. uint8_t bits = swd_get_data(ctx);
  96. swd_set_clock(ctx, 0);
  97. return bits;
  98. }
  99. /* send a byte or less LSB-first */
  100. static void swd_write_byte(AppFSM* const ctx, const uint8_t data, size_t bits) {
  101. for(size_t pos = 0; pos < bits; pos++) {
  102. swd_write_bit(ctx, data & (1 << pos));
  103. }
  104. }
  105. /* send a sequence of bytes LSB-first */
  106. static void swd_write(AppFSM* const ctx, const uint8_t* data, size_t bits) {
  107. size_t byte_pos = 0;
  108. while(bits > 0) {
  109. size_t remain = (bits > 8) ? 8 : bits;
  110. swd_write_byte(ctx, data[byte_pos++], remain);
  111. bits -= remain;
  112. }
  113. }
  114. static uint32_t swd_detect(AppFSM* const ctx) {
  115. swd_configure_pins(ctx, true);
  116. uint8_t data[] = {0xA5};
  117. swd_write(ctx, data, sizeof(data) * 8);
  118. /* turnaround cycle */
  119. swd_configure_pins(ctx, false);
  120. uint8_t ack_bits[3];
  121. uint8_t rdata[32];
  122. /* receive 3 ACK bits */
  123. for(int pos = 0; pos < 3; pos++) {
  124. ack_bits[pos] = swd_read_bit(ctx);
  125. }
  126. /* receive 32 RDATA bits */
  127. for(int pos = 0; pos < 32; pos++) {
  128. rdata[pos] = swd_read_bit(ctx);
  129. }
  130. /* receive parity bit */
  131. uint8_t parity = swd_read_bit(ctx);
  132. for(int io = 0; io < 8; io++) {
  133. uint8_t bitmask = 1 << io;
  134. /* skip if it's a clock */
  135. if(ctx->current_mask & bitmask) {
  136. continue;
  137. }
  138. uint8_t ack = 0;
  139. for(int pos = 0; pos < 3; pos++) {
  140. ack >>= 1;
  141. ack |= (ack_bits[pos] & bitmask) ? 4 : 0;
  142. }
  143. uint32_t dpidr = 0;
  144. bool dpdidr_parity = false;
  145. for(int pos = 0; pos < 32; pos++) {
  146. bool bit_set = (rdata[pos] & bitmask);
  147. dpidr >>= 1;
  148. dpidr |= bit_set ? 0x80000000 : 0;
  149. dpdidr_parity ^= bit_set;
  150. }
  151. if(ack == 1) {
  152. bool received_parity = (parity & bitmask);
  153. if(dpdidr_parity == received_parity) {
  154. ctx->dpidr = dpidr;
  155. ctx->detected = true;
  156. ctx->io_swd = bitmask;
  157. ctx->io_swc &= ctx->current_mask;
  158. }
  159. }
  160. }
  161. return 0;
  162. }
  163. /* A line reset is achieved by holding the data signal HIGH for at least 50 clock cycles, followed by at least two idle cycles. */
  164. static void swd_line_reset(AppFSM* const ctx) {
  165. for(int bitcount = 0; bitcount < 50; bitcount += 8) {
  166. swd_write_byte(ctx, 0xFF, 8);
  167. }
  168. swd_write_byte(ctx, 0, 8);
  169. }
  170. static void swd_scan(AppFSM* const ctx) {
  171. /* To switch SWJ-DP from JTAG to SWD operation:
  172. 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.
  173. 2. Send the 16-bit JTAG-to-SWD select sequence 0x79e7 on SWDIOTMS.
  174. 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.
  175. */
  176. swd_configure_pins(ctx, true);
  177. /* reset JTAG interface */
  178. for(int bitcount = 0; bitcount < 50; bitcount += 8) {
  179. swd_write_byte(ctx, 0xFF, 8);
  180. }
  181. /* Send the 16-bit JTAG-to-SWD select sequence */
  182. swd_write_byte(ctx, 0x9E, 8);
  183. swd_write_byte(ctx, 0xE7, 8);
  184. /* resynchronize SWD */
  185. swd_line_reset(ctx);
  186. swd_detect(ctx);
  187. }
  188. static void render_callback(Canvas* const canvas, void* cb_ctx) {
  189. AppFSM* ctx = acquire_mutex((ValueMutex*)cb_ctx, 25);
  190. if(ctx == NULL) {
  191. return;
  192. }
  193. char buffer[64];
  194. int y = 10;
  195. if(ctx->detected_device) {
  196. snprintf(buffer, sizeof(buffer), "FOUND!");
  197. } else {
  198. snprintf(
  199. buffer, sizeof(buffer), "Searching... %c", gpio_direction_ind[ctx->current_mask_id]);
  200. }
  201. canvas_draw_frame(canvas, 0, 0, 128, 64);
  202. canvas_set_font(canvas, FontPrimary);
  203. canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, buffer);
  204. y += 10;
  205. if(ctx->detected_device) {
  206. canvas_set_font(canvas, FontKeyboard);
  207. snprintf(
  208. buffer,
  209. sizeof(buffer),
  210. "SWC/SWD: %s/%s",
  211. gpio_name(ctx->io_swc),
  212. gpio_name(ctx->io_swd));
  213. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  214. y += 10;
  215. snprintf(buffer, sizeof(buffer), "DPIDR 0x%08lX", ctx->dpidr);
  216. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  217. y += 10;
  218. uint16_t rev = (ctx->dpidr >> 28) & 0x0F;
  219. uint16_t part = (ctx->dpidr >> 20) & 0xFF;
  220. uint16_t dap = (ctx->dpidr >> 12) & 0x0F;
  221. uint16_t designer_id = (ctx->dpidr >> 1) & 0x3FF;
  222. const char* designer = jep106_manufacturer(designer_id);
  223. snprintf(buffer, sizeof(buffer), "Part %02X Rev %X DAPv%d", part, rev, dap);
  224. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  225. y += 10;
  226. canvas_set_font(canvas, FontSecondary);
  227. snprintf(buffer, sizeof(buffer), "Des.: %s", designer);
  228. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  229. y += 10;
  230. }
  231. release_mutex((ValueMutex*)cb_ctx, ctx);
  232. }
  233. static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
  234. furi_assert(event_queue);
  235. AppEvent event = {.type = EventKeyPress, .input = *input_event};
  236. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  237. }
  238. static void timer_tick_callback(FuriMessageQueue* event_queue) {
  239. furi_assert(event_queue);
  240. AppEvent event = {.type = EventTimerTick};
  241. furi_message_queue_put(event_queue, &event, 0);
  242. }
  243. static void app_init(AppFSM* const ctx, FuriMessageQueue* event_queue) {
  244. ctx->_event_queue = event_queue;
  245. FuriTimer* timer =
  246. furi_timer_alloc(timer_tick_callback, FuriTimerTypePeriodic, ctx->_event_queue);
  247. furi_timer_start(timer, furi_kernel_get_tick_frequency() / TIMER_HZ);
  248. ctx->_timer = timer;
  249. ctx->current_mask_id = 0;
  250. ctx->current_mask = gpio_direction_mask[ctx->current_mask_id];
  251. ctx->io_swd = 0xFF;
  252. ctx->io_swc = 0xFF;
  253. strcpy(ctx->state_string, "none");
  254. }
  255. static void app_deinit(AppFSM* const ctx) {
  256. furi_timer_free(ctx->_timer);
  257. }
  258. static void on_timer_tick(AppFSM* ctx) {
  259. /* reset after timeout */
  260. if(ctx->detected_timeout == 0) {
  261. ctx->detected_device = false;
  262. ctx->io_swd = 0xFF;
  263. ctx->io_swc = 0xFF;
  264. } else {
  265. ctx->detected_timeout--;
  266. }
  267. ctx->detected = false;
  268. ctx->current_mask = gpio_direction_mask[ctx->current_mask_id];
  269. /* when SWD was already detected, set it to data pin regardless of the mask */
  270. if(ctx->detected_device) {
  271. ctx->current_mask &= ~ctx->io_swd;
  272. }
  273. /* do the scan */
  274. swd_scan(ctx);
  275. /* now when detected a device, set the timeout */
  276. if(ctx->detected) {
  277. ctx->detected_device = true;
  278. ctx->detected_timeout = TIMER_HZ * 3;
  279. }
  280. ctx->current_mask_id = (ctx->current_mask_id + 1) % COUNT(gpio_direction_mask);
  281. }
  282. int32_t swd_probe_app_main(void* p) {
  283. UNUSED(p);
  284. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent));
  285. AppFSM* ctx = malloc(sizeof(AppFSM));
  286. app_init(ctx, event_queue);
  287. ValueMutex state_mutex;
  288. if(!init_mutex(&state_mutex, ctx, sizeof(AppFSM))) {
  289. FURI_LOG_E(TAG, "cannot create mutex\r\n");
  290. free(ctx);
  291. return 255;
  292. }
  293. ViewPort* view_port = view_port_alloc();
  294. view_port_draw_callback_set(view_port, render_callback, &state_mutex);
  295. view_port_input_callback_set(view_port, input_callback, event_queue);
  296. Gui* gui = furi_record_open(RECORD_GUI);
  297. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  298. ctx->notification = furi_record_open(RECORD_NOTIFICATION);
  299. notification_message_block(ctx->notification, &sequence_display_backlight_enforce_on);
  300. DOLPHIN_DEED(DolphinDeedPluginGameStart);
  301. AppEvent event;
  302. for(bool processing = true; processing;) {
  303. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  304. AppFSM* ctx = (AppFSM*)acquire_mutex_block(&state_mutex);
  305. if(event_status == FuriStatusOk) {
  306. if(event.type == EventKeyPress) {
  307. if(event.input.type == InputTypePress) {
  308. switch(event.input.key) {
  309. case InputKeyUp:
  310. ctx->last_key = KeyUp;
  311. break;
  312. case InputKeyDown:
  313. ctx->last_key = KeyDown;
  314. break;
  315. case InputKeyRight:
  316. ctx->last_key = KeyRight;
  317. break;
  318. case InputKeyLeft:
  319. ctx->last_key = KeyLeft;
  320. break;
  321. case InputKeyOk:
  322. ctx->last_key = KeyOK;
  323. break;
  324. case InputKeyBack:
  325. processing = false;
  326. break;
  327. default:
  328. break;
  329. }
  330. }
  331. } else if(event.type == EventTimerTick) {
  332. FURI_CRITICAL_ENTER();
  333. on_timer_tick(ctx);
  334. FURI_CRITICAL_EXIT();
  335. }
  336. } else {
  337. /* timeout */
  338. }
  339. view_port_update(view_port);
  340. if(ctx->detected_device && !ctx->detected_notified) {
  341. notification_message_block(ctx->notification, &seq_c_minor);
  342. ctx->detected_notified = true;
  343. }
  344. if(!ctx->detected_device && ctx->detected_notified) {
  345. ctx->detected_notified = false;
  346. }
  347. release_mutex(&state_mutex, ctx);
  348. }
  349. // Wait for all notifications to be played and return backlight to normal state
  350. app_deinit(ctx);
  351. notification_message_block(ctx->notification, &sequence_display_backlight_enforce_auto);
  352. view_port_enabled_set(view_port, false);
  353. gui_remove_view_port(gui, view_port);
  354. furi_record_close(RECORD_GUI);
  355. furi_record_close(RECORD_NOTIFICATION);
  356. view_port_free(view_port);
  357. furi_message_queue_free(event_queue);
  358. delete_mutex(&state_mutex);
  359. free(ctx);
  360. return 0;
  361. }