X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Fjtag%2Fdrivers%2Fjlink.c;h=04297ca07406d1d46eb38c8b074690d85a5a9d1d;hp=9b2326bdb0918a9f3c7614b136bdcf0ea6ccef0c;hb=a6c4eb03455f6e97fc25183aae249d6ccdcbfb0f;hpb=865efd828a267992db0f2a92a731c5ce23a34236 diff --git a/src/jtag/drivers/jlink.c b/src/jtag/drivers/jlink.c index 9b2326bdb0..04297ca074 100644 --- a/src/jtag/drivers/jlink.c +++ b/src/jtag/drivers/jlink.c @@ -5,6 +5,12 @@ * Copyright (C) 2008 by Spencer Oliver * * spen@spen-soft.co.uk * * * + * Copyright (C) 2011 by Jean-Christophe PLAGNIOL-VIILARD * + * plagnioj@jcrosoft.com * + * * + * Copyright (C) 2015 by Marc Schink * + * openocd-dev@marcschink.de * + * * * 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 * @@ -18,120 +24,93 @@ * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif -#include "interface.h" -#include "commands.h" -#include "usb_common.h" - - -#define VID 0x1366 -#define PID 0x0101 - -#define JLINK_WRITE_ENDPOINT 0x02 -#define JLINK_READ_ENDPOINT 0x81 - -static unsigned int jlink_write_ep = JLINK_WRITE_ENDPOINT; -static unsigned int jlink_read_ep = JLINK_READ_ENDPOINT; -static unsigned int jlink_hw_jtag_version = 2; - -#define JLINK_USB_TIMEOUT 1000 - -// See Section 1.3.2 of the Segger JLink USB protocol manual -/* 2048 is the max value we can use here */ -//#define JLINK_TAP_BUFFER_SIZE 2048 -#define JLINK_TAP_BUFFER_SIZE 256 -//#define JLINK_TAP_BUFFER_SIZE 384 - -#define JLINK_IN_BUFFER_SIZE 2048 -#define JLINK_OUT_BUFFER_SIZE 2*2048 + 4 -#define JLINK_EMU_RESULT_BUFFER_SIZE 64 - -/* Global USB buffers */ -static uint8_t usb_in_buffer[JLINK_IN_BUFFER_SIZE]; -static uint8_t usb_out_buffer[JLINK_OUT_BUFFER_SIZE]; -static uint8_t usb_emu_result_buffer[JLINK_EMU_RESULT_BUFFER_SIZE]; - -/* Constants for JLink command */ -#define EMU_CMD_VERSION 0x01 -#define EMU_CMD_SET_SPEED 0x05 -#define EMU_CMD_GET_STATE 0x07 -#define EMU_CMD_HW_CLOCK 0xc8 -#define EMU_CMD_HW_TMS0 0xc9 -#define EMU_CMD_HW_TMS1 0xca -#define EMU_CMD_HW_JTAG2 0xce -#define EMU_CMD_HW_JTAG3 0xcf -#define EMU_CMD_GET_MAX_MEM_BLOCK 0xd4 -#define EMU_CMD_HW_RESET0 0xdc -#define EMU_CMD_HW_RESET1 0xdd -#define EMU_CMD_HW_TRST0 0xde -#define EMU_CMD_HW_TRST1 0xdf -#define EMU_CMD_GET_CAPS 0xe8 -#define EMU_CMD_GET_HW_VERSION 0xf0 - -/* bits return from EMU_CMD_GET_CAPS */ -#define EMU_CAP_GET_HW_VERSION 1 -#define EMU_CAP_GET_MAX_BLOCK_SIZE 11 - -/* max speed 12MHz v5.0 jlink */ -#define JLINK_MAX_SPEED 12000 +#include +#include + +#include +#include +#include + +#include + +static struct jaylink_context *jayctx; +static struct jaylink_device_handle *devh; +static struct jaylink_connection conn; +static struct jaylink_connection connlist[JAYLINK_MAX_CONNECTIONS]; +static enum jaylink_jtag_version jtag_command_version; +static uint8_t caps[JAYLINK_DEV_EXT_CAPS_SIZE]; + +static uint32_t serial_number; +static bool use_serial_number; +static uint8_t usb_address; +static bool use_usb_address; +static uint8_t iface = JAYLINK_TIF_JTAG; +static bool trace_enabled; + +#define JLINK_MAX_SPEED 12000 +#define JLINK_TAP_BUFFER_SIZE 2048 + +static unsigned int swd_buffer_size = JLINK_TAP_BUFFER_SIZE; + +/* 256 byte non-volatile memory */ +struct device_config { + uint8_t usb_address; + /* 0ffset 0x01 to 0x03 */ + uint8_t reserved_1[3]; + uint32_t target_power; + /* 0ffset 0x08 to 0x1f */ + uint8_t reserved_2[24]; + /* IP only for J-Link Pro */ + uint8_t ip_address[4]; + uint8_t subnet_mask[4]; + /* 0ffset 0x28 to 0x2f */ + uint8_t reserved_3[8]; + uint8_t mac_address[6]; + /* 0ffset 0x36 to 0xff */ + uint8_t reserved_4[202]; +} __attribute__ ((packed)); + +static struct device_config config; +static struct device_config tmp_config; /* Queue command functions */ static void jlink_end_state(tap_state_t state); static void jlink_state_move(void); static void jlink_path_move(int num_states, tap_state_t *path); static void jlink_runtest(int num_cycles); -static void jlink_scan(bool ir_scan, enum scan_type type, uint8_t *buffer, int scan_size, struct scan_command *command); +static void jlink_scan(bool ir_scan, enum scan_type type, uint8_t *buffer, + int scan_size, struct scan_command *command); static void jlink_reset(int trst, int srst); -static void jlink_simple_command(uint8_t command); -static int jlink_get_status(void); +static int jlink_swd_run_queue(void); +static void jlink_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data, uint32_t ap_delay_clk); +static int jlink_swd_switch_seq(enum swd_special_seq seq); /* J-Link tap buffer functions */ static void jlink_tap_init(void); static int jlink_tap_execute(void); static void jlink_tap_ensure_space(int scans, int bits); static void jlink_tap_append_step(int tms, int tdi); -static void jlink_tap_append_scan(int length, uint8_t *buffer, struct scan_command *command); - -/* Jlink lowlevel functions */ -struct jlink { - struct usb_dev_handle* usb_handle; -}; - -static struct jlink *jlink_usb_open(void); -static void jlink_usb_close(struct jlink *jlink); -static int jlink_usb_message(struct jlink *jlink, int out_length, int in_length); -static int jlink_usb_write(struct jlink *jlink, int out_length); -static int jlink_usb_read(struct jlink *jlink, int expected_size); -static int jlink_usb_read_emu_result(struct jlink *jlink); - -/* helper functions */ -static int jlink_get_version_info(void); - -#ifdef _DEBUG_USB_COMMS_ -static void jlink_debug_buffer(uint8_t *buffer, int length); -#endif +static void jlink_tap_append_scan(int length, uint8_t *buffer, + struct scan_command *command); static enum tap_state jlink_last_state = TAP_RESET; - -static struct jlink* jlink_handle; +static int queued_retval; /***************************************************************************/ /* External interface implementation */ static void jlink_execute_runtest(struct jtag_command *cmd) { - DEBUG_JTAG_IO("runtest %i cycles, end in %i", - cmd->cmd.runtest->num_cycles, - cmd->cmd.runtest->end_state); + DEBUG_JTAG_IO("runtest %i cycles, end in %i", cmd->cmd.runtest->num_cycles, + cmd->cmd.runtest->end_state); jlink_end_state(cmd->cmd.runtest->end_state); - jlink_runtest(cmd->cmd.runtest->num_cycles); } @@ -149,8 +128,7 @@ static void jlink_execute_pathmove(struct jtag_command *cmd) cmd->cmd.pathmove->num_states, cmd->cmd.pathmove->path[cmd->cmd.pathmove->num_states - 1]); - jlink_path_move(cmd->cmd.pathmove->num_states, - cmd->cmd.pathmove->path); + jlink_path_move(cmd->cmd.pathmove->num_states, cmd->cmd.pathmove->path); } static void jlink_execute_scan(struct jtag_command *cmd) @@ -166,18 +144,14 @@ static void jlink_execute_scan(struct jtag_command *cmd) scan_size = jtag_build_buffer(cmd->cmd.scan, &buffer); DEBUG_JTAG_IO("scan input, length = %d", scan_size); -#ifdef _DEBUG_USB_COMMS_ - jlink_debug_buffer(buffer, (scan_size + 7) / 8); -#endif type = jtag_scan_type(cmd->cmd.scan); - jlink_scan(cmd->cmd.scan->ir_scan, - type, buffer, scan_size, cmd->cmd.scan); + jlink_scan(cmd->cmd.scan->ir_scan, type, buffer, scan_size, cmd->cmd.scan); } static void jlink_execute_reset(struct jtag_command *cmd) { - DEBUG_JTAG_IO("reset trst: %i srst %i", - cmd->cmd.reset->trst, cmd->cmd.reset->srst); + DEBUG_JTAG_IO("reset trst: %i srst %i", cmd->cmd.reset->trst, + cmd->cmd.reset->srst); jlink_tap_execute(); jlink_reset(cmd->cmd.reset->trst, cmd->cmd.reset->srst); @@ -186,71 +160,104 @@ static void jlink_execute_reset(struct jtag_command *cmd) static void jlink_execute_sleep(struct jtag_command *cmd) { - DEBUG_JTAG_IO("sleep %i", cmd->cmd.sleep->us); + DEBUG_JTAG_IO("sleep %" PRIi32 "", cmd->cmd.sleep->us); jlink_tap_execute(); jtag_sleep(cmd->cmd.sleep->us); } -static void jlink_execute_command(struct jtag_command *cmd) +static int jlink_execute_command(struct jtag_command *cmd) { - switch (cmd->type) - { - case JTAG_RUNTEST: jlink_execute_runtest(cmd); break; - case JTAG_STATEMOVE: jlink_execute_statemove(cmd); break; - case JTAG_PATHMOVE: jlink_execute_pathmove(cmd); break; - case JTAG_SCAN: jlink_execute_scan(cmd); break; - case JTAG_RESET: jlink_execute_reset(cmd); break; - case JTAG_SLEEP: jlink_execute_sleep(cmd); break; - default: - LOG_ERROR("BUG: unknown JTAG command type encountered"); - exit(-1); + switch (cmd->type) { + case JTAG_RUNTEST: + jlink_execute_runtest(cmd); + break; + case JTAG_TLR_RESET: + jlink_execute_statemove(cmd); + break; + case JTAG_PATHMOVE: + jlink_execute_pathmove(cmd); + break; + case JTAG_SCAN: + jlink_execute_scan(cmd); + break; + case JTAG_RESET: + jlink_execute_reset(cmd); + break; + case JTAG_SLEEP: + jlink_execute_sleep(cmd); + break; + default: + LOG_ERROR("BUG: Unknown JTAG command type encountered."); + return ERROR_JTAG_QUEUE_FAILED; } + + return ERROR_OK; } static int jlink_execute_queue(void) { + int ret; struct jtag_command *cmd = jtag_command_queue; - while (cmd != NULL) - { - jlink_execute_command(cmd); + while (cmd != NULL) { + ret = jlink_execute_command(cmd); + + if (ret != ERROR_OK) + return ret; + cmd = cmd->next; } return jlink_tap_execute(); } -/* Sets speed in kHz. */ static int jlink_speed(int speed) { - int result; + int ret; + uint32_t freq; + uint16_t div; + int max_speed; - if (speed > JLINK_MAX_SPEED) - { - LOG_INFO("Ignoring speed request: %dkHz exceeds %dkHz maximum", - speed, JLINK_MAX_SPEED); - return ERROR_OK; + if (jaylink_has_cap(caps, JAYLINK_DEV_CAP_GET_SPEEDS)) { + ret = jaylink_get_speeds(devh, &freq, &div); + + if (ret != JAYLINK_OK) { + LOG_ERROR("jaylink_get_speeds() failed: %s.", + jaylink_strerror_name(ret)); + return ERROR_JTAG_DEVICE_ERROR; + } + + freq = freq / 1000; + max_speed = freq / div; + } else { + max_speed = JLINK_MAX_SPEED; } - /* check for RTCK setting */ - if (speed == 0) - speed = -1; + if (!speed) { + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_ADAPTIVE_CLOCKING)) { + LOG_ERROR("Adaptive clocking is not supported by the device."); + return ERROR_JTAG_NOT_IMPLEMENTED; + } + + speed = JAYLINK_SPEED_ADAPTIVE_CLOCKING; + } else if (speed > max_speed) { + LOG_INFO("Reduced speed from %d kHz to %d kHz (maximum).", speed, + max_speed); + speed = max_speed; + } - usb_out_buffer[0] = EMU_CMD_SET_SPEED; - usb_out_buffer[1] = (speed >> 0) & 0xff; - usb_out_buffer[2] = (speed >> 8) & 0xff; + ret = jaylink_set_speed(devh, speed); - result = jlink_usb_write(jlink_handle, 3); - if (result != 3) - { - LOG_ERROR("J-Link setting speed failed (%d)", result); + if (ret != JAYLINK_OK) { + LOG_ERROR("jaylink_set_speed() failed: %s.", + jaylink_strerror_name(ret)); return ERROR_JTAG_DEVICE_ERROR; } return ERROR_OK; } -static int jlink_speed_div(int speed, int* khz) +static int jlink_speed_div(int speed, int *khz) { *khz = speed; @@ -264,61 +271,364 @@ static int jlink_khz(int khz, int *jtag_speed) return ERROR_OK; } -static int jlink_init(void) +static bool read_device_config(struct device_config *cfg) +{ + int ret; + + ret = jaylink_read_raw_config(devh, (uint8_t *)cfg); + + if (ret != JAYLINK_OK) { + LOG_ERROR("jaylink_read_raw_config() failed: %s.", + jaylink_strerror_name(ret)); + return false; + } + + if (cfg->usb_address == 0xff) + cfg->usb_address = 0x00; + + if (cfg->target_power == 0xffffffff) + cfg->target_power = 0; + + return true; +} + +static int select_interface(void) +{ + int ret; + uint32_t interfaces; + + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_SELECT_TIF)) { + if (iface != JAYLINK_TIF_JTAG) { + LOG_ERROR("Device supports JTAG transport only."); + return ERROR_JTAG_INIT_FAILED; + } + + return ERROR_OK; + } + + ret = jaylink_get_available_interfaces(devh, &interfaces); + + if (ret != JAYLINK_OK) { + LOG_ERROR("jaylink_get_available_interfaces() failed: %s.", + jaylink_strerror_name(ret)); + return ERROR_JTAG_INIT_FAILED; + } + + if (!(interfaces & (1 << iface))) { + LOG_ERROR("Selected transport is not supported by the device."); + return ERROR_JTAG_INIT_FAILED; + } + + ret = jaylink_select_interface(devh, iface); + + if (ret < 0) { + LOG_ERROR("jaylink_select_interface() failed: %s.", + jaylink_strerror_name(ret)); + return ERROR_JTAG_INIT_FAILED; + } + + return ERROR_OK; +} + +static int jlink_register(void) { + int ret; int i; + bool handle_found; + + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_REGISTER)) + return ERROR_OK; - jlink_handle = jlink_usb_open(); + ret = jaylink_register(devh, &conn, connlist, NULL, NULL); - if (jlink_handle == 0) - { - LOG_ERROR("Cannot find jlink Interface! Please check connection and permissions."); + if (ret < 0) { + LOG_ERROR("jaylink_register() failed: %s.", + jaylink_strerror_name(ret)); + return ERROR_FAIL; + } + + handle_found = false; + + for (i = 0; i < ret; i++) { + if (connlist[i].handle == conn.handle) { + handle_found = true; + break; + } + } + + if (!handle_found) { + LOG_ERROR("Registration failed: maximum number of connections on the " + "device reached."); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +/* + * Adjust the SWD transaction buffer size depending on the free device internal + * memory. This ensures that the SWD transactions sent to the device do not + * exceed the internal memory of the device. + */ +static bool adjust_swd_buffer_size(void) +{ + int ret; + uint32_t tmp; + + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_GET_FREE_MEMORY)) + return true; + + ret = jaylink_get_free_memory(devh, &tmp); + + if (ret != JAYLINK_OK) { + LOG_ERROR("jaylink_get_free_memory() failed: %s.", + jaylink_strerror_name(ret)); + return false; + } + + if (tmp < 143) { + LOG_ERROR("Not enough free device internal memory: %u bytes.", tmp); + return false; + } + + tmp = MIN(JLINK_TAP_BUFFER_SIZE, (tmp - 16) / 2); + + if (tmp != swd_buffer_size) { + swd_buffer_size = tmp; + LOG_DEBUG("Adjusted SWD transaction buffer size to %u bytes.", + swd_buffer_size); + } + + return true; +} + +static int jlink_init(void) +{ + int ret; + struct jaylink_device **devs; + unsigned int i; + bool found_device; + uint32_t tmp; + char *firmware_version; + struct jaylink_hardware_version hwver; + struct jaylink_hardware_status hwstatus; + + ret = jaylink_init(&jayctx); + + if (ret != JAYLINK_OK) { + LOG_ERROR("jaylink_init() failed: %s.", + jaylink_strerror_name(ret)); + return ERROR_JTAG_INIT_FAILED; + } + + ret = jaylink_get_device_list(jayctx, &devs); + + if (ret < 0) { + LOG_ERROR("jaylink_get_device_list() failed: %s.", + jaylink_strerror_name(ret)); + jaylink_exit(jayctx); + return ERROR_JTAG_INIT_FAILED; + } + + found_device = false; + + if (!use_serial_number && !use_usb_address) + LOG_INFO("No device selected, using first device."); + + for (i = 0; devs[i]; i++) { + jaylink_device_get_serial_number(devs[i], &tmp); + ret = jaylink_device_get_usb_address(devs[i]); + + if (use_usb_address && usb_address != ret) + continue; + + if (use_serial_number && tmp != serial_number) + continue; + + ret = jaylink_open(devs[i], &devh); + + if (ret != JAYLINK_OK) { + LOG_ERROR("Failed to open device: %s.", jaylink_strerror_name(ret)); + continue; + } + + found_device = true; + break; + } + + jaylink_free_device_list(devs, 1); + + if (!found_device) { + LOG_ERROR("No J-Link device found."); + jaylink_exit(jayctx); return ERROR_JTAG_INIT_FAILED; } /* - * The next three instructions were added after discovering a problem while using an oscilloscope. For the V8 - * SAM-ICE dongle (and likely other j-link device variants), the reset line to the target microprocessor was found to - * cycle only intermittently during emulator startup (even after encountering the downstream reset instruction later - * in the code). This was found to create two issues: 1) In general it is a bad practice to not reset a CPU to a known - * state when starting an emulator and 2) something critical happens inside the dongle when it does the first read - * following a new USB session. Keeping the processor in reset during the first read collecting version information - * seems to prevent errant "J-Link command EMU_CMD_VERSION failed" issues. + * Be careful with changing the following initialization sequence because + * some devices are known to be sensitive regarding the order. */ - LOG_INFO("J-Link initialization started / target CPU reset initiated"); - jlink_simple_command(EMU_CMD_HW_TRST0); - jlink_simple_command(EMU_CMD_HW_RESET0); - usleep(1000); + ret = jaylink_get_firmware_version(devh, &firmware_version); + + if (ret > 0) { + LOG_INFO("%s", firmware_version); + free(firmware_version); + } else if (!ret) { + LOG_WARNING("Device responds empty firmware version string."); + } else { + LOG_ERROR("jaylink_get_firmware_version() failed: %s.", + jaylink_strerror_name(ret)); + jaylink_close(devh); + jaylink_exit(jayctx); + return ERROR_JTAG_INIT_FAILED; + } - jlink_hw_jtag_version = 2; + memset(caps, 0, JAYLINK_DEV_EXT_CAPS_SIZE); + ret = jaylink_get_caps(devh, caps); - if (jlink_get_version_info() == ERROR_OK) - { - /* attempt to get status */ - jlink_get_status(); + if (ret != JAYLINK_OK) { + LOG_ERROR("jaylink_get_caps() failed: %s.", jaylink_strerror_name(ret)); + jaylink_close(devh); + jaylink_exit(jayctx); + return ERROR_JTAG_INIT_FAILED; + } + + if (jaylink_has_cap(caps, JAYLINK_DEV_CAP_GET_EXT_CAPS)) { + ret = jaylink_get_extended_caps(devh, caps); + + if (ret != JAYLINK_OK) { + LOG_ERROR("jaylink_get_extended_caps() failed: %s.", + jaylink_strerror_name(ret)); + jaylink_close(devh); + jaylink_exit(jayctx); + return ERROR_JTAG_INIT_FAILED; + } + } + + jtag_command_version = JAYLINK_JTAG_V2; + + if (jaylink_has_cap(caps, JAYLINK_DEV_CAP_GET_HW_VERSION)) { + ret = jaylink_get_hardware_version(devh, &hwver); + + if (ret != JAYLINK_OK) { + LOG_ERROR("Failed to retrieve hardware version: %s.", + jaylink_strerror_name(ret)); + jaylink_close(devh); + jaylink_exit(jayctx); + return ERROR_JTAG_INIT_FAILED; + } + + LOG_INFO("Hardware version: %u.%02u", hwver.major, hwver.minor); + + if (hwver.major >= 5) + jtag_command_version = JAYLINK_JTAG_V3; + } + + if (iface == JAYLINK_TIF_SWD) { + /* + * Adjust the SWD transaction buffer size in case there is already + * allocated memory on the device. This happens for example if the + * memory for SWO capturing is still allocated because the software + * which used the device before has not been shut down properly. + */ + if (!adjust_swd_buffer_size()) { + jaylink_close(devh); + jaylink_exit(jayctx); + return ERROR_JTAG_INIT_FAILED; + } + } + + if (jaylink_has_cap(caps, JAYLINK_DEV_CAP_READ_CONFIG)) { + if (!read_device_config(&config)) { + LOG_ERROR("Failed to read device configuration data."); + jaylink_close(devh); + jaylink_exit(jayctx); + return ERROR_JTAG_INIT_FAILED; + } + + memcpy(&tmp_config, &config, sizeof(struct device_config)); + } + + ret = jaylink_get_hardware_status(devh, &hwstatus); + + if (ret != JAYLINK_OK) { + LOG_ERROR("jaylink_get_hardware_status() failed: %s.", + jaylink_strerror_name(ret)); + jaylink_close(devh); + jaylink_exit(jayctx); + return ERROR_JTAG_INIT_FAILED; + } + + LOG_INFO("VTarget = %u.%03u V", hwstatus.target_voltage / 1000, + hwstatus.target_voltage % 1000); + + conn.handle = 0; + conn.pid = 0; + conn.hid = 0; + conn.iid = 0; + conn.cid = 0; + + ret = jlink_register(); + + if (ret != ERROR_OK) { + jaylink_close(devh); + jaylink_exit(jayctx); + return ERROR_JTAG_INIT_FAILED; } - LOG_INFO("J-Link JTAG Interface ready"); + ret = select_interface(); + + if (ret != ERROR_OK) { + jaylink_close(devh); + jaylink_exit(jayctx); + return ret; + } jlink_reset(0, 0); jtag_sleep(3000); jlink_tap_init(); - jlink_speed(jtag_get_speed()); - /* v5/6 jlink seems to have an issue if the first tap move - * is not divisible by 8, so we send a TLR on first power up */ - for (i = 0; i < 8; i++) { - jlink_tap_append_step(1, 0); + jlink_speed(jtag_get_speed_khz()); + + if (iface == JAYLINK_TIF_JTAG) { + /* + * J-Link devices with firmware version v5 and v6 seems to have an issue + * if the first tap move is not divisible by 8, so we send a TLR on + * first power up. + */ + for (i = 0; i < 8; i++) + jlink_tap_append_step(1, 0); + + jlink_tap_execute(); } - jlink_tap_execute(); return ERROR_OK; } static int jlink_quit(void) { - jlink_usb_close(jlink_handle); + int ret; + + if (trace_enabled) { + ret = jaylink_swo_stop(devh); + + if (ret != JAYLINK_OK) + LOG_ERROR("jaylink_swo_stop() failed: %s.", + jaylink_strerror_name(ret)); + } + + if (jaylink_has_cap(caps, JAYLINK_DEV_CAP_REGISTER)) { + ret = jaylink_unregister(devh, &conn, connlist, NULL, NULL); + + if (ret < 0) + LOG_ERROR("jaylink_unregister() failed: %s.", + jaylink_strerror_name(ret)); + } + + jaylink_close(devh); + jaylink_exit(jayctx); + return ERROR_OK; } @@ -328,11 +638,8 @@ static int jlink_quit(void) static void jlink_end_state(tap_state_t state) { if (tap_is_state_stable(state)) - { tap_set_end_state(state); - } - else - { + else { LOG_ERROR("BUG: %i is not a valid end state", state); exit(-1); } @@ -343,11 +650,13 @@ static void jlink_state_move(void) { int i; int tms = 0; - uint8_t tms_scan = tap_get_tms_path(tap_get_state(), tap_get_end_state()); - uint8_t tms_scan_bits = tap_get_tms_path_len(tap_get_state(), tap_get_end_state()); + uint8_t tms_scan; + uint8_t tms_scan_bits; - for (i = 0; i < tms_scan_bits; i++) - { + tms_scan = tap_get_tms_path(tap_get_state(), tap_get_end_state()); + tms_scan_bits = tap_get_tms_path_len(tap_get_state(), tap_get_end_state()); + + for (i = 0; i < tms_scan_bits; i++) { tms = (tms_scan >> i) & 1; jlink_tap_append_step(tms, 0); } @@ -359,19 +668,14 @@ static void jlink_path_move(int num_states, tap_state_t *path) { int i; - for (i = 0; i < num_states; i++) - { + for (i = 0; i < num_states; i++) { if (path[i] == tap_state_transition(tap_get_state(), false)) - { jlink_tap_append_step(0, 0); - } else if (path[i] == tap_state_transition(tap_get_state(), true)) - { jlink_tap_append_step(1, 0); - } - else - { - LOG_ERROR("BUG: %s -> %s isn't a valid TAP transition", tap_state_name(tap_get_state()), tap_state_name(path[i])); + else { + LOG_ERROR("BUG: %s -> %s isn't a valid TAP transition.", + tap_state_name(tap_get_state()), tap_state_name(path[i])); exit(-1); } @@ -387,31 +691,28 @@ static void jlink_runtest(int num_cycles) tap_state_t saved_end_state = tap_get_end_state(); - jlink_tap_ensure_space(1,num_cycles + 16); + jlink_tap_ensure_space(1, num_cycles + 16); - /* only do a state_move when we're not already in IDLE */ - if (tap_get_state() != TAP_IDLE) - { + /* Only do a state_move when we're not already in IDLE. */ + if (tap_get_state() != TAP_IDLE) { jlink_end_state(TAP_IDLE); jlink_state_move(); -// num_cycles--; + /* num_cycles--; */ } - /* execute num_cycles */ + /* Execute num_cycles. */ for (i = 0; i < num_cycles; i++) - { jlink_tap_append_step(0, 0); - } - /* finish in end_state */ + /* Finish in end_state. */ jlink_end_state(saved_end_state); + if (tap_get_state() != tap_get_end_state()) - { jlink_state_move(); - } } -static void jlink_scan(bool ir_scan, enum scan_type type, uint8_t *buffer, int scan_size, struct scan_command *command) +static void jlink_scan(bool ir_scan, enum scan_type type, uint8_t *buffer, + int scan_size, struct scan_command *command) { tap_state_t saved_end_state; @@ -419,259 +720,886 @@ static void jlink_scan(bool ir_scan, enum scan_type type, uint8_t *buffer, int s saved_end_state = tap_get_end_state(); - /* Move to appropriate scan state */ + /* Move to appropriate scan state. */ jlink_end_state(ir_scan ? TAP_IRSHIFT : TAP_DRSHIFT); - /* Only move if we're not already there */ + /* Only move if we're not already there. */ if (tap_get_state() != tap_get_end_state()) jlink_state_move(); jlink_end_state(saved_end_state); - /* Scan */ + /* Scan. */ jlink_tap_append_scan(scan_size, buffer, command); - /* We are in Exit1, go to Pause */ + /* We are in Exit1, go to Pause. */ jlink_tap_append_step(0, 0); tap_set_state(ir_scan ? TAP_IRPAUSE : TAP_DRPAUSE); if (tap_get_state() != tap_get_end_state()) - { jlink_state_move(); - } } static void jlink_reset(int trst, int srst) { - LOG_DEBUG("trst: %i, srst: %i", trst, srst); + LOG_DEBUG("TRST: %i, SRST: %i.", trst, srst); - /* Signals are active low */ + /* Signals are active low. */ if (srst == 0) - { - jlink_simple_command(EMU_CMD_HW_RESET1); - } + jaylink_set_reset(devh); + if (srst == 1) - { - jlink_simple_command(EMU_CMD_HW_RESET0); - } + jaylink_clear_reset(devh); if (trst == 1) - { - jlink_simple_command(EMU_CMD_HW_TRST0); - } + jaylink_jtag_clear_trst(devh); if (trst == 0) - { - jlink_simple_command(EMU_CMD_HW_TRST1); - } + jaylink_jtag_set_trst(devh); } -static void jlink_simple_command(uint8_t command) +COMMAND_HANDLER(jlink_usb_command) { - int result; - - DEBUG_JTAG_IO("0x%02x", command); + if (CMD_ARGC != 1) { + command_print(CMD_CTX, "Need exactly one argument for jlink usb."); + return ERROR_COMMAND_SYNTAX_ERROR; + } - usb_out_buffer[0] = command; - result = jlink_usb_write(jlink_handle, 1); + if (sscanf(CMD_ARGV[0], "%" SCNd8, &usb_address) != 1) { + command_print(CMD_CTX, "Invalid USB address: %s.", CMD_ARGV[0]); + return ERROR_FAIL; + } - if (result != 1) - { - LOG_ERROR("J-Link command 0x%02x failed (%d)", command, result); + if (usb_address > JAYLINK_USB_ADDRESS_3) { + command_print(CMD_CTX, "Invalid USB address: %s.", CMD_ARGV[0]); + return ERROR_FAIL; } -} -static int jlink_get_status(void) -{ - int result; + use_serial_number = false; + use_usb_address = true; - jlink_simple_command(EMU_CMD_GET_STATE); + return ERROR_OK; +} - result = jlink_usb_read(jlink_handle, 8); - if (result != 8) - { - LOG_ERROR("J-Link command EMU_CMD_GET_STATE failed (%d)\n", result); - return ERROR_JTAG_DEVICE_ERROR; +COMMAND_HANDLER(jlink_serial_command) +{ + if (CMD_ARGC != 1) { + command_print(CMD_CTX, "Need exactly one argument for jlink serial."); + return ERROR_COMMAND_SYNTAX_ERROR; } - int vref = usb_in_buffer[0] + (usb_in_buffer[1] << 8); - LOG_INFO("Vref = %d.%d TCK = %d TDI = %d TDO = %d TMS = %d SRST = %d TRST = %d\n", \ - vref / 1000, vref % 1000, \ - usb_in_buffer[2], usb_in_buffer[3], usb_in_buffer[4], \ - usb_in_buffer[5], usb_in_buffer[6], usb_in_buffer[7]); + if (sscanf(CMD_ARGV[0], "%" SCNd32, &serial_number) != 1) { + command_print(CMD_CTX, "Invalid serial number: %s.", CMD_ARGV[0]); + return ERROR_FAIL; + } - if (vref < 1500) - LOG_ERROR("Vref too low. Check Target Power\n"); + use_serial_number = true; + use_usb_address = false; return ERROR_OK; } -static int jlink_get_version_info(void) +COMMAND_HANDLER(jlink_handle_hwstatus_command) { - int result; - int len; - uint32_t jlink_caps, jlink_max_size; + int ret; + struct jaylink_hardware_status status; - /* query hardware version */ - jlink_simple_command(EMU_CMD_VERSION); + ret = jaylink_get_hardware_status(devh, &status); - result = jlink_usb_read(jlink_handle, 2); - if (2 != result) - { - LOG_ERROR("J-Link command EMU_CMD_VERSION failed (%d)\n", result); - return ERROR_JTAG_DEVICE_ERROR; + if (ret != JAYLINK_OK) { + command_print(CMD_CTX, "jaylink_get_hardware_status() failed: %s.", + jaylink_strerror_name(ret)); + return ERROR_FAIL; } - len = buf_get_u32(usb_in_buffer, 0, 16); - if (len > JLINK_IN_BUFFER_SIZE) - { - LOG_ERROR("J-Link command EMU_CMD_VERSION impossible return length 0x%0x", len); - len = JLINK_IN_BUFFER_SIZE; - } + command_print(CMD_CTX, "VTarget = %u.%03u V", + status.target_voltage / 1000, status.target_voltage % 1000); - result = jlink_usb_read(jlink_handle, len); - if (result != len) - { - LOG_ERROR("J-Link command EMU_CMD_VERSION failed (%d)\n", result); - return ERROR_JTAG_DEVICE_ERROR; - } + command_print(CMD_CTX, "TCK = %u TDI = %u TDO = %u TMS = %u SRST = %u " + "TRST = %u", status.tck, status.tdi, status.tdo, status.tms, + status.tres, status.trst); - usb_in_buffer[result] = 0; - LOG_INFO("%s", (char *)usb_in_buffer); + if (status.target_voltage < 1500) + command_print(CMD_CTX, "Target voltage too low. Check target power."); - /* query hardware capabilities */ - jlink_simple_command(EMU_CMD_GET_CAPS); + return ERROR_OK; +} - result = jlink_usb_read(jlink_handle, 4); - if (4 != result) - { - LOG_ERROR("J-Link command EMU_CMD_GET_CAPS failed (%d)\n", result); - return ERROR_JTAG_DEVICE_ERROR; - } +COMMAND_HANDLER(jlink_handle_free_memory_command) +{ + int ret; + uint32_t tmp; - jlink_caps = buf_get_u32(usb_in_buffer, 0, 32); - LOG_INFO("JLink caps 0x%x", (unsigned)jlink_caps); + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_GET_FREE_MEMORY)) { + command_print(CMD_CTX, "Retrieval of free memory is not supported by " + "the device."); + return ERROR_OK; + } - if (jlink_caps & (1 << EMU_CAP_GET_HW_VERSION)) - { - /* query hardware version */ - jlink_simple_command(EMU_CMD_GET_HW_VERSION); + ret = jaylink_get_free_memory(devh, &tmp); - result = jlink_usb_read(jlink_handle, 4); - if (4 != result) - { - LOG_ERROR("J-Link command EMU_CMD_GET_HW_VERSION failed (%d)\n", result); - return ERROR_JTAG_DEVICE_ERROR; - } + if (ret != JAYLINK_OK) { + command_print(CMD_CTX, "jaylink_get_free_memory() failed: %s.", + jaylink_strerror_name(ret)); + return ERROR_FAIL; + } - uint32_t jlink_hw_version = buf_get_u32(usb_in_buffer, 0, 32); - uint32_t major_revision = (jlink_hw_version / 10000) % 100; - if (major_revision >= 5) - jlink_hw_jtag_version = 3; + command_print(CMD_CTX, "Device has %u bytes of free memory.", tmp); - LOG_INFO("JLink hw version %i", (int)jlink_hw_version); - } + return ERROR_OK; +} - if (jlink_caps & (1 << EMU_CAP_GET_MAX_BLOCK_SIZE)) - { - /* query hardware maximum memory block */ - jlink_simple_command(EMU_CMD_GET_MAX_MEM_BLOCK); +COMMAND_HANDLER(jlink_handle_jlink_jtag_command) +{ + int tmp; + int version; + + if (!CMD_ARGC) { + switch (jtag_command_version) { + case JAYLINK_JTAG_V2: + version = 2; + break; + case JAYLINK_JTAG_V3: + version = 3; + break; + default: + return ERROR_FAIL; + } - result = jlink_usb_read(jlink_handle, 4); - if (4 != result) - { - LOG_ERROR("J-Link command EMU_CMD_GET_MAX_MEM_BLOCK failed (%d)\n", result); - return ERROR_JTAG_DEVICE_ERROR; + command_print(CMD_CTX, "JTAG command version: %i", version); + } else if (CMD_ARGC == 1) { + if (sscanf(CMD_ARGV[0], "%i", &tmp) != 1) { + command_print(CMD_CTX, "Invalid argument: %s.", CMD_ARGV[0]); + return ERROR_COMMAND_SYNTAX_ERROR; } - jlink_max_size = buf_get_u32(usb_in_buffer, 0, 32); - LOG_INFO("JLink max mem block %i", (int)jlink_max_size); + switch (tmp) { + case 2: + jtag_command_version = JAYLINK_JTAG_V2; + break; + case 3: + jtag_command_version = JAYLINK_JTAG_V3; + break; + default: + command_print(CMD_CTX, "Invalid argument: %s.", CMD_ARGV[0]); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } else { + command_print(CMD_CTX, "Need exactly one argument for jlink jtag."); + return ERROR_COMMAND_SYNTAX_ERROR; } return ERROR_OK; } -COMMAND_HANDLER(jlink_handle_jlink_info_command) +COMMAND_HANDLER(jlink_handle_target_power_command) { - if (jlink_get_version_info() == ERROR_OK) - { - /* attempt to get status */ - jlink_get_status(); + int ret; + int enable; + + if (CMD_ARGC != 1) { + command_print(CMD_CTX, "Need exactly one argument for jlink " + "targetpower."); + return ERROR_COMMAND_SYNTAX_ERROR; } - return ERROR_OK; -} + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_SET_TARGET_POWER)) { + command_print(CMD_CTX, "Target power supply is not supported by the " + "device."); + return ERROR_OK; + } -COMMAND_HANDLER(jlink_handle_jlink_hw_jtag_command) -{ - switch (CMD_ARGC) { - case 0: - command_print(CMD_CTX, "jlink hw jtag %i", jlink_hw_jtag_version); - break; - case 1: { - int request_version = atoi(CMD_ARGV[0]); - switch (request_version) { - case 2: case 3: - jlink_hw_jtag_version = request_version; - break; - default: - return ERROR_COMMAND_SYNTAX_ERROR; - } - break; + if (!strcmp(CMD_ARGV[0], "on")) { + enable = true; + } else if (!strcmp(CMD_ARGV[0], "off")) { + enable = false; + } else { + command_print(CMD_CTX, "Invalid argument: %s.", CMD_ARGV[0]); + return ERROR_FAIL; } - default: - return ERROR_COMMAND_SYNTAX_ERROR; + + ret = jaylink_set_target_power(devh, enable); + + if (ret != JAYLINK_OK) { + command_print(CMD_CTX, "jaylink_set_target_power() failed: %s.", + jaylink_strerror_name(ret)); + return ERROR_FAIL; } return ERROR_OK; } -static const struct command_registration jlink_command_handlers[] = { - { - .name = "jlink_info", - .handler = &jlink_handle_jlink_info_command, - .mode = COMMAND_EXEC, - .help = "show jlink info", - }, - { - .name = "jlink_hw_jtag", - .handler = &jlink_handle_jlink_hw_jtag_command, - .mode = COMMAND_EXEC, - .help = "access J-Link HW JTAG command version", - .usage = "[2|3]", +static void show_config_usb_address(struct command_context *ctx) +{ + if (config.usb_address != tmp_config.usb_address) + command_print(ctx, "USB address: %u [%u]", config.usb_address, + tmp_config.usb_address); + else + command_print(ctx, "USB address: %u", config.usb_address); +} + +static void show_config_ip_address(struct command_context *ctx) +{ + if (!memcmp(config.ip_address, tmp_config.ip_address, 4)) + command_print(ctx, "IP address: %d.%d.%d.%d", + config.ip_address[3], config.ip_address[2], + config.ip_address[1], config.ip_address[0]); + else + command_print(ctx, "IP address: %d.%d.%d.%d [%d.%d.%d.%d]", + config.ip_address[3], config.ip_address[2], + config.ip_address[1], config.ip_address[0], + tmp_config.ip_address[3], tmp_config.ip_address[2], + tmp_config.ip_address[1], tmp_config.ip_address[0]); + + if (!memcmp(config.subnet_mask, tmp_config.subnet_mask, 4)) + command_print(ctx, "Subnet mask: %d.%d.%d.%d", + config.subnet_mask[3], config.subnet_mask[2], + config.subnet_mask[1], config.subnet_mask[0]); + else + command_print(ctx, "Subnet mask: %d.%d.%d.%d [%d.%d.%d.%d]", + config.subnet_mask[3], config.subnet_mask[2], + config.subnet_mask[1], config.subnet_mask[0], + tmp_config.subnet_mask[3], tmp_config.subnet_mask[2], + tmp_config.subnet_mask[1], tmp_config.subnet_mask[0]); +} + +static void show_config_mac_address(struct command_context *ctx) +{ + if (!memcmp(config.mac_address, tmp_config.mac_address, 6)) + command_print(ctx, "MAC address: %.02x:%.02x:%.02x:%.02x:%.02x:%.02x", + config.mac_address[5], config.mac_address[4], + config.mac_address[3], config.mac_address[2], + config.mac_address[1], config.mac_address[0]); + else + command_print(ctx, "MAC address: %.02x:%.02x:%.02x:%.02x:%.02x:%.02x " + "[%.02x:%.02x:%.02x:%.02x:%.02x:%.02x]", + config.mac_address[5], config.mac_address[4], + config.mac_address[3], config.mac_address[2], + config.mac_address[1], config.mac_address[0], + tmp_config.mac_address[5], tmp_config.mac_address[4], + tmp_config.mac_address[3], tmp_config.mac_address[2], + tmp_config.mac_address[1], tmp_config.mac_address[0]); +} + +static void show_config_target_power(struct command_context *ctx) +{ + const char *target_power; + const char *current_target_power; + + if (!config.target_power) + target_power = "off"; + else + target_power = "on"; + + if (!tmp_config.target_power) + current_target_power = "off"; + else + current_target_power = "on"; + + if (config.target_power != tmp_config.target_power) + command_print(ctx, "Target power supply: %s [%s]", target_power, + current_target_power); + else + command_print(ctx, "Target power supply: %s", target_power); +} + +static void show_config(struct command_context *ctx) +{ + command_print(ctx, "J-Link device configuration:"); + + show_config_usb_address(ctx); + + if (jaylink_has_cap(caps, JAYLINK_DEV_CAP_SET_TARGET_POWER)) + show_config_target_power(ctx); + + if (jaylink_has_cap(caps, JAYLINK_DEV_CAP_ETHERNET)) { + show_config_ip_address(ctx); + show_config_mac_address(ctx); + } +} + +static int poll_trace(uint8_t *buf, size_t *size) +{ + int ret; + uint32_t length; + + length = *size; + + ret = jaylink_swo_read(devh, buf, &length); + + if (ret != JAYLINK_OK) { + LOG_ERROR("jaylink_swo_read() failed: %s.", jaylink_strerror_name(ret)); + return ERROR_FAIL; + } + + *size = length; + + return ERROR_OK; +} + +static uint32_t calculate_trace_buffer_size(void) +{ + int ret; + uint32_t tmp; + + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_GET_FREE_MEMORY)) + return 0; + + ret = jaylink_get_free_memory(devh, &tmp); + + if (ret != JAYLINK_OK) { + LOG_ERROR("jaylink_get_free_memory() failed: %s.", + jaylink_strerror_name(ret)); + return ERROR_FAIL; + } + + if (tmp > 0x3fff || tmp <= 0x600) + tmp = tmp >> 1; + else + tmp = tmp - 0x400; + + return tmp & 0xffffff00; +} + +static bool check_trace_freq(uint32_t freq, uint32_t div, uint32_t trace_freq) +{ + double min; + double deviation; + + min = fabs(1.0 - (freq / ((double)trace_freq * div))); + + while (freq / div > 0) { + deviation = fabs(1.0 - (freq / ((double)trace_freq * div))); + + if (deviation < 0.03) { + LOG_DEBUG("Found suitable frequency divider %u with deviation of " + "%.02f %%.", div, deviation); + return true; + } + + if (deviation < min) + min = deviation; + + div++; + } + + LOG_ERROR("Selected trace frequency is not supported by the device. " + "Please choose a different trace frequency."); + LOG_ERROR("Maximum permitted deviation is 3.00 %%, but only %.02f %% " + "could be achieved.", min * 100); + + return false; +} + +static int config_trace(bool enabled, enum tpio_pin_protocol pin_protocol, + uint32_t port_size, unsigned int *trace_freq) +{ + int ret; + uint32_t buffer_size; + uint32_t freq; + uint32_t div; + + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_SWO)) { + LOG_ERROR("Trace capturing is not supported by the device."); + return ERROR_FAIL; + } + + if (pin_protocol != ASYNC_UART) { + LOG_ERROR("Selected pin protocol is not supported."); + return ERROR_FAIL; + } + + trace_enabled = enabled; + + ret = jaylink_swo_stop(devh); + + if (ret != JAYLINK_OK) { + LOG_ERROR("jaylink_swo_stop() failed: %s.", jaylink_strerror_name(ret)); + return ERROR_FAIL; + } + + if (!enabled) { + /* + * Adjust the SWD transaction buffer size as stopping SWO capturing + * deallocates device internal memory. + */ + if (!adjust_swd_buffer_size()) + return ERROR_FAIL; + + return ERROR_OK; + } + + buffer_size = calculate_trace_buffer_size(); + + if (!buffer_size) { + LOG_ERROR("Not enough free device memory to start trace capturing."); + return ERROR_FAIL; + } + + ret = jaylink_swo_get_speeds(devh, JAYLINK_SWO_MODE_UART, &freq, &div); + + if (ret != JAYLINK_OK) { + LOG_ERROR("jaylink_swo_get_speeds() failed: %s.", + jaylink_strerror_name(ret)); + return ERROR_FAIL; + } + + if (!*trace_freq) + *trace_freq = freq / div; + + if (!check_trace_freq(freq, div, *trace_freq)) + return ERROR_FAIL; + + LOG_DEBUG("Using %u bytes device memory for trace capturing.", buffer_size); + + ret = jaylink_swo_start(devh, JAYLINK_SWO_MODE_UART, *trace_freq, + buffer_size); + + if (ret != JAYLINK_OK) { + LOG_ERROR("jaylink_start_swo() failed: %s.", + jaylink_strerror_name(ret)); + return ERROR_FAIL; + } + + /* + * Adjust the SWD transaction buffer size as starting SWO capturing + * allocates device internal memory. + */ + if (!adjust_swd_buffer_size()) + return ERROR_FAIL; + + return ERROR_OK; +} + +COMMAND_HANDLER(jlink_handle_config_usb_address_command) +{ + uint8_t tmp; + + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_READ_CONFIG)) { + command_print(CMD_CTX, "Reading configuration is not supported by the " + "device."); + return ERROR_OK; + } + + if (!CMD_ARGC) { + show_config_usb_address(CMD_CTX); + } else if (CMD_ARGC == 1) { + if (sscanf(CMD_ARGV[0], "%" SCNd8, &tmp) != 1) { + command_print(CMD_CTX, "Invalid USB address: %s.", CMD_ARGV[0]); + return ERROR_FAIL; + } + + if (tmp > JAYLINK_USB_ADDRESS_3) { + command_print(CMD_CTX, "Invalid USB address: %u.", tmp); + return ERROR_FAIL; + } + + tmp_config.usb_address = tmp; + } else { + command_print(CMD_CTX, "Need exactly one argument for jlink config " + "usb."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + return ERROR_OK; +} + +COMMAND_HANDLER(jlink_handle_config_target_power_command) +{ + int enable; + + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_READ_CONFIG)) { + command_print(CMD_CTX, "Reading configuration is not supported by the " + "device."); + return ERROR_OK; + } + + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_SET_TARGET_POWER)) { + command_print(CMD_CTX, "Target power supply is not supported by the " + "device."); + return ERROR_OK; + } + + if (!CMD_ARGC) { + show_config_target_power(CMD_CTX); + } else if (CMD_ARGC == 1) { + if (!strcmp(CMD_ARGV[0], "on")) { + enable = true; + } else if (!strcmp(CMD_ARGV[0], "off")) { + enable = false; + } else { + command_print(CMD_CTX, "Invalid argument: %s.", CMD_ARGV[0]); + return ERROR_FAIL; + } + + tmp_config.target_power = enable; + } else { + command_print(CMD_CTX, "Need exactly one argument for jlink config " + "targetpower."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + return ERROR_OK; +} + +COMMAND_HANDLER(jlink_handle_config_mac_address_command) +{ + uint8_t addr[6]; + int i; + char *e; + const char *str; + + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_READ_CONFIG)) { + command_print(CMD_CTX, "Reading configuration is not supported by the " + "device."); + return ERROR_OK; + } + + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_ETHERNET)) { + command_print(CMD_CTX, "Ethernet connectivity is not supported by the " + "device."); + return ERROR_OK; + } + + if (!CMD_ARGC) { + show_config_mac_address(CMD_CTX); + } else if (CMD_ARGC == 1) { + str = CMD_ARGV[0]; + + if ((strlen(str) != 17) || (str[2] != ':' || str[5] != ':' || \ + str[8] != ':' || str[11] != ':' || str[14] != ':')) { + command_print(CMD_CTX, "Invalid MAC address format."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + for (i = 5; i >= 0; i--) { + addr[i] = strtoul(str, &e, 16); + str = e + 1; + } + + if (!(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5])) { + command_print(CMD_CTX, "Invalid MAC address: zero address."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (!(0x01 & addr[0])) { + command_print(CMD_CTX, "Invalid MAC address: multicast address."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + memcpy(tmp_config.mac_address, addr, sizeof(addr)); + } else { + command_print(CMD_CTX, "Need exactly one argument for jlink config " + " mac."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + return ERROR_OK; +} + +static bool string_to_ip(const char *s, uint8_t *ip, int *pos) +{ + uint8_t lip[4]; + char *e; + const char *s_save = s; + int i; + + if (!s) + return false; + + for (i = 0; i < 4; i++) { + lip[i] = strtoul(s, &e, 10); + + if (*e != '.' && i != 3) + return false; + + s = e + 1; + } + + *pos = e - s_save; + memcpy(ip, lip, sizeof(lip)); + + return true; +} + +static void cpy_ip(uint8_t *dst, uint8_t *src) +{ + int i, j; + + for (i = 0, j = 3; i < 4; i++, j--) + dst[i] = src[j]; +} + +COMMAND_HANDLER(jlink_handle_config_ip_address_command) +{ + uint8_t ip_address[4]; + uint32_t subnet_mask = 0; + int i, len; + uint8_t subnet_bits = 24; + + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_READ_CONFIG)) { + command_print(CMD_CTX, "Reading configuration is not supported by the " + "device."); + return ERROR_OK; + } + + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_ETHERNET)) { + command_print(CMD_CTX, "Ethernet connectivity is not supported by the " + "device."); + return ERROR_OK; + } + + if (!CMD_ARGC) { + show_config_ip_address(CMD_CTX); + } else { + if (!string_to_ip(CMD_ARGV[0], ip_address, &i)) + return ERROR_COMMAND_SYNTAX_ERROR; + + len = strlen(CMD_ARGV[0]); + + /* Check for format A.B.C.D/E. */ + if (i < len) { + if (CMD_ARGV[0][i] != '/') + return ERROR_COMMAND_SYNTAX_ERROR; + + COMMAND_PARSE_NUMBER(u8, CMD_ARGV[0] + i + 1, subnet_bits); + } else if (CMD_ARGC > 1) { + if (!string_to_ip(CMD_ARGV[1], (uint8_t *)&subnet_mask, &i)) + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (!subnet_mask) + subnet_mask = (uint32_t)(subnet_bits < 32 ? + ((1ULL << subnet_bits) - 1) : 0xffffffff); + + cpy_ip(tmp_config.ip_address, ip_address); + cpy_ip(tmp_config.subnet_mask, (uint8_t *)&subnet_mask); + } + + return ERROR_OK; +} + +COMMAND_HANDLER(jlink_handle_config_reset_command) +{ + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_READ_CONFIG)) + return ERROR_OK; + + memcpy(&tmp_config, &config, sizeof(struct device_config)); + + return ERROR_OK; +} + + +COMMAND_HANDLER(jlink_handle_config_write_command) +{ + int ret; + + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_READ_CONFIG)) { + command_print(CMD_CTX, "Reading configuration is not supported by the " + "device."); + return ERROR_OK; + } + + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_WRITE_CONFIG)) { + command_print(CMD_CTX, "Writing configuration is not supported by the " + "device."); + return ERROR_OK; + } + + if (!memcmp(&config, &tmp_config, sizeof(struct device_config))) { + command_print(CMD_CTX, "Operation not performed due to no changes in " + "the configuration."); + return ERROR_OK; + } + + ret = jaylink_write_raw_config(devh, (const uint8_t *)&tmp_config); + + if (ret != JAYLINK_OK) { + LOG_ERROR("jaylink_write_raw_config() failed: %s.", + jaylink_strerror_name(ret)); + return ERROR_FAIL; + } + + if (!read_device_config(&config)) { + LOG_ERROR("Failed to read device configuration for verification."); + return ERROR_FAIL; + } + + if (memcmp(&config, &tmp_config, sizeof(struct device_config))) { + LOG_ERROR("Verification of device configuration failed. Please check " + "your device."); + return ERROR_FAIL; + } + + memcpy(&tmp_config, &config, sizeof(struct device_config)); + command_print(CMD_CTX, "The new device configuration applies after power " + "cycling the J-Link device."); + + return ERROR_OK; +} + +COMMAND_HANDLER(jlink_handle_config_command) +{ + if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_READ_CONFIG)) { + command_print(CMD_CTX, "Device doesn't support reading configuration."); + return ERROR_OK; + } + + if (CMD_ARGC == 0) + show_config(CMD_CTX); + + return ERROR_OK; +} + +static const struct command_registration jlink_config_subcommand_handlers[] = { + { + .name = "usb", + .handler = &jlink_handle_config_usb_address_command, + .mode = COMMAND_EXEC, + .help = "set the USB address", + .usage = "[0-3]", + }, + { + .name = "targetpower", + .handler = &jlink_handle_config_target_power_command, + .mode = COMMAND_EXEC, + .help = "set the target power supply", + .usage = "[on|off]" + }, + { + .name = "mac", + .handler = &jlink_handle_config_mac_address_command, + .mode = COMMAND_EXEC, + .help = "set the MAC Address", + .usage = "[ff:ff:ff:ff:ff:ff]", + }, + { + .name = "ip", + .handler = &jlink_handle_config_ip_address_command, + .mode = COMMAND_EXEC, + .help = "set the IP address, where A.B.C.D is the IP address, " + "E the bit of the subnet mask, F.G.H.I the subnet mask", + .usage = "[A.B.C.D[/E] [F.G.H.I]]", + }, + { + .name = "reset", + .handler = &jlink_handle_config_reset_command, + .mode = COMMAND_EXEC, + .help = "undo configuration changes" + }, + { + .name = "write", + .handler = &jlink_handle_config_write_command, + .mode = COMMAND_EXEC, + .help = "write configuration to the device" }, COMMAND_REGISTRATION_DONE }; -struct jtag_interface jlink_interface = { +static const struct command_registration jlink_subcommand_handlers[] = { + { + .name = "jtag", + .handler = &jlink_handle_jlink_jtag_command, + .mode = COMMAND_EXEC, + .help = "select the JTAG command version", + .usage = "[2|3]", + }, + { + .name = "targetpower", + .handler = &jlink_handle_target_power_command, + .mode = COMMAND_EXEC, + .help = "set the target power supply", + .usage = "" + }, + { + .name = "freemem", + .handler = &jlink_handle_free_memory_command, + .mode = COMMAND_EXEC, + .help = "show free device memory" + }, + { + .name = "hwstatus", + .handler = &jlink_handle_hwstatus_command, + .mode = COMMAND_EXEC, + .help = "show the hardware status" + }, + { + .name = "usb", + .handler = &jlink_usb_command, + .mode = COMMAND_CONFIG, + .help = "set the USB address of the device that should be used", + .usage = "<0-3>" + }, + { + .name = "serial", + .handler = &jlink_serial_command, + .mode = COMMAND_CONFIG, + .help = "set the serial number of the device that should be used", + .usage = "" + }, + { + .name = "config", + .handler = &jlink_handle_config_command, + .mode = COMMAND_EXEC, + .help = "access the device configuration. If no argument is given " + "this will show the device configuration", + .chain = jlink_config_subcommand_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration jlink_command_handlers[] = { + { .name = "jlink", + .mode = COMMAND_ANY, + .help = "perform jlink management", + .chain = jlink_subcommand_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +static int jlink_swd_init(void) +{ + iface = JAYLINK_TIF_SWD; + + return ERROR_OK; +} + +static void jlink_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk) +{ + assert(!(cmd & SWD_CMD_RnW)); + jlink_swd_queue_cmd(cmd, NULL, value, ap_delay_clk); +} - .commands = jlink_command_handlers, +static void jlink_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_clk) +{ + assert(cmd & SWD_CMD_RnW); + jlink_swd_queue_cmd(cmd, value, 0, ap_delay_clk); +} - .execute_queue = &jlink_execute_queue, - .speed = &jlink_speed, - .speed_div = &jlink_speed_div, - .khz = &jlink_khz, +static int_least32_t jlink_swd_frequency(int_least32_t hz) +{ + if (hz > 0) + jlink_speed(hz / 1000); - .init = &jlink_init, - .quit = &jlink_quit, - }; + return hz; +} /***************************************************************************/ /* J-Link tap functions */ - -static unsigned tap_length = 0; +static unsigned tap_length; +/* In SWD mode use tms buffer for direction control */ static uint8_t tms_buffer[JLINK_TAP_BUFFER_SIZE]; static uint8_t tdi_buffer[JLINK_TAP_BUFFER_SIZE]; static uint8_t tdo_buffer[JLINK_TAP_BUFFER_SIZE]; struct pending_scan_result { - int first; /* First bit position in tdo_buffer to read */ - int length; /* Number of bits to read */ - struct scan_command *command; /* Corresponding scan command */ - uint8_t *buffer; + int first; /* First bit position in tdo_buffer to read. */ + int length; /* Number of bits to read. */ + struct scan_command *command; /* Corresponding scan command. */ + void *buffer; }; #define MAX_PENDING_SCAN_RESULTS 256 @@ -691,45 +1619,37 @@ static void jlink_tap_ensure_space(int scans, int bits) int available_bits = JLINK_TAP_BUFFER_SIZE * 8 - tap_length - 32; if (scans > available_scans || bits > available_bits) - { jlink_tap_execute(); - } } static void jlink_tap_append_step(int tms, int tdi) { - int index = tap_length / 8; + int index_var = tap_length / 8; - if (index >= JLINK_TAP_BUFFER_SIZE) - { - LOG_ERROR("jlink_tap_append_step: overflow"); - *(uint32_t *)0xFFFFFFFF = 0; - exit(-1); - } + assert(index_var < JLINK_TAP_BUFFER_SIZE); int bit_index = tap_length % 8; uint8_t bit = 1 << bit_index; - // we do not pad TMS, so be sure to initialize all bits + /* We do not pad TMS, so be sure to initialize all bits. */ if (0 == bit_index) - { - tms_buffer[index] = tdi_buffer[index] = 0; - } + tms_buffer[index_var] = tdi_buffer[index_var] = 0; if (tms) - tms_buffer[index] |= bit; + tms_buffer[index_var] |= bit; else - tms_buffer[index] &= ~bit; + tms_buffer[index_var] &= ~bit; if (tdi) - tdi_buffer[index] |= bit; + tdi_buffer[index_var] |= bit; else - tdi_buffer[index] &= ~bit; + tdi_buffer[index_var] &= ~bit; tap_length++; } -static void jlink_tap_append_scan(int length, uint8_t *buffer, struct scan_command *command) +static void jlink_tap_append_scan(int length, uint8_t *buffer, + struct scan_command *command) { struct pending_scan_result *pending_scan_result = &pending_scan_results_buffer[pending_scan_results_length]; @@ -740,346 +1660,254 @@ static void jlink_tap_append_scan(int length, uint8_t *buffer, struct scan_comma pending_scan_result->command = command; pending_scan_result->buffer = buffer; - for (i = 0; i < length; i++) - { + for (i = 0; i < length; i++) { int tms = (i < (length - 1)) ? 0 : 1; int tdi = (buffer[i / 8] & (1 << (i % 8))) != 0; jlink_tap_append_step(tms, tdi); } + pending_scan_results_length++; } -/* Pad and send a tap sequence to the device, and receive the answer. - * For the purpose of padding we assume that we are in idle or pause state. */ +/* + * Pad and send a tap sequence to the device, and receive the answer. For the + * purpose of padding we assume that we are in idle or pause state. + */ static int jlink_tap_execute(void) { - int byte_length; int i; - int result; + int ret; if (!tap_length) return ERROR_OK; - /* JLink returns an extra NULL in packet when size of incoming - * message is a multiple of 64, creates problems with USB comms. - * WARNING: This will interfere with tap state counting. */ - while ((DIV_ROUND_UP(tap_length, 8) % 64) == 0) - { - jlink_tap_append_step((tap_get_state() == TAP_RESET)?1:0, 0); - } - - // number of full bytes (plus one if some would be left over) - byte_length = DIV_ROUND_UP(tap_length, 8); - - bool use_jtag3 = jlink_hw_jtag_version >= 3; - usb_out_buffer[0] = use_jtag3 ? EMU_CMD_HW_JTAG3 : EMU_CMD_HW_JTAG2; - usb_out_buffer[1] = 0; - usb_out_buffer[2] = (tap_length >> 0) & 0xff; - usb_out_buffer[3] = (tap_length >> 8) & 0xff; - memcpy(usb_out_buffer + 4, tms_buffer, byte_length); - memcpy(usb_out_buffer + 4 + byte_length, tdi_buffer, byte_length); + jlink_last_state = jtag_debug_state_machine(tms_buffer, tdi_buffer, + tap_length, jlink_last_state); jlink_last_state = jtag_debug_state_machine(tms_buffer, tdi_buffer, - tap_length, jlink_last_state); + tap_length, jlink_last_state); - result = jlink_usb_message(jlink_handle, 4 + 2 * byte_length, byte_length); - if (result != byte_length) - { - LOG_ERROR("jlink_tap_execute, wrong result %d (expected %d)", result, byte_length); + ret = jaylink_jtag_io(devh, tms_buffer, tdi_buffer, tdo_buffer, + tap_length, jtag_command_version); + + if (ret != JAYLINK_OK) { + LOG_ERROR("jaylink_jtag_io() failed: %s.", jaylink_strerror_name(ret)); jlink_tap_init(); return ERROR_JTAG_QUEUE_FAILED; } - memcpy(tdo_buffer, usb_in_buffer, byte_length); - - for (i = 0; i < pending_scan_results_length; i++) - { + for (i = 0; i < pending_scan_results_length; i++) { struct pending_scan_result *pending_scan_result = &pending_scan_results_buffer[i]; uint8_t *buffer = pending_scan_result->buffer; int length = pending_scan_result->length; int first = pending_scan_result->first; struct scan_command *command = pending_scan_result->command; - /* Copy to buffer */ + /* Copy to buffer. */ buf_set_buf(tdo_buffer, first, buffer, 0, length); - DEBUG_JTAG_IO("pending scan result, length = %d", length); - -#ifdef _DEBUG_USB_COMMS_ - jlink_debug_buffer(buffer, DIV_ROUND_UP(length, 8)); -#endif + DEBUG_JTAG_IO("Pending scan result, length = %d.", length); - if (jtag_read_buffer(buffer, command) != ERROR_OK) - { + if (jtag_read_buffer(buffer, command) != ERROR_OK) { jlink_tap_init(); return ERROR_JTAG_QUEUE_FAILED; } if (pending_scan_result->buffer != NULL) - { free(pending_scan_result->buffer); - } } jlink_tap_init(); + return ERROR_OK; } -/*****************************************************************************/ -/* JLink USB low-level functions */ - -static struct jlink* jlink_usb_open() +static void fill_buffer(uint8_t *buf, uint32_t val, uint32_t len) { - usb_init(); - - const uint16_t vids[] = { VID, 0 }; - const uint16_t pids[] = { PID, 0 }; - struct usb_dev_handle *dev; - if (jtag_usb_open(vids, pids, &dev) != ERROR_OK) - return NULL; - - /* BE ***VERY CAREFUL*** ABOUT MAKING CHANGES IN THIS - * AREA!!!!!!!!!!! The behavior of libusb is not completely - * consistent across Windows, Linux, and Mac OS X platforms. - * The actions taken in the following compiler conditionals may - * not agree with published documentation for libusb, but were - * found to be necessary through trials and tribulations. Even - * little tweaks can break one or more platforms, so if you do - * make changes test them carefully on all platforms before - * committing them! - */ - -#if IS_WIN32 == 0 + unsigned int tap_pos = tap_length; - usb_reset(dev); - -#if IS_DARWIN == 0 - - int timeout = 5; - /* reopen jlink after usb_reset - * on win32 this may take a second or two to re-enumerate */ - int retval; - while ((retval = jtag_usb_open(vids, pids, &dev)) != ERROR_OK) - { - usleep(1000); - timeout--; - if (!timeout) { - break; - } + while (len > 32) { + buf_set_u32(buf, tap_pos, 32, val); + len -= 32; + tap_pos += 32; } - if (ERROR_OK != retval) - return NULL; -#endif -#endif + if (len) + buf_set_u32(buf, tap_pos, len, val); +} - /* usb_set_configuration required under win32 */ - struct usb_device *udev = usb_device(dev); - usb_set_configuration(dev, udev->config[0].bConfigurationValue); - usb_claim_interface(dev, 0); +static void jlink_queue_data_out(const uint8_t *data, uint32_t len) +{ + const uint32_t dir_out = 0xffffffff; -#if 0 - /* - * This makes problems under Mac OS X. And is not needed - * under Windows. Hopefully this will not break a linux build - */ - usb_set_altinterface(result->usb_handle, 0); -#endif - struct usb_interface *iface = udev->config->interface; - struct usb_interface_descriptor *desc = iface->altsetting; - for (int i = 0; i < desc->bNumEndpoints; i++) - { - uint8_t epnum = desc->endpoint[i].bEndpointAddress; - bool is_input = epnum & 0x80; - LOG_DEBUG("usb ep %s %02x", is_input ? "in" : "out", epnum); - if (is_input) - jlink_read_ep = epnum; - else - jlink_write_ep = epnum; - } - - struct jlink *result = malloc(sizeof(struct jlink)); - result->usb_handle = dev; - return result; + if (data) + bit_copy(tdi_buffer, tap_length, data, 0, len); + else + fill_buffer(tdi_buffer, 0, len); + + fill_buffer(tms_buffer, dir_out, len); + tap_length += len; } -static void jlink_usb_close(struct jlink *jlink) +static void jlink_queue_data_in(uint32_t len) { - usb_close(jlink->usb_handle); - free(jlink); + const uint32_t dir_in = 0; + + fill_buffer(tms_buffer, dir_in, len); + tap_length += len; } -/* Send a message and receive the reply. */ -static int jlink_usb_message(struct jlink *jlink, int out_length, int in_length) +static int jlink_swd_switch_seq(enum swd_special_seq seq) { - int result; - - result = jlink_usb_write(jlink, out_length); - if (result != out_length) - { - LOG_ERROR("usb_bulk_write failed (requested=%d, result=%d)", - out_length, result); - return ERROR_JTAG_DEVICE_ERROR; + const uint8_t *s; + unsigned int s_len; + + switch (seq) { + case LINE_RESET: + LOG_DEBUG("SWD line reset"); + s = swd_seq_line_reset; + s_len = swd_seq_line_reset_len; + break; + case JTAG_TO_SWD: + LOG_DEBUG("JTAG-to-SWD"); + s = swd_seq_jtag_to_swd; + s_len = swd_seq_jtag_to_swd_len; + break; + case SWD_TO_JTAG: + LOG_DEBUG("SWD-to-JTAG"); + s = swd_seq_swd_to_jtag; + s_len = swd_seq_swd_to_jtag_len; + break; + default: + LOG_ERROR("Sequence %d not supported.", seq); + return ERROR_FAIL; } - result = jlink_usb_read(jlink, in_length); - if ((result != in_length) && (result != (in_length + 1))) - { - LOG_ERROR("usb_bulk_read failed (requested=%d, result=%d)", - in_length, result); - return ERROR_JTAG_DEVICE_ERROR; - } + jlink_queue_data_out(s, s_len); - if (jlink_hw_jtag_version < 3) - return result; + return ERROR_OK; +} - int result2 = ERROR_OK; - if (result == in_length) - { - /* Must read the result from the EMU too */ - result2 = jlink_usb_read_emu_result(jlink); - if (1 != result2) - { - LOG_ERROR("jlink_usb_read_emu_result retried requested = 1, result=%d, in_length=%i", result2,in_length); - /* Try again once, should only happen if (in_length%64 == 0) */ - result2 = jlink_usb_read_emu_result(jlink); - if (1 != result2) - { - LOG_ERROR("jlink_usb_read_emu_result failed " - "(requested = 1, result=%d)", result2); - return ERROR_JTAG_DEVICE_ERROR; - } - } +static int jlink_swd_run_queue(void) +{ + int i; + int ret; - /* Check the result itself */ - result2 = usb_emu_result_buffer[0]; - } - else - { - /* Save the result, then remove it from return value */ - result2 = usb_in_buffer[result--]; - } + LOG_DEBUG("Executing %d queued transactions.", pending_scan_results_length); - if (result2) - { - LOG_ERROR("jlink_usb_message failed with result=%d)", result2); - return ERROR_JTAG_DEVICE_ERROR; + if (queued_retval != ERROR_OK) { + LOG_DEBUG("Skipping due to previous errors: %d.", queued_retval); + goto skip; } - return result; -} + /* + * A transaction must be followed by another transaction or at least 8 idle + * cycles to ensure that data is clocked through the AP. + */ + jlink_queue_data_out(NULL, 8); -/* calls the given usb_bulk_* function, allowing for the data to trickle in with some timeouts */ -static int usb_bulk_with_retries( - int (*f)(usb_dev_handle *, int, char *, int, int), - usb_dev_handle *dev, int ep, - char *bytes, int size, int timeout) -{ - int tries = 3, count = 0; + ret = jaylink_swd_io(devh, tms_buffer, tdi_buffer, tdo_buffer, tap_length); - while (tries && (count < size)) - { - int result = f(dev, ep, bytes + count, size - count, timeout); - if (result > 0) - count += result; - else if ((-ETIMEDOUT != result) || !--tries) - return result; + if (ret != JAYLINK_OK) { + LOG_ERROR("jaylink_swd_io() failed: %s.", jaylink_strerror_name(ret)); + goto skip; } - return count; -} -static int wrap_usb_bulk_write(usb_dev_handle *dev, int ep, - char *buff, int size, int timeout) -{ - /* usb_bulk_write() takes const char *buff */ - return usb_bulk_write(dev, ep, buff, size, timeout); -} + for (i = 0; i < pending_scan_results_length; i++) { + int ack = buf_get_u32(tdo_buffer, pending_scan_results_buffer[i].first, 3); + + if (ack != SWD_ACK_OK) { + LOG_DEBUG("SWD ack not OK: %d %s", ack, + ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK"); + queued_retval = ack == SWD_ACK_WAIT ? ERROR_WAIT : ERROR_FAIL; + goto skip; + } else if (pending_scan_results_buffer[i].length) { + uint32_t data = buf_get_u32(tdo_buffer, 3 + pending_scan_results_buffer[i].first, 32); + int parity = buf_get_u32(tdo_buffer, 3 + 32 + pending_scan_results_buffer[i].first, 1); + + if (parity != parity_u32(data)) { + LOG_ERROR("SWD: Read data parity mismatch."); + queued_retval = ERROR_FAIL; + goto skip; + } -static inline int usb_bulk_write_ex(usb_dev_handle *dev, int ep, - char *bytes, int size, int timeout) -{ - return usb_bulk_with_retries(&wrap_usb_bulk_write, - dev, ep, bytes, size, timeout); -} + if (pending_scan_results_buffer[i].buffer) + *(uint32_t *)pending_scan_results_buffer[i].buffer = data; + } + } -static inline int usb_bulk_read_ex(usb_dev_handle *dev, int ep, - char *bytes, int size, int timeout) -{ - return usb_bulk_with_retries(&usb_bulk_read, - dev, ep, bytes, size, timeout); +skip: + jlink_tap_init(); + ret = queued_retval; + queued_retval = ERROR_OK; + + return ret; } -/* Write data from out_buffer to USB. */ -static int jlink_usb_write(struct jlink *jlink, int out_length) +static void jlink_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data, uint32_t ap_delay_clk) { - int result; - - if (out_length > JLINK_OUT_BUFFER_SIZE) - { - LOG_ERROR("jlink_write illegal out_length=%d (max=%d)", out_length, JLINK_OUT_BUFFER_SIZE); - return -1; + uint8_t data_parity_trn[DIV_ROUND_UP(32 + 1, 8)]; + if (tap_length + 46 + 8 + ap_delay_clk >= sizeof(tdi_buffer) * 8 || + pending_scan_results_length == MAX_PENDING_SCAN_RESULTS) { + /* Not enough room in the queue. Run the queue. */ + queued_retval = jlink_swd_run_queue(); } - result = usb_bulk_write_ex(jlink->usb_handle, jlink_write_ep, - (char *)usb_out_buffer, out_length, JLINK_USB_TIMEOUT); + if (queued_retval != ERROR_OK) + return; - DEBUG_JTAG_IO("jlink_usb_write, out_length = %d, result = %d", out_length, result); + cmd |= SWD_CMD_START | SWD_CMD_PARK; -#ifdef _DEBUG_USB_COMMS_ - jlink_debug_buffer(usb_out_buffer, out_length); -#endif - return result; -} + jlink_queue_data_out(&cmd, 8); -/* Read data from USB into in_buffer. */ -static int jlink_usb_read(struct jlink *jlink, int expected_size) -{ - int result = usb_bulk_read_ex(jlink->usb_handle, jlink_read_ep, - (char *)usb_in_buffer, expected_size, JLINK_USB_TIMEOUT); + pending_scan_results_buffer[pending_scan_results_length].first = tap_length; - DEBUG_JTAG_IO("jlink_usb_read, result = %d", result); + if (cmd & SWD_CMD_RnW) { + /* Queue a read transaction. */ + pending_scan_results_buffer[pending_scan_results_length].length = 32; + pending_scan_results_buffer[pending_scan_results_length].buffer = dst; -#ifdef _DEBUG_USB_COMMS_ - jlink_debug_buffer(usb_in_buffer, result); -#endif - return result; -} + jlink_queue_data_in(1 + 3 + 32 + 1 + 1); + } else { + /* Queue a write transaction. */ + pending_scan_results_buffer[pending_scan_results_length].length = 0; + jlink_queue_data_in(1 + 3 + 1); -/* Read the result from the previous EMU cmd into result_buffer. */ -static int jlink_usb_read_emu_result(struct jlink *jlink) -{ - int result = usb_bulk_read_ex(jlink->usb_handle, jlink_read_ep, - (char *)usb_emu_result_buffer, 1 /* JLINK_EMU_RESULT_BUFFER_SIZE */, - JLINK_USB_TIMEOUT); + buf_set_u32(data_parity_trn, 0, 32, data); + buf_set_u32(data_parity_trn, 32, 1, parity_u32(data)); - DEBUG_JTAG_IO("jlink_usb_read_result, result = %d", result); + jlink_queue_data_out(data_parity_trn, 32 + 1); + } -#ifdef _DEBUG_USB_COMMS_ - jlink_debug_buffer(usb_emu_result_buffer, result); -#endif - return result; -} + pending_scan_results_length++; -#ifdef _DEBUG_USB_COMMS_ -#define BYTES_PER_LINE 16 + /* Insert idle cycles after AP accesses to avoid WAIT. */ + if (cmd & SWD_CMD_APnDP) + jlink_queue_data_out(NULL, ap_delay_clk); +} -static void jlink_debug_buffer(uint8_t *buffer, int length) -{ - char line[81]; - char s[4]; - int i; - int j; +static const struct swd_driver jlink_swd = { + .init = &jlink_swd_init, + .frequency = &jlink_swd_frequency, + .switch_seq = &jlink_swd_switch_seq, + .read_reg = &jlink_swd_read_reg, + .write_reg = &jlink_swd_write_reg, + .run = &jlink_swd_run_queue, +}; - for (i = 0; i < length; i += BYTES_PER_LINE) - { - snprintf(line, 5, "%04x", i); - for (j = i; j < i + BYTES_PER_LINE && j < length; j++) - { - snprintf(s, 4, " %02x", buffer[j]); - strcat(line, s); - } - LOG_DEBUG("%s", line); - } -} -#endif +static const char * const jlink_transports[] = { "jtag", "swd", NULL }; +struct jtag_interface jlink_interface = { + .name = "jlink", + .commands = jlink_command_handlers, + .transports = jlink_transports, + .swd = &jlink_swd, + .execute_queue = &jlink_execute_queue, + .speed = &jlink_speed, + .speed_div = &jlink_speed_div, + .khz = &jlink_khz, + .init = &jlink_init, + .quit = &jlink_quit, + .config_trace = &config_trace, + .poll_trace = &poll_trace, +};