+static int jlink_quit(void)
+{
+ int ret;
+ size_t count;
+
+ if (trace_enabled) {
+ ret = jaylink_swo_stop(devh);
+
+ if (ret != JAYLINK_OK)
+ LOG_ERROR("jaylink_swo_stop() failed: %s.", jaylink_strerror(ret));
+ }
+
+ if (jaylink_has_cap(caps, JAYLINK_DEV_CAP_REGISTER)) {
+ ret = jaylink_unregister(devh, &conn, connlist, &count);
+
+ if (ret != JAYLINK_OK)
+ LOG_ERROR("jaylink_unregister() failed: %s.",
+ jaylink_strerror(ret));
+ }
+
+ jaylink_close(devh);
+ jaylink_exit(jayctx);
+
+ return ERROR_OK;
+}
+
+/***************************************************************************/
+/* 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(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(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(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_VERSION_2:
+ version = 2;
+ break;
+ case JAYLINK_JTAG_VERSION_3:
+ 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_VERSION_2;
+ break;
+ case 3:
+ jtag_command_version = JAYLINK_JTAG_VERSION_3;
+ 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(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(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(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 tpiu_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 != TPIU_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(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(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(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(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;
+}
+
+COMMAND_HANDLER(jlink_handle_emucom_write_command)
+{
+ int ret;
+ size_t tmp;
+ uint32_t channel;
+ uint32_t length;
+ uint8_t *buf;
+ size_t dummy;
+
+ if (CMD_ARGC != 2)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_EMUCOM)) {
+ LOG_ERROR("Device does not support EMUCOM.");
+ return ERROR_FAIL;
+ }
+
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], channel);
+
+ tmp = strlen(CMD_ARGV[1]);
+
+ if (tmp % 2 != 0) {
+ LOG_ERROR("Data must be encoded as hexadecimal pairs.");
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+
+ buf = malloc(tmp / 2);
+
+ if (!buf) {
+ LOG_ERROR("Failed to allocate buffer.");
+ return ERROR_FAIL;
+ }
+
+ dummy = unhexify(buf, CMD_ARGV[1], tmp / 2);
+
+ if (dummy != (tmp / 2)) {
+ LOG_ERROR("Data must be encoded as hexadecimal pairs.");
+ free(buf);
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+
+ length = tmp / 2;
+ ret = jaylink_emucom_write(devh, channel, buf, &length);
+
+ free(buf);
+
+ if (ret == JAYLINK_ERR_DEV_NOT_SUPPORTED) {
+ LOG_ERROR("Channel not supported by the device.");
+ return ERROR_FAIL;
+ } else if (ret != JAYLINK_OK) {
+ LOG_ERROR("Failed to write to channel: %s.", jaylink_strerror(ret));
+ return ERROR_FAIL;
+ }
+
+ if (length != (tmp / 2))
+ LOG_WARNING("Only %" PRIu32 " bytes written to the channel.", length);
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(jlink_handle_emucom_read_command)
+{
+ int ret;
+ uint32_t channel;
+ uint32_t length;
+ uint8_t *buf;
+ size_t tmp;
+
+ if (CMD_ARGC != 2)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_EMUCOM)) {
+ LOG_ERROR("Device does not support EMUCOM.");
+ return ERROR_FAIL;
+ }
+
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], channel);
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], length);
+
+ buf = malloc(length * 3 + 1);
+
+ if (!buf) {
+ LOG_ERROR("Failed to allocate buffer.");
+ return ERROR_FAIL;
+ }
+
+ ret = jaylink_emucom_read(devh, channel, buf, &length);
+
+ if (ret == JAYLINK_ERR_DEV_NOT_SUPPORTED) {
+ LOG_ERROR("Channel is not supported by the device.");
+ free(buf);
+ return ERROR_FAIL;
+ } else if (ret == JAYLINK_ERR_DEV_NOT_AVAILABLE) {
+ LOG_ERROR("Channel is not available for the requested amount of data. "
+ "%" PRIu32 " bytes are avilable.", length);
+ free(buf);
+ return ERROR_FAIL;
+ } else if (ret != JAYLINK_OK) {
+ LOG_ERROR("Failed to read from channel: %s.", jaylink_strerror(ret));
+ free(buf);
+ return ERROR_FAIL;
+ }
+
+ tmp = hexify((char *)buf + length, buf, length, 2 * length + 1);
+
+ if (tmp != 2 * length) {
+ LOG_ERROR("Failed to convert data into hexadecimal string.");
+ free(buf);
+ return ERROR_FAIL;
+ }
+
+ command_print(CMD_CTX, "%s", buf + length);
+ free(buf);
+
+ 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
+};
+
+static const struct command_registration jlink_emucom_subcommand_handlers[] = {
+ {
+ .name = "write",
+ .handler = &jlink_handle_emucom_write_command,
+ .mode = COMMAND_EXEC,
+ .help = "write to a channel",
+ .usage = "<channel> <data>",
+ },