From 8f927d51643ceb6b3696772c5fabd5ba9f48fcd3 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Micka=C3=ABl=20Thomas?= Date: Tue, 29 Jan 2019 21:14:58 +0100 Subject: [PATCH 1/1] Add CMSIS-DAP v2 support MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This change implements CMSIS-DAP v2 which works with raw USB bulk transfers. The old driver is now split into a generic CMSIS part and a HID backend, with a new raw USB backend for CMSIS-DAP v2. New commands: - cmsis_dap_backend (usb_bulk | hid | auto) - cmsis_dap_usb interface Change-Id: I4218477b12ccbfe19c9b332321cd21394bf44e30 Signed-off-by: Mickaël Thomas Signed-off-by: Tomas Vanek Reviewed-on: http://openocd.zylin.com/4831 Tested-by: jenkins --- configure.ac | 3 +- src/jtag/drivers/Makefile.am | 12 +- .../drivers/{cmsis_dap_usb.c => cmsis_dap.c} | 309 +++++++-------- src/jtag/drivers/cmsis_dap.h | 28 ++ src/jtag/drivers/cmsis_dap_usb_bulk.c | 373 ++++++++++++++++++ src/jtag/drivers/cmsis_dap_usb_hid.c | 208 ++++++++++ src/jtag/interfaces.c | 4 +- 7 files changed, 755 insertions(+), 182 deletions(-) rename src/jtag/drivers/{cmsis_dap_usb.c => cmsis_dap.c} (88%) create mode 100644 src/jtag/drivers/cmsis_dap.h create mode 100644 src/jtag/drivers/cmsis_dap_usb_bulk.c create mode 100644 src/jtag/drivers/cmsis_dap_usb_hid.c diff --git a/configure.ac b/configure.ac index 3e49213cc4..9cb20ad89e 100644 --- a/configure.ac +++ b/configure.ac @@ -119,6 +119,7 @@ m4_define([USB1_ADAPTERS], [[ft232r], [Bitbang mode of FT232R based devices], [FT232R]], [[vsllink], [Versaloon-Link JTAG Programmer], [VSLLINK]], [[xds110], [TI XDS110 Debug Probe], [XDS110]], + [[cmsis_dap_v2], [CMSIS-DAP v2 Compliant Debugger], [CMSIS_DAP_USB]], [[osbdm], [OSBDM (JTAG only) Programmer], [OSBDM]], [[opendous], [eStick/opendous JTAG Programmer], [OPENDOUS]], [[aice], [Andes JTAG Programmer], [AICE]]]) @@ -129,7 +130,7 @@ m4_define([USB0_ADAPTERS], [[armjtagew], [Olimex ARM-JTAG-EW Programmer], [ARMJTAGEW]]]) m4_define([HIDAPI_ADAPTERS], - [[[cmsis_dap], [CMSIS-DAP Compliant Debugger], [CMSIS_DAP]], + [[[cmsis_dap], [CMSIS-DAP Compliant Debugger], [CMSIS_DAP_HID]], [[nulink], [Nu-Link Programmer], [HLADAPTER_NULINK]]]) m4_define([HIDAPI_USB1_ADAPTERS], diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index 1a5ab4a2d9..f7a54b0033 100644 --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -170,8 +170,15 @@ endif if OPENJTAG DRIVERFILES += %D%/openjtag.c endif -if CMSIS_DAP -DRIVERFILES += %D%/cmsis_dap_usb.c +if CMSIS_DAP_HID +DRIVERFILES += %D%/cmsis_dap_usb_hid.c +DRIVERFILES += %D%/cmsis_dap.c +endif +if CMSIS_DAP_USB +DRIVERFILES += %D%/cmsis_dap_usb_bulk.c +if !CMSIS_DAP_HID +DRIVERFILES += %D%/cmsis_dap.c +endif endif if IMX_GPIO DRIVERFILES += %D%/imx_gpio.c @@ -189,6 +196,7 @@ DRIVERHEADERS = \ %D%/jtag_usb_common.h \ %D%/libftdi_helper.h \ %D%/libusb_helper.h \ + %D%/cmsis_dap.h \ %D%/minidriver_imp.h \ %D%/mpsse.h \ %D%/rlink.h \ diff --git a/src/jtag/drivers/cmsis_dap_usb.c b/src/jtag/drivers/cmsis_dap.c similarity index 88% rename from src/jtag/drivers/cmsis_dap_usb.c rename to src/jtag/drivers/cmsis_dap.c index 6d55392d5a..8ddb541798 100644 --- a/src/jtag/drivers/cmsis_dap_usb.c +++ b/src/jtag/drivers/cmsis_dap.c @@ -1,4 +1,7 @@ /*************************************************************************** + * Copyright (C) 2018 by Mickaël Thomas * + * mickael9@gmail.com * + * * * Copyright (C) 2016 by Maksym Hilliaka * * oter@frozen-team.com * * * @@ -38,12 +41,26 @@ #include #include -#include +#include "cmsis_dap.h" -/* - * See CMSIS-DAP documentation: - * Version 0.01 - Beta. - */ +#if BUILD_CMSIS_DAP_USB == 1 +const struct cmsis_dap_backend cmsis_dap_usb_backend; +extern const struct command_registration cmsis_dap_usb_subcommand_handlers[]; +#endif + +#if BUILD_CMSIS_DAP_HID == 1 +const struct cmsis_dap_backend cmsis_dap_hid_backend; +#endif + +static const struct cmsis_dap_backend *const cmsis_dap_backends[] = { +#if BUILD_CMSIS_DAP_USB == 1 + &cmsis_dap_usb_backend, +#endif + +#if BUILD_CMSIS_DAP_HID == 1 + &cmsis_dap_hid_backend, +#endif +}; /* USB Config */ @@ -52,6 +69,7 @@ * PID 0xf001: LPC-Link-II CMSIS_DAP * PID 0xf002: OPEN-SDA CMSIS_DAP (Freedom Board) * PID 0x2722: Keil ULINK2 CMSIS-DAP + * PID 0x2750: Keil ULINKplus CMSIS-DAP * * VID 0x0d28: mbed Software * PID 0x0204: MBED CMSIS-DAP @@ -61,10 +79,10 @@ /* vid = pid = 0 marks the end of the list */ static uint16_t cmsis_dap_vid[MAX_USB_IDS + 1] = { 0 }; static uint16_t cmsis_dap_pid[MAX_USB_IDS + 1] = { 0 }; -static wchar_t *cmsis_dap_serial; +static char *cmsis_dap_serial; +static int cmsis_dap_backend = -1; static bool swd_mode; -#define PACKET_SIZE (64 + 1) /* 64 bytes plus report id */ #define USB_TIMEOUT 1000 /* CMSIS-DAP General Commands */ @@ -163,14 +181,6 @@ static const char * const info_caps_str[] = { /* max clock speed (kHz) */ #define DAP_MAX_CLOCK 5000 -struct cmsis_dap { - hid_device *dev_handle; - uint16_t packet_size; - int packet_count; - uint8_t *packet_buffer; - uint8_t caps; - uint8_t mode; -}; struct pending_transfer_result { uint8_t cmd; @@ -223,134 +233,66 @@ static uint8_t output_pins = SWJ_PIN_SRST | SWJ_PIN_TRST; static struct cmsis_dap *cmsis_dap_handle; -static int cmsis_dap_usb_open(void) -{ - hid_device *dev = NULL; - int i; - struct hid_device_info *devs, *cur_dev; - unsigned short target_vid, target_pid; - bool found = false; - target_vid = 0; - target_pid = 0; +static int cmsis_dap_open(void) +{ + const struct cmsis_dap_backend *backend = NULL; - if (hid_init() != 0) { - LOG_ERROR("unable to open HIDAPI"); + struct cmsis_dap *dap = malloc(sizeof(struct cmsis_dap)); + if (dap == NULL) { + LOG_ERROR("unable to allocate memory"); return ERROR_FAIL; } - /* - * The CMSIS-DAP specification stipulates: - * "The Product String must contain "CMSIS-DAP" somewhere in the string. This is used by the - * debuggers to identify a CMSIS-DAP compliant Debug Unit that is connected to a host computer." - */ - devs = hid_enumerate(0x0, 0x0); - cur_dev = devs; - while (NULL != cur_dev) { - if (0 == cmsis_dap_vid[0]) { - if (NULL == cur_dev->product_string) { - LOG_DEBUG("Cannot read product string of device 0x%x:0x%x", - cur_dev->vendor_id, cur_dev->product_id); - } else { - if (wcsstr(cur_dev->product_string, L"CMSIS-DAP")) { - /* if the user hasn't specified VID:PID *and* - * product string contains "CMSIS-DAP", pick it - */ - found = true; - } - } - } else { - /* otherwise, exhaustively compare against all VID:PID in list */ - for (i = 0; cmsis_dap_vid[i] || cmsis_dap_pid[i]; i++) { - if ((cmsis_dap_vid[i] == cur_dev->vendor_id) && (cmsis_dap_pid[i] == cur_dev->product_id)) - found = true; - } - - if (cmsis_dap_vid[i] || cmsis_dap_pid[i]) - found = true; - } - - /* LPC-LINK2 has cmsis-dap on interface 0 and other HID functions on other interfaces */ - if (cur_dev->vendor_id == 0x1fc9 && cur_dev->product_id == 0x0090 && cur_dev->interface_number != 0) - found = false; + dap->caps = 0; + dap->mode = 0; + dap->packet_size = 0; /* initialized by backend */ - if (found) { - /* we have found an adapter, so exit further checks */ - /* check serial number matches if given */ - if (cmsis_dap_serial != NULL) { - if ((cur_dev->serial_number != NULL) && wcscmp(cmsis_dap_serial, cur_dev->serial_number) == 0) { - break; - } - } else + if (cmsis_dap_backend >= 0) { + /* Use forced backend */ + backend = cmsis_dap_backends[cmsis_dap_backend]; + if (backend->open(dap, cmsis_dap_vid, cmsis_dap_pid, cmsis_dap_serial) != ERROR_OK) + backend = NULL; + } else { + /* Try all backends */ + for (unsigned int i = 0; i < ARRAY_SIZE(cmsis_dap_backends); i++) { + backend = cmsis_dap_backends[i]; + if (backend->open(dap, cmsis_dap_vid, cmsis_dap_pid, cmsis_dap_serial) == ERROR_OK) break; - - found = false; + else + backend = NULL; } - - cur_dev = cur_dev->next; - } - - if (NULL != cur_dev) { - target_vid = cur_dev->vendor_id; - target_pid = cur_dev->product_id; } - if (target_vid == 0 && target_pid == 0) { - LOG_ERROR("unable to find CMSIS-DAP device"); - hid_free_enumeration(devs); + if (backend == NULL) { + LOG_ERROR("unable to find a matching CMSIS-DAP device"); + free(dap); return ERROR_FAIL; } - dev = hid_open_path(cur_dev->path); - hid_free_enumeration(devs); + assert(dap->packet_size > 0); - if (dev == NULL) { - LOG_ERROR("unable to open CMSIS-DAP device 0x%x:0x%x", target_vid, target_pid); - return ERROR_FAIL; - } + dap->backend = backend; + dap->packet_buffer = malloc(dap->packet_size); - struct cmsis_dap *dap = malloc(sizeof(struct cmsis_dap)); - if (dap == NULL) { + if (dap->packet_buffer == NULL) { LOG_ERROR("unable to allocate memory"); + dap->backend->close(dap); + free(dap); return ERROR_FAIL; } - dap->dev_handle = dev; - dap->caps = 0; - dap->mode = 0; - cmsis_dap_handle = dap; - /* allocate default packet buffer, may be changed later. - * currently with HIDAPI we have no way of getting the output report length - * without this info we cannot communicate with the adapter. - * For the moment we ahve to hard code the packet size */ - - int packet_size = PACKET_SIZE; - - /* atmel cmsis-dap uses 512 byte reports */ - /* except when it doesn't e.g. with mEDBG on SAMD10 Xplained - * board */ - /* TODO: HID report descriptor should be parsed instead of - * hardcoding a match by VID */ - if (target_vid == 0x03eb && target_pid != 0x2145 && target_pid != 0x2175) - packet_size = 512 + 1; - - cmsis_dap_handle->packet_buffer = malloc(packet_size); - cmsis_dap_handle->packet_size = packet_size; - - if (cmsis_dap_handle->packet_buffer == NULL) { - LOG_ERROR("unable to allocate memory"); - return ERROR_FAIL; - } - return ERROR_OK; } -static void cmsis_dap_usb_close(struct cmsis_dap *dap) +static void cmsis_dap_close(struct cmsis_dap *dap) { - hid_close(dap->dev_handle); - hid_exit(); + if (dap->backend) { + dap->backend->close(dap); + dap->backend = NULL; + } free(cmsis_dap_handle->packet_buffer); free(cmsis_dap_handle); @@ -364,47 +306,27 @@ static void cmsis_dap_usb_close(struct cmsis_dap *dap) } } -static int cmsis_dap_usb_write(struct cmsis_dap *dap, int txlen) -{ -#ifdef CMSIS_DAP_JTAG_DEBUG - LOG_DEBUG("cmsis-dap usb xfer cmd=%02X", dap->packet_buffer[1]); -#endif - /* Pad the rest of the TX buffer with 0's */ - memset(dap->packet_buffer + txlen, 0, dap->packet_size - txlen); - - /* write data to device */ - int retval = hid_write(dap->dev_handle, dap->packet_buffer, dap->packet_size); - if (retval == -1) { - LOG_ERROR("error writing data: %ls", hid_error(dap->dev_handle)); - return ERROR_FAIL; - } - - return ERROR_OK; -} - /* Send a message and receive the reply */ -static int cmsis_dap_usb_xfer(struct cmsis_dap *dap, int txlen) +static int cmsis_dap_xfer(struct cmsis_dap *dap, int txlen) { if (pending_fifo_block_count) { LOG_ERROR("pending %d blocks, flushing", pending_fifo_block_count); while (pending_fifo_block_count) { - hid_read_timeout(dap->dev_handle, dap->packet_buffer, dap->packet_size, 10); + dap->backend->read(dap, 10); pending_fifo_block_count--; } pending_fifo_put_idx = 0; pending_fifo_get_idx = 0; } - int retval = cmsis_dap_usb_write(dap, txlen); - if (retval != ERROR_OK) + int retval = dap->backend->write(dap, txlen, USB_TIMEOUT); + if (retval < 0) return retval; /* get reply */ - retval = hid_read_timeout(dap->dev_handle, dap->packet_buffer, dap->packet_size, USB_TIMEOUT); - if (retval == -1 || retval == 0) { - LOG_DEBUG("error reading data: %ls", hid_error(dap->dev_handle)); - return ERROR_FAIL; - } + retval = dap->backend->read(dap, USB_TIMEOUT); + if (retval < 0) + return retval; return ERROR_OK; } @@ -422,7 +344,7 @@ static int cmsis_dap_cmd_DAP_SWJ_Pins(uint8_t pins, uint8_t mask, uint32_t delay buffer[5] = (delay >> 8) & 0xff; buffer[6] = (delay >> 16) & 0xff; buffer[7] = (delay >> 24) & 0xff; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 8); + retval = cmsis_dap_xfer(cmsis_dap_handle, 8); if (retval != ERROR_OK) { LOG_ERROR("CMSIS-DAP command CMD_DAP_SWJ_PINS failed."); @@ -448,7 +370,7 @@ static int cmsis_dap_cmd_DAP_SWJ_Clock(uint32_t swj_clock) buffer[3] = (swj_clock >> 8) & 0xff; buffer[4] = (swj_clock >> 16) & 0xff; buffer[5] = (swj_clock >> 24) & 0xff; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 6); + retval = cmsis_dap_xfer(cmsis_dap_handle, 6); if (retval != ERROR_OK || buffer[1] != DAP_OK) { LOG_ERROR("CMSIS-DAP command CMD_DAP_SWJ_CLOCK failed."); @@ -477,7 +399,7 @@ static int cmsis_dap_cmd_DAP_SWJ_Sequence(uint8_t s_len, const uint8_t *sequence buffer[2] = s_len; bit_copy(&buffer[3], 0, sequence, 0, s_len); - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, DIV_ROUND_UP(s_len, 8) + 3); + retval = cmsis_dap_xfer(cmsis_dap_handle, DIV_ROUND_UP(s_len, 8) + 3); if (retval != ERROR_OK || buffer[1] != DAP_OK) return ERROR_FAIL; @@ -493,7 +415,7 @@ static int cmsis_dap_cmd_DAP_Info(uint8_t info, uint8_t **data) buffer[0] = 0; /* report number */ buffer[1] = CMD_DAP_INFO; buffer[2] = info; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 3); + retval = cmsis_dap_xfer(cmsis_dap_handle, 3); if (retval != ERROR_OK) { LOG_ERROR("CMSIS-DAP command CMD_INFO failed."); @@ -514,7 +436,7 @@ static int cmsis_dap_cmd_DAP_LED(uint8_t led, uint8_t state) buffer[1] = CMD_DAP_LED; buffer[2] = led; buffer[3] = state; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 4); + retval = cmsis_dap_xfer(cmsis_dap_handle, 4); if (retval != ERROR_OK || buffer[1] != 0x00) { LOG_ERROR("CMSIS-DAP command CMD_LED failed."); @@ -532,7 +454,7 @@ static int cmsis_dap_cmd_DAP_Connect(uint8_t mode) buffer[0] = 0; /* report number */ buffer[1] = CMD_DAP_CONNECT; buffer[2] = mode; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 3); + retval = cmsis_dap_xfer(cmsis_dap_handle, 3); if (retval != ERROR_OK) { LOG_ERROR("CMSIS-DAP command CMD_CONNECT failed."); @@ -554,7 +476,7 @@ static int cmsis_dap_cmd_DAP_Disconnect(void) buffer[0] = 0; /* report number */ buffer[1] = CMD_DAP_DISCONNECT; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 2); + retval = cmsis_dap_xfer(cmsis_dap_handle, 2); if (retval != ERROR_OK || buffer[1] != DAP_OK) { LOG_ERROR("CMSIS-DAP command CMD_DISCONNECT failed."); @@ -576,7 +498,7 @@ static int cmsis_dap_cmd_DAP_TFER_Configure(uint8_t idle, uint16_t retry_count, buffer[4] = (retry_count >> 8) & 0xff; buffer[5] = match_retry & 0xff; buffer[6] = (match_retry >> 8) & 0xff; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 7); + retval = cmsis_dap_xfer(cmsis_dap_handle, 7); if (retval != ERROR_OK || buffer[1] != DAP_OK) { LOG_ERROR("CMSIS-DAP command CMD_TFER_Configure failed."); @@ -594,7 +516,7 @@ static int cmsis_dap_cmd_DAP_SWD_Configure(uint8_t cfg) buffer[0] = 0; /* report number */ buffer[1] = CMD_DAP_SWD_CONFIGURE; buffer[2] = cfg; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 3); + retval = cmsis_dap_xfer(cmsis_dap_handle, 3); if (retval != ERROR_OK || buffer[1] != DAP_OK) { LOG_ERROR("CMSIS-DAP command CMD_SWD_Configure failed."); @@ -614,7 +536,7 @@ static int cmsis_dap_cmd_DAP_Delay(uint16_t delay_us) buffer[1] = CMD_DAP_DELAY; buffer[2] = delay_us & 0xff; buffer[3] = (delay_us >> 8) & 0xff; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 4); + retval = cmsis_dap_xfer(cmsis_dap_handle, 4); if (retval != ERROR_OK || buffer[1] != DAP_OK) { LOG_ERROR("CMSIS-DAP command CMD_Delay failed."); @@ -684,9 +606,14 @@ static void cmsis_dap_swd_write_from_queue(struct cmsis_dap *dap) } } - queued_retval = cmsis_dap_usb_write(dap, idx); - if (queued_retval != ERROR_OK) + int retval = dap->backend->write(dap, idx, USB_TIMEOUT); + + if (retval < 0) { + queued_retval = retval; goto skip; + } else { + queued_retval = ERROR_OK; + } pending_fifo_put_idx = (pending_fifo_put_idx + 1) % dap->packet_count; pending_fifo_block_count++; @@ -708,12 +635,12 @@ static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms) LOG_ERROR("no pending write"); /* get reply */ - int retval = hid_read_timeout(dap->dev_handle, dap->packet_buffer, dap->packet_size, timeout_ms); - if (retval == 0 && timeout_ms < USB_TIMEOUT) + int retval = dap->backend->read(dap, timeout_ms); + if (retval == ERROR_TIMEOUT_REACHED && timeout_ms < USB_TIMEOUT) return; - if (retval == -1 || retval == 0) { - LOG_DEBUG("error reading data: %ls", hid_error(dap->dev_handle)); + if (retval <= 0) { + LOG_DEBUG("error reading data"); queued_retval = ERROR_FAIL; goto skip; } @@ -969,7 +896,7 @@ static int cmsis_dap_init(void) int retval; uint8_t *data; - retval = cmsis_dap_usb_open(); + retval = cmsis_dap_open(); if (retval != ERROR_OK) return retval; @@ -1048,7 +975,7 @@ static int cmsis_dap_init(void) LOG_DEBUG("CMSIS-DAP: Packet Count = %d", pkt_cnt); } - LOG_DEBUG("Allocating FIFO for %d pending HID requests", cmsis_dap_handle->packet_count); + LOG_DEBUG("Allocating FIFO for %d pending packets", cmsis_dap_handle->packet_count); for (int i = 0; i < cmsis_dap_handle->packet_count; i++) { pending_fifo[i].transfers = malloc(pending_queue_len * sizeof(struct pending_transfer_result)); if (!pending_fifo[i].transfers) { @@ -1120,7 +1047,7 @@ static int cmsis_dap_quit(void) cmsis_dap_cmd_DAP_LED(LED_ID_RUN, LED_OFF); cmsis_dap_cmd_DAP_LED(LED_ID_CONNECT, LED_OFF); - cmsis_dap_usb_close(cmsis_dap_handle); + cmsis_dap_close(cmsis_dap_handle); return ERROR_OK; } @@ -1261,7 +1188,7 @@ static void cmsis_dap_flush(void) #endif /* send command to USB device */ - int retval = cmsis_dap_usb_xfer(cmsis_dap_handle, queued_seq_buf_end + 3); + int retval = cmsis_dap_xfer(cmsis_dap_handle, queued_seq_buf_end + 3); if (retval != ERROR_OK || buffer[1] != DAP_OK) { LOG_ERROR("CMSIS-DAP command CMD_DAP_JTAG_SEQ failed."); exit(-1); @@ -1668,7 +1595,7 @@ COMMAND_HANDLER(cmsis_dap_handle_cmd_command) for (i = 0; i < CMD_ARGC; i++) buffer[i + 1] = strtoul(CMD_ARGV[i], NULL, 16); - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, CMD_ARGC + 1); + retval = cmsis_dap_xfer(cmsis_dap_handle, CMD_ARGC + 1); if (retval != ERROR_OK) { LOG_ERROR("CMSIS-DAP command failed."); @@ -1712,21 +1639,32 @@ COMMAND_HANDLER(cmsis_dap_handle_vid_pid_command) } COMMAND_HANDLER(cmsis_dap_handle_serial_command) +{ + if (CMD_ARGC == 1) + cmsis_dap_serial = strdup(CMD_ARGV[0]); + else + LOG_ERROR("expected exactly one argument to cmsis_dap_serial "); + + return ERROR_OK; +} + +COMMAND_HANDLER(cmsis_dap_handle_backend_command) { if (CMD_ARGC == 1) { - size_t len = mbstowcs(NULL, CMD_ARGV[0], 0); - cmsis_dap_serial = calloc(len + 1, sizeof(wchar_t)); - if (cmsis_dap_serial == NULL) { - LOG_ERROR("unable to allocate memory"); - return ERROR_OK; - } - if (mbstowcs(cmsis_dap_serial, CMD_ARGV[0], len + 1) == (size_t)-1) { - free(cmsis_dap_serial); - cmsis_dap_serial = NULL; - LOG_ERROR("unable to convert serial"); + if (strcmp(CMD_ARGV[0], "auto") == 0) { + cmsis_dap_backend = -1; /* autoselect */ + } else { + for (unsigned int i = 0; i < ARRAY_SIZE(cmsis_dap_backends); i++) { + if (strcasecmp(cmsis_dap_backends[i]->name, CMD_ARGV[0]) == 0) { + cmsis_dap_backend = i; + return ERROR_OK; + } + } + + LOG_ERROR("invalid backend argument to cmsis_dap_backend "); } } else { - LOG_ERROR("expected exactly one argument to cmsis_dap_serial "); + LOG_ERROR("expected exactly one argument to cmsis_dap_backend "); } return ERROR_OK; @@ -1750,6 +1688,7 @@ static const struct command_registration cmsis_dap_subcommand_handlers[] = { COMMAND_REGISTRATION_DONE }; + static const struct command_registration cmsis_dap_command_handlers[] = { { .name = "cmsis-dap", @@ -1772,6 +1711,22 @@ static const struct command_registration cmsis_dap_command_handlers[] = { .help = "set the serial number of the adapter", .usage = "serial_string", }, + { + .name = "cmsis_dap_backend", + .handler = &cmsis_dap_handle_backend_command, + .mode = COMMAND_CONFIG, + .help = "set the communication backend to use (USB bulk or HID).", + .usage = "(auto | usb_bulk | hid)", + }, +#if BUILD_CMSIS_DAP_USB + { + .name = "cmsis_dap_usb", + .chain = cmsis_dap_usb_subcommand_handlers, + .mode = COMMAND_ANY, + .help = "USB bulk backend-specific commands", + .usage = "", + }, +#endif COMMAND_REGISTRATION_DONE }; diff --git a/src/jtag/drivers/cmsis_dap.h b/src/jtag/drivers/cmsis_dap.h new file mode 100644 index 0000000000..8cbb8dd07c --- /dev/null +++ b/src/jtag/drivers/cmsis_dap.h @@ -0,0 +1,28 @@ +#ifndef OPENOCD_JTAG_DRIVERS_CMSIS_DAP_H +#define OPENOCD_JTAG_DRIVERS_CMSIS_DAP_H + +#include + +struct cmsis_dap_backend; +struct cmsis_dap_backend_data; +struct command_registration; + +struct cmsis_dap { + struct cmsis_dap_backend_data *bdata; + const struct cmsis_dap_backend *backend; + uint16_t packet_size; + int packet_count; + uint8_t *packet_buffer; + uint8_t caps; + uint8_t mode; +}; + +struct cmsis_dap_backend { + const char *name; + int (*open)(struct cmsis_dap *dap, uint16_t vids[], uint16_t pids[], char *serial); + void (*close)(struct cmsis_dap *dap); + int (*read)(struct cmsis_dap *dap, int timeout_ms); + int (*write)(struct cmsis_dap *dap, int len, int timeout_ms); +}; + +#endif diff --git a/src/jtag/drivers/cmsis_dap_usb_bulk.c b/src/jtag/drivers/cmsis_dap_usb_bulk.c new file mode 100644 index 0000000000..b82e3dae56 --- /dev/null +++ b/src/jtag/drivers/cmsis_dap_usb_bulk.c @@ -0,0 +1,373 @@ +/*************************************************************************** + * Copyright (C) 2018 by Mickaël Thomas * + * mickael9@gmail.com * + * * + * Copyright (C) 2016 by Maksym Hilliaka * + * oter@frozen-team.com * + * * + * Copyright (C) 2016 by Phillip Pearson * + * pp@myelin.co.nz * + * * + * Copyright (C) 2014 by Paul Fertser * + * fercerpav@gmail.com * + * * + * Copyright (C) 2013 by mike brown * + * mike@theshedworks.org.uk * + * * + * Copyright (C) 2013 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "cmsis_dap.h" + +struct cmsis_dap_backend_data { + libusb_context *usb_ctx; + libusb_device_handle *dev_handle; + unsigned int ep_out; + unsigned int ep_in; + int interface; +}; + +static int cmsis_dap_usb_interface = -1; + +static int cmsis_dap_usb_open(struct cmsis_dap *dap, uint16_t vids[], uint16_t pids[], char *serial) +{ + int err; + libusb_context *ctx; + libusb_device **device_list; + + err = libusb_init(&ctx); + if (err) { + LOG_ERROR("libusb initialization failed: %s", libusb_strerror(err)); + return ERROR_FAIL; + } + + int num_devices = libusb_get_device_list(ctx, &device_list); + if (num_devices < 0) { + LOG_ERROR("could not enumerate USB devices: %s", libusb_strerror(num_devices)); + libusb_exit(ctx); + return ERROR_FAIL; + } + + for (int i = 0; i < num_devices; i++) { + libusb_device *dev = device_list[i]; + struct libusb_device_descriptor dev_desc; + + err = libusb_get_device_descriptor(dev, &dev_desc); + if (err) { + LOG_ERROR("could not get device descriptor for device %d: %s", i, libusb_strerror(err)); + continue; + } + + /* Match VID/PID */ + + bool id_match = true; /* match if we don't enter the loop (no filter) */ + for (int id = 0; vids[id] || pids[id]; id++) { + id_match = !vids[id] || dev_desc.idVendor == vids[id]; + id_match &= !pids[id] || dev_desc.idProduct == pids[id]; + + if (id_match) + break; + } + + if (!id_match) + continue; + + /* Don't continue if we asked for a serial number and the device doesn't have one */ + if (dev_desc.iSerialNumber == 0 && serial && serial[0]) + continue; + + libusb_device_handle *dev_handle = NULL; + err = libusb_open(dev, &dev_handle); + if (err) { + /* It's to be expected that most USB devices can't be opened + * so only report an error if it was explicitly selected + */ + if (vids[0] || pids[0]) { + LOG_ERROR("could not open device 0x%04x:0x%04x: %s", + dev_desc.idVendor, dev_desc.idProduct, libusb_strerror(err)); + } else { + LOG_DEBUG("could not open device 0x%04x:0x%04x: %s", + dev_desc.idVendor, dev_desc.idProduct, libusb_strerror(err)); + } + continue; + } + + /* Match serial number */ + + bool serial_match = (serial == NULL); + char dev_serial[256] = {0}; + if (dev_desc.iSerialNumber > 0) { + err = libusb_get_string_descriptor_ascii( + dev_handle, dev_desc.iSerialNumber, + (uint8_t *)dev_serial, sizeof(dev_serial)); + + if (err < 0) { + LOG_ERROR("could not read serial number for device 0x%04x:0x%04x: %s", + dev_desc.idVendor, dev_desc.idProduct, libusb_strerror(err)); + } else if (serial && strncmp(dev_serial, serial, sizeof(dev_serial)) == 0) { + serial_match = true; + } + } + + if (!serial_match) { + libusb_close(dev_handle); + continue; + } + + /* Find the CMSIS-DAP string in product string */ + + bool cmsis_dap_found = false; + char product_string[256] = {0}; + if (dev_desc.iProduct > 0) { + err = libusb_get_string_descriptor_ascii( + dev_handle, dev_desc.iProduct, + (uint8_t *)product_string, sizeof(product_string)); + if (err < 0) { + LOG_ERROR("could not read product string for device 0x%04x:0x%04x: %s", + dev_desc.idVendor, dev_desc.idProduct, libusb_strerror(err)); + } else if (strstr(product_string, "CMSIS-DAP")) { + LOG_DEBUG("CMSIS-DAP found in product string"); + cmsis_dap_found = true; + } + } + + /* Find the CMSIS-DAP interface */ + + for (int config = 0; config < dev_desc.bNumConfigurations; config++) { + struct libusb_config_descriptor *config_desc; + err = libusb_get_config_descriptor(dev, config, &config_desc); + if (err) { + LOG_ERROR("could not get configuration descriptor %d for device 0x%04x:0x%04x: %s", + config, dev_desc.idVendor, dev_desc.idProduct, libusb_strerror(err)); + continue; + } + + int config_num = config_desc->bConfigurationValue; + + for (int interface = 0; interface < config_desc->bNumInterfaces; interface++) { + const struct libusb_interface_descriptor *intf_desc = &config_desc->interface[interface].altsetting[0]; + int interface_num = intf_desc->bInterfaceNumber; + + /* Skip this interface if another one was requested explicitly */ + if (cmsis_dap_usb_interface != -1 && cmsis_dap_usb_interface != interface_num) + continue; + + /* CMSIS-DAP v2 spec says: + * + * CMSIS-DAP with default V2 configuration uses WinUSB and is therefore faster. + * Optionally support for streaming SWO trace is provided via an additional USB endpoint. + * + * The WinUSB configuration requires custom class support with the interface setting + * Class Code: 0xFF (Vendor specific) + * Subclass: 0x00 + * Protocol code: 0x00 + * + * Depending on the configuration it uses the following USB endpoints which should be configured + * in the interface descriptor in this order: + * - Endpoint 1: Bulk Out – used for commands received from host PC. + * - Endpoint 2: Bulk In – used for responses send to host PC. + * - Endpoint 3: Bulk In (optional) – used for streaming SWO trace (if enabled with SWO_STREAM). + */ + + if (intf_desc->bNumEndpoints < 2) + continue; + + if ((intf_desc->endpoint[0].bmAttributes & 3) != LIBUSB_TRANSFER_TYPE_BULK || + (intf_desc->endpoint[0].bEndpointAddress & 0x80) != LIBUSB_ENDPOINT_OUT) + continue; + + if ((intf_desc->endpoint[1].bmAttributes & 3) != LIBUSB_TRANSFER_TYPE_BULK || + (intf_desc->endpoint[1].bEndpointAddress & 0x80) != LIBUSB_ENDPOINT_IN) + continue; + + /* Bypass the following checks if this interface was explicitly requested. */ + if (cmsis_dap_usb_interface == -1) { + if (intf_desc->bInterfaceClass != LIBUSB_CLASS_VENDOR_SPEC || + intf_desc->bInterfaceSubClass != 0 || intf_desc->bInterfaceProtocol != 0) + continue; + + /* Search for "CMSIS-DAP" in the interface string */ + if (cmsis_dap_usb_interface != -1 && !cmsis_dap_found) { + if (intf_desc->iInterface == 0) + continue; + + char interface_str[256] = {0}; + + err = libusb_get_string_descriptor_ascii( + dev_handle, intf_desc->iInterface, + (uint8_t *)interface_str, sizeof(interface_str)); + if (err < 0) { + LOG_ERROR("could not read interface string for device 0x%04x:0x%04x: %s", + dev_desc.idVendor, dev_desc.idProduct, libusb_strerror(err)); + continue; + } else if (!strstr(interface_str, "CMSIS-DAP")) { + continue; + } else { + LOG_DEBUG("CMSIS-DAP found in interface string"); + } + } + } + + int packet_size = intf_desc->endpoint[0].wMaxPacketSize; + int ep_out = intf_desc->endpoint[0].bEndpointAddress; + int ep_in = intf_desc->endpoint[1].bEndpointAddress; + + /* That's the one! */ + libusb_free_config_descriptor(config_desc); + libusb_free_device_list(device_list, true); + + LOG_INFO("Using CMSIS-DAPv2 interface with VID:PID=0x%04x:0x%04x, serial=%s", + dev_desc.idVendor, dev_desc.idProduct, dev_serial); + + int current_config; + err = libusb_get_configuration(dev_handle, ¤t_config); + if (err) { + LOG_ERROR("could not find current configuration: %s", libusb_strerror(err)); + libusb_close(dev_handle); + libusb_exit(ctx); + return ERROR_FAIL; + } + + if (config_num != current_config) { + err = libusb_set_configuration(dev_handle, config_num); + if (err) { + LOG_ERROR("could not set configuration: %s", libusb_strerror(err)); + libusb_close(dev_handle); + libusb_exit(ctx); + return ERROR_FAIL; + } + } + + err = libusb_claim_interface(dev_handle, interface_num); + if (err) + LOG_WARNING("could not claim interface: %s", libusb_strerror(err)); + + dap->bdata = malloc(sizeof(struct cmsis_dap_backend_data)); + if (dap->bdata == NULL) { + LOG_ERROR("unable to allocate memory"); + libusb_release_interface(dev_handle, interface_num); + libusb_close(dev_handle); + libusb_exit(ctx); + return ERROR_FAIL; + } + + dap->packet_size = packet_size + 1; /* "+ 1" for compatibility with the HID backend */ + dap->bdata->usb_ctx = ctx; + dap->bdata->dev_handle = dev_handle; + dap->bdata->ep_out = ep_out; + dap->bdata->ep_in = ep_in; + dap->bdata->interface = interface_num; + return ERROR_OK; + } + + libusb_free_config_descriptor(config_desc); + } + + libusb_close(dev_handle); + } + + libusb_free_device_list(device_list, true); + + libusb_exit(ctx); + return ERROR_FAIL; +} + +static void cmsis_dap_usb_close(struct cmsis_dap *dap) +{ + libusb_release_interface(dap->bdata->dev_handle, dap->bdata->interface); + libusb_close(dap->bdata->dev_handle); + libusb_exit(dap->bdata->usb_ctx); + free(dap->bdata); + dap->bdata = NULL; +} + +static int cmsis_dap_usb_read(struct cmsis_dap *dap, int timeout_ms) +{ + int transferred = 0; + int err; + + err = libusb_bulk_transfer(dap->bdata->dev_handle, dap->bdata->ep_in, + dap->packet_buffer, dap->packet_size, &transferred, timeout_ms); + if (err) { + if (err == LIBUSB_ERROR_TIMEOUT) { + return ERROR_TIMEOUT_REACHED; + } else { + LOG_ERROR("error reading data: %s", libusb_strerror(err)); + return ERROR_FAIL; + } + } + + memset(&dap->packet_buffer[transferred], 0, dap->packet_size - transferred); + + return transferred; +} + +static int cmsis_dap_usb_write(struct cmsis_dap *dap, int txlen, int timeout_ms) +{ + int transferred = 0; + int err; + + /* skip the first byte that is only used by the HID backend */ + err = libusb_bulk_transfer(dap->bdata->dev_handle, dap->bdata->ep_out, + dap->packet_buffer + 1, txlen - 1, &transferred, timeout_ms); + if (err) { + if (err == LIBUSB_ERROR_TIMEOUT) { + return ERROR_TIMEOUT_REACHED; + } else { + LOG_ERROR("error writing data: %s", libusb_strerror(err)); + return ERROR_FAIL; + } + } + + return transferred; +} + +COMMAND_HANDLER(cmsis_dap_handle_usb_interface_command) +{ + if (CMD_ARGC == 1) + cmsis_dap_usb_interface = strtoul(CMD_ARGV[0], NULL, 10); + else + LOG_ERROR("expected exactly one argument to cmsis_dap_usb_interface "); + + return ERROR_OK; +} + +const struct command_registration cmsis_dap_usb_subcommand_handlers[] = { + { + .name = "interface", + .handler = &cmsis_dap_handle_usb_interface_command, + .mode = COMMAND_CONFIG, + .help = "set the USB interface number to use (for USB bulk backend only)", + .usage = "", + }, + COMMAND_REGISTRATION_DONE +}; + +const struct cmsis_dap_backend cmsis_dap_usb_backend = { + .name = "usb_bulk", + .open = cmsis_dap_usb_open, + .close = cmsis_dap_usb_close, + .read = cmsis_dap_usb_read, + .write = cmsis_dap_usb_write, +}; diff --git a/src/jtag/drivers/cmsis_dap_usb_hid.c b/src/jtag/drivers/cmsis_dap_usb_hid.c new file mode 100644 index 0000000000..681aef1712 --- /dev/null +++ b/src/jtag/drivers/cmsis_dap_usb_hid.c @@ -0,0 +1,208 @@ +/*************************************************************************** + * Copyright (C) 2018 by Mickaël Thomas * + * mickael9@gmail.com * + * * + * Copyright (C) 2016 by Maksym Hilliaka * + * oter@frozen-team.com * + * * + * Copyright (C) 2016 by Phillip Pearson * + * pp@myelin.co.nz * + * * + * Copyright (C) 2014 by Paul Fertser * + * fercerpav@gmail.com * + * * + * Copyright (C) 2013 by mike brown * + * mike@theshedworks.org.uk * + * * + * Copyright (C) 2013 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "cmsis_dap.h" + +#define PACKET_SIZE (64 + 1) /* 64 bytes plus report id */ + +struct cmsis_dap_backend_data { + hid_device *dev_handle; +}; + +static int cmsis_dap_hid_open(struct cmsis_dap *dap, uint16_t vids[], uint16_t pids[], char *serial) +{ + hid_device *dev = NULL; + int i; + struct hid_device_info *devs, *cur_dev; + unsigned short target_vid, target_pid; + + target_vid = 0; + target_pid = 0; + + if (hid_init() != 0) { + LOG_ERROR("unable to open HIDAPI"); + return ERROR_FAIL; + } + + /* + * The CMSIS-DAP specification stipulates: + * "The Product String must contain "CMSIS-DAP" somewhere in the string. This is used by the + * debuggers to identify a CMSIS-DAP compliant Debug Unit that is connected to a host computer." + */ + devs = hid_enumerate(0x0, 0x0); + cur_dev = devs; + while (NULL != cur_dev) { + bool found = false; + + if (0 == vids[0]) { + if (NULL == cur_dev->product_string) { + LOG_DEBUG("Cannot read product string of device 0x%x:0x%x", + cur_dev->vendor_id, cur_dev->product_id); + } else if (wcsstr(cur_dev->product_string, L"CMSIS-DAP")) { + /* if the user hasn't specified VID:PID *and* + * product string contains "CMSIS-DAP", pick it + */ + found = true; + } + } else { + /* otherwise, exhaustively compare against all VID:PID in list */ + for (i = 0; vids[i] || pids[i]; i++) { + if ((vids[i] == cur_dev->vendor_id) && (pids[i] == cur_dev->product_id)) + found = true; + } + } + + /* LPC-LINK2 has cmsis-dap on interface 0 and other HID functions on other interfaces */ + if (cur_dev->vendor_id == 0x1fc9 && cur_dev->product_id == 0x0090 && cur_dev->interface_number != 0) + found = false; + + if (found) { + /* check serial number matches if given */ + if (serial == NULL) + break; + + if (cur_dev->serial_number != NULL) { + size_t len = (strlen(serial) + 1) * sizeof(wchar_t); + wchar_t *wserial = malloc(len); + mbstowcs(wserial, serial, len); + + if (wcscmp(wserial, cur_dev->serial_number) == 0) { + free(wserial); + break; + } else { + free(wserial); + wserial = NULL; + } + } + } + + cur_dev = cur_dev->next; + } + + if (NULL != cur_dev) { + target_vid = cur_dev->vendor_id; + target_pid = cur_dev->product_id; + } + + if (target_vid == 0 && target_pid == 0) { + hid_free_enumeration(devs); + return ERROR_FAIL; + } + + dap->bdata = malloc(sizeof(struct cmsis_dap_backend_data)); + if (dap->bdata == NULL) { + LOG_ERROR("unable to allocate memory"); + return ERROR_FAIL; + } + + dev = hid_open_path(cur_dev->path); + hid_free_enumeration(devs); + + if (dev == NULL) { + LOG_ERROR("unable to open CMSIS-DAP device 0x%x:0x%x", target_vid, target_pid); + return ERROR_FAIL; + } + + /* allocate default packet buffer, may be changed later. + * currently with HIDAPI we have no way of getting the output report length + * without this info we cannot communicate with the adapter. + * For the moment we have to hard code the packet size */ + + dap->packet_size = PACKET_SIZE; + + /* atmel cmsis-dap uses 512 byte reports */ + /* except when it doesn't e.g. with mEDBG on SAMD10 Xplained + * board */ + /* TODO: HID report descriptor should be parsed instead of + * hardcoding a match by VID */ + if (target_vid == 0x03eb && target_pid != 0x2145 && target_pid != 0x2175) + dap->packet_size = 512 + 1; + + dap->bdata->dev_handle = dev; + + return ERROR_OK; +} + +static void cmsis_dap_hid_close(struct cmsis_dap *dap) +{ + hid_close(dap->bdata->dev_handle); + hid_exit(); + free(dap->bdata); + dap->bdata = NULL; +} + +static int cmsis_dap_hid_read(struct cmsis_dap *dap, int timeout_ms) +{ + int retval = hid_read_timeout(dap->bdata->dev_handle, dap->packet_buffer, dap->packet_size, timeout_ms); + + if (retval == 0) { + return ERROR_TIMEOUT_REACHED; + } else if (retval == -1) { + LOG_ERROR("error reading data: %ls", hid_error(dap->bdata->dev_handle)); + return ERROR_FAIL; + } + + return retval; +} + +static int cmsis_dap_hid_write(struct cmsis_dap *dap, int txlen, int timeout_ms) +{ + (void) timeout_ms; + + /* Pad the rest of the TX buffer with 0's */ + memset(dap->packet_buffer + txlen, 0, dap->packet_size - txlen); + + /* write data to device */ + int retval = hid_write(dap->bdata->dev_handle, dap->packet_buffer, dap->packet_size); + if (retval == -1) { + LOG_ERROR("error writing data: %ls", hid_error(dap->bdata->dev_handle)); + return ERROR_FAIL; + } + + return retval; +} + +const struct cmsis_dap_backend cmsis_dap_hid_backend = { + .name = "hid", + .open = cmsis_dap_hid_open, + .close = cmsis_dap_hid_close, + .read = cmsis_dap_hid_read, + .write = cmsis_dap_hid_write, +}; diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c index 2fa53be2b6..061a78f9c2 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -134,7 +134,7 @@ extern struct adapter_driver aice_adapter_driver; #if BUILD_BCM2835GPIO == 1 extern struct adapter_driver bcm2835gpio_adapter_driver; #endif -#if BUILD_CMSIS_DAP == 1 +#if BUILD_CMSIS_DAP_USB == 1 || BUILD_CMSIS_DAP_HID == 1 extern struct adapter_driver cmsis_dap_adapter_driver; #endif #if BUILD_KITPROG == 1 @@ -254,7 +254,7 @@ struct adapter_driver *adapter_drivers[] = { #if BUILD_BCM2835GPIO == 1 &bcm2835gpio_adapter_driver, #endif -#if BUILD_CMSIS_DAP == 1 +#if BUILD_CMSIS_DAP_USB == 1 || BUILD_CMSIS_DAP_HID == 1 &cmsis_dap_adapter_driver, #endif #if BUILD_KITPROG == 1 -- 2.30.2