├── readJTAG.py ├── Makefile ├── license.txt ├── README.txt ├── ftdiJTAG.1 └── ftdiJTAG.c /readJTAG.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # XVC FTDI JTAG Copyright (c) 2021, The Regents of the University of 4 | # California, through Lawrence Berkeley National Laboratory (subject to 5 | # receipt of any required approvals from the U.S. Dept. of Energy). All 6 | # rights reserved. 7 | # 8 | # If you have questions about your rights to use or distribute this software, 9 | # please contact Berkeley Lab's Intellectual Property Office at 10 | # IPO@lbl.gov. 11 | # 12 | # NOTICE. This Software was developed under funding from the U.S. Department 13 | # of Energy and the U.S. Government consequently retains certain rights. As 14 | # such, the U.S. Government has been granted for itself and others acting on 15 | # its behalf a paid-up, nonexclusive, irrevocable, worldwide license in the 16 | # Software to reproduce, distribute copies to the public, prepare derivative 17 | # works, and perform publicly and display publicly, and to permit others to 18 | # do so. 19 | 20 | from __future__ import print_function 21 | import socket 22 | import time 23 | 24 | xvcGetinfo = bytearray(b'getinfo:') 25 | xvcResetTapAndGoToShiftDR = bytearray(b'shift:\x09\x00\x00\x00\x5F\x00\x00\x00') 26 | xvcGetID = bytearray(b'shift:\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') 27 | 28 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 29 | sock.connect(("127.0.0.1", 2542)) 30 | 31 | for cmd in (xvcGetinfo, xvcResetTapAndGoToShiftDR): 32 | sock.send(cmd) 33 | print(sock.recv(100)) 34 | 35 | sock.send(xvcGetID) 36 | id = bytearray(sock.recv(100)) 37 | print("%02X%02X%02X%02X"%(id[3], id[2], id[1], id[0])) 38 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # XVC FTDI JTAG Copyright (c) 2021, The Regents of the University of 2 | # California, through Lawrence Berkeley National Laboratory (subject to 3 | # receipt of any required approvals from the U.S. Dept. of Energy). All 4 | # rights reserved. 5 | # 6 | # If you have questions about your rights to use or distribute this software, 7 | # please contact Berkeley Lab's Intellectual Property Office at 8 | # IPO@lbl.gov. 9 | # 10 | # NOTICE. This Software was developed under funding from the U.S. Department 11 | # of Energy and the U.S. Government consequently retains certain rights. As 12 | # such, the U.S. Government has been granted for itself and others acting on 13 | # its behalf a paid-up, nonexclusive, irrevocable, worldwide license in the 14 | # Software to reproduce, distribute copies to the public, prepare derivative 15 | # works, and perform publicly and display publicly, and to permit others to 16 | # do so. 17 | 18 | # Install locations 19 | INSTALL_BIN = /usr/local/bin 20 | INSTALL_MAN = /usr/local/share/man/man1 21 | 22 | # Uncomment and modify if libusb stuff is not on compiler search paths 23 | #USBLIB_INCLUDE = -I/usr/local/include 24 | #USBLIB_LIB = -L/usr/local/lib 25 | 26 | CFLAGS = -g -O2 $(USBLIB_INCLUDE) $(USBLIB_LIB) 27 | 28 | # Assorted flags to ensure good coding style and to 29 | # enable some features from the current millenium. 30 | # Adjust these as required by what your compiler supports 31 | CFLAGS += --std=c99 -D_POSIX_C_SOURCE=199309L -D_DEFAULT_SOURCE 32 | CFLAGS += -Wall -Wextra -pedantic -Wstrict-prototypes -Wmissing-prototypes -Wundef -Wshadow 33 | CFLAGS += -Wpointer-arith -Wcast-align -Wcast-qual -Wredundant-decls 34 | 35 | LDLIBS = -lusb-1.0 36 | 37 | all: ftdiJTAG 38 | 39 | clean: 40 | rm -rf ftdiJTAG ftdiJTAG.dSYM 41 | 42 | install: $(INSTALL_BIN)/ftdiJTAG $(INSTALL_MAN)/ftdiJTAG.1 43 | $(INSTALL_BIN)/ftdiJTAG: ftdiJTAG 44 | cp ftdiJTAG $(INSTALL_BIN) 45 | $(INSTALL_MAN)/ftdiJTAG.1: ftdiJTAG.1 46 | cp ftdiJTAG.1 $(INSTALL_MAN) 47 | 48 | uninstall: 49 | rm -f $(INSTALL_BIN)/ftdiJTAG $(INSTALL_MAN)/ftdiJTAG.1 50 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | XVC FTDI JTAG Copyright (c) 2021, The Regents of the University of 2 | California, through Lawrence Berkeley National Laboratory (subject to 3 | receipt of any required approvals from the U.S. Dept. of Energy). All 4 | rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | (1) Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | (3) Neither the name of the University of California, Lawrence Berkeley 17 | National Laboratory, U.S. Dept. of Energy nor the names of its contributors 18 | may be used to endorse or promote products derived from this software 19 | without specific prior written permission. 20 | 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 26 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | POSSIBILITY OF SUCH DAMAGE. 33 | 34 | You are under no obligation whatsoever to provide any bug fixes, patches, 35 | or upgrades to the features, functionality or performance of the source 36 | code ("Enhancements") to anyone; however, if you choose to make your 37 | Enhancements available either publicly, or directly to Lawrence Berkeley 38 | National Laboratory, without imposing a separate written license agreement 39 | for such Enhancements, then you hereby grant the following license: a 40 | non-exclusive, royalty-free perpetual license to install, use, modify, 41 | prepare derivative works, incorporate into other computer software, 42 | distribute, and sublicense such enhancements or derivative works thereof, 43 | in binary and source code form. 44 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | INTRODUCTION 2 | ============ 3 | Xilinx tools such as Vivado and Vitis communicate with FPGAs through a JTAG 4 | interface and an FTDI USB/Serial chip driven by a Xilinx server (hw_server) 5 | that communicates with the tools using a proprietary protocol. The server 6 | and tools recognize the FTDI chip as a valid target only if it is equipped 7 | with a special EEPROM from Digilent. 8 | 9 | The tools also support an older, documented*, protocol called Xilinx Virtual 10 | Cable (XVC). The code in this repository provides a server that communicates 11 | with the Xilinx tools using XVC and works with FTDI chips whether or not they 12 | are equipped with the Digilent EEPROM. 13 | 14 | * Thanks to tmbinc's 2012 reverse-engineering (https://github.com/tmbinc/xvcd) 15 | and Xilinx's XAPP1251, XVC can now be considered a properly documented and 16 | usable protocol. 17 | 18 | AUTHOR 19 | ====== 20 | Eric Norum (wenorum@lbl.gov) 21 | 22 | PREREQUISITES 23 | ============= 24 | C development environment 25 | libusb -- https://github.com/libusb/libusb 26 | 27 | BUILDING 28 | ======== 29 | In many cases all that's needed is 30 | make 31 | make install 32 | 33 | You will have to edit the Makefile if you want to install the executable 34 | and manual page in non-default locations, or if your C compiler doesn't 35 | find the libusb header or library. 36 | 37 | LICENSE 38 | ======= 39 | XVC FTDI JTAG Copyright (c) 2021, The Regents of the University of 40 | California, through Lawrence Berkeley National Laboratory (subject to 41 | receipt of any required approvals from the U.S. Dept. of Energy). All 42 | rights reserved. 43 | 44 | Redistribution and use in source and binary forms, with or without 45 | modification, are permitted provided that the following conditions are met: 46 | 47 | (1) Redistributions of source code must retain the above copyright notice, 48 | this list of conditions and the following disclaimer. 49 | 50 | (2) Redistributions in binary form must reproduce the above copyright 51 | notice, this list of conditions and the following disclaimer in the 52 | documentation and/or other materials provided with the distribution. 53 | 54 | (3) Neither the name of the University of California, Lawrence Berkeley 55 | National Laboratory, U.S. Dept. of Energy nor the names of its contributors 56 | may be used to endorse or promote products derived from this software 57 | without specific prior written permission. 58 | 59 | 60 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 61 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 62 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 63 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 64 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 65 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 66 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 67 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 68 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 69 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 70 | POSSIBILITY OF SUCH DAMAGE. 71 | 72 | You are under no obligation whatsoever to provide any bug fixes, patches, 73 | or upgrades to the features, functionality or performance of the source 74 | code ("Enhancements") to anyone; however, if you choose to make your 75 | Enhancements available either publicly, or directly to Lawrence Berkeley 76 | National Laboratory, without imposing a separate written license agreement 77 | for such Enhancements, then you hereby grant the following license: a 78 | non-exclusive, royalty-free perpetual license to install, use, modify, 79 | prepare derivative works, incorporate into other computer software, 80 | distribute, and sublicense such enhancements or derivative works thereof, 81 | in binary and source code form. 82 | -------------------------------------------------------------------------------- /ftdiJTAG.1: -------------------------------------------------------------------------------- 1 | .\" XVC FTDI JTAG Copyright (c) 2021, The Regents of the University of 2 | .\" California, through Lawrence Berkeley National Laboratory (subject to 3 | .\" receipt of any required approvals from the U.S. Dept. of Energy). All 4 | .\" rights reserved. 5 | .\" 6 | .\" If you have questions about your rights to use or distribute this software, 7 | .\" please contact Berkeley Lab's Intellectual Property Office at 8 | .\" IPO@lbl.gov. 9 | .\" 10 | .\" NOTICE. This Software was developed under funding from the U.S. Department 11 | .\" of Energy and the U.S. Government consequently retains certain rights. As 12 | .\" such, the U.S. Government has been granted for itself and others acting on 13 | .\" its behalf a paid-up, nonexclusive, irrevocable, worldwide license in the 14 | .\" Software to reproduce, distribute copies to the public, prepare derivative 15 | .\" works, and perform publicly and display publicly, and to permit others to 16 | .\" do so. 17 | .TH FTDI_JTAG 1 2020-06-12 "LBNL" "Lawrence Berkeley National Laboratory" 18 | .SH NAME 19 | ftdiJTAG \- Xilinx Virtual Cable Server for FTDI FT232H/FT2232H/FT4232H 20 | .SH SYNOPSIS 21 | .nh 22 | .ad l 23 | .TP 9.1em 24 | .B ftdiJTAG 25 | .RB [ \-a\ address ] 26 | .RB [ \-p\ port ] 27 | .RB [ \-d\ vendor:product\fR[\fB:\fR[\fBserial\fR]] ] 28 | .RB [ \-g\ DirectionValue\fR[\fB:DirectionValue...\fR]\fB ] 29 | .RB [ \-c\ frequency ] 30 | .RB [ \-q ] 31 | .RB [ \-B ] 32 | .RB [ \-L ] 33 | .RB [ \-R ] 34 | .RB [ \-S ] 35 | .RB [ \-U ] 36 | .RB [ \-X ] 37 | .hy 38 | .SH DESCRIPTION 39 | This server converts Xilinx Virtual Cable requests to FTDI Multi-Protocol Synchronous Serial Engine USB commands. 40 | .IP \-a\ address 41 | Address of network interface on which to listen for connections from XVC clients. Default is 127.0.0.1 (localhost). Specify 0.0.0.0 to listen on all networks. 42 | .IP \-p\ port 43 | TCP port number on which to listen. Default is 2542. 44 | .IP \-d\ vendor:product[:[serial]] 45 | USB vendor, product and optional serial number of the FTDI chip to be used. Default is vendor 0403, product 6010 (FT2232H) or 6011 (FT4432H) or 6014 (FT232H), and any serial number. Vendor and product numbers are in hexadecimal. 46 | .IP \-g\ DirectionValue[:DirectionValue...] 47 | Colon-separated pairs of hexadecimal values (0x00 to 0xFF) specifying the direction and state 48 | of the most significant four bits of the FTDI general-purpose I/O port. 49 | The first value of each pair sets the directions and the 50 | second value sets the states of the four I/O port bits. 51 | A one bit in the direction configures the corresponding bit in the port as an output. 52 | A zero bit in the direction configures the corresponding bit in the port as an input. 53 | The least significant bit in the direction and state value is applied to bit 4 of the I/O port and the most significant bit to bit 7 of the I/O port. 54 | By default all four pins are inputs. 55 | Multiple direction/state pairs take effect in turn separated 56 | by about 100 milliseconds. This can be used to generate a startup reset pulse, for example. 57 | .IP \-c\ f 58 | Lock the JTAG clock at the specified frequency (ignore XVC settck: commands). 59 | The numeric frequency argument can be followed by a 'k' or an 'M' to multiply the value by 1000 or 1000000, respectively. 60 | .IP -q 61 | Quiet mode. Don't print USB device information on startup nor client connect/disconnect messages during operation. Useful when running this server in the background or as a daemon. 62 | .IP -B 63 | Use FTDI port B as the JTAG interface rather than the default port A. 64 | .IP -L 65 | Put JTAG port into loopback mode. 66 | .IP -R 67 | Report on runt reply packets, namely those shorter than three bytes. 68 | Normally these are silently ignored. 69 | Some FTDI devices return a few 2 byte, modem status only, replies. 70 | .IP -S 71 | Show I/O statistics when client disconnects. 72 | .IP -U 73 | Enable diagnostic messages for USB transactions. 74 | .IP -X 75 | Enable diagnostic messages for Xilinx virtual cable transactions. 76 | .SH USAGE 77 | The Xilinx hardware manager does not automatically detect the presence of this server. The following procedure is required after starting the server. 78 | .IP Vivado: 79 | Click 'Open Hardware Manager'. 80 | .br 81 | Click 'Open Target'. 82 | .br 83 | Click 'Open New Target...'. 84 | .br 85 | In the 'Open New Hardware Target' window that pops up, click 'Next>'. 86 | .br 87 | Set the menu to 'Connect to Local server', then click 'Next>'. 88 | This menu item must be chosen even if the server was started on a different machine. 89 | .br 90 | If the desired device is not present in the 'Hardware Targets' table, click the 'Add Xilinx Virtual Cable (XVC)' button and set the IP address and port corresponding to this server. 91 | .br 92 | Ensure that the desired device is highlighted in the 'Hardware Targets' table, then click 'Next>'. 93 | .br 94 | Click 'Finish'. 95 | .IP Vitis: 96 | Open the XSCT console. 97 | .br 98 | Enter the command 'connect -xvc-url localhost:2542' (substitute the appropriate host and port information if the server is not using the defaults). 99 | .br 100 | Specify the 'Local' connection when offered the option, for example in the 'Program FPGA' window. 101 | .SH USE\ WITH\ DOCKER 102 | I run the Xilinx tools in a Linux Docker container on my OS X machine. To allow them to connect to the JTAG port of my development hardware I run ftdiJTAG on the OS X machine as normal, but in the Vivado 'Add Xilinx Virtual Cable (XVC)' window I specify the host as 'host.docker.internal' rather than the default 'localhost'. 103 | .SH REFERENCES 104 | .nh 105 | .IP 1. 3em 106 | https:/\:/\:www.xilinx.com/\:support/\:documentation/\:application_notes/\:xapp1251-xvc-zynq-petalinux.pdf 107 | .IP 2. 108 | https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT4232H.pdf 109 | .IP 3. 110 | https:/\:/\:www.ftdichip.com/\:Support/\:Documents/\:AppNotes/\:AN_108_Command_Processor_for_MPSSE_and_MCU_Host_Bus_Emulation_Modes.pdf 111 | .IP 4. 112 | https://libusb.info 113 | .SH EXAMPLES 114 | .ft CW 115 | LBNL Marble: -c 30M -g 11 \fR(Applies to Marble Mini, too)\f(CW 116 | .br 117 | Xilinx ZCU111: -c 30M -g 44 \fR(\f(CW-g 40:44 \fRto pulse power-on reset line) 118 | .SH CAVEATS 119 | The Xilinx virtual cable protocol provides no means for the server to validate or authenticate a client. Be very careful when specifying an address and port accessible from the wider internet. 120 | .PP 121 | Tests show that the Xilinx hw_server using the new protocol programs devices about three times faster than this server. 122 | .PP 123 | If more than one USB device matches the vendor:product:serial values the first one found will be used. There is no indication from \fBftdiJTAG\fR that the match is not unique. 124 | -------------------------------------------------------------------------------- /ftdiJTAG.c: -------------------------------------------------------------------------------- 1 | /* 2 | * XVC FTDI JTAG Copyright (c) 2021, The Regents of the University of 3 | * California, through Lawrence Berkeley National Laboratory (subject to 4 | * receipt of any required approvals from the U.S. Dept. of Energy). All 5 | * rights reserved. 6 | * 7 | * If you have questions about your rights to use or distribute this software, 8 | * please contact Berkeley Lab's Intellectual Property Office at 9 | * IPO@lbl.gov. 10 | * 11 | * NOTICE. This Software was developed under funding from the U.S. Department 12 | * of Energy and the U.S. Government consequently retains certain rights. As 13 | * such, the U.S. Government has been granted for itself and others acting on 14 | * its behalf a paid-up, nonexclusive, irrevocable, worldwide license in the 15 | * Software to reproduce, distribute copies to the public, prepare derivative 16 | * works, and perform publicly and display publicly, and to permit others to 17 | * do so. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #if (!defined(LIBUSBX_API_VERSION) || (LIBUSBX_API_VERSION < 0x01000102)) 34 | # error "You need to get a newer version of libusb-1.0 (16 at the very least)" 35 | #endif 36 | 37 | #define XVC_BUFSIZE 1024 38 | #define FTDI_CLOCK_RATE 60000000 39 | #define IDSTRING_CAPACITY 100 40 | #define USB_BUFSIZE 512 41 | 42 | /* libusb bmRequestType */ 43 | #define BMREQTYPE_OUT (LIBUSB_REQUEST_TYPE_VENDOR | \ 44 | LIBUSB_RECIPIENT_DEVICE | \ 45 | LIBUSB_ENDPOINT_OUT) 46 | 47 | 48 | /* libusb bRequest */ 49 | #define BREQ_RESET 0x00 50 | #define BREQ_SET_LATENCY 0x09 51 | #define BREQ_SET_BITMODE 0x0B 52 | 53 | /* libusb wValue for assorted bRequest values */ 54 | #define WVAL_RESET_RESET 0x00 55 | #define WVAL_RESET_PURGE_RX 0x01 56 | #define WVAL_RESET_PURGE_TX 0x02 57 | #define WVAL_SET_BITMODE_MPSSE (0x0200 | \ 58 | FTDI_PIN_TCK | \ 59 | FTDI_PIN_TDI | \ 60 | FTDI_PIN_TMS) 61 | 62 | /* FTDI commands (first byte of bulk write transfer) */ 63 | #define FTDI_MPSSE_BIT_WRITE_TMS 0x40 64 | #define FTDI_MPSSE_BIT_READ_DATA 0x20 65 | #define FTDI_MPSSE_BIT_WRITE_DATA 0x10 66 | #define FTDI_MPSSE_BIT_LSB_FIRST 0x08 67 | #define FTDI_MPSSE_BIT_READ_ON_FALLING_EDGE 0x04 68 | #define FTDI_MPSSE_BIT_BIT_MODE 0x02 69 | #define FTDI_MPSSE_BIT_WRITE_ON_FALLING_EDGE 0x01 70 | #define FTDI_MPSSE_XFER_TDI_BYTES (FTDI_MPSSE_BIT_WRITE_DATA | \ 71 | FTDI_MPSSE_BIT_READ_DATA | \ 72 | FTDI_MPSSE_BIT_LSB_FIRST | \ 73 | FTDI_MPSSE_BIT_WRITE_ON_FALLING_EDGE) 74 | #define FTDI_MPSSE_XFER_TDI_BITS (FTDI_MPSSE_BIT_WRITE_DATA | \ 75 | FTDI_MPSSE_BIT_READ_DATA | \ 76 | FTDI_MPSSE_BIT_LSB_FIRST | \ 77 | FTDI_MPSSE_BIT_BIT_MODE | \ 78 | FTDI_MPSSE_BIT_WRITE_ON_FALLING_EDGE) 79 | #define FTDI_MPSSE_XFER_TMS_BITS (FTDI_MPSSE_BIT_WRITE_TMS | \ 80 | FTDI_MPSSE_BIT_READ_DATA | \ 81 | FTDI_MPSSE_BIT_LSB_FIRST | \ 82 | FTDI_MPSSE_BIT_BIT_MODE | \ 83 | FTDI_MPSSE_BIT_WRITE_ON_FALLING_EDGE) 84 | #define FTDI_SET_LOW_BYTE 0x80 85 | #define FTDI_ENABLE_LOOPBACK 0x84 86 | #define FTDI_DISABLE_LOOPBACK 0x85 87 | #define FTDI_SET_TCK_DIVISOR 0x86 88 | #define FTDI_DISABLE_TCK_PRESCALER 0x8A 89 | #define FTDI_DISABLE_3_PHASE_CLOCK 0x8D 90 | #define FTDI_ACK_BAD_COMMAND 0xFA 91 | 92 | /* FTDI I/O pin bits */ 93 | #define FTDI_PIN_TCK 0x1 94 | #define FTDI_PIN_TDI 0x2 95 | #define FTDI_PIN_TDO 0x4 96 | #define FTDI_PIN_TMS 0x8 97 | 98 | typedef struct usbInfo { 99 | /* 100 | * Diagnostics 101 | */ 102 | int quietFlag; 103 | int runtFlag; 104 | int loopback; 105 | int showUSB; 106 | int showXVC; 107 | unsigned int lockedSpeed; 108 | 109 | /* 110 | * Statistics 111 | */ 112 | int statisticsFlag; 113 | int largestShiftRequest; 114 | int largestWriteRequest; 115 | int largestWriteSent; 116 | int largestReadRequest; 117 | uint64_t shiftCount; 118 | uint64_t chunkCount; 119 | uint64_t bitCount; 120 | 121 | /* 122 | * Used to find matching device 123 | */ 124 | int vendorId; 125 | int productId; 126 | const char *serialNumber; 127 | 128 | /* 129 | * Matched device 130 | */ 131 | int deviceVendorId; 132 | int deviceProductId; 133 | char deviceVendorString[IDSTRING_CAPACITY]; 134 | char deviceProductString[IDSTRING_CAPACITY]; 135 | char deviceSerialString[IDSTRING_CAPACITY]; 136 | 137 | /* 138 | * Libusb hooks 139 | */ 140 | libusb_context *usb; 141 | libusb_device_handle *handle; 142 | int bInterfaceNumber; 143 | int isConnected; 144 | int termChar; 145 | unsigned char bTag; 146 | int bulkOutEndpointAddress; 147 | int bulkOutRequestSize; 148 | int bulkInEndpointAddress; 149 | int bulkInRequestSize; 150 | 151 | /* 152 | * FTDI info 153 | */ 154 | int ftdiJTAGindex; 155 | const char *gpioArgument; 156 | 157 | /* 158 | * I/O buffers 159 | */ 160 | unsigned char tmsBuf[XVC_BUFSIZE]; 161 | unsigned char tdiBuf[XVC_BUFSIZE]; 162 | unsigned char tdoBuf[XVC_BUFSIZE]; 163 | int txCount; 164 | unsigned char ioBuf[USB_BUFSIZE]; 165 | unsigned char rxBuf[USB_BUFSIZE]; 166 | unsigned char cmdBuf[USB_BUFSIZE]; 167 | } usbInfo; 168 | 169 | /************************************* MISC ***************************/ 170 | static void 171 | showBuf(const char *name, const unsigned char *buf, int numBytes) 172 | { 173 | int i; 174 | printf("%s%4d:", name, numBytes); 175 | if (numBytes > 40) numBytes = 40; 176 | for (i = 0 ; i < numBytes ; i++) printf(" %02X", buf[i]); 177 | printf("\n"); 178 | } 179 | 180 | static void 181 | badEOF(void) 182 | { 183 | fprintf(stderr, "Unexpected EOF!\n"); 184 | } 185 | 186 | static void 187 | badChar(void) 188 | { 189 | fprintf(stderr, "Unexpected character!\n"); 190 | } 191 | 192 | /* 193 | * Fetch 32 bit value from client 194 | */ 195 | static int 196 | fetch32(FILE *fp, uint32_t *value) 197 | { 198 | int i; 199 | uint32_t v; 200 | 201 | for (i = 0, v = 0 ; i < 32 ; i += 8) { 202 | int c = fgetc(fp); 203 | if (c == EOF) { 204 | badEOF(); 205 | return 0; 206 | } 207 | v |= c << i; 208 | } 209 | *value = v; 210 | return 1; 211 | } 212 | 213 | /************************************* USB ***************************/ 214 | static void 215 | getDeviceString(usbInfo *usb, int i, char *dest) 216 | { 217 | ssize_t n; 218 | 219 | n = libusb_get_string_descriptor_ascii(usb->handle, i, 220 | usb->ioBuf, sizeof usb->ioBuf); 221 | if (n < 0) { 222 | *dest = '\0'; 223 | return; 224 | } 225 | if (n >= IDSTRING_CAPACITY) 226 | n = IDSTRING_CAPACITY - 1; 227 | memcpy(dest, (char *)usb->ioBuf, n); 228 | *(dest + n) = '\0'; 229 | } 230 | 231 | static void 232 | getDeviceStrings(usbInfo *usb, struct libusb_device_descriptor *desc) 233 | { 234 | getDeviceString(usb, desc->iManufacturer, usb->deviceVendorString); 235 | getDeviceString(usb, desc->iProduct, usb->deviceProductString); 236 | getDeviceString(usb, desc->iSerialNumber, usb->deviceSerialString); 237 | } 238 | 239 | /* 240 | * Get endpoints 241 | */ 242 | static void 243 | getEndpoints(usbInfo *usb, const struct libusb_interface_descriptor *iface_desc) 244 | { 245 | int e; 246 | 247 | usb->bulkInEndpointAddress = 0; 248 | usb->bulkOutEndpointAddress = 0; 249 | for (e = 0 ; e < iface_desc->bNumEndpoints ; e++) { 250 | const struct libusb_endpoint_descriptor *ep = &iface_desc->endpoint[e]; 251 | if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == 252 | LIBUSB_TRANSFER_TYPE_BULK) { 253 | if ((ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == 254 | LIBUSB_ENDPOINT_IN) { 255 | if (usb->bulkInEndpointAddress != 0) { 256 | fprintf(stderr, "Too many input endpoints!\n"); 257 | exit(10); 258 | } 259 | usb->bulkInEndpointAddress = ep->bEndpointAddress; 260 | usb->bulkInRequestSize = ep->wMaxPacketSize; 261 | if ((size_t)usb->bulkInRequestSize > sizeof usb->ioBuf) { 262 | usb->bulkInRequestSize = sizeof usb->ioBuf; 263 | } 264 | } 265 | else { 266 | if (usb->bulkOutEndpointAddress != 0) { 267 | fprintf(stderr, "Too many output endpoints!\n"); 268 | exit(10); 269 | } 270 | usb->bulkOutEndpointAddress = ep->bEndpointAddress; 271 | usb->bulkOutRequestSize = ep->wMaxPacketSize; 272 | if ((size_t)usb->bulkOutRequestSize > sizeof usb->cmdBuf) { 273 | usb->bulkOutRequestSize = sizeof usb->cmdBuf; 274 | } 275 | } 276 | } 277 | } 278 | if (usb->bulkInEndpointAddress == 0) { 279 | fprintf(stderr, "No input endpoint!\n"); 280 | exit(10); 281 | } 282 | if (usb->bulkOutEndpointAddress == 0) { 283 | fprintf(stderr, "No output endpoint!\n"); 284 | exit(10); 285 | } 286 | } 287 | 288 | /* 289 | * Search the bus for a matching device 290 | */ 291 | static int 292 | findDevice(usbInfo *usb, libusb_device **list, int n) 293 | { 294 | int i; 295 | for (i = 0 ; i < n ; i++) { 296 | libusb_device *dev = list[i]; 297 | struct libusb_device_descriptor desc; 298 | struct libusb_config_descriptor *config; 299 | int productMatch = 0; 300 | int s = libusb_get_device_descriptor(dev, &desc); 301 | if (s != 0) { 302 | fprintf(stderr, "libusb_get_device_descriptor failed: %s", 303 | libusb_strerror(s)); 304 | return 0; 305 | } 306 | if (desc.bDeviceClass != LIBUSB_CLASS_PER_INTERFACE) 307 | continue; 308 | if (usb->productId < 0) { 309 | static const uint16_t validCodes[] = { 0x6010, /* FT2232H */ 310 | 0x6011, /* FT4232H */ 311 | 0x6014 /* FT232H */ 312 | }; 313 | int nCodes = sizeof validCodes / sizeof validCodes[0]; 314 | int p; 315 | for (p = 0 ; p < nCodes ; p++) { 316 | if (desc.idProduct == validCodes[p]) { 317 | productMatch = 1; 318 | break; 319 | } 320 | } 321 | } 322 | else if (usb->productId == desc.idProduct) { 323 | productMatch = 1; 324 | } 325 | if ((usb->vendorId != desc.idVendor) || !productMatch) { 326 | continue; 327 | } 328 | if ((libusb_get_active_config_descriptor(dev, &config) < 0) 329 | && (libusb_get_config_descriptor(dev, 0, &config) < 0)) { 330 | fprintf(stderr, 331 | "Can't get vendor %04X product %04X configuration.\n", 332 | desc.idVendor, desc.idProduct); 333 | continue; 334 | } 335 | if (config == NULL) { 336 | continue; 337 | } 338 | if (config->bNumInterfaces >= usb->ftdiJTAGindex) { 339 | s = libusb_open(dev, &usb->handle); 340 | if (s == 0) { 341 | const struct libusb_interface *iface = 342 | &config->interface[usb->ftdiJTAGindex-1]; 343 | const struct libusb_interface_descriptor *iface_desc = 344 | &iface->altsetting[0]; 345 | usb->bInterfaceNumber = iface_desc->bInterfaceNumber; 346 | usb->deviceVendorId = desc.idVendor; 347 | usb->deviceProductId = desc.idProduct; 348 | getDeviceStrings(usb, &desc); 349 | if ((usb->serialNumber == NULL) 350 | || (strcmp(usb->serialNumber, 351 | usb->deviceSerialString) == 0)) { 352 | getEndpoints(usb, iface_desc); 353 | libusb_free_config_descriptor(config); 354 | usb->productId = desc.idProduct; 355 | return 1; 356 | } 357 | libusb_close(usb->handle); 358 | } 359 | else { 360 | fprintf(stderr, "libusb_open failed: %s\n", 361 | libusb_strerror(s)); 362 | exit(1); 363 | } 364 | } 365 | libusb_free_config_descriptor(config); 366 | } 367 | return 0; 368 | } 369 | 370 | static int 371 | usbControl(usbInfo *usb, int bmRequestType, int bRequest, int wValue) 372 | { 373 | int c; 374 | if (usb->showUSB) { 375 | printf("usbControl bmRequestType:%02X bRequest:%02X wValue:%04X\n", 376 | bmRequestType, bRequest, wValue); 377 | } 378 | c = libusb_control_transfer(usb->handle, bmRequestType, bRequest, wValue, 379 | usb->ftdiJTAGindex, NULL, 0, 1000); 380 | if (c != 0) { 381 | fprintf(stderr, "usb_control_transfer failed: %s\n",libusb_strerror(c)); 382 | exit(1); 383 | } 384 | return 1; 385 | } 386 | 387 | static int 388 | usbWriteData(usbInfo *usb, unsigned char *buf, int nSend) 389 | { 390 | int nSent, s; 391 | 392 | if (usb->showUSB) { 393 | showBuf("Tx", buf, nSend); 394 | } 395 | if (nSend > usb->largestWriteRequest) { 396 | usb->largestWriteRequest = nSend; 397 | } 398 | while (nSend) { 399 | s = libusb_bulk_transfer(usb->handle, usb->bulkOutEndpointAddress, buf, 400 | nSend, &nSent, 10000); 401 | if (s) { 402 | fprintf(stderr, "Bulk write (%d) failed: %s\n", nSend, 403 | libusb_strerror(s)); 404 | exit(1); 405 | } 406 | nSend -= nSent; 407 | buf += nSent; 408 | if (nSent > usb->largestWriteSent) { 409 | usb->largestWriteSent = nSent; 410 | } 411 | } 412 | return 1; 413 | } 414 | 415 | static int 416 | usbReadData(usbInfo *usb, unsigned char *buf, int nWant) 417 | { 418 | int nWanted = nWant; 419 | const unsigned char *base = buf; 420 | 421 | if (nWant > usb->largestReadRequest) { 422 | usb->largestReadRequest = nWant; 423 | if ((nWant+2) > usb->bulkInRequestSize) { 424 | fprintf(stderr, "usbReadData requested %d, limit is %d.\n", 425 | nWant+2, usb->bulkInRequestSize); 426 | exit(1); 427 | } 428 | } 429 | while (nWant) { 430 | int nRecv, s; 431 | const unsigned char *src = usb->ioBuf; 432 | s = libusb_bulk_transfer(usb->handle, usb->bulkInEndpointAddress, 433 | usb->ioBuf, nWant+2, &nRecv, 5000); 434 | if (s) { 435 | fprintf(stderr, "Bulk read failed: %s\n", libusb_strerror(s)); 436 | exit(1); 437 | } 438 | if (nRecv <= 2) { 439 | if (usb->runtFlag) { 440 | fprintf(stderr, "wanted:%d want:%d got:%d",nWanted,nWant,nRecv); 441 | if (nRecv >= 1) { 442 | fprintf(stderr, " [%02X", src[0]); 443 | if (nRecv >= 2) { 444 | fprintf(stderr, " %02X", src[1]); 445 | } 446 | fprintf(stderr, "]"); 447 | } 448 | fprintf(stderr, "\n"); 449 | } 450 | continue; 451 | } 452 | else { 453 | /* Skip FTDI status bytes */ 454 | nRecv -= 2; 455 | src += 2; 456 | } 457 | if (nRecv > nWant) nRecv = nWant; 458 | memcpy(buf, src, nRecv); 459 | nWant -= nRecv; 460 | buf += nRecv; 461 | } 462 | if (usb->showUSB) { 463 | showBuf("Rx", base, nWanted); 464 | } 465 | return 1; 466 | } 467 | 468 | /************************************* FTDI/JTAG ***************************/ 469 | static int 470 | divisorForFrequency(unsigned int frequency) 471 | { 472 | unsigned int divisor; 473 | unsigned int actual; 474 | double r; 475 | static unsigned int warned = ~0; 476 | 477 | if (frequency <= 0) frequency = 1; 478 | divisor = ((FTDI_CLOCK_RATE / 2) + (frequency - 1)) / frequency; 479 | if (divisor >= 0x10000) { 480 | divisor = 0x10000; 481 | } 482 | if (divisor < 1) { 483 | divisor = 1; 484 | } 485 | actual = FTDI_CLOCK_RATE / (2 * divisor); 486 | r = (double)frequency / actual; 487 | if (warned != actual) { 488 | warned = actual; 489 | if ((r < 0.999) || (r > 1.001)) { 490 | fprintf(stderr, "Warning -- %d Hz clock requested, %d Hz actual\n", 491 | frequency, actual); 492 | } 493 | if (actual < 500000) { 494 | fprintf(stderr, "Warning -- %d Hz clock is a slow choice.\n", 495 | actual); 496 | } 497 | } 498 | return divisor; 499 | } 500 | 501 | static int 502 | ftdiSetClockSpeed(usbInfo *usb, unsigned int frequency) 503 | { 504 | unsigned int count; 505 | if (usb->lockedSpeed) { 506 | frequency = usb->lockedSpeed; 507 | } 508 | count = divisorForFrequency(frequency) - 1; 509 | usb->ioBuf[0] = FTDI_DISABLE_TCK_PRESCALER; 510 | usb->ioBuf[1] = FTDI_SET_TCK_DIVISOR; 511 | usb->ioBuf[2] = count; 512 | usb->ioBuf[3] = count >> 8; 513 | return usbWriteData(usb, usb->ioBuf, 4); 514 | } 515 | 516 | static int 517 | ftdiGPIO(usbInfo *usb) 518 | { 519 | unsigned long value; 520 | unsigned int direction; 521 | const char *str = usb->gpioArgument; 522 | char *endp; 523 | static const struct timespec ms100 = { .tv_sec = 0, .tv_nsec = 100000000 }; 524 | 525 | usb->ioBuf[0] = FTDI_SET_LOW_BYTE; 526 | for (;;) { 527 | value = strtol(str, &endp, 16); 528 | if ((endp == str) || ((*endp != '\0') && (*endp != ':'))) { 529 | break; 530 | } 531 | str = endp + 1; 532 | if (value > 0xFF) { 533 | break; 534 | } 535 | direction = value >> 4; 536 | value &= 0xF; 537 | usb->ioBuf[1] = (value << 4) | FTDI_PIN_TMS; 538 | usb->ioBuf[2] = (direction << 4) | 539 | FTDI_PIN_TMS | FTDI_PIN_TDI | FTDI_PIN_TCK; 540 | if (!usbWriteData(usb, usb->ioBuf, 3)) { 541 | break; 542 | } 543 | if (*endp == '\0') { 544 | return 1; 545 | } 546 | nanosleep(&ms100, NULL); 547 | } 548 | return 0; 549 | } 550 | 551 | static int 552 | ftdiInit(usbInfo *usb) 553 | { 554 | static unsigned char startup[] = { 555 | FTDI_DISABLE_LOOPBACK, 556 | FTDI_DISABLE_3_PHASE_CLOCK, 557 | FTDI_SET_LOW_BYTE, 558 | FTDI_PIN_TMS, 559 | FTDI_PIN_TMS | FTDI_PIN_TDI | FTDI_PIN_TCK 560 | }; 561 | if (!usbControl(usb, BMREQTYPE_OUT, BREQ_RESET, WVAL_RESET_RESET) 562 | || !usbControl(usb, BMREQTYPE_OUT, BREQ_SET_BITMODE,WVAL_SET_BITMODE_MPSSE) 563 | || !usbControl(usb, BMREQTYPE_OUT, BREQ_SET_LATENCY, 2) 564 | || !usbControl(usb, BMREQTYPE_OUT, BREQ_RESET, WVAL_RESET_PURGE_TX) 565 | || !usbControl(usb, BMREQTYPE_OUT, BREQ_RESET, WVAL_RESET_PURGE_RX) 566 | || !ftdiSetClockSpeed(usb, 10000000) 567 | || !usbWriteData(usb, startup, sizeof startup)) { 568 | return 0; 569 | } 570 | if (usb->gpioArgument && !ftdiGPIO(usb)) { 571 | fprintf(stderr, "Bad -g direction:value[:value...]\n"); 572 | return 0; 573 | } 574 | return 1; 575 | } 576 | 577 | /************************************* XVC ***************************/ 578 | static void 579 | cmdByte(usbInfo *usb, int byte) 580 | { 581 | if (usb->txCount == USB_BUFSIZE) { 582 | fprintf(stderr, "USB TX OVERFLOW!\n"); 583 | exit(4); 584 | } 585 | usb->ioBuf[usb->txCount++] = byte; 586 | } 587 | 588 | /* 589 | * The USB/JTAG chip can't shift data to TMS and TDI simultaneously 590 | * so switch between TMS and TDI shift commands as necessary. 591 | * Break into chunks small enough to fit in single packet. 592 | */ 593 | static int 594 | shiftChunks(usbInfo *usb, int nBits) 595 | { 596 | int iBit = 0x01, iIndex = 0; 597 | int cmdBit, cmdIndex, cmdBitcount; 598 | int tmsBit, tmsBits, tmsState; 599 | int rxBit, rxIndex; 600 | int tdoBit = 0x01, tdoIndex = 0; 601 | unsigned short rxBitcounts[USB_BUFSIZE/3]; 602 | 603 | if (usb->loopback) { 604 | cmdByte(usb, FTDI_ENABLE_LOOPBACK); 605 | } 606 | while (nBits) { 607 | int rxBytesWanted = 0; 608 | int rxBitcountIndex = 0; 609 | usb->txCount = 0; 610 | usb->chunkCount++; 611 | do { 612 | /* 613 | * Stash TMS bits until bit limit reached or TDI would change state 614 | */ 615 | int tdiFirstState = ((usb->tdiBuf[iIndex] & iBit) != 0); 616 | cmdBitcount = 0; 617 | cmdBit = 0x01; 618 | tmsBits = 0; 619 | do { 620 | tmsBit = (usb->tmsBuf[iIndex] & iBit) ? cmdBit : 0; 621 | tmsBits |= tmsBit; 622 | if (iBit == 0x80) { 623 | iBit = 0x01; 624 | iIndex++; 625 | } 626 | else { 627 | iBit <<= 1; 628 | } 629 | cmdBitcount++; 630 | cmdBit <<= 1; 631 | } while ((cmdBitcount < 6) && (cmdBitcount < nBits) 632 | && (((usb->tdiBuf[iIndex] & iBit) != 0) == tdiFirstState)); 633 | 634 | /* 635 | * Duplicate the final TMS bit so the TMS pin holds 636 | * its value for subsequent TDI shift commands. 637 | * This is why the bit limit above is 6 and not 7 since 638 | * we need space to hold the copy of the final bit. 639 | */ 640 | tmsBits |= (tmsBit << 1); 641 | tmsState = (tmsBit != 0); 642 | 643 | /* 644 | * Send the TMS bits and TDI value. 645 | */ 646 | cmdByte(usb, FTDI_MPSSE_XFER_TMS_BITS); 647 | cmdByte(usb, cmdBitcount - 1); 648 | cmdByte(usb, (tdiFirstState << 7) | tmsBits); 649 | rxBitcounts[rxBitcountIndex++] = cmdBitcount; 650 | rxBytesWanted++; 651 | nBits -= cmdBitcount; 652 | 653 | /* 654 | * Stash TDI bits until bit limit reached 655 | * or TMS change of state 656 | * or transmitter buffer capacity reached. 657 | */ 658 | cmdBitcount = 0; 659 | cmdIndex = 0; 660 | cmdBit = 0x01; 661 | usb->cmdBuf[0] = 0; 662 | while ((nBits != 0) 663 | && (((usb->tmsBuf[iIndex] & iBit) != 0) == tmsState) 664 | && ((usb->txCount+(cmdBitcount/8))<(usb->bulkOutRequestSize-5))){ 665 | if (usb->tdiBuf[iIndex] & iBit) { 666 | usb->cmdBuf[cmdIndex] |= cmdBit; 667 | } 668 | if (cmdBit == 0x80) { 669 | cmdBit = 0x01; 670 | cmdIndex++; 671 | usb->cmdBuf[cmdIndex] = 0; 672 | } 673 | else { 674 | cmdBit <<= 1; 675 | } 676 | if (iBit == 0x80) { 677 | iBit = 0x01; 678 | iIndex++; 679 | } 680 | else { 681 | iBit <<= 1; 682 | } 683 | cmdBitcount++; 684 | nBits--; 685 | } 686 | 687 | /* 688 | * Send stashed TDI bits 689 | */ 690 | if (cmdBitcount > 0) { 691 | int cmdBytes = cmdBitcount / 8; 692 | rxBitcounts[rxBitcountIndex++] = cmdBitcount; 693 | if (cmdBitcount >= 8) { 694 | int i; 695 | rxBytesWanted += cmdBytes; 696 | cmdBitcount -= cmdBytes * 8; 697 | i = cmdBytes - 1; 698 | cmdByte(usb, FTDI_MPSSE_XFER_TDI_BYTES); 699 | cmdByte(usb, i); 700 | cmdByte(usb, i >> 8); 701 | for (i = 0 ; i < cmdBytes ; i++) { 702 | cmdByte(usb, usb->cmdBuf[i]); 703 | } 704 | } 705 | if (cmdBitcount) { 706 | rxBytesWanted++; 707 | cmdByte(usb, FTDI_MPSSE_XFER_TDI_BITS); 708 | cmdByte(usb, cmdBitcount - 1); 709 | cmdByte(usb, usb->cmdBuf[cmdBytes]); 710 | } 711 | } 712 | } while ((nBits != 0) 713 | && ((usb->txCount+(cmdBitcount/8))<(usb->bulkOutRequestSize-6))); 714 | 715 | /* 716 | * Shift 717 | */ 718 | if (!usbWriteData(usb, usb->ioBuf, usb->txCount) 719 | || !usbReadData(usb, usb->rxBuf, rxBytesWanted)) { 720 | return 0; 721 | } 722 | 723 | /* 724 | * Process received data 725 | */ 726 | rxIndex = 0; 727 | for (int i = 0 ; i < rxBitcountIndex ; i++) { 728 | int rxBitcount = rxBitcounts[i]; 729 | if (rxBitcount < 8) { 730 | rxBit = 0x1 << (8 - rxBitcount); 731 | } 732 | else { 733 | rxBit = 0x01; 734 | } 735 | while (rxBitcount--) { 736 | if (tdoBit == 0x1) { 737 | usb->tdoBuf[tdoIndex] = 0; 738 | } 739 | if (usb->rxBuf[rxIndex] & rxBit) { 740 | usb->tdoBuf[tdoIndex] |= tdoBit; 741 | } 742 | if (rxBit == 0x80) { 743 | if (rxBitcount < 8) { 744 | rxBit = 0x1 << (8 - rxBitcount); 745 | } 746 | else { 747 | rxBit = 0x01; 748 | } 749 | rxIndex++; 750 | } 751 | else { 752 | rxBit <<= 1; 753 | } 754 | if (tdoBit == 0x80) { 755 | tdoBit = 0x01; 756 | tdoIndex++; 757 | } 758 | else { 759 | tdoBit <<= 1; 760 | } 761 | } 762 | } 763 | if (rxIndex != rxBytesWanted) { 764 | printf("Warning -- consumed %d but supplied %d\n", rxIndex, 765 | rxBytesWanted); 766 | } 767 | } 768 | return 1; 769 | } 770 | 771 | /* 772 | * Shift a client packet set of bits 773 | */ 774 | static int 775 | shift(usbInfo *usb, FILE *fp) 776 | { 777 | uint32_t nBits, nBytes; 778 | 779 | if (!fetch32(fp, &nBits)) { 780 | return 0; 781 | } 782 | if (nBits > (unsigned int)usb->largestShiftRequest) { 783 | usb->largestShiftRequest = nBits; 784 | } 785 | usb->bitCount += nBits; 786 | usb->shiftCount++; 787 | nBytes = (nBits + 7) / 8; 788 | if (usb->showXVC) { 789 | printf("shift:%d\n", (int)nBits); 790 | } 791 | if (nBytes > XVC_BUFSIZE) { 792 | fprintf(stderr, "Client requested %u, max is %u\n", nBytes,XVC_BUFSIZE); 793 | exit(1); 794 | } 795 | if ((fread(usb->tmsBuf, 1, nBytes, fp) != nBytes) 796 | || (fread(usb->tdiBuf, 1, nBytes, fp) != nBytes)) { 797 | return 0; 798 | } 799 | if (usb->showXVC) { 800 | showBuf("TMS", usb->tmsBuf, nBytes); 801 | showBuf("TDI", usb->tdiBuf, nBytes); 802 | } 803 | if (!shiftChunks(usb, nBits)) { 804 | return 0; 805 | } 806 | if (usb->showXVC) { 807 | showBuf("TDO", usb->tdoBuf, nBytes); 808 | } 809 | if (usb->loopback) { 810 | if (memcmp(usb->tdiBuf, usb->tdoBuf, nBytes)) { 811 | printf("Loopback failed.\n"); 812 | } 813 | } 814 | return nBytes; 815 | } 816 | 817 | /* 818 | * Fetch a known string 819 | */ 820 | static int 821 | matchInput(FILE *fp, const char *str) 822 | { 823 | while (*str) { 824 | int c = fgetc(fp); 825 | if (c == EOF) { 826 | badEOF(); 827 | return 0; 828 | } 829 | if (c != *str) { 830 | fprintf(stderr, "Expected 0x%2x, got 0x%2x\n", *str, c); 831 | return 0; 832 | } 833 | str++; 834 | } 835 | return 1; 836 | } 837 | 838 | static int 839 | reply(int fd, const unsigned char *buf, int len) 840 | { 841 | if (write(fd, buf, len) != len) { 842 | fprintf(stderr, "reply failed: %s\n", strerror(errno)); 843 | return 0; 844 | } 845 | return 1; 846 | } 847 | 848 | /* 849 | * Return a 32 bit value 850 | */ 851 | static int 852 | reply32(int fd, uint32_t value) 853 | { 854 | int i; 855 | unsigned char cbuf[4]; 856 | 857 | for (i = 0 ; i < 4 ; i++) { 858 | cbuf[i] = value; 859 | value >>= 8; 860 | } 861 | return reply(fd, cbuf, 4); 862 | } 863 | 864 | /* 865 | * Read and process commands 866 | */ 867 | static void 868 | processCommands(FILE *fp, int fd, usbInfo *usb) 869 | { 870 | int c; 871 | 872 | for (;;) { 873 | switch(c = fgetc(fp)) { 874 | case 's': 875 | switch(c = fgetc(fp)) { 876 | case 'e': 877 | { 878 | uint32_t num; 879 | int frequency; 880 | if (!matchInput(fp, "ttck:")) return; 881 | if (!fetch32(fp, &num)) return; 882 | frequency = 1000000000 / num; 883 | if (usb->showXVC) { 884 | printf("settck:%d (%d Hz)\n", (int)num, frequency); 885 | } 886 | if (!ftdiSetClockSpeed(usb, frequency)) return; 887 | if (!reply32(fd, num)) return; 888 | } 889 | break; 890 | 891 | case 'h': 892 | { 893 | int nBytes; 894 | if (!matchInput(fp, "ift:")) return; 895 | nBytes = shift(usb, fp); 896 | if ((nBytes <= 0) || !reply(fd, usb->tdoBuf, nBytes)) { 897 | return; 898 | } 899 | } 900 | break; 901 | 902 | default: 903 | if (usb->showXVC) { 904 | printf("Bad second char 0x%02x\n", c); 905 | } 906 | badChar(); 907 | return; 908 | } 909 | break; 910 | 911 | case 'g': 912 | if (matchInput(fp, "etinfo:")) { 913 | char cBuf[40]; 914 | int len;; 915 | if (usb->showXVC) { 916 | printf("getinfo:\n"); 917 | } 918 | len = sprintf(cBuf, "xvcServer_v1.0:%u\n", XVC_BUFSIZE); 919 | if (reply(fd, (unsigned char *)cBuf, len)) { 920 | break; 921 | } 922 | } 923 | return; 924 | 925 | case EOF: 926 | return; 927 | 928 | default: 929 | if (usb->showXVC) { 930 | printf("Bad initial char 0x%02x\n", c); 931 | } 932 | badChar(); 933 | return; 934 | } 935 | } 936 | } 937 | 938 | static int 939 | createSocket(const char *interface, int port) 940 | { 941 | int s, o; 942 | struct sockaddr_in myAddr; 943 | 944 | s = socket (AF_INET, SOCK_STREAM, 0); 945 | if (s < 0) { 946 | return -1; 947 | } 948 | o = 1; 949 | if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &o, sizeof o) < 0) { 950 | return -1; 951 | } 952 | memset (&myAddr, '\0', sizeof myAddr); 953 | myAddr.sin_family = AF_INET; 954 | if (inet_pton(AF_INET, interface, &myAddr.sin_addr) != 1) { 955 | fprintf(stderr, "Bad address \"%s\"\n", interface); 956 | return -1; 957 | } 958 | myAddr.sin_port = htons(port); 959 | if (bind (s, (struct sockaddr *)&myAddr, sizeof myAddr) < 0) { 960 | fprintf(stderr, "Bind() failed: %s\n", strerror (errno)); 961 | return -1; 962 | } 963 | if (listen (s, 1) < 0) { 964 | fprintf(stderr, "Listen() failed: %s\n", strerror (errno)); 965 | return -1; 966 | } 967 | return s; 968 | } 969 | 970 | /************************************* Application ***************************/ 971 | static int 972 | connectUSB(usbInfo *usb) 973 | { 974 | libusb_device **list; 975 | ssize_t n; 976 | int s; 977 | 978 | n = libusb_get_device_list(usb->usb, &list); 979 | if (n < 0) { 980 | fprintf(stderr, "libusb_get_device_list failed: %s", libusb_strerror((int)n)); 981 | return 0; 982 | } 983 | s = findDevice(usb, list, n); 984 | libusb_free_device_list(list, 1); 985 | if (s) { 986 | s = libusb_kernel_driver_active(usb->handle, usb->bInterfaceNumber); 987 | if (s < 0) { 988 | fprintf(stderr, "libusb_kernel_driver_active() failed: %s\n", libusb_strerror(s)); 989 | } 990 | else if (s) { 991 | s = libusb_detach_kernel_driver(usb->handle, usb->bInterfaceNumber); 992 | if (s) { 993 | fprintf(stderr, "libusb_detach_kernel_driver() failed: %s\n", libusb_strerror(s)); 994 | } 995 | } 996 | s = libusb_claim_interface(usb->handle, usb->bInterfaceNumber); 997 | if (s) { 998 | libusb_close(usb->handle); 999 | fprintf(stderr, "libusb_claim_interface failed: %s\n", libusb_strerror(s)); 1000 | return 0; 1001 | } 1002 | if (usb->showUSB || !usb->quietFlag) { 1003 | printf(" Vendor (%04X): \"%s\"\n", usb->vendorId, usb->deviceVendorString); 1004 | printf("Product (%04X): \"%s\"\n", usb->productId, usb->deviceProductString); 1005 | printf(" Serial: \"%s\"\n", usb->deviceSerialString); 1006 | fflush(stdout); 1007 | } 1008 | } 1009 | else { 1010 | fprintf(stderr, "Can't find USB device.\n"); 1011 | return 0; 1012 | } 1013 | if (!ftdiInit(usb)) { 1014 | return 0; 1015 | } 1016 | return 1; 1017 | } 1018 | 1019 | static void 1020 | usage(char *name) 1021 | { 1022 | fprintf(stderr, "Usage: %s [-a address] [-p port] " 1023 | "[-d vendor:product[:[serial]]] [-g direction_value[:direction_value...]] " 1024 | "[-c frequency] [-q] [-B] [-L] [-R] [-S] [-U] [-X]\n", name); 1025 | exit(2); 1026 | } 1027 | 1028 | static int 1029 | convertInt(const char *str) 1030 | { 1031 | long v; 1032 | char *endp; 1033 | v = strtol(str, &endp, 0); 1034 | if ((endp == str) || (*endp != '\0')) { 1035 | fprintf(stderr, "Bad integer argument \"%s\"\n", str); 1036 | exit(2); 1037 | } 1038 | return v; 1039 | } 1040 | 1041 | static void 1042 | deviceConfig(usbInfo *usb, const char *str) 1043 | { 1044 | unsigned long vendor, product; 1045 | char *endp; 1046 | vendor = strtol(str, &endp, 16); 1047 | if ((endp != str) && (*endp == ':')) { 1048 | str = endp + 1; 1049 | product = strtol(str, &endp, 16); 1050 | if ((endp != str) 1051 | && ((*endp == '\0') || (*endp == ':')) 1052 | && (vendor <= 0xFFFF) 1053 | && (product <= 0xFFFF)) { 1054 | if (*endp == ':') { 1055 | usb->serialNumber = endp + 1; 1056 | } 1057 | usb->vendorId = vendor; 1058 | usb->productId = product; 1059 | return; 1060 | } 1061 | } 1062 | fprintf(stderr, "Bad -d vendor:product[:[serial]]\n"); 1063 | exit(2); 1064 | } 1065 | 1066 | static int 1067 | clockSpeed(const char *str) 1068 | { 1069 | double frequency; 1070 | char *endp; 1071 | frequency = strtod(str, &endp); 1072 | if ((endp == str) 1073 | || ((*endp != '\0') && (*endp != 'M') && (*endp != 'k')) 1074 | || ((*endp != '\0') && (*(endp+1) != '\0'))) { 1075 | fprintf(stderr, "Bad clock frequency argument.\n"); 1076 | exit(2); 1077 | } 1078 | if (*endp == 'M') frequency *= 1000000; 1079 | if (*endp == 'k') frequency *= 1000; 1080 | if (frequency >= INT_MAX) frequency = INT_MAX; 1081 | if (frequency <= 0) frequency = 1; 1082 | divisorForFrequency(frequency); 1083 | return frequency; 1084 | } 1085 | 1086 | int 1087 | main(int argc, char **argv) 1088 | { 1089 | int c; 1090 | const char *bindAddress = "127.0.0.1"; 1091 | int port = 2542; 1092 | int s; 1093 | char farName[100]; 1094 | static usbInfo usbWorkspace = { 1095 | .vendorId = 0x0403, 1096 | .productId = -1, 1097 | .ftdiJTAGindex = 1 1098 | }; 1099 | usbInfo *usb = &usbWorkspace; 1100 | 1101 | while ((c = getopt(argc, argv, "a:b:c:d:g:hp:qBLRSUX")) >= 0) { 1102 | switch(c) { 1103 | case 'a': bindAddress = optarg; break; 1104 | case 'c': usb->lockedSpeed = clockSpeed(optarg); break; 1105 | case 'd': deviceConfig(usb, optarg); break; 1106 | case 'g': usb->gpioArgument = optarg; break; 1107 | case 'h': usage(argv[0]); break; 1108 | case 'p': port = convertInt(optarg); break; 1109 | case 'q': usb->quietFlag = 1; break; 1110 | case 'u': usb->showUSB = 1; break; 1111 | case 'x': usb->showXVC = 1; break; 1112 | case 'B': usb->ftdiJTAGindex = 2; break; 1113 | case 'L': usb->loopback = 1; break; 1114 | case 'R': usb->runtFlag = 1; break; 1115 | case 'S': usb->statisticsFlag = 1; break; 1116 | case 'U': usb->showUSB = 1; break; 1117 | case 'X': usb->showXVC = 1; break; 1118 | default: usage(argv[0]); 1119 | } 1120 | } 1121 | if (optind != argc) { 1122 | fprintf(stderr, "Unexpected argument.\n"); 1123 | usage(argv[0]); 1124 | } 1125 | s = libusb_init(&usb->usb); 1126 | if (!connectUSB(usb)) { 1127 | exit(1); 1128 | } 1129 | if (s != 0) { 1130 | fprintf(stderr, "libusb_init() failed: %s\n", libusb_strerror(s)); 1131 | return 0; 1132 | } 1133 | if ((s = createSocket(bindAddress, port)) < 0) { 1134 | exit(1); 1135 | } 1136 | for (;;) { 1137 | struct sockaddr_in farAddr; 1138 | socklen_t addrlen = sizeof farAddr; 1139 | FILE *fp; 1140 | 1141 | int fd = accept(s, (struct sockaddr *)&farAddr, &addrlen); 1142 | if (fd < 0) { 1143 | fprintf(stderr, "Can't accept connection: %s\n", strerror (errno)); 1144 | exit(1); 1145 | } 1146 | if ((usb->handle == NULL) && !connectUSB(usb)) { 1147 | exit(1); 1148 | } 1149 | usb->shiftCount = 0; 1150 | usb->chunkCount = 0; 1151 | usb->bitCount = 0; 1152 | if (!usb->quietFlag) { 1153 | inet_ntop(farAddr.sin_family, &(farAddr.sin_addr), farName, sizeof farName); 1154 | printf("Connect %s\n", farName); 1155 | } 1156 | fp = fdopen(fd, "r"); 1157 | if (fp == NULL) { 1158 | fprintf(stderr, "fdopen failed: %s\n", strerror(errno)); 1159 | close(fd); 1160 | exit(2); 1161 | } 1162 | else { 1163 | processCommands(fp, fd, usb); 1164 | fclose(fp); /* Closes underlying socket, too */ 1165 | } 1166 | if (!usb->quietFlag) { 1167 | printf("Disconnect %s\n", farName); 1168 | } 1169 | if (usb->statisticsFlag) { 1170 | printf(" Shifts: %" PRIu64 "\n", usb->shiftCount); 1171 | printf(" Chunks: %" PRIu64 "\n", usb->chunkCount); 1172 | printf(" Bits: %" PRIu64 "\n", usb->bitCount); 1173 | printf(" Largest shift request: %d\n", usb->largestShiftRequest); 1174 | printf(" Largest write request: %d\n", usb->largestWriteRequest); 1175 | printf("Largest write transfer: %d\n", usb->largestWriteSent); 1176 | printf(" Largest read request: %d\n", usb->largestReadRequest); 1177 | } 1178 | libusb_close(usb->handle); 1179 | usb->handle = NULL; 1180 | } 1181 | } 1182 | --------------------------------------------------------------------------------