hla: cleanup read/write api
[openocd.git] / src / jtag / drivers / ti_icdi_usb.c
1 /***************************************************************************
2 * *
3 * Copyright (C) 2012 by Spencer Oliver *
4 * spen@spen-soft.co.uk *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
20 ***************************************************************************/
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 /* project specific includes */
27 #include <helper/binarybuffer.h>
28 #include <jtag/interface.h>
29 #include <jtag/hla/hla_layout.h>
30 #include <jtag/hla/hla_transport.h>
31 #include <jtag/hla/hla_interface.h>
32 #include <target/target.h>
33
34 #include <target/cortex_m.h>
35
36 #include <libusb.h>
37
38 #define ICDI_WRITE_ENDPOINT 0x02
39 #define ICDI_READ_ENDPOINT 0x83
40
41 #define ICDI_WRITE_TIMEOUT 1000
42 #define ICDI_READ_TIMEOUT 1000
43 #define ICDI_PACKET_SIZE 2048
44
45 #define PACKET_START "$"
46 #define PACKET_END "#"
47
48 struct icdi_usb_handle_s {
49 libusb_context *usb_ctx;
50 libusb_device_handle *usb_dev;
51
52 char *read_buffer;
53 char *write_buffer;
54 int max_packet;
55 int read_count;
56 };
57
58 static int icdi_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
59 uint32_t count, uint8_t *buffer);
60 static int icdi_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
61 uint32_t count, const uint8_t *buffer);
62
63 static int remote_escape_output(const char *buffer, int len, char *out_buf, int *out_len, int out_maxlen)
64 {
65 int input_index, output_index;
66
67 output_index = 0;
68
69 for (input_index = 0; input_index < len; input_index++) {
70
71 char b = buffer[input_index];
72
73 if (b == '$' || b == '#' || b == '}' || b == '*') {
74 /* These must be escaped. */
75 if (output_index + 2 > out_maxlen)
76 break;
77 out_buf[output_index++] = '}';
78 out_buf[output_index++] = b ^ 0x20;
79 } else {
80 if (output_index + 1 > out_maxlen)
81 break;
82 out_buf[output_index++] = b;
83 }
84 }
85
86 *out_len = input_index;
87 return output_index;
88 }
89
90 static int remote_unescape_input(const char *buffer, int len, char *out_buf, int out_maxlen)
91 {
92 int input_index, output_index;
93 int escaped;
94
95 output_index = 0;
96 escaped = 0;
97
98 for (input_index = 0; input_index < len; input_index++) {
99
100 char b = buffer[input_index];
101
102 if (output_index + 1 > out_maxlen)
103 LOG_ERROR("Received too much data from the target.");
104
105 if (escaped) {
106 out_buf[output_index++] = b ^ 0x20;
107 escaped = 0;
108 } else if (b == '}')
109 escaped = 1;
110 else
111 out_buf[output_index++] = b;
112 }
113
114 if (escaped)
115 LOG_ERROR("Unmatched escape character in target response.");
116
117 return output_index;
118 }
119
120 static int icdi_send_packet(void *handle, int len)
121 {
122 unsigned char cksum = 0;
123 struct icdi_usb_handle_s *h;
124 int result, retry = 0;
125 int transferred = 0;
126
127 assert(handle != NULL);
128 h = (struct icdi_usb_handle_s *)handle;
129
130 /* check we have a large enough buffer for checksum "#00" */
131 if (len + 3 > h->max_packet) {
132 LOG_ERROR("packet buffer too small");
133 return ERROR_FAIL;
134 }
135
136 /* calculate checksum - offset start of packet */
137 for (int i = 1; i < len; i++)
138 cksum += h->write_buffer[i];
139
140 len += sprintf(&h->write_buffer[len], PACKET_END "%02x", cksum);
141
142 #ifdef _DEBUG_USB_COMMS_
143 char buffer[50];
144 char ch = h->write_buffer[1];
145 if (ch == 'x' || ch == 'X')
146 LOG_DEBUG("writing packet: <binary>");
147 else {
148 memcpy(buffer, h->write_buffer, len >= 50 ? 50-1 : len);
149 buffer[len] = 0;
150 LOG_DEBUG("writing packet: %s", buffer);
151 }
152 #endif
153
154 while (1) {
155
156 result = libusb_bulk_transfer(h->usb_dev, ICDI_WRITE_ENDPOINT, (unsigned char *)h->write_buffer, len,
157 &transferred, ICDI_WRITE_TIMEOUT);
158 if (result != 0 || transferred != len) {
159 LOG_DEBUG("Error TX Data %d", result);
160 return ERROR_FAIL;
161 }
162
163 /* check that the client got the message ok, or shall we resend */
164 result = libusb_bulk_transfer(h->usb_dev, ICDI_READ_ENDPOINT, (unsigned char *)h->read_buffer, h->max_packet,
165 &transferred, ICDI_READ_TIMEOUT);
166 if (result != 0 || transferred < 1) {
167 LOG_DEBUG("Error RX Data %d", result);
168 return ERROR_FAIL;
169 }
170
171 #ifdef _DEBUG_USB_COMMS_
172 LOG_DEBUG("received reply: '%c' : count %d", h->read_buffer[0], transferred);
173 #endif
174
175 if (h->read_buffer[0] == '-') {
176 LOG_DEBUG("Resending packet %d", ++retry);
177 } else {
178 if (h->read_buffer[0] != '+')
179 LOG_DEBUG("Unexpected Reply from ICDI: %c", h->read_buffer[0]);
180 break;
181 }
182
183 if (retry == 3) {
184 LOG_DEBUG("maximum nack retries attempted");
185 return ERROR_FAIL;
186 }
187 }
188
189 retry = 0;
190 h->read_count = transferred;
191
192 while (1) {
193
194 /* read reply from icdi */
195 result = libusb_bulk_transfer(h->usb_dev, ICDI_READ_ENDPOINT, (unsigned char *)h->read_buffer + h->read_count,
196 h->max_packet - h->read_count, &transferred, ICDI_READ_TIMEOUT);
197
198 #ifdef _DEBUG_USB_COMMS_
199 LOG_DEBUG("received data: count %d", transferred);
200 #endif
201
202 /* check for errors but retry for timeout */
203 if (result != 0) {
204
205 if (result == LIBUSB_ERROR_TIMEOUT) {
206 LOG_DEBUG("Error RX timeout %d", result);
207 } else {
208 LOG_DEBUG("Error RX Data %d", result);
209 return ERROR_FAIL;
210 }
211 }
212
213 h->read_count += transferred;
214
215 /* we need to make sure we have a full packet, including checksum */
216 if (h->read_count > 5) {
217
218 /* check that we have received an packet delimiter
219 * we do not validate the checksum
220 * reply should contain $...#AA - so we check for # */
221 if (h->read_buffer[h->read_count - 3] == '#')
222 return ERROR_OK;
223 }
224
225 if (retry++ == 3) {
226 LOG_DEBUG("maximum data retries attempted");
227 break;
228 }
229 }
230
231 return ERROR_FAIL;
232 }
233
234 static int icdi_send_cmd(void *handle, const char *cmd)
235 {
236 struct icdi_usb_handle_s *h;
237 h = (struct icdi_usb_handle_s *)handle;
238
239 int cmd_len = snprintf(h->write_buffer, h->max_packet, PACKET_START "%s", cmd);
240 return icdi_send_packet(handle, cmd_len);
241 }
242
243 static int icdi_send_remote_cmd(void *handle, const char *data)
244 {
245 struct icdi_usb_handle_s *h;
246 h = (struct icdi_usb_handle_s *)handle;
247
248 size_t cmd_len = sprintf(h->write_buffer, PACKET_START "qRcmd,");
249 cmd_len += hexify(h->write_buffer + cmd_len, data, 0, h->max_packet - cmd_len);
250
251 return icdi_send_packet(handle, cmd_len);
252 }
253
254 static int icdi_get_cmd_result(void *handle)
255 {
256 struct icdi_usb_handle_s *h;
257 int offset = 0;
258 char ch;
259
260 assert(handle != NULL);
261 h = (struct icdi_usb_handle_s *)handle;
262
263 do {
264 ch = h->read_buffer[offset++];
265 if (offset > h->read_count)
266 return ERROR_FAIL;
267 } while (ch != '$');
268
269 if (memcmp("OK", h->read_buffer + offset, 2) == 0)
270 return ERROR_OK;
271
272 if (h->read_buffer[offset] == 'E') {
273 /* get error code */
274 char result;
275 if (unhexify(&result, h->read_buffer + offset + 1, 1) != 1)
276 return ERROR_FAIL;
277 return result;
278 }
279
280 /* for now we assume everything else is ok */
281 return ERROR_OK;
282 }
283
284 static int icdi_usb_idcode(void *handle, uint32_t *idcode)
285 {
286 return ERROR_OK;
287 }
288
289 static int icdi_usb_write_debug_reg(void *handle, uint32_t addr, uint32_t val)
290 {
291 return icdi_usb_write_mem(handle, addr, 4, 1, (uint8_t *)&val);
292 }
293
294 static enum target_state icdi_usb_state(void *handle)
295 {
296 int result;
297 struct icdi_usb_handle_s *h;
298 uint32_t dhcsr;
299
300 h = (struct icdi_usb_handle_s *)handle;
301
302 result = icdi_usb_read_mem(h, DCB_DHCSR, 4, 1, (uint8_t *)&dhcsr);
303 if (result != ERROR_OK)
304 return TARGET_UNKNOWN;
305
306 if (dhcsr & S_HALT)
307 return TARGET_HALTED;
308
309 return TARGET_RUNNING;
310 }
311
312 static int icdi_usb_version(void *handle)
313 {
314 struct icdi_usb_handle_s *h;
315 h = (struct icdi_usb_handle_s *)handle;
316
317 char version[20];
318
319 /* get info about icdi */
320 int result = icdi_send_remote_cmd(handle, "version");
321 if (result != ERROR_OK)
322 return result;
323
324 if (h->read_count < 8) {
325 LOG_ERROR("Invalid Reply Received");
326 return ERROR_FAIL;
327 }
328
329 /* convert reply */
330 if (unhexify(version, h->read_buffer + 2, 4) != 4) {
331 LOG_WARNING("unable to get ICDI version");
332 return ERROR_OK;
333 }
334
335 /* null terminate and print info */
336 version[4] = 0;
337
338 LOG_INFO("ICDI Firmware version: %s", version);
339
340 return ERROR_OK;
341 }
342
343 static int icdi_usb_query(void *handle)
344 {
345 int result;
346
347 struct icdi_usb_handle_s *h;
348 h = (struct icdi_usb_handle_s *)handle;
349
350 result = icdi_send_cmd(handle, "qSupported");
351 if (result != ERROR_OK)
352 return result;
353
354 /* check result */
355 result = icdi_get_cmd_result(handle);
356 if (result != ERROR_OK) {
357 LOG_ERROR("query supported failed: 0x%x", result);
358 return ERROR_FAIL;
359 }
360
361 /* from this we can get the max packet supported */
362
363 /* query packet buffer size */
364 char *offset = strstr(h->read_buffer, "PacketSize");
365 if (offset) {
366 char *separator;
367 int max_packet;
368
369 max_packet = strtoul(offset + 11, &separator, 16);
370 if (!max_packet)
371 LOG_ERROR("invalid max packet, using defaults");
372 else
373 h->max_packet = max_packet;
374 LOG_DEBUG("max packet supported : %" PRIu32 " bytes", h->max_packet);
375 }
376
377
378 /* if required re allocate packet buffer */
379 if (h->max_packet != ICDI_PACKET_SIZE) {
380 h->read_buffer = realloc(h->read_buffer, h->max_packet);
381 h->write_buffer = realloc(h->write_buffer, h->max_packet);
382 if (h->read_buffer == 0 || h->write_buffer == 0) {
383 LOG_ERROR("unable to reallocate memory");
384 return ERROR_FAIL;
385 }
386 }
387
388 /* set extended mode */
389 result = icdi_send_cmd(handle, "!");
390 if (result != ERROR_OK)
391 return result;
392
393 /* check result */
394 result = icdi_get_cmd_result(handle);
395 if (result != ERROR_OK) {
396 LOG_ERROR("unable to enable extended mode: 0x%x", result);
397 return ERROR_FAIL;
398 }
399
400 return ERROR_OK;
401 }
402
403 static int icdi_usb_reset(void *handle)
404 {
405 /* we do this in hla_target.c */
406 return ERROR_OK;
407 }
408
409 static int icdi_usb_assert_srst(void *handle, int srst)
410 {
411 /* TODO not supported yet */
412 return ERROR_COMMAND_NOTFOUND;
413 }
414
415 static int icdi_usb_run(void *handle)
416 {
417 int result;
418
419 /* resume target at current address */
420 result = icdi_send_cmd(handle, "c");
421 if (result != ERROR_OK)
422 return result;
423
424 /* check result */
425 result = icdi_get_cmd_result(handle);
426 if (result != ERROR_OK) {
427 LOG_ERROR("continue failed: 0x%x", result);
428 return ERROR_FAIL;
429 }
430
431 return result;
432 }
433
434 static int icdi_usb_halt(void *handle)
435 {
436 int result;
437
438 /* this query halts the target ?? */
439 result = icdi_send_cmd(handle, "?");
440 if (result != ERROR_OK)
441 return result;
442
443 /* check result */
444 result = icdi_get_cmd_result(handle);
445 if (result != ERROR_OK) {
446 LOG_ERROR("halt failed: 0x%x", result);
447 return ERROR_FAIL;
448 }
449
450 return result;
451 }
452
453 static int icdi_usb_step(void *handle)
454 {
455 int result;
456
457 /* step target at current address */
458 result = icdi_send_cmd(handle, "s");
459 if (result != ERROR_OK)
460 return result;
461
462 /* check result */
463 result = icdi_get_cmd_result(handle);
464 if (result != ERROR_OK) {
465 LOG_ERROR("step failed: 0x%x", result);
466 return ERROR_FAIL;
467 }
468
469 return result;
470 }
471
472 static int icdi_usb_read_regs(void *handle)
473 {
474 /* currently unsupported */
475 return ERROR_OK;
476 }
477
478 static int icdi_usb_read_reg(void *handle, int num, uint32_t *val)
479 {
480 int result;
481 struct icdi_usb_handle_s *h;
482 char cmd[10];
483
484 h = (struct icdi_usb_handle_s *)handle;
485
486 snprintf(cmd, sizeof(cmd), "p%x", num);
487 result = icdi_send_cmd(handle, cmd);
488 if (result != ERROR_OK)
489 return result;
490
491 /* check result */
492 result = icdi_get_cmd_result(handle);
493 if (result != ERROR_OK) {
494 LOG_ERROR("register read failed: 0x%x", result);
495 return ERROR_FAIL;
496 }
497
498 /* convert result */
499 if (unhexify((char *)val, h->read_buffer + 2, 4) != 4) {
500 LOG_ERROR("failed to convert result");
501 return ERROR_FAIL;
502 }
503
504 return result;
505 }
506
507 static int icdi_usb_write_reg(void *handle, int num, uint32_t val)
508 {
509 int result;
510 char cmd[20];
511
512 int cmd_len = snprintf(cmd, sizeof(cmd), "P%x=", num);
513 hexify(cmd + cmd_len, (char *)&val, 4, sizeof(cmd));
514
515 result = icdi_send_cmd(handle, cmd);
516 if (result != ERROR_OK)
517 return result;
518
519 /* check result */
520 result = icdi_get_cmd_result(handle);
521 if (result != ERROR_OK) {
522 LOG_ERROR("register write failed: 0x%x", result);
523 return ERROR_FAIL;
524 }
525
526 return result;
527 }
528
529 static int icdi_usb_read_mem_int(void *handle, uint32_t addr, uint32_t len, uint8_t *buffer)
530 {
531 int result;
532 struct icdi_usb_handle_s *h;
533 char cmd[20];
534
535 h = (struct icdi_usb_handle_s *)handle;
536
537 snprintf(cmd, sizeof(cmd), "x%x,%x", addr, len);
538 result = icdi_send_cmd(handle, cmd);
539 if (result != ERROR_OK)
540 return result;
541
542 /* check result */
543 result = icdi_get_cmd_result(handle);
544 if (result != ERROR_OK) {
545 LOG_ERROR("memory read failed: 0x%x", result);
546 return ERROR_FAIL;
547 }
548
549 /* unescape input */
550 int read_len = remote_unescape_input(h->read_buffer + 5, h->read_count - 8, (char *)buffer, len);
551 if (read_len != (int)len) {
552 LOG_ERROR("read more bytes than expected: actual 0x%" PRIx32 " expected 0x%" PRIx32, read_len, len);
553 return ERROR_FAIL;
554 }
555
556 return ERROR_OK;
557 }
558
559 static int icdi_usb_write_mem_int(void *handle, uint32_t addr, uint32_t len, const uint8_t *buffer)
560 {
561 int result;
562 struct icdi_usb_handle_s *h;
563
564 h = (struct icdi_usb_handle_s *)handle;
565
566 size_t cmd_len = snprintf(h->write_buffer, h->max_packet, PACKET_START "X%x,%x:", addr, len);
567
568 int out_len;
569 cmd_len += remote_escape_output((char *)buffer, len, h->write_buffer + cmd_len,
570 &out_len, h->max_packet - cmd_len);
571
572 if (out_len < (int)len) {
573 /* for now issue a error as we have no way of allocating a larger buffer */
574 LOG_ERROR("memory buffer too small: requires 0x%" PRIx32 " actual 0x%" PRIx32, out_len, len);
575 return ERROR_FAIL;
576 }
577
578 result = icdi_send_packet(handle, cmd_len);
579 if (result != ERROR_OK)
580 return result;
581
582 /* check result */
583 result = icdi_get_cmd_result(handle);
584 if (result != ERROR_OK) {
585 LOG_ERROR("memory write failed: 0x%x", result);
586 return ERROR_FAIL;
587 }
588
589 return ERROR_OK;
590 }
591
592 static int icdi_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
593 uint32_t count, uint8_t *buffer)
594 {
595 if (size == 4)
596 count *= size;
597 return icdi_usb_read_mem_int(handle, addr, count, buffer);
598 }
599
600 static int icdi_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
601 uint32_t count, const uint8_t *buffer)
602 {
603 if (size == 4)
604 count *= size;
605 return icdi_usb_write_mem_int(handle, addr, count, buffer);
606 }
607
608 static int icdi_usb_close(void *handle)
609 {
610 struct icdi_usb_handle_s *h;
611
612 h = (struct icdi_usb_handle_s *)handle;
613
614 if (h->usb_dev)
615 libusb_close(h->usb_dev);
616
617 if (h->usb_ctx)
618 libusb_exit(h->usb_ctx);
619
620 if (h->read_buffer)
621 free(h->read_buffer);
622
623 if (h->write_buffer)
624 free(h->write_buffer);
625
626 free(handle);
627
628 return ERROR_OK;
629 }
630
631 static int icdi_usb_open(struct hl_interface_param_s *param, void **fd)
632 {
633 int retval;
634 struct icdi_usb_handle_s *h;
635
636 LOG_DEBUG("icdi_usb_open");
637
638 h = calloc(1, sizeof(struct icdi_usb_handle_s));
639
640 if (h == 0) {
641 LOG_ERROR("unable to allocate memory");
642 return ERROR_FAIL;
643 }
644
645 LOG_DEBUG("transport: %d vid: 0x%04x pid: 0x%04x", param->transport,
646 param->vid, param->pid);
647
648 if (libusb_init(&h->usb_ctx) != 0) {
649 LOG_ERROR("libusb init failed");
650 goto error_open;
651 }
652
653 h->usb_dev = libusb_open_device_with_vid_pid(h->usb_ctx, param->vid, param->pid);
654 if (!h->usb_dev) {
655 LOG_ERROR("open failed");
656 goto error_open;
657 }
658
659 if (libusb_claim_interface(h->usb_dev, 2)) {
660 LOG_DEBUG("claim interface failed");
661 goto error_open;
662 }
663
664 /* check if mode is supported */
665 retval = ERROR_OK;
666
667 switch (param->transport) {
668 #if 0
669 /* TODO place holder as swd is not currently supported */
670 case HL_TRANSPORT_SWD:
671 #endif
672 case HL_TRANSPORT_JTAG:
673 break;
674 default:
675 retval = ERROR_FAIL;
676 break;
677 }
678
679 if (retval != ERROR_OK) {
680 LOG_ERROR("mode (transport) not supported by device");
681 goto error_open;
682 }
683
684 /* allocate buffer */
685 h->read_buffer = malloc(ICDI_PACKET_SIZE);
686 h->write_buffer = malloc(ICDI_PACKET_SIZE);
687 h->max_packet = ICDI_PACKET_SIZE;
688
689 if (h->read_buffer == 0 || h->write_buffer == 0) {
690 LOG_DEBUG("malloc failed");
691 goto error_open;
692 }
693
694 /* query icdi version etc */
695 retval = icdi_usb_version(h);
696 if (retval != ERROR_OK)
697 goto error_open;
698
699 /* query icdi support */
700 retval = icdi_usb_query(h);
701 if (retval != ERROR_OK)
702 goto error_open;
703
704 *fd = h;
705
706 /* set the max target read/write buffer in bytes
707 * as we are using gdb binary packets to transfer memory we have to
708 * reserve half the buffer for any possible escape chars plus
709 * at least 64 bytes for the gdb packet header */
710 param->max_buffer = (((h->max_packet - 64) / 4) * 4) / 2;
711
712 return ERROR_OK;
713
714 error_open:
715 icdi_usb_close(h);
716
717 return ERROR_FAIL;
718 }
719
720 struct hl_layout_api_s icdi_usb_layout_api = {
721 .open = icdi_usb_open,
722 .close = icdi_usb_close,
723 .idcode = icdi_usb_idcode,
724 .state = icdi_usb_state,
725 .reset = icdi_usb_reset,
726 .assert_srst = icdi_usb_assert_srst,
727 .run = icdi_usb_run,
728 .halt = icdi_usb_halt,
729 .step = icdi_usb_step,
730 .read_regs = icdi_usb_read_regs,
731 .read_reg = icdi_usb_read_reg,
732 .write_reg = icdi_usb_write_reg,
733 .read_mem = icdi_usb_read_mem,
734 .write_mem = icdi_usb_write_mem,
735 .write_debug_reg = icdi_usb_write_debug_reg
736 };

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)