This is an automated email from Gerrit.

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

-- gerrit

commit eb3abfc9fcd4f48a80589e95f1c96744a129a3d1
Author: Zale Yu <[email protected]>
Date:   Tue Oct 23 17:09:14 2018 +0800

    add Nuvoton Nu-Link support
    
    Change-Id: Ic4f7dc5b7d2c32a7d2bfe6fc19926ddce97ac30a
    Signed-off-by: Zale Yu <[email protected]>

diff --git a/configure.ac b/configure.ac
index d4338df..f4a183e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -112,6 +112,7 @@ m4_define([USB1_ADAPTERS],
        [[[ftdi], [MPSSE mode of FTDI based devices], [FTDI]],
        [[stlink], [ST-Link JTAG Programmer], [HLADAPTER_STLINK]],
        [[ti_icdi], [TI ICDI JTAG Programmer], [HLADAPTER_ICDI]],
+       [[nulink], [Nu-Link Programmer], [HLADAPTER_NULINK]],
        [[ulink], [Keil ULINK JTAG Programmer], [ULINK]],
        [[usb_blaster_2], [Altera USB-Blaster II Compatible], [USB_BLASTER_2]],
        [[ft232r], [Bitbang mode of FT232R based devices], [FT232R]],
diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am
index ccef018..6bee3dd 100644
--- a/src/jtag/drivers/Makefile.am
+++ b/src/jtag/drivers/Makefile.am
@@ -134,6 +134,7 @@ endif
 if HLADAPTER
 DRIVERFILES += %D%/stlink_usb.c
 DRIVERFILES += %D%/ti_icdi_usb.c
+DRIVERFILES += %D%/nulink_usb.c
 endif
 if OSBDM
 DRIVERFILES += %D%/osbdm.c
diff --git a/src/jtag/drivers/libusb1_common.c 
b/src/jtag/drivers/libusb1_common.c
index a1db86f..9d6685c 100644
--- a/src/jtag/drivers/libusb1_common.c
+++ b/src/jtag/drivers/libusb1_common.c
@@ -136,6 +136,26 @@ int jtag_libusb_control_transfer(jtag_libusb_device_handle 
*dev, uint8_t request
        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(jtag_libusb_device_handle *dev, int ep, char *bytes,
                int size, int timeout)
 {
diff --git a/src/jtag/drivers/libusb1_common.h 
b/src/jtag/drivers/libusb1_common.h
index 7c73d29..5e74b99 100644
--- a/src/jtag/drivers/libusb1_common.h
+++ b/src/jtag/drivers/libusb1_common.h
@@ -33,6 +33,12 @@
 #define jtag_libusb_reset_device(dev)          libusb_reset_device(dev)
 #define jtag_libusb_get_device(devh)           libusb_get_device(devh)
 
+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)
 {
@@ -52,6 +58,10 @@ void jtag_libusb_close(jtag_libusb_device_handle *dev);
 int jtag_libusb_control_transfer(jtag_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_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 jtag_libusb_bulk_read(struct jtag_libusb_device_handle *dev, int ep,
diff --git a/src/jtag/drivers/nulink_usb.c b/src/jtag/drivers/nulink_usb.c
new file mode 100755
index 0000000..b5973fe
--- /dev/null
+++ b/src/jtag/drivers/nulink_usb.c
@@ -0,0 +1,1517 @@
+/***************************************************************************
+ *   Copyright (C) 2018 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_common.h"
+
+#define ENDPOINT_IN  0x80
+#define ENDPOINT_OUT 0x00
+
+#define NULINK_WRITE_TIMEOUT 1000
+#define NULINK_READ_TIMEOUT  1000
+
+#define NULINK_INTERFACE_NUM  0
+#define NULINK2_INTERFACE_NUM 3
+
+#define NULINK_RX_EP  (1|ENDPOINT_IN)
+#define NULINK_TX_EP  (2|ENDPOINT_OUT)
+#define NULINK2_RX_EP (6|ENDPOINT_IN)
+#define NULINK2_TX_EP (7|ENDPOINT_OUT)
+
+#define NULINK_HID_MAX_SIZE    (64)
+#define NULINK2_HID_MAX_SIZE   (1024)
+#define V6M_MAX_COMMAND_LENGTH (NULINK_HID_MAX_SIZE - 2)
+#define V7M_MAX_COMMAND_LENGTH (NULINK_HID_MAX_SIZE - 3)
+
+#define USBCMD_TIMEOUT 5000
+
+#define SHOW_BUFFER 0
+
+struct nulink_usb_handle_s {
+       struct jtag_libusb_device_handle *fd;
+       struct libusb_transfer *trans;
+       uint8_t interface_num;
+       uint8_t rx_ep;
+       uint8_t tx_ep;
+       uint32_t usbcmdidx;
+       uint8_t cmdidx;
+       uint8_t cmdbuf[NULINK2_HID_MAX_SIZE];
+       uint8_t tempbuf[NULINK2_HID_MAX_SIZE];
+       uint8_t databuf[NULINK2_HID_MAX_SIZE];
+       uint32_t max_mem_packet;
+       enum hl_transports transport;
+       uint16_t hardwareconfig; /* bit 0: 1:Nu-Link-Pro, 0:Normal Nu-Link | 
bit 1: 1:Nu-Link2, 0:Nu-Link */
+} *m_nulink_usb_handle;
+
+struct nulink_usb_internal_api_s {
+       int (*nulink_usb_xfer) (void *handle, uint8_t *buf, int size);
+       void (*nulink_usb_init_buffer) (void *handle, uint32_t size);
+} m_nulink_usb_api;
+
+/* ICE Command */
+#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 CMD_ERASE_FLASHCHIP         0xA4UL
+#define ARM_SRAM_BASE               0x20000000
+
+#define HARDWARECONFIG_NULINKPRO    1
+#define HARDWARECONFIG_NULINK2      2
+
+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 */
+       RESET_NONE_NULINK   = 5, /* Connect only */
+       RESET_NONE2         = 6  /* For 8051 1T */
+};
+
+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*/
+};
+
+#if (SHOW_BUFFER)
+static void print64bytesbuffercontent(char *buffername, uint8_t *buf, int size)
+{
+       unsigned i, j;
+       LOG_DEBUG("%s:", buffername);
+
+       for (i = 0; i < 4; i++) {
+               j = i * 16;
+               LOG_DEBUG("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x 
%02x %02x %02x %02x %02x %02x ",
+               buf[j + 0],  buf[j + 1],  buf[j + 2],  buf[j + 3],
+               buf[j + 4],  buf[j + 5],  buf[j + 6],  buf[j + 7],
+               buf[j + 8],  buf[j + 9],  buf[j + 10], buf[j + 11],
+               buf[j + 12], buf[j + 13], buf[j + 14], buf[j + 15]
+               );
+       }
+}
+#endif
+
+#ifndef _WIN32
+double GetTickCount(void)
+{
+       struct timespec now;
+       if (clock_gettime(CLOCK_MONOTONIC, &now))
+               return 0;
+       return now.tv_sec * 1000.0 + now.tv_nsec / 1000000.0;
+}
+#endif
+
+static void nulink_usb_init_buffer(void *handle, uint32_t size);
+static void nulink_usb_init_buffer2(void *handle, uint32_t size);
+
+static int nulink_usb_xfer_rw(void *handle, int cmdsize, uint8_t *buf)
+{
+       struct nulink_usb_handle_s *h = handle;
+       int starttime = GetTickCount(), cmdid;
+       assert(handle != NULL);
+
+       jtag_libusb_interrupt_write(h->fd, h->tx_ep, (char *)h->cmdbuf, 
NULINK_HID_MAX_SIZE,
+               NULINK_WRITE_TIMEOUT);
+#if (SHOW_BUFFER)
+       char bufname[20] = "cmd transferred";
+       print64bytesbuffercontent(bufname, h->cmdbuf, NULINK_HID_MAX_SIZE);
+#endif
+       do {
+               jtag_libusb_interrupt_read(h->fd, h->rx_ep, (char *)buf,
+                       NULINK_HID_MAX_SIZE, NULINK_READ_TIMEOUT);
+#if (SHOW_BUFFER)
+               char bufname1[20] = "data received";
+               print64bytesbuffercontent(bufname1, buf, NULINK_HID_MAX_SIZE);
+#endif
+               if(GetTickCount() - starttime > USBCMD_TIMEOUT)
+               {
+                       break;
+               }
+               cmdid = h->cmdbuf[2];
+       } while ((h->cmdbuf[0] != (buf[0] & 0x7F)) ||
+                       (cmdsize != buf[1]) ||
+                       (cmdid != 0xff && cmdid != CMD_WRITE_REG && cmdid != 
CMD_WRITE_RAM &&
+                        cmdid != CMD_CHECK_MCU_STOP  && cmdid != buf[2]));
+
+       return ERROR_OK;
+}
+
+static int nulink2_usb_xfer_rw(void *handle, int cmdsize, uint8_t *buf)
+{
+       struct nulink_usb_handle_s *h = handle;
+       int starttime = GetTickCount(), cmdid;
+       assert(handle != NULL);
+
+       jtag_libusb_interrupt_write(h->fd, h->tx_ep, (char *)h->cmdbuf, 
NULINK2_HID_MAX_SIZE,
+               NULINK_WRITE_TIMEOUT);
+#if (SHOW_BUFFER)
+       char bufname[20] = "cmd transferred";
+       print64bytesbuffercontent(bufname, h->cmdbuf, NULINK2_HID_MAX_SIZE);
+#endif
+       do {
+               jtag_libusb_interrupt_read(h->fd, h->rx_ep, (char *)buf,
+                       NULINK2_HID_MAX_SIZE, NULINK_READ_TIMEOUT);
+#if (SHOW_BUFFER)
+               char bufname1[20] = "data received";
+               print64bytesbuffercontent(bufname1, buf, NULINK2_HID_MAX_SIZE);
+#endif 
+               if(GetTickCount() - starttime > USBCMD_TIMEOUT)
+               {
+                       break;
+               }
+               cmdid = h->cmdbuf[3];
+       } while ((h->cmdbuf[0] != (buf[0] & 0x7F)) || 
+                       (cmdsize != (((int)buf[1]) << 8) + ((int)buf[2] & 
0xFF)) ||
+                       (cmdid != 0xff && cmdid != CMD_WRITE_REG && cmdid != 
CMD_WRITE_RAM &&
+                        cmdid != CMD_CHECK_MCU_STOP && cmdid != buf[3]));
+
+       return ERROR_OK;
+}
+
+static int nulink_usb_xfer(void *handle, uint8_t *buf, int size)
+{
+       int err;
+       struct nulink_usb_handle_s *h = handle;
+
+       assert(handle != NULL);
+
+       err = nulink_usb_xfer_rw(h, size, h->tempbuf);
+       memcpy(buf, h->tempbuf + 2, V6M_MAX_COMMAND_LENGTH);
+
+       return err;
+}
+
+static int nulink2_usb_xfer(void *handle, uint8_t *buf, int size)
+{
+       int err;
+       struct nulink_usb_handle_s *h = handle;
+
+       assert(handle != NULL);
+
+       err = nulink2_usb_xfer_rw(h, size, h->tempbuf);
+       memcpy(buf, h->tempbuf + 3, V7M_MAX_COMMAND_LENGTH);
+
+       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 void nulink2_usb_init_buffer(void *handle, uint32_t size)
+{
+       struct nulink_usb_handle_s *h = handle;
+
+       h->cmdidx = 0;
+
+       memset(h->cmdbuf, 0, NULINK2_HID_MAX_SIZE);
+       memset(h->tempbuf, 0, NULINK2_HID_MAX_SIZE);
+       memset(h->databuf, 0, NULINK2_HID_MAX_SIZE);
+
+       h->cmdbuf[0] = (char)(++h->usbcmdidx & (unsigned char)0x7F);
+       h->cmdbuf[1] = (char)((size >> 8) & 0xFF);
+       h->cmdbuf[2] = (char)(size & 0xFF);
+       h->cmdidx += 3;
+}
+
+static int nulink_usb_version(void *handle)
+{
+       int res;
+       struct nulink_usb_handle_s *h = handle;
+
+       LOG_DEBUG("nulink_usb_version");
+
+       assert(handle != NULL);
+
+       m_nulink_usb_api.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 = m_nulink_usb_api.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 is_nulink_pro = ((le_to_h_u32(h->databuf + 4 * 2) & 1) ? true : 
false);
+
+       if (is_nulink_pro)
+       {
+               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_DEBUG("NULINK is Normal Nu-Link");
+       }       
+
+       return ERROR_OK;
+}
+
+static int nulink_usb_assert_srst(void *handle, int srst);
+
+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);
+
+       m_nulink_usb_api.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 = m_nulink_usb_api.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);
+
+       m_nulink_usb_api.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 = m_nulink_usb_api.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 is not supported");
+
+       return ERROR_OK;
+}
+
+static enum target_state nulink_usb_state(void *handle)
+{
+       int res;
+       struct nulink_usb_handle_s *h = handle;
+
+       assert(handle != NULL);
+
+       m_nulink_usb_api.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 = m_nulink_usb_api.nulink_usb_xfer(handle, h->databuf, 4 * 3);
+
+       if (res != ERROR_OK)
+               return TARGET_UNKNOWN;
+
+       if (le_to_h_u32(h->databuf + 4 * 2) == 0) {
+       /*      
+               LOG_DEBUG("NULINK  MCU is stopping");
+               LOG_DEBUG("NULINK  stop_pc(0x%08x)", le_to_h_u32(h->databuf + 4 
* 1));
+       */
+               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);
+       
+       m_nulink_usb_api.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 = m_nulink_usb_api.nulink_usb_xfer(handle, h->databuf, 4 * 1);
+
+       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);
+
+       m_nulink_usb_api.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_AUTO);
+       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 = m_nulink_usb_api.nulink_usb_xfer(handle, h->databuf, 4 * 1);
+
+       return res;
+}
+
+int nulink_usb_M2351_erase()
+{
+       int res = ERROR_FAIL;
+       struct nulink_usb_handle_s *h = m_nulink_usb_handle;
+
+       LOG_DEBUG("nulink_usb_M2351_erase");
+
+       if (m_nulink_usb_handle != NULL) {
+               /* SET_CONFIG for M2351 */
+               m_nulink_usb_api.nulink_usb_init_buffer(m_nulink_usb_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, 1000);
+               h->cmdidx += 4;
+               /* chip type: NUC_CHIP_TYPE_M2351 */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, 0x321);
+               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;
+
+               m_nulink_usb_api.nulink_usb_xfer(m_nulink_usb_handle, 
h->databuf, 4 * 3);
+               
+               /* Erase whole chip */
+               m_nulink_usb_api.nulink_usb_init_buffer(m_nulink_usb_handle, 4 
* 6);
+               /* set command ID */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, CMD_ERASE_FLASHCHIP);
+               h->cmdidx += 4;
+               /* set count */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, 0);
+               h->cmdidx += 4;
+               /* set config 0 */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, 0xFFFFFFFF);
+               h->cmdidx += 4;
+               /* set config 1 */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, 0xFFFFFFFF);
+               h->cmdidx += 4;
+               /* set config 2 */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, 0xFFFFFFFF);
+               h->cmdidx += 4;
+               /* set config 3 */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, 0xFFFFFFFF);
+               h->cmdidx += 4;
+
+               res = m_nulink_usb_api.nulink_usb_xfer(m_nulink_usb_handle, 
h->databuf, 4 * 1);
+       }
+       else {
+               LOG_DEBUG("m_nulink_usb_handle not found");
+       }
+       
+       return res;
+}
+
+int nulink_usb_assert_reset()
+{
+       int res;
+       struct nulink_usb_handle_s *h = m_nulink_usb_handle;
+
+       LOG_DEBUG("nulink_usb_assert_reset");
+       
+       m_nulink_usb_api.nulink_usb_init_buffer(m_nulink_usb_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 = m_nulink_usb_api.nulink_usb_xfer(m_nulink_usb_handle, h->databuf, 
4 * 1);
+
+       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);
+
+       m_nulink_usb_api.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 = m_nulink_usb_api.nulink_usb_xfer(handle, h->databuf, 4 * 1);
+
+       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);
+
+       m_nulink_usb_api.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 = m_nulink_usb_api.nulink_usb_xfer(handle, h->databuf, 4 * 2);
+
+       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);
+
+       m_nulink_usb_api.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 = m_nulink_usb_api.nulink_usb_xfer(handle, h->databuf, 4 * 2);
+
+       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);
+
+       m_nulink_usb_api.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 = m_nulink_usb_api.nulink_usb_xfer(handle, h->databuf, 4 * 2);
+
+       *val = le_to_h_u32(h->databuf + 4 * 1);
+       /*
+       LOG_DEBUG("NULINK read_reg(%d): 0x%08x",
+               num,
+               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);
+
+       m_nulink_usb_api.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 = m_nulink_usb_api.nulink_usb_xfer(handle, h->databuf, 4 * 2);
+
+       /*
+       LOG_DEBUG("NULINK write_reg(%d): %d",
+               num,
+               val);
+       */
+
+       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 = 4;
+       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 dose not follow 
alignment. 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 /* len == 4 */
+                       count = 2;
+
+               m_nulink_usb_api.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 = m_nulink_usb_api.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 * 1, h->databuf + 4 * (2 * 1 
+ 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 dose not follow 
alignment. 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;
+
+               m_nulink_usb_api.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 { /* len == 2 */
+                                               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 { /* len == 2 */
+                                               h_u32_to_le(h->cmdbuf + 
h->cmdidx, (unsigned long)0x0000FFFF);
+                                               
LOG_DEBUG("nulink_usb_write_mem8: count(%d), mask: 0x0000FFFF", i);
+                                       }
+                               }
+                       }
+                       else { /* i == 1 */
+                               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;
+
+                       /*
+                       LOG_DEBUG("NULINK write_ram(0x%08x): 0x%04x",
+                               addr,
+                               u32bufferdata);
+                       */
+
+                       /* proceed to the next one */
+                       addr += 4;
+                       buffer += 4;
+               }
+
+               res = m_nulink_usb_api.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;
+
+       /* LOG_DEBUG("nulink_usb_read_mem32: addr(0x%08x), len(%d)", addr, 
len); */
+
+       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;
+
+               m_nulink_usb_api.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 = m_nulink_usb_api.nulink_usb_xfer(handle, h->databuf, 4 * 
count * 2);
+
+               /* fill in the output buffer */
+               for (i = 0; i < count; i++) {
+                       memcpy(buffer, h->databuf + 4 * (2 * i + 1), 4);
+                       buffer += 4;
+                       /*
+                       LOG_DEBUG("NULINK read_ram(0x%08x): 0x%08x",
+                               addr - 4,
+                               le_to_h_u32(buffer - 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;
+
+       /* LOG_DEBUG("nulink_usb_write_mem32: addr(0x%08x), len(%d)", addr, 
len); */
+
+       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;
+
+               m_nulink_usb_api.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;
+                       
+                       /*
+                       LOG_DEBUG("NULINK write_ram(0x%08x): 0x%04x",
+                               addr,
+                               u32bufferdata);
+                       */
+
+                       /* proceed to the next one */
+                       addr += 4;
+                       buffer += 4;
+               }
+               
+               res = m_nulink_usb_api.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;
+
+       /* LOG_DEBUG("nulink_max_block_size: %d", max_tar_block); */
+
+       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;
+
+       /* LOG_DEBUG("nulink_usb_read_mem: addr(%04x), size(%d), count(%d)", 
addr, size, count); */
+
+       /* 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;
+
+               if (bytes_remaining >= 4)
+                       size = 4;
+               
+               /* 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, 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;
+
+       /* LOG_DEBUG("nulink_usb_read_mem: addr(%04x), size(%d), count(%d)", 
addr, size, count); */
+
+       if (addr < ARM_SRAM_BASE) {
+               LOG_DEBUG("since the address is below ARM_SRAM_BASE, the 
function may not support this kind of writing.");
+       }
+
+       /* 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;
+
+               if (bytes_remaining >= 4)
+                       size = 4;
+               
+               /* 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, 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) {
+               m_nulink_usb_api.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;
+
+               m_nulink_usb_api.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
+                               );
+               }               
+               /* wait for NUC505 IBR operations */
+               Sleep(50);
+       }
+
+       return max_ice_clock;
+}
+
+static int nulink_usb_close(void *handle)
+{
+       int res;
+       struct nulink_usb_handle_s *h = handle;
+
+       LOG_DEBUG("nulink_usb_close");
+
+       if (handle != NULL) {
+               LOG_DEBUG("trying to disconnect with nulink");
+               m_nulink_usb_api.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_NONE_NULINK);
+               h->cmdidx += 4;
+               /* set connect type */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, CONNECT_DISCONNECT);
+               h->cmdidx += 4;
+               /* set extMode */
+               h_u32_to_le(h->cmdbuf + h->cmdidx, 0);
+               h->cmdidx += 4;
+
+               res = m_nulink_usb_api.nulink_usb_xfer(handle, h->databuf, 4 * 
1);
+       }
+
+       return ERROR_OK;
+}
+
+static int nulink_usb_open(struct hl_interface_param_s *param, void **fd)
+{
+       int err, retry_count = 1, result = 0;
+       struct nulink_usb_handle_s *h;
+
+       LOG_DEBUG("nulink_usb_open");
+
+       m_nulink_usb_handle = NULL;     
+       h = calloc(1, sizeof(struct nulink_usb_handle_s));
+
+       if (h == 0) {
+               LOG_ERROR("malloc failed");
+               return ERROR_FAIL;
+       }
+
+       h->transport = param->transport;
+
+       const uint16_t vid_nulink2[] = { 0x0416, 0 };
+       const uint16_t pid_nulink2[] = { 0x5200, 0 };
+       const char *serial = param->serial;
+
+
+       for (unsigned i = 0; param->vid[i]; i++) {
+               LOG_DEBUG("transport: %d vid: 0x%04x pid: 0x%04x serial: %s",
+                         param->transport, param->vid[i], param->pid[i],
+                         param->serial ? param->serial : "");
+       }
+       
+       do {
+               /* get the Nu-Link version */
+               if (jtag_libusb_open(vid_nulink2, pid_nulink2, serial, &h->fd) 
== ERROR_OK) {
+                       h->hardwareconfig = (h->hardwareconfig & 
~(HARDWARECONFIG_NULINK2)) | HARDWARECONFIG_NULINK2;
+                       m_nulink_usb_api.nulink_usb_xfer = nulink2_usb_xfer;
+                       m_nulink_usb_api.nulink_usb_init_buffer = 
nulink2_usb_init_buffer;
+                       h->interface_num = NULINK2_INTERFACE_NUM;
+                       h->rx_ep = NULINK2_RX_EP;
+                       h->tx_ep = NULINK2_TX_EP;
+                       LOG_INFO("NULINK is Nu-Link2");
+               }
+               else {
+                       if (jtag_libusb_open(param->vid, param->pid, serial, 
&h->fd) != ERROR_OK) {
+                               LOG_ERROR("open failed");
+                               goto error_open;
+                       }
+
+                       m_nulink_usb_api.nulink_usb_xfer = nulink_usb_xfer;
+                       m_nulink_usb_api.nulink_usb_init_buffer = 
nulink_usb_init_buffer;
+                       h->interface_num = NULINK_INTERFACE_NUM;
+                       h->rx_ep = NULINK_RX_EP;
+                       h->tx_ep = NULINK_TX_EP;
+                       LOG_INFO("NULINK is Nu-Link1");
+               }
+
+               LOG_DEBUG("jtag_libusb_open succeeded");
+
+               jtag_libusb_set_configuration(h->fd, 0);
+
+               err = jtag_libusb_detach_kernel_driver(h->fd, h->interface_num);
+               if (err != ERROR_OK) {
+                       LOG_DEBUG("detach kernel driver failed(%d)", err);
+               }
+               else {
+                       LOG_DEBUG("jtag_libusb_detach_kernel_driver succeeded");
+               }
+
+               err = jtag_libusb_claim_interface(h->fd, h->interface_num);
+               if (err != ERROR_OK) {
+                       LOG_ERROR("claim interface failed(%d)", err);
+                       goto error_open;
+               }
+               else {
+                       LOG_DEBUG("jtag_libusb_claim_interface succeeded");
+               }
+
+               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);
+                       retry_count--;
+               }
+       } while (1);
+
+       /* SWD clock rate : 1MHz */
+       nulink_speed(h, 1000, false);
+       
+       LOG_DEBUG("nulink_usb_open: we manually perform nulink_usb_reset");
+       nulink_usb_write_debug_reg(h, 0xe000edf0, 0xa05f0001);
+       nulink_usb_reset(h);
+
+       /* get cpuid, so we can determine the max page size
+       * start with a safe default for Cortex-M0*/
+       h->max_mem_packet = (1 << 10);
+
+       uint8_t buffer[4];
+       err = nulink_usb_read_mem32(h, CPUID, 4, buffer);
+       if (err == ERROR_OK) {
+               uint32_t cpuid = le_to_h_u32(buffer);
+               int i;
+
+               if (((cpuid >> 4) & 0xfff) == 0xD20 || ((cpuid >> 4) & 0xfff) 
== 0xD21) {
+                       i = 23;
+               }
+               else {
+                       i = (cpuid >> 4) & 0xf;
+               }
+
+               if (i == 4 || i == 3 || i == 23) {
+                       /* Cortex-M3/M4/M23 has 4096 bytes autoincrement range 
*/
+                       h->max_mem_packet = (1 << 12);
+               }
+       }
+
+       LOG_DEBUG("max page size: %" PRIu32, h->max_mem_packet);
+
+       *fd = h;
+       m_nulink_usb_handle = h;
+       
+       return ERROR_OK;
+
+error_open:
+       if (h && h->fd)
+               jtag_libusb_close(h->fd);
+
+       free(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)
+{
+       /* 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_interface.c b/src/jtag/hla/hla_interface.c
index 2abed21..744df2c 100644
--- a/src/jtag/hla/hla_interface.c
+++ b/src/jtag/hla/hla_interface.c
@@ -294,6 +294,32 @@ COMMAND_HANDLER(hl_interface_handle_vid_pid_command)
        return ERROR_OK;
 }
 
+COMMAND_HANDLER(hl_interface_handle_reset_command)
+{
+       LOG_DEBUG("hl_interface_handle_reset_command");
+
+       if (CMD_ARGC != 0) {
+               LOG_ERROR("Do not need any argument");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       if (hl_if.layout->api->assert_srst)
+               return hl_if.layout->api->assert_srst(hl_if.handle, 0);
+}
+
+COMMAND_HANDLER(hl_interface_handle_close_command)
+{
+       LOG_DEBUG("hl_interface_handle_close_command");
+
+       if (CMD_ARGC != 0) {
+               LOG_ERROR("Do not need any argument");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       if (hl_if.layout->api->close)
+               return hl_if.layout->api->close(hl_if.handle);
+}
+
 COMMAND_HANDLER(interface_handle_hla_command)
 {
        if (CMD_ARGC != 1)
@@ -339,6 +365,20 @@ static const struct command_registration 
hl_interface_command_handlers[] = {
         .usage = "(vid pid)* ",
         },
         {
+        .name = "hla_reset",
+        .handler = &hl_interface_handle_reset_command,
+        .mode = COMMAND_EXEC,
+        .help = "reset hl interface",
+        .usage = "",
+       },
+       {
+        .name = "hla_close",
+        .handler = &hl_interface_handle_close_command,
+        .mode = COMMAND_EXEC,
+        .help = "close hl interface",
+        .usage = "",
+       },
+        {
         .name = "hla_command",
         .handler = &interface_handle_hla_command,
         .mode = COMMAND_EXEC,
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 9f41b59..a3c60cf 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 {
diff --git a/tcl/interface/nulink.cfg b/tcl/interface/nulink.cfg
new file mode 100755
index 0000000..7e636e1
--- /dev/null
+++ b/tcl/interface/nulink.cfg
@@ -0,0 +1,8 @@
+#
+# Nuvoton Nu-Link in-circuit debugger/programmer
+#
+
+interface hla
+hla_layout nulink
+hla_device_desc "Nu-Link"
+hla_vid_pid 0x0416 0x511b 0x0416 0x511c 0x0416 0x511d

-- 


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

Reply via email to