Set empty usage field for commands that do not need parameters
[openocd.git] / src / flash / nor / tcl.c
index ab3b1eaf5e9c262aa23ca317dbb884dae225a73a..87d599a59cae593d9315cc92181d9edadba1e472 100644 (file)
@@ -3,6 +3,7 @@
  *   Copyright (C) 2007,2008 Ã˜yvind Harboe <oyvind.harboe@zylin.com>       *
  *   Copyright (C) 2008 by Spencer Oliver <spen@spen-soft.co.uk>           *
  *   Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net>             *
+ *   Copyright (C) 2017-2018 Tomas Vanek <vanekt@fbl.cz>                   *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   it under the terms of the GNU General Public License as published by  *
@@ -97,13 +98,21 @@ COMMAND_HANDLER(handle_flash_info_command)
                if (retval != ERROR_OK)
                        return retval;
 
-               /* We must query the hardware to avoid printing stale information! */
-               retval = p->driver->protect_check(p);
-               if (retval != ERROR_OK)
-                       return retval;
+               /* If the driver does not implement protection, we show the default
+                * state of is_protected array - usually protection state unknown */
+               if (p->driver->protect_check == NULL) {
+                       retval = ERROR_FLASH_OPER_UNSUPPORTED;
+               } else {
+                       /* We must query the hardware to avoid printing stale information! */
+                       retval = p->driver->protect_check(p);
+                       if (retval != ERROR_OK && retval != ERROR_FLASH_OPER_UNSUPPORTED)
+                               return retval;
+               }
+               if (retval == ERROR_FLASH_OPER_UNSUPPORTED)
+                       LOG_WARNING("Flash protection check is not implemented.");
 
                command_print(CMD_CTX,
-                       "#%d : %s at 0x%8.8" PRIx32 ", size 0x%8.8" PRIx32
+                       "#%d : %s at " TARGET_ADDR_FMT ", size 0x%8.8" PRIx32
                        ", buswidth %i, chipwidth %i",
                        p->bank_number,
                        p->driver->name,
@@ -168,7 +177,7 @@ COMMAND_HANDLER(handle_flash_probe_command)
                retval = p->driver->probe(p);
                if (retval == ERROR_OK)
                        command_print(CMD_CTX,
-                               "flash '%s' found at 0x%8.8" PRIx32,
+                               "flash '%s' found at " TARGET_ADDR_FMT,
                                p->driver->name,
                                p->base);
        } else {
@@ -196,7 +205,8 @@ COMMAND_HANDLER(handle_flash_erase_check_command)
                command_print(CMD_CTX, "successfully checked erase state");
        else {
                command_print(CMD_CTX,
-                       "unknown error when checking erase state of flash bank #%s at 0x%8.8" PRIx32,
+                       "unknown error when checking erase state of flash bank #%s at "
+                       TARGET_ADDR_FMT,
                        CMD_ARGV[0],
                        p->base);
        }
@@ -230,7 +240,7 @@ COMMAND_HANDLER(handle_flash_erase_address_command)
 {
        struct flash_bank *p;
        int retval = ERROR_OK;
-       uint32_t address;
+       target_addr_t address;
        uint32_t length;
        bool do_pad = false;
        bool do_unlock = false;
@@ -253,7 +263,7 @@ COMMAND_HANDLER(handle_flash_erase_address_command)
        if (CMD_ARGC != 2)
                return ERROR_COMMAND_SYNTAX_ERROR;
 
-       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
+       COMMAND_PARSE_ADDRESS(CMD_ARGV[0], address);
        COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], length);
 
        if (length <= 0) {
@@ -279,7 +289,8 @@ COMMAND_HANDLER(handle_flash_erase_address_command)
                retval = flash_erase_address_range(target, do_pad, address, length);
 
        if ((ERROR_OK == retval) && (duration_measure(&bench) == ERROR_OK)) {
-               command_print(CMD_CTX, "erased address 0x%8.8" PRIx32 " (length %" PRIi32 ")"
+               command_print(CMD_CTX, "erased address " TARGET_ADDR_FMT " (length %"
+                               PRIi32 ")"
                        " in %fs (%0.3f KiB/s)", address, length,
                        duration_elapsed(&bench), duration_kbps(&bench, length));
        }
@@ -287,24 +298,6 @@ COMMAND_HANDLER(handle_flash_erase_address_command)
        return retval;
 }
 
-static int flash_check_sector_parameters(struct command_context *cmd_ctx,
-       uint32_t first, uint32_t last, uint32_t num_sectors)
-{
-       if (!(first <= last)) {
-               command_print(cmd_ctx, "ERROR: "
-                       "first sector must be <= last sector");
-               return ERROR_FAIL;
-       }
-
-       if (!(last <= (num_sectors - 1))) {
-               command_print(cmd_ctx, "ERROR: last sector must be <= %" PRIu32,
-                       num_sectors - 1);
-               return ERROR_FAIL;
-       }
-
-       return ERROR_OK;
-}
-
 COMMAND_HANDLER(handle_flash_erase_command)
 {
        if (CMD_ARGC != 3)
@@ -326,9 +319,18 @@ COMMAND_HANDLER(handle_flash_erase_command)
        else
                COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], last);
 
