diff --git a/Makefile b/Makefile index 15ce20aa..3f7a53cb 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ ifeq ($(PROBE_HOST), pc-stlinkv2) PC_HOSTED = true NO_LIBOPENCM3 = true endif -ifeq ($(PROBE_HOST), pc-hosted) +ifeq ($(PROBE_HOST), hosted) PC_HOSTED = true NO_LIBOPENCM3 = true endif diff --git a/UsingSWO b/UsingSWO index 36517ead..a9d8ee16 100644 --- a/UsingSWO +++ b/UsingSWO @@ -109,12 +109,13 @@ Note that swolisten can be used with either BMP firmware, or with a conventional TTL serial dongle. See at the bottom of this file for information on how to use a dongle. -The command line to build the swolisten tool is; +The command line to build the swolisten tool may look like: -gcc -I /usr/local/include/libusb-1.0 -L /usr/local/lib -lusb-1.0 swolisten.c -o swolisten +E.g. for Ubuntu +gcc -I /usr/local/include/libusb-1.0 -L /usr/local/lib swolisten.c -o swolisten -lusb-1.0 -For Opensuse: -gcc -I /usr/include/libusb-1.0 -lusb-1.0 swolisten.c swolisten -std=gnu99 -g -Og +E.g. For Opensuse: +gcc -I /usr/include/libusb-1.0 swolisten.c swolisten -std=gnu99 -g -Og -lusb-1.0 ...you will obviously need to change the paths to your libusb files. diff --git a/scripts/stm32_mem.py b/scripts/stm32_mem.py index ae994029..c6feed9d 100755 --- a/scripts/stm32_mem.py +++ b/scripts/stm32_mem.py @@ -18,6 +18,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from __future__ import print_function from time import sleep import struct import os @@ -27,9 +28,9 @@ import argparse import usb import dfu -CMD_GETCOMMANDS = 0x00 -CMD_SETADDRESSPOINTER = 0x21 -CMD_ERASE = 0x41 +CMD_GETCOMMANDS = 0x00 +CMD_SETADDRESSPOINTER = 0x21 +CMD_ERASE = 0x41 def stm32_erase(dev, addr): erase_cmd = struct.pack(" 0x1f800): + if (os.path.getsize(args.progfile) > 0x1f800): print("File too large") exit(0) @@ -212,7 +213,7 @@ if __name__ == "__main__": start = 0x8002000 addr = start while bin: - print ("Programming memory at 0x%08X\r" % addr), + print ("Programming memory at 0x%08X" % addr, end="\r") stdout.flush() try: # STM DFU bootloader erases always. @@ -243,7 +244,7 @@ if __name__ == "__main__": except: # Abort silent if bootloader does not support upload break - print ("Verifying memory at 0x%08X\r" % addr), + print ("Verifying memory at 0x%08X" % addr, end="\r") stdout.flush() if len > 1024 : size = 1024 diff --git a/src/gdb_main.c b/src/gdb_main.c index 9157f2b8..fcf79e69 100644 --- a/src/gdb_main.c +++ b/src/gdb_main.c @@ -490,19 +490,23 @@ handle_v_packet(char *packet, int plen) target_reset(cur_target); flash_mode = 1; } - if(target_flash_erase(cur_target, addr, len) == 0) + if(target_flash_erase(cur_target, addr, len) == 0) { gdb_putpacketz("OK"); - else + } else { + flash_mode = 0; gdb_putpacketz("EFF"); + } } else if (sscanf(packet, "vFlashWrite:%08lx:%n", &addr, &bin) == 1) { /* Write Flash Memory */ len = plen - bin; DEBUG_GDB("Flash Write %08lX %08lX\n", addr, len); - if(cur_target && target_flash_write(cur_target, addr, (void*)packet + bin, len) == 0) + if(cur_target && target_flash_write(cur_target, addr, (void*)packet + bin, len) == 0) { gdb_putpacketz("OK"); - else + } else { + flash_mode = 0; gdb_putpacketz("EFF"); + } } else if (!strcmp(packet, "vFlashDone")) { /* Commit flash operations. */ diff --git a/src/platforms/Readme.md b/src/platforms/Readme.md new file mode 100644 index 00000000..b3480bb4 --- /dev/null +++ b/src/platforms/Readme.md @@ -0,0 +1,23 @@ +# Platforms and platform support files + +This directory contains the implementation of platforms and support file +used by (multiple) platforms. + +## Implementation directories + +native : Firmware for original Black Magic Probe
+stlink : Firmware for STLINK-V2 and V21
+swlink : Firmware for STLINK-V1 and Bluepill
+hydrabus : Firmware https://hydrabus.com/
+f4discovery : Firmware for STM32F407DISCO
+launchpad-icdi :
+tm4c:
+hosted: PC-hosted BMP running as PC application talking to firmware BMPs, + STLINK-V2/21/3, FTDI MPSSE probes, CMSIS-DAP and JLINK + + +## Support directories + +common: libopencm3 based support for firmware BMPs
+stm32: STM32 specific libopencm3 based support for firmware BMPs
+pc: Support for PC-hosted BMPs.
diff --git a/src/platforms/common/cdcacm.c b/src/platforms/common/cdcacm.c index 5ebbae02..73f594aa 100644 --- a/src/platforms/common/cdcacm.c +++ b/src/platforms/common/cdcacm.c @@ -61,7 +61,7 @@ static const struct usb_device_descriptor dev = { #ifdef SAMD21E17 .bMaxPacketSize0 = 64, #else - .bMaxPacketSize0 = 8, + .bMaxPacketSize0 = 32, #endif .idVendor = 0x1D50, .idProduct = 0x6018, diff --git a/src/platforms/common/swdptap.c b/src/platforms/common/swdptap.c index 29278ccc..d6e82d21 100644 --- a/src/platforms/common/swdptap.c +++ b/src/platforms/common/swdptap.c @@ -52,6 +52,8 @@ static void swdptap_turnaround(int dir) SWDIO_MODE_FLOAT(); gpio_set(SWCLK_PORT, SWCLK_PIN); gpio_set(SWCLK_PORT, SWCLK_PIN); + gpio_set(SWCLK_PORT, SWCLK_PIN); + gpio_set(SWCLK_PORT, SWCLK_PIN); gpio_clear(SWCLK_PORT, SWCLK_PIN); if(dir == SWDIO_STATUS_DRIVE) SWDIO_MODE_DRIVE(); @@ -68,6 +70,9 @@ static uint32_t swdptap_seq_in(int ticks) int res; res = gpio_get(SWDIO_PORT, SWDIO_PIN); gpio_set(SWCLK_PORT, SWCLK_PIN); + gpio_set(SWCLK_PORT, SWCLK_PIN); + gpio_set(SWCLK_PORT, SWCLK_PIN); + gpio_set(SWCLK_PORT, SWCLK_PIN); if (res) ret |= index; index <<= 1; @@ -90,20 +95,22 @@ static bool swdptap_seq_in_parity(uint32_t *ret, int ticks) swdptap_turnaround(SWDIO_STATUS_FLOAT); while (len--) { - gpio_clear(SWCLK_PORT, SWCLK_PIN); bit = gpio_get(SWDIO_PORT, SWDIO_PIN); gpio_set(SWCLK_PORT, SWCLK_PIN); - if (bit) { + gpio_set(SWCLK_PORT, SWCLK_PIN); + gpio_set(SWCLK_PORT, SWCLK_PIN); + if (bit) res |= index; - } index <<= 1; + gpio_clear(SWCLK_PORT, SWCLK_PIN); } - gpio_clear(SWCLK_PORT, SWCLK_PIN); int parity = __builtin_popcount(res); bit = gpio_get(SWDIO_PORT, SWDIO_PIN); gpio_set(SWCLK_PORT, SWCLK_PIN); if (bit) parity++; + else + gpio_set(SWCLK_PORT, SWCLK_PIN); gpio_clear(SWCLK_PORT, SWCLK_PIN); #ifdef DEBUG_SWD_BITS for (int i = 0; i < len; i++) @@ -115,42 +122,39 @@ static bool swdptap_seq_in_parity(uint32_t *ret, int ticks) static void swdptap_seq_out(uint32_t MS, int ticks) { - int data = MS & 1; #ifdef DEBUG_SWD_BITS for (int i = 0; i < ticks; i++) DEBUG("%d", (MS & (1 << i)) ? 1 : 0); #endif swdptap_turnaround(SWDIO_STATUS_DRIVE); + gpio_set_val(SWDIO_PORT, SWDIO_PIN, MS & 1); while (ticks--) { - gpio_set_val(SWDIO_PORT, SWDIO_PIN, data); - gpio_clear(SWCLK_PORT, SWCLK_PIN); - MS >>= 1; - data = MS & 1; gpio_set(SWCLK_PORT, SWCLK_PIN); + MS >>= 1; + gpio_set_val(SWDIO_PORT, SWDIO_PIN, MS & 1); + gpio_clear(SWCLK_PORT, SWCLK_PIN); + gpio_clear(SWCLK_PORT, SWCLK_PIN); } - gpio_clear(SWCLK_PORT, SWCLK_PIN); } static void swdptap_seq_out_parity(uint32_t MS, int ticks) { int parity = __builtin_popcount(MS); - int data = MS & 1; #ifdef DEBUG_SWD_BITS for (int i = 0; i < ticks; i++) DEBUG("%d", (MS & (1 << i)) ? 1 : 0); #endif swdptap_turnaround(SWDIO_STATUS_DRIVE); - gpio_set_val(SWDIO_PORT, SWDIO_PIN, data); + gpio_set_val(SWDIO_PORT, SWDIO_PIN, MS & 1); MS >>= 1; while (ticks--) { - data = MS & 1; gpio_set(SWCLK_PORT, SWCLK_PIN); - gpio_set_val(SWDIO_PORT, SWDIO_PIN, data); + gpio_set_val(SWDIO_PORT, SWDIO_PIN, MS & 1); MS >>= 1; gpio_clear(SWCLK_PORT, SWCLK_PIN); } gpio_set_val(SWDIO_PORT, SWDIO_PIN, parity & 1); - gpio_clear(SWCLK_PORT, SWCLK_PIN); + gpio_set(SWCLK_PORT, SWCLK_PIN); gpio_set(SWCLK_PORT, SWCLK_PIN); gpio_set(SWCLK_PORT, SWCLK_PIN); gpio_clear(SWCLK_PORT, SWCLK_PIN); diff --git a/src/platforms/f4discovery/Readme b/src/platforms/f4discovery/Readme deleted file mode 100644 index bbcfc4fb..00000000 --- a/src/platforms/f4discovery/Readme +++ /dev/null @@ -1,28 +0,0 @@ -System vs BMP Bootloader -======================== -For the BMP bootloader, flashing was not reliable. So we use the system -bootloder unconditional. - -Connections: -==================== - -PA0: User button to force system bootloader entry with reset -PA2/PA3 eventual connected to the STLINK/ STM32F103C8 - -PC2: TDI -PC4: TMS/SWDIO -PC5: TCK/SWCLK -PC6: TDO/TRACESWO - -PC1: TRST -PC8: SRST - -PD15/Blue Led: Indicator that system bootloader is entered via BMP - -Bootstrapping the F4Discovery on-board ST-Link -============================================== -http://embdev.net/articles/STM_Discovery_as_Black_Magic_Probe has some hints -how to modify the F4Discovery on-board ST-Link. If you try to do so and hit -a problem that stands some test like that you load the right firmware to the -right device via the right BMP probe, explain, report and ask on the -blackmagic mailing list http://sourceforge.net/mail/?group_id=407419. diff --git a/src/platforms/f4discovery/Readme.md b/src/platforms/f4discovery/Readme.md new file mode 100644 index 00000000..a3dae25f --- /dev/null +++ b/src/platforms/f4discovery/Readme.md @@ -0,0 +1,13 @@ +# Firmware BMP for STM32F407 DISCO boards + +Kept for historical reasons to load BMP bootloader to the STM32F103 of the onboard STLINK or external STLINKs. As stlink-tool now allows to load BMP firmware via the original STLINK bootloader is no longer really needed. + +## Connections: + +PC2: TDI
+PC4: TMS/SWDIO
+PC5: TCK/SWCLK
+PC6: TDO/TRACESWO
+ +PC1: TRST
+PC8: SRST
diff --git a/src/platforms/hosted/Makefile.inc b/src/platforms/hosted/Makefile.inc index dd1dfc44..d1700d01 100644 --- a/src/platforms/hosted/Makefile.inc +++ b/src/platforms/hosted/Makefile.inc @@ -1,5 +1,6 @@ SYS = $(shell $(CC) -dumpmachine) CFLAGS += -DENABLE_DEBUG -DPLATFORM_HAS_DEBUG +CFLAGS += -DUSE_USB_VERSION_BIT CFLAGS +=-I ./target -I./platforms/pc ifneq (, $(findstring linux, $(SYS))) @@ -15,22 +16,28 @@ SRC += serial_win.c LDFLAGS += -lws2_32 LDFLAGS += -lsetupapi #https://github.com/dmlc/xgboost/issues/1945 indicates macosx as indicator -else ifneq (filter, macosx darwin, $(SYS))) -LDFLAGS += -lsetupapi -LDFLAGS += hidapi/mac/.libs/libhidapi.a -framework IOKit -LDFLAGS += -framework CoreFoundation hidapi/mac/.libs/libhidapi.a +else ifneq (filter, macosx darwin, $(SYS)) +SRC += serial_unix.c +LDFLAGS += -lhidapi +LDFLAGS += -framework CoreFoundation CFLAGS += -Ihidapi/hidapi endif LDFLAGS += -lusb-1.0 CFLAGS += $(shell pkg-config --cflags libftdi1) LDFLAGS += $(shell pkg-config --libs libftdi1) +CFLAGS += -Wno-missing-field-initializers -ifeq ($(shell pkg-config --exists hidapi-libusb && echo 0), 0) -CFLAGS += $(shell pkg-config --cflags hidapi-libusb) -LDFLAGS += $(shell pkg-config --libs hidapi-libusb) -CFLAGS += -DCMSIS_DAP -SRC += cmsis_dap.c dap.c +ifneq (, $(findstring mingw, $(SYS))) + SRC += cmsis_dap.c dap.c hid.c + CFLAGS += -DCMSIS_DAP +else + ifeq ($(shell pkg-config --exists hidapi-libusb && echo 0), 0) + CFLAGS += $(shell pkg-config --cflags hidapi-libusb) + LDFLAGS += $(shell pkg-config --libs hidapi-libusb) + CFLAGS += -DCMSIS_DAP + SRC += cmsis_dap.c dap.c + endif endif VPATH += platforms/pc diff --git a/src/platforms/hosted/Readme.md b/src/platforms/hosted/Readme.md index bfbfaf6d..a3f31c43 100644 --- a/src/platforms/hosted/Readme.md +++ b/src/platforms/hosted/Readme.md @@ -13,19 +13,51 @@ connect to the BMP with the CDCACM GDB serial server. GDB functionality is the same, monitor option may vary. More arguments allow to -### print information on the connected target "blackmagiv -t" -### directly flash a binary file at 0x0800000 "blackmagic " +### Print information on the connected target +``` +blackmagic -t +``` +### Directly flash a binary file at lowest flash address +``` +blackmagic +``` or with the -S argument at some other address -### read flash to binary file "blackmagic -r .bin -### verify flash against binary file "blackmagic -V .bin - -Use "blackmagic -h" to see all options. +``` +blackmagic -S 0x08002000 +``` +### Read flash to binary file +``` +blackmagic -r .bin +``` +### Verify flash against binary file +``` +blackmagic -V .bin +``` +### Show more options +``` +blackmagic -h" +``` ## Used libraries: ### libusb ### libftdi, for FTDI support ### hidapi-libusb, for CMSIS-DAP support +## Compiling on windows + +You can crosscompile blackmagic for windows with mingw or on windows +with cygwin. For compilation, headers for libftdi1 and libusb-1.0 are +needed. For running, libftdi1.dll and libusb-1.0.dll are needed and +the executable must be able to find them. Mingw on cygwin does not provide +a libftdi package yet. + +To prepare libusb access to the ftdi device, run zadig https://zadig.akeo.ie/. +Choose WinUSB(libusb-1.0) for the BMP Ftdi device. + +Running cygwin/blackmagic in a cygwin console, the program does not react +on ^C. In another console, run "ps ax" to find the WINPID of the process +and then "taskkill /F ?PID (WINPID)". + ## Supported debuggers REMOTE_BMP is a "normal" BMP usb connected @@ -39,16 +71,76 @@ REMOTE_BMP is a "normal" BMP usb connected | FTDI MPSSE | ++ | Requires a device descrition | JLINK | - | Usefull to add BMP support for MCUs with built-in JLINK +## Device matching +As other USB dongles already connected to the host PC may use FTDI chips, +cable descriptions must be provided to match with the dongle. +To match the dongle, at least USB VID/PID that must match. +If a description is given, the USB device must provide that string. If a +serial number string is given on the command line, that number must match +with serial number in the USB descriptor of the device. + +## FTDI connection possibilities: + +| Direct Connection | +| ----------------------| +| MPSSE_SK --> JTAG_TCK | +| MPSSE_DO --> JTAG_TDI | +| MPSSE_DI <-- JTAG_TDO | +| MPSSE_CS <-> JTAG_TMS | + +\+ JTAG and bitbanging SWD is possible
+\- No level translation, no buffering, no isolation
+Example: [Flossjtag](https://randomprojects.org/wiki/Floss-JTAG). + +| Resistor SWD | +|------------------------| +| MPSSE_SK ---> JTAG_TCK | +| MPSSE_DO -R-> JTAG_TMS | +| MPSSE_DI <--- JTAG_TMS | + +BMP would allow direct MPSSE_DO ->JTAG_TMS connections as BMP tristates DO +when reading. Resistor defeats contentions anyways. R is typical choosen +in the range of 470R + +\+ MPSSE SWD possible
+\- No Jtag, no level translation, no buffering, no isolation
+ +|Direct buffered Connection| +|--------------------------| +| MPSSE_SK -B-> JTAG_TCK | +| MPSSE_DO -B-> JTAG_TDI | +| MPSSE_DI <-B- JTAG_TDO | +| MPSSE_CS -B-> JTAG_TMS | + +\+ Only Jtag, buffered, possible level translation and isolation
+\- No SWD
+Example: [Turtelizer]http://www.ethernut.de/en/hardware/turtelizer/index.html) +[schematics](http://www.ethernut.de/pdf/turtelizer20c-schematic.pdf) + +The 'r' command line arguments allows to specify an external SWD +resistor connection added to some existing cable. Jtag is not possible +together with the 'r' argument. + +### Many variants possible +As the FTDI has more pins, these pins may be used to control +enables of buffers and multiplexer selection in many variants. + +### FTDI SWD speed +SWD read needs two USB round trip, one for the acknowledge and one +round-trip after the data phase, while JTAG only needs one round-trip. +For that, SWD read speed is about half the JTAG read speed. + +### Reset, Target voltage readback etc +The additional pins may also control Reset functionality, provide +information if target voltage is applied. etc. + +### Cable descriptions +Please help to verify the cable description and give feedback on the +cables already listed and propose other cable. A link to the schematics +is welcome. + ## Feedback ### Issues and Pull request on https://github.com/blacksphere/blackmagic/ ### Discussions on Discord. You can find the Discord link here: https://1bitsquared.com/pages/chat ### Blackmagic mailing list http://sourceforge.net/mail/?group_id=407419 - -## Known deficiencies -### For REMOTE_BMP -#### On windows, the device node must be given on the command line -Finding the device from USB VID/PID/Serial in not yet implemented -### FTDI MPSSE -#### No auto detection -Cable description must be given on the command line diff --git a/src/platforms/hosted/bmp_remote.c b/src/platforms/hosted/bmp_remote.c index 22d7a494..9b679473 100644 --- a/src/platforms/hosted/bmp_remote.c +++ b/src/platforms/hosted/bmp_remote.c @@ -51,18 +51,7 @@ int remote_init(bool verbose) } if (verbose) DEBUG_WARN("Remote is %s\n", &construct[1]); - char *p = strstr(&construct[1], "(Firmware v"); - if (!p) - return -1; - int major = 0, minor = 0, step = 0; - int res = sscanf(p, "(Firmware v%d.%d.%d", &major, &minor, &step); - if (res !=3) - return -1; - uint32_t version = major * 10000 + minor * 100 + step; - /* check that firmare is > 1.6.1 */ - if (version < 10602) - return -1; - return 0; + return 0; } bool remote_target_get_power(void) @@ -346,7 +335,12 @@ static void remote_ap_mem_write_sized( void remote_adiv5_dp_defaults(ADIv5_DP_t *dp) { - if (remote_init(false)) { + uint8_t construct[REMOTE_MAX_MSG_SIZE]; + int s = snprintf((char *)construct, REMOTE_MAX_MSG_SIZE, "%s", + REMOTE_HL_CHECK_STR); + platform_buffer_write(construct, s); + s = platform_buffer_read(construct, REMOTE_MAX_MSG_SIZE); + if ((!s) || (construct[0] == REMOTE_RESP_ERR)) { DEBUG_WARN( "Please update BMP firmware for substantial speed increase!\n"); return; diff --git a/src/platforms/hosted/cmsis_dap.c b/src/platforms/hosted/cmsis_dap.c index d9fe402a..c955761a 100644 --- a/src/platforms/hosted/cmsis_dap.c +++ b/src/platforms/hosted/cmsis_dap.c @@ -51,6 +51,7 @@ static int report_size = 512 + 1; // TODO: read actual report size /* LPC845 Breakout Board Rev. 0 report invalid response with > 65 bytes */ int dap_init(bmp_info_t *info) { + printf("dap_init\n"); if (hid_init()) return -1; int size = strlen(info->serial); @@ -111,7 +112,7 @@ static uint32_t dap_dp_error(ADIv5_DP_t *dp) return err; } -static uint32_t dap_dp_low_access(struct ADIv5_DP_s *dp, uint8_t RnW, +static uint32_t dap_dp_low_access(struct ADIv5_DP_s *dp, uint8_t RnW, uint16_t addr, uint32_t value) { bool APnDP = addr & ADIV5_APnDP; @@ -128,7 +129,16 @@ static uint32_t dap_dp_low_access(struct ADIv5_DP_s *dp, uint8_t RnW, static uint32_t dap_dp_read_reg(ADIv5_DP_t *dp, uint16_t addr) { - return dap_read_reg(dp, addr); + uint32_t res; + if (addr & ADIV5_APnDP) { + dap_dp_low_access(dp, ADIV5_LOW_READ, addr, 0); + res = dap_dp_low_access(dp, ADIV5_LOW_READ, + ADIV5_DP_RDBUFF, 0); + } else { + res = dap_dp_low_access(dp, ADIV5_LOW_READ, addr, 0); + } + DEBUG_PROBE("dp_read %04x %08" PRIx32 "\n", addr, res); + return res; } void dap_exit_function(void) diff --git a/src/platforms/hosted/dap.c b/src/platforms/hosted/dap.c index 5db31c9a..fefaf353 100644 --- a/src/platforms/hosted/dap.c +++ b/src/platforms/hosted/dap.c @@ -374,11 +374,11 @@ void dap_write_reg(ADIv5_DP_t *dp, uint8_t reg, uint32_t data) } while (buf[1] == DAP_TRANSFER_WAIT); if (buf[1] > DAP_TRANSFER_WAIT) { - DEBUG_PROBE("dap_write_reg %02x data %08x:fault\n", reg, data); + DEBUG_WARN("dap_write_reg %02x data %08x:fault\n", reg, data); dp->fault = 1; } if (buf[1] == DAP_TRANSFER_ERROR) { - DEBUG_PROBE("dap_write_reg %02x data %08x: protocoll error\n", + DEBUG_WARN("dap_write_reg %02x data %08x: protocoll error\n", reg, data); dap_line_reset(); } @@ -399,8 +399,8 @@ unsigned int dap_read_block(ADIv5_AP_t *ap, void *dest, uint32_t src, buf[4] = SWD_AP_DRW | DAP_TRANSFER_RnW; dbg_dap_cmd(buf, 1023, 5 + 1); unsigned int transferred = buf[0] + (buf[1] << 8); - if (buf[2] > DAP_TRANSFER_FAULT) { - DEBUG_PROBE("line_reset\n"); + if (buf[2] >= DAP_TRANSFER_FAULT) { + DEBUG_WARN("dap_read_block @ %08 "PRIx32 " fault -> line reset\n", src); dap_line_reset(); } if (sz != transferred) { diff --git a/src/platforms/hosted/ftdi_bmp.c b/src/platforms/hosted/ftdi_bmp.c index 46f969fe..f4f4fe47 100644 --- a/src/platforms/hosted/ftdi_bmp.c +++ b/src/platforms/hosted/ftdi_bmp.c @@ -2,6 +2,7 @@ * This file is part of the Black Magic Debug project. * * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Copyright (C) 2018 Uwe Bonnes(bon@elektron.ikp.physik.tu-darmstadt.de) * Written by Gareth McMullin * * This program is free software: you can redistribute it and/or modify @@ -36,42 +37,110 @@ static uint8_t outbuf[BUF_SIZE]; static uint16_t bufptr = 0; cable_desc_t *active_cable; +data_desc_t active_state; cable_desc_t cable_desc[] = { { - /* Direct connection from FTDI to Jtag/Swd.*/ + /* Direct connection from FTDI to Jtag/Swd. + Pin 6 direct connected to RST.*/ + .vendor = 0x0403, + .product = 0x6014, + .interface = INTERFACE_A, + // No explicit reset + .bb_swdio_in_port_cmd = GET_BITS_LOW, + .bb_swdio_in_pin = MPSSE_CS, + .description = "UM232H", + .name = "um232h" + }, + { + /* Direct connection from FTDI to Jtag/Swd. + Pin 6 direct connected to RST.*/ .vendor = 0x0403, .product = 0x6010, .interface = INTERFACE_A, - .dbus_data = 0x08, - .dbus_ddr = 0x1B, - .bitbang_tms_in_port_cmd = GET_BITS_LOW, - .bitbang_tms_in_pin = MPSSE_TMS, + .init.data_low = PIN6, /* PULL nRST high*/ + .bb_swdio_in_port_cmd = GET_BITS_LOW, + .bb_swdio_in_pin = MPSSE_CS, + .assert_srst.data_low = ~PIN6, + .assert_srst.ddr_low = PIN6, + .deassert_srst.data_low = PIN6, + .deassert_srst.ddr_low = ~PIN6, .description = "FLOSS-JTAG", .name = "flossjtag" }, + { + /* MPSSE_SK (DB0) ----------- SWDCK/JTCK + * MPSSE-DO (DB1) -- 470 R -- SWDIO/JTMS + * MPSSE-DI (DB2) ----------- SWDIO/JTMS + * DO is tristated with SWD read, so + * resistor is not necessary, but protects + * from contentions in case of errors. + * JTAG not possible + * PIN6 (DB6) ----------- NRST */ + .vendor = 0x0403, + .product = 0x6010,/*FT2232H*/ + .interface = INTERFACE_B, + .init.data_low = PIN4, /* Pull up pin 4 */ + .init.ddr_low = PIN4, /* Pull up pin 4 */ + .mpsse_swd_read.set_data_low = MPSSE_DO, + .mpsse_swd_write.set_data_low = MPSSE_DO, + .assert_srst.data_low = ~PIN6, + .assert_srst.ddr_low = PIN6, + .deassert_srst.data_low = PIN6, + .deassert_srst.ddr_low = ~PIN6, + .target_voltage_cmd = GET_BITS_LOW, + .target_voltage_pin = PIN4, /* Always read as target voltage present.*/ + .description = "USBMATE", + .name = "usbmate" + }, + { + /* MPSSE_SK (DB0) ----------- SWDCK/JTCK + * MPSSE-DO (DB1) -- 470 R -- SWDIO/JTMS + * MPSSE-DI (DB2) ----------- SWDIO/JTMS + * DO is tristated with SWD read, so + * resistor is not necessary, but protects + * from contentions in case of errors. + * JTAG not possible.*/ + .vendor = 0x0403, + .product = 0x6014,/*FT232H*/ + .interface = INTERFACE_A, + .mpsse_swd_read.set_data_low = MPSSE_DO, + .mpsse_swd_write.set_data_low = MPSSE_DO, + .name = "ft232h_resistor_swd" + }, { /* Buffered connection from FTDI to Jtag/Swd. * TCK and TMS not independant switchable! - * SWD not possible. */ + * SWD not possible. + * PIN4 low enables buffers + * PIN5 Low indicates VRef applied + * PIN6 reads back SRST + * CBUS PIN1 Sets SRST + * CBUS PIN2 low drives SRST + */ .vendor = 0x0403, .product = 0x6010, .interface = INTERFACE_A, - .dbus_data = 0x08, - .dbus_ddr = 0x1B, + .init.ddr_low = PIN4, + .init.data_high = PIN4 | PIN3 | PIN2, + .init.ddr_high = PIN4 | PIN3 | PIN2 | PIN1 | PIN0, + .assert_srst.data_high = ~PIN2, + .deassert_srst.data_high = PIN2, + .srst_get_port_cmd = GET_BITS_LOW, + .srst_get_pin = PIN6, .description = "FTDIJTAG", .name = "ftdijtag" }, { /* UART/SWO on Interface A * JTAG and control on INTERFACE_B - * Bit 5 high selects SWD-READ (TMS routed to TDO) - * Bit 6 high selects JTAG vs SWD (TMS routed to TDI/TDO) + * Bit 5 high selects SWD-WRITE (TMS routed to MPSSE_DI) + * Bit 6 high selects JTAG vs SWD (TMS routed to MPSSE_CS) * BCBUS 1 (Output) N_SRST - * BCBUS 2 (Input) V_ISO available + * BCBUS 2 (Input/ Internal Pull Up) V_ISO available * * For bitbanged SWD, set Bit 5 low and select SWD read with - * Bit 6 low. Read Connector TMS as FTDI TDO. + * Bit 6 low. Read Connector TMS as MPSSE_DI. * * TDO is routed to Interface 0 RXD as SWO or with Uart * Connector pin 10 pulled to ground will connect Interface 0 RXD @@ -80,33 +149,51 @@ cable_desc_t cable_desc[] = { .vendor = 0x0403, .product = 0x6010, .interface = INTERFACE_B, - .dbus_data = 0x6A, - .dbus_ddr = 0x6B, - .cbus_data = 0x02, - .cbus_ddr = 0x02, - .bitbang_tms_in_port_cmd = GET_BITS_LOW, - .bitbang_tms_in_pin = MPSSE_TDO, /* keep bit 5 low*/ - .bitbang_swd_dbus_read_data = 0x02, - .name = "ftdiswd" + .init.data_low = PIN6 | PIN5, + .init.ddr_low = PIN6 | PIN5, + .init.data_high = PIN1 | PIN2, + .assert_srst.data_high = ~PIN1, + .assert_srst.ddr_high = PIN1, + .deassert_srst.data_high = PIN1, + .deassert_srst.ddr_high = ~PIN1, + .mpsse_swd_read.clr_data_low = PIN5 | PIN6, + .mpsse_swd_write.set_data_low = PIN5, + .mpsse_swd_write.clr_data_low = PIN6, + .jtag.set_data_low = PIN6, + .target_voltage_cmd = GET_BITS_HIGH, + .target_voltage_pin = ~PIN2, + .name = "ftdiswd", + .description = "FTDISWD" }, { .vendor = 0x15b1, .product = 0x0003, .interface = INTERFACE_A, - .dbus_data = 0x08, - .dbus_ddr = 0x1B, + .init.ddr_low = PIN5, .name = "olimex" }, { /* Buffered connection from FTDI to Jtag/Swd. * TCK and TMS not independant switchable! - * => SWD not possible. */ + * => SWD not possible. + * DBUS PIN4 / JTAGOE low enables buffers + * DBUS PIN5 / TRST high drives nTRST low OC + * DBUS PIN6 / RST high drives nSRST low OC + * CBUS PIN0 reads back SRST + */ .vendor = 0x0403, .product = 0xbdc8, .interface = INTERFACE_A, - .dbus_data = 0x08, - .dbus_ddr = 0x1B, - .name = "turtelizer" + /* Drive low to activate JTAGOE and deassert TRST/RST.*/ + .init.data_low = 0, + .init.ddr_low = PIN6 | PIN5 | PIN4, + .init.ddr_high = PIN2, /* ONE LED */ + .assert_srst.data_low = PIN6, + .deassert_srst.data_low = ~PIN6, + .srst_get_port_cmd = GET_BITS_HIGH, + .srst_get_pin = PIN0, + .name = "turtelizer", + .description = "Turtelizer JTAG/RS232 Adapter" }, { /* https://reference.digilentinc.com/jtag_hs1/jtag_hs1 @@ -117,8 +204,6 @@ cable_desc_t cable_desc[] = { .vendor = 0x0403, .product = 0xbdc8, .interface = INTERFACE_A, - .dbus_data = 0x08, - .dbus_ddr = 0x1B, .name = "jtaghs1" }, { @@ -126,10 +211,10 @@ cable_desc_t cable_desc[] = { .vendor = 0x0403, .product = 0xbdc8, .interface = INTERFACE_A, - .dbus_data = 0xA8, - .dbus_ddr = 0xAB, - .bitbang_tms_in_port_cmd = GET_BITS_LOW, - .bitbang_tms_in_pin = MPSSE_TMS, + .init.data_low = MPSSE_CS | MPSSE_DO | MPSSE_DI, + .init.ddr_low = MPSSE_CS | MPSSE_DO | MPSSE_SK, + .bb_swdio_in_port_cmd = GET_BITS_LOW, + .bb_swdio_in_pin = MPSSE_CS, .name = "ftdi" }, { @@ -137,10 +222,10 @@ cable_desc_t cable_desc[] = { .vendor = 0x0403, .product = 0x6014, .interface = INTERFACE_A, - .dbus_data = 0x88, - .dbus_ddr = 0x8B, - .cbus_data = 0x20, - .cbus_ddr = 0x3f, + .init.data_low = PIN7, + .init.ddr_low = PIN7, + .init.data_high = PIN5, + .init.ddr_high = PIN5 | PIN4 | PIN3 | PIN2 | PIN1 | PIN0, .name = "digilent" }, { @@ -148,10 +233,10 @@ cable_desc_t cable_desc[] = { .vendor = 0x0403, .product = 0x6014, .interface = INTERFACE_A, - .dbus_data = 0x08, - .dbus_ddr = 0x0B, - .bitbang_tms_in_port_cmd = GET_BITS_LOW, - .bitbang_tms_in_pin = MPSSE_TMS, + .init.data_low = MPSSE_CS | MPSSE_DO | MPSSE_DI, + .init.ddr_low = MPSSE_CS | MPSSE_DO | MPSSE_SK, + .bb_swdio_in_port_cmd = GET_BITS_LOW, + .bb_swdio_in_pin = MPSSE_CS, .name = "ft232h" }, { @@ -159,45 +244,63 @@ cable_desc_t cable_desc[] = { .vendor = 0x0403, .product = 0x6011, .interface = INTERFACE_A, - .dbus_data = 0x08, - .dbus_ddr = 0x0B, - .bitbang_tms_in_port_cmd = GET_BITS_LOW, - .bitbang_tms_in_pin = MPSSE_TMS, + .bb_swdio_in_port_cmd = GET_BITS_LOW, + .bb_swdio_in_pin = MPSSE_CS, .name = "ft4232h" }, { /* http://www.olimex.com/dev/pdf/ARM-USB-OCD.pdf. - * BDUS 4 global enables JTAG Buffer. + * DBUS 4 global enables JTAG Buffer. * => TCK and TMS not independant switchable! * => SWD not possible. */ .vendor = 0x15ba, .product = 0x002b, .interface = INTERFACE_A, - .dbus_data = 0x08, - .dbus_ddr = 0x1B, - .cbus_data = 0x00, - .cbus_ddr = 0x08, + .init.ddr_low = PIN4, + .init.data_high = PIN3 | PIN1 | PIN0, + .init.ddr_high = PIN4 | PIN3 | PIN1 | PIN0, .name = "arm-usb-ocd-h" }, + { + } }; int ftdi_bmp_init(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info) { int err; - unsigned index = 0; - for(index = 0; index < sizeof(cable_desc)/sizeof(cable_desc[0]); - index++) - if (strcmp(cable_desc[index].name, cl_opts->opt_cable) == 0) + cable_desc_t *cable = &cable_desc[0]; + for(; cable->name; cable++) { + if (strcmp(cable->name, cl_opts->opt_cable) == 0) break; + } - if (index == sizeof(cable_desc)/sizeof(cable_desc[0])) { - DEBUG_WARN( "No cable matching %s found\n", cl_opts->opt_cable); + if (!cable->name ) { + DEBUG_WARN( "No cable matching found for %s\n", cl_opts->opt_cable); return -1; } - active_cable = &cable_desc[index]; - - DEBUG_WARN("Black Magic Probe for FTDI/MPSSE\n"); + active_cable = cable; + memcpy(&active_state, &active_cable->init, sizeof(data_desc_t)); + /* If swd_(read|write) is not given for the selected cable and + the 'r' command line argument is give, assume resistor SWD + connection.*/ + if (cl_opts->external_resistor_swd && + (active_cable->mpsse_swd_read.set_data_low == 0) && + (active_cable->mpsse_swd_read.clr_data_low == 0) && + (active_cable->mpsse_swd_read.set_data_high == 0) && + (active_cable->mpsse_swd_read.clr_data_high == 0) && + (active_cable->mpsse_swd_write.set_data_low == 0) && + (active_cable->mpsse_swd_write.clr_data_low == 0) && + (active_cable->mpsse_swd_write.set_data_high == 0) && + (active_cable->mpsse_swd_write.clr_data_high == 0)) { + DEBUG_INFO("Using external resistor SWD\n"); + active_cable->mpsse_swd_read.set_data_low = MPSSE_DO; + active_cable->mpsse_swd_write.set_data_low = MPSSE_DO; + } else if (!libftdi_swd_possible(NULL, NULL) && + !cl_opts->opt_usejtag) { + DEBUG_WARN("SWD with cable not possible, trying JTAG\n"); + cl_opts->opt_usejtag = true; + } if(ftdic) { ftdi_usb_close(ftdic); ftdi_free(ftdic); @@ -237,7 +340,58 @@ int ftdi_bmp_init(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info) err, ftdi_get_error_string(ftdic)); goto error_2; } - return 0; + assert(ftdic != NULL); + err = ftdi_usb_purge_buffers(ftdic); + if (err != 0) { + fprintf(stderr, "ftdi_usb_purge_buffer: %d: %s\n", + err, ftdi_get_error_string(ftdic)); + goto error_2; + } + /* Reset MPSSE controller. */ + err = ftdi_set_bitmode(ftdic, 0, BITMODE_RESET); + if (err != 0) { + fprintf(stderr, "ftdi_set_bitmode: %d: %s\n", + err, ftdi_get_error_string(ftdic)); + goto error_2; + } + /* Enable MPSSE controller. Pin directions are set later.*/ + err = ftdi_set_bitmode(ftdic, 0, BITMODE_MPSSE); + if (err != 0) { + fprintf(stderr, "ftdi_set_bitmode: %d: %s\n", + err, ftdi_get_error_string(ftdic)); + goto error_2; + } + uint8_t ftdi_init[16]; + /* Test for pending garbage.*/ + int garbage = ftdi_read_data(ftdic, ftdi_init, sizeof(ftdi_init)); + if (garbage > 0) { + DEBUG_WARN("FTDI init garbage at start:"); + for (int i = 0; i < garbage; i++) + DEBUG_WARN(" %02x", ftdi_init[i]); + DEBUG_WARN("\n"); + } + int index = 0; + ftdi_init[index++]= LOOPBACK_END; /* FT2232D gets upset otherwise*/ + ftdi_init[index++]= TCK_DIVISOR; + /* Use CLK/2 for about 50 % SWDCLK duty cycle on FT2232c.*/ + ftdi_init[index++]= 1; + ftdi_init[index++]= 0; + ftdi_init[index++]= SET_BITS_LOW; + ftdi_init[index++]= active_state.data_low; + ftdi_init[index++]= active_state.ddr_low; + ftdi_init[index++]= SET_BITS_HIGH; + ftdi_init[index++]= active_state.data_high; + ftdi_init[index++]= active_state.ddr_high; + libftdi_buffer_write(ftdi_init, index); + libftdi_buffer_flush(); + garbage = ftdi_read_data(ftdic, ftdi_init, sizeof(ftdi_init)); + if (garbage > 0) { + DEBUG_WARN("FTDI init garbage at end:"); + for (int i = 0; i < garbage; i++) + DEBUG_WARN(" %02x", ftdi_init[i]); + DEBUG_WARN("\n"); + } return 0; + error_2: ftdi_usb_close(ftdic); error_1: @@ -245,24 +399,106 @@ int ftdi_bmp_init(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info) return -1; } -void libftdi_srst_set_val(bool assert) +static void libftdi_set_data(data_desc_t* data) { - (void)assert; - libftdi_buffer_flush(); + uint8_t cmd[6]; + int index = 0; + if ((data->data_low) || (data->ddr_low)) { + if (data->data_low > 0) + active_state.data_low |= (data->data_low & 0xff); + else + active_state.data_low &= (data->data_low & 0xff); + if (data->ddr_low > 0) + active_state.ddr_low |= (data->ddr_low & 0xff); + else + active_state.ddr_low &= (data->ddr_low & 0xff); + cmd[index++] = SET_BITS_LOW; + cmd[index++] = active_state.data_low; + cmd[index++] = active_state.ddr_low; + } + if ((data->data_high) || (data->ddr_high)) { + if (data->data_high > 0) + active_state.data_high |= (data->data_high & 0xff); + else + active_state.data_high &= (data->data_high & 0xff); + if (data->ddr_high > 0) + active_state.ddr_high |= (data->ddr_high & 0xff); + else + active_state.ddr_high &= (data->ddr_high & 0xff); + cmd[index++] = SET_BITS_HIGH; + cmd[index++] = active_state.data_high; + cmd[index++] = active_state.ddr_high; + } + if (index) { + libftdi_buffer_write(cmd, index); + libftdi_buffer_flush(); + } } -bool libftdi_srst_get_val(void) { return false; } +void libftdi_srst_set_val(bool assert) +{ + if (assert) + libftdi_set_data(&active_cable->assert_srst); + else + libftdi_set_data(&active_cable->deassert_srst); +} + +bool libftdi_srst_get_val(void) +{ + uint8_t cmd[1] = {0}; + uint8_t pin = 0; + if (active_cable->srst_get_port_cmd && active_cable->srst_get_pin) { + cmd[0]= active_cable->srst_get_port_cmd; + pin = active_cable->srst_get_pin; + } else if (active_cable->assert_srst.data_low && + active_cable->assert_srst.ddr_low) { + cmd[0]= GET_BITS_LOW; + pin = active_cable->assert_srst.data_low; + } else if (active_cable->assert_srst.data_high && + active_cable->assert_srst.ddr_high) { + cmd[0]= GET_BITS_HIGH; + pin = active_cable->assert_srst.data_high; + }else { + return false; + } + libftdi_buffer_write(cmd, 1); + uint8_t data[1]; + libftdi_buffer_read(data, 1); + bool res = false; + if (((pin < 0x7f) || (pin == PIN7))) + res = data[0] & pin; + else + res = !(data[0] & ~pin); + return res; +} void libftdi_buffer_flush(void) { + if (!bufptr) + return; + DEBUG_WIRE("Flush %d\n", bufptr); +#if defined(USE_USB_VERSION_BIT) +static struct ftdi_transfer_control *tc_write = NULL; + if (tc_write) + ftdi_transfer_data_done(tc_write); + tc_write = ftdi_write_data_submit(ftdic, outbuf, bufptr); +#else assert(ftdi_write_data(ftdic, outbuf, bufptr) == bufptr); DEBUG_WIRE("FT2232 libftdi_buffer flush: %d bytes\n", bufptr); +#endif bufptr = 0; } int libftdi_buffer_write(const uint8_t *data, int size) { if((bufptr + size) / BUF_SIZE > 0) libftdi_buffer_flush(); + DEBUG_WIRE("Write %d bytes:", size); + for (int i = 0; i < size; i++) { + DEBUG_WIRE(" %02x", data[i]); + if (i && ((i & 0xf) == 0xf)) + DEBUG_WIRE("\n\t"); + } + DEBUG_WIRE("\n"); memcpy(outbuf + bufptr, data, size); bufptr += size; return size; @@ -270,14 +506,111 @@ int libftdi_buffer_write(const uint8_t *data, int size) int libftdi_buffer_read(uint8_t *data, int size) { - int index = 0; +#if defined(USE_USB_VERSION_BIT) + struct ftdi_transfer_control *tc; outbuf[bufptr++] = SEND_IMMEDIATE; libftdi_buffer_flush(); + tc = ftdi_read_data_submit(ftdic, data, size); + ftdi_transfer_data_done(tc); +#else + int index = 0; + const uint8_t cmd[1] = {SEND_IMMEDIATE}; + libftdi_buffer_write(cmd, 1); + libftdi_buffer_flush(); while((index += ftdi_read_data(ftdic, data + index, size-index)) != size); +#endif + DEBUG_WIRE("Read %d bytes:", size); + for (int i = 0; i < size; i++) { + DEBUG_WIRE(" %02x", data[i]); + if (i && ((i & 0xf) == 0xf)) + DEBUG_WIRE("\n\t"); + } + DEBUG_WIRE("\n"); return size; } +void libftdi_jtagtap_tdi_tdo_seq( + uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int ticks) +{ + int rsize, rticks; + + if(!ticks) return; + if (!DI && !DO) return; + +// printf("ticks: %d\n", ticks); + if(final_tms) ticks--; + rticks = ticks & 7; + ticks >>= 3; + uint8_t data[8]; + uint8_t cmd = ((DO)? MPSSE_DO_READ : 0) | + ((DI)? (MPSSE_DO_WRITE | MPSSE_WRITE_NEG) : 0) | MPSSE_LSB; + rsize = ticks; + if(ticks) { + data[0] = cmd; + data[1] = ticks - 1; + data[2] = 0; + libftdi_buffer_write(data, 3); + if (DI) + libftdi_buffer_write(DI, ticks); + } + int index = 0; + if(rticks) { + rsize++; + data[index++] = cmd | MPSSE_BITMODE; + data[index++] = rticks - 1; + if (DI) + data[index++] = DI[ticks]; + } + if(final_tms) { + rsize++; + data[index++] = MPSSE_WRITE_TMS | ((DO)? MPSSE_DO_READ : 0) | + MPSSE_LSB | MPSSE_BITMODE | MPSSE_WRITE_NEG; + data[index++] = 0; + if (DI) + data[index++] = (DI[ticks]) >> rticks?0x81 : 0x01; + } + if (index) + libftdi_buffer_write(data, index); + if (DO) { + int index = 0; + uint8_t *tmp = alloca(rsize); + libftdi_buffer_read(tmp, rsize); + if(final_tms) rsize--; + + while(rsize--) { + /*if(rsize) printf("%02X ", tmp[index]);*/ + *DO++ = tmp[index++]; + } + if (rticks == 0) + *DO++ = 0; + if(final_tms) { + rticks++; + *(--DO) >>= 1; + *DO |= tmp[index] & 0x80; + } else DO--; + if(rticks) { + *DO >>= (8-rticks); + } + /*printf("%02X\n", *DO);*/ + } +} + const char *libftdi_target_voltage(void) { - return "not supported"; + uint8_t pin = active_cable->target_voltage_pin; + if (active_cable->target_voltage_cmd && pin) { + libftdi_buffer_write(&active_cable->target_voltage_cmd, 1); + uint8_t data[1]; + libftdi_buffer_read(data, 1); + bool res = false; + if (((pin < 0x7f) || (pin == PIN7))) + res = data[0] & pin; + else + res = !(data[0] & ~pin); + if (res) + return "Present"; + else + return "Absent"; + } + return NULL; } diff --git a/src/platforms/hosted/ftdi_bmp.h b/src/platforms/hosted/ftdi_bmp.h index c44607b4..fc2a8def 100644 --- a/src/platforms/hosted/ftdi_bmp.h +++ b/src/platforms/hosted/ftdi_bmp.h @@ -3,6 +3,7 @@ * * Copyright (C) 2011 Black Sphere Technologies Ltd. * Written by Gareth McMullin + * Copyright (C) 2018 Uwe Bonnes (non@elektron.ikp.physik.tu-darmstadt.de) * * 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 @@ -25,25 +26,82 @@ #include "swdptap.h" #include "jtagtap.h" +typedef struct data_desc_s { + int16_t data_low; + int16_t ddr_low; + int16_t data_high; + int16_t ddr_high; +}data_desc_t; + +typedef struct pin_settings_s { + uint8_t set_data_low; + uint8_t clr_data_low; + uint8_t set_data_high; + uint8_t clr_data_high; +}pin_settings_t; + typedef struct cable_desc_s { int vendor; int product; int interface; - uint8_t dbus_data; - uint8_t dbus_ddr; - uint8_t cbus_data; - uint8_t cbus_ddr; - uint8_t bitbang_tms_in_port_cmd; - uint8_t bitbang_tms_in_pin; - uint8_t bitbang_swd_dbus_read_data; - /* bitbang_swd_dbus_read_data is same as dbus_data, - * as long as CBUS is not involved.*/ + /* Initial (C|D)(Bus|Ddr) values for additional pins. + * MPSSE_CS|DI|DO|SK are initialized accordig to mode.*/ + data_desc_t init; + /* MPSSE command to read TMS/SWDIO in bitbanging SWD. + * In many cases this is the TMS port, so then use "GET_PIN_LOW".*/ + uint8_t bb_swdio_in_port_cmd; + /* bus bit to read TMS/SWDIO in bitbanging SWD. + * In many cases this is the TMS port, so then use "MPSSE_CS".*/ + uint8_t bb_swdio_in_pin; + /* Bus data to allow bitbanging switched SWD read. + * TMS is routed to bb_swdio_in_port/pin.*/ + pin_settings_t bb_swd_read; + /* Bus data to allow bitbanging switched SWD write. + * TMS is routed to MPSSE_CS.*/ + pin_settings_t bb_swd_write; + /* dbus_data, dbus_ddr, cbus_data, cbus_ddr value to assert SRST. + * E.g. with CBUS Pin 1 low, + * give data_high = ~PIN1, ddr_high = PIN1 */ + data_desc_t assert_srst; + /* Bus_data, dbus_ddr, cbus_data, cbus_ddr value to release SRST. + * E.g. with CBUS Pin 1 floating with internal pull up, + * give data_high = PIN1, ddr_high = ~PIN1 */ + data_desc_t deassert_srst; + /* Command to read back SRST. If 0, port from assert_srst is used*/ + uint8_t srst_get_port_cmd; + /* PIN to read back as SRST. if 0 port from assert_srst is ised. + * Use PINX if active high, use Complement (~PINX) if active low*/ + uint8_t srst_get_pin; + /* Bbus data for pure MPSSE SWD read. + * Use together with swd_write if by some bits on DBUS, + * SWDIO can be routed to TDI and TDO. + * If both mpsse_swd_read|write and + * bitbang_swd_dbus_read_data/bitbang_tms_in_port_cmd/bitbang_tms_in_pin + * are provided, pure MPSSE SWD is choosen. + * If neither a complete set of swd_read|write or + * bitbang_swd_dbus_read_data/bitbang_tms_in_port_cmd/bitbang_tms_in_pin + * are provided, SWD can not be done. + * swd_read.set_data_low == swd_write.set_data_low == MPSSE_DO + * indicated resistor SWD and inhibits Jtag.*/ + pin_settings_t mpsse_swd_read; + /* dbus data for pure MPSSE SWD write.*/ + pin_settings_t mpsse_swd_write; + /* dbus data for jtag.*/ + pin_settings_t jtag; + /* Command to read port to check target voltage.*/ + uint8_t target_voltage_cmd; + /* Pin to check target voltage.*/ + uint8_t target_voltage_pin; + /* USB readable description of the device.*/ char *description; + /* Command line argument to -c option to select this device.*/ char * name; }cable_desc_t; +extern cable_desc_t cable_desc[]; extern cable_desc_t *active_cable; extern struct ftdi_context *ftdic; +extern data_desc_t active_state; int ftdi_bmp_init(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info); @@ -53,9 +111,20 @@ void libftdi_buffer_flush(void); int libftdi_buffer_write(const uint8_t *data, int size); int libftdi_buffer_read(uint8_t *data, int size); const char *libftdi_target_voltage(void); +void libftdi_jtagtap_tdi_tdo_seq( + uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int ticks); +bool libftdi_swd_possible(bool *do_mpsse, bool *direct_bb_swd); -#define MPSSE_TDI 2 -#define MPSSE_TDO 4 -#define MPSSE_TMS 8 - +#define MPSSE_SK 1 +#define PIN0 1 +#define MPSSE_DO 2 +#define PIN1 2 +#define MPSSE_DI 4 +#define PIN2 4 +#define MPSSE_CS 8 +#define PIN3 8 +#define PIN4 0x10 +#define PIN5 0x20 +#define PIN6 0x40 +#define PIN7 0x80 #endif diff --git a/src/platforms/hosted/libftdi_jtagtap.c b/src/platforms/hosted/libftdi_jtagtap.c index 30c8578b..7642129d 100644 --- a/src/platforms/hosted/libftdi_jtagtap.c +++ b/src/platforms/hosted/libftdi_jtagtap.c @@ -20,8 +20,6 @@ /* Low level JTAG implementation using FT2232 with libftdi. * - * Issues: - * Should share interface with swdptap.c or at least clean up... */ #include @@ -38,50 +36,59 @@ extern struct ftdi_context *ftdic; static void jtagtap_reset(void); static void jtagtap_tms_seq(uint32_t MS, int ticks); -static void jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, - const uint8_t *DI, int ticks); static void jtagtap_tdi_seq( const uint8_t final_tms, const uint8_t *DI, int ticks); static uint8_t jtagtap_next(uint8_t dTMS, uint8_t dTDI); int libftdi_jtagtap_init(jtag_proc_t *jtag_proc) { - assert(ftdic != NULL); - int err = ftdi_usb_purge_buffers(ftdic); - if (err != 0) { - DEBUG_WARN("ftdi_usb_purge_buffer: %d: %s\n", - err, ftdi_get_error_string(ftdic)); - abort(); + if ((active_cable->mpsse_swd_read.set_data_low == MPSSE_DO) && + (active_cable->mpsse_swd_write.set_data_low == MPSSE_DO)) { + printf("Jtag not possible with resistor SWD!\n"); + return -1; } - /* Reset MPSSE controller. */ - err = ftdi_set_bitmode(ftdic, 0, BITMODE_RESET); - if (err != 0) { - DEBUG_WARN("ftdi_set_bitmode: %d: %s\n", - err, ftdi_get_error_string(ftdic)); - return -1; - } - /* Enable MPSSE controller. Pin directions are set later.*/ - err = ftdi_set_bitmode(ftdic, 0, BITMODE_MPSSE); - if (err != 0) { - DEBUG_WARN("ftdi_set_bitmode: %d: %s\n", - err, ftdi_get_error_string(ftdic)); - return -1; - } - uint8_t ftdi_init[9] = {TCK_DIVISOR, 0x00, 0x00, SET_BITS_LOW, 0,0, - SET_BITS_HIGH, 0,0}; - ftdi_init[4]= active_cable->dbus_data; - ftdi_init[5]= active_cable->dbus_ddr; - ftdi_init[7]= active_cable->cbus_data; - ftdi_init[8]= active_cable->cbus_ddr; - libftdi_buffer_write(ftdi_init, 9); - libftdi_buffer_flush(); - jtag_proc->jtagtap_reset = jtagtap_reset; jtag_proc->jtagtap_next =jtagtap_next; jtag_proc->jtagtap_tms_seq = jtagtap_tms_seq; - jtag_proc->jtagtap_tdi_tdo_seq = jtagtap_tdi_tdo_seq; + jtag_proc->jtagtap_tdi_tdo_seq = libftdi_jtagtap_tdi_tdo_seq; jtag_proc->jtagtap_tdi_seq = jtagtap_tdi_seq; + active_state.data_low |= active_cable->jtag.set_data_low | + MPSSE_CS | MPSSE_DI | MPSSE_DO; + active_state.data_low &= ~(active_cable->jtag.clr_data_low | MPSSE_SK); + active_state.ddr_low |= MPSSE_CS | MPSSE_DO | MPSSE_SK; + active_state.ddr_low &= ~(MPSSE_DI); + active_state.data_high |= active_cable->jtag.set_data_high; + active_state.data_high &= ~(active_cable->jtag.clr_data_high); + uint8_t gab[16]; + int garbage = ftdi_read_data(ftdic, gab, sizeof(gab)); + if (garbage > 0) { + DEBUG_WARN("FTDI JTAG init got garbage:"); + for (int i = 0; i < garbage; i++) + DEBUG_WARN(" %02x", gab[i]); + DEBUG_WARN("\n"); + } + uint8_t cmd_write[16] = { + SET_BITS_LOW, active_state.data_low, + active_state.ddr_low, + SET_BITS_HIGH, active_state.data_high, active_state.ddr_high}; + libftdi_buffer_write(cmd_write, 6); + libftdi_buffer_flush(); + /* Write out start condition and pull garbage from read buffer. + * FT2232D otherwise misbehaves on runs follwoing the first run.*/ + garbage = ftdi_read_data(ftdic, cmd_write, sizeof(cmd_write)); + if (garbage > 0) { + DEBUG_WARN("FTDI JTAG end init got garbage:"); + for (int i = 0; i < garbage; i++) + DEBUG_WARN(" %02x", cmd_write[i]); + DEBUG_WARN("\n"); + } + /* Go to JTAG mode for SWJ-DP */ + for (int i = 0; i <= 50; i++) + jtag_proc->jtagtap_next(1, 0); /* Reset SW-DP */ + jtag_proc->jtagtap_tms_seq(0xE73C, 16); /* SWD to JTAG sequence */ + jtag_proc->jtagtap_tms_seq(0x1F, 6); + return 0; } @@ -93,7 +100,7 @@ static void jtagtap_reset(void) static void jtagtap_tms_seq(uint32_t MS, int ticks) { uint8_t tmp[3] = { - MPSSE_WRITE_TMS | MPSSE_LSB | MPSSE_BITMODE| MPSSE_READ_NEG, 0, 0}; + MPSSE_WRITE_TMS | MPSSE_LSB | MPSSE_BITMODE | MPSSE_WRITE_NEG, 0, 0}; while(ticks >= 0) { tmp[1] = ticks<7?ticks-1:6; tmp[2] = 0x80 | (MS & 0x7F); @@ -103,77 +110,10 @@ static void jtagtap_tms_seq(uint32_t MS, int ticks) } } -static void jtagtap_tdi_tdo_seq( - uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int ticks) -{ - int rsize, rticks; - - if(!ticks) return; - if (!DI && !DO) return; - -// DEBUG_PROBE("ticks: %d\n", ticks); - if(final_tms) ticks--; - rticks = ticks & 7; - ticks >>= 3; - uint8_t data[3]; - uint8_t cmd = ((DO)? MPSSE_DO_READ : 0) | - ((DI)? (MPSSE_DO_WRITE | MPSSE_WRITE_NEG) : 0) | MPSSE_LSB; - rsize = ticks; - if(ticks) { - data[0] = cmd; - data[1] = ticks - 1; - data[2] = 0; - libftdi_buffer_write(data, 3); - if (DI) - libftdi_buffer_write(DI, ticks); - } - if(rticks) { - int index = 0; - rsize++; - data[index++] = cmd | MPSSE_BITMODE; - data[index++] = rticks - 1; - if (DI) - data[index++] = DI[ticks]; - libftdi_buffer_write(data, index); - } - if(final_tms) { - int index = 0; - rsize++; - data[index++] = MPSSE_WRITE_TMS | ((DO)? MPSSE_DO_READ : 0) | - MPSSE_LSB | MPSSE_BITMODE | MPSSE_WRITE_NEG; - data[index++] = 0; - if (DI) - data[index++] = (DI[ticks]) >> rticks?0x81 : 0x01; - libftdi_buffer_write(data, index); - } - if (DO) { - int index = 0; - uint8_t *tmp = alloca(ticks); - libftdi_buffer_read(tmp, rsize); - if(final_tms) rsize--; - - while(rsize--) { - if(rsize) DEBUG_WIRE("%02X ", tmp[index]); - *DO++ = tmp[index++]; - } - if (rticks == 0) - *DO++ = 0; - if(final_tms) { - rticks++; - *(--DO) >>= 1; - *DO |= tmp[index] & 0x80; - } else DO--; - if(rticks) { - *DO >>= (8-rticks); - } - DEBUG_WIRE("%02X\n", *DO); - } -} - static void jtagtap_tdi_seq( const uint8_t final_tms, const uint8_t *DI, int ticks) { - return jtagtap_tdi_tdo_seq(NULL, final_tms, DI, ticks); + return libftdi_jtagtap_tdi_tdo_seq(NULL, final_tms, DI, ticks); } static uint8_t jtagtap_next(uint8_t dTMS, uint8_t dTDI) @@ -187,7 +127,5 @@ static uint8_t jtagtap_next(uint8_t dTMS, uint8_t dTDI) ret &= 0x80; -// DEBUG("jtagtap_next(TMS = %d, TDI = %d) = %02X\n", dTMS, dTDI, ret); - return ret; } diff --git a/src/platforms/hosted/libftdi_swdptap.c b/src/platforms/hosted/libftdi_swdptap.c index 91e54284..575b7138 100644 --- a/src/platforms/hosted/libftdi_swdptap.c +++ b/src/platforms/hosted/libftdi_swdptap.c @@ -28,52 +28,183 @@ #include "general.h" #include "ftdi_bmp.h" -static uint8_t olddir = 0; +enum swdio_status{ + SWDIO_STATUS_DRIVE = 0, + SWDIO_STATUS_FLOAT, +}; -#define MPSSE_MASK (MPSSE_TDI | MPSSE_TDO | MPSSE_TMS) -#define MPSSE_TD_MASK (MPSSE_TDI | MPSSE_TDO) +static enum swdio_status olddir; +static bool do_mpsse; +static bool direct_bb_swd; + +#define MPSSE_MASK (MPSSE_DO | MPSSE_DI | MPSSE_CS) +#define MPSSE_TD_MASK (MPSSE_DO | MPSSE_DI) #define MPSSE_TMS_SHIFT (MPSSE_WRITE_TMS | MPSSE_LSB |\ MPSSE_BITMODE | MPSSE_WRITE_NEG) +#define MPSSE_TDO_SHIFT (MPSSE_DO_WRITE | MPSSE_LSB |\ + MPSSE_BITMODE | MPSSE_WRITE_NEG) +static void swdptap_turnaround(enum swdio_status dir) +{ + if (dir == olddir) + return; + olddir = dir; + DEBUG_PROBE("Turnaround %s\n", (dir == SWDIO_STATUS_FLOAT) ? "float": "drive"); + if (do_mpsse) { + if (dir == SWDIO_STATUS_FLOAT) /* SWDIO goes to input */ { + active_state.data_low |= active_cable->mpsse_swd_read.set_data_low | MPSSE_DO; + active_state.data_low &= ~active_cable->mpsse_swd_read.clr_data_low; + active_state.ddr_low &= ~MPSSE_DO; + active_state.data_high |= active_cable->mpsse_swd_read.set_data_high; + active_state.data_high &= ~active_cable->mpsse_swd_read.clr_data_high; + uint8_t cmd_read[6] = { + SET_BITS_LOW, active_state.data_low, + active_state.ddr_low, + SET_BITS_HIGH, active_state.data_high, active_state.ddr_high}; + libftdi_buffer_write(cmd_read, 6); + } + uint8_t cmd[] = {MPSSE_TDO_SHIFT, 0, 0}; /* One clock cycle */ + libftdi_buffer_write(cmd, sizeof(cmd)); + if (dir == SWDIO_STATUS_DRIVE) /* SWDIO goes to output */ { + active_state.data_low |= active_cable->mpsse_swd_write.set_data_low | MPSSE_DO; + active_state.data_low &= ~active_cable->mpsse_swd_write.clr_data_low; + active_state.ddr_low |= MPSSE_DO; + active_state.data_high |= active_cable->mpsse_swd_write.set_data_high; + active_state.data_high &= ~active_cable->mpsse_swd_write.clr_data_high; + uint8_t cmd_write[6] = { + SET_BITS_LOW, active_state.data_low, + active_state.ddr_low, + SET_BITS_HIGH, active_state.data_high, active_state.ddr_high}; + libftdi_buffer_write(cmd_write, 6); + } + } else { + uint8_t cmd[9]; + int index = 0; + + if(dir == SWDIO_STATUS_FLOAT) { /* SWDIO goes to input */ + if (direct_bb_swd) { + active_state.data_low |= MPSSE_CS; + active_state.ddr_low &= ~MPSSE_CS; + } else { + active_state.data_low |= active_cable->bb_swd_read.set_data_low; + active_state.data_low &= ~active_cable->bb_swd_read.clr_data_low; + active_state.data_high |= active_cable->bb_swd_read.set_data_high; + active_state.data_high &= ~active_cable->bb_swd_read.clr_data_high; + } + cmd[index++] = SET_BITS_LOW; + cmd[index++] = active_state.data_low; + cmd[index++] = active_state.ddr_low; + cmd[index++] = SET_BITS_HIGH; + cmd[index++] = active_state.data_high; + cmd[index++] = active_state.ddr_high; + } + /* One clock cycle */ + cmd[index++] = MPSSE_TMS_SHIFT; + cmd[index++] = 0; + cmd[index++] = 0; + if (dir == SWDIO_STATUS_DRIVE) { + if (direct_bb_swd) { + active_state.data_low |= MPSSE_CS; + active_state.ddr_low |= MPSSE_CS; + } else { + active_state.data_low |= active_cable->bb_swd_write.set_data_low; + active_state.data_low &= ~active_cable->bb_swd_write.clr_data_low; + active_state.data_high |= active_cable->bb_swd_write.set_data_high; + active_state.data_high &= ~active_cable->bb_swd_write.clr_data_high; + } + cmd[index++] = SET_BITS_LOW; + cmd[index++] = active_state.data_low; + cmd[index++] = active_state.ddr_low; + cmd[index++] = SET_BITS_HIGH; + cmd[index++] = active_state.data_high; + cmd[index++] = active_state.ddr_high; + } + libftdi_buffer_write(cmd, index); + } +} static bool swdptap_seq_in_parity(uint32_t *res, int ticks); static uint32_t swdptap_seq_in(int ticks); static void swdptap_seq_out(uint32_t MS, int ticks); static void swdptap_seq_out_parity(uint32_t MS, int ticks); +bool libftdi_swd_possible(bool *do_mpsse, bool *direct_bb_swd) +{ + bool swd_read = + active_cable->mpsse_swd_read.set_data_low || + active_cable->mpsse_swd_read.clr_data_low || + active_cable->mpsse_swd_read.set_data_high || + active_cable->mpsse_swd_read.clr_data_high; + bool swd_write = + active_cable->mpsse_swd_write.set_data_low || + active_cable->mpsse_swd_write.clr_data_low || + active_cable->mpsse_swd_write.set_data_high || + active_cable->mpsse_swd_write.clr_data_high; + bool mpsse = swd_read && swd_write; + if (do_mpsse) + *do_mpsse = mpsse; + if (!mpsse) { + bool bb_swd_read = + active_cable->bb_swd_read.set_data_low || + active_cable->bb_swd_read.clr_data_low || + active_cable->bb_swd_read.set_data_high || + active_cable->bb_swd_read.clr_data_high; + bool bb_swd_write = + active_cable->bb_swd_write.set_data_low || + active_cable->bb_swd_write.clr_data_low || + active_cable->bb_swd_write.set_data_high || + active_cable->bb_swd_write.clr_data_high; + bool bb_direct_possible = + active_cable->bb_swdio_in_port_cmd == GET_BITS_LOW && + active_cable->bb_swdio_in_pin == MPSSE_CS; + if (!bb_swd_read && !bb_swd_write) { + if (!bb_direct_possible) + return false; + } + if (direct_bb_swd) + *direct_bb_swd = true; + } + return true; +} + int libftdi_swdptap_init(swd_proc_t *swd_proc) { - if (!active_cable->bitbang_tms_in_pin) { + if (!libftdi_swd_possible(&do_mpsse, &direct_bb_swd)) { DEBUG_WARN("SWD not possible or missing item in cable description.\n"); return -1; } - int err = ftdi_usb_purge_buffers(ftdic); - if (err != 0) { - DEBUG_WARN("ftdi_usb_purge_buffer: %d: %s\n", - err, ftdi_get_error_string(ftdic)); - return -1; + active_state.data_low |= MPSSE_CS | MPSSE_DI | MPSSE_DO; + active_state.data_low &= MPSSE_SK; + active_state.ddr_low |= MPSSE_SK; + active_state.ddr_low &= ~(MPSSE_CS | MPSSE_DI | MPSSE_DO); + if (do_mpsse) { + DEBUG_INFO("Using genuine MPSSE for SWD.\n"); + active_state.data_low |= active_cable->mpsse_swd_read.set_data_low; + active_state.data_low &= ~(active_cable->mpsse_swd_read.clr_data_low); + active_state.data_high |= active_cable->mpsse_swd_read.set_data_high; + active_state.data_high &= ~(active_cable->mpsse_swd_read.clr_data_high); + } else if (direct_bb_swd) { + DEBUG_INFO("Using direct bitbang with SWDIO %cBUS%d.\n", + (active_cable->bb_swdio_in_port_cmd == GET_BITS_LOW) ? 'C' : 'D', + __builtin_ctz(active_cable->bb_swdio_in_pin)); + } else { + DEBUG_INFO("Using switched bitbang for SWD.\n"); + active_state.data_low |= active_cable->bb_swd_read.set_data_low; + active_state.data_low &= ~(active_cable->bb_swd_read.clr_data_low); + active_state.data_high |= active_cable->bb_swd_read.set_data_high; + active_state.data_high &= ~(active_cable->bb_swd_read.clr_data_high); + active_state.ddr_low |= MPSSE_CS; + if (active_cable->bb_swdio_in_port_cmd == GET_BITS_LOW) + active_state.ddr_low &= ~active_cable->bb_swdio_in_pin; + else if (active_cable->bb_swdio_in_port_cmd == GET_BITS_HIGH) + active_state.ddr_high &= ~active_cable->bb_swdio_in_pin; } - /* Reset MPSSE controller. */ - err = ftdi_set_bitmode(ftdic, 0, BITMODE_RESET); - if (err != 0) { - DEBUG_WARN("ftdi_set_bitmode: %d: %s\n", - err, ftdi_get_error_string(ftdic)); - return -1; - } - /* Enable MPSSE controller. Pin directions are set later.*/ - err = ftdi_set_bitmode(ftdic, 0, BITMODE_MPSSE); - if (err != 0) { - DEBUG_WARN("ftdi_set_bitmode: %d: %s\n", - err, ftdi_get_error_string(ftdic)); - return -1; - } - uint8_t ftdi_init[9] = {TCK_DIVISOR, 0x01, 0x00, SET_BITS_LOW, 0,0, - SET_BITS_HIGH, 0,0}; - ftdi_init[4]= active_cable->dbus_data | MPSSE_MASK; - ftdi_init[5]= active_cable->dbus_ddr & ~MPSSE_TD_MASK; - ftdi_init[7]= active_cable->cbus_data; - ftdi_init[8]= active_cable->cbus_ddr; - libftdi_buffer_write(ftdi_init, 9); + uint8_t cmd_write[6] = { + SET_BITS_LOW, active_state.data_low, + active_state.ddr_low, + SET_BITS_HIGH, active_state.data_high, active_state.ddr_high}; + libftdi_buffer_write(cmd_write, 6); libftdi_buffer_flush(); + olddir = SWDIO_STATUS_FLOAT; swd_proc->swdptap_seq_in = swdptap_seq_in; swd_proc->swdptap_seq_in_parity = swdptap_seq_in_parity; @@ -83,136 +214,185 @@ int libftdi_swdptap_init(swd_proc_t *swd_proc) return 0; } -static void swdptap_turnaround(uint8_t dir) +bool swdptap_bit_in(void) { - if (dir == olddir) - return; - olddir = dir; - uint8_t cmd[6]; + swdptap_turnaround(SWDIO_STATUS_FLOAT); + uint8_t cmd[4]; int index = 0; + bool result = false; - if(dir) { /* SWDIO goes to input */ - cmd[index++] = SET_BITS_LOW; - if (active_cable->bitbang_swd_dbus_read_data) - cmd[index] = active_cable->bitbang_swd_dbus_read_data; - else - cmd[index] = active_cable->dbus_data; - index++; - cmd[index++] = active_cable->dbus_ddr & ~MPSSE_MASK; + if (do_mpsse) { + uint8_t cmd[2] = {MPSSE_DO_READ | MPSSE_LSB | MPSSE_BITMODE, 0}; + libftdi_buffer_write(cmd, sizeof(cmd)); + uint8_t data[1]; + libftdi_buffer_read(data, sizeof(data)); + result = (data[0] & 0x80); + } else { + cmd[index++] = active_cable->bb_swdio_in_port_cmd; + cmd[index++] = MPSSE_TMS_SHIFT; + cmd[index++] = 0; + cmd[index++] = 0; + libftdi_buffer_write(cmd, index); + uint8_t data[1]; + libftdi_buffer_read(data, sizeof(data)); + result = (data[0] &= active_cable->bb_swdio_in_pin); } - /* One clock cycle */ - cmd[index++] = MPSSE_TMS_SHIFT; - cmd[index++] = 0; - cmd[index++] = 0; - if (!dir) { - cmd[index++] = SET_BITS_LOW; - cmd[index++] = active_cable->dbus_data | MPSSE_MASK; - cmd[index++] = active_cable->dbus_ddr & ~MPSSE_TD_MASK; - } - libftdi_buffer_write(cmd, index); + return result; } -static bool swdptap_seq_in_parity(uint32_t *res, int ticks) +void swdptap_bit_out(bool val) { - int index = ticks + 1; - uint8_t cmd[4]; - unsigned int parity = 0; - - cmd[0] = active_cable->bitbang_tms_in_port_cmd; - cmd[1] = MPSSE_TMS_SHIFT; - cmd[2] = 0; - cmd[3] = 0; - swdptap_turnaround(1); - while (index--) { - libftdi_buffer_write(cmd, 4); + swdptap_turnaround(SWDIO_STATUS_DRIVE); + if (do_mpsse) { + uint8_t cmd[3] = {MPSSE_TDO_SHIFT, 0, (val)? 1:0}; + libftdi_buffer_write(cmd, sizeof(cmd)); + } else { + uint8_t cmd[3]; + cmd[0] = MPSSE_TMS_SHIFT; + cmd[1] = 0; + cmd[2] = (val)? 1 : 0; + libftdi_buffer_write(cmd, sizeof(cmd)); } - uint8_t data[33]; - unsigned int ret = 0; - libftdi_buffer_read(data, ticks + 1); - if (data[ticks] & active_cable->bitbang_tms_in_pin) - parity ^= 1; - while (ticks--) { - if (data[ticks] & active_cable->bitbang_tms_in_pin) { +} + +bool swdptap_seq_in_parity(uint32_t *res, int ticks) +{ + assert(ticks == 32); + swdptap_turnaround(SWDIO_STATUS_FLOAT); + unsigned int parity = 0; + unsigned int result = 0; + if (do_mpsse) { + uint8_t DO[5]; + libftdi_jtagtap_tdi_tdo_seq(DO, 0, NULL, ticks + 1); + result = DO[0] + (DO[1] << 8) + (DO[2] << 16) + (DO[3] << 24); + parity = __builtin_parity(result & ((1LL << ticks) - 1)) & 1; + parity ^= DO[4] & 1; + } else { + int index = ticks + 1; + uint8_t cmd[4]; + + cmd[0] = active_cable->bb_swdio_in_port_cmd; + cmd[1] = MPSSE_TMS_SHIFT; + cmd[2] = 0; + cmd[3] = 0; + while (index--) { + libftdi_buffer_write(cmd, sizeof(cmd)); + } + uint8_t data[33]; + libftdi_buffer_read(data, ticks + 1); + if (data[ticks] & active_cable->bb_swdio_in_pin) parity ^= 1; - ret |= (1 << ticks); + index = ticks; + while (index--) { + if (data[index] & active_cable->bb_swdio_in_pin) { + parity ^= 1; + result |= (1 << index); + } } } - *res = ret; + *res = result; return parity; } static uint32_t swdptap_seq_in(int ticks) { - int index = ticks; - uint8_t cmd[4]; + if (!ticks) + return 0; + uint32_t result = 0; + swdptap_turnaround(SWDIO_STATUS_FLOAT); + if (do_mpsse) { + uint8_t DO[4]; + libftdi_jtagtap_tdi_tdo_seq(DO, 0, NULL, ticks); + for (int i = 0; i < (ticks >> 3) + (ticks & 7)? 1: 0; i++) { + result |= DO[i] << (8 * i); + } + } else { + int index = ticks; + uint8_t cmd[4]; - cmd[0] = active_cable->bitbang_tms_in_port_cmd; - cmd[1] = MPSSE_TMS_SHIFT; - cmd[2] = 0; - cmd[3] = 0; + cmd[0] = active_cable->bb_swdio_in_port_cmd; + cmd[1] = MPSSE_TMS_SHIFT; + cmd[2] = 0; + cmd[3] = 0; - swdptap_turnaround(1); - while (index--) { - libftdi_buffer_write(cmd, 4); + while (index--) { + libftdi_buffer_write(cmd, sizeof(cmd)); + } + uint8_t data[32]; + libftdi_buffer_read(data, ticks); + index = ticks; + while (index--) { + if (data[index] & active_cable->bb_swdio_in_pin) + result |= (1 << index); + } } - uint8_t data[32]; - uint32_t ret = 0; - libftdi_buffer_read(data, ticks); - while (ticks--) { - if (data[ticks] & active_cable->bitbang_tms_in_pin) - ret |= (1 << ticks); - } - return ret; + return result; } static void swdptap_seq_out(uint32_t MS, int ticks) { - uint8_t cmd[15]; - unsigned int index = 0; - swdptap_turnaround(0); - while (ticks) { - cmd[index++] = MPSSE_TMS_SHIFT; - if (ticks >= 7) { - cmd[index++] = 6; - cmd[index++] = MS & 0x7f; - MS >>= 7; - ticks -= 7; - } else { - cmd[index++] = ticks - 1; - cmd[index++] = MS & 0x7f; - ticks = 0; + if (!ticks) + return; + swdptap_turnaround(SWDIO_STATUS_DRIVE); + if (do_mpsse) { + uint8_t DI[4]; + DI[0] = (MS >> 0) & 0xff; + DI[1] = (MS >> 8) & 0xff; + DI[2] = (MS >> 16) & 0xff; + DI[3] = (MS >> 24) & 0xff; + libftdi_jtagtap_tdi_tdo_seq(NULL, 0, DI, ticks); + } else { + uint8_t cmd[16]; + unsigned int index = 0; + while (ticks) { + cmd[index++] = MPSSE_TMS_SHIFT; + if (ticks >= 7) { + cmd[index++] = 6; + cmd[index++] = MS & 0x7f; + MS >>= 7; + ticks -= 7; + } else { + cmd[index++] = ticks - 1; + cmd[index++] = MS & 0x7f; + ticks = 0; + } } + libftdi_buffer_write(cmd, index); } - libftdi_buffer_write(cmd, index); } static void swdptap_seq_out_parity(uint32_t MS, int ticks) { - uint8_t parity = 0; - int steps = ticks; - uint8_t cmd[18]; + int parity = __builtin_parity(MS & ((1LL << ticks) - 1)) & 1; unsigned int index = 0; - uint32_t data = MS; - swdptap_turnaround(0); - while (steps) { - cmd[index++] = MPSSE_TMS_SHIFT; - if (steps >= 7) { - cmd[index++] = 6; - cmd[index++] = data & 0x7f; - data >>= 7; - steps -= 7; - } else { - cmd[index++] = steps - 1; - cmd[index++] = data & 0x7f; - steps = 0; + swdptap_turnaround(SWDIO_STATUS_DRIVE); + if (do_mpsse) { + uint8_t DI[8]; + DI[0] = (MS >> 0) & 0xff; + DI[1] = (MS >> 8) & 0xff; + DI[2] = (MS >> 16) & 0xff; + DI[3] = (MS >> 24) & 0xff; + DI[4] = parity; + libftdi_jtagtap_tdi_tdo_seq(NULL, 0, DI, ticks + 1); + } else { + uint8_t cmd[32]; + int steps = ticks; + while (steps) { + cmd[index++] = MPSSE_TMS_SHIFT; + if (steps >= 7) { + cmd[index++] = 6; + cmd[index++] = MS & 0x7f; + MS >>= 7; + steps -= 7; + } else { + cmd[index++] = steps - 1; + cmd[index++] = MS & 0x7f; + steps = 0; + } } + cmd[index++] = MPSSE_TMS_SHIFT; + cmd[index++] = 0; + cmd[index++] = parity; + libftdi_buffer_write(cmd, index); } - while (ticks--) { - parity ^= MS; - MS >>= 1; - } - cmd[index++] = MPSSE_TMS_SHIFT; - cmd[index++] = 0; - cmd[index++] = parity; - libftdi_buffer_write(cmd, index); } diff --git a/src/platforms/hosted/platform.c b/src/platforms/hosted/platform.c index 631fa49c..1bb738a6 100644 --- a/src/platforms/hosted/platform.c +++ b/src/platforms/hosted/platform.c @@ -37,9 +37,6 @@ #include "jlink.h" #include "cmsis_dap.h" -#define VENDOR_ID_BMP 0x1d50 -#define PRODUCT_ID_BMP 0x6018 - #define VENDOR_ID_STLINK 0x0483 #define PRODUCT_ID_STLINK_MASK 0xffe0 #define PRODUCT_ID_STLINK_GROUP 0x3740 @@ -101,6 +98,8 @@ static int find_debuggers( BMP_CL_OPTIONS_t *cl_opts,bmp_info_t *info) char product[128]; bmp_type_t type = BMP_TYPE_NONE; bool access_problems = false; + char *active_cable = NULL; + bool ftdi_unknown = false; rescan: found_debuggers = 0; for (int i = 0; devs[i]; i++) { @@ -165,6 +164,8 @@ static int find_debuggers( BMP_CL_OPTIONS_t *cl_opts,bmp_info_t *info) if ((desc.idVendor == VENDOR_ID_BMP) && (desc.idProduct == PRODUCT_ID_BMP)) { type = BMP_TYPE_BMP; + } else if ((strstr(manufacturer, "CMSIS")) || (strstr(product, "CMSIS"))) { + type = BMP_TYPE_CMSIS_DAP; } else if (desc.idVendor == VENDOR_ID_STLINK) { if ((desc.idProduct == PRODUCT_ID_STLINKV2) || (desc.idProduct == PRODUCT_ID_STLINKV21) || @@ -177,16 +178,45 @@ static int find_debuggers( BMP_CL_OPTIONS_t *cl_opts,bmp_info_t *info) DEBUG_WARN( "INFO: STLINKV1 not supported\n"); continue; } - } else if ((strstr(manufacturer, "CMSIS")) || (strstr(product, "CMSIS"))) { - type = BMP_TYPE_CMSIS_DAP; } else if (desc.idVendor == VENDOR_ID_SEGGER) { type = BMP_TYPE_JLINK; - } else{ - continue; + } else { + cable_desc_t *cable = &cable_desc[0]; + for (; cable->name; cable++) { + bool found = false; + if ((cable->vendor != desc.idVendor) || (cable->product != desc.idProduct)) + continue; /* VID/PID do not match*/ + if (cl_opts->opt_cable) { + if (strcmp(cable->name, cl_opts->opt_cable)) + continue; /* cable names do not match*/ + else + found = true; + } + if (cable->description) { + if (strcmp(cable->description, product)) + continue; /* discriptions do not match*/ + else + found = true; + } else { /* VID/PID fits, but no cl_opts->opt_cable and no description*/ + if ((cable->vendor == 0x0403) && /* FTDI*/ + ((cable->product == 0x6010) || /* FT2232C/D/H*/ + (cable->product == 0x6011) || /* FT4232H Quad HS USB-UART/FIFO IC */ + (cable->product == 0x6014))) { /* FT232H Single HS USB-UART/FIFO IC */ + ftdi_unknown = true; + continue; /* Cable name is needed */ + } + } + if (found) { + active_cable = cable->name; + type = BMP_TYPE_LIBFTDI; + break; + } + } + if (!cable->name) + continue; } - found_debuggers ++; if (report) { - DEBUG_WARN("%2d: %s, %s, %s\n", found_debuggers, + DEBUG_WARN("%2d: %s, %s, %s\n", found_debuggers + 1, serial, manufacturer,product); } @@ -197,11 +227,19 @@ static int find_debuggers( BMP_CL_OPTIONS_t *cl_opts,bmp_info_t *info) strncpy(info->product, product, sizeof(info->product)); strncpy(info->manufacturer, manufacturer, sizeof(info->manufacturer)); if (cl_opts->opt_position && - (cl_opts->opt_position == found_debuggers)) { + (cl_opts->opt_position == (found_debuggers + 1))) { found_debuggers = 1; break; + } else { + found_debuggers++; } } + if ((found_debuggers == 0) && ftdi_unknown) + DEBUG_WARN("Generic FTDI MPSSE VID/PID found. Please specify exact type with \"-c \" !\n"); + if ((found_debuggers == 1) && !cl_opts->opt_cable && (type == BMP_TYPE_LIBFTDI)) + cl_opts->opt_cable = active_cable; + if (!found_debuggers && cl_opts->opt_list_only) + DEBUG_WARN("No usable debugger found\n"); if ((found_debuggers > 1) || ((found_debuggers == 1) && (cl_opts->opt_list_only))) { if (!report) { @@ -242,12 +280,16 @@ void platform_init(int argc, char **argv) if (cl_opts.opt_device) { info.bmp_type = BMP_TYPE_BMP; } else if (cl_opts.opt_cable) { - /* check for libftdi devices*/ - res = ftdi_bmp_init(&cl_opts, &info); - if (res) - exit(-1); - else - info.bmp_type = BMP_TYPE_LIBFTDI; + if ((!strcmp(cl_opts.opt_cable, "list")) || + (!strcmp(cl_opts.opt_cable, "l"))) { + cable_desc_t *cable = &cable_desc[0]; + DEBUG_WARN("Available cables:\n"); + for (; cable->name; cable++) { + DEBUG_WARN("\t%s\n", cable->name); + } + exit(0); + } + info.bmp_type = BMP_TYPE_LIBFTDI; } else if (find_debuggers(&cl_opts, &info)) { exit(-1); } @@ -269,6 +311,8 @@ void platform_init(int argc, char **argv) exit(-1); break; case BMP_TYPE_LIBFTDI: + if (ftdi_bmp_init(&cl_opts, &info)) + exit(-1); break; case BMP_TYPE_JLINK: if (jlink_init(&info)) @@ -490,33 +534,51 @@ static void ap_decode_access(uint16_t addr, uint8_t RnW) fprintf(stderr, "Read "); else fprintf(stderr, "Write "); - switch(addr) { - case 0x00: - if (RnW) - fprintf(stderr, "DP_DPIDR :"); - else - fprintf(stderr, "DP_ABORT :"); - break; - case 0x004: fprintf(stderr, "CTRL/STAT:"); - break; - case 0x008: - if (RnW) - fprintf(stderr, "RESEND :"); - else - fprintf(stderr, "DP_SELECT:"); - break; - case 0x00c: fprintf(stderr, "DP_RDBUFF:"); - break; - case 0x100: fprintf(stderr, "AP_CSW :"); - break; - case 0x104: fprintf(stderr, "AP_TAR :"); - break; - case 0x10c: fprintf(stderr, "AP_DRW :"); - break; - case 0x1f8: fprintf(stderr, "AP_BASE :"); - break; - case 0x1fc: fprintf(stderr, "AP_IDR :"); - break; + if (addr < 0x100) { + switch(addr) { + case 0x00: + if (RnW) + fprintf(stderr, "DP_DPIDR :"); + else + fprintf(stderr, "DP_ABORT :"); + break; + case 0x04: fprintf(stderr, "CTRL/STAT:"); + break; + case 0x08: + if (RnW) + fprintf(stderr, "RESEND :"); + else + fprintf(stderr, "DP_SELECT:"); + break; + case 0x0c: fprintf(stderr, "DP_RDBUFF:"); + break; + default: fprintf(stderr, "Unknown %02x :", addr); + } + } else { + fprintf(stderr, "AP 0x%02x ", addr >> 8); + switch (addr & 0xff) { + case 0x00: fprintf(stderr, "CSW :"); + break; + case 0x04: fprintf(stderr, "TAR :"); + break; + case 0x0c: fprintf(stderr, "DRW :"); + break; + case 0x10: fprintf(stderr, "DB0 :"); + break; + case 0x14: fprintf(stderr, "DB1 :"); + break; + case 0x18: fprintf(stderr, "DB2 :"); + break; + case 0x1c: fprintf(stderr, "DB3 :"); + break; + case 0xf8: fprintf(stderr, "BASE :"); + break; + case 0xf4: fprintf(stderr, "CFG :"); + break; + case 0xfc: fprintf(stderr, "IDR :"); + break; + default: fprintf(stderr, "RSVD%02x:", addr & 0xff); + } } } diff --git a/src/platforms/hosted/platform.h b/src/platforms/hosted/platform.h index b0e50432..e36d7b96 100644 --- a/src/platforms/hosted/platform.h +++ b/src/platforms/hosted/platform.h @@ -14,6 +14,9 @@ void platform_buffer_flush(void); #define SET_IDLE_STATE(x) #define SET_RUN_STATE(x) +#define VENDOR_ID_BMP 0x1d50 +#define PRODUCT_ID_BMP 0x6018 + typedef enum bmp_type_s { BMP_TYPE_NONE = 0, BMP_TYPE_BMP, diff --git a/src/platforms/hosted/stlinkv2.c b/src/platforms/hosted/stlinkv2.c index 110bbc63..9a645d6f 100644 --- a/src/platforms/hosted/stlinkv2.c +++ b/src/platforms/hosted/stlinkv2.c @@ -311,6 +311,7 @@ static int stlink_usb_error_check(uint8_t *data, bool verbose) case STLINK_SWD_AP_STICKY_ERROR: if (verbose) DEBUG_WARN("STLINK_SWD_AP_STICKY_ERROR\n"); + Stlink.ap_error = true; return STLINK_ERROR_FAIL; case STLINK_SWD_AP_STICKYORUN_ERROR: if (verbose) @@ -686,11 +687,10 @@ static int stlink_enter_debug_jtag(bmp_info_t *info) static uint32_t stlink_read_coreid(void) { - uint8_t cmd[16] = {STLINK_DEBUG_COMMAND, - STLINK_DEBUG_READCOREID}; - uint8_t data[4]; - send_recv(info.usb_link, cmd, 16, data, 4); - uint32_t id = data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24; + uint8_t cmd[16] = {STLINK_DEBUG_COMMAND, STLINK_DEBUG_APIV2_READ_IDCODES}; + uint8_t data[12]; + send_recv(info.usb_link, cmd, 16, data, 12); + uint32_t id = data[4] | data[5] << 8 | data[6] << 16 | data[7] << 24; DEBUG_INFO("Read Core ID: 0x%08" PRIx32 "\n", id); return id; } @@ -897,7 +897,8 @@ static void stlink_readmem(ADIv5_AP_t *ap, void *dest, uint32_t src, size_t len) * Approach taken: * Fill the memory with some fixed pattern so hopefully * the caller notices the error*/ - DEBUG_WARN("stlink_readmem failed\n"); + DEBUG_WARN("stlink_readmem from %" PRIx32 " to %" PRIx32 ", len %" + PRIx32 "failed\n", src, dest, (uint32_t) len); memset(dest, 0xff, len); } DEBUG_PROBE("stlink_readmem from %" PRIx32 " to %" PRIx32 ", len %" PRIx32 @@ -1027,10 +1028,6 @@ static uint32_t stlink_ap_read(ADIv5_AP_t *ap, uint16_t addr) return ret; } -struct jtag_dev_s jtag_devs[JTAG_MAX_DEVS+1]; -int jtag_dev_count; -jtag_proc_t jtag_proc; - int jtag_scan_stlinkv2(bmp_info_t *info, const uint8_t *irlens) { uint32_t idcodes[JTAG_MAX_DEVS+1]; @@ -1094,7 +1091,7 @@ int stlink_enter_debug_swd(bmp_info_t *info, ADIv5_DP_t *dp) STLINK_DEBUG_ENTER_SWD_NO_RESET}; uint8_t data[2]; DEBUG_INFO("Enter SWD\n"); - send_recv(info->usb_link, cmd, 16, data, 2); + stlink_send_recv_retry(cmd, 16, data, 2); if (stlink_usb_error_check(data, true)) return -1; dp->idcode = stlink_read_coreid(); diff --git a/src/platforms/pc/cl_utils.c b/src/platforms/pc/cl_utils.c index 38d3c729..1ff350e7 100644 --- a/src/platforms/pc/cl_utils.c +++ b/src/platforms/pc/cl_utils.c @@ -129,12 +129,15 @@ static void cl_help(char **argv, BMP_CL_OPTIONS_t *opt) DEBUG_WARN("\t-s \"serial\"\t: Use dongle with (partial) " "serial number \"serial\"\n"); DEBUG_WARN("\t-c \"string\"\t: Use ftdi dongle with type \"string\"\n"); + DEBUG_WARN("\t\t Use \"list\" to list available cables\n"); DEBUG_WARN("Run mode related options:\n"); DEBUG_WARN("\tDefault mode is to start the debug server at :2000\n"); DEBUG_WARN("\t-j\t\t: Use JTAG. SWD is default.\n"); DEBUG_WARN("\t-C\t\t: Connect under reset\n"); DEBUG_WARN("\t-t\t\t: Scan SWD or JTAG and display information about \n" "\t\t\t connected devices\n"); + DEBUG_WARN("\t-e\t\t: Assume \"resistor SWD connection\" on FTDI: TDI\n" + "\t\t\t connected to TMS, TDO to TDI with eventual resistor\n"); DEBUG_WARN("\t-E\t\t: Erase flash until flash end or for given size\n"); DEBUG_WARN("\t-V\t\t: Verify flash against binary file\n"); DEBUG_WARN("\t-r\t\t: Read flash and write to binary file\n"); @@ -154,7 +157,7 @@ void cl_init(BMP_CL_OPTIONS_t *opt, int argc, char **argv) int c; opt->opt_target_dev = 1; opt->opt_flash_size = 16 * 1024 *1024; - while((c = getopt(argc, argv, "Ehv:d:s:I:c:CnltVta:S:jpP:rR")) != -1) { + while((c = getopt(argc, argv, "eEhv:d:s:I:c:CnltVta:S:jpP:rR")) != -1) { switch(c) { case 'c': if (optarg) @@ -177,6 +180,9 @@ void cl_init(BMP_CL_OPTIONS_t *opt, int argc, char **argv) case 'C': opt->opt_connect_under_reset = true; break; + case 'e': + opt->external_resistor_swd = true; + break; case 'd': if (optarg) opt->opt_device = optarg; diff --git a/src/platforms/pc/cl_utils.h b/src/platforms/pc/cl_utils.h index 3fedb7ec..e2bd4356 100644 --- a/src/platforms/pc/cl_utils.h +++ b/src/platforms/pc/cl_utils.h @@ -42,6 +42,7 @@ typedef struct BMP_CL_OPTIONS_s { bool opt_tpwr; bool opt_list_only; bool opt_connect_under_reset; + bool external_resistor_swd; char *opt_flash_file; char *opt_device; char *opt_serial; diff --git a/src/platforms/pc/hid.c b/src/platforms/pc/hid.c new file mode 100755 index 00000000..3d0425c5 --- /dev/null +++ b/src/platforms/pc/hid.c @@ -0,0 +1,1026 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + https://github.com/libusb/hidapi . +********************************************************/ + +#include + +#ifndef _NTDEF_ +typedef LONG NTSTATUS; +#endif + +#ifdef __MINGW32__ +#include +#include +#endif + +#ifdef __CYGWIN__ +#include +#define _wcsdup wcsdup +#endif + +/* The maximum number of characters that can be passed into the + HidD_Get*String() functions without it failing.*/ +#define MAX_STRING_WCHARS 0xFFF + +/*#define HIDAPI_USE_DDK*/ + +#ifdef __cplusplus +extern "C" { +#endif + #include + #include + #ifdef HIDAPI_USE_DDK + #include + #endif + + /* Copied from inc/ddk/hidclass.h, part of the Windows DDK. */ + #define HID_OUT_CTL_CODE(id) \ + CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS) + #define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100) + #define IOCTL_HID_GET_INPUT_REPORT HID_OUT_CTL_CODE(104) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include +#include + + +#include "hidapi.h" + +#undef MIN +#define MIN(x,y) ((x) < (y)? (x): (y)) + +#ifdef _MSC_VER + /* Thanks Microsoft, but I know how to use strncpy(). */ + #pragma warning(disable:4996) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HIDAPI_USE_DDK + /* Since we're not building with the DDK, and the HID header + files aren't part of the SDK, we have to define all this + stuff here. In lookup_functions(), the function pointers + defined below are set. */ + typedef struct _HIDD_ATTRIBUTES{ + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; + } HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; + + typedef USHORT USAGE; + typedef struct _HIDP_CAPS { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT fields_not_used_by_hidapi[10]; + } HIDP_CAPS, *PHIDP_CAPS; + typedef void* PHIDP_PREPARSED_DATA; + #define HIDP_STATUS_SUCCESS 0x110000 + + typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib); + typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length); + typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length); + typedef BOOLEAN (__stdcall *HidD_GetInputReport_)(HANDLE handle, PVOID data, ULONG length); + typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data); + typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data); + typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps); + typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers); + + static HidD_GetAttributes_ HidD_GetAttributes; + static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; + static HidD_GetManufacturerString_ HidD_GetManufacturerString; + static HidD_GetProductString_ HidD_GetProductString; + static HidD_SetFeature_ HidD_SetFeature; + static HidD_GetFeature_ HidD_GetFeature; + static HidD_GetInputReport_ HidD_GetInputReport; + static HidD_GetIndexedString_ HidD_GetIndexedString; + static HidD_GetPreparsedData_ HidD_GetPreparsedData; + static HidD_FreePreparsedData_ HidD_FreePreparsedData; + static HidP_GetCaps_ HidP_GetCaps; + static HidD_SetNumInputBuffers_ HidD_SetNumInputBuffers; + + static HMODULE lib_handle = NULL; + static BOOLEAN initialized = FALSE; +#endif /* HIDAPI_USE_DDK */ + +struct hid_device_ { + HANDLE device_handle; + BOOL blocking; + USHORT output_report_length; + size_t input_report_length; + void *last_error_str; + DWORD last_error_num; + BOOL read_pending; + char *read_buf; + OVERLAPPED ol; +}; + +static hid_device *new_hid_device() +{ + hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); + dev->device_handle = INVALID_HANDLE_VALUE; + dev->blocking = TRUE; + dev->output_report_length = 0; + dev->input_report_length = 0; + dev->last_error_str = NULL; + dev->last_error_num = 0; + dev->read_pending = FALSE; + dev->read_buf = NULL; + memset(&dev->ol, 0, sizeof(dev->ol)); + dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL); + + return dev; +} + +static void free_hid_device(hid_device *dev) +{ + CloseHandle(dev->ol.hEvent); + CloseHandle(dev->device_handle); + LocalFree(dev->last_error_str); + free(dev->read_buf); + free(dev); +} + +static void register_error(hid_device *dev, const char *op) +{ + WCHAR *ptr, *msg; + (void) op; + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPVOID)&msg, 0/*sz*/, + NULL); + + /* Get rid of the CR and LF that FormatMessage() sticks at the + end of the message. Thanks Microsoft! */ + ptr = msg; + while (*ptr) { + if (*ptr == '\r') { + *ptr = 0x0000; + break; + } + ptr++; + } + + /* Store the message off in the Device entry so that + the hid_error() function can pick it up. */ + LocalFree(dev->last_error_str); + dev->last_error_str = msg; +} + +#ifndef HIDAPI_USE_DDK +static int lookup_functions() +{ + lib_handle = LoadLibraryA("hid.dll"); + if (lib_handle) { +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-function-type" +#endif +#define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1; + RESOLVE(HidD_GetAttributes); + RESOLVE(HidD_GetSerialNumberString); + RESOLVE(HidD_GetManufacturerString); + RESOLVE(HidD_GetProductString); + RESOLVE(HidD_SetFeature); + RESOLVE(HidD_GetFeature); + RESOLVE(HidD_GetInputReport); + RESOLVE(HidD_GetIndexedString); + RESOLVE(HidD_GetPreparsedData); + RESOLVE(HidD_FreePreparsedData); + RESOLVE(HidP_GetCaps); + RESOLVE(HidD_SetNumInputBuffers); +#undef RESOLVE +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + } + else + return -1; + + return 0; +} +#endif + +static HANDLE open_device(const char *path, BOOL open_rw) +{ + HANDLE handle; + DWORD desired_access = (open_rw)? (GENERIC_WRITE | GENERIC_READ): 0; + DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE; + + handle = CreateFileA(path, + desired_access, + share_mode, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/ + 0); + + return handle; +} + +int HID_API_EXPORT hid_init(void) +{ +#ifndef HIDAPI_USE_DDK + if (!initialized) { + if (lookup_functions() < 0) { + hid_exit(); + return -1; + } + initialized = TRUE; + } +#endif + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ +#ifndef HIDAPI_USE_DDK + if (lib_handle) + FreeLibrary(lib_handle); + lib_handle = NULL; + initialized = FALSE; +#endif + return 0; +} + +struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + BOOL res; + struct hid_device_info *root = NULL; /* return object */ + struct hid_device_info *cur_dev = NULL; + + /* Windows objects for interacting with the driver. */ + GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} }; + SP_DEVINFO_DATA devinfo_data; + SP_DEVICE_INTERFACE_DATA device_interface_data; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL; + HDEVINFO device_info_set = INVALID_HANDLE_VALUE; + int device_index = 0; + int i; + + if (hid_init() < 0) + return NULL; + + /* Initialize the Windows objects. */ + memset(&devinfo_data, 0x0, sizeof(devinfo_data)); + devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); + device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + /* Get information for all the devices belonging to the HID class. */ + device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + /* Iterate over each device in the HID class, looking for the right one. */ + + for (;;) { + HANDLE write_handle = INVALID_HANDLE_VALUE; + DWORD required_size = 0; + HIDD_ATTRIBUTES attrib; + + res = SetupDiEnumDeviceInterfaces(device_info_set, + NULL, + &InterfaceClassGuid, + device_index, + &device_interface_data); + + if (!res) { + /* A return of FALSE from this function means that + there are no more devices. */ + break; + } + + /* Call with 0-sized detail size, and let the function + tell us how long the detail struct needs to be. The + size is put in &required_size. */ + res = SetupDiGetDeviceInterfaceDetailA(device_info_set, + &device_interface_data, + NULL, + 0, + &required_size, + NULL); + + /* Allocate a long enough structure for device_interface_detail_data. */ + device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size); + device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + + /* Get the detailed data for this device. The detail data gives us + the device path for this device, which is then passed into + CreateFile() to get a handle to the device. */ + res = SetupDiGetDeviceInterfaceDetailA(device_info_set, + &device_interface_data, + device_interface_detail_data, + required_size, + NULL, + NULL); + + if (!res) { + /* register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail"); + Continue to the next device. */ + goto cont; + } + + /* Make sure this device is of Setup Class "HIDClass" and has a + driver bound to it. */ + for (i = 0; ; i++) { + char driver_name[256]; + + /* Populate devinfo_data. This function will return failure + when there are no more interfaces left. */ + res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data); + if (!res) + goto cont; + + res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, + SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); + if (!res) + goto cont; + + if ((strcmp(driver_name, "HIDClass") == 0) || + (strcmp(driver_name, "Mouse") == 0) || + (strcmp(driver_name, "Keyboard") == 0)) { + /* See if there's a driver bound. */ + res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, + SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); + if (res) + break; + } + } + + //wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath); + + /* Open a handle to the device */ + write_handle = open_device(device_interface_detail_data->DevicePath, FALSE); + + /* Check validity of write_handle. */ + if (write_handle == INVALID_HANDLE_VALUE) { + /* Unable to open the device. */ + //register_error(dev, "CreateFile"); + goto cont_close; + } + + + /* Get the Vendor ID and Product ID for this device. */ + attrib.Size = sizeof(HIDD_ATTRIBUTES); + HidD_GetAttributes(write_handle, &attrib); + //wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID); + + /* Check the VID/PID to see if we should add this + device to the enumeration list. */ + if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) && + (product_id == 0x0 || attrib.ProductID == product_id)) { + + #define WSTR_LEN 512 + const char *str; + struct hid_device_info *tmp; + PHIDP_PREPARSED_DATA pp_data = NULL; + HIDP_CAPS caps; + BOOLEAN res; + NTSTATUS nt_res; + wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */ + size_t len; + + /* VID/PID match. Create the record. */ + tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + /* Get the Usage Page and Usage for this device. */ + res = HidD_GetPreparsedData(write_handle, &pp_data); + if (res) { + nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res == HIDP_STATUS_SUCCESS) { + cur_dev->usage_page = caps.UsagePage; + cur_dev->usage = caps.Usage; + } + + HidD_FreePreparsedData(pp_data); + } + + /* Fill out the record */ + cur_dev->next = NULL; + str = device_interface_detail_data->DevicePath; + if (str) { + len = strlen(str); + cur_dev->path = (char*) calloc(len+1, sizeof(char)); + strncpy(cur_dev->path, str, len+1); + cur_dev->path[len] = '\0'; + } + else + cur_dev->path = NULL; + + /* Serial Number */ + wstr[0]= 0x0000; + res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->serial_number = _wcsdup(wstr); + } + + /* Manufacturer String */ + wstr[0]= 0x0000; + res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->manufacturer_string = _wcsdup(wstr); + } + + /* Product String */ + wstr[0]= 0x0000; + res = HidD_GetProductString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->product_string = _wcsdup(wstr); + } + + /* VID/PID */ + cur_dev->vendor_id = attrib.VendorID; + cur_dev->product_id = attrib.ProductID; + + /* Release Number */ + cur_dev->release_number = attrib.VersionNumber; + + /* Interface Number. It can sometimes be parsed out of the path + on Windows if a device has multiple interfaces. See + http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or + search for "Hardware IDs for HID Devices" at MSDN. If it's not + in the path, it's set to -1. */ + cur_dev->interface_number = -1; + if (cur_dev->path) { + char *interface_component = strstr(cur_dev->path, "&mi_"); + if (interface_component) { + char *hex_str = interface_component + 4; + char *endptr = NULL; + cur_dev->interface_number = strtol(hex_str, &endptr, 16); + if (endptr == hex_str) { + /* The parsing failed. Set interface_number to -1. */ + cur_dev->interface_number = -1; + } + } + } + } + +cont_close: + CloseHandle(write_handle); +cont: + /* We no longer need the detail data. It can be freed */ + free(device_interface_detail_data); + + device_index++; + + } + + /* Close the device information handle. */ + SetupDiDestroyDeviceInfoList(device_info_set); + + return root; + +} + +void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs) +{ + /* TODO: Merge this with the Linux version. This function is platform-independent. */ + struct hid_device_info *d = devs; + while (d) { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + + +HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) +{ + /* TODO: Merge this functions with the Linux version. This function should be platform independent. */ + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) { + if (cur_dev->vendor_id == vendor_id && + cur_dev->product_id == product_id) { + if (serial_number) { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) { + path_to_open = cur_dev->path; + break; + } + } + else { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) +{ + hid_device *dev; + HIDP_CAPS caps; + PHIDP_PREPARSED_DATA pp_data = NULL; + BOOLEAN res; + NTSTATUS nt_res; + + if (hid_init() < 0) { + return NULL; + } + + dev = new_hid_device(); + + /* Open a handle to the device */ + dev->device_handle = open_device(path, TRUE); + + /* Check validity of write_handle. */ + if (dev->device_handle == INVALID_HANDLE_VALUE) { + /* System devices, such as keyboards and mice, cannot be opened in + read-write mode, because the system takes exclusive control over + them. This is to prevent keyloggers. However, feature reports + can still be sent and received. Retry opening the device, but + without read/write access. */ + dev->device_handle = open_device(path, FALSE); + + /* Check the validity of the limited device_handle. */ + if (dev->device_handle == INVALID_HANDLE_VALUE) { + /* Unable to open the device, even without read-write mode. */ + register_error(dev, "CreateFile"); + goto err; + } + } + + /* Set the Input Report buffer size to 64 reports. */ + res = HidD_SetNumInputBuffers(dev->device_handle, 64); + if (!res) { + register_error(dev, "HidD_SetNumInputBuffers"); + goto err; + } + + /* Get the Input Report length for the device. */ + res = HidD_GetPreparsedData(dev->device_handle, &pp_data); + if (!res) { + register_error(dev, "HidD_GetPreparsedData"); + goto err; + } + nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res != HIDP_STATUS_SUCCESS) { + register_error(dev, "HidP_GetCaps"); + goto err_pp_data; + } + dev->output_report_length = caps.OutputReportByteLength; + dev->input_report_length = caps.InputReportByteLength; + HidD_FreePreparsedData(pp_data); + + dev->read_buf = (char*) malloc(dev->input_report_length); + + return dev; + +err_pp_data: + HidD_FreePreparsedData(pp_data); +err: + free_hid_device(dev); + return NULL; +} + +int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + DWORD bytes_written; + BOOL res; + + OVERLAPPED ol; + unsigned char *buf; + memset(&ol, 0, sizeof(ol)); + + /* Make sure the right number of bytes are passed to WriteFile. Windows + expects the number of bytes which are in the _longest_ report (plus + one for the report number) bytes even if the data is a report + which is shorter than that. Windows gives us this value in + caps.OutputReportByteLength. If a user passes in fewer bytes than this, + create a temporary buffer which is the proper size. */ + if (length >= dev->output_report_length) { + /* The user passed the right number of bytes. Use the buffer as-is. */ + buf = (unsigned char *) data; + } else { + /* Create a temporary buffer and copy the user's data + into it, padding the rest with zeros. */ + buf = (unsigned char *) malloc(dev->output_report_length); + memcpy(buf, data, length); + memset(buf + length, 0, dev->output_report_length - length); + length = dev->output_report_length; + } + + res = WriteFile(dev->device_handle, buf, length, NULL, &ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + /* WriteFile() failed. Return error. */ + register_error(dev, "WriteFile"); + bytes_written = -1; + goto end_of_function; + } + } + + /* Wait here until the write is done. This makes + hid_write() synchronous. */ + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/); + if (!res) { + /* The Write operation failed. */ + register_error(dev, "WriteFile"); + bytes_written = -1; + goto end_of_function; + } + +end_of_function: + if (buf != data) + free(buf); + + return bytes_written; +} + + +int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + DWORD bytes_read = 0; + size_t copy_len = 0; + BOOL res; + + /* Copy the handle for convenience. */ + HANDLE ev = dev->ol.hEvent; + + if (!dev->read_pending) { + /* Start an Overlapped I/O read. */ + dev->read_pending = TRUE; + memset(dev->read_buf, 0, dev->input_report_length); + ResetEvent(ev); + res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + /* ReadFile() has failed. + Clean up and return error. */ + CancelIo(dev->device_handle); + dev->read_pending = FALSE; + goto end_of_function; + } + } + } + + if (milliseconds >= 0) { + /* See if there is any data yet. */ + res = WaitForSingleObject(ev, milliseconds); + if (res != WAIT_OBJECT_0) { + /* There was no data this time. Return zero bytes available, + but leave the Overlapped I/O running. */ + return 0; + } + } + + /* Either WaitForSingleObject() told us that ReadFile has completed, or + we are in non-blocking mode. Get the number of bytes read. The actual + data has been copied to the data[] array which was passed to ReadFile(). */ + res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/); + + /* Set pending back to false, even if GetOverlappedResult() returned error. */ + dev->read_pending = FALSE; + + if (res && bytes_read > 0) { + if (dev->read_buf[0] == 0x0) { + /* If report numbers aren't being used, but Windows sticks a report + number (0x0) on the beginning of the report anyway. To make this + work like the other platforms, and to make it work more like the + HID spec, we'll skip over this byte. */ + bytes_read--; + copy_len = length > bytes_read ? bytes_read : length; + memcpy(data, dev->read_buf+1, copy_len); + } + else { + /* Copy the whole buffer, report number and all. */ + copy_len = length > bytes_read ? bytes_read : length; + memcpy(data, dev->read_buf, copy_len); + } + } + +end_of_function: + if (!res) { + register_error(dev, "GetOverlappedResult"); + return -1; + } + + return copy_len; +} + +int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); +} + +int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock) +{ + dev->blocking = !nonblock; + return 0; /* Success */ +} + +int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, length); + if (!res) { + register_error(dev, "HidD_SetFeature"); + return -1; + } + + return length; +} + + +int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + BOOL res; +#if 0 + res = HidD_GetFeature(dev->device_handle, data, length); + if (!res) { + register_error(dev, "HidD_GetFeature"); + return -1; + } + return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */ +#else + DWORD bytes_returned; + + OVERLAPPED ol; + memset(&ol, 0, sizeof(ol)); + + res = DeviceIoControl(dev->device_handle, + IOCTL_HID_GET_FEATURE, + data, length, + data, length, + &bytes_returned, &ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + /* DeviceIoControl() failed. Return error. */ + register_error(dev, "Send Feature Report DeviceIoControl"); + return -1; + } + } + + /* Wait here until the write is done. This makes + hid_get_feature_report() synchronous. */ + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/); + if (!res) { + /* The operation failed. */ + register_error(dev, "Send Feature Report GetOverLappedResult"); + return -1; + } + + /* bytes_returned does not include the first byte which contains the + report ID. The data buffer actually contains one more byte than + bytes_returned. */ + bytes_returned++; + + return bytes_returned; +#endif +} + + +int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length) +{ + BOOL res; +#if 0 + res = HidD_GetInputReport(dev->device_handle, data, length); + if (!res) { + register_error(dev, "HidD_GetInputReport"); + return -1; + } + return length; +#else + DWORD bytes_returned; + + OVERLAPPED ol; + memset(&ol, 0, sizeof(ol)); + + res = DeviceIoControl(dev->device_handle, + IOCTL_HID_GET_INPUT_REPORT, + data, length, + data, length, + &bytes_returned, &ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + /* DeviceIoControl() failed. Return error. */ + register_error(dev, "Send Input Report DeviceIoControl"); + return -1; + } + } + + /* Wait here until the write is done. This makes + hid_get_feature_report() synchronous. */ + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/); + if (!res) { + /* The operation failed. */ + register_error(dev, "Send Input Report GetOverLappedResult"); + return -1; + } + + /* bytes_returned does not include the first byte which contains the + report ID. The data buffer actually contains one more byte than + bytes_returned. */ + bytes_returned++; + + return bytes_returned; +#endif +} + +void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) +{ + if (!dev) + return; + CancelIo(dev->device_handle); + free_hid_device(dev); +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetManufacturerString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetProductString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetSerialNumberString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetIndexedString"); + return -1; + } + + return 0; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + if (dev) { + if (dev->last_error_str == NULL) + return L"Success"; + return (wchar_t*)dev->last_error_str; + } + + // Global error messages are not (yet) implemented on Windows. + return L"hid_error for global errors is not implemented yet"; +} + + +/*#define PICPGM*/ +/*#define S11*/ +#define P32 +#ifdef S11 + unsigned short VendorID = 0xa0a0; + unsigned short ProductID = 0x0001; +#endif + +#ifdef P32 + unsigned short VendorID = 0x04d8; + unsigned short ProductID = 0x3f; +#endif + + +#ifdef PICPGM + unsigned short VendorID = 0x04d8; + unsigned short ProductID = 0x0033; +#endif + + +#if 0 +int __cdecl main(int argc, char* argv[]) +{ + int res; + unsigned char buf[65]; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + /* Set up the command buffer. */ + memset(buf,0x00,sizeof(buf)); + buf[0] = 0; + buf[1] = 0x81; + + + /* Open the device. */ + int handle = open(VendorID, ProductID, L"12345"); + if (handle < 0) + printf("unable to open device\n"); + + + /* Toggle LED (cmd 0x80) */ + buf[1] = 0x80; + res = write(handle, buf, 65); + if (res < 0) + printf("Unable to write()\n"); + + /* Request state (cmd 0x81) */ + buf[1] = 0x81; + write(handle, buf, 65); + if (res < 0) + printf("Unable to write() (2)\n"); + + /* Read requested state */ + read(handle, buf, 65); + if (res < 0) + printf("Unable to read()\n"); + + /* Print out the returned buffer. */ + for (int i = 0; i < 4; i++) + printf("buf[%d]: %d\n", i, buf[i]); + + return 0; +} +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/src/platforms/pc/hidapi.h b/src/platforms/pc/hidapi.h new file mode 100644 index 00000000..ce8892e2 --- /dev/null +++ b/src/platforms/pc/hidapi.h @@ -0,0 +1,445 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + https://github.com/libusb/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + */ + +#ifndef HIDAPI_H__ +#define HIDAPI_H__ + +#include + +#ifdef _WIN32 + #define HID_API_EXPORT __declspec(dllexport) + #define HID_API_CALL +#else + #define HID_API_EXPORT /**< API export macro */ + #define HID_API_CALL /**< API call macro */ +#endif + +#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ + +#ifdef __cplusplus +extern "C" { +#endif + struct hid_device_; + typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ + + /** hidapi info structure */ + struct hid_device_info { + /** Platform-specific device path */ + char *path; + /** Device Vendor ID */ + unsigned short vendor_id; + /** Device Product ID */ + unsigned short product_id; + /** Serial Number */ + wchar_t *serial_number; + /** Device Release Number in binary-coded decimal, + also known as Device Version Number */ + unsigned short release_number; + /** Manufacturer String */ + wchar_t *manufacturer_string; + /** Product string */ + wchar_t *product_string; + /** Usage Page for this Device/Interface + (Windows/Mac only). */ + unsigned short usage_page; + /** Usage for this Device/Interface + (Windows/Mac only).*/ + unsigned short usage; + /** The USB interface which this logical device + represents. + + * Valid on both Linux implementations in all cases. + * Valid on the Windows implementation only if the device + contains more than one interface. + * Valid on the Mac implementation if and only if the device + is a USB HID device. */ + int interface_number; + + /** Pointer to the next device */ + struct hid_device_info *next; + }; + + + /** @brief Initialize the HIDAPI library. + + This function initializes the HIDAPI library. Calling it is not + strictly necessary, as it will be called automatically by + hid_enumerate() and any of the hid_open_*() functions if it is + needed. This function should be called at the beginning of + execution however, if there is a chance of HIDAPI handles + being opened by different threads simultaneously. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_init(void); + + /** @brief Finalize the HIDAPI library. + + This function frees all of the static data associated with + HIDAPI. It should be called at the end of execution to avoid + memory leaks. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_exit(void); + + /** @brief Enumerate the HID Devices. + + This function returns a linked list of all the HID devices + attached to the system which match vendor_id and product_id. + If @p vendor_id is set to 0 then any vendor matches. + If @p product_id is set to 0 then any product matches. + If @p vendor_id and @p product_id are both set to 0, then + all HID devices will be returned. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the types of device + to open. + @param product_id The Product ID (PID) of the types of + device to open. + + @returns + This function returns a pointer to a linked list of type + struct #hid_device_info, containing information about the HID devices + attached to the system, or NULL in the case of failure. Free + this linked list by calling hid_free_enumeration(). + */ + struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); + + /** @brief Free an enumeration Linked List + + This function frees a linked list created by hid_enumerate(). + + @ingroup API + @param devs Pointer to a list of struct_device returned from + hid_enumerate(). + */ + void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); + + /** @brief Open a HID device using a Vendor ID (VID), Product ID + (PID) and optionally a serial number. + + If @p serial_number is NULL, the first device with the + specified VID and PID is opened. + + This function sets the return value of hid_error(). + + @ingroup API + @param vendor_id The Vendor ID (VID) of the device to open. + @param product_id The Product ID (PID) of the device to open. + @param serial_number The Serial Number of the device to open + (Optionally NULL). + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); + + /** @brief Open a HID device by its path name. + + The path name be determined by calling hid_enumerate(), or a + platform-specific path name can be used (eg: /dev/hidraw0 on + Linux). + + This function sets the return value of hid_error(). + + @ingroup API + @param path The path name of the device to open + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); + + /** @brief Write an Output report to a HID device. + + The first byte of @p data[] must contain the Report ID. For + devices which only support a single report, this must be set + to 0x0. The remaining bytes contain the report data. Since + the Report ID is mandatory, calls to hid_write() will always + contain one more byte than the report contains. For example, + if a hid report is 16 bytes long, 17 bytes must be passed to + hid_write(), the Report ID (or 0x0, for devices with a + single report), followed by the report data (16 bytes). In + this example, the length passed in would be 17. + + hid_write() will send the data on the first OUT endpoint, if + one exists. If it does not, it will send the data through + the Control Endpoint (Endpoint 0). + + This function sets the return value of hid_error(). + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length); + + /** @brief Read an Input report from a HID device with timeout. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + This function sets the return value of hid_error(). + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + @param milliseconds timeout in milliseconds or -1 for blocking wait. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read within + the timeout period, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); + + /** @brief Read an Input report from a HID device. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + This function sets the return value of hid_error(). + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read and + the handle is in non-blocking mode, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length); + + /** @brief Set the device handle to be non-blocking. + + In non-blocking mode calls to hid_read() will return + immediately with a value of 0 if there is no data to be + read. In blocking mode, hid_read() will wait (block) until + there is data to read before returning. + + Nonblocking can be turned on and off at any time. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param nonblock enable or not the nonblocking reads + - 1 to enable nonblocking + - 0 to disable nonblocking. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock); + + /** @brief Send a Feature report to the device. + + Feature reports are sent over the Control endpoint as a + Set_Report transfer. The first byte of @p data[] must + contain the Report ID. For devices which only support a + single report, this must be set to 0x0. The remaining bytes + contain the report data. Since the Report ID is mandatory, + calls to hid_send_feature_report() will always contain one + more byte than the report contains. For example, if a hid + report is 16 bytes long, 17 bytes must be passed to + hid_send_feature_report(): the Report ID (or 0x0, for + devices which do not use numbered reports), followed by the + report data (16 bytes). In this example, the length passed + in would be 17. + + This function sets the return value of hid_error(). + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send, including + the report number. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length); + + /** @brief Get a feature report from a HID device. + + Set the first byte of @p data[] to the Report ID of the + report to be read. Make sure to allow space for this + extra byte in @p data[]. Upon return, the first byte will + still contain the Report ID, and the report data will + start in data[1]. + + This function sets the return value of hid_error(). + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into, including + the Report ID. Set the first byte of @p data[] to the + Report ID of the report to be read, or set it to zero + if your device does not use numbered reports. + @param length The number of bytes to read, including an + extra byte for the report ID. The buffer can be longer + than the actual report. + + @returns + This function returns the number of bytes read plus + one for the report ID (which is still in the first + byte), or -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length); + + /** @brief Get a input report from a HID device. + + Set the first byte of @p data[] to the Report ID of the + report to be read. Make sure to allow space for this + extra byte in @p data[]. Upon return, the first byte will + still contain the Report ID, and the report data will + start in data[1]. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into, including + the Report ID. Set the first byte of @p data[] to the + Report ID of the report to be read, or set it to zero + if your device does not use numbered reports. + @param length The number of bytes to read, including an + extra byte for the report ID. The buffer can be longer + than the actual report. + + @returns + This function returns the number of bytes read plus + one for the report ID (which is still in the first + byte), or -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length); + + /** @brief Close a HID device. + + This function sets the return value of hid_error(). + + @ingroup API + @param dev A device handle returned from hid_open(). + */ + void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev); + + /** @brief Get The Manufacturer String from a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen); + + /** @brief Get The Product String from a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen); + + /** @brief Get The Serial Number String from a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen); + + /** @brief Get a string from a HID device, based on its string index. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string_index The index of the string to get. + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen); + + /** @brief Get a string describing the last error which occurred. + + Whether a function sets the last error is noted in its + documentation. These functions will reset the last error + to NULL before their execution. + + Strings returned from hid_error() must not be freed by the user! + + This function is thread-safe, and error messages are thread-local. + + @ingroup API + @param dev A device handle returned from hid_open(), + or NULL to get the last non-device-specific error + (e.g. for errors in hid_open() itself). + + @returns + This function returns a string containing the last error + which occurred or NULL if none has occurred. + */ + HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/platforms/pc/serial_unix.c b/src/platforms/pc/serial_unix.c index 063ac850..aa7d30d3 100644 --- a/src/platforms/pc/serial_unix.c +++ b/src/platforms/pc/serial_unix.c @@ -67,6 +67,33 @@ static int set_interface_attribs(void) } return 0; } + +#ifdef __APPLE__ +int serial_open(BMP_CL_OPTIONS_t *cl_opts, char *serial) +{ + char name[4096]; + if (!cl_opts->opt_device) { + /* Try to find some BMP if0*/ + if (!serial) { + DEBUG_WARN("No serial device found\n"); + return -1; + } else { + sprintf(name, "/dev/tty.usbmodem%s1", serial); + } + } else { + strncpy(name, cl_opts->opt_device, sizeof(name) - 1); + } + fd = open(name, O_RDWR | O_SYNC | O_NOCTTY); + if (fd < 0) { + DEBUG_WARN("Couldn't open serial port %s\n", name); + return -1; + } + /* BMP only offers an USB-Serial connection with no real serial + * line in between. No need for baudrate or parity.! + */ + return set_interface_attribs(); +} +#else #define BMP_IDSTRING "usb-Black_Sphere_Technologies_Black_Magic_Probe" #define DEVICE_BY_ID "/dev/serial/by-id/" int serial_open(BMP_CL_OPTIONS_t *cl_opts, char *serial) @@ -130,6 +157,7 @@ int serial_open(BMP_CL_OPTIONS_t *cl_opts, char *serial) */ return set_interface_attribs(); } +#endif void serial_close(void) { diff --git a/src/platforms/pc/serial_win.c b/src/platforms/pc/serial_win.c index 2424d46d..42904281 100644 --- a/src/platforms/pc/serial_win.c +++ b/src/platforms/pc/serial_win.c @@ -23,22 +23,64 @@ #include "remote.h" #include "cl_utils.h" -HANDLE hComm; +static HANDLE hComm; + +static char *find_bmp_by_serial(const char *serial) +{ + char regpath[258]; + /* First find the containers of the BMP comports */ + sprintf(regpath, + "SYSTEM\\CurrentControlSet\\Enum\\USB\\VID_%04X&PID_%04X\\%s", + VENDOR_ID_BMP, PRODUCT_ID_BMP, serial); + HKEY hkeySection; + LSTATUS res; + res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &hkeySection); + if (res != ERROR_SUCCESS) + return NULL; + BYTE prefix[128]; + DWORD maxlen = sizeof(prefix); + res = RegQueryValueEx(hkeySection, "ParentIdPrefix", NULL, NULL, prefix, + &maxlen); + RegCloseKey(hkeySection); + if (res != ERROR_SUCCESS) + return NULL; + printf("prefix %s\n", prefix); + sprintf(regpath, + "SYSTEM\\CurrentControlSet\\Enum\\USB\\VID_%04X&PID_%04X&MI_00\\%s" + "&0000\\Device Parameters", + VENDOR_ID_BMP, PRODUCT_ID_BMP, prefix); + printf("%s\n", regpath); + res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &hkeySection); + if (res != ERROR_SUCCESS) { + printf("Failuere\n"); + return NULL; + } + BYTE port[128]; + maxlen = sizeof(port); + res = RegQueryValueEx(hkeySection, "PortName", NULL, NULL, port, &maxlen); + RegCloseKey(hkeySection); + if (res != ERROR_SUCCESS) + return NULL; + printf("Portname %s\n", port); + return strdup((char*)port); +} int serial_open(BMP_CL_OPTIONS_t *cl_opts, char * serial) { (void) serial; /* FIXME: Does Windows allow open with USB serial no? */ + char device[256]; + if (!cl_opts->opt_device) + cl_opts->opt_device = find_bmp_by_serial(serial); if (!cl_opts->opt_device) { - DEBUG_WARN("Specify the serial device to use!\n"); + DEBUG_WARN("Unexpected problems finding the device!\n"); return -1; } - char device[256]; - if (strstr(device, "\\\\.\\")) { - strncpy(device, cl_opts->opt_device, sizeof(cl_opts->opt_device) - 1); + if (strstr(cl_opts->opt_device, "\\\\.\\")) { + strncpy(device, cl_opts->opt_device, sizeof(device) - 1); } else { strcpy(device, "\\\\.\\"); strncat(device, cl_opts->opt_device, - sizeof(cl_opts->opt_device) - strlen(cl_opts->opt_device) - 1); + sizeof(device) - strlen(device) - 1); } hComm = CreateFile(device, //port name GENERIC_READ | GENERIC_WRITE, //Read/Write @@ -59,6 +101,7 @@ int serial_open(BMP_CL_OPTIONS_t *cl_opts, char * serial) return -1; } dcbSerialParams.ByteSize = 8; + dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE; if (!SetCommState(hComm, &dcbSerialParams)) { DEBUG_WARN("SetCommState failed %ld\n", GetLastError()); return -1; diff --git a/src/platforms/stlink/platform.c b/src/platforms/stlink/platform.c index 64216e90..54b80461 100644 --- a/src/platforms/stlink/platform.c +++ b/src/platforms/stlink/platform.c @@ -34,8 +34,6 @@ #include #include -uint8_t running_status; - uint16_t led_idle_run; uint16_t srst_pin; static uint32_t rev; diff --git a/src/remote.c b/src/remote.c index 89887ebf..83418920 100644 --- a/src/remote.c +++ b/src/remote.c @@ -309,6 +309,9 @@ void remotePacketProcessHL(uint8_t i, char *packet) remote_ap.apsel = remotehston(2, packet); remote_ap.dp = &remote_dp; switch (index) { + case REMOTE_HL_CHECK: + _respond(REMOTE_RESP_OK, 0); + break; case REMOTE_DP_READ: packet += 2; uint16_t addr16 = remotehston(4, packet); diff --git a/src/remote.h b/src/remote.h index dfb8a6d8..550f7ebe 100644 --- a/src/remote.h +++ b/src/remote.h @@ -84,6 +84,7 @@ #define REMOTE_RESP_NOTSUP 'N' /* High level protocol elements */ +#define REMOTE_HL_CHECK 'C' #define REMOTE_HL_PACKET 'H' #define REMOTE_DP_READ 'd' #define REMOTE_LOW_ACCESS 'L' @@ -142,6 +143,7 @@ #define HEX_U32(x) '%', '0', '8', 'x' #define CHR(x) '%', 'c' +#define REMOTE_HL_CHECK_STR (char []){ REMOTE_SOM, REMOTE_HL_PACKET, REMOTE_HL_CHECK, REMOTE_EOM, 0 } #define REMOTE_MEM_READ_STR (char []){ REMOTE_SOM, REMOTE_HL_PACKET, REMOTE_MEM_READ, \ HEX_U32(address), HEX_U32(count), REMOTE_EOM, 0 } #define REMOTE_DP_READ_STR (char []){ REMOTE_SOM, REMOTE_HL_PACKET, REMOTE_DP_READ, \ diff --git a/src/target/adiv5.c b/src/target/adiv5.c index 63e3c75d..638c1e54 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -106,6 +106,13 @@ static const char * const cidc_debug_strings[] = #define PIDR_PN_MASK 0x000000FFFULL /* Part number bits. */ #define PIDR_ARM_BITS 0x4000BB000ULL /* These make up the ARM JEP-106 code. */ +#define DEVTYPE_OFFSET 0xFCC /* CoreSight Device Type Register */ +#define DEVARCH_OFFSET 0xFBC /* CoreSight Device Architecture Register */ + +#define DEVTYPE_MASK 0x000000FF +#define DEVARCH_PRESENT (1u << 20) +#define DEVARCH_ARCHID_MASK 0x0000FFFF + enum arm_arch { aa_nosupport, aa_cortexm, @@ -152,9 +159,20 @@ enum arm_arch { * * We left out some of the Part numbers included in OpenOCD, we only include * the ones that have ARM as the designer. + * + * To properly identify ADIv6 CoreSight components, two additional fields, + * DEVTYPE and ARCHID are read. + * The dev_type and arch_id values in the table below were found in the + * corresponding logic in pyOCD: + * https://github.com/mbedmicro/pyOCD/blob/master/pyocd/coresight/component_ids.py + * + * Additional reference on the DEVTYPE and DEVARCH registers can be found in the + * ARM CoreSight Architecture Specification v3.0, sections B2.3.4 and B2.3.8. */ static const struct { uint16_t part_number; + uint8_t dev_type; + uint16_t arch_id; enum arm_arch arch; enum cid_class cidc; #ifdef ENABLE_DEBUG @@ -162,59 +180,73 @@ static const struct { const char *full; #endif } pidr_pn_bits[] = { - {0x000, aa_cortexm, cidc_gipc, PIDR_PN_BIT_STRINGS("Cortex-M3 SCS", "(System Control Space)")}, - {0x001, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M3 ITM", "(Instrumentation Trace Module)")}, - {0x002, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M3 DWT", "(Data Watchpoint and Trace)")}, - {0x003, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M3 FBP", "(Flash Patch and Breakpoint)")}, - {0x008, aa_cortexm, cidc_gipc, PIDR_PN_BIT_STRINGS("Cortex-M0 SCS", "(System Control Space)")}, - {0x00a, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M0 DWT", "(Data Watchpoint and Trace)")}, - {0x00b, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M0 BPU", "(Breakpoint Unit)")}, - {0x00c, aa_cortexm, cidc_gipc, PIDR_PN_BIT_STRINGS("Cortex-M4 SCS", "(System Control Space)")}, - {0x00d, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight ETM11", "(Embedded Trace)")}, - {0x00e, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M7 FBP", "(Flash Patch and Breakpoint)")}, - {0x101, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("System TSGEN", "(Time Stamp Generator)")}, - {0x490, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A15 GIC", "(Generic Interrupt Controller)")}, - {0x4c7, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M7 PPB", "(Private Peripheral Bus ROM Table)")}, - {0x906, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight CTI", "(Cross Trigger)")}, - {0x907, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight ETB", "(Trace Buffer)")}, - {0x908, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight CSTF", "(Trace Funnel)")}, - {0x910, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight ETM9", "(Embedded Trace)")}, - {0x912, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight TPIU", "(Trace Port Interface Unit)")}, - {0x913, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight ITM", "(Instrumentation Trace Macrocell)")}, - {0x914, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight SWO", "(Single Wire Output)")}, - {0x917, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight HTM", "(AHB Trace Macrocell)")}, - {0x920, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight ETM11", "(Embedded Trace)")}, - {0x921, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A8 ETM", "(Embedded Trace)")}, - {0x922, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A8 CTI", "(Cross Trigger)")}, - {0x923, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M3 TPIU", "(Trace Port Interface Unit)")}, - {0x924, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M3 ETM", "(Embedded Trace)")}, - {0x925, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M4 ETM", "(Embedded Trace)")}, - {0x930, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-R4 ETM", "(Embedded Trace)")}, - {0x932, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight MTB-M0+", "(Simple Execution Trace)")}, - {0x941, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight TPIU-Lite", "(Trace Port Interface Unit)")}, - {0x950, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight Component", "(unidentified Cortex-A9 component)")}, - {0x955, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight Component", "(unidentified Cortex-A5 component)")}, - {0x956, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A7 ETM", "(Embedded Trace)")}, - {0x95f, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A15 PTM", "(Program Trace Macrocell)")}, - {0x961, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight TMC", "(Trace Memory Controller)")}, - {0x962, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight STM", "(System Trace Macrocell)")}, - {0x963, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight STM", "(System Trace Macrocell)")}, - {0x975, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M7 ETM", "(Embedded Trace)")}, - {0x9a0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight PMU", "(Performance Monitoring Unit)")}, - {0x9a1, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M4 TPIU", "(Trace Port Interface Unit)")}, - {0x9a9, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M7 TPIU", "(Trace Port Interface Unit)")}, - {0x9a5, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A5 ETM", "(Embedded Trace)")}, - {0x9a7, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A7 PMU", "(Performance Monitor Unit)")}, - {0x9af, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A15 PMU", "(Performance Monitor Unit)")}, - {0xc05, aa_cortexa, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-A5 Debug", "(Debug Unit)")}, - {0xc07, aa_cortexa, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-A7 Debug", "(Debug Unit)")}, - {0xc08, aa_cortexa, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-A8 Debug", "(Debug Unit)")}, - {0xc09, aa_cortexa, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-A9 Debug", "(Debug Unit)")}, - {0xc0f, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A15 Debug", "(Debug Unit)")}, /* support? */ - {0xc14, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-R4 Debug", "(Debug Unit)")}, /* support? */ - {0xcd0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Atmel DSU", "(Device Service Unit)")}, - {0xd21, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M33", "()")}, /* support? */ - {0xfff, aa_end, cidc_unknown, PIDR_PN_BIT_STRINGS("end", "end")} + {0x000, 0x00, 0, aa_cortexm, cidc_gipc, PIDR_PN_BIT_STRINGS("Cortex-M3 SCS", "(System Control Space)")}, + {0x001, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M3 ITM", "(Instrumentation Trace Module)")}, + {0x002, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M3 DWT", "(Data Watchpoint and Trace)")}, + {0x003, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M3 FBP", "(Flash Patch and Breakpoint)")}, + {0x008, 0x00, 0, aa_cortexm, cidc_gipc, PIDR_PN_BIT_STRINGS("Cortex-M0 SCS", "(System Control Space)")}, + {0x00a, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M0 DWT", "(Data Watchpoint and Trace)")}, + {0x00b, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M0 BPU", "(Breakpoint Unit)")}, + {0x00c, 0x00, 0, aa_cortexm, cidc_gipc, PIDR_PN_BIT_STRINGS("Cortex-M4 SCS", "(System Control Space)")}, + {0x00d, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight ETM11", "(Embedded Trace)")}, + {0x00e, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M7 FBP", "(Flash Patch and Breakpoint)")}, + {0x101, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("System TSGEN", "(Time Stamp Generator)")}, + {0x490, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A15 GIC", "(Generic Interrupt Controller)")}, + {0x4c7, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M7 PPB", "(Private Peripheral Bus ROM Table)")}, + {0x906, 0x14, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight CTI", "(Cross Trigger)")}, + {0x907, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight ETB", "(Trace Buffer)")}, + {0x908, 0x12, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight CSTF", "(Trace Funnel)")}, + {0x910, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight ETM9", "(Embedded Trace)")}, + {0x912, 0x11, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight TPIU", "(Trace Port Interface Unit)")}, + {0x913, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight ITM", "(Instrumentation Trace Macrocell)")}, + {0x914, 0x11, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight SWO", "(Single Wire Output)")}, + {0x917, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight HTM", "(AHB Trace Macrocell)")}, + {0x920, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight ETM11", "(Embedded Trace)")}, + {0x921, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A8 ETM", "(Embedded Trace)")}, + {0x922, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A8 CTI", "(Cross Trigger)")}, + {0x923, 0x11, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M3 TPIU", "(Trace Port Interface Unit)")}, + {0x924, 0x13, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M3 ETM", "(Embedded Trace)")}, + {0x925, 0x13, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M4 ETM", "(Embedded Trace)")}, + {0x930, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-R4 ETM", "(Embedded Trace)")}, + {0x932, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight MTB-M0+", "(Simple Execution Trace)")}, + {0x941, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight TPIU-Lite", "(Trace Port Interface Unit)")}, + {0x950, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight Component", "(unidentified Cortex-A9 component)")}, + {0x955, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight Component", "(unidentified Cortex-A5 component)")}, + {0x956, 0x13, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A7 ETM", "(Embedded Trace)")}, + {0x95f, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A15 PTM", "(Program Trace Macrocell)")}, + {0x961, 0x32, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight TMC", "(Trace Memory Controller)")}, + {0x962, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight STM", "(System Trace Macrocell)")}, + {0x963, 0x63, 0x0a63, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight STM", "(System Trace Macrocell)")}, + {0x975, 0x13, 0x4a13, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M7 ETM", "(Embedded Trace)")}, + {0x9a0, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight PMU", "(Performance Monitoring Unit)")}, + {0x9a1, 0x11, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M4 TPIU", "(Trace Port Interface Unit)")}, + {0x9a9, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M7 TPIU", "(Trace Port Interface Unit)")}, + {0x9a5, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A5 ETM", "(Embedded Trace)")}, + {0x9a7, 0x16, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A7 PMU", "(Performance Monitor Unit)")}, + {0x9af, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A15 PMU", "(Performance Monitor Unit)")}, + {0xc05, 0x00, 0, aa_cortexa, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-A5 Debug", "(Debug Unit)")}, + {0xc07, 0x15, 0, aa_cortexa, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-A7 Debug", "(Debug Unit)")}, + {0xc08, 0x00, 0, aa_cortexa, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-A8 Debug", "(Debug Unit)")}, + {0xc09, 0x00, 0, aa_cortexa, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-A9 Debug", "(Debug Unit)")}, + {0xc0f, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A15 Debug", "(Debug Unit)")}, /* support? */ + {0xc14, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-R4 Debug", "(Debug Unit)")}, /* support? */ + {0xcd0, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Atmel DSU", "(Device Service Unit)")}, + {0xd20, 0x00, 0x2a04, aa_cortexm, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-M23", "(System Control Space)")}, + {0xd20, 0x11, 0, aa_nosupport, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-M23", "(Trace Port Interface Unit)")}, + {0xd20, 0x13, 0, aa_nosupport, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-M23", "(Embedded Trace)")}, + {0xd20, 0x31, 0x0a31, aa_nosupport, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-M23", "(Micro Trace Buffer)")}, + {0xd20, 0x00, 0x1a02, aa_nosupport, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-M23", "(Data Watchpoint and Trace)")}, + {0xd20, 0x00, 0x1a03, aa_nosupport, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-M23", "(Breakpoint Unit)")}, + {0xd20, 0x14, 0x1a14, aa_nosupport, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-M23", "(Cross Trigger)")}, + {0xd21, 0x00, 0x2a04, aa_cortexm, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-M33", "(System Control Space)")}, + {0xd21, 0x31, 0x0a31, aa_nosupport, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-M33", "(Micro Trace Buffer)")}, + {0xd21, 0x43, 0x1a01, aa_nosupport, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-M33", "(Instrumentation Trace Macrocell)")}, + {0xd21, 0x00, 0x1a02, aa_nosupport, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-M33", "(Data Watchpoint and Trace)")}, + {0xd21, 0x00, 0x1a03, aa_nosupport, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-M33", "(Breakpoint Unit)")}, + {0xd21, 0x14, 0x1a14, aa_nosupport, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-M33", "(Cross Trigger)")}, + {0xd21, 0x13, 0x4a13, aa_nosupport, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-M33", "(Embedded Trace)")}, + {0xd21, 0x11, 0, aa_nosupport, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-M33", "(Trace Port Interface Unit)")}, + {0xfff, 0x00, 0, aa_end, cidc_unknown, PIDR_PN_BIT_STRINGS("end", "end")} }; extern bool cortexa_probe(ADIv5_AP_t *apb, uint32_t debug_base); @@ -347,6 +379,19 @@ static bool adiv5_component_probe(ADIv5_AP_t *ap, uint32_t addr, int recursion, return false; } + /* ADIv6: For CoreSight components, read DEVTYPE and ARCHID */ + uint16_t arch_id = 0; + uint8_t dev_type = 0; + if (cid_class == cidc_dc) { + dev_type = adiv5_mem_read32(ap, addr + DEVTYPE_OFFSET) & DEVTYPE_MASK; + + uint32_t devarch = adiv5_mem_read32(ap, addr + DEVARCH_OFFSET); + + if (devarch & DEVARCH_PRESENT) { + arch_id = devarch & DEVARCH_ARCHID_MASK; + } + } + /* Extract part number from the part id register. */ uint16_t part_number = pidr & PIDR_PN_MASK; /* Find the part number in our part list and run the appropriate probe @@ -354,13 +399,15 @@ static bool adiv5_component_probe(ADIv5_AP_t *ap, uint32_t addr, int recursion, */ int i; for (i = 0; pidr_pn_bits[i].arch != aa_end; i++) { - if (pidr_pn_bits[i].part_number == part_number) { + if ((pidr_pn_bits[i].part_number == part_number) + && (pidr_pn_bits[i].dev_type == dev_type) + && (pidr_pn_bits[i].arch_id == arch_id)) { DEBUG_INFO("%s%d 0x%" PRIx32 ": %s - %s %s (PIDR = 0x%02" PRIx32 - "%08" PRIx32 ")", + "%08" PRIx32 " DEVTYPE = 0x%02" PRIx8 " ARCHID = 0x%04" PRIx16 ")", indent + 1, num_entry, addr, cidc_debug_strings[cid_class], pidr_pn_bits[i].type, pidr_pn_bits[i].full, - (uint32_t)(pidr >> 32), (uint32_t)pidr); + (uint32_t)(pidr >> 32), (uint32_t)pidr, dev_type, arch_id); /* Perform sanity check, if we know what to expect as * component ID class. */ @@ -390,9 +437,9 @@ static bool adiv5_component_probe(ADIv5_AP_t *ap, uint32_t addr, int recursion, } if (pidr_pn_bits[i].arch == aa_end) { DEBUG_WARN("%s0x%" PRIx32 ": %s - Unknown (PIDR = 0x%02" PRIx32 - "%08" PRIx32 ")\n", + "%08" PRIx32 " DEVTYPE = 0x%02" PRIx8 " ARCHID = 0x%04" PRIx16 ")\n", indent, addr, cidc_debug_strings[cid_class], - (uint32_t)(pidr >> 32), (uint32_t)pidr); + (uint32_t)(pidr >> 32), (uint32_t)pidr, dev_type, arch_id); } } return res; diff --git a/src/target/adiv5_swdp.c b/src/target/adiv5_swdp.c index 1691a924..5097febd 100644 --- a/src/target/adiv5_swdp.c +++ b/src/target/adiv5_swdp.c @@ -45,11 +45,16 @@ int adiv5_swdp_scan(void) } #if PC_HOSTED == 1 - if (platform_swdptap_init()) + if (platform_swdptap_init()) { + free(dp); + exit(-1); + } #else - if (swdptap_init()) -#endif + if (swdptap_init()) { + free(dp); return -1; + } +#endif /* Switch from JTAG to SWD mode */ swd_proc.swdptap_seq_out(0xFFFFFFFF, 16); @@ -78,7 +83,8 @@ int adiv5_swdp_scan(void) firmware_swdp_error(dp); adiv5_dp_init(dp); - + if (!target_list) + free(dp); return target_list?1:0; } diff --git a/src/target/cortexm.c b/src/target/cortexm.c index fd1bde1e..10ea6352 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -269,16 +269,17 @@ static bool cortexm_forced_halt(target *t) platform_srst_set_val(false); uint32_t dhcsr = 0; uint32_t start_time = platform_time_ms(); + const uint32_t dhcsr_halted_bits = CORTEXM_DHCSR_S_HALT | CORTEXM_DHCSR_S_REGRDY | + CORTEXM_DHCSR_C_HALT | CORTEXM_DHCSR_C_DEBUGEN; /* Try hard to halt the target. STM32F7 in WFI needs multiple writes!*/ while (platform_time_ms() < start_time + cortexm_wait_timeout) { dhcsr = target_mem_read32(t, CORTEXM_DHCSR); - if (dhcsr == (CORTEXM_DHCSR_S_HALT | CORTEXM_DHCSR_S_REGRDY | - CORTEXM_DHCSR_C_HALT | CORTEXM_DHCSR_C_DEBUGEN)) + if ((dhcsr & dhcsr_halted_bits) == dhcsr_halted_bits) break; target_halt_request(t); } - if (dhcsr != 0x00030003) + if ((dhcsr & dhcsr_halted_bits) != dhcsr_halted_bits) return false; return true; } @@ -293,7 +294,6 @@ bool cortexm_probe(ADIv5_AP_t *ap, bool forced) } adiv5_ap_ref(ap); - uint32_t identity = ap->idr & 0xff; struct cortexm_priv *priv = calloc(1, sizeof(*priv)); if (!priv) { /* calloc failed: heap exhaustion */ DEBUG_WARN("calloc: failed in %s\n", __func__); @@ -309,18 +309,42 @@ bool cortexm_probe(ADIv5_AP_t *ap, bool forced) t->mem_write = cortexm_mem_write; t->driver = cortexm_driver_str; - switch (identity) { - case 0x11: /* M3/M4 */ - t->core = "M3/M4"; + + /* The CPUID register is defined in the ARMv7-M and ARMv8-M + * architecture manuals. The PARTNO field is implementation defined, + * that is, the actual values are found in the Technical Reference Manual + * for each Cortex-M core. + */ + uint32_t cpuid = target_mem_read32(t, CORTEXM_CPUID); + uint16_t partno = (cpuid >> 4) & 0xfff; + + switch (partno) { + case 0xd21: + t->core = "M33"; break; - case 0x21: /* M0 */ - t->core = "M0"; + + case 0xd20: + t->core = "M23"; break; - case 0x31: /* M0+ */ + + case 0xc23: + t->core = "M3"; + break; + + case 0xc24: + t->core = "M4"; + break; + + case 0xc27: + t->core = "M7"; + break; + + case 0xc60: t->core = "M0+"; break; - case 0x01: /* M7 */ - t->core = "M7"; + + case 0xc20: + t->core = "M0"; break; } diff --git a/src/target/cortexm.h b/src/target/cortexm.h index 01077620..95952f5b 100644 --- a/src/target/cortexm.h +++ b/src/target/cortexm.h @@ -28,6 +28,7 @@ extern long cortexm_wait_timeout; #define CORTEXM_SCS_BASE (CORTEXM_PPB_BASE + 0xE000) +#define CORTEXM_CPUID (CORTEXM_SCS_BASE + 0xD00) #define CORTEXM_AIRCR (CORTEXM_SCS_BASE + 0xD0C) #define CORTEXM_CFSR (CORTEXM_SCS_BASE + 0xD28) #define CORTEXM_HFSR (CORTEXM_SCS_BASE + 0xD2C) diff --git a/src/target/lpc11xx.c b/src/target/lpc11xx.c index e734fbfd..ba2dc6a4 100644 --- a/src/target/lpc11xx.c +++ b/src/target/lpc11xx.c @@ -29,7 +29,8 @@ #define MIN_RAM_SIZE 1024 #define RAM_USAGE_FOR_IAP_ROUTINES 32 /* IAP routines use 32 bytes at top of ram */ -#define IAP_ENTRYPOINT 0x1fff1ff1 +#define IAP_ENTRY_MOST 0x1fff1ff1 /* all except LPC84x */ +#define IAP_ENTRY_84x 0x0f001ff1 #define IAP_RAM_BASE 0x10000000 #define LPC11XX_DEVICE_ID 0x400483F4 @@ -55,13 +56,13 @@ const struct command_s lpc11xx_cmd_list[] = { {NULL, NULL, NULL} }; -void lpc11xx_add_flash(target *t, uint32_t addr, size_t len, size_t erasesize) +void lpc11xx_add_flash(target *t, uint32_t addr, size_t len, size_t erasesize, uint32_t iap_entry) { struct lpc_flash *lf = lpc_add_flash(t, addr, len); lf->f.blocksize = erasesize; lf->f.buf_size = IAP_PGM_CHUNKSIZE; lf->f.write = lpc_flash_write_magic_vect; - lf->iap_entry = IAP_ENTRYPOINT; + lf->iap_entry = iap_entry; lf->iap_ram = IAP_RAM_BASE; lf->iap_msp = IAP_RAM_BASE + MIN_RAM_SIZE - RAM_USAGE_FOR_IAP_ROUTINES; } @@ -108,7 +109,7 @@ lpc11xx_probe(target *t) case 0x2980002B: /* lpc11u24x/401 */ t->driver = "LPC11xx"; target_add_ram(t, 0x10000000, 0x2000); - lpc11xx_add_flash(t, 0x00000000, 0x20000, 0x1000); + lpc11xx_add_flash(t, 0x00000000, 0x20000, 0x1000, IAP_ENTRY_MOST); target_add_commands(t, lpc11xx_cmd_list, "LPC11xx"); return true; @@ -116,23 +117,23 @@ lpc11xx_probe(target *t) case 0x1A24902B: t->driver = "LPC1112"; target_add_ram(t, 0x10000000, 0x1000); - lpc11xx_add_flash(t, 0x00000000, 0x10000, 0x1000); + lpc11xx_add_flash(t, 0x00000000, 0x10000, 0x1000, IAP_ENTRY_MOST); return true; case 0x1000002b: // FX LPC11U6 32 kB SRAM/256 kB flash (max) t->driver = "LPC11U6"; target_add_ram(t, 0x10000000, 0x8000); - lpc11xx_add_flash(t, 0x00000000, 0x40000, 0x1000); + lpc11xx_add_flash(t, 0x00000000, 0x40000, 0x1000, IAP_ENTRY_MOST); return true; case 0x3000002B: case 0x3D00002B: t->driver = "LPC1343"; target_add_ram(t, 0x10000000, 0x2000); - lpc11xx_add_flash(t, 0x00000000, 0x8000, 0x1000); + lpc11xx_add_flash(t, 0x00000000, 0x8000, 0x1000, IAP_ENTRY_MOST); return true; case 0x00008A04: /* LPC8N04 (see UM11074 Rev.1.3 section 4.5.19) */ t->driver = "LPC8N04"; target_add_ram(t, 0x10000000, 0x2000); - lpc11xx_add_flash(t, 0x00000000, 0x8000, 0x400); + lpc11xx_add_flash(t, 0x00000000, 0x8000, 0x400, IAP_ENTRY_MOST); target_add_commands(t, lpc11xx_cmd_list, "LPC8N04"); return true; } @@ -148,7 +149,7 @@ lpc11xx_probe(target *t) case 0x00008122: /* LPC812M101JDH20 / LPC812M101JTB16 */ t->driver = "LPC81x"; target_add_ram(t, 0x10000000, 0x1000); - lpc11xx_add_flash(t, 0x00000000, 0x4000, 0x400); + lpc11xx_add_flash(t, 0x00000000, 0x4000, 0x400, IAP_ENTRY_MOST); target_add_commands(t, lpc11xx_cmd_list, "LPC81x"); return true; case 0x00008221: /* LPC822M101JHI33 */ @@ -157,7 +158,7 @@ lpc11xx_probe(target *t) case 0x00008242: /* LPC824M201JDH20 */ t->driver = "LPC82x"; target_add_ram(t, 0x10000000, 0x2000); - lpc11xx_add_flash(t, 0x00000000, 0x8000, 0x400); + lpc11xx_add_flash(t, 0x00000000, 0x8000, 0x400, IAP_ENTRY_MOST); target_add_commands(t, lpc11xx_cmd_list, "LPC82x"); return true; case 0x00008441: @@ -166,7 +167,7 @@ lpc11xx_probe(target *t) case 0x00008444: t->driver = "LPC844"; target_add_ram(t, 0x10000000, 0x2000); - lpc11xx_add_flash(t, 0x00000000, 0x10000, 0x400); + lpc11xx_add_flash(t, 0x00000000, 0x10000, 0x400, IAP_ENTRY_84x); return true; case 0x00008451: case 0x00008452: @@ -174,7 +175,7 @@ lpc11xx_probe(target *t) case 0x00008454: t->driver = "LPC845"; target_add_ram(t, 0x10000000, 0x4000); - lpc11xx_add_flash(t, 0x00000000, 0x10000, 0x400); + lpc11xx_add_flash(t, 0x00000000, 0x10000, 0x400, IAP_ENTRY_84x); return true; case 0x0003D440: /* LPC11U34/311 */ case 0x0001cc40: /* LPC11U34/421 */ @@ -186,13 +187,13 @@ lpc11xx_probe(target *t) case 0x00007C40: /* LPC11U37FBD64/501 */ t->driver = "LPC11U3x"; target_add_ram(t, 0x10000000, 0x2000); - lpc11xx_add_flash(t, 0x00000000, 0x20000, 0x1000); + lpc11xx_add_flash(t, 0x00000000, 0x20000, 0x1000, IAP_ENTRY_MOST); return true; case 0x00040070: /* LPC1114/333 */ case 0x00050080: /* lpc1115XL */ t->driver = "LPC1100XL"; target_add_ram(t, 0x10000000, 0x2000); - lpc11xx_add_flash(t, 0x00000000, 0x20000, 0x1000); + lpc11xx_add_flash(t, 0x00000000, 0x20000, 0x1000, IAP_ENTRY_MOST); return true; } if (idcode) { diff --git a/src/target/stm32f1.c b/src/target/stm32f1.c index 84fdd776..4aac170d 100644 --- a/src/target/stm32f1.c +++ b/src/target/stm32f1.c @@ -65,6 +65,9 @@ static int stm32f1_flash_write(struct target_flash *f, #define FLASH_OBR (FPEC_BASE+0x1C) #define FLASH_WRPR (FPEC_BASE+0x20) +#define FLASH_BANK2_OFFSET 0x40 +#define FLASH_BANK_SPLIT 0x08080000 + #define FLASH_CR_OBL_LAUNCH (1<<13) #define FLASH_CR_OPTWRE (1 << 9) #define FLASH_CR_STRT (1 << 6) @@ -124,7 +127,7 @@ bool stm32f1_probe(target *t) case 0x420: /* Value Line, Low-/Medium density */ target_add_ram(t, 0x20000000, 0x5000); stm32f1_add_flash(t, 0x8000000, 0x20000, 0x400); - target_add_commands(t, stm32f1_cmd_list, "STM32 LD/MD"); + target_add_commands(t, stm32f1_cmd_list, "STM32 LD/MD/VL-LD/VL-MD"); /* Test for non-genuine parts with Core rev 2*/ ADIv5_AP_t *ap = cortexm_ap(t); if ((ap->idr >> 28) > 1) { @@ -139,11 +142,19 @@ bool stm32f1_probe(target *t) case 0x414: /* High density */ case 0x418: /* Connectivity Line */ case 0x428: /* Value Line, High Density */ - t->driver = "STM32F1 high density"; + t->driver = "STM32F1 VL density"; target_add_ram(t, 0x20000000, 0x10000); stm32f1_add_flash(t, 0x8000000, 0x80000, 0x800); - target_add_commands(t, stm32f1_cmd_list, "STM32 HD/CL"); + target_add_commands(t, stm32f1_cmd_list, "STM32 HF/CL/VL-HD"); return true; + case 0x430: /* XL-density */ + t->driver = "STM32F1 XL density"; + target_add_ram(t, 0x20000000, 0x18000); + stm32f1_add_flash(t, 0x8000000, 0x80000, 0x800); + stm32f1_add_flash(t, 0x8080000, 0x80000, 0x800); + target_add_commands(t, stm32f1_cmd_list, "STM32 XL/VL-XL"); + return true; + case 0x438: /* STM32F303x6/8 and STM32F328 */ case 0x422: /* STM32F30x */ case 0x446: /* STM32F303xD/E and STM32F398xE */ @@ -192,29 +203,37 @@ bool stm32f1_probe(target *t) return true; } -static void stm32f1_flash_unlock(target *t) +static void stm32f1_flash_unlock(target *t, uint32_t bank_offset) { - target_mem_write32(t, FLASH_KEYR, KEY1); - target_mem_write32(t, FLASH_KEYR, KEY2); + target_mem_write32(t, FLASH_KEYR + bank_offset, KEY1); + target_mem_write32(t, FLASH_KEYR + bank_offset, KEY2); } static int stm32f1_flash_erase(struct target_flash *f, target_addr addr, size_t len) { target *t = f->t; + target_addr end = addr + len - 1; + target_addr start = addr; - stm32f1_flash_unlock(t); - + if ((t->idcode == 0x430) && (end >= FLASH_BANK_SPLIT)) + stm32f1_flash_unlock(t, FLASH_BANK2_OFFSET); + if (addr < FLASH_BANK_SPLIT) + stm32f1_flash_unlock(t, 0); while(len) { + uint32_t bank_offset = 0; + if (addr >= FLASH_BANK_SPLIT) + bank_offset = FLASH_BANK2_OFFSET; /* Flash page erase instruction */ - target_mem_write32(t, FLASH_CR, FLASH_CR_PER); + target_mem_write32(t, FLASH_CR + bank_offset, FLASH_CR_PER); /* write address to FMA */ - target_mem_write32(t, FLASH_AR, addr); + target_mem_write32(t, FLASH_AR + bank_offset, addr); /* Flash page erase start instruction */ - target_mem_write32(t, FLASH_CR, FLASH_CR_STRT | FLASH_CR_PER); + target_mem_write32(t, FLASH_CR + bank_offset, + FLASH_CR_STRT | FLASH_CR_PER); /* Read FLASH_SR to poll for BSY bit */ - while (target_mem_read32(t, FLASH_SR) & FLASH_SR_BSY) + while (target_mem_read32(t, FLASH_SR + bank_offset) & FLASH_SR_BSY) if(target_check_error(t)) { DEBUG_WARN("stm32f1 flash erase: comm error\n"); return -1; @@ -227,12 +246,20 @@ static int stm32f1_flash_erase(struct target_flash *f, } /* Check for error */ - uint32_t sr = target_mem_read32(t, FLASH_SR); - if ((sr & SR_ERROR_MASK) || !(sr & SR_EOP)) { - DEBUG_INFO("stm32f1 flash erase error 0x%" PRIx32 "\n", sr); - return -1; + if (start < FLASH_BANK_SPLIT) { + uint32_t sr = target_mem_read32(t, FLASH_SR); + if ((sr & SR_ERROR_MASK) || !(sr & SR_EOP)) { + DEBUG_INFO("stm32f1 flash erase error 0x%" PRIx32 "\n", sr); + return -1; + } + } + if ((t->idcode == 0x430) && (end >= FLASH_BANK_SPLIT)) { + uint32_t sr = target_mem_read32(t, FLASH_SR + FLASH_BANK2_OFFSET); + if ((sr & SR_ERROR_MASK) || !(sr & SR_EOP)) { + DEBUG_INFO("stm32f1 bank 2 flash erase error 0x%" PRIx32 "\n", sr); + return -1; + } } - return 0; } @@ -241,21 +268,49 @@ static int stm32f1_flash_write(struct target_flash *f, { target *t = f->t; uint32_t sr; - target_mem_write32(t, FLASH_CR, FLASH_CR_PG); - cortexm_mem_write_sized(t, dest, src, len, ALIGN_HALFWORD); - /* Read FLASH_SR to poll for BSY bit */ - /* Wait for completion or an error */ - do { - sr = target_mem_read32(t, FLASH_SR); - if(target_check_error(t)) { - DEBUG_WARN("stm32f1 flash write: comm error\n"); + size_t length = 0; + if (dest < FLASH_BANK_SPLIT) { + if ((dest + len - 1) >= FLASH_BANK_SPLIT) + length = FLASH_BANK_SPLIT - dest; + else + length = len; + target_mem_write32(t, FLASH_CR, FLASH_CR_PG); + cortexm_mem_write_sized(t, dest, src, length, ALIGN_HALFWORD); + /* Read FLASH_SR to poll for BSY bit */ + /* Wait for completion or an error */ + do { + sr = target_mem_read32(t, FLASH_SR); + if(target_check_error(t)) { + DEBUG_WARN("stm32f1 flash write: comm error\n"); + return -1; + } + } while (sr & FLASH_SR_BSY); + + if (sr & SR_ERROR_MASK) { + DEBUG_WARN("stm32f1 flash write error 0x%" PRIx32 "\n", sr); return -1; } - } while (sr & FLASH_SR_BSY); + dest += length; + src += length; + } + length = len - length; + if ((t->idcode == 0x430) && length) { /* Write on bank 2 */ + target_mem_write32(t, FLASH_CR + FLASH_BANK2_OFFSET, FLASH_CR_PG); + cortexm_mem_write_sized(t, dest, src, length, ALIGN_HALFWORD); + /* Read FLASH_SR to poll for BSY bit */ + /* Wait for completion or an error */ + do { + sr = target_mem_read32(t, FLASH_SR + FLASH_BANK2_OFFSET); + if(target_check_error(t)) { + DEBUG_WARN("stm32f1 flash bank2 write: comm error\n"); + return -1; + } + } while (sr & FLASH_SR_BSY); - if (sr & SR_ERROR_MASK) { - DEBUG_WARN("stm32f1 flash write error 0x%" PRIx32 "\n", sr); - return -1; + if (sr & SR_ERROR_MASK) { + DEBUG_WARN("stm32f1 flash bank2 write error 0x%" PRIx32 "\n", sr); + return -1; + } } return 0; } @@ -264,7 +319,7 @@ static bool stm32f1_cmd_erase_mass(target *t, int argc, const char **argv) { (void)argc; (void)argv; - stm32f1_flash_unlock(t); + stm32f1_flash_unlock(t, 0); /* Flash mass erase start instruction */ target_mem_write32(t, FLASH_CR, FLASH_CR_MER); @@ -279,7 +334,23 @@ static bool stm32f1_cmd_erase_mass(target *t, int argc, const char **argv) uint16_t sr = target_mem_read32(t, FLASH_SR); if ((sr & SR_ERROR_MASK) || !(sr & SR_EOP)) return false; + if (t->idcode == 0x430) { + stm32f1_flash_unlock(t, FLASH_BANK2_OFFSET); + /* Flash mass erase start instruction on bank 2*/ + target_mem_write32(t, FLASH_CR + FLASH_BANK2_OFFSET, FLASH_CR_MER); + target_mem_write32(t, FLASH_CR + FLASH_BANK2_OFFSET, + FLASH_CR_STRT | FLASH_CR_MER); + + /* Read FLASH_SR to poll for BSY bit */ + while (target_mem_read32(t, FLASH_SR + FLASH_BANK2_OFFSET) & FLASH_SR_BSY) + if(target_check_error(t)) + return false; + /* Check for error */ + uint16_t sr = target_mem_read32(t, FLASH_SR + FLASH_BANK2_OFFSET); + if ((sr & SR_ERROR_MASK) || !(sr & SR_EOP)) + return false; + } return true; } @@ -359,7 +430,7 @@ static bool stm32f1_cmd_option(target *t, int argc, const char **argv) default: flash_obp_rdp_key = FLASH_OBP_RDP_KEY; } rdprt = target_mem_read32(t, FLASH_OBR) & FLASH_OBR_RDPRT; - stm32f1_flash_unlock(t); + stm32f1_flash_unlock(t, 0); target_mem_write32(t, FLASH_OPTKEYR, KEY1); target_mem_write32(t, FLASH_OPTKEYR, KEY2);