stlink/src/usb.c

1196 wiersze
34 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <limits.h>
#if !defined(_MSC_VER)
#include <sys/time.h>
#endif
#include <sys/types.h>
#include <mingw.h>
#include <errno.h>
#include <unistd.h>
#include "stlink.h"
enum SCSI_Generic_Direction {SG_DXFER_TO_DEV=0, SG_DXFER_FROM_DEV=0x80};
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 int _stlink_match_speed_map(const uint32_t *map, unsigned int map_size, uint32_t khz)
{
unsigned int i;
int speed_index = -1;
int speed_diff = INT_MAX;
int 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 {
int current_diff = khz - map[i];
/* get abs value for comparison */
current_diff = (current_diff > 0) ? current_diff : -current_diff;
if ((current_diff < speed_diff) && khz >= map[i]) {
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, int terminate,
unsigned char* txbuf, size_t txsize,
unsigned char* rxbuf, size_t rxsize) {
/* note: txbuf and rxbuf can point to the same area */
int res = 0;
int t;
t = libusb_bulk_transfer(handle->usb_handle, handle->ep_req,
txbuf,
(int) txsize,
&res,
3000);
if (t) {
printf("[!] send_recv send request failed: %s\n", libusb_error_name(t));
return -1;
} else if ((size_t)res != txsize) {
printf("[!] send_recv send request wrote %u bytes (instead of %u).\n",
(unsigned int)res, (unsigned int)txsize);
}
if (rxsize != 0) {
t = libusb_bulk_transfer(handle->usb_handle, handle->ep_rep,
rxbuf,
(int) rxsize,
&res,
3000);
if (t) {
printf("[!] send_recv read reply failed: %s\n",
libusb_error_name(t));
return -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) {
printf("[!] send_recv read storage failed: %s\n",
libusb_error_name(t));
return -1;
}
/* The STLink doesn't seem to evaluate the sequence number */
handle->sg_transfer_idx++;
}
return res;
}
static inline int send_only
(struct stlink_libusb* handle, int terminate,
unsigned char* txbuf, size_t txsize) {
return (int) send_recv(handle, terminate, txbuf, txsize, NULL, 0);
}
static int 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;
int 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;
}
int _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 = 6;
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
cmd[i++] = STLINK_GET_VERSION;
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
if (size == -1) {
printf("[!] send_recv STLINK_GET_VERSION\n");
return (int) size;
}
/* STLINK-V3 requires a specific command */
if (sl->version.stlink_v == 3) {
rep_len = 12;
i = fill_command(sl, SG_DXFER_FROM_DEV, 16);
cmd[i++] = STLINK_APIV3_GET_VERSION_EX;
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
if (size != (ssize_t)rep_len) {
printf("[!] send_recv STLINK_APIV3_GET_VERSION_EX\n");
return (int) size;
}
}
return 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;
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
uint32_t factor, reading;
int voltage;
cmd[i++] = STLINK_GET_TARGET_VOLTAGE;
size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len);
if (size == -1) {
printf("[!] send_recv STLINK_GET_TARGET_VOLTAGE\n");
return -1;
} else if (size != 8) {
printf("[!] wrong length STLINK_GET_TARGET_VOLTAGE\n");
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;
}
int _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 int rep_len = 8;
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
cmd[i++] = STLINK_DEBUG_COMMAND;
cmd[i++] = STLINK_JTAG_READDEBUG_32BIT;
write_uint32(&cmd[i], addr);
size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len);
if (size == -1) {
printf("[!] send_recv STLINK_JTAG_READDEBUG_32BIT\n");
return (int) size;
}
*data = read_uint32(rdata, 4);
return 0;
}
int _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 int rep_len = 2;
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
cmd[i++] = STLINK_DEBUG_COMMAND;
cmd[i++] = STLINK_JTAG_WRITEDEBUG_32BIT;
write_uint32(&cmd[i], addr);
write_uint32(&cmd[i + 4], data);
size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len);
if (size == -1) {
printf("[!] send_recv STLINK_JTAG_WRITEDEBUG_32BIT\n");
return (int) size;
}
return 0;
}
int _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;
int 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);
if (ret == -1)
return ret;
ret = send_only(slu, 1, data, len);
if (ret == -1)
return ret;
return 0;
}
int _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;
int i, ret;
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);
if (ret == -1)
return ret;
ret = send_only(slu, 1, data, len);
if (ret == -1)
return ret;
return 0;
}
int _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;
int rep_len = 2;
int 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);
if (size == -1) {
printf("[!] send_recv STLINK_GET_CURRENT_MODE\n");
return -1;
}
return sl->q_buf[0];
}
int _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;
int rep_len = 4;
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
cmd[i++] = STLINK_DEBUG_COMMAND;
cmd[i++] = STLINK_DEBUG_READCOREID;
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
if (size == -1) {
printf("[!] send_recv STLINK_DEBUG_READCOREID\n");
return -1;
}
sl->core_id = read_uint32(data, 0);
return 0;
}
int _stlink_usb_status(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;
int rep_len = 2;
int 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);
if (size == -1) {
printf("[!] send_recv STLINK_DEBUG_GETSTATUS\n");
return (int) size;
}
sl->q_len = (int) size;
return 0;
}
int _stlink_usb_force_debug(stlink_t *sl) {
struct stlink_libusb *slu = sl->backend_data;
unsigned char* const data = sl->q_buf;
unsigned char* const cmd = sl->c_buf;
ssize_t size;
int rep_len = 2;
int 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);
if (size == -1) {
printf("[!] send_recv STLINK_DEBUG_FORCEDEBUG\n");
return (int) size;
}
return 0;
}
int _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.stlink_v > 1 ? 2 : 0;
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
cmd[i++] = STLINK_DEBUG_COMMAND;
cmd[i++] = sl->version.stlink_v > 1? STLINK_DEBUG_APIV2_ENTER : STLINK_DEBUG_ENTER;
cmd[i++] = STLINK_DEBUG_ENTER_SWD;
if (rep_len == 0) {
size = send_only(slu, 1, cmd, slu->cmd_len);
} else {
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
}
if (size == -1) {
printf("[!] send_recv STLINK_DEBUG_ENTER\n");
return (int) size;
}
return 0;
}
int _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;
int 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);
if (size == -1) {
printf("[!] send_recv STLINK_DFU_EXIT\n");
return (int) size;
}
return 0;
}
int _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;
int rep_len = 2;
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
cmd[i++] = STLINK_DEBUG_COMMAND;
cmd[i++] = STLINK_DEBUG_RESETSYS;
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
if (size == -1) {
printf("[!] send_recv STLINK_DEBUG_RESETSYS\n");
return (int) size;
}
// Reset through AIRCR so NRST does not need to be connected
return stlink_write_debug32(sl, STLINK_REG_AIRCR,
STLINK_REG_AIRCR_VECTKEY | STLINK_REG_AIRCR_SYSRESETREQ);
}
int _stlink_usb_jtag_reset(stlink_t * sl, int 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;
int rep_len = 2;
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
cmd[i++] = STLINK_DEBUG_COMMAND;
cmd[i++] = STLINK_JTAG_DRIVE_NRST;
cmd[i++] = value;
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
if (size == -1) {
printf("[!] send_recv STLINK_JTAG_DRIVE_NRST\n");
return (int) size;
}
return 0;
}
int _stlink_usb_step(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;
int rep_len = 2;
int 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);
if (size == -1) {
printf("[!] send_recv STLINK_DEBUG_STEPCORE\n");
return (int) size;
}
return 0;
}
/**
* This seems to do a good job of restarting things from the beginning?
* @param sl
*/
int _stlink_usb_run(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;
int rep_len = 2;
int 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);
if (size == -1) {
printf("[!] send_recv STLINK_DEBUG_RUNCORE\n");
return (int) size;
}
return 0;
}
int _stlink_usb_set_swdclk(stlink_t* sl, uint16_t clk_divisor) {
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;
int rep_len = 2;
int i;
// clock speed only supported by stlink/v2 and for firmware >= 22
if (sl->version.stlink_v == 2 && sl->version.jtag_v >= 22) {
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);
if (size == -1) {
printf("[!] send_recv STLINK_DEBUG_APIV2_SWD_SET_FREQ\n");
return (int) size;
}
return 0;
} else if (sl->version.stlink_v == 3) {
int 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_APIV3_GET_COM_FREQ;
cmd[i++] = 0; // SWD mode
size = send_recv(slu, 1, cmd, slu->cmd_len, data, 52);
if (size == -1) {
printf("[!] send_recv STLINK_APIV3_GET_COM_FREQ\n");
return (int) size;
}
int 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;
speed_index = _stlink_match_speed_map(map, STLINK_ARRAY_SIZE(map), 1800);
i = fill_command(sl, SG_DXFER_FROM_DEV, 16);
cmd[i++] = STLINK_DEBUG_COMMAND;
cmd[i++] = STLINK_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);
if (size == -1) {
printf("[!] send_recv STLINK_APIV3_SET_COM_FREQ\n");
return (int) size;
}
return 0;
}
return -1;
}
int _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;
int 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);
if (size == -1) {
printf("[!] send_only STLINK_DEBUG_EXIT\n");
return (int) size;
}
return 0;
}
int _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;
int 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);
if (size == -1) {
printf("[!] send_recv STLINK_DEBUG_READMEM_32BIT\n");
return (int) size;
}
sl->q_len = (int) size;
stlink_print_data(sl);
return 0;
}
int _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 = 84;
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
cmd[i++] = STLINK_DEBUG_COMMAND;
cmd[i++] = STLINK_DEBUG_READALLREGS;
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
if (size == -1) {
printf("[!] send_recv STLINK_DEBUG_READALLREGS\n");
return (int) size;
}
sl->q_len = (int) size;
stlink_print_data(sl);
for (i=0; i<16; i++)
regp->r[i]= read_uint32(sl->q_buf, i*4);
regp->xpsr = read_uint32(sl->q_buf, 64);
regp->main_sp = read_uint32(sl->q_buf, 68);
regp->process_sp = read_uint32(sl->q_buf, 72);
regp->rw = read_uint32(sl->q_buf, 76);
regp->rw2 = read_uint32(sl->q_buf, 80);
if (sl->verbose < 2)
return 0;
DLOG("xpsr = 0x%08x\n", read_uint32(sl->q_buf, 64));
DLOG("main_sp = 0x%08x\n", read_uint32(sl->q_buf, 68));
DLOG("process_sp = 0x%08x\n", read_uint32(sl->q_buf, 72));
DLOG("rw = 0x%08x\n", read_uint32(sl->q_buf, 76));
DLOG("rw2 = 0x%08x\n", read_uint32(sl->q_buf, 80));
return 0;
}
int _stlink_usb_read_reg(stlink_t *sl, int 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 = 4;
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
cmd[i++] = STLINK_DEBUG_COMMAND;
cmd[i++] = STLINK_DEBUG_READREG;
cmd[i++] = (uint8_t) r_idx;
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
if (size == -1) {
printf("[!] send_recv STLINK_DEBUG_READREG\n");
return (int) size;
}
sl->q_len = (int) size;
stlink_print_data(sl);
r = read_uint32(sl->q_buf, 0);
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 */
int _stlink_usb_read_unsupported_reg(stlink_t *sl, int r_idx, struct stlink_reg *regp) {
uint32_t r;
int ret;
sl->q_buf[0] = (unsigned char) r_idx;
for (int 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;
}
int _stlink_usb_read_all_unsupported_regs(stlink_t *sl, struct stlink_reg *regp) {
int 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 (int 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 */
int _stlink_usb_write_unsupported_reg(stlink_t *sl, uint32_t val, int r_idx, struct stlink_reg *regp) {
int 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);
}
int _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int 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;
int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len);
cmd[i++] = STLINK_DEBUG_COMMAND;
cmd[i++] = STLINK_DEBUG_WRITEREG;
cmd[i++] = idx;
write_uint32(&cmd[i], reg);
size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
if (size == -1) {
printf("[!] send_recv STLINK_DEBUG_WRITEREG\n");
return (int) size;
}
sl->q_len = (int) size;
stlink_print_data(sl);
return 0;
}
static stlink_backend_t _stlink_usb_backend = {
_stlink_usb_close,
_stlink_usb_exit_debug_mode,
_stlink_usb_enter_swd_mode,
NULL, // no 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_t *stlink_open_usb(enum ugly_loglevel verbose, bool reset, char serial[STLINK_SERIAL_MAX_SIZE])
{
stlink_t* sl = NULL;
struct stlink_libusb* slu = NULL;
int ret = -1;
int config;
sl = calloc(1, sizeof (stlink_t));
slu = calloc(1, sizeof (struct stlink_libusb));
if (sl == NULL)
goto on_malloc_error;
if (slu == NULL)
goto on_malloc_error;
ugly_init(verbose);
sl->backend = &_stlink_usb_backend;
sl->backend_data = slu;
sl->core_stat = STLINK_CORE_STAT_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;
/** @todo We should use ssize_t and use it as a counter if > 0. As per libusb API: ssize_t libusb_get_device_list (libusb_context *ctx, libusb_device ***list) */
int cnt = (int) libusb_get_device_list(slu->libusb_ctx, &list);
struct libusb_device_descriptor desc;
int devBus =0;
int devAddr=0;
/* @TODO: Reading a environment variable in a usb open function is not very nice, this
should be refactored and moved into the CLI tools, and instead of giving USB_BUS:USB_ADDR a real stlink
serial string should be passed to this function. Probably people are using this but this is very odd because
as programmer can change to multiple busses and it is better to detect them based on serial. */
char *device = getenv("STLINK_DEVICE");
if (device) {
char *c = strchr(device,':');
if (c==NULL) {
WLOG("STLINK_DEVICE must be <USB_BUS>:<USB_ADDR> format\n");
goto on_error;
}
devBus=atoi(device);
*c++=0;
devAddr=atoi(c);
ILOG("bus %03d dev %03d\n",devBus, devAddr);
}
while (cnt--) {
struct libusb_device_handle *handle;
libusb_get_device_descriptor( list[cnt], &desc );
if (desc.idVendor != STLINK_USB_VID_ST)
continue;
if (devBus && devAddr) {
if ((libusb_get_bus_number(list[cnt]) != devBus)
|| (libusb_get_device_address(list[cnt]) != devAddr)) {
continue;
}
}
ret = libusb_open(list[cnt], &handle);
/* could not open device, continue */
if (ret)
continue;
sl->serial_size = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber,
(unsigned char *)sl->serial, sizeof(sl->serial));
libusb_close(handle);
/* could not read serial, continue */
if (sl->serial_size < 0)
continue;
/* if no serial provided, or if serial match device, fixup version and protocol */
if (((serial == NULL) || (*serial == 0)) || (memcmp(serial, &sl->serial, sl->serial_size) == 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 %s ST-Link devices\n", (devBus && devAddr) ? "matched":"any");
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);
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;
}
}
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 techniq 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) {
slu->ep_req = 1 /* ep req */ | LIBUSB_ENDPOINT_OUT;
} else {
slu->ep_req = 2 /* ep req */ | LIBUSB_ENDPOINT_OUT;
}
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);
if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) {
// This seems to work, and is unnecessary information for the user.
// Demoted to debug -- REW
DLOG("-- exit_dfu_mode\n");
stlink_exit_dfu_mode(sl);
}
// 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)
stlink_set_swdclk(sl, STLINK_SWDCLK_1P8MHZ_DIVISOR);
if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) {
stlink_enter_swd_mode(sl);
}
if (reset) {
if ( sl->version.stlink_v > 1)stlink_jtag_reset(sl, 2);
stlink_reset(sl);
usleep(10000);
}
ret = stlink_load_device_params(sl);
if (ret == -1) {
// This one didn't have any message.
goto on_libusb_error;
}
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 size_t stlink_probe_usb_devs(libusb_device **devs, stlink_t **sldevs[]) {
stlink_t **_sldevs;
libusb_device *dev;
int i = 0;
size_t slcnt = 0;
size_t slcur = 0;
/* Count stlink */
while ((dev = devs[i++]) != NULL) {
struct libusb_device_descriptor desc;
int 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++;
}
/* Allocate list of pointers */
_sldevs = calloc(slcnt, sizeof(stlink_t *));
if (!_sldevs) {
*sldevs = NULL;
return 0;
}
/* Open stlinks and attach to list */
i = 0;
while ((dev = devs[i++]) != NULL) {
struct libusb_device_descriptor desc;
int 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_MAX_SIZE] = {0,};
ret = libusb_open(dev, &handle);
if (ret < 0) {
if (ret == LIBUSB_ERROR_ACCESS) {
WLOG("failed to open USB device (LIBUSB_ERROR_ACCESS), try running as root?\n");
} else {
WLOG("failed to open USB device (libusb error: %d)\n", ret);
}
break;
}
ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, (unsigned char *)&serial, sizeof(serial));
libusb_close(handle);
if (ret < 0) {
continue;
}
stlink_t *sl = stlink_open_usb(0, 1, serial);
if (!sl)
continue;
_sldevs[slcur++] = sl;
}
*sldevs = _sldevs;
return slcur;
}
size_t stlink_probe_usb(stlink_t **stdevs[]) {
libusb_device **devs;
stlink_t **sldevs;
size_t slcnt = 0;
int 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);
libusb_free_device_list(devs, 1);
libusb_exit(NULL);
*stdevs = sldevs;
return slcnt;
}
void stlink_probe_usb_free(stlink_t ***stdevs, size_t size) {
if (stdevs == NULL || *stdevs == NULL || size == 0)
return;
for (size_t n = 0; n < size; n++)
stlink_close((*stdevs)[n]);
free(*stdevs);
*stdevs = NULL;
}