-       retval = flash_check_sector_parameters(CMD_CTX, first, last, p->num_sectors);
-       if (retval != ERROR_OK)
-               return retval;
+       if (!(first <= last)) {
+               command_print(CMD_CTX, "ERROR: "
+                       "first sector must be <= last");
+               return ERROR_FAIL;
+       }
+
+       if (!(last <= (uint32_t)(p->num_sectors - 1))) {
+               command_print(CMD_CTX, "ERROR: "
+                       "last sector must be <= %" PRIu32,
+                       p->num_sectors - 1);
+               return ERROR_FAIL;
+       }
 
        struct duration bench;
        duration_start(&bench);
@@ -374,15 +376,28 @@ COMMAND_HANDLER(handle_flash_protect_command)
        bool set;
        COMMAND_PARSE_ON_OFF(CMD_ARGV[3], set);
 
-       retval = flash_check_sector_parameters(CMD_CTX, first, last, num_blocks);
-       if (retval != ERROR_OK)
-               return retval;
+       if (!(first <= last)) {
+               command_print(CMD_CTX, "ERROR: "
+                       "first %s must be <= last",
+                       (p->num_prot_blocks) ? "block" : "sector");
+               return ERROR_FAIL;
+       }
+
+       if (!(last <= (uint32_t)(num_blocks - 1))) {
+               command_print(CMD_CTX, "ERROR: "
+                       "last %s must be <= %" PRIu32,
+                       (p->num_prot_blocks) ? "block" : "sector",
+                       num_blocks - 1);
+               return ERROR_FAIL;
+       }
 
        retval = flash_driver_protect(p, set, first, last);
        if (retval == ERROR_OK) {
-               command_print(CMD_CTX, "%s protection for sectors %" PRIu32
+               command_print(CMD_CTX, "%s protection for %s %" PRIu32
                        " through %" PRIu32 " on flash bank %d",
-                       (set) ? "set" : "cleared", first, last, p->bank_number);
+                       (set) ? "set" : "cleared",
+                       (p->num_prot_blocks) ? "blocks" : "sectors",
+                       first, last, p->bank_number);
        }
 
        return retval;
@@ -460,42 +475,25 @@ COMMAND_HANDLER(handle_flash_write_image_command)
 
 COMMAND_HANDLER(handle_flash_fill_command)
 {
-       int err = ERROR_OK;
-       uint32_t address;
+       target_addr_t address;
        uint32_t pattern;
        uint32_t count;
-       uint32_t wrote = 0;
-       uint32_t cur_size = 0;
-       uint32_t chunk_count;
        struct target *target = get_current_target(CMD_CTX);
        unsigned i;
        uint32_t wordsize;
-       int retval = ERROR_OK;
-
-       static size_t const chunksize = 1024;
-       uint8_t *chunk = NULL, *readback = NULL;
+       int retval;
 
-       if (CMD_ARGC != 3) {
-               retval = ERROR_COMMAND_SYNTAX_ERROR;
-               goto done;
-       }
+       if (CMD_ARGC != 3)
+               return ERROR_COMMAND_SYNTAX_ERROR;
 
-       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
+       COMMAND_PARSE_ADDRESS(CMD_ARGV[0], address);
        COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], pattern);
        COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], count);
 
