+/***************************************************************************/
+/* Queue command implementations */
+
+static void jlink_end_state(tap_state_t state)
+{
+ if (tap_is_state_stable(state))
+ tap_set_end_state(state);
+ else {
+ LOG_ERROR("BUG: %i is not a valid end state", state);
+ exit(-1);
+ }
+}
+
+/* Goes to the end state. */
+static void jlink_state_move(void)
+{
+ uint8_t tms_scan;
+ uint8_t tms_scan_bits;
+
+ 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());
+
+ jlink_clock_data(NULL, 0, &tms_scan, 0, NULL, 0, tms_scan_bits);
+
+ tap_set_state(tap_get_end_state());
+}
+
+static void jlink_path_move(int num_states, tap_state_t *path)
+{
+ int i;
+ uint8_t tms = 0xff;
+
+ for (i = 0; i < num_states; i++) {
+ if (path[i] == tap_state_transition(tap_get_state(), false))
+ jlink_clock_data(NULL, 0, NULL, 0, NULL, 0, 1);
+ else if (path[i] == tap_state_transition(tap_get_state(), true))
+ jlink_clock_data(NULL, 0, &tms, 0, NULL, 0, 1);
+ 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);
+ }
+
+ tap_set_state(path[i]);
+ }
+
+ tap_set_end_state(tap_get_state());
+}
+
+static void jlink_stableclocks(int num_cycles)
+{
+ int i;
+
+ uint8_t tms = tap_get_state() == TAP_RESET;
+ /* Execute num_cycles. */
+ for (i = 0; i < num_cycles; i++)
+ jlink_clock_data(NULL, 0, &tms, 0, NULL, 0, 1);
+}
+
+static void jlink_runtest(int num_cycles)
+{
+ tap_state_t saved_end_state = tap_get_end_state();
+
+ /* 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--; */
+ }
+
+ jlink_stableclocks(num_cycles);
+
+ /* Finish in end_state. */
+ jlink_end_state(saved_end_state);
+
+ 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);
+
+ /* Signals are active low. */
+ if (srst == 0)
+ jaylink_set_reset(devh);
+
+ if (srst == 1)
+ jaylink_clear_reset(devh);
+
+ if (trst == 1)
+ jaylink_jtag_clear_trst(devh);
+
+ if (trst == 0)
+ jaylink_jtag_set_trst(devh);
+}
+
+COMMAND_HANDLER(jlink_usb_command)
+{
+ int tmp;
+
+ if (CMD_ARGC != 1) {
+ command_print(CMD_CTX, "Need exactly one argument for jlink usb.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ if (sscanf(CMD_ARGV[0], "%i", &tmp) != 1) {
+ command_print(CMD_CTX, "Invalid USB address: %s.", CMD_ARGV[0]);
+ return ERROR_FAIL;
+ }
+
+ if (tmp < JAYLINK_USB_ADDRESS_0 || tmp > JAYLINK_USB_ADDRESS_3) {
+ command_print(CMD_CTX, "Invalid USB address: %s.", CMD_ARGV[0]);
+ return ERROR_FAIL;
+ }
+
+ usb_address = tmp;
+
+ use_serial_number = false;
+ use_usb_address = true;
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(jlink_serial_command)
+{
+ int ret;
+
+ if (CMD_ARGC != 1) {
+ command_print(CMD_CTX, "Need exactly one argument for jlink serial.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ ret = jaylink_parse_serial_number(CMD_ARGV[0], &serial_number);
+
+ if (ret == JAYLINK_ERR) {
+ command_print(CMD_CTX, "Invalid serial number: %s.", CMD_ARGV[0]);
+ return ERROR_FAIL;
+ } else if (ret != JAYLINK_OK) {
+ command_print(CMD_CTX, "jaylink_parse_serial_number() failed: %s.",
+ jaylink_strerror_name(ret));
+ return ERROR_FAIL;
+ }
+
+ use_serial_number = true;
+ use_usb_address = false;
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(jlink_handle_hwstatus_command)
+{
+ int ret;
+ struct jaylink_hardware_status status;
+
+ ret = jaylink_get_hardware_status(devh, &status);
+
+ if (ret != JAYLINK_OK) {
+ command_print(CMD_CTX, "jaylink_get_hardware_status() failed: %s.",
+ jaylink_strerror_name(ret));
+ return ERROR_FAIL;
+ }
+
+ command_print(CMD_CTX, "VTarget = %u.%03u V",
+ status.target_voltage / 1000, status.target_voltage % 1000);
+
+ 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);
+
+ if (status.target_voltage < 1500)
+ command_print(CMD_CTX, "Target voltage too low. Check target power.");
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(jlink_handle_free_memory_command)
+{
+ int ret;
+ uint32_t tmp;
+
+ 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;
+ }
+
+ ret = jaylink_get_free_memory(devh, &tmp);
+
+ if (ret != JAYLINK_OK) {
+ command_print(CMD_CTX, "jaylink_get_free_memory() failed: %s.",
+ jaylink_strerror_name(ret));
+ return ERROR_FAIL;
+ }
+
+ command_print(CMD_CTX, "Device has %u bytes of free memory.", tmp);
+
+ return ERROR_OK;
+}
+
+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;
+ }
+
+ 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;
+ }
+
+ 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_target_power_command)
+{
+ int ret;
+ int enable;
+
+ if (CMD_ARGC != 1) {
+ command_print(CMD_CTX, "Need exactly one argument for jlink "
+ "targetpower.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ 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 (!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;
+ }
+
+ 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 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(struct jaylink_swo_speed speed,
+ uint32_t trace_freq)
+{
+ double min;
+ double deviation;
+ uint32_t divider;
+
+ min = fabs(1.0 - (speed.freq / ((double)trace_freq * speed.min_div)));
+
+ for (divider = speed.min_div; divider < speed.max_div; divider++) {
+ deviation = fabs(1.0 - (speed.freq / ((double)trace_freq * divider)));
+
+ if (deviation < 0.03) {
+ LOG_DEBUG("Found suitable frequency divider %u with deviation of "
+ "%.02f %%.", divider, deviation);
+ return true;
+ }
+
+ if (deviation < min)
+ min = deviation;
+ }
+
+ 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;
+ struct jaylink_swo_speed speed;
+
+ 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, &speed);
+
+ if (ret != JAYLINK_OK) {
+ LOG_ERROR("jaylink_swo_get_speeds() failed: %s.",
+ jaylink_strerror_name(ret));
+ return ERROR_FAIL;
+ }
+
+ if (!*trace_freq)
+ *trace_freq = speed.freq / speed.min_div;
+
+ if (!check_trace_freq(speed, *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)