ftd2xx: fix build warnings
[openocd.git] / src / jtag / drivers / usb_blaster.c
1 /***************************************************************************
2 * Driver for USB-JTAG, Altera USB-Blaster and compatibles *
3 * Original code from Kolja Waschk's USB-JTAG project *
4 * (http://www.ixo.de/info/usb_jtag/). *
5 * Some updates by Anthony Liu (2006). *
6 * Minor updates and cleanup by Catalin Patulea (2009). *
7 * Speed updates by Ali Lown (2011). *
8 * *
9 * Copyright (C) 2011 Ali Lown *
10 * ali@lown.me.uk *
11 * *
12 * Copyright (C) 2009 Catalin Patulea *
13 * cat@vv.carleton.ca *
14 * *
15 * Copyright (C) 2006 Kolja Waschk *
16 * usbjtag@ixo.de *
17 * *
18 * Based on ft2232.c and bitbang.c, *
19 * Copyright (C) 2004,2006 by Dominic Rath *
20 * *
21 * This program is free software; you can redistribute it and/or modify *
22 * it under the terms of the GNU General Public License as published by *
23 * the Free Software Foundation; either version 2 of the License, or *
24 * (at your option) any later version. *
25 * *
26 * This program is distributed in the hope that it will be useful, *
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
29 * GNU General Public License for more details. *
30 * *
31 * You should have received a copy of the GNU General Public License *
32 * along with this program; if not, write to the *
33 * Free Software Foundation, Inc., *
34 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
35 ***************************************************************************/
36
37 /*
38 * The following information is originally from Kolja Waschk's USB-JTAG,
39 * where it was obtained by reverse engineering an Altera USB-Blaster.
40 * See http://www.ixo.de/info/usb_jtag/ for USB-Blaster block diagram and
41 * usb_jtag-20080705-1200.zip#usb_jtag/host/openocd for protocol.
42 *
43 * The same information is also on the UrJTAG mediawiki, with some additional
44 * notes on bits marked as "unknown" by usb_jtag.
45 * (http://sourceforge.net/apps/mediawiki/urjtag/index.php?
46 * title=Cable_Altera_USB-Blaster)
47 *
48 * USB-JTAG, Altera USB-Blaster and compatibles are typically implemented as
49 * an FTDIChip FT245 followed by a CPLD which handles a two-mode protocol:
50 *
51 * _________
52 * | |
53 * | AT93C46 |
54 * |_________|
55 * __|__________ _________
56 * | | | |
57 * USB__| FTDI 245BM |__| EPM7064 |__JTAG (B_TDO,B_TDI,B_TMS,B_TCK)
58 * |_____________| |_________|
59 * __|__________ _|___________
60 * | | | |
61 * | 6 MHz XTAL | | 24 MHz Osc. |
62 * |_____________| |_____________|
63 *
64 * Protocol details are given in the code below.
65 *
66 * It is also possible to emulate this configuration using a single-chip USB
67 * controller like the Cypress FX2 (again, see usb_jtag for details).
68 */
69 #ifdef HAVE_CONFIG_H
70 #include "config.h"
71 #endif
72
73 #if IS_CYGWIN == 1
74 #include "windows.h"
75 #undef LOG_ERROR
76 #endif
77
78 /* project specific includes */
79 #include <jtag/interface.h>
80 #include <jtag/commands.h>
81 #include <helper/time_support.h>
82
83 /* system includes */
84 #include <string.h>
85 #include <stdlib.h>
86 #include <unistd.h>
87
88 #include "bitbang.h"
89
90 #if (BUILD_USB_BLASTER_FTD2XX == 1 && BUILD_USB_BLASTER_LIBFTDI == 1)
91 #error "BUILD_USB_BLASTER_FTD2XX && BUILD_USB_BLASTER_LIBFTDI "
92 "are mutually exclusive"
93 #elif (BUILD_USB_BLASTER_FTD2XX != 1 && BUILD_USB_BLASTER_LIBFTDI != 1)
94 #error "BUILD_USB_BLASTER_FTD2XX || BUILD_USB_BLASTER_LIBFTDI must be chosen"
95 #endif
96
97 /* USB_BLASTER access library includes */
98 #if BUILD_USB_BLASTER_FTD2XX == 1
99 #include <ftd2xx.h>
100 #include "ftd2xx_common.h"
101 #elif BUILD_USB_BLASTER_LIBFTDI == 1
102 #include <ftdi.h>
103 #endif
104
105 #include <sys/time.h>
106 #include <time.h>
107
108 static char *usb_blaster_device_desc;
109 static uint16_t usb_blaster_vid = 0x09fb; /* Altera */
110 static uint16_t usb_blaster_pid = 0x6001; /* USB-Blaster */
111
112 /* last output byte in simple bit banging (legacy) mode */
113 static uint8_t out_value;
114 /* global output buffer for bit banging */
115 #define BUF_LEN 64 //Size of EP1
116 static uint8_t out_buffer[BUF_LEN];
117 static uint16_t out_count = 0;
118
119 #if BUILD_USB_BLASTER_FTD2XX == 1
120 static FT_HANDLE ftdih;
121 #elif BUILD_USB_BLASTER_LIBFTDI == 1
122 static struct ftdi_context ftdic;
123 #endif
124
125 static int usb_blaster_buf_write(
126 uint8_t *buf, int size, uint32_t *bytes_written)
127 {
128 #if BUILD_USB_BLASTER_FTD2XX == 1
129 FT_STATUS status;
130 DWORD dw_bytes_written;
131
132 #ifdef _DEBUG_JTAG_IO_
133 LOG_DEBUG("usb_blaster_buf_write %02X (%d)", buf[0], size);
134 #endif
135 status = FT_Write(ftdih, buf, size, &dw_bytes_written);
136 if (status != FT_OK)
137 {
138 *bytes_written = dw_bytes_written;
139 LOG_ERROR("FT_Write returned: %s", ftd2xx_status_string(status));
140 return ERROR_JTAG_DEVICE_ERROR;
141 }
142 *bytes_written = dw_bytes_written;
143 return ERROR_OK;
144 #elif BUILD_USB_BLASTER_LIBFTDI == 1
145 int retval;
146 #ifdef _DEBUG_JTAG_IO_
147 LOG_DEBUG("usb_blaster_buf_write %02X (%d)", buf[0], size);
148 #endif
149 retval = ftdi_write_data(&ftdic, buf, size);
150 if (retval < 0)
151 {
152 *bytes_written = 0;
153 LOG_ERROR("ftdi_write_data: %s", ftdi_get_error_string(&ftdic));
154 return ERROR_JTAG_DEVICE_ERROR;
155 }
156 *bytes_written = retval;
157 return ERROR_OK;
158 #endif
159 }
160
161 static int
162 usb_blaster_buf_read(uint8_t *buf, unsigned size, uint32_t *bytes_read)
163 {
164 #if BUILD_USB_BLASTER_FTD2XX == 1
165 DWORD dw_bytes_read;
166 FT_STATUS status;
167
168 status = FT_Read(ftdih, buf, size, &dw_bytes_read);
169 if (status != FT_OK)
170 {
171 *bytes_read = dw_bytes_read;
172 LOG_ERROR("FT_Read returned: %s", ftd2xx_status_string(status));
173 return ERROR_JTAG_DEVICE_ERROR;
174 }
175 #ifdef _DEBUG_JTAG_IO_
176 LOG_DEBUG("usb_blaster_buf_read %02X (%" PRIu32 ")", buf[0], dw_bytes_read);
177 #endif
178 *bytes_read = dw_bytes_read;
179 return ERROR_OK;
180
181 #elif BUILD_USB_BLASTER_LIBFTDI == 1
182 int retval;
183 int timeout = 100;
184
185 *bytes_read = 0;
186 while ((*bytes_read < size) && timeout--)
187 {
188 retval = ftdi_read_data(&ftdic, buf + *bytes_read,
189 size - *bytes_read);
190 if (retval < 0)
191 {
192 *bytes_read = 0;
193 LOG_ERROR("ftdi_read_data: %s",
194 ftdi_get_error_string(&ftdic));
195 return ERROR_JTAG_DEVICE_ERROR;
196 }
197 *bytes_read += retval;
198 }
199 #ifdef _DEBUG_JTAG_IO_
200 LOG_DEBUG("usb_blaster_buf_read %02X (%d)", buf[0], *bytes_read);
201 #endif
202 return ERROR_OK;
203 #endif
204 }
205
206 /* The following code doesn't fully utilize the possibilities of the
207 * USB-Blaster. It only buffers data up to the maximum packet size of 64 bytes.
208 *
209 * Actually, the USB-Blaster offers a byte-shift mode to transmit up to 504 data
210 * bits (bidirectional) in a single USB packet. A header byte has to be sent as
211 * the first byte in a packet with the following meaning:
212 *
213 * Bit 7 (0x80): Must be set to indicate byte-shift mode.
214 * Bit 6 (0x40): If set, the USB-Blaster will also read data, not just write.
215 * Bit 5..0: Define the number N of following bytes
216 *
217 * All N following bytes will then be clocked out serially on TDI. If Bit 6 was
218 * set, it will afterwards return N bytes with TDO data read while clocking out
219 * the TDI data. LSB of the first byte after the header byte will appear first
220 * on TDI.
221 */
222
223 /* Simple bit banging mode:
224 *
225 * Bit 7 (0x80): Must be zero (see byte-shift mode above)
226 * Bit 6 (0x40): If set, you will receive a byte indicating the state of TDO
227 * in return.
228 * Bit 5 (0x20): Output Enable/LED.
229 * Bit 4 (0x10): TDI Output.
230 * Bit 3 (0x08): nCS Output (not used in JTAG mode).
231 * Bit 2 (0x04): nCE Output (not used in JTAG mode).
232 * Bit 1 (0x02): TMS Output.
233 * Bit 0 (0x01): TCK Output.
234 *
235 * For transmitting a single data bit, you need to write two bytes. Up to 64
236 * bytes can be combined in a single USB packet.
237 * It isn't possible to read a data without transmitting data.
238 */
239
240 #define TCK (1 << 0)
241 #define TMS (1 << 1)
242 #define NCE (1 << 2)
243 #define NCS (1 << 3)
244 #define TDI (1 << 4)
245 #define LED (1 << 5)
246 #define READ (1 << 6)
247 #define SHMODE (1 << 7)
248 #define OTHERS ((1 << 2) | (1 << 3) | (1 << 5))
249
250 #define READ_TDO (1 << 0)
251
252 static void usb_blaster_write_databuffer(uint8_t* buf, uint16_t len)
253 {
254 uint32_t bytes_written;
255 usb_blaster_buf_write(buf, len, &bytes_written);
256 out_count = 0;
257 #ifdef _DEBUG_JTAG_IO_
258 LOG_DEBUG("---- WROTE %d",bytes_written);
259 #endif
260 }
261
262 static void usb_blaster_addtowritebuffer(uint8_t value, bool forcewrite)
263 {
264 out_buffer[out_count] = value;
265 out_count += 1;
266 if(out_count == BUF_LEN || forcewrite)
267 usb_blaster_write_databuffer(out_buffer, out_count);
268 }
269
270 static int usb_blaster_read_data(void)
271 {
272 int status;
273 uint8_t buf[1];
274 uint32_t bytes_read;
275
276 if(out_count > 0)
277 usb_blaster_write_databuffer(out_buffer, out_count);
278
279 out_value |= READ;
280 usb_blaster_addtowritebuffer(out_value, true);
281 out_value &= ~READ;
282
283 status = usb_blaster_buf_read(buf, 1, &bytes_read);
284 if (status < 0)
285 return 0;
286
287 return !!(buf[0] & READ_TDO);
288 }
289
290 static void usb_blaster_write(int tck, int tms, int tdi)
291 {
292 #ifdef _DEBUG_JTAG_IO_
293 LOG_DEBUG("---- usb_blaster_write(%d,%d,%d)", tck, tms, tdi);
294 #endif
295 out_value &= ~(TCK | TMS | TDI);
296 if (tck)
297 out_value |= TCK;
298 if (tms)
299 out_value |= TMS;
300 if (tdi)
301 out_value |= TDI;
302
303 usb_blaster_addtowritebuffer(out_value, false);
304 }
305
306 static int usb_blaster_speed(int speed)
307 {
308 #if BUILD_USB_BLASTER_FTD2XX == 1
309 LOG_DEBUG("TODO: usb_blaster_speed() isn't implemented for libftd2xx!");
310 #elif BUILD_USB_BLASTER_LIBFTDI == 1
311 LOG_DEBUG("TODO: usb_blaster_speed() isn't optimally implemented!");
312
313 /* TODO: libftdi's ftdi_set_baudrate chokes on high rates, use lowlevel
314 * usb function instead! And additionally allow user to throttle.
315 */
316 if (ftdi_set_baudrate(&ftdic, 3000000 / 4) < 0)
317 {
318 LOG_ERROR("Can't set baud rate to max: %s",
319 ftdi_get_error_string(&ftdic));
320 return ERROR_JTAG_DEVICE_ERROR;
321 };
322 #endif
323
324 return ERROR_OK;
325 }
326
327 static void usb_blaster_reset(int trst, int srst)
328 {
329 LOG_DEBUG("TODO: usb_blaster_reset(%d,%d) isn't implemented!",
330 trst, srst);
331 }
332
333 static void usb_blaster_blink(int state)
334 {
335 out_value = 0x00;
336 if(state)
337 out_value |= LED;
338
339 usb_blaster_addtowritebuffer(out_value, true);
340 }
341
342 static struct bitbang_interface usb_blaster_bitbang = {
343 .read = usb_blaster_read_data,
344 .write = usb_blaster_write,
345 .reset = usb_blaster_reset,
346 .blink = usb_blaster_blink,
347 };
348
349 static int usb_blaster_init(void)
350 {
351 uint8_t latency_timer;
352
353 #if BUILD_USB_BLASTER_FTD2XX == 1
354 FT_STATUS status;
355 #endif
356
357 #if BUILD_USB_BLASTER_FTD2XX == 1
358 LOG_DEBUG("'usb_blaster' interface using FTD2XX");
359 #elif BUILD_USB_BLASTER_LIBFTDI == 1
360 LOG_DEBUG("'usb_blaster' interface using libftdi");
361 #endif
362
363 #if BUILD_USB_BLASTER_FTD2XX == 1
364 /* Open by device description */
365 if (usb_blaster_device_desc == NULL)
366 {
367 LOG_WARNING("no usb_blaster device description specified, "
368 "using default 'USB-Blaster'");
369 usb_blaster_device_desc = "USB-Blaster";
370 }
371
372 #if IS_WIN32 == 0
373 /* Add non-standard Vid/Pid to the linux driver */
374 status = FT_SetVIDPID(usb_blaster_vid, usb_blaster_pid);
375 if (status != FT_OK)
376 {
377 LOG_WARNING("couldn't add %4.4x:%4.4x",
378 usb_blaster_vid, usb_blaster_pid);
379 }
380 #endif
381
382 status = FT_OpenEx(usb_blaster_device_desc, FT_OPEN_BY_DESCRIPTION,
383 &ftdih);
384 if (status != FT_OK)
385 {
386 DWORD num_devices;
387
388 LOG_ERROR("unable to open ftdi device: %s",
389 ftd2xx_status_string(status));
390 status = FT_ListDevices(&num_devices, NULL,
391 FT_LIST_NUMBER_ONLY);
392 if (status == FT_OK)
393 {
394 char **desc_array = malloc(sizeof(char *)
395 * (num_devices + 1));
396 unsigned int i;
397
398 for (i = 0; i < num_devices; i++)
399 desc_array[i] = malloc(64);
400 desc_array[num_devices] = NULL;
401
402 status = FT_ListDevices(desc_array, &num_devices,
403 FT_LIST_ALL | FT_OPEN_BY_DESCRIPTION);
404
405 if (status == FT_OK)
406 {
407 LOG_ERROR("ListDevices: %" PRIu32, (uint32_t)num_devices);
408 for (i = 0; i < num_devices; i++)
409 LOG_ERROR("%i: %s", i, desc_array[i]);
410 }
411
412 for (i = 0; i < num_devices; i++)
413 free(desc_array[i]);
414 free(desc_array);
415 }
416 else
417 {
418 printf("ListDevices: NONE\n");
419 }
420 return ERROR_JTAG_INIT_FAILED;
421 }
422
423 status = FT_SetLatencyTimer(ftdih, 2);
424 if (status != FT_OK)
425 {
426 LOG_ERROR("unable to set latency timer: %s",
427 ftd2xx_status_string(status));
428 return ERROR_JTAG_INIT_FAILED;
429 }
430
431 status = FT_GetLatencyTimer(ftdih, &latency_timer);
432 if (status != FT_OK)
433 {
434 LOG_ERROR("unable to get latency timer: %s",
435 ftd2xx_status_string(status));
436 return ERROR_JTAG_INIT_FAILED;
437 }
438 LOG_DEBUG("current latency timer: %i", latency_timer);
439
440 status = FT_SetBitMode(ftdih, 0x00, 0);
441 if (status != FT_OK)
442 {
443 LOG_ERROR("unable to disable bit i/o mode: %s",
444 ftd2xx_status_string(status));
445 return ERROR_JTAG_INIT_FAILED;
446 }
447 #elif BUILD_USB_BLASTER_LIBFTDI == 1
448 if (ftdi_init(&ftdic) < 0)
449 return ERROR_JTAG_INIT_FAILED;
450
451 /* context, vendor id, product id */
452 if (ftdi_usb_open(&ftdic, usb_blaster_vid, usb_blaster_pid) < 0)
453 {
454 LOG_ERROR("unable to open ftdi device: %s", ftdic.error_str);
455 return ERROR_JTAG_INIT_FAILED;
456 }
457
458 if (ftdi_usb_reset(&ftdic) < 0)
459 {
460 LOG_ERROR("unable to reset ftdi device");
461 return ERROR_JTAG_INIT_FAILED;
462 }
463
464 if (ftdi_set_latency_timer(&ftdic, 2) < 0)
465 {
466 LOG_ERROR("unable to set latency timer");
467 return ERROR_JTAG_INIT_FAILED;
468 }
469
470 if (ftdi_get_latency_timer(&ftdic, &latency_timer) < 0)
471 {
472 LOG_ERROR("unable to get latency timer");
473 return ERROR_JTAG_INIT_FAILED;
474 }
475 LOG_DEBUG("current latency timer: %u", latency_timer);
476
477 ftdi_disable_bitbang(&ftdic);
478 #endif
479
480 bitbang_interface = &usb_blaster_bitbang;
481
482 #if 0
483 #if BUILD_USB_BLASTER_FTD2XX == 1
484 if ((status = FT_Purge(ftdih, FT_PURGE_RX | FT_PURGE_TX)) != FT_OK)
485 {
486 LOG_ERROR("error purging ftd2xx device: %i", status);
487 return ERROR_JTAG_INIT_FAILED;
488 }
489 #elif BUILD_USB_BLASTER_LIBFTDI == 1
490 if (ftdi_usb_purge_buffers(&ftdic) < 0)
491 {
492 LOG_ERROR("ftdi_purge_buffers: %s", ftdic.error_str);
493 return ERROR_JTAG_INIT_FAILED;
494 }
495 #endif
496 #endif
497
498 return ERROR_OK;
499 }
500
501 static int usb_blaster_quit(void)
502 {
503 if(out_count > 0)
504 usb_blaster_write_databuffer(out_buffer, out_count);
505
506 #if BUILD_USB_BLASTER_FTD2XX == 1
507 FT_STATUS status;
508
509 status = FT_Close(ftdih);
510 #elif BUILD_USB_BLASTER_LIBFTDI == 1
511 ftdi_usb_close(&ftdic);
512 ftdi_deinit(&ftdic);
513 #endif
514
515 return ERROR_OK;
516 }
517
518 COMMAND_HANDLER(usb_blaster_handle_device_desc_command)
519 {
520 if (CMD_ARGC == 1)
521 usb_blaster_device_desc = strdup(CMD_ARGV[0]);
522 else
523 LOG_ERROR("require exactly one argument to "
524 "usb_blaster_device_desc <description>");
525
526 return ERROR_OK;
527 }
528
529 COMMAND_HANDLER(usb_blaster_handle_vid_pid_command)
530 {
531 if (CMD_ARGC > 2)
532 {
533 LOG_WARNING("ignoring extra IDs in usb_blaster_vid_pid "
534 "(maximum is 1 pair)");
535 CMD_ARGC = 2;
536 }
537 if (CMD_ARGC == 2)
538 {
539 COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], usb_blaster_vid);
540 COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], usb_blaster_pid);
541 }
542 else
543 LOG_WARNING("incomplete usb_blaster_vid_pid configuration");
544
545 return ERROR_OK;
546 }
547
548 COMMAND_HANDLER(usb_blaster_handle_pin_command)
549 {
550 if (CMD_ARGC == 2)
551 {
552 const char * const pin_name = CMD_ARGV[0];
553 uint8_t mask;
554 unsigned int state;
555
556 if (!strcmp(pin_name, "pin6"))
557 mask = NCE;
558 else if (!strcmp(pin_name, "pin8"))
559 mask = NCS;
560 else
561 {
562 LOG_ERROR("%s: pin name must be \"pin6\" or \"pin8\"",
563 CMD_NAME);
564 return ERROR_COMMAND_SYNTAX_ERROR;
565 }
566
567 COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], state);
568 if (state == 0)
569 {
570 out_value &= ~mask;
571 usb_blaster_addtowritebuffer(out_value, true);
572 }
573 else if (state == 1)
574 {
575 out_value |= mask;
576 usb_blaster_addtowritebuffer(out_value, true);
577 }
578 else
579 {
580 LOG_ERROR("%s: pin state must be 0 or 1", CMD_NAME);
581 return ERROR_COMMAND_SYNTAX_ERROR;
582 }
583
584 return ERROR_OK;
585 }
586 else
587 {
588 LOG_ERROR("%s takes exactly two arguments", CMD_NAME);
589 return ERROR_COMMAND_SYNTAX_ERROR;
590 }
591 }
592
593 static const struct command_registration usb_blaster_command_handlers[] = {
594 {
595 .name = "usb_blaster_device_desc",
596 .handler = usb_blaster_handle_device_desc_command,
597 .mode = COMMAND_CONFIG,
598 .help = "set the USB device description of the USB-Blaster",
599 .usage = "description-string",
600 },
601 {
602 .name = "usb_blaster_vid_pid",
603 .handler = usb_blaster_handle_vid_pid_command,
604 .mode = COMMAND_CONFIG,
605 .help = "the vendor ID and product ID of the USB-Blaster",
606 .usage = "vid pid",
607 },
608 {
609 .name = "usb_blaster",
610 .handler = usb_blaster_handle_pin_command,
611 .mode = COMMAND_ANY,
612 .help = "set pin state for the unused GPIO pins",
613 .usage = "(pin6|pin8) (0|1)",
614 },
615 COMMAND_REGISTRATION_DONE
616 };
617
618 struct jtag_interface usb_blaster_interface = {
619 .name = "usb_blaster",
620 .commands = usb_blaster_command_handlers,
621 .supported = DEBUG_CAP_TMS_SEQ,
622
623 .execute_queue = bitbang_execute_queue,
624
625 .speed = usb_blaster_speed,
626 .init = usb_blaster_init,
627 .quit = usb_blaster_quit,
628 };

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)