-       chunk = malloc(chunksize);
-       if (chunk == NULL)
-               return ERROR_FAIL;
-
-       readback = malloc(chunksize);
-       if (readback == NULL) {
-               free(chunk);
-               return ERROR_FAIL;
-       }
-
-       if (count == 0)
-               goto done;
+       struct flash_bank *bank;
+       retval = get_flash_bank_by_addr(target, address, true, &bank);
+       if (retval != ERROR_OK)
+               return retval;
 
        switch (CMD_NAME[4]) {
                case 'w':
@@ -508,73 +506,109 @@ COMMAND_HANDLER(handle_flash_fill_command)
                        wordsize = 1;
                        break;
                default:
-                       retval = ERROR_COMMAND_SYNTAX_ERROR;
-                       goto done;
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       if (count == 0)
+               return ERROR_OK;
+
+       if (address + count * wordsize > bank->base + bank->size) {
+               LOG_ERROR("Cannot cross flash bank borders");
+               return ERROR_FAIL;
        }
 
-       chunk_count = MIN(count, (chunksize / wordsize));
+       uint32_t size_bytes = count * wordsize;
+       target_addr_t aligned_start = flash_write_align_start(bank, address);
+       target_addr_t end_addr = address + size_bytes - 1;
+       target_addr_t aligned_end = flash_write_align_end(bank, end_addr);
+       uint32_t aligned_size = aligned_end + 1 - aligned_start;
+       uint32_t padding_at_start = address - aligned_start;
+       uint32_t padding_at_end = aligned_end - end_addr;
+
+       uint8_t *buffer = malloc(aligned_size);
+       if (buffer == NULL)
+               return ERROR_FAIL;
+
+       if (padding_at_start) {
+               memset(buffer, bank->default_padded_value, padding_at_start);
+               LOG_WARNING("Start address " TARGET_ADDR_FMT
+                       " breaks the required alignment of flash bank %s",
+                       address, bank->name);
+               LOG_WARNING("Padding %" PRId32 " bytes from " TARGET_ADDR_FMT,
+                   padding_at_start, aligned_start);
+       }
+
+       uint8_t *ptr = buffer + padding_at_start;
+
        switch (wordsize) {
                case 4:
-                       for (i = 0; i < chunk_count; i++)
-                               target_buffer_set_u32(target, chunk + i * wordsize, pattern);
+                       for (i = 0; i < count; i++, ptr += wordsize)
+                               target_buffer_set_u32(target, ptr, pattern);
                        break;
                case 2:
-                       for (i = 0; i < chunk_count; i++)
-                               target_buffer_set_u16(target, chunk + i * wordsize, pattern);
+                       for (i = 0; i < count; i++, ptr += wordsize)
+                               target_buffer_set_u16(target, ptr, pattern);
                        break;
                case 1:
-                       memset(chunk, pattern, chunk_count);
+                       memset(ptr, pattern, count);
+                       ptr += count;
                        break;
                default:
                        LOG_ERROR("BUG: can't happen");
                        exit(-1);
        }
 
+       if (padding_at_end) {
+               memset(ptr, bank->default_padded_value, padding_at_end);
+               LOG_INFO("Padding at " TARGET_ADDR_FMT " with %" PRId32
+                       " bytes (bank write end alignment)",
+                       end_addr + 1, padding_at_end);
+       }
+
        struct duration bench;
        duration_start(&bench);
 
-       for (wrote = 0; wrote < (count*wordsize); wrote += cur_size) {
-               struct flash_bank *bank;
+       retval = flash_driver_write(bank, buffer, aligned_start - bank->base, aligned_size);
+       if (retval != ERROR_OK)
+               goto done;
 
-               retval = get_flash_bank_by_addr(target, address, true, &bank);
-               if (retval != ERROR_OK)
-                       goto done;
+       retval = flash_driver_read(bank, buffer, address - bank->base, size_bytes);
+       if (retval != ERROR_OK)
+               goto done;
 
-               cur_size = MIN((count * wordsize - wrote), chunksize);
-               err = flash_driver_write(bank, chunk, address - bank->base + wrote, cur_size);
-               if (err != ERROR_OK) {
-                       retval = err;
-                       goto done;
-               }
+       for (i = 0, ptr = buffer; i < count; i++) {
+               uint32_t readback = 0;
 
-               err = flash_driver_read(bank, readback, address - bank->base + wrote, cur_size);
-               if (err != ERROR_OK) {
-                       retval = err;
-                       goto done;
+               switch (wordsize) {
+                       case 4:
+                               readback = target_buffer_get_u32(target, ptr);
+                               break;
+                       case 2:
+                               readback = target_buffer_get_u16(target, ptr);
+                               break;
+                       case 1:
+                               readback = *ptr;
+                               break;
                }
-
-               for (i = 0; i < cur_size; i++) {
-                       if (readback[i] != chunk[i]) {
-                               LOG_ERROR(
-                                       "Verification error address 0x%08" PRIx32 ", read back 0x%02x, expected 0x%02x",
-                                       address + wrote + i,
-                                       readback[i],
-                                       chunk[i]);
-                               retval = ERROR_FAIL;
-                               goto done;
-                       }
+               if (readback != pattern) {
+                       LOG_ERROR(
+                               "Verification error address " TARGET_ADDR_FMT
+                               ", read back 0x%02" PRIx32 ", expected 0x%02" PRIx32,
+                               address + i * wordsize, readback, pattern);
+                       retval = ERROR_FAIL;
+                       goto done;
                }
+               ptr += wordsize;
        }
 
        if ((retval == ERROR_OK) && (duration_measure(&bench) == ERROR_OK)) {
-               command_print(CMD_CTX, "wrote %" PRIu32 " bytes to 0x%8.8" PRIx32
-                       " in %fs (%0.3f KiB/s)", wrote, address,
-                       duration_elapsed(&bench), duration_kbps(&bench, wrote));
+               command_print(CMD_CTX, "wrote %" PRIu32 " bytes to " TARGET_ADDR_FMT
+                       " in %fs (%0.3f KiB/s)", size_bytes, address,
+                       duration_elapsed(&bench), duration_kbps(&bench, size_bytes));
        }
 
 done:
-       free(readback);
-       free(chunk);
+       free(buffer);
 
        return retval;
 }
@@ -592,8 +626,8 @@ COMMAND_HANDLER(handle_flash_write_bank_command)
        struct duration bench;
        duration_start(&bench);
 
-       struct flash_bank *p;
-       int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &p);
+       struct flash_bank *bank;
+       int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
        if (ERROR_OK != retval)
                return retval;
 
@@ -602,7 +636,7 @@ COMMAND_HANDLER(handle_flash_write_bank_command)
        if (CMD_ARGC > 2)
                COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], offset);
 
