X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=src%2Fstlink-usb.c;h=98654129eccdca57b5a20ebaca2b5d525f48d873;hb=refs%2Fheads%2Fdebian;hp=d945f66ecc391abf500160c9de78a507d57dcf7d;hpb=691e9af28da2e1178353a5c016c478e508a04f19;p=fw%2Fstlink diff --git a/src/stlink-usb.c b/src/stlink-usb.c index d945f66..9865412 100644 --- a/src/stlink-usb.c +++ b/src/stlink-usb.c @@ -2,45 +2,24 @@ #include #include #include -#include +#include #include #include +#include +#include #include "stlink-common.h" #include "stlink-usb.h" -#include "uglylogging.h" - -#define LOG_TAG __FILE__ -#define DLOG(format, args...) ugly_log(UDEBUG, LOG_TAG, format, ## args) -#define ILOG(format, args...) ugly_log(UINFO, LOG_TAG, format, ## args) -#define WLOG(format, args...) ugly_log(UWARN, LOG_TAG, format, ## args) -#define fatal(format, args...) ugly_log(UFATAL, LOG_TAG, format, ## args) - -#ifndef timersub -/* This is a copy from GNU C Library (GNU LGPL 2.1), sys/time.h. */ -# define timersub(a, b, result) \ - do { \ - (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ - (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ - if ((result)->tv_usec < 0) { \ - --(result)->tv_sec; \ - (result)->tv_usec += 1000000; \ - } \ - } while (0) -#endif enum SCSI_Generic_Direction {SG_DXFER_TO_DEV=0, SG_DXFER_FROM_DEV=0x80}; 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->req_trans != NULL) - libusb_free_transfer(handle->req_trans); - - if (handle->rep_trans != NULL) - libusb_free_transfer(handle->rep_trans); - if (handle->usb_handle != NULL) { libusb_close(handle->usb_handle); } @@ -50,107 +29,42 @@ void _stlink_usb_close(stlink_t* sl) { } } - -struct trans_ctx { -#define TRANS_FLAGS_IS_DONE (1 << 0) -#define TRANS_FLAGS_HAS_ERROR (1 << 1) - volatile unsigned long flags; -}; - -static void on_trans_done(struct libusb_transfer * trans) { - struct trans_ctx * const ctx = trans->user_data; - - if (trans->status != LIBUSB_TRANSFER_COMPLETED) - ctx->flags |= TRANS_FLAGS_HAS_ERROR; - - ctx->flags |= TRANS_FLAGS_IS_DONE; -} - -int submit_wait(struct stlink_libusb *slu, struct libusb_transfer * trans) { - struct timeval start; - struct timeval now; - struct timeval diff; - struct trans_ctx trans_ctx; - enum libusb_error error; - - trans_ctx.flags = 0; - - /* brief intrusion inside the libusb interface */ - trans->callback = on_trans_done; - trans->user_data = &trans_ctx; - - if ((error = libusb_submit_transfer(trans))) { - printf("libusb_submit_transfer(%d)\n", error); - return -1; - } - - gettimeofday(&start, NULL); - - while (trans_ctx.flags == 0) { - struct timeval timeout; - timeout.tv_sec = 3; - timeout.tv_usec = 0; - if (libusb_handle_events_timeout(slu->libusb_ctx, &timeout)) { - printf("libusb_handle_events()\n"); - return -1; - } - - gettimeofday(&now, NULL); - timersub(&now, &start, &diff); - if (diff.tv_sec >= 3) { - printf("libusb_handle_events() timeout\n"); - return -1; - } - } - - if (trans_ctx.flags & TRANS_FLAGS_HAS_ERROR) { - printf("libusb_handle_events() | has_error\n"); - return -1; - } - - return 0; -} - 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; - libusb_fill_bulk_transfer(handle->req_trans, handle->usb_handle, - handle->ep_req, - txbuf, txsize, - NULL, NULL, - 0 - ); - - if (submit_wait(handle, handle->req_trans)) return -1; + if (libusb_bulk_transfer(handle->usb_handle, handle->ep_req, + txbuf, + txsize, + &res, + 3000)) + return -1; - /* send_only */ if (rxsize != 0) { - - /* read the response */ - - libusb_fill_bulk_transfer(handle->rep_trans, handle->usb_handle, - handle->ep_rep, rxbuf, rxsize, NULL, NULL, 0); - - if (submit_wait(handle, handle->rep_trans)) return -1; - res = handle->rep_trans->actual_length; + if (libusb_bulk_transfer(handle->usb_handle, handle->ep_rep, + rxbuf, + rxsize, + &res, + 3000)) + return -1; } - + if ((handle->protocoll == 1) && terminate) { /* Read the SG reply */ unsigned char sg_buf[13]; - libusb_fill_bulk_transfer - (handle->rep_trans, handle->usb_handle, - handle->ep_rep, sg_buf, 13, NULL, NULL, 0); - res = submit_wait(handle, handle->rep_trans); - /* The STLink doesn't seem to evaluate the sequence number */ + if (libusb_bulk_transfer(handle->usb_handle, handle->ep_rep, + sg_buf, + 13, + &res, + 3000)) + return -1; + /* The STLink doesn't seem to evaluate the sequence number */ handle->sg_transfer_idx++; - if (res ) return -1; } - return handle->rep_trans->actual_length; + return res; } static inline int send_only @@ -181,7 +95,7 @@ static int fill_command return i; } -void _stlink_usb_version(stlink_t *sl) { +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; @@ -194,17 +108,47 @@ void _stlink_usb_version(stlink_t *sl) { size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); if (size == -1) { printf("[!] send_recv\n"); - return; + return 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\n"); + return -1; + } else if (size != 8) { + printf("[!] wrong length\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; } -uint32_t _stlink_usb_read_debug32(stlink_t *sl, uint32_t addr) { +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; @@ -212,18 +156,19 @@ uint32_t _stlink_usb_read_debug32(stlink_t *sl, uint32_t addr) { size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len); if (size == -1) { printf("[!] send_recv\n"); - return 0; + return size; } - return read_uint32(rdata, 4); + *data = read_uint32(rdata, 4); + return 0; } -void _stlink_usb_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) { +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; @@ -232,37 +177,54 @@ void _stlink_usb_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) { size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len); if (size == -1) { printf("[!] send_recv\n"); - return; + return size; } + + return 0; } -void _stlink_usb_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { +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; - int i = fill_command(sl, SG_DXFER_TO_DEV, len); + 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); - send_only(slu, 0, cmd, slu->cmd_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; - send_only(slu, 1, data, len); + return 0; } -void _stlink_usb_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) { +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; - int i = fill_command(sl, SG_DXFER_TO_DEV, 0); + 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); - send_only(slu, 0, cmd, slu->cmd_len); - send_only(slu, 1, data, 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; } @@ -273,7 +235,7 @@ int _stlink_usb_current_mode(stlink_t * sl) { 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) { @@ -283,7 +245,7 @@ int _stlink_usb_current_mode(stlink_t * sl) { return sl->q_buf[0]; } -void _stlink_usb_core_id(stlink_t * sl) { +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; @@ -297,13 +259,14 @@ void _stlink_usb_core_id(stlink_t * sl) { size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); if (size == -1) { printf("[!] send_recv\n"); - return; + return -1; } sl->core_id = read_uint32(data, 0); + return 0; } -void _stlink_usb_status(stlink_t * sl) { +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; @@ -317,11 +280,14 @@ void _stlink_usb_status(stlink_t * sl) { size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); if (size == -1) { printf("[!] send_recv\n"); - return; + return size; } + sl->q_len = (size_t) size; + + return 0; } -void _stlink_usb_force_debug(stlink_t *sl) { +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; @@ -334,11 +300,13 @@ void _stlink_usb_force_debug(stlink_t *sl) { size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); if (size == -1) { printf("[!] send_recv\n"); - return; + return size; } + + return 0; } -void _stlink_usb_enter_swd_mode(stlink_t * sl) { +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; @@ -352,11 +320,13 @@ void _stlink_usb_enter_swd_mode(stlink_t * sl) { size = send_only(slu, 1, cmd, slu->cmd_len); if (size == -1) { printf("[!] send_recv\n"); - return; + return size; } + + return 0; } -void _stlink_usb_exit_dfu_mode(stlink_t* sl) { +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; @@ -368,15 +338,17 @@ void _stlink_usb_exit_dfu_mode(stlink_t* sl) { size = send_only(slu, 1, cmd, slu->cmd_len); if (size == -1) { printf("[!] send_recv\n"); - return; + return size; } + + return 0; } /** * TODO - not convinced this does anything... * @param sl */ -void _stlink_usb_reset(stlink_t * sl) { +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; @@ -390,12 +362,14 @@ void _stlink_usb_reset(stlink_t * sl) { size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); if (size == -1) { printf("[!] send_recv\n"); - return; + return size; } + + return 0; } -void _stlink_usb_jtag_reset(stlink_t * sl, int value) { +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; @@ -405,17 +379,19 @@ void _stlink_usb_jtag_reset(stlink_t * sl, int value) { cmd[i++] = STLINK_DEBUG_COMMAND; cmd[i++] = STLINK_JTAG_DRIVE_NRST; - cmd[i++] = (value)?0:1; + cmd[i++] = value; size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); if (size == -1) { printf("[!] send_recv\n"); - return; + return size; } + + return 0; } -void _stlink_usb_step(stlink_t* sl) { +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; @@ -429,15 +405,17 @@ void _stlink_usb_step(stlink_t* sl) { size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); if (size == -1) { printf("[!] send_recv\n"); - return; + return size; } + + return 0; } /** * This seems to do a good job of restarting things from the beginning? * @param sl */ -void _stlink_usb_run(stlink_t* 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; @@ -451,11 +429,13 @@ void _stlink_usb_run(stlink_t* sl) { size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); if (size == -1) { printf("[!] send_recv\n"); - return; + return size; } + + return 0; } -void _stlink_usb_exit_debug_mode(stlink_t *sl) { +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; @@ -467,11 +447,13 @@ void _stlink_usb_exit_debug_mode(stlink_t *sl) { size = send_only(slu, 1, cmd, slu->cmd_len); if (size == -1) { printf("[!] send_only\n"); - return; + return size; } + + return 0; } -void _stlink_usb_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { +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; @@ -486,15 +468,16 @@ void _stlink_usb_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { size = send_recv(slu, 1, cmd, slu->cmd_len, data, len); if (size == -1) { printf("[!] send_recv\n"); - return; + return size; } sl->q_len = (size_t) size; stlink_print_data(sl); + return 0; } -void _stlink_usb_read_all_regs(stlink_t *sl, reg *regp) { +int _stlink_usb_read_all_regs(stlink_t *sl, reg *regp) { struct stlink_libusb * const slu = sl->backend_data; unsigned char* const cmd = sl->c_buf; unsigned char* const data = sl->q_buf; @@ -507,7 +490,7 @@ void _stlink_usb_read_all_regs(stlink_t *sl, reg *regp) { size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); if (size == -1) { printf("[!] send_recv\n"); - return; + return size; } sl->q_len = (size_t) size; stlink_print_data(sl); @@ -519,16 +502,18 @@ void _stlink_usb_read_all_regs(stlink_t *sl, reg *regp) { regp->rw = read_uint32(sl->q_buf, 76); regp->rw2 = read_uint32(sl->q_buf, 80); if (sl->verbose < 2) - return; + 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; } -void _stlink_usb_read_reg(stlink_t *sl, int r_idx, reg *regp) { +int _stlink_usb_read_reg(stlink_t *sl, int r_idx, reg *regp) { struct stlink_libusb * const slu = sl->backend_data; unsigned char* const data = sl->q_buf; unsigned char* const cmd = sl->c_buf; @@ -543,13 +528,13 @@ void _stlink_usb_read_reg(stlink_t *sl, int r_idx, reg *regp) { size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); if (size == -1) { printf("[!] send_recv\n"); - return; + return size; } sl->q_len = (size_t) 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; @@ -569,9 +554,114 @@ void _stlink_usb_read_reg(stlink_t *sl, int r_idx, reg *regp) { 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, 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, DCRSR, 4); + if (ret == -1) + return ret; + + _stlink_usb_read_mem32(sl, 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, 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, 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, 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, DCRSR, 4); } -void _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int idx) { +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; @@ -586,10 +676,12 @@ void _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int idx) { size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); if (size == -1) { printf("[!] send_recv\n"); - return; + return size; } sl->q_len = (size_t) size; stlink_print_data(sl); + + return 0; } stlink_backend_t _stlink_usb_backend = { @@ -611,54 +703,119 @@ stlink_backend_t _stlink_usb_backend = { _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_force_debug, + _stlink_usb_target_voltage }; - -stlink_t* stlink_open_usb(const int verbose) { +stlink_t *stlink_open_usb(enum ugly_loglevel verbose, bool reset, char serial[16]) +{ stlink_t* sl = NULL; struct stlink_libusb* slu = NULL; - int error = -1; - libusb_device** devs = NULL; + int ret = -1; int config; - sl = malloc(sizeof (stlink_t)); - slu = malloc(sizeof (struct stlink_libusb)); - if (sl == NULL) goto on_error; - if (slu == NULL) goto on_error; - memset(sl, 0, sizeof (stlink_t)); - memset(slu, 0, sizeof (struct stlink_libusb)); + 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; + 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; } - - slu->usb_handle = libusb_open_device_with_vid_pid(slu->libusb_ctx, USB_ST_VID, USB_STLINK_32L_PID); - if (slu->usb_handle == NULL) { - slu->usb_handle = libusb_open_device_with_vid_pid(slu->libusb_ctx, USB_ST_VID, USB_STLINK_PID); - if (slu->usb_handle == NULL) { - WLOG("Couldn't find any ST-Link/V2 devices"); - goto on_error; - } - slu->protocoll = 1; + + libusb_device **list; + int cnt = 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 : format\n"); + goto on_error; + } + devBus=atoi(device); + *c++=0; + devAddr=atoi(c); + ILOG("bus %03d dev %03d\n",devBus, devAddr); + } + + while (cnt--) { + libusb_get_device_descriptor( list[cnt], &desc ); + if (desc.idVendor != USB_ST_VID) + continue; + + if (devBus && devAddr) { + if ((libusb_get_bus_number(list[cnt]) != devBus) + || (libusb_get_device_address(list[cnt]) != devAddr)) { + continue; + } + } + + if ((desc.idProduct == USB_STLINK_32L_PID) || (desc.idProduct == USB_STLINK_NUCLEO_PID)) { + struct libusb_device_handle *handle; + + libusb_open(list[cnt], &handle); + sl->serial_size = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, + (unsigned char *)sl->serial, sizeof(sl->serial)); + libusb_close(handle); + + if ((serial == NULL) || (*serial == 0)) + break; + + if (sl->serial_size < 0) + continue; + + if (memcmp(serial, &sl->serial, sl->serial_size) == 0) + break; + + continue; + } + + if (desc.idProduct == USB_STLINK_PID) { + slu->protocoll = 1; + break; + } + } + + if (cnt < 0) { + WLOG ("Couldn't find %s ST-Link/V2 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/V2 device %03d:%03d\n", + ret, strerror (errno), libusb_get_bus_number(list[cnt]), libusb_get_device_address(list[cnt])); + goto on_error; + } } + libusb_free_device_list(list, 1); + if (libusb_kernel_driver_active(slu->usb_handle, 0) == 1) { - int r; - - r = libusb_detach_kernel_driver(slu->usb_handle, 0); - if (r<0) { - WLOG("libusb_detach_kernel_driver(() error %s\n", strerror(-r)); + 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; } } @@ -679,64 +836,172 @@ stlink_t* stlink_open_usb(const int verbose) { } if (libusb_claim_interface(slu->usb_handle, 0)) { - WLOG("libusb_claim_interface() failed\n"); + WLOG("Stlink usb device found, but unable to claim (probably already in use?)\n"); goto on_libusb_error; } - slu->req_trans = libusb_alloc_transfer(0); - if (slu->req_trans == NULL) { - WLOG("libusb_alloc_transfer failed\n"); - goto on_libusb_error; - } - - slu->rep_trans = libusb_alloc_transfer(0); - if (slu->rep_trans == NULL) { - WLOG("libusb_alloc_transfer failed\n"); - goto on_libusb_error; - } // TODO - could use the scanning techniq from stm8 code here... slu->ep_rep = 1 /* ep rep */ | LIBUSB_ENDPOINT_IN; - slu->ep_req = 2 /* ep req */ | LIBUSB_ENDPOINT_OUT; + if (desc.idProduct == USB_STLINK_NUCLEO_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; // TODO - never used at the moment, always CMD_SIZE slu->cmd_len = (slu->protocoll == 1)? STLINK_SG_SIZE: STLINK_CMD_SIZE; - /* success */ - if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) { - ILOG("-- exit_dfu_mode\n"); - stlink_exit_dfu_mode(sl); + ILOG("-- exit_dfu_mode\n"); + stlink_exit_dfu_mode(sl); } if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) { - stlink_enter_swd_mode(sl); + stlink_enter_swd_mode(sl); } - stlink_reset(sl); - stlink_load_device_params(sl); - stlink_version(sl); + if (reset) { + stlink_reset(sl); + usleep(10000); + } - error = 0; + stlink_version(sl); + ret = stlink_load_device_params(sl); on_libusb_error: - if (devs != NULL) { - libusb_free_device_list(devs, 1); - } - - if (error == -1) { + if (ret == -1) { stlink_close(sl); return NULL; } - /* success */ return sl; on_error: - if( slu->libusb_ctx) - libusb_exit(slu->libusb_ctx); - if (sl != NULL) free(sl); - if (slu != NULL) free(slu); - return 0; + 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; + int ret = 0; + size_t slcnt = 0; + size_t slcur = 0; + + /* Count stlink */ + while ((dev = devs[i++]) != NULL) { + struct libusb_device_descriptor desc; + int r = libusb_get_device_descriptor(dev, &desc); + if (r < 0) { + WLOG("failed to get libusb device descriptor\n"); + break; + } + + if (desc.idProduct != USB_STLINK_32L_PID && + desc.idProduct != USB_STLINK_NUCLEO_PID) + 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; + ret = libusb_get_device_descriptor(dev, &desc); + if (ret < 0) { + WLOG("failed to get libusb device descriptor\n"); + break; + } + + if (desc.idProduct != USB_STLINK_32L_PID && + desc.idProduct != USB_STLINK_NUCLEO_PID) + continue; + + struct libusb_device_handle* handle; + char serial[13]; + memset(serial, 0, sizeof(serial)); + + ret = libusb_open(dev, &handle); + if (ret < 0) { + WLOG("failed to get libusb device descriptor\n"); + break; + } + + ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, (unsigned char *)&serial, sizeof(serial)); + if (ret < 0) + *serial = NULL; + + libusb_close(handle); + + stlink_t *sl = NULL; + sl = stlink_open_usb(0, 1, serial); + if (!sl) + continue; + + _sldevs[slcur] = sl; + slcur++; + } + + /* Something went wrong */ + if (ret < 0) { + free(_sldevs); + *sldevs = NULL; + return 0; + } + + *sldevs = _sldevs; + return slcnt; +} + +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; +}