#include "general.h"
#include "gdb_if.h"
#include "adiv5.h"
#include "jlink.h"
#include "jlink_protocol.h"
#include "exception.h"
#include "buffer_utils.h"
#include <assert.h>
#include <unistd.h>
#include <signal.h>
#include <ctype.h>
#include <sys/time.h>
#include <libusb.h>
#include "cli.h"
typedef struct jlink {
char fw_version[256U];
uint32_t hw_version;
uint32_t capabilities;
uint32_t available_interfaces;
struct jlink_interface_frequency {
uint32_t base;
uint16_t min_divisor;
uint16_t current_divisor;
} interface_frequency[JLINK_INTERFACE_MAX];
} jlink_s;
jlink_s jlink;
bool jlink_simple_query(const uint8_t command, void *const rx_buffer, const size_t rx_len)
{
return bmda_usb_transfer(
bmda_probe_info.usb_link, &command, sizeof(command), rx_buffer, rx_len, JLINK_USB_TIMEOUT) >= 0;
}
bool jlink_simple_request_8(const uint8_t command, const uint8_t operation, void *const rx_buffer, const size_t rx_len)
{
const uint8_t request[2U] = {command, operation};
return bmda_usb_transfer(
bmda_probe_info.usb_link, request, sizeof(request), rx_buffer, rx_len, JLINK_USB_TIMEOUT) >= 0;
}
bool jlink_simple_request_16(
const uint8_t command, const uint16_t operation, void *const rx_buffer, const size_t rx_len)
{
uint8_t request[3U] = {command};
write_le2(request, 1U, operation);
return bmda_usb_transfer(
bmda_probe_info.usb_link, request, sizeof(request), rx_buffer, rx_len, JLINK_USB_TIMEOUT) >= 0;
}
bool jlink_simple_request_32(
const uint8_t command, const uint32_t operation, void *const rx_buffer, const size_t rx_len)
{
uint8_t request[5U] = {command};
write_le4(request, 1U, operation);
return bmda_usb_transfer(
bmda_probe_info.usb_link, request, sizeof(request), rx_buffer, rx_len, JLINK_USB_TIMEOUT) >= 0;
}
bool jlink_transfer(const uint16_t clock_cycles, const uint8_t *const tms, const uint8_t *const tdi, uint8_t *const tdo)
{
if (!clock_cycles)
return true;
const size_t byte_count = (clock_cycles + 7U) >> 3U;
if (byte_count > 512U)
return false;
uint8_t buffer[1028U] = {0};
jlink_io_transact_s *header = (jlink_io_transact_s *)buffer;
header->command = JLINK_CMD_IO_TRANSACTION;
write_le2(header->clock_cycles, 0, clock_cycles);
if (tms)
memcpy(buffer + 4U, tms, byte_count);
if (tdi)
memcpy(buffer + 4U + byte_count, tdi, byte_count);
if (bmda_usb_transfer(bmda_probe_info.usb_link, buffer, sizeof(*header) + (byte_count * 2U), buffer, byte_count,
JLINK_USB_TIMEOUT) < 0 ||
bmda_usb_transfer(bmda_probe_info.usb_link, NULL, 0, buffer + byte_count, 1U, JLINK_USB_TIMEOUT) < 0)
return false;
if (tdo)
memcpy(tdo, buffer, byte_count);
return buffer[byte_count] == 0U;
}
bool jlink_transfer_fixed_tms(
const uint16_t clock_cycles, const bool final_tms, const uint8_t *const tdi, uint8_t *const tdo)
{
if (!clock_cycles)
return true;
const size_t byte_count = (clock_cycles + 7U) >> 3U;
if (byte_count > 512U)
return false;
uint8_t tms[512U] = {0};
const size_t cycles = clock_cycles - 1U;
const size_t final_byte = cycles >> 3U;
const uint8_t final_bit = cycles & 7U;
tms[final_byte] |= (final_tms ? 1U : 0U) << final_bit;
return jlink_transfer(clock_cycles, tms, tdi, tdo);
}
bool jlink_transfer_swd(
const uint16_t clock_cycles, const jlink_swd_dir_e direction, const uint8_t *const data_in, uint8_t *const data_out)
{
uint8_t dir[8U] = {0};
memset(dir, direction == JLINK_SWD_IN ? 0x00U : 0xffU, sizeof(dir));
return jlink_transfer(clock_cycles, dir, data_in, data_out);
}
static bool jlink_claim_interface(void)
{
libusb_config_descriptor_s *config;
const int get_descriptor_result = libusb_get_active_config_descriptor(bmda_probe_info.libusb_dev, &config);
if (get_descriptor_result != LIBUSB_SUCCESS) {
DEBUG_ERROR("Failed to get configuration descriptor: %s\n", libusb_error_name(get_descriptor_result));
return false;
}
const libusb_interface_descriptor_s *descriptor = NULL;
for (size_t idx = 0; idx < config->bNumInterfaces; ++idx) {
const libusb_interface_s *const interface = &config->interface[idx];
const libusb_interface_descriptor_s *const interface_desc = &interface->altsetting[0];
if (interface_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC &&
interface_desc->bInterfaceSubClass == LIBUSB_CLASS_VENDOR_SPEC && interface_desc->bNumEndpoints > 1U) {
const int claim_result = libusb_claim_interface(bmda_probe_info.usb_link->device_handle, (int)idx);
if (claim_result) {
DEBUG_ERROR("Can not claim handle: %s\n", libusb_error_name(claim_result));
break;
}
bmda_probe_info.usb_link->interface = idx;
descriptor = interface_desc;
}
}
if (!descriptor) {
DEBUG_ERROR("No suitable interface found\n");
libusb_free_config_descriptor(config);
return false;
}
for (size_t i = 0; i < descriptor->bNumEndpoints; i++) {
const libusb_endpoint_descriptor_s *endpoint = &descriptor->endpoint[i];
if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN)
bmda_probe_info.usb_link->ep_rx = endpoint->bEndpointAddress;
else
bmda_probe_info.usb_link->ep_tx = endpoint->bEndpointAddress;
}
libusb_free_config_descriptor(config);
return true;
}
static char *jlink_hw_type_to_string(const uint8_t hw_type)
{
switch (hw_type) {
case JLINK_HARDWARE_VERSION_TYPE_JLINK:
return "J-Link";
case JLINK_HARDWARE_VERSION_TYPE_JTRACE:
return "J-Trace";
case JLINK_HARDWARE_VERSION_TYPE_FLASHER:
return "Flasher";
case JLINK_HARDWARE_VERSION_TYPE_JLINKPRO:
return "J-Link Pro";
case JLINK_HARDWARE_VERSION_TYPE_LPCLINK2:
return "LPC-Link2";
default:
return "Unknown";
}
}
static char *jlink_interface_to_string(const uint8_t interface)
{
switch (interface) {
case JLINK_INTERFACE_JTAG:
return "JTAG";
case JLINK_INTERFACE_SWD:
return "SWD";
case JLINK_INTERFACE_BDM3:
return "BDM3";
case JLINK_INTERFACE_FINE:
return "FINE";
case JLINK_INTERFACE_2W_JTAG_PIC32:
return "PIC32 2-wire JTAG";
case JLINK_INTERFACE_SPI:
return "SPI";
case JLINK_INTERFACE_C2:
return "C2";
case JLINK_INTERFACE_CJTAG:
return "cJTAG";
default:
return "Unknown";
}
}
static bool jlink_get_version(void)
{
uint8_t buffer[4U];
if (!jlink_simple_query(JLINK_CMD_INFO_GET_FIRMWARE_VERSION, buffer, 2U))
return false;
const uint16_t version_length = read_le2(buffer, 0);
if (version_length > sizeof(jlink.fw_version))
return false;
bmda_usb_transfer(bmda_probe_info.usb_link, NULL, 0, jlink.fw_version, version_length, JLINK_USB_TIMEOUT);
jlink.fw_version[version_length - 1U] = '\0';
DEBUG_INFO("Firmware version: %s\n", jlink.fw_version);
if (jlink.capabilities & JLINK_CAPABILITY_HARDWARE_VERSION) {
if (!jlink_simple_query(JLINK_CMD_INFO_GET_HARDWARE_VERSION, buffer, 4U))
return false;
jlink.hw_version = read_le4(buffer, 0);
DEBUG_INFO("Hardware version: %s v%" PRIu32 ".%" PRIu32 ".%" PRIu32 "\n",
jlink_hw_type_to_string(JLINK_HARDWARE_VERSION_TYPE(jlink.hw_version)),
JLINK_HARDWARE_VERSION_MAJOR(jlink.hw_version), JLINK_HARDWARE_VERSION_MINOR(jlink.hw_version),
JLINK_HARDWARE_VERSION_REVISION(jlink.hw_version));
}
return true;
}
static bool jlink_get_capabilities(void)
{
uint8_t buffer[4U];
if (!jlink_simple_query(JLINK_CMD_INFO_GET_PROBE_CAPABILITIES, buffer, sizeof(buffer)))
return false;
jlink.capabilities = read_le4(buffer, 0);
DEBUG_INFO("Capabilities: 0x%08" PRIx32 "\n", jlink.capabilities);
return true;
}
static inline bool jlink_interface_available(const uint8_t interface)
{
return jlink.available_interfaces & (1U << interface);
}
static uint8_t jlink_selected_interface(void)
{
if (!(jlink.capabilities & JLINK_CAPABILITY_INTERFACES))
return JLINK_INTERFACE_JTAG;
uint8_t buffer[4U];
if (!jlink_simple_request_8(JLINK_CMD_INTERFACE_GET, JLINK_INTERFACE_GET_CURRENT, buffer, sizeof(buffer)))
return UINT8_MAX;
return buffer[0];
}
bool jlink_select_interface(const uint8_t interface)
{
if (!jlink_interface_available(interface)) {
DEBUG_ERROR("Interface [%u] %s is not available\n", interface, jlink_interface_to_string(interface));
return false;
}
if (jlink_selected_interface() == interface)
return true;
uint8_t buffer[4U];
if (!jlink_simple_request_8(JLINK_CMD_INTERFACE_SET_SELECTED, interface, buffer, sizeof(buffer)))
return false;
platform_delay(10U);
return true;
}
static bool jlink_get_interfaces(void)
{
uint8_t buffer[4U];
if (!(jlink.capabilities & JLINK_CAPABILITY_INTERFACES))
jlink.available_interfaces = JLINK_INTERFACE_AVAILABLE(JLINK_INTERFACE_JTAG);
else {
if (!jlink_simple_request_8(JLINK_CMD_INTERFACE_GET, JLINK_INTERFACE_GET_AVAILABLE, buffer, sizeof(buffer)))
return false;
jlink.available_interfaces = read_le4(buffer, 0);
}
const uint8_t selected_interface = jlink_selected_interface();
DEBUG_INFO("Available interfaces: \n");
for (size_t i = 0; i < JLINK_INTERFACE_MAX; i++) {
if (jlink_interface_available(i)) {
const bool is_current = i == selected_interface;
const bool is_bmda_supported = i == JLINK_INTERFACE_SWD || i == JLINK_INTERFACE_JTAG;
DEBUG_INFO("\t%zu: %s%c %s\n", i, jlink_interface_to_string(i), is_current ? '*' : ' ',
is_bmda_supported ? "" : "(Not supported)");
}
}
return true;
}
static bool jlink_get_interface_frequency(const uint8_t interface)
{
if (!(jlink.capabilities & JLINK_CAPABILITY_INTERFACE_FREQUENCY)) {
DEBUG_WARN("J-Link does not support interface frequency commands\n");
return false;
}
if (!jlink_interface_available(interface))
return false;
if (jlink.interface_frequency[interface].base != 0U)
return true;
const uint8_t selected_interface = jlink_selected_interface();
if (selected_interface != interface) {
DEBUG_WARN("Trying to get frequency for interface %s but it is not selected, selecting it\n",
jlink_interface_to_string(interface));
if (!jlink_select_interface(interface))
return false;
}
uint8_t buffer[6U];
if (!jlink_simple_query(JLINK_CMD_INTERFACE_GET_BASE_FREQUENCY, buffer, sizeof(buffer)))
return false;
struct jlink_interface_frequency *const interface_frequency = &jlink.interface_frequency[interface];
interface_frequency->base = read_le4(buffer, JLINK_INTERFACE_BASE_FREQUENCY_OFFSET);
interface_frequency->min_divisor = read_le2(buffer, JLINK_INTERFACE_MIN_DIV_OFFSET);
interface_frequency->current_divisor = interface_frequency->min_divisor;
DEBUG_INFO("%s interface frequency:\n\tBase frequency: %" PRIu32 "Hz\n\tMinimum divisor: %u\n",
jlink_interface_to_string(interface), interface_frequency->base, interface_frequency->min_divisor);
#if 0#endif
return true;
}
static bool jlink_set_interface_frequency(const uint8_t interface, const uint32_t frequency)
{
if (!(jlink.capabilities & JLINK_CAPABILITY_INTERFACE_FREQUENCY)) {
DEBUG_WARN("J-Link does not support interface frequency command\n");
return false;
}
const uint8_t selected_interface = jlink_selected_interface();
if (selected_interface != interface) {
DEBUG_WARN("Trying to set frequency for interface %s but it is not selected, selecting it\n",
jlink_interface_to_string(interface));
if (!jlink_select_interface(interface))
return false;
}
if (!jlink_get_interface_frequency(interface))
return false;
struct jlink_interface_frequency *const interface_frequency = &jlink.interface_frequency[interface];
uint16_t divisor = (interface_frequency->base + frequency - 1U) / frequency;
if (divisor < interface_frequency->min_divisor)
divisor = interface_frequency->min_divisor;
const uint16_t frequency_khz = (interface_frequency->base / divisor) / 1000U;
if (!jlink_simple_request_16(JLINK_CMD_INTERFACE_SET_FREQUENCY_KHZ, frequency_khz, NULL, 0))
return false;
interface_frequency->current_divisor = divisor;
#if 0#endif
return true;
}
static uint16_t jlink_target_voltage(void)
{
uint8_t buffer[8U];
if (!jlink_simple_query(JLINK_CMD_SIGNAL_GET_STATE, buffer, sizeof(buffer)))
return UINT16_MAX;
return read_le2(buffer, JLINK_SIGNAL_STATE_VOLTAGE_OFFSET);
}
static bool jlink_kickstart_power(void)
{
if (!(jlink.capabilities & JLINK_CAPABILITY_POWER_STATE)) {
if (jlink.capabilities & JLINK_CAPABILITY_KICKSTART_POWER)
DEBUG_ERROR("J-Link does not support JLINK_CMD_POWER_GET_STATE command, but does support kickstart power"
", this is unexpected\n");
return false;
}
uint8_t buffer[4U];
if (!jlink_simple_request_32(
JLINK_CMD_POWER_GET_STATE, JLINK_POWER_STATE_KICKSTART_ENABLED_MASK, buffer, sizeof(buffer)))
return false;
return buffer[0] == 1U;
}
static bool jlink_set_kickstart_power(const bool enable)
{
if (!(jlink.capabilities & JLINK_CAPABILITY_KICKSTART_POWER))
return false;
return jlink_simple_request_8(JLINK_CMD_POWER_SET_KICKSTART, enable ? JLINK_POWER_KICKSTART_ENABLE : 0, NULL, 0);
}
bool jlink_init(void)
{
usb_link_s *link = calloc(1, sizeof(usb_link_s));
if (!link)
return false;
bmda_probe_info.usb_link = link;
link->context = bmda_probe_info.libusb_ctx;
const int result = libusb_open(bmda_probe_info.libusb_dev, &link->device_handle);
if (result != LIBUSB_SUCCESS) {
DEBUG_ERROR("libusb_open() failed (%d): %s\n", result, libusb_error_name(result));
return false;
}
if (!jlink_claim_interface()) {
libusb_close(bmda_probe_info.usb_link->device_handle);
return false;
}
if (!link->ep_tx || !link->ep_rx) {
DEBUG_ERROR("Device setup failed\n");
libusb_release_interface(bmda_probe_info.usb_link->device_handle, bmda_probe_info.usb_link->interface);
libusb_close(bmda_probe_info.usb_link->device_handle);
return false;
}
if (!jlink_get_capabilities() || !jlink_get_version() || !jlink_get_interfaces()) {
DEBUG_ERROR("Failed to read J-Link information\n");
libusb_release_interface(bmda_probe_info.usb_link->device_handle, bmda_probe_info.usb_link->interface);
libusb_close(bmda_probe_info.usb_link->device_handle);
return false;
}
memcpy(bmda_probe_info.version, jlink.fw_version, strlen(jlink.fw_version) + 1U);
return true;
}
uint32_t jlink_target_voltage_sense(void)
{
return jlink_target_voltage() / 100U;
}
const char *jlink_target_voltage_string(void)
{
static char result[8U] = {'\0'};
const uint16_t millivolts = jlink_target_voltage();
if (millivolts == UINT16_MAX)
return "ERROR!";
snprintf(result, sizeof(result), "%2u.%03uV", millivolts / 1000U, millivolts % 1000U);
return result;
}
void jlink_nrst_set_val(const bool assert)
{
jlink_simple_query(assert ? JLINK_CMD_SIGNAL_CLEAR_RESET : JLINK_CMD_SIGNAL_SET_RESET, NULL, 0);
platform_delay(2U);
}
bool jlink_nrst_get_val(void)
{
uint8_t result[8U];
if (!jlink_simple_query(JLINK_CMD_SIGNAL_GET_STATE, result, sizeof(result)))
return false;
return result[JLINK_SIGNAL_STATE_TRES_OFFSET] == 0;
}
void jlink_max_frequency_set(const uint32_t frequency)
{
const uint8_t bmda_interface = bmda_probe_info.is_jtag ? JLINK_INTERFACE_JTAG : JLINK_INTERFACE_SWD;
if (!jlink_set_interface_frequency(bmda_interface, frequency))
DEBUG_ERROR("Failed to set J-Link %s interface frequency\n", jlink_interface_to_string(bmda_interface));
}
uint32_t jlink_max_frequency_get(void)
{
const uint8_t bmda_interface = bmda_probe_info.is_jtag ? JLINK_INTERFACE_JTAG : JLINK_INTERFACE_SWD;
if (!jlink_get_interface_frequency(bmda_interface)) {
DEBUG_ERROR("No frequency info available for interface %s\n", jlink_interface_to_string(bmda_interface));
return FREQ_FIXED;
}
struct jlink_interface_frequency *const interface_frequency = &jlink.interface_frequency[bmda_interface];
return interface_frequency->base / interface_frequency->current_divisor;
}
bool jlink_target_set_power(const bool power)
{
return jlink_set_kickstart_power(power);
}
bool jlink_target_get_power(void)
{
return jlink_kickstart_power();
}