-       if (offset > p->size) {
+       if (offset > bank->size) {
                LOG_ERROR("Offset 0x%8.8" PRIx32 " is out of range of the flash bank",
                        offset);
                return ERROR_COMMAND_ARGUMENT_INVALID;
@@ -618,7 +652,7 @@ COMMAND_HANDLER(handle_flash_write_bank_command)
                return retval;
        }
 
-       length = MIN(filesize, p->size - offset);
+       length = MIN(filesize, bank->size - offset);
 
        if (!length) {
                LOG_INFO("Nothing to write to flash bank");
@@ -630,14 +664,33 @@ COMMAND_HANDLER(handle_flash_write_bank_command)
                LOG_INFO("File content exceeds flash bank size. Only writing the "
                        "first %zu bytes of the file", length);
 
-       buffer = malloc(length);
+       target_addr_t start_addr = bank->base + offset;
+       target_addr_t aligned_start = flash_write_align_start(bank, start_addr);
+       target_addr_t end_addr = start_addr + length - 1;
+       target_addr_t aligned_end = flash_write_align_end(bank, end_addr);
+       uint32_t aligned_size = aligned_end + 1 - aligned_start;
+       uint32_t padding_at_start = start_addr - aligned_start;
+       uint32_t padding_at_end = aligned_end - end_addr;
+
+       buffer = malloc(aligned_size);
        if (buffer == NULL) {
                fileio_close(fileio);
                LOG_ERROR("Out of memory");
                return ERROR_FAIL;
        }
+
+       if (padding_at_start) {
+               memset(buffer, bank->default_padded_value, padding_at_start);
+               LOG_WARNING("Start offset 0x%08" PRIx32
+                       " breaks the required alignment of flash bank %s",
+                       offset, bank->name);
+               LOG_WARNING("Padding %" PRId32 " bytes from " TARGET_ADDR_FMT,
+                   padding_at_start, aligned_start);
+       }
+
+       uint8_t *ptr = buffer + padding_at_start;
        size_t buf_cnt;
-       if (fileio_read(fileio, length, buffer, &buf_cnt) != ERROR_OK) {
+       if (fileio_read(fileio, length, ptr, &buf_cnt) != ERROR_OK) {
                free(buffer);
                fileio_close(fileio);
                return ERROR_FAIL;
@@ -649,15 +702,23 @@ COMMAND_HANDLER(handle_flash_write_bank_command)
                return ERROR_FAIL;
        }
 
-       retval = flash_driver_write(p, buffer, offset, length);
+       ptr += length;
+
+       if (padding_at_end) {
+               memset(ptr, bank->default_padded_value, padding_at_end);
+               LOG_INFO("Padding at " TARGET_ADDR_FMT " with %" PRId32
+                       " bytes (bank write end alignment)",
+                       end_addr + 1, padding_at_end);
+       }
+
+       retval = flash_driver_write(bank, buffer, aligned_start - bank->base, aligned_size);
 
        free(buffer);
-       buffer = NULL;
 
        if ((ERROR_OK == retval) && (duration_measure(&bench) == ERROR_OK)) {
                command_print(CMD_CTX, "wrote %zu bytes from file %s to flash bank %u"
                        " at offset 0x%8.8" PRIx32 " in %fs (%0.3f KiB/s)",
-                       length, CMD_ARGV[1], p->bank_number, offset,
+                       length, CMD_ARGV[1], bank->bank_number, offset,
                        duration_elapsed(&bench), duration_kbps(&bench, length));
        }
 
@@ -674,7 +735,7 @@ COMMAND_HANDLER(handle_flash_read_bank_command)
        uint32_t length;
        size_t written;
 
-       if (CMD_ARGC != 4)
+       if (CMD_ARGC < 2 || CMD_ARGC > 4)
                return ERROR_COMMAND_SYNTAX_ERROR;
 
        struct duration bench;
@@ -682,11 +743,31 @@ COMMAND_HANDLER(handle_flash_read_bank_command)
 
        struct flash_bank *p;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &p);
+
        if (ERROR_OK != retval)
                return retval;
 
-       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], offset);
-       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], length);
+       offset = 0;
+
+       if (CMD_ARGC > 2)
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], offset);
+
+       if (offset > p->size) {
+               LOG_ERROR("Offset 0x%8.8" PRIx32 " is out of range of the flash bank",
+                       offset);
+               return ERROR_COMMAND_ARGUMENT_INVALID;
+       }
+
+       length = p->size - offset;
+
+       if (CMD_ARGC > 3)
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], length);
+
+       if (offset + length > p->size) {
+               LOG_ERROR("Length of %" PRIu32 " bytes with offset 0x%8.8" PRIx32
+                       " is out of range of the flash bank", length, offset);
+               return ERROR_COMMAND_ARGUMENT_INVALID;
+       }
 
        buffer = malloc(length);
        if (buffer == NULL) {
@@ -966,10 +1047,9 @@ static const struct command_registration flash_exec_command_handlers[] = {
                .name = "read_bank",
                .handler = handle_flash_read_bank_command,
                .mode = COMMAND_EXEC,
-               .usage = "bank_id filename offset length",
-               .help = "Read binary data from flash bank to file, "
-                       "starting at specified byte offset from the "
-                       "beginning of the bank.",
+               .usage = "bank_id filename [offset [length]]",
+               .help = "Read binary data from flash bank to file. Allow optional "
+                       "offset from beginning of the bank (defaults to zero).",
        },
        {
                .name = "verify_bank",
@@ -1027,7 +1107,7 @@ COMMAND_HANDLER(handle_flash_bank_command)
        }
 
        const char *driver_name = CMD_ARGV[0];
-       struct flash_driver *driver = flash_driver_find_by_name(driver_name);
+       const struct flash_driver *driver = flash_driver_find_by_name(driver_name);
        if (NULL == driver) {
                /* no matching flash driver found */
                LOG_ERROR("flash driver '%s' not found", driver_name);
@@ -1052,27 +1132,22 @@ COMMAND_HANDLER(handle_flash_bank_command)
                }
        }
 
