+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,