This is an automated email from Gerrit.

Saravanan Sekar ([email protected]) just uploaded a new patch set to 
Gerrit, which you can find at http://openocd.zylin.com/5635

-- gerrit

commit 20388b6d6288ea374a4b7725b488c6afc7fc548b
Author: Zale Yu <[email protected]>
Date:   Wed Apr 29 16:53:52 2020 +0200

    add support for Nu-Link (Nuvoton ICE) over usb
    
    add support for Nu-Link over usb and new part numbers for M480 SoC
    series.
    
    Original work is fetched from Nuvoton github.
    Made code cleanup, fixed merge conflicts, compile and runtime issues.
    
    Change-Id: I9738de4e26783ba462ea3e39ec32069fd5bb7d94
    Signed-off-by: Saravanan Sekar <[email protected]>

diff --git a/contrib/60-openocd.rules b/contrib/60-openocd.rules
index 9904127..6cf0cfc 100644
--- a/contrib/60-openocd.rules
+++ b/contrib/60-openocd.rules
@@ -52,6 +52,12 @@ ATTRS{idVendor}=="0403", ATTRS{idProduct}=="c141", 
MODE="660", GROUP="plugdev",
 # Amontec JTAGkey and JTAGkey-tiny
 ATTRS{idVendor}=="0403", ATTRS{idProduct}=="cff8", MODE="660", 
GROUP="plugdev", TAG+="uaccess"
 
+# Nuvoton NuLink
+ATTRS{idVendor}=="0416", ATTRS{idProduct}=="511b", MODE="660", 
GROUP="plugdev", TAG+="uaccess"
+ATTRS{idVendor}=="0416", ATTRS{idProduct}=="511c", MODE="660", 
GROUP="plugdev", TAG+="uaccess"
+ATTRS{idVendor}=="0416", ATTRS{idProduct}=="511d", MODE="660", 
GROUP="plugdev", TAG+="uaccess"
+ATTRS{idVendor}=="0416", ATTRS{idProduct}=="5200", MODE="660", 
GROUP="plugdev", TAG+="uaccess"
+
 # TI ICDI
 ATTRS{idVendor}=="0451", ATTRS{idProduct}=="c32a", MODE="660", 
GROUP="plugdev", TAG+="uaccess"
 
diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am
index ba758e7..d2e887d 100644
--- a/src/jtag/drivers/Makefile.am
+++ b/src/jtag/drivers/Makefile.am
@@ -132,6 +132,7 @@ endif
 if HLADAPTER
 DRIVERFILES += %D%/stlink_usb.c
 DRIVERFILES += %D%/ti_icdi_usb.c
+DRIVERFILES += %D%/nulink_usb.c
 endif
 if RSHIM
 DRIVERFILES += %D%/rshim.c
diff --git a/src/jtag/drivers/libusb_helper.c b/src/jtag/drivers/libusb_helper.c
index fbbfb41..6da88f5 100644
--- a/src/jtag/drivers/libusb_helper.c
+++ b/src/jtag/drivers/libusb_helper.c
@@ -234,6 +234,26 @@ int jtag_libusb_control_transfer(struct 
libusb_device_handle *dev, uint8_t reque
        return transferred;
 }
 