-       struct flash_bank *c = malloc(sizeof(*c));
+       struct flash_bank *c = calloc(1, sizeof(*c));
        c->name = strdup(bank_name);
        c->target = target;
        c->driver = driver;
-       c->driver_priv = NULL;
-       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], c->base);
+       COMMAND_PARSE_NUMBER(target_addr, CMD_ARGV[1], c->base);
        COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], c->size);
        COMMAND_PARSE_NUMBER(int, CMD_ARGV[3], c->chip_width);
        COMMAND_PARSE_NUMBER(int, CMD_ARGV[4], c->bus_width);
        c->default_padded_value = c->erased_value = 0xff;
-       c->num_sectors = 0;
-       c->sectors = NULL;
-       c->num_prot_blocks = 0;
-       c->prot_blocks = NULL;
-       c->next = NULL;
+       c->minimal_write_gap = FLASH_WRITE_GAP_SECTOR;
 
        int retval;
        retval = CALL_COMMAND_HANDLER(driver->flash_bank_command, c);
        if (ERROR_OK != retval) {
-               LOG_ERROR("'%s' driver rejected flash bank at 0x%8.8" PRIx32 "; usage: %s",
-                       driver_name, c->base, driver->usage);
+               LOG_ERROR("'%s' driver rejected flash bank at " TARGET_ADDR_FMT
+                               "; usage: %s", driver_name, c->base, driver->usage);
                free(c);
                return retval;
        }
