kopia lustrzana https://github.com/stlink-org/stlink
1420 wiersze
45 KiB
C
1420 wiersze
45 KiB
C
/*
|
||
* File: usb.c
|
||
*
|
||
* USB commands & interaction with ST-LINK devices
|
||
*/
|
||
|
||
#if !defined(_MSC_VER)
|
||
#include <sys/time.h>
|
||
#endif // _MSC_VER
|
||
|
||
#if defined(_WIN32)
|
||
#include <win32_socket.h>
|
||
#endif // _WIN32
|
||
|
||
#include <stdint.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
#include <errno.h>
|
||
#include <limits.h>
|
||
#include <stdbool.h>
|
||
#include <unistd.h>
|
||
|
||
#include <stlink.h>
|
||
#include "usb.h"
|
||
|
||
#include "commands.h"
|
||
#include "logging.h"
|
||
#include "read_write.h"
|
||
#include "register.h"
|
||
|
||
static inline uint32_t le_to_h_u32(const uint8_t* buf) {
|
||
return ((uint32_t)((uint32_t)buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24));
|
||
}
|
||
|
||
static int32_t _stlink_match_speed_map(const uint32_t *map, uint32_t map_size, uint32_t khz) {
|
||
uint32_t i;
|
||
int32_t speed_index = -1;
|
||
int32_t speed_diff = INT_MAX;
|
||
int32_t last_valid_speed = -1;
|
||
bool match = true;
|
||
|
||
for (i = 0; i < map_size; i++) {
|
||
if (!map[i]) { continue; }
|
||
|
||
last_valid_speed = i;
|
||
|
||
if (khz == map[i]) {
|
||
speed_index = i;
|
||
break;
|
||
} else {
|
||
int32_t current_diff = khz - map[i];
|
||
// get abs value for comparison
|
||
current_diff = (current_diff > 0) ? current_diff : -current_diff;
|
||
|
||
if (current_diff < speed_diff) {
|
||
speed_diff = current_diff;
|
||
speed_index = i;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (speed_index == -1) {
|
||
// This will only be here if we cannot match the slow speed.
|
||
// Use the slowest speed we support.
|
||
speed_index = last_valid_speed;
|
||
match = false;
|
||
} else if (i == map_size) {
|
||
match = false;
|
||
}
|
||
|
||
if (!match) {
|
||
ILOG("Unable to match requested speed %d kHz, using %d kHz\n", khz, map[speed_index]);
|
||
}
|
||
|
||
return (speed_index);
|
||
}
|
||
|
||
void _stlink_usb_close(stlink_t* sl) {
|
||
if (!sl) { return; }
|
||
|
||
struct stlink_libusb * const handle = sl->backend_data;
|
||
|
||
// maybe we couldn't even get the usb device?
|
||
if (handle != NULL) {
|
||
if (handle->usb_handle != NULL) { libusb_close(handle->usb_handle); }
|
||
|
||
libusb_exit(handle->libusb_ctx);
|
||
free(handle);
|
||
}
|
||
}
|
||
|
||
ssize_t send_recv(struct stlink_libusb* handle, int32_t terminate, unsigned char* txbuf, uint32_t txsize,
|
||
unsigned char* rxbuf, uint32_t rxsize, int32_t check_error, const char *cmd) {
|
||
// Note: txbuf and rxbuf can point to the same area
|
||
int32_t res, t, retry = 0;
|
||
|
||
while (1) {
|
||
res = 0;
|
||
t = libusb_bulk_transfer(handle->usb_handle, handle->ep_req, txbuf, (int32_t)txsize, &res, 3000);
|
||
|
||
if (t) {
|
||
ELOG("%s send request failed: %s\n", cmd, libusb_error_name(t));
|
||
return (-1);
|
||
} else if ((size_t)res != txsize) {
|
||
ELOG("%s send request wrote %u bytes, instead of %u\n", cmd, (uint32_t)res, (uint32_t)txsize);
|
||
}
|
||
|
||
if (rxsize != 0) {
|
||
t = libusb_bulk_transfer(handle->usb_handle, handle->ep_rep, rxbuf, (int32_t)rxsize, &res, 3000);
|
||
|
||
if (t) {
|
||
ELOG("%s read reply failed: %s\n", cmd, libusb_error_name(t));
|
||
return (-1);
|
||
}
|
||
|
||
/* Checking the command execution status stored in the first byte of the response */
|
||
if (handle->protocoll != 1 && check_error >= CMD_CHECK_STATUS &&
|
||
rxbuf[0] != STLINK_DEBUG_ERR_OK) {
|
||
switch(rxbuf[0]) {
|
||
case STLINK_DEBUG_ERR_AP_WAIT:
|
||
case STLINK_DEBUG_ERR_DP_WAIT:
|
||
if (check_error == CMD_CHECK_RETRY && retry < 3) {
|
||
uint32_t delay_us = (1<<retry) * 1000;
|
||
DLOG("%s wait error (0x%02X), delaying %u us and retry\n", cmd, rxbuf[0], delay_us);
|
||
usleep(delay_us);
|
||
retry++;
|
||
continue;
|
||
}
|
||
DLOG("%s wait error (0x%02X)\n", cmd, rxbuf[0]);
|
||
break;
|
||
case STLINK_DEBUG_ERR_FAULT: DLOG("%s response fault\n", cmd); break;
|
||
case STLINK_DEBUG_ERR_AP_FAULT: DLOG("%s access port fault\n", cmd); break;
|
||
case STLINK_DEBUG_ERR_DP_FAULT: DLOG("%s debug port fault\n", cmd); break;
|
||
case STLINK_DEBUG_ERR_AP_ERROR: DLOG("%s access port error\n", cmd); break;
|
||
case STLINK_DEBUG_ERR_DP_ERROR: DLOG("%s debug port error\n", cmd); break;
|
||
case STLINK_DEBUG_ERR_WRITE_VERIFY: DLOG("%s verification error\n", cmd); break;
|
||
case STLINK_DEBUG_ERR_WRITE: DLOG("%s write error\n", cmd); break;
|
||
default: DLOG("%s error (0x%02X)\n", cmd, rxbuf[0]); break;
|
||
}
|
||
|
||
return (-1);
|
||
}
|
||
|
||
if (check_error == CMD_CHECK_REP_LEN && res != (int32_t)rxsize) {
|
||
ELOG("%s wrong reply length\n", cmd);
|
||
res = -1;
|
||
}
|
||
}
|
||
|
||
if ((handle->protocoll == 1) && terminate) {
|
||
// read the SG reply
|
||
unsigned char sg_buf[13];
|
||
t = libusb_bulk_transfer(handle->usb_handle, handle->ep_rep, sg_buf, 13, &res, 3000);
|
||
|
||
if (t) {
|
||
ELOG("%s read storage failed: %s\n", cmd, libusb_error_name(t));
|
||
return (-1);
|
||
}
|
||
|
||
// The STLink doesn't seem to evaluate the sequence number.
|
||
handle->sg_transfer_idx++;
|
||
}
|
||
|
||
return (res);
|
||
}
|
||
}
|
||
|
||
static inline int32_t send_only(struct stlink_libusb* handle, int32_t terminate, unsigned char* txbuf,
|
||
uint32_t txsize, const char *cmd) {
|
||
return ((int32_t)send_recv(handle, terminate, txbuf, txsize, NULL, 0, CMD_CHECK_NO, cmd));
|
||
}
|
||
|
||
|
||
static int32_t fill_command(stlink_t * sl, enum SCSI_Generic_Direction dir, uint32_t len) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
int32_t i = 0;
|
||
memset(cmd, 0, sizeof(sl->c_buf));
|
||
|
||
if (slu->protocoll == 1) {
|
||
cmd[i++] = 'U';
|
||
cmd[i++] = 'S';
|
||
cmd[i++] = 'B';
|
||
cmd[i++] = 'C';
|
||
write_uint32(&cmd[i], slu->sg_transfer_idx);
|
||
write_uint32(&cmd[i + 4], len);
|
||
i += 8;
|
||
cmd[i++] = (dir == SG_DXFER_FROM_DEV) ? 0x80 : 0;
|
||
cmd[i++] = 0; // logical unit
|
||
cmd[i++] = 0xa; // command length
|
||
}
|
||
return (i);
|
||
}
|
||
|
||
int32_t _stlink_usb_version(stlink_t *sl) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const data = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
uint32_t rep_len;
|
||
int32_t i;
|
||
|
||
if (sl->version.stlink_v == 3) {
|
||
// STLINK-V3 version is determined by another command
|
||
rep_len = 12;
|
||
i = fill_command(sl, SG_DXFER_FROM_DEV, 16);
|
||
cmd[i++] = STLINK_GET_VERSION_APIV3;
|
||
} else {
|
||
rep_len = 6;
|
||
i = fill_command(sl, SG_DXFER_FROM_DEV, 6);
|
||
cmd[i++] = STLINK_GET_VERSION;
|
||
}
|
||
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len, CMD_CHECK_REP_LEN, "GET_VERSION");
|
||
|
||
return (size < 0 ? -1 : 0);
|
||
}
|
||
|
||
int32_t _stlink_usb_target_voltage(stlink_t *sl) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const rdata = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
uint32_t rep_len = 8;
|
||
int32_t i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
||
uint32_t factor, reading;
|
||
int32_t voltage;
|
||
|
||
cmd[i++] = STLINK_GET_TARGET_VOLTAGE;
|
||
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len, CMD_CHECK_REP_LEN, "GET_TARGET_VOLTAGE");
|
||
|
||
if (size < 0) {
|
||
return (-1);
|
||
}
|
||
|
||
factor = (rdata[3] << 24) | (rdata[2] << 16) | (rdata[1] << 8) | (rdata[0] << 0);
|
||
reading = (rdata[7] << 24) | (rdata[6] << 16) | (rdata[5] << 8) | (rdata[4] << 0);
|
||
voltage = 2400 * reading / factor;
|
||
|
||
return (voltage);
|
||
}
|
||
|
||
int32_t _stlink_usb_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const rdata = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
const int32_t rep_len = 8;
|
||
|
||
int32_t i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
cmd[i++] = STLINK_DEBUG_APIV2_READDEBUGREG;
|
||
write_uint32(&cmd[i], addr);
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len, CMD_CHECK_RETRY, "READDEBUGREG");
|
||
|
||
if (size < 0) {
|
||
return (-1);
|
||
}
|
||
|
||
*data = read_uint32(rdata, 4);
|
||
|
||
return (0);
|
||
}
|
||
|
||
int32_t _stlink_usb_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const rdata = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
const int32_t rep_len = 2;
|
||
|
||
int32_t i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
cmd[i++] = STLINK_DEBUG_APIV2_WRITEDEBUGREG;
|
||
write_uint32(&cmd[i], addr);
|
||
write_uint32(&cmd[i + 4], data);
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len, CMD_CHECK_RETRY, "WRITEDEBUGREG");
|
||
|
||
return (size < 0 ? -1 : 0);
|
||
}
|
||
|
||
int32_t _stlink_usb_get_rw_status(stlink_t *sl) {
|
||
if (sl->version.jtag_api == STLINK_JTAG_API_V1) { return (0); }
|
||
|
||
unsigned char* const rdata = sl->q_buf;
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
int32_t i;
|
||
int16_t ret = 0;
|
||
|
||
i = fill_command(sl, SG_DXFER_FROM_DEV, 12);
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
|
||
if (sl->version.flags & STLINK_F_HAS_GETLASTRWSTATUS2) {
|
||
cmd[i++] = STLINK_DEBUG_APIV2_GETLASTRWSTATUS2;
|
||
ret = send_recv(slu, 1, cmd, slu->cmd_len, rdata, 12, CMD_CHECK_STATUS, "GETLASTRWSTATUS2");
|
||
} else {
|
||
cmd[i++] = STLINK_DEBUG_APIV2_GETLASTRWSTATUS;
|
||
ret = send_recv(slu, 1, cmd, slu->cmd_len, rdata, 2, CMD_CHECK_STATUS, "GETLASTRWSTATUS");
|
||
}
|
||
|
||
return (ret < 0 ? -1 : 0);
|
||
}
|
||
|
||
int32_t _stlink_usb_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const data = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
int32_t i, ret;
|
||
|
||
i = fill_command(sl, SG_DXFER_TO_DEV, len);
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
cmd[i++] = STLINK_DEBUG_WRITEMEM_32BIT;
|
||
write_uint32(&cmd[i], addr);
|
||
write_uint16(&cmd[i + 4], len);
|
||
ret = send_only(slu, 0, cmd, slu->cmd_len, "WRITEMEM_32BIT");
|
||
|
||
if (ret == -1) { return (ret); }
|
||
|
||
ret = send_only(slu, 1, data, len, "WRITEMEM_32BIT");
|
||
|
||
if (ret == -1) { return (ret); }
|
||
|
||
return (_stlink_usb_get_rw_status(sl));
|
||
}
|
||
|
||
int32_t _stlink_usb_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const data = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
int32_t i, ret;
|
||
|
||
if ((sl->version.jtag_api < STLINK_JTAG_API_V3 && len > 64) ||
|
||
(sl->version.jtag_api >= STLINK_JTAG_API_V3 && len > 512)) {
|
||
ELOG("WRITEMEM_8BIT: bulk packet limits exceeded (data len %d byte)\n", len);
|
||
return (-1);
|
||
}
|
||
|
||
i = fill_command(sl, SG_DXFER_TO_DEV, 0);
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
cmd[i++] = STLINK_DEBUG_WRITEMEM_8BIT;
|
||
write_uint32(&cmd[i], addr);
|
||
write_uint16(&cmd[i + 4], len);
|
||
ret = send_only(slu, 0, cmd, slu->cmd_len, "WRITEMEM_8BIT");
|
||
|
||
if (ret == -1) { return (ret); }
|
||
|
||
ret = send_only(slu, 1, data, len, "WRITEMEM_8BIT");
|
||
|
||
if (ret == -1) { return (ret); }
|
||
|
||
return (0);
|
||
}
|
||
|
||
int32_t _stlink_usb_current_mode(stlink_t * sl) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
unsigned char* const data = sl->q_buf;
|
||
ssize_t size;
|
||
int32_t rep_len = 2;
|
||
int32_t i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
||
|
||
cmd[i++] = STLINK_GET_CURRENT_MODE;
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len, CMD_CHECK_NO, "GET_CURRENT_MODE");
|
||
|
||
if (size < 0) {
|
||
return (-1);
|
||
}
|
||
|
||
return (sl->q_buf[0]);
|
||
}
|
||
|
||
int32_t _stlink_usb_core_id(stlink_t * sl) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
unsigned char* const data = sl->q_buf;
|
||
ssize_t size;
|
||
int32_t offset, rep_len = sl->version.jtag_api == STLINK_JTAG_API_V1 ? 4 : 12;
|
||
int32_t i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
||
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
|
||
if (sl->version.jtag_api == STLINK_JTAG_API_V1) {
|
||
cmd[i++] = STLINK_DEBUG_READCOREID;
|
||
offset = 0;
|
||
} else {
|
||
cmd[i++] = STLINK_DEBUG_APIV2_READ_IDCODES;
|
||
offset = 4;
|
||
}
|
||
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len, CMD_CHECK_STATUS, "READ_IDCODES");
|
||
|
||
if (size < 0) {
|
||
return (-1);
|
||
}
|
||
|
||
sl->core_id = read_uint32(data, offset);
|
||
|
||
return (0);
|
||
}
|
||
|
||
int32_t _stlink_usb_status_v2(stlink_t *sl) {
|
||
int32_t result;
|
||
uint32_t status = 0;
|
||
|
||
result = _stlink_usb_read_debug32(sl, STLINK_REG_DHCSR, &status);
|
||
DLOG("core status: %08X\n", status);
|
||
|
||
if (result != 0) {
|
||
sl->core_stat = TARGET_UNKNOWN;
|
||
} else {
|
||
if (status & STLINK_REG_DHCSR_C_HALT) {
|
||
sl->core_stat = TARGET_HALTED;
|
||
} else if (status & STLINK_REG_DHCSR_S_RESET_ST) {
|
||
sl->core_stat = TARGET_RESET;
|
||
} else {
|
||
sl->core_stat = TARGET_RUNNING;
|
||
}
|
||
}
|
||
|
||
return (result);
|
||
}
|
||
|
||
int32_t _stlink_usb_status(stlink_t * sl) {
|
||
if (sl->version.jtag_api != STLINK_JTAG_API_V1) { return (_stlink_usb_status_v2(sl)); }
|
||
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const data = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
int32_t rep_len = 2;
|
||
int32_t i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
||
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
cmd[i++] = STLINK_DEBUG_GETSTATUS;
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len, CMD_CHECK_NO, "GETSTATUS");
|
||
|
||
if (size > 1) {
|
||
if (sl->q_buf[0] == STLINK_CORE_RUNNING) {
|
||
sl->core_stat = TARGET_RUNNING;
|
||
} else if (sl->q_buf[0] == STLINK_CORE_HALTED) {
|
||
sl->core_stat = TARGET_HALTED;
|
||
} else {
|
||
sl->core_stat = TARGET_UNKNOWN;
|
||
}
|
||
} else {
|
||
sl->core_stat = TARGET_UNKNOWN;
|
||
}
|
||
|
||
return (size < 0 ? -1 : 0);
|
||
}
|
||
|
||
int32_t _stlink_usb_force_debug(stlink_t *sl) {
|
||
struct stlink_libusb *slu = sl->backend_data;
|
||
|
||
int32_t res;
|
||
|
||
if (sl->version.jtag_api != STLINK_JTAG_API_V1) {
|
||
res = _stlink_usb_write_debug32(sl, STLINK_REG_DHCSR, STLINK_REG_DHCSR_DBGKEY | STLINK_REG_DHCSR_C_HALT | STLINK_REG_DHCSR_C_DEBUGEN);
|
||
return (res);
|
||
}
|
||
|
||
unsigned char* const data = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
int32_t rep_len = 2;
|
||
int32_t i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
||
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
cmd[i++] = STLINK_DEBUG_FORCEDEBUG;
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len, CMD_CHECK_RETRY, "FORCEDEBUG");
|
||
|
||
return (size < 0 ? -1 : 0);
|
||
}
|
||
|
||
int32_t _stlink_usb_enter_swd_mode(stlink_t * sl) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
unsigned char* const data = sl->q_buf;
|
||
const uint32_t rep_len = sl->version.jtag_api == STLINK_JTAG_API_V1 ? 0 : 2;
|
||
int32_t i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
||
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
// select correct API-Version for entering SWD mode: V1 API (0x20) or V2 API (0x30).
|
||
cmd[i++] = sl->version.jtag_api == STLINK_JTAG_API_V1 ? STLINK_DEBUG_APIV1_ENTER : STLINK_DEBUG_APIV2_ENTER;
|
||
cmd[i++] = STLINK_DEBUG_ENTER_SWD;
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len, CMD_CHECK_RETRY, "ENTER_SWD");
|
||
|
||
return (size < 0 ? -1 : 0);
|
||
}
|
||
|
||
int32_t _stlink_usb_exit_dfu_mode(stlink_t* sl) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
int32_t i = fill_command(sl, SG_DXFER_FROM_DEV, 0);
|
||
|
||
cmd[i++] = STLINK_DFU_COMMAND;
|
||
cmd[i++] = STLINK_DFU_EXIT;
|
||
size = send_only(slu, 1, cmd, slu->cmd_len, "DFU_EXIT");
|
||
|
||
return (size < 0 ? -1 : 0);
|
||
}
|
||
|
||
|
||
int32_t _stlink_usb_reset(stlink_t * sl) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const data = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
int32_t i, rep_len = 2;
|
||
|
||
// send reset command
|
||
i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
|
||
if (sl->version.jtag_api == STLINK_JTAG_API_V1) {
|
||
cmd[i++] = STLINK_DEBUG_APIV1_RESETSYS;
|
||
} else {
|
||
cmd[i++] = STLINK_DEBUG_APIV2_RESETSYS;
|
||
}
|
||
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len, CMD_CHECK_RETRY, "RESETSYS");
|
||
|
||
return (size < 0 ? -1 : 0);
|
||
}
|
||
|
||
int32_t _stlink_usb_jtag_reset(stlink_t * sl, int32_t value) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const data = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
int32_t rep_len = 2;
|
||
int32_t i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
||
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
cmd[i++] = STLINK_DEBUG_APIV2_DRIVE_NRST;
|
||
cmd[i++] = value;
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len, CMD_CHECK_RETRY, "DRIVE_NRST");
|
||
|
||
return (size < 0 ? -1 : 0);
|
||
}
|
||
|
||
|
||
int32_t _stlink_usb_step(stlink_t* sl) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
|
||
if (sl->version.jtag_api != STLINK_JTAG_API_V1) {
|
||
// emulates the JTAG v1 API by using DHCSR
|
||
_stlink_usb_write_debug32(sl, STLINK_REG_DHCSR, STLINK_REG_DHCSR_DBGKEY | STLINK_REG_DHCSR_C_HALT |
|
||
STLINK_REG_DHCSR_C_MASKINTS | STLINK_REG_DHCSR_C_DEBUGEN);
|
||
_stlink_usb_write_debug32(sl, STLINK_REG_DHCSR, STLINK_REG_DHCSR_DBGKEY | STLINK_REG_DHCSR_C_STEP |
|
||
STLINK_REG_DHCSR_C_MASKINTS | STLINK_REG_DHCSR_C_DEBUGEN);
|
||
return _stlink_usb_write_debug32(sl, STLINK_REG_DHCSR, STLINK_REG_DHCSR_DBGKEY | STLINK_REG_DHCSR_C_HALT |
|
||
STLINK_REG_DHCSR_C_DEBUGEN);
|
||
}
|
||
|
||
unsigned char* const data = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
int32_t rep_len = 2;
|
||
int32_t i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
||
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
cmd[i++] = STLINK_DEBUG_STEPCORE;
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len, CMD_CHECK_RETRY, "STEPCORE");
|
||
|
||
return (size < 0 ? -1 : 0);
|
||
}
|
||
|
||
/**
|
||
* This seems to do a good job of restarting things from the beginning?
|
||
* @param sl
|
||
* @param type
|
||
*/
|
||
int32_t _stlink_usb_run(stlink_t* sl, enum run_type type) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
|
||
int32_t res;
|
||
|
||
if (sl->version.jtag_api != STLINK_JTAG_API_V1) {
|
||
res = _stlink_usb_write_debug32(sl, STLINK_REG_DHCSR, STLINK_REG_DHCSR_DBGKEY | STLINK_REG_DHCSR_C_DEBUGEN |
|
||
((type==RUN_FLASH_LOADER)?STLINK_REG_DHCSR_C_MASKINTS:0));
|
||
return (res);
|
||
}
|
||
|
||
|
||
unsigned char* const data = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
int32_t rep_len = 2;
|
||
int32_t i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
||
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
cmd[i++] = STLINK_DEBUG_RUNCORE;
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len, CMD_CHECK_RETRY, "RUNCORE");
|
||
|
||
return (size < 0 ? -1 : 0);
|
||
}
|
||
|
||
int32_t _stlink_usb_set_swdclk(stlink_t* sl, int32_t clk_freq) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const data = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
int32_t rep_len = 2;
|
||
int32_t i;
|
||
|
||
// clock speed only supported by stlink/v2 and for firmware >= 22
|
||
if (sl->version.stlink_v == 2 && sl->version.jtag_v >= 22) {
|
||
uint16_t clk_divisor;
|
||
if (clk_freq) {
|
||
const uint32_t map[] = {5, 15, 25, 50, 100, 125, 240, 480, 950, 1200, 1800, 4000};
|
||
int32_t speed_index = _stlink_match_speed_map(map, STLINK_ARRAY_SIZE(map), clk_freq);
|
||
switch (map[speed_index]) {
|
||
case 5: clk_divisor = STLINK_SWDCLK_5KHZ_DIVISOR; break;
|
||
case 15: clk_divisor = STLINK_SWDCLK_15KHZ_DIVISOR; break;
|
||
case 25: clk_divisor = STLINK_SWDCLK_25KHZ_DIVISOR; break;
|
||
case 50: clk_divisor = STLINK_SWDCLK_50KHZ_DIVISOR; break;
|
||
case 100: clk_divisor = STLINK_SWDCLK_100KHZ_DIVISOR; break;
|
||
case 125: clk_divisor = STLINK_SWDCLK_125KHZ_DIVISOR; break;
|
||
case 240: clk_divisor = STLINK_SWDCLK_240KHZ_DIVISOR; break;
|
||
case 480: clk_divisor = STLINK_SWDCLK_480KHZ_DIVISOR; break;
|
||
case 950: clk_divisor = STLINK_SWDCLK_950KHZ_DIVISOR; break;
|
||
case 1200: clk_divisor = STLINK_SWDCLK_1P2MHZ_DIVISOR; break;
|
||
default:
|
||
case 1800: clk_divisor = STLINK_SWDCLK_1P8MHZ_DIVISOR; break;
|
||
case 4000: clk_divisor = STLINK_SWDCLK_4MHZ_DIVISOR; break;
|
||
}
|
||
} else
|
||
clk_divisor = STLINK_SWDCLK_1P8MHZ_DIVISOR;
|
||
|
||
i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
||
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
cmd[i++] = STLINK_DEBUG_APIV2_SWD_SET_FREQ;
|
||
cmd[i++] = clk_divisor & 0xFF;
|
||
cmd[i++] = (clk_divisor >> 8) & 0xFF;
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len, CMD_CHECK_RETRY, "SWD_SET_FREQ");
|
||
|
||
return (size < 0 ? -1 : 0);
|
||
} else if (sl->version.stlink_v == 3) {
|
||
int32_t speed_index;
|
||
uint32_t map[STLINK_V3_MAX_FREQ_NB];
|
||
i = fill_command(sl, SG_DXFER_FROM_DEV, 16);
|
||
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
cmd[i++] = STLINK_DEBUG_APIV3_GET_COM_FREQ;
|
||
cmd[i++] = 0; // SWD mode
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, 52, CMD_CHECK_STATUS, "GET_COM_FREQ");
|
||
|
||
if (size < 0) {
|
||
return (-1);
|
||
}
|
||
|
||
int32_t speeds_size = data[8];
|
||
if (speeds_size > STLINK_V3_MAX_FREQ_NB) {
|
||
speeds_size = STLINK_V3_MAX_FREQ_NB;
|
||
}
|
||
|
||
for (i = 0; i < speeds_size; i++) map[i] = le_to_h_u32(&data[12 + 4 * i]);
|
||
|
||
// Set to zero all the next entries
|
||
for (i = speeds_size; i < STLINK_V3_MAX_FREQ_NB; i++) map[i] = 0;
|
||
|
||
if (!clk_freq) clk_freq = 1000; // set default frequency
|
||
speed_index = _stlink_match_speed_map(map, STLINK_ARRAY_SIZE(map), clk_freq);
|
||
|
||
i = fill_command(sl, SG_DXFER_FROM_DEV, 16);
|
||
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
cmd[i++] = STLINK_DEBUG_APIV3_SET_COM_FREQ;
|
||
cmd[i++] = 0; // SWD mode
|
||
cmd[i++] = 0;
|
||
cmd[i++] = (uint8_t)((map[speed_index] >> 0) & 0xFF);
|
||
cmd[i++] = (uint8_t)((map[speed_index] >> 8) & 0xFF);
|
||
cmd[i++] = (uint8_t)((map[speed_index] >> 16) & 0xFF);
|
||
cmd[i++] = (uint8_t)((map[speed_index] >> 24) & 0xFF);
|
||
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, 8, CMD_CHECK_STATUS, "SET_COM_FREQ");
|
||
|
||
return (size < 0 ? -1 : 0);
|
||
} else if (clk_freq) {
|
||
WLOG("ST-Link firmware does not support frequency setup\n");
|
||
}
|
||
|
||
return (-1);
|
||
}
|
||
|
||
int32_t _stlink_usb_exit_debug_mode(stlink_t *sl) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
int32_t i = fill_command(sl, SG_DXFER_FROM_DEV, 0);
|
||
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
cmd[i++] = STLINK_DEBUG_EXIT;
|
||
|
||
size = send_only(slu, 1, cmd, slu->cmd_len, "DEBUG_EXIT");
|
||
|
||
return (size < 0 ? -1 : 0);
|
||
}
|
||
|
||
int32_t _stlink_usb_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const data = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
int32_t i = fill_command(sl, SG_DXFER_FROM_DEV, len);
|
||
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
cmd[i++] = STLINK_DEBUG_READMEM_32BIT;
|
||
write_uint32(&cmd[i], addr);
|
||
write_uint16(&cmd[i + 4], len);
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, len, CMD_CHECK_NO, "READMEM_32BIT");
|
||
|
||
if (size < 0) {
|
||
return (-1);
|
||
}
|
||
|
||
sl->q_len = (int32_t)size;
|
||
stlink_print_data(sl);
|
||
|
||
return (0);
|
||
}
|
||
|
||
int32_t _stlink_usb_read_all_regs(stlink_t *sl, struct stlink_reg *regp) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
unsigned char* const data = sl->q_buf;
|
||
ssize_t size;
|
||
uint32_t rep_len = sl->version.jtag_api == STLINK_JTAG_API_V1 ? 84 : 88;
|
||
int32_t i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
||
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
|
||
if (sl->version.jtag_api == STLINK_JTAG_API_V1) {
|
||
cmd[i++] = STLINK_DEBUG_APIV1_READALLREGS;
|
||
} else {
|
||
cmd[i++] = STLINK_DEBUG_APIV2_READALLREGS;
|
||
}
|
||
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len, CMD_CHECK_STATUS, "READALLREGS");
|
||
|
||
if (size < 0) {
|
||
return (-1);
|
||
}
|
||
|
||
/* V1: regs data from offset 0 */
|
||
/* V2: status at offset 0, regs data from offset 4 */
|
||
int32_t reg_offset = sl->version.jtag_api == STLINK_JTAG_API_V1 ? 0 : 4;
|
||
sl->q_len = (int32_t)size;
|
||
stlink_print_data(sl);
|
||
|
||
for (i = 0; i < 16; i++) regp->r[i] = read_uint32(sl->q_buf, reg_offset + i * 4);
|
||
|
||
regp->xpsr = read_uint32(sl->q_buf, reg_offset + 64);
|
||
regp->main_sp = read_uint32(sl->q_buf, reg_offset + 68);
|
||
regp->process_sp = read_uint32(sl->q_buf, reg_offset + 72);
|
||
regp->rw = read_uint32(sl->q_buf, reg_offset + 76);
|
||
regp->rw2 = read_uint32(sl->q_buf, reg_offset + 80);
|
||
|
||
if (sl->verbose < 2) { return (0); }
|
||
|
||
DLOG("xpsr = 0x%08x\n", regp->xpsr);
|
||
DLOG("main_sp = 0x%08x\n", regp->main_sp);
|
||
DLOG("process_sp = 0x%08x\n", regp->process_sp);
|
||
DLOG("rw = 0x%08x\n", regp->rw);
|
||
DLOG("rw2 = 0x%08x\n", regp->rw2);
|
||
|
||
return (0);
|
||
}
|
||
|
||
int32_t _stlink_usb_read_reg(stlink_t *sl, int32_t r_idx, struct stlink_reg *regp) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const data = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
uint32_t r;
|
||
uint32_t rep_len = sl->version.jtag_api == STLINK_JTAG_API_V1 ? 4 : 8;
|
||
int32_t reg_offset = sl->version.jtag_api == STLINK_JTAG_API_V1 ? 0 : 4;
|
||
int32_t i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
||
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
|
||
if (sl->version.jtag_api == STLINK_JTAG_API_V1) {
|
||
cmd[i++] = STLINK_DEBUG_APIV1_READREG;
|
||
} else {
|
||
cmd[i++] = STLINK_DEBUG_APIV2_READREG;
|
||
}
|
||
|
||
cmd[i++] = (uint8_t)r_idx;
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len, CMD_CHECK_RETRY, "READREG");
|
||
|
||
if (size < 0) {
|
||
return (-1);
|
||
}
|
||
|
||
sl->q_len = (int32_t)size;
|
||
stlink_print_data(sl);
|
||
r = read_uint32(sl->q_buf, reg_offset);
|
||
DLOG("r_idx (%2d) = 0x%08x\n", r_idx, r);
|
||
|
||
switch (r_idx) {
|
||
case 16:
|
||
regp->xpsr = r;
|
||
break;
|
||
case 17:
|
||
regp->main_sp = r;
|
||
break;
|
||
case 18:
|
||
regp->process_sp = r;
|
||
break;
|
||
case 19:
|
||
regp->rw = r; // XXX ?(primask, basemask etc.)
|
||
break;
|
||
case 20:
|
||
regp->rw2 = r; // XXX ?(primask, basemask etc.)
|
||
break;
|
||
default:
|
||
regp->r[r_idx] = r;
|
||
}
|
||
|
||
return (0);
|
||
}
|
||
|
||
/* See section C1.6 of the ARMv7-M Architecture Reference Manual */
|
||
int32_t _stlink_usb_read_unsupported_reg(stlink_t *sl, int32_t r_idx, struct stlink_reg *regp) {
|
||
uint32_t r;
|
||
int32_t ret;
|
||
|
||
sl->q_buf[0] = (unsigned char)r_idx;
|
||
|
||
for (int32_t i = 1; i < 4; i++) sl->q_buf[i] = 0;
|
||
|
||
ret = _stlink_usb_write_mem32(sl, STLINK_REG_DCRSR, 4);
|
||
|
||
if (ret == -1) { return (ret); }
|
||
|
||
ret = _stlink_usb_read_mem32(sl, STLINK_REG_DCRDR, 4);
|
||
|
||
if (ret == -1) { return (ret); }
|
||
|
||
r = read_uint32(sl->q_buf, 0);
|
||
DLOG("r_idx (%2d) = 0x%08x\n", r_idx, r);
|
||
|
||
switch (r_idx) {
|
||
case 0x14:
|
||
regp->primask = (uint8_t)(r & 0xFF);
|
||
regp->basepri = (uint8_t)((r >> 8) & 0xFF);
|
||
regp->faultmask = (uint8_t)((r >> 16) & 0xFF);
|
||
regp->control = (uint8_t)((r >> 24) & 0xFF);
|
||
break;
|
||
case 0x21:
|
||
regp->fpscr = r;
|
||
break;
|
||
default:
|
||
regp->s[r_idx - 0x40] = r;
|
||
break;
|
||
}
|
||
|
||
return (0);
|
||
}
|
||
|
||
int32_t _stlink_usb_read_all_unsupported_regs(stlink_t *sl, struct stlink_reg *regp) {
|
||
int32_t ret;
|
||
|
||
ret = _stlink_usb_read_unsupported_reg(sl, 0x14, regp);
|
||
|
||
if (ret == -1) { return (ret); }
|
||
|
||
ret = _stlink_usb_read_unsupported_reg(sl, 0x21, regp);
|
||
|
||
if (ret == -1) { return (ret); }
|
||
|
||
for (int32_t i = 0; i < 32; i++) {
|
||
ret = _stlink_usb_read_unsupported_reg(sl, 0x40 + i, regp);
|
||
|
||
if (ret == -1) { return (ret); }
|
||
}
|
||
|
||
return (0);
|
||
}
|
||
|
||
/* See section C1.6 of the ARMv7-M Architecture Reference Manual */
|
||
int32_t _stlink_usb_write_unsupported_reg(stlink_t *sl, uint32_t val, int32_t r_idx, struct stlink_reg *regp) {
|
||
int32_t ret;
|
||
|
||
if (r_idx >= 0x1C && r_idx <= 0x1F) { // primask, basepri, faultmask, or control
|
||
/* These are held in the same register */
|
||
ret = _stlink_usb_read_unsupported_reg(sl, 0x14, regp);
|
||
|
||
if (ret == -1) { return (ret); }
|
||
|
||
val = (uint8_t)(val >> 24);
|
||
|
||
switch (r_idx) {
|
||
case 0x1C: /* control */
|
||
val = (((uint32_t)val) << 24) |
|
||
(((uint32_t)regp->faultmask) << 16) |
|
||
(((uint32_t)regp->basepri) << 8) |
|
||
((uint32_t)regp->primask);
|
||
break;
|
||
case 0x1D: /* faultmask */
|
||
val = (((uint32_t)regp->control) << 24) |
|
||
(((uint32_t)val) << 16) |
|
||
(((uint32_t)regp->basepri) << 8) |
|
||
((uint32_t)regp->primask);
|
||
break;
|
||
case 0x1E: /* basepri */
|
||
val = (((uint32_t)regp->control) << 24) |
|
||
(((uint32_t)regp->faultmask) << 16) |
|
||
(((uint32_t)val) << 8) |
|
||
((uint32_t)regp->primask);
|
||
break;
|
||
case 0x1F: /* primask */
|
||
val = (((uint32_t)regp->control) << 24) |
|
||
(((uint32_t)regp->faultmask) << 16) |
|
||
(((uint32_t)regp->basepri) << 8) |
|
||
((uint32_t)val);
|
||
break;
|
||
}
|
||
|
||
r_idx = 0x14;
|
||
}
|
||
|
||
write_uint32(sl->q_buf, val);
|
||
|
||
ret = _stlink_usb_write_mem32(sl, STLINK_REG_DCRDR, 4);
|
||
|
||
if (ret == -1) { return (ret); }
|
||
|
||
sl->q_buf[0] = (unsigned char)r_idx;
|
||
sl->q_buf[1] = 0;
|
||
sl->q_buf[2] = 0x01;
|
||
sl->q_buf[3] = 0;
|
||
|
||
return (_stlink_usb_write_mem32(sl, STLINK_REG_DCRSR, 4));
|
||
}
|
||
|
||
int32_t _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int32_t idx) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const data = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
uint32_t rep_len = 2;
|
||
int32_t i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
||
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
|
||
if (sl->version.jtag_api == STLINK_JTAG_API_V1) {
|
||
cmd[i++] = STLINK_DEBUG_APIV1_WRITEREG;
|
||
} else {
|
||
cmd[i++] = STLINK_DEBUG_APIV2_WRITEREG;
|
||
}
|
||
|
||
cmd[i++] = idx;
|
||
write_uint32(&cmd[i], reg);
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len, CMD_CHECK_RETRY, "WRITEREG");
|
||
|
||
return (size < 0 ? -1 : 0);
|
||
}
|
||
|
||
int32_t _stlink_usb_enable_trace(stlink_t* sl, uint32_t frequency) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const data = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
uint32_t rep_len = 2;
|
||
|
||
int32_t i = fill_command(sl, SG_DXFER_TO_DEV, rep_len);
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
cmd[i++] = STLINK_DEBUG_APIV2_START_TRACE_RX;
|
||
write_uint16(&cmd[i + 0], 2 * STLINK_TRACE_BUF_LEN);
|
||
write_uint32(&cmd[i + 2], frequency);
|
||
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len, CMD_CHECK_STATUS, "START_TRACE_RX");
|
||
|
||
return (size < 0 ? -1 : 0);
|
||
}
|
||
|
||
int32_t _stlink_usb_disable_trace(stlink_t* sl) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const data = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
ssize_t size;
|
||
uint32_t rep_len = 2;
|
||
|
||
int32_t i = fill_command(sl, SG_DXFER_TO_DEV, rep_len);
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
cmd[i++] = STLINK_DEBUG_APIV2_STOP_TRACE_RX;
|
||
|
||
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len, CMD_CHECK_STATUS, "STOP_TRACE_RX");
|
||
|
||
return (size < 0 ? -1 : 0);
|
||
}
|
||
|
||
int32_t _stlink_usb_read_trace(stlink_t* sl, uint8_t* buf, uint32_t size) {
|
||
struct stlink_libusb * const slu = sl->backend_data;
|
||
unsigned char* const data = sl->q_buf;
|
||
unsigned char* const cmd = sl->c_buf;
|
||
uint32_t rep_len = 2;
|
||
int32_t i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
|
||
|
||
cmd[i++] = STLINK_DEBUG_COMMAND;
|
||
cmd[i++] = STLINK_DEBUG_APIV2_GET_TRACE_NB;
|
||
ssize_t send_size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len, CMD_CHECK_NO, "GET_TRACE_NB");
|
||
|
||
if (send_size < 0) {
|
||
return (-1);
|
||
} else if (send_size != 2) {
|
||
ELOG("STLINK_DEBUG_APIV2_GET_TRACE_NB reply size %d\n", (int32_t)send_size);
|
||
return (-1);
|
||
}
|
||
|
||
uint16_t trace_count = read_uint16(sl->q_buf, 0);
|
||
|
||
if (trace_count > size) {
|
||
ELOG("read_trace insufficient buffer length\n");
|
||
return -1;
|
||
}
|
||
|
||
if (trace_count != 0) {
|
||
int32_t res = 0;
|
||
int32_t t = libusb_bulk_transfer(slu->usb_handle, slu->ep_trace, buf, trace_count, &res, 3000);
|
||
|
||
if (t || res != (int32_t)trace_count) {
|
||
ELOG("read_trace read error %d\n", t);
|
||
return (-1);
|
||
}
|
||
}
|
||
|
||
return trace_count;
|
||
}
|
||
|
||
static stlink_backend_t _stlink_usb_backend = {
|
||
_stlink_usb_close,
|
||
_stlink_usb_exit_debug_mode,
|
||
_stlink_usb_enter_swd_mode,
|
||
NULL, // don't enter_jtag_mode here...
|
||
_stlink_usb_exit_dfu_mode,
|
||
_stlink_usb_core_id,
|
||
_stlink_usb_reset,
|
||
_stlink_usb_jtag_reset,
|
||
_stlink_usb_run,
|
||
_stlink_usb_status,
|
||
_stlink_usb_version,
|
||
_stlink_usb_read_debug32,
|
||
_stlink_usb_read_mem32,
|
||
_stlink_usb_write_debug32,
|
||
_stlink_usb_write_mem32,
|
||
_stlink_usb_write_mem8,
|
||
_stlink_usb_read_all_regs,
|
||
_stlink_usb_read_reg,
|
||
_stlink_usb_read_all_unsupported_regs,
|
||
_stlink_usb_read_unsupported_reg,
|
||
_stlink_usb_write_unsupported_reg,
|
||
_stlink_usb_write_reg,
|
||
_stlink_usb_step,
|
||
_stlink_usb_current_mode,
|
||
_stlink_usb_force_debug,
|
||
_stlink_usb_target_voltage,
|
||
_stlink_usb_set_swdclk,
|
||
_stlink_usb_enable_trace,
|
||
_stlink_usb_disable_trace,
|
||
_stlink_usb_read_trace
|
||
};
|
||
|
||
/* return the length of serial or (0) in case of errors */
|
||
size_t stlink_serial(struct libusb_device_handle *handle, struct libusb_device_descriptor *desc, char *serial) {
|
||
unsigned char desc_serial[(STLINK_SERIAL_LENGTH) * 2];
|
||
|
||
/* truncate the string in the serial buffer */
|
||
serial[0] = '\0';
|
||
|
||
/* get the LANGID from String Descriptor Zero */
|
||
int32_t ret = libusb_get_string_descriptor(handle, 0, 0, desc_serial, sizeof(desc_serial));
|
||
if (ret < 4) return 0;
|
||
|
||
uint32_t langid = desc_serial[2] | (desc_serial[3] << 8);
|
||
|
||
/* get the serial */
|
||
ret = libusb_get_string_descriptor(handle, desc->iSerialNumber, langid, desc_serial,
|
||
sizeof(desc_serial));
|
||
if (ret < 0) return 0; // could not read serial
|
||
|
||
unsigned char len = desc_serial[0];
|
||
|
||
if (len == ((STLINK_SERIAL_LENGTH + 1) * 2)) { /* len == 50 */
|
||
/* good ST-Link adapter */
|
||
ret = libusb_get_string_descriptor_ascii(
|
||
handle, desc->iSerialNumber, (unsigned char *)serial, STLINK_SERIAL_BUFFER_SIZE);
|
||
if (ret < 0) return 0;
|
||
} else if (len == ((STLINK_SERIAL_LENGTH / 2 + 1) * 2)) { /* len == 26 */
|
||
/* fix-up the buggy serial */
|
||
for (uint32_t i = 0; i < STLINK_SERIAL_LENGTH; i += 2)
|
||
sprintf(serial + i, "%02X", desc_serial[i + 2]);
|
||
serial[STLINK_SERIAL_LENGTH] = '\0';
|
||
} else {
|
||
return 0;
|
||
}
|
||
|
||
return strlen(serial);
|
||
}
|
||
|
||
/**
|
||
* Open a stlink
|
||
* @param verbose Verbosity loglevel
|
||
* @param connect Type of connect to target
|
||
* @param serial Serial number to search for, when NULL the first stlink found is opened (binary format)
|
||
* @retval NULL Error while opening the stlink
|
||
* @retval !NULL Stlink found and ready to use
|
||
*/
|
||
stlink_t *stlink_open_usb(enum ugly_loglevel verbose, enum connect_type connect, char serial[STLINK_SERIAL_BUFFER_SIZE], int32_t freq) {
|
||
stlink_t* sl = NULL;
|
||
struct stlink_libusb* slu = NULL;
|
||
int32_t ret = -1;
|
||
int32_t config;
|
||
|
||
sl = calloc(1, sizeof(stlink_t));
|
||
if (sl == NULL) { goto on_malloc_error; }
|
||
|
||
slu = calloc(1, sizeof(struct stlink_libusb));
|
||
if (slu == NULL) { goto on_malloc_error; }
|
||
|
||
ugly_init(verbose);
|
||
sl->backend = &_stlink_usb_backend;
|
||
sl->backend_data = slu;
|
||
|
||
sl->core_stat = TARGET_UNKNOWN;
|
||
|
||
if (libusb_init(&(slu->libusb_ctx))) {
|
||
WLOG("failed to init libusb context, wrong version of libraries?\n");
|
||
goto on_error;
|
||
}
|
||
|
||
#if LIBUSB_API_VERSION < 0x01000106
|
||
libusb_set_debug(slu->libusb_ctx, ugly_libusb_log_level(verbose));
|
||
#else
|
||
libusb_set_option(slu->libusb_ctx, LIBUSB_OPTION_LOG_LEVEL, ugly_libusb_log_level(verbose));
|
||
#endif
|
||
|
||
libusb_device **list = NULL;
|
||
ssize_t cnt = libusb_get_device_list(slu->libusb_ctx, &list);
|
||
struct libusb_device_descriptor desc;
|
||
|
||
while (cnt-- > 0) {
|
||
struct libusb_device_handle *handle;
|
||
|
||
libusb_get_device_descriptor(list[cnt], &desc);
|
||
|
||
if (desc.idVendor != STLINK_USB_VID_ST) { continue; }
|
||
|
||
ret = libusb_open(list[cnt], &handle);
|
||
|
||
if (ret) { continue; } // could not open device
|
||
|
||
uint32_t serial_len = stlink_serial(handle, &desc, sl->serial);
|
||
|
||
libusb_close(handle);
|
||
|
||
if (serial_len != STLINK_SERIAL_LENGTH) { continue; } // could not read the serial
|
||
|
||
// if no serial provided, or if serial match device, fixup version and protocol
|
||
if (((serial == NULL) || (*serial == 0)) || (memcmp(serial, &sl->serial, STLINK_SERIAL_LENGTH) == 0)) {
|
||
if (STLINK_V1_USB_PID(desc.idProduct)) {
|
||
slu->protocoll = 1;
|
||
sl->version.stlink_v = 1;
|
||
} else if (STLINK_V2_USB_PID(desc.idProduct) || STLINK_V2_1_USB_PID(desc.idProduct)) {
|
||
sl->version.stlink_v = 2;
|
||
} else if (STLINK_V3_USB_PID(desc.idProduct)) {
|
||
sl->version.stlink_v = 3;
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (cnt < 0) {
|
||
WLOG ("Couldn't find any ST-Link devices\n");
|
||
libusb_free_device_list(list, 1);
|
||
goto on_error;
|
||
} else {
|
||
ret = libusb_open(list[cnt], &slu->usb_handle);
|
||
|
||
if (ret != 0) {
|
||
WLOG("Error %d (%s) opening ST-Link v%d device %03d:%03d\n", ret,
|
||
strerror(errno),
|
||
sl->version.stlink_v,
|
||
libusb_get_bus_number(list[cnt]),
|
||
libusb_get_device_address(list[cnt]));
|
||
libusb_free_device_list(list, 1);
|
||
goto on_error;
|
||
}
|
||
}
|
||
|
||
libusb_free_device_list(list, 1);
|
||
|
||
// libusb_kernel_driver_active is not available on Windows.
|
||
#if !defined(_WIN32)
|
||
if (libusb_kernel_driver_active(slu->usb_handle, 0) == 1) {
|
||
ret = libusb_detach_kernel_driver(slu->usb_handle, 0);
|
||
|
||
if (ret < 0) {
|
||
WLOG("libusb_detach_kernel_driver(() error %s\n", strerror(-ret));
|
||
goto on_libusb_error;
|
||
}
|
||
}
|
||
#endif // NOT _WIN32
|
||
|
||
if (libusb_get_configuration(slu->usb_handle, &config)) {
|
||
// this may fail for a previous configured device
|
||
WLOG("libusb_get_configuration()\n");
|
||
goto on_libusb_error;
|
||
}
|
||
|
||
if (config != 1) {
|
||
printf("setting new configuration (%d -> 1)\n", config);
|
||
|
||
if (libusb_set_configuration(slu->usb_handle, 1)) {
|
||
// this may fail for a previous configured device
|
||
WLOG("libusb_set_configuration() failed\n");
|
||
goto on_libusb_error;
|
||
}
|
||
}
|
||
|
||
if (libusb_claim_interface(slu->usb_handle, 0)) {
|
||
WLOG("Stlink usb device found, but unable to claim (probably already in use?)\n");
|
||
goto on_libusb_error;
|
||
}
|
||
|
||
// TODO: Could use the scanning technique from STM8 code here...
|
||
slu->ep_rep = 1 /* ep rep */ | LIBUSB_ENDPOINT_IN;
|
||
|
||
if (desc.idProduct == STLINK_USB_PID_STLINK_NUCLEO ||
|
||
desc.idProduct == STLINK_USB_PID_STLINK_32L_AUDIO ||
|
||
desc.idProduct == STLINK_USB_PID_STLINK_V2_1 ||
|
||
desc.idProduct == STLINK_USB_PID_STLINK_V3_USBLOADER ||
|
||
desc.idProduct == STLINK_USB_PID_STLINK_V3E_PID ||
|
||
desc.idProduct == STLINK_USB_PID_STLINK_V3S_PID ||
|
||
desc.idProduct == STLINK_USB_PID_STLINK_V3_2VCP_PID ||
|
||
desc.idProduct == STLINK_USB_PID_STLINK_V3_NO_MSD_PID) {
|
||
slu->ep_req = 1 /* ep req */ | LIBUSB_ENDPOINT_OUT;
|
||
slu->ep_trace = 2 | LIBUSB_ENDPOINT_IN;
|
||
} else {
|
||
slu->ep_req = 2 /* ep req */ | LIBUSB_ENDPOINT_OUT;
|
||
slu->ep_trace = 3 | LIBUSB_ENDPOINT_IN;
|
||
}
|
||
|
||
slu->sg_transfer_idx = 0;
|
||
slu->cmd_len = (slu->protocoll == 1) ? STLINK_SG_SIZE : STLINK_CMD_SIZE;
|
||
|
||
// initialize stlink version (sl->version)
|
||
stlink_version(sl);
|
||
|
||
int32_t mode = stlink_current_mode(sl);
|
||
if (mode == STLINK_DEV_DFU_MODE) {
|
||
DLOG("-- exit_dfu_mode\n");
|
||
_stlink_usb_exit_dfu_mode(sl);
|
||
}
|
||
|
||
if (connect == CONNECT_UNDER_RESET) {
|
||
// for the connect under reset only
|
||
// OpenOСD says (official documentation is not available) that
|
||
// the NRST pin must be pull down before selecting the SWD/JTAG mode
|
||
if (mode == STLINK_DEV_DEBUG_MODE) {
|
||
DLOG("-- exit_debug_mode\n");
|
||
_stlink_usb_exit_debug_mode(sl);
|
||
}
|
||
|
||
_stlink_usb_jtag_reset(sl, STLINK_DEBUG_APIV2_DRIVE_NRST_LOW);
|
||
}
|
||
|
||
sl->freq = freq;
|
||
// set the speed before entering the mode as the chip discovery phase
|
||
// should be done at this speed too
|
||
// set the stlink clock speed (default is 1800kHz)
|
||
DLOG("JTAG/SWD freq set to %d\n", freq);
|
||
_stlink_usb_set_swdclk(sl, freq);
|
||
|
||
stlink_target_connect(sl, connect);
|
||
return (sl);
|
||
|
||
on_libusb_error:
|
||
stlink_close(sl);
|
||
return (NULL);
|
||
|
||
on_error:
|
||
if (slu->libusb_ctx) { libusb_exit(slu->libusb_ctx); }
|
||
|
||
on_malloc_error:
|
||
if (sl != NULL) { free(sl); }
|
||
if (slu != NULL) { free(slu); }
|
||
|
||
return (NULL);
|
||
}
|
||
|
||
static uint32_t stlink_probe_usb_devs(libusb_device **devs, stlink_t **sldevs[], enum connect_type connect, int32_t freq) {
|
||
stlink_t **_sldevs;
|
||
libusb_device *dev;
|
||
int32_t i = 0;
|
||
uint32_t slcnt = 0;
|
||
uint32_t slcur = 0;
|
||
|
||
/* Count STLINKs */
|
||
while ((dev = devs[i++]) != NULL) {
|
||
struct libusb_device_descriptor desc;
|
||
int32_t ret = libusb_get_device_descriptor(dev, &desc);
|
||
|
||
if (ret < 0) {
|
||
WLOG("failed to get libusb device descriptor (libusb error: %d)\n", ret);
|
||
break;
|
||
}
|
||
|
||
if (desc.idVendor != STLINK_USB_VID_ST) { continue; }
|
||
|
||
if (!STLINK_SUPPORTED_USB_PID(desc.idProduct)) {
|
||
WLOG("skipping ST device : %#04x:%#04x)\n", desc.idVendor, desc.idProduct);
|
||
continue;
|
||
}
|
||
|
||
slcnt++;
|
||
}
|
||
|
||
_sldevs = calloc(slcnt, sizeof(stlink_t *)); // allocate list of pointers
|
||
|
||
if (!_sldevs) {
|
||
*sldevs = NULL;
|
||
return (0);
|
||
}
|
||
|
||
/* Open STLINKS and attach them to list */
|
||
i = 0;
|
||
|
||
while ((dev = devs[i++]) != NULL) {
|
||
struct libusb_device_descriptor desc;
|
||
int32_t ret = libusb_get_device_descriptor(dev, &desc);
|
||
|
||
if (ret < 0) {
|
||
WLOG("failed to get libusb device descriptor (libusb error: %d)\n", ret);
|
||
break;
|
||
}
|
||
|
||
if (!STLINK_SUPPORTED_USB_PID(desc.idProduct)) { continue; }
|
||
|
||
struct libusb_device_handle* handle;
|
||
char serial[STLINK_SERIAL_BUFFER_SIZE] = {0, };
|
||
|
||
ret = libusb_open(dev, &handle);
|
||
|
||
if (ret < 0) {
|
||
if (ret == LIBUSB_ERROR_ACCESS) {
|
||
ELOG("Could not open USB device %#06x:%#06x, access error.\n", desc.idVendor, desc.idProduct);
|
||
} else {
|
||
ELOG("Failed to open USB device %#06x:%#06x, libusb error: %d)\n", desc.idVendor, desc.idProduct, ret);
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
uint32_t serial_len = stlink_serial(handle, &desc, serial);
|
||
|
||
libusb_close(handle);
|
||
|
||
if (serial_len != STLINK_SERIAL_LENGTH) { continue; }
|
||
|
||
stlink_t *sl = stlink_open_usb(0, connect, serial, freq);
|
||
|
||
if (!sl) {
|
||
ELOG("Failed to open USB device %#06x:%#06x\n", desc.idVendor, desc.idProduct);
|
||
continue;
|
||
}
|
||
|
||
_sldevs[slcur++] = sl;
|
||
}
|
||
|
||
*sldevs = _sldevs;
|
||
|
||
return (slcur);
|
||
}
|
||
|
||
size_t stlink_probe_usb(stlink_t **stdevs[], enum connect_type connect, int32_t freq) {
|
||
libusb_device **devs;
|
||
stlink_t **sldevs;
|
||
|
||
uint32_t slcnt = 0;
|
||
int32_t r;
|
||
ssize_t cnt;
|
||
|
||
r = libusb_init(NULL);
|
||
|
||
if (r < 0) { return (0); }
|
||
|
||
cnt = libusb_get_device_list(NULL, &devs);
|
||
|
||
if (cnt < 0) { return (0); }
|
||
|
||
slcnt = stlink_probe_usb_devs(devs, &sldevs, connect, freq);
|
||
libusb_free_device_list(devs, 1);
|
||
|
||
libusb_exit(NULL);
|
||
|
||
*stdevs = sldevs;
|
||
|
||
return (slcnt);
|
||
}
|
||
|
||
void stlink_probe_usb_free(stlink_t ***stdevs, uint32_t size) {
|
||
if (stdevs == NULL || *stdevs == NULL || size == 0) { return; }
|
||
|
||
for (uint32_t n = 0; n < size; n++) { stlink_close((*stdevs)[n]); }
|
||
|
||
free(*stdevs);
|
||
*stdevs = NULL;
|
||
}
|