+int jtag_libusb_interrupt_write(jtag_libusb_device_handle *dev, int ep, char 
*bytes,
+                               int size, int timeout)
+{
+       int transferred = 0;
+
+       libusb_interrupt_transfer(dev, ep, (unsigned char *)bytes, size,
+                                 &transferred, timeout);
+       return transferred;
+}
+
+int jtag_libusb_interrupt_read(jtag_libusb_device_handle *dev, int ep, char 
*bytes,
+                              int size, int timeout)
+{
+       int transferred = 0;
+
+       libusb_interrupt_transfer(dev, ep, (unsigned char *)bytes, size,
+                                 &transferred, timeout);
+       return transferred;
+}
+
 int jtag_libusb_bulk_write(struct libusb_device_handle *dev, int ep, char 
*bytes,
                           int size, int timeout, int *transferred)
 {
diff --git a/src/jtag/drivers/libusb_helper.h b/src/jtag/drivers/libusb_helper.h
index 74bb23c..dbfb9c2 100644
--- a/src/jtag/drivers/libusb_helper.h
+++ b/src/jtag/drivers/libusb_helper.h
@@ -21,12 +21,40 @@
 #define OPENOCD_JTAG_DRIVERS_LIBUSB_HELPER_H
 
 #include <libusb.h>
+#define jtag_libusb_device                     libusb_device
+#define jtag_libusb_device_handle              libusb_device_handle
+#define jtag_libusb_device_descriptor          libusb_device_descriptor
+#define jtag_libusb_interface                  libusb_interface
+#define jtag_libusb_interface_descriptor       libusb_interface_descriptor
+#define jtag_libusb_endpoint_descriptor                
libusb_endpoint_descriptor
+#define jtag_libusb_config_descriptor          libusb_config_descriptor
+
+#define jtag_libusb_reset_device(dev)          libusb_reset_device(dev)
+#define jtag_libusb_get_device(devh)           libusb_get_device(devh)
 
 /* this callback should return a non NULL value only when the serial could not
  * be retrieved by the standard 'libusb_get_string_descriptor_ascii' */
 typedef char * (*adapter_get_alternate_serial_fn)(libusb_device_handle *device,
                struct libusb_device_descriptor *dev_desc);
 
+static inline int jtag_libusb_detach_kernel_driver(jtag_libusb_device_handle 
*devh,
+               int iface)
+{
+       return libusb_detach_kernel_driver(devh, iface);
+};
+
+static inline int jtag_libusb_claim_interface(jtag_libusb_device_handle *devh,
+               int iface)
+{
+       return libusb_claim_interface(devh, iface);
+};
+
+static inline int jtag_libusb_release_interface(jtag_libusb_device_handle 
*devh,
+               int iface)
+{
+       return libusb_release_interface(devh, iface);
+}
+
 int jtag_libusb_open(const uint16_t vids[], const uint16_t pids[],
                const char *serial,
                struct libusb_device_handle **out,
@@ -35,11 +63,15 @@ void jtag_libusb_close(struct libusb_device_handle *dev);
 int jtag_libusb_control_transfer(struct libusb_device_handle *dev,
                uint8_t requestType, uint8_t request, uint16_t wValue,
                uint16_t wIndex, char *bytes,   uint16_t size, unsigned int 
timeout);
-int jtag_libusb_bulk_write(struct libusb_device_handle *dev, int ep,
-               char *bytes, int size, int timeout, int *transferred);
-int jtag_libusb_bulk_read(struct libusb_device_handle *dev, int ep,
+int jtag_libusb_interrupt_write(struct jtag_libusb_device_handle *dev, int ep,
+       char *bytes, int size, int timeout);
+int jtag_libusb_interrupt_read(struct jtag_libusb_device_handle *dev, int ep,
+       char *bytes, int size, int timeout);
+int jtag_libusb_bulk_write(struct jtag_libusb_device_handle *dev, int ep,
+               char *bytes,    int size, int timeout, int *transferred);
+int jtag_libusb_bulk_read(struct jtag_libusb_device_handle *dev, int ep,
                char *bytes, int size, int timeout, int *transferred);
-int jtag_libusb_set_configuration(struct libusb_device_handle *devh,
+int jtag_libusb_set_configuration(jtag_libusb_device_handle *devh,
                int configuration);
 /**
  * Find the first interface optionally matching class, subclass and
diff --git a/src/jtag/drivers/nulink_usb.c b/src/jtag/drivers/nulink_usb.c
new file mode 100644
index 0000000..4681ecd
--- /dev/null
+++ b/src/jtag/drivers/nulink_usb.c
@@ -0,0 +1,1173 @@
+/***************************************************************************
+ *   Copyright (C) 2016-2017 by Nuvoton                                    *
+ *   Zale Yu <[email protected]>                                            *
+ *                                                                         *
+ *   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  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* project specific includes */
+#include <helper/binarybuffer.h>
+#include <jtag/interface.h>
+#include <jtag/hla/hla_layout.h>
+#include <jtag/hla/hla_transport.h>
+#include <jtag/hla/hla_interface.h>
+#include <target/target.h>
+
+#include <target/cortex_m.h>
+
+#include "libusb_helper.h"
+
+#define ENDPOINT_IN  0x80
+#define ENDPOINT_OUT 0x00
+
+#define NULINK_WRITE_TIMEOUT 1000
+#define NULINK_READ_TIMEOUT  1000
+
+#define NULINK_RX_EP          (1|ENDPOINT_IN)
+#define NULINK_TX_EP          (2|ENDPOINT_OUT)
+
+#define NULINK_HID_MAX_SIZE   (64)
+#define V6M_MAX_COMMAND_LENGTH (NULINK_HID_MAX_SIZE - 2)
+#define V7M_MAX_COMMAND_LENGTH (NULINK_HID_MAX_SIZE - 2)
+
+struct nulink_usb_handle_s {
+       struct jtag_libusb_device_handle *fd;
+       struct libusb_transfer *trans;
+       uint8_t rx_ep;
+       uint8_t tx_ep;
+       uint32_t usbcmdidx;
+       uint8_t cmdidx;
+       uint8_t cmdbuf[NULINK_HID_MAX_SIZE];
+       uint8_t tempbuf[NULINK_HID_MAX_SIZE];
+       uint8_t databuf[NULINK_HID_MAX_SIZE];
+       uint32_t max_mem_packet;
+       enum hl_transports transport;
+       uint16_t hardwareConfig; /* bit 0: 1:Nu-Link-Pro, 0:Nu-Link */
+};
+
+/* ICE Command */
+#define CMD_READ_REG                           0xB5UL
+#define CMD_READ_RAM                           0xB1UL
+#define CMD_WRITE_REG                          0xB8UL
+#define CMD_WRITE_RAM                          0xB9UL
+#define CMD_CHECK_ID                           0xA3UL
+#define CMD_MCU_RESET                          0xE2UL
+#define CMD_CHECK_MCU_STOP                     0xD8UL
+#define CMD_MCU_STEP_RUN                       0xD1UL
+#define CMD_MCU_STOP_RUN                       0xD2UL
+#define CMD_MCU_FREE_RUN                       0xD3UL
+#define CMD_SET_CONFIG                         0xA2UL
+
+#define ARM_SRAM_BASE                          0x20000000
+
+#define HARDWARECONFIG_NULINKPRO    1
+
+enum RESET_E {
+       RESET_AUTO = 0,
+       RESET_HW = 1,
+       RESET_SYSRESETREQ = 2,
+       RESET_VECTRESET = 3,
+       RESET_FAST_RESCUE = 4, /* Rescue and erase the chip, need very fast 
speed */
+};
+
+enum CONNECT_E {
+       CONNECT_NORMAL = 0,      /* Support all reset method */
+       CONNECT_PRE_RESET = 1,   /* Support all reset method */
+       CONNECT_UNDER_RESET = 2, /* Support all reset method */
+       CONNECT_NONE = 3,        /* Support RESET_HW, (RESET_AUTO = RESET_HW) */
+       CONNECT_DISCONNECT = 4,  /* Support RESET_NONE, (RESET_AUTO = 
RESET_NONE) */
+       CONNECT_ICP_MODE = 5     /* Support NUC505 ICP mode*/
+};
+
+static int nulink_usb_xfer_rw(void *handle, int cmdsize, uint8_t *buf)
+{
+       struct nulink_usb_handle_s *h = handle;
+
+       assert(handle != NULL);
+
+       jtag_libusb_interrupt_write(h->fd, h->tx_ep, (char *)h->cmdbuf, cmdsize,
+               NULINK_WRITE_TIMEOUT);
+
+       jtag_libusb_interrupt_read(h->fd, h->rx_ep, (char *)buf,
+               cmdsize, NULINK_READ_TIMEOUT);
+
+       return ERROR_OK;
+}
+
+static int nulink_usb_xfer(void *handle, uint8_t *buf, int size)
+{
+       int err, cmdsize;
+       struct nulink_usb_handle_s *h = handle;
+
+       assert(handle != NULL);
+
+       cmdsize = NULINK_HID_MAX_SIZE;
+       err = nulink_usb_xfer_rw(h, cmdsize, h->tempbuf);
+
+       memcpy(buf, h->tempbuf + 2, NULINK_HID_MAX_SIZE - 2);
+
+       return err;
+}
+
+static void nulink_usb_init_buffer(void *handle, uint32_t size)
+{
+       struct nulink_usb_handle_s *h = handle;
+
+       h->cmdidx = 0;
+
+       memset(h->cmdbuf, 0, NULINK_HID_MAX_SIZE);
+       memset(h->tempbuf, 0, NULINK_HID_MAX_SIZE);
+       memset(h->databuf, 0, NULINK_HID_MAX_SIZE);
+
+       h->cmdbuf[0] = (char)(++h->usbcmdidx & (unsigned char)0x7F);
+       h->cmdbuf[1] = (char)size;
+       h->cmdidx += 2;
+}
+
+static int nulink_usb_version(void *handle)
+{
+       int res;
+       struct nulink_usb_handle_s *h = handle;
+
+       LOG_DEBUG("nulink_usb_version");
+
+       assert(handle != NULL);
+
+       nulink_usb_init_buffer(handle, V6M_MAX_COMMAND_LENGTH);
+
+       memset(h->cmdbuf + h->cmdidx, 0xFF, V6M_MAX_COMMAND_LENGTH);
+       h->cmdbuf[h->cmdidx + 4] = (char)0xA1; /* host_rev_num: 6561 */;
+       h->cmdbuf[h->cmdidx + 5] = (char)0x19;
+
+       res = nulink_usb_xfer(handle, h->databuf, 4 * 5);
+
+       if (res != ERROR_OK)
+               return res;
+
+       LOG_INFO("NULINK firmware_version(%d), product_id(0x%08x)",
+               le_to_h_u32(h->databuf),
+               le_to_h_u32(h->databuf + 4 * 1));
+
+       bool isNulinkPro = ((le_to_h_u32(h->databuf + 4 * 2) & 1) ? true : 
false);
+
+       if (isNulinkPro) {
+               LOG_INFO("NULINK is Nu-Link-Pro, target_voltage_mv(%d), 
usb_voltage_mv(%d)",
+                       (int)(unsigned short)(le_to_h_u32(h->databuf + 4 * 3)),
+                       (int)(unsigned short)(le_to_h_u32(h->databuf + 4 * 3) 
>> 16));
+
+               h->hardwareConfig = (h->hardwareConfig & 
~(HARDWARECONFIG_NULINKPRO)) | HARDWARECONFIG_NULINKPRO;
+       } else {
+               LOG_INFO("NULINK is Nu-Link");
+       }
+
+       return ERROR_OK;
+}
+
+static int nulink_usb_idcode(void *handle, uint32_t *idcode)
+{
+       int res;
+       struct nulink_usb_handle_s *h = handle;
+
+       LOG_DEBUG("nulink_usb_idcode");
+
+       assert(handle != NULL);
+
+       nulink_usb_init_buffer(handle, 4 * 1);
+       /* set command ID */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, CMD_CHECK_ID);
+       h->cmdidx += 4;
+
+       res = nulink_usb_xfer(handle, h->databuf, 4 * 2);
+
+       if (res != ERROR_OK)
+               return res;
+
+       *idcode = le_to_h_u32(h->databuf + 4 * 1);
+
+       LOG_INFO("IDCODE: 0x%08"PRIX32, *idcode);
+
+       return ERROR_OK;
+}
+
+static int nulink_usb_write_debug_reg(void *handle, uint32_t addr, uint32_t 
val)
+{
+       int res;
+       struct nulink_usb_handle_s *h = handle;
+
+       LOG_DEBUG("nulink_usb_write_debug_reg(0x%08x): 0x%08x",
+               addr,
+               val);
+
+       nulink_usb_init_buffer(handle, 8 + 12 * 1);
+       /* set command ID */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, CMD_WRITE_RAM);
+       h->cmdidx += 4;
+       /* Count of registers */
+       h->cmdbuf[h->cmdidx] = 1;
+       h->cmdidx += 1;
+       /* Array of bool value (u8ReadOld) */
+       h->cmdbuf[h->cmdidx] = (unsigned char)0x00;
+       h->cmdidx += 1;
+       /* Array of bool value (u8Verify) */
+       h->cmdbuf[h->cmdidx] = (unsigned char)0x00;
+       h->cmdidx += 1;
+       /* ignore */
+       h->cmdbuf[h->cmdidx] = 0;
+       h->cmdidx += 1;
+       /* u32Addr */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, addr);
+       h->cmdidx += 4;
+       /* u32Data */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, val);
+       h->cmdidx += 4;
+       /* u32Mask */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, (unsigned long)0x00000000);
+       h->cmdidx += 4;
+
+       res = nulink_usb_xfer(handle, h->databuf, 4 * 2);
+
+       return res;
+}
+
+static int nulink_usb_trace_read(void *handle, uint8_t *buf, size_t *size)
+{
+       /* not supported*/
+       LOG_DEBUG("nulink_usb_trace_read");
+
+       return ERROR_OK;
+}
+
+static enum target_state nulink_usb_state(void *handle)
+{
+       int res;
+       struct nulink_usb_handle_s *h = handle;
+
+       assert(handle != NULL);
+
+       nulink_usb_init_buffer(handle, 4 * 1);
+       /* set command ID */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, CMD_CHECK_MCU_STOP);
+       h->cmdidx += 4;
+
+       res = nulink_usb_xfer(handle, h->databuf, 4 * 4);
+
+       if (res != ERROR_OK)
+               return TARGET_UNKNOWN;
+
+       if (!le_to_h_u32(h->databuf + 4 * 2))
+               return TARGET_HALTED;
+       else
+               return TARGET_RUNNING;
+
+       return TARGET_UNKNOWN;
+}
+
+static int nulink_usb_assert_srst(void *handle, int srst)
+{
+       int res;
+       struct nulink_usb_handle_s *h = handle;
+
+       LOG_DEBUG("nulink_usb_assert_srst");
+
+       assert(handle != NULL);
+
+       nulink_usb_init_buffer(handle, 4 * 4);
+       /* set command ID */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, CMD_MCU_RESET);
+       h->cmdidx += 4;
+       /* set reset type */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, RESET_SYSRESETREQ);
+       h->cmdidx += 4;
+       /* set connect type */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, CONNECT_NORMAL);
+       h->cmdidx += 4;
+       /* set extMode */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, 0);
+       h->cmdidx += 4;
+
+       res = nulink_usb_xfer(handle, h->databuf, 4 * 4);
+
+       return res;
+}
+
+static int nulink_usb_reset(void *handle)
+{
+       int res;
+       struct nulink_usb_handle_s *h = handle;
+
+       LOG_DEBUG("nulink_usb_reset");
+
+       assert(handle != NULL);
+
+       nulink_usb_init_buffer(handle, 4 * 4);
+       /* set command ID */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, CMD_MCU_RESET);
+       h->cmdidx += 4;
+       /* set reset type */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, RESET_HW);
+       h->cmdidx += 4;
+       /* set connect type */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, CONNECT_NORMAL);
+       h->cmdidx += 4;
+       /* set extMode */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, 0);
+       h->cmdidx += 4;
+
+       res = nulink_usb_xfer(handle, h->databuf, 4 * 4);
+
+       return res;
+}
+
+static int nulink_usb_run(void *handle)
+{
+       int res;
+       struct nulink_usb_handle_s *h = handle;
+
+       LOG_DEBUG("nulink_usb_run");
+
+       assert(handle != NULL);
+
+       nulink_usb_init_buffer(handle, 4 * 1);
+       /* set command ID */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, CMD_MCU_FREE_RUN);
+       h->cmdidx += 4;
+
+       res = nulink_usb_xfer(handle, h->databuf, 4 * 4);
+
+       return res;
+}
+
+static int nulink_usb_halt(void *handle)
+{
+       int res;
+       struct nulink_usb_handle_s *h = handle;
+
+       LOG_DEBUG("nulink_usb_halt");
+
+       assert(handle != NULL);
+
+       nulink_usb_init_buffer(handle, 4 * 1);
+       /* set command ID */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, CMD_MCU_STOP_RUN);
+       h->cmdidx += 4;
+
+       res = nulink_usb_xfer(handle, h->databuf, 4 * 4);
+
+       LOG_DEBUG("NULINK  stop_pc(0x%08x)",
+               le_to_h_u32(h->databuf + 4));
+
+       return res;
+}
+
+static int nulink_usb_step(void *handle)
+{
+       int res;
+       struct nulink_usb_handle_s *h = handle;
+
+       LOG_DEBUG("nulink_usb_step");
+
+       assert(handle != NULL);
+
+       nulink_usb_init_buffer(handle, 4 * 1);
+       /* set command ID */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, CMD_MCU_STEP_RUN);
+       h->cmdidx += 4;
+
+       res = nulink_usb_xfer(handle, h->databuf, 4 * 4);
+
+       LOG_DEBUG("NULINK pc(0x%08x)",
+               le_to_h_u32(h->databuf + 4));
+
+       return res;
+}
+
+static int nulink_usb_read_regs(void *handle)
+{
+       /* not supported*/
+       LOG_DEBUG("nulink_usb_read_regs");
+
+       return ERROR_OK;
+}
+
+static int nulink_usb_read_reg(void *handle, int num, uint32_t *val)
+{
+       int res;
+       struct nulink_usb_handle_s *h = handle;
+
+       assert(handle != NULL);
+
+       nulink_usb_init_buffer(handle, 8 + 12 * 1);
+       /* set command ID */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, CMD_WRITE_REG);
+       h->cmdidx += 4;
+       /* Count of registers */
+       h->cmdbuf[h->cmdidx] = 1;
+       h->cmdidx += 1;
+       /* Array of bool value (u8ReadOld) */
+       h->cmdbuf[h->cmdidx] = (unsigned char)0xFF;
+       h->cmdidx += 1;
+       /* Array of bool value (u8Verify) */
+       h->cmdbuf[h->cmdidx] = (unsigned char)0x00;
+       h->cmdidx += 1;
+       /* ignore */
+       h->cmdbuf[h->cmdidx] = 0;
+       h->cmdidx += 1;
+       /* u32Addr */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, num);
+       h->cmdidx += 4;
+       /* u32Data */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, 0);
+       h->cmdidx += 4;
+       /* u32Mask */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, (unsigned long)0xFFFFFFFF);
+       h->cmdidx += 4;
+
+       res = nulink_usb_xfer(handle, h->databuf, 4 * 2);
+
+       *val = le_to_h_u32(h->databuf + 4 * 1);
+
+       return res;
+}
+
+static int nulink_usb_write_reg(void *handle, int num, uint32_t val)
+{
+       int res;
+       struct nulink_usb_handle_s *h = handle;
+
+       assert(handle != NULL);
+
+       nulink_usb_init_buffer(handle, 8 + 12 * 1);
+       /* set command ID */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, CMD_WRITE_REG);
+       h->cmdidx += 4;
+       /* Count of registers */
+       h->cmdbuf[h->cmdidx] = 1;
+       h->cmdidx += 1;
+       /* Array of bool value (u8ReadOld) */
+       h->cmdbuf[h->cmdidx] = (unsigned char)0x00;
+       h->cmdidx += 1;
+       /* Array of bool value (u8Verify) */
+       h->cmdbuf[h->cmdidx] = (unsigned char)0x00;
+       h->cmdidx += 1;
+       /* ignore */
+       h->cmdbuf[h->cmdidx] = 0;
+       h->cmdidx += 1;
+       /* u32Addr */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, num);
+       h->cmdidx += 4;
+       /* u32Data */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, val);
+       h->cmdidx += 4;
+       /* u32Mask */
+       h_u32_to_le(h->cmdbuf + h->cmdidx, (unsigned long)0x00000000);
+       h->cmdidx += 4;
+
+       res = nulink_usb_xfer(handle, h->databuf, 4 * 2);
+
+       return res;
+}
+
+static int nulink_usb_read_mem8(void *handle, uint32_t addr, uint16_t len,
+                         uint8_t *buffer)
+{
+       int res = ERROR_OK;
+       unsigned i, count;
+       unsigned alignedAddr, offset = 0;
+       uint32_t bytes_remaining = 12;
+       struct nulink_usb_handle_s *h = handle;
+
+       LOG_DEBUG("nulink_usb_read_mem8: addr(0x%08x), len(%d)", addr, len);
+
+       assert(handle != NULL);
+
+       /* check whether data is word aligned */
+       if (addr % 4) {
+               alignedAddr = addr / 4;
+               alignedAddr = alignedAddr * 4;
+               offset = addr - alignedAddr;
+               LOG_DEBUG("nulink_usb_read_mem8: address not aligned. 
addr(0x%08x)/alignedAddr(0x%08x)/offset(%d)",
+                       addr, alignedAddr, offset);
+
+               addr = alignedAddr;
+       }
+
+       while (len) {
+               if (len < bytes_remaining)
+                       bytes_remaining = len;
+
+               if (len < 4)
+                       count = 1;
+               else
+                       count = 2;
+
+               nulink_usb_init_buffer(handle, 8 + 12 * count);
+               /* set command ID */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, CMD_WRITE_RAM);
+               h->cmdidx += 4;
+               /* Count of registers */
+               h->cmdbuf[h->cmdidx] = count;
+               h->cmdidx += 1;
+               /* Array of bool value (u8ReadOld) */
+               h->cmdbuf[h->cmdidx] = (unsigned char)0xFF;
+               h->cmdidx += 1;
+               /* Array of bool value (u8Verify) */
+               h->cmdbuf[h->cmdidx] = (unsigned char)0x00;
+               h->cmdidx += 1;
+               /* ignore */
+               h->cmdbuf[h->cmdidx] = 0;
+               h->cmdidx += 1;
+
+               for (i = 0; i < count; i++) {
+                       /* u32Addr */
+                       h_u32_to_le(h->cmdbuf + h->cmdidx, addr);
+                       h->cmdidx += 4;
+                       /* u32Data */
+                       h_u32_to_le(h->cmdbuf + h->cmdidx, 0);
+                       h->cmdidx += 4;
+                       /* u32Mask */
+                       h_u32_to_le(h->cmdbuf + h->cmdidx, (unsigned 
long)0xFFFFFFFF);
+                       h->cmdidx += 4;
+                       /* proceed to the next one  */
+                       addr += 4;
+               }
+
+               res = nulink_usb_xfer(handle, h->databuf, 4 * count * 2);
+
+               /* fill in the output buffer */
+               for (i = 0; i < count; i++) {
+                       if (i == 0)
+                               memcpy(buffer, h->databuf + 4 + offset, len);
+                       else
+                               memcpy(buffer + 2 * i, h->databuf + 4 * (2 * i 
+ 1), len - 2);
+               }
+
+               if (len >= bytes_remaining)
+                       len -= bytes_remaining;
+               else
+                       len = 0;
+       }
+
+       return res;
+}
+
+static int nulink_usb_write_mem8(void *handle, uint32_t addr, uint16_t len,
+                          const uint8_t *buffer)
+{
+       int res = ERROR_OK;
+       unsigned i, count;
+       unsigned alignedAddr, offset = 0;
+       uint32_t bytes_remaining = 12;
+       uint32_t u32bufferData;
+       struct nulink_usb_handle_s *h = handle;
+
+       LOG_DEBUG("nulink_usb_write_mem8: addr(0x%08x), len(%d)", addr, len);
+
+       assert(handle != NULL);
+
+       /* check whether data is word aligned */
+       if (addr % 4) {
+               alignedAddr = addr / 4;
+               alignedAddr = alignedAddr * 4;
+               offset = addr - alignedAddr;
+               LOG_DEBUG("nulink_usb_write_mem8: address not aligned. 
addr(0x%08x)/alignedAddr(0x%08x)/offset(%d)",
+                         addr, alignedAddr, offset);
+
+               addr = alignedAddr;
+       }
+
+       while (len) {
+               if (len < bytes_remaining)
+                       bytes_remaining = len;
+
+               if (len < 4)
+                       count = 1;
+               else
+                       count = 2;
+
+               nulink_usb_init_buffer(handle, 8 + 12 * count);
+               /* set command ID */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, CMD_WRITE_RAM);
+               h->cmdidx += 4;
+               /* Count of registers */
+               h->cmdbuf[h->cmdidx] = count;
+               h->cmdidx += 1;
+               /* Array of bool value (u8ReadOld) */
+               h->cmdbuf[h->cmdidx] = (unsigned char)0x00;
+               h->cmdidx += 1;
+               /* Array of bool value (u8Verify) */
+               h->cmdbuf[h->cmdidx] = (unsigned char)0x00;
+               h->cmdidx += 1;
+               /* ignore */
+               h->cmdbuf[h->cmdidx] = 0;
+               h->cmdidx += 1;
+
+               for (i = 0; i < count; i++) {
+                       /* u32Addr */
+                       h_u32_to_le(h->cmdbuf + h->cmdidx, addr);
+                       h->cmdidx += 4;
+                       /* u32Data */
+                       u32bufferData = buf_get_u32(buffer, 0, len * 8);
+                       u32bufferData = (u32bufferData << offset * 8);
+                       h_u32_to_le(h->cmdbuf + h->cmdidx, u32bufferData);
+                       h->cmdidx += 4;
+                       /* u32Mask */
+                       if (i == 0) {
+                               if (offset == 0) {
+                                       if (len == 1) {
+                                               h_u32_to_le(h->cmdbuf + 
h->cmdidx, (unsigned long)0xFFFFFF00);
+                                               
LOG_DEBUG("nulink_usb_write_mem8: count(%d), mask: 0xFFFFFF00", i);
+                                       } else {
+                                               h_u32_to_le(h->cmdbuf + 
h->cmdidx, (unsigned long)0xFFFF0000);
+                                               
LOG_DEBUG("nulink_usb_write_mem8: count(%d), mask: 0xFFFF0000", i);
+                                       }
+                               } else {
+                                       if (len == 1) {
+                                               h_u32_to_le(h->cmdbuf + 
h->cmdidx, (unsigned long)0xFF00FFFF);
+                                               
LOG_DEBUG("nulink_usb_write_mem8: count(%d), mask: 0xFF00FFFF", i);
+
+                                       } else {
+                                               h_u32_to_le(h->cmdbuf + 
h->cmdidx, (unsigned long)0x0000FFFF);
+                                               
LOG_DEBUG("nulink_usb_write_mem8: count(%d), mask: 0x0000FFFF", i);
+                                       }
+                               }
+                       } else {
+                               if (len == 4) {
+                                       h_u32_to_le(h->cmdbuf + h->cmdidx, 
(unsigned long)0xFFFF0000);
+                                       LOG_DEBUG("nulink_usb_write_mem8: 
count(%d), mask: 0xFFFF0000", i);
+                               } else {
+                                       h_u32_to_le(h->cmdbuf + h->cmdidx, 
(unsigned long)0x00000000);
+                                       LOG_DEBUG("nulink_usb_write_mem8: 
count(%d), mask: 0x00000000", i);
+                               }
+                       }
+                       h->cmdidx += 4;
+
+                       /* proceed to the next one */
+                       addr += 4;
+                       buffer += 4;
+               }
+
+               res = nulink_usb_xfer(handle, h->databuf, 4 * count * 2);
+
+               if (len >= bytes_remaining)
+                       len -= bytes_remaining;
+               else
+                       len = 0;
+       }
+
+       return res;
+}
+
+static int nulink_usb_read_mem32(void *handle, uint32_t addr, uint16_t len,
+                         uint8_t *buffer)
+{
+       int res = ERROR_OK;
+       unsigned i, count;
+       uint32_t bytes_remaining = 12;
+       struct nulink_usb_handle_s *h = handle;
+
+       assert(handle != NULL);
+
+       /* data must be a multiple of 4 and word aligned */
+       if (len % 4 || addr % 4) {
+               LOG_ERROR("Invalid data alignment");
+               return ERROR_TARGET_UNALIGNED_ACCESS;
+       }
+
+       while (len) {
+               if (len < bytes_remaining)
+                       bytes_remaining = len;
+
+               count = bytes_remaining / 4;
+
+               nulink_usb_init_buffer(handle, 8 + 12 * count);
+               /* set command ID */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, CMD_WRITE_RAM);
+               h->cmdidx += 4;
+               /* Count of registers */
+               h->cmdbuf[h->cmdidx] = count;
+               h->cmdidx += 1;
+               /* Array of bool value (u8ReadOld) */
+               h->cmdbuf[h->cmdidx] = (unsigned char)0xFF;
+               h->cmdidx += 1;
+               /* Array of bool value (u8Verify) */
+               h->cmdbuf[h->cmdidx] = (unsigned char)0x00;
+               h->cmdidx += 1;
+               /* ignore */
+               h->cmdbuf[h->cmdidx] = 0;
+               h->cmdidx += 1;
+
+               for (i = 0; i < count; i++) {
+                       /* u32Addr */
+                       h_u32_to_le(h->cmdbuf + h->cmdidx, addr);
+                       h->cmdidx += 4;
+                       /* u32Data */
+                       h_u32_to_le(h->cmdbuf + h->cmdidx, 0);
+                       h->cmdidx += 4;
+                       /* u32Mask */
+                       h_u32_to_le(h->cmdbuf + h->cmdidx, (unsigned 
long)0xFFFFFFFF);
+                       h->cmdidx += 4;
+                       /* proceed to the next one  */
+                       addr += 4;
+               }
+
+               res = nulink_usb_xfer(handle, h->databuf, 4 * count * 2);
+
+               /* fill in the output buffer */
+               for (i = 0; i < count; i++)
+                       memcpy(buffer + 4 * i, h->databuf + 4 * (2 * i + 1), 4);
+
+               if (len >= bytes_remaining)
+                       len -= bytes_remaining;
+               else
+                       len = 0;
+       }
+
+       return res;
+}
+
+static int nulink_usb_write_mem32(void *handle, uint32_t addr, uint16_t len,
+       const uint8_t *buffer)
+{
+       int res = ERROR_OK;
+       unsigned i, count;
+       uint32_t bytes_remaining = 12;
+       uint32_t u32bufferData;
+       struct nulink_usb_handle_s *h = handle;
+
+       assert(handle != NULL);
+
+       /* data must be a multiple of 4 and word aligned */
+       if (len % 4 || addr % 4) {
+               LOG_ERROR("Invalid data alignment");
+               return ERROR_TARGET_UNALIGNED_ACCESS;
+       }
+
+       while (len) {
+               if (len < bytes_remaining)
+                       bytes_remaining = len;
+
+               count = bytes_remaining / 4;
+
+               nulink_usb_init_buffer(handle, 8 + 12 * count);
+               /* set command ID */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, CMD_WRITE_RAM);
+               h->cmdidx += 4;
+               /* Count of registers */
+               h->cmdbuf[h->cmdidx] = count;
+               h->cmdidx += 1;
+               /* Array of bool value (u8ReadOld) */
+               h->cmdbuf[h->cmdidx] = (unsigned char)0x00;
+               h->cmdidx += 1;
+               /* Array of bool value (u8Verify) */
+               h->cmdbuf[h->cmdidx] = (unsigned char)0x00;
+               h->cmdidx += 1;
+               /* ignore */
+               h->cmdbuf[h->cmdidx] = 0;
+               h->cmdidx += 1;
+
+               for (i = 0; i < count; i++) {
+                       /* u32Addr */
+                       h_u32_to_le(h->cmdbuf + h->cmdidx, addr);
+                       h->cmdidx += 4;
+                       /* u32Data */
+                       u32bufferData = buf_get_u32(buffer, 0, 32);
+                       h_u32_to_le(h->cmdbuf + h->cmdidx, u32bufferData);
+                       h->cmdidx += 4;
+                       /* u32Mask */
+                       h_u32_to_le(h->cmdbuf + h->cmdidx, (unsigned 
long)0x00000000);
+                       h->cmdidx += 4;
+
+                       /* proceed to the next one */
+                       addr += 4;
+                       buffer += 4;
+               }
+
+               res = nulink_usb_xfer(handle, h->databuf, 4 * count * 2);
+
+               if (len >= bytes_remaining)
+                       len -= bytes_remaining;
+               else
+                       len = 0;
+       }
+
+       return res;
+}
+
+static uint32_t nulink_max_block_size(uint32_t tar_autoincr_block, uint32_t 
address)
+{
+       uint32_t max_tar_block = (tar_autoincr_block - ((tar_autoincr_block - 
1) & address));
+
+       if (max_tar_block == 0)
+               max_tar_block = 4;
+
+       return max_tar_block;
+}
+
+static int nulink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
+               uint32_t count, uint8_t *buffer)
+{
+       int retval = ERROR_OK;
+       uint32_t bytes_remaining;
+       struct nulink_usb_handle_s *h = handle;
+
+       /* calculate byte count */
+       count *= size;
+
+       while (count) {
+
+               bytes_remaining = nulink_max_block_size(h->max_mem_packet, 
addr);
+
+               if (count < bytes_remaining)
+                       bytes_remaining = count;
+
+               /* the nulink only supports 8/32bit memory read/writes
+                * honour 32bit, all others will be handled as 8bit access */
+               if (size == 4) {
+
+                       /* When in jtag mode the nulink uses the auto-increment 
functinality.
+                        * However it expects us to pass the data correctly, 
this includes
+                        * alignment and any page boundaries. We already do 
this as part of the
+                        * adi_v5 implementation, but the nulink is a hla 
adapter and so this
+                        * needs implementiong manually.
+                        * currently this only affects jtag mode, according to 
ST they do single
+                        * access in SWD mode - but this may change and so we 
do it for both modes */
+
+                       /* we first need to check for any unaligned bytes */
+                       if (addr % 4) {
+
+                               uint32_t head_bytes = 4 - (addr % 4);
+                               retval = nulink_usb_read_mem8(handle, addr, 
head_bytes, buffer);
+                               if (retval != ERROR_OK)
+                                       return retval;
+                               buffer += head_bytes;
+                               addr += head_bytes;
+                               count -= head_bytes;
+                               bytes_remaining -= head_bytes;
+                       }
+
+                       if (bytes_remaining % 4)
+                               retval = nulink_usb_read_mem(handle, addr, 1, 
bytes_remaining, buffer);
+                       else
+                               retval = nulink_usb_read_mem32(handle, addr, 
bytes_remaining, buffer);
+               } else
+                       retval = nulink_usb_read_mem8(handle, addr, 
bytes_remaining, buffer);
+
+               if (retval != ERROR_OK)
+                       return retval;
+
+               buffer += bytes_remaining;
+               addr += bytes_remaining;
+               count -= bytes_remaining;
+       }
+
+       return retval;
+}
+
+static int nulink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
+               uint32_t count, const uint8_t *buffer)
+{
+       int retval = ERROR_OK;
+       uint32_t bytes_remaining;
+       struct nulink_usb_handle_s *h = handle;
+
+       if (addr < ARM_SRAM_BASE) {
+               LOG_DEBUG("nulink_usb_write_mem: address below ARM_SRAM_BASE, 
not supported.\n");
+               return retval;
+       }
+
+       /* calculate byte count */
+       count *= size;
+
+       while (count) {
+               bytes_remaining = nulink_max_block_size(h->max_mem_packet, 
addr);
+
+               if (count < bytes_remaining)
+                       bytes_remaining = count;
+
+               /* the nulink only supports 8/32bit memory read/writes
+                * honour 32bit, all others will be handled as 8bit access */
+               if (size == 4) {
+
+                       /* When in jtag mode the nulink uses the auto-increment 
functinality.
+                        * However it expects us to pass the data correctly, 
this includes
+                        * alignment and any page boundaries. We already do 
this as part of the
+                        * adi_v5 implementation, but the nulink is a hla 
adapter and so this
+                        * needs implementiong manually.
+                        * currently this only affects jtag mode, according to 
ST they do single
+                        * access in SWD mode - but this may change and so we 
do it for both modes */
+
+                       /* we first need to check for any unaligned bytes */
+                       if (addr % 4) {
+
+                               uint32_t head_bytes = 4 - (addr % 4);
+                               retval = nulink_usb_write_mem8(handle, addr, 
head_bytes, buffer);
+                               if (retval != ERROR_OK)
+                                       return retval;
+                               buffer += head_bytes;
+                               addr += head_bytes;
+               count -= head_bytes;
+                               bytes_remaining -= head_bytes;
+                       }
+
+                       if (bytes_remaining % 4)
+                               retval = nulink_usb_write_mem(handle, addr, 1, 
bytes_remaining, buffer);
+                       else
+                               retval = nulink_usb_write_mem32(handle, addr, 
bytes_remaining, buffer);
+
+               } else
+                       retval = nulink_usb_write_mem8(handle, addr, 
bytes_remaining, buffer);
+
+               if (retval != ERROR_OK)
+                       return retval;
+
+               buffer += bytes_remaining;
+               addr += bytes_remaining;
+               count -= bytes_remaining;
+       }
+
+       return retval;
+}
+
+static int nulink_usb_override_target(const char *targetname)
+{
+       LOG_DEBUG("nulink_usb_override_target");
+
+       return !strcmp(targetname, "cortex_m");
+}
+
+static int nulink_speed(void *handle, int khz, bool query)
+{
+       struct nulink_usb_handle_s *h = handle;
+       unsigned long max_ice_clock = khz;
+
+       LOG_DEBUG("nulink_speed: query(%d)", query);
+
+       if (max_ice_clock > 12000)
+               max_ice_clock = 12000;
+       else if ((max_ice_clock == 3 * 512) || (max_ice_clock == 1500))
+               max_ice_clock = 1500;
+       else if (max_ice_clock >= 1000)
+               max_ice_clock = max_ice_clock / 1000 * 1000;
+       else
+               max_ice_clock = max_ice_clock / 100 * 100;
+
+       LOG_DEBUG("NULINK nulink_speed: %lu", max_ice_clock);
+
+       if (!query) {
+               nulink_usb_init_buffer(handle, 4 * 6);
+               /* set command ID */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, CMD_SET_CONFIG);
+               h->cmdidx += 4;
+               /* set max SWD clock */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, max_ice_clock);
+               h->cmdidx += 4;
+               /* chip type: NUC_CHIP_TYPE_GENERAL_V6M */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, 0);
+               h->cmdidx += 4;
+               /* IO voltage */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, 5000);
+               h->cmdidx += 4;
+               /* If supply voltage to target or not */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, 0);
+               h->cmdidx += 4;
+               /* USB_FUNC_E: USB_FUNC_HID_BULK */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, 2);
+               h->cmdidx += 4;
+
+               nulink_usb_xfer(handle, h->databuf, 4 * 3);
+
+               LOG_DEBUG("nulink_speed: h->hardwareConfig(%d)", 
h->hardwareConfig);
+               if (h->hardwareConfig & 1) {
+                       LOG_INFO("NULINK target_voltage_mv[0](%04x), 
target_voltage_mv[1](%04x), target_voltage_mv[2](%04x), 
if_target_power_supplied(%d)",
+                               le_to_h_u32(h->databuf + 4 * 1),
+                               le_to_h_u32(h->databuf + 4 * 1) >> 16,
+                               le_to_h_u32(h->databuf + 4 * 2),
+                               (le_to_h_u32(h->databuf + 4 * 2) >> 16) & 1
+                               );
+               }
+       }
+
+       return max_ice_clock;
+}
+
+static int nulink_usb_close(void *handle)
+{
+       struct nulink_usb_handle_s *h = handle;
+
+       LOG_DEBUG("nulink_usb_close");
+
+       if (h && h->fd)
+               jtag_libusb_close(h->fd);
+
+       free(h);
+
+       return ERROR_OK;
+}
+
+static int nulink_usb_open(struct hl_interface_param_s *param, void **fd)
+{
+       int err, retry_count = 1;
+       struct nulink_usb_handle_s *h;
+
+       LOG_DEBUG("nulink_usb_open");
+
+       h = calloc(1, sizeof(struct nulink_usb_handle_s));
+
+       if (h == 0) {
+               LOG_ERROR("malloc failed");
+               return ERROR_FAIL;
+       }
+
+       h->transport = param->transport;
+
+       const char *serial = param->serial;
+
+       do {
+               if (jtag_libusb_open(param->vid, param->pid, serial, &h->fd, 
NULL) != ERROR_OK) {
+                       LOG_ERROR("open failed");
+                       return ERROR_FAIL;
+               } else {
+                       LOG_DEBUG("jtag_libusb_open succeeded");
+               }
+
+               jtag_libusb_set_configuration(h->fd, 0);
+
+               err = jtag_libusb_detach_kernel_driver(h->fd, 0);
+               if (err != ERROR_OK) {
+                       LOG_ERROR("detach kernel driver failed(%d)", err);
+                       /* drivers might not binded, procced */
+               } else {
+                       LOG_DEBUG("jtag_libusb_detach_kernel_driver succeeded");
+               }
+
+               err = jtag_libusb_claim_interface(h->fd, 0);
+               if (err != ERROR_OK) {
+                       LOG_ERROR("claim interface failed(%d)", err);
+                       goto error_open;
+               } else {
+                       LOG_DEBUG("jtag_libusb_claim_interface succeeded");
+               }
+
+               h->rx_ep = NULINK_RX_EP;
+               h->tx_ep = NULINK_TX_EP;
+               h->usbcmdidx = 0;
+               h->hardwareConfig = 0;
+
+               /* get the device version */
+               err = nulink_usb_version(h);
+
+               if (err == ERROR_OK) {
+                       break;
+               } else {
+                       err = jtag_libusb_release_interface(h->fd, 0);
+                       if (err != ERROR_OK) {
+                               LOG_ERROR("release interface failed");
+                               goto error_open;
+                       }
+
+                       err = jtag_libusb_reset_device(h->fd);
+                       if (err != ERROR_OK) {
+                               LOG_ERROR("reset device failed");
+                               goto error_open;
+                       }
+
+                       jtag_libusb_close(h->fd);
+                       /*
+                         Give the device one second to settle down and
+                         reenumerate.
+                        */
+                       usleep(1 * 1000 * 1000);
+                       if (0 == retry_count--)
+                               goto error_open;
+               }
+       } while (1);
+
+       /* SWD clock rate : 1MHz */
+       nulink_speed(h, 1000, false);
+
+       /* get cpuid, so we can determine the max page size
+        * start with a safe default */
+       h->max_mem_packet = (1 << 10);
+
+       LOG_DEBUG("nulink_usb_open: we manually perform nulink_usb_reset");
+       nulink_usb_write_debug_reg(h, 0xe000edf0, 0xa05f0001);
+       nulink_usb_write_debug_reg(h, 0xe000edfc, 0x01000001);
+       nulink_usb_write_debug_reg(h, 0xe000ed0c, 0x05fa0004);
+       nulink_usb_reset(h);
+
+       *fd = h;
+
+       return ERROR_OK;
+
+error_open:
+       nulink_usb_close(h);
+
+       return ERROR_FAIL;
+}
+
+int nulink_config_trace(void *handle, bool enabled,
+                       enum tpiu_pin_protocol pin_protocol, uint32_t port_size,
+                       unsigned int *trace_freq, unsigned int traceclkin_freq,
+                       uint16_t *prescaler)
+{
+       /* not supported */
+       LOG_DEBUG("nulink_config_trace");
+
+       return ERROR_OK;
+}
+
+struct hl_layout_api_s nulink_usb_layout_api = {
+
+       .open = nulink_usb_open,
+
+       .close = nulink_usb_close,
+
+       .idcode = nulink_usb_idcode,
+
+       .state = nulink_usb_state,
+
+       .reset = nulink_usb_reset,
+
+       .assert_srst = nulink_usb_assert_srst,
+
+       .run = nulink_usb_run,
+
+       .halt = nulink_usb_halt,
+
+       .step = nulink_usb_step,
+
+       .read_regs = nulink_usb_read_regs,
+
+       .read_reg = nulink_usb_read_reg,
+
+       .write_reg = nulink_usb_write_reg,
+
+       .read_mem = nulink_usb_read_mem,
+
+       .write_mem = nulink_usb_write_mem,
+
+       .write_debug_reg = nulink_usb_write_debug_reg,
+
+       .override_target = nulink_usb_override_target,
+
+       .speed = nulink_speed,
+
+       .config_trace = nulink_config_trace,
+
+       .poll_trace = nulink_usb_trace_read,
+};
diff --git a/src/jtag/hla/hla_layout.c b/src/jtag/hla/hla_layout.c
index c5e3518..917fb0d 100644
--- a/src/jtag/hla/hla_layout.c
+++ b/src/jtag/hla/hla_layout.c
@@ -69,6 +69,12 @@ static const struct hl_layout hl_layouts[] = {
         .close = hl_layout_close,
         .api = &icdi_usb_layout_api,
        },
+       {
+        .name = "nulink",
+        .open = hl_layout_open,
+        .close = hl_layout_close,
+        .api = &nulink_usb_layout_api,
+       },
        {.name = NULL, /* END OF TABLE */ },
 };
 
diff --git a/src/jtag/hla/hla_layout.h b/src/jtag/hla/hla_layout.h
index 1d759e1..6ae1dfa 100644
--- a/src/jtag/hla/hla_layout.h
+++ b/src/jtag/hla/hla_layout.h
@@ -31,6 +31,7 @@ struct hl_interface_param_s;
 /** */
 extern struct hl_layout_api_s stlink_usb_layout_api;
 extern struct hl_layout_api_s icdi_usb_layout_api;
+extern struct hl_layout_api_s nulink_usb_layout_api;
 
 /** */
 struct hl_layout_api_s {

-- 


_______________________________________________
OpenOCD-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openocd-devel

Reply via email to