@@ -1092,7 +1167,7 @@ COMMAND_HANDLER(handle_flash_banks_command)
 
        unsigned n = 0;
        for (struct flash_bank *p = flash_bank_list(); p; p = p->next, n++) {
-               LOG_USER("#%d : %s (%s) at 0x%8.8" PRIx32 ", size 0x%8.8" PRIx32 ", "
+               LOG_USER("#%d : %s (%s) at " TARGET_ADDR_FMT ", size 0x%8.8" PRIx32 ", "
                        "buswidth %u, chipwidth %u", p->bank_number,
                        p->name, p->driver->name, p->base, p->size,
                        p->bus_width, p->chip_width);
@@ -1164,12 +1239,14 @@ static const struct command_registration flash_config_command_handlers[] = {
                .mode = COMMAND_CONFIG,
                .handler = handle_flash_init_command,
                .help = "Initialize flash devices.",
+               .usage = "",
        },
        {
                .name = "banks",
                .mode = COMMAND_ANY,
                .handler = handle_flash_banks_command,
                .help = "Display table with information about flash banks.",
+               .usage = "",
        },
        {
                .name = "list",
@@ -1185,6 +1262,7 @@ static const struct command_registration flash_command_handlers[] = {
                .mode = COMMAND_ANY,
                .help = "NOR flash command group",
                .chain = flash_config_command_handlers,
+               .usage = "",
        },
        COMMAND_REGISTRATION_DONE
 };

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)