diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c834a2c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +src/*.bin +src/*.elf +src/*.d +src/*.o +src/*.map diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a2b2549 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "unicore-mx"] + path = unicore-mx + url = https://github.com/insane-adding-machines/unicore-mx diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f657149 --- /dev/null +++ b/Makefile @@ -0,0 +1,61 @@ +OBJS := src/dirtyjtag.o src/jtag.o src/usb.o src/delay.o src/cmd.o + +PREFIX ?= arm-none-eabi +TARGETS := stm32/f1 +DEFS += -DSTM32F1 +ARCH_FLAGS := -mthumb -mcpu=cortex-m3 -msoft-float -mfix-cortex-m3-ldrd +LD_SCRIPT := dirtyjtag.ld + +UCMX_DIR := $(realpath unicore-mx) +UCMX_INCLUDE_DIR := $(UCMX_DIR)/include +UCMX_LIB_DIR := $(UCMX_DIR)/lib + +CFLAGS = -g +CFLAGS += -Wall -Wextra -Werror +CFLAGS += -fno-common -ffunction-sections -fdata-sections +CFLAGS += -std=gnu11 + +CPPFLAGS = -MD -g +CPPFLAGS += -Wall -Wundef +CPPFLAGS += -I$(UCMX_INCLUDE_DIR) $(DEFS) + +LDFLAGS += --static -nostartfiles +LDFLAGS += -L"$(UCMX_LIB_DIR)" +LDFLAGS += -T$(LD_SCRIPT) +LDFLAGS += -Wl,-Map=$(*).map +LDFLAGS += -Wl,--gc-sections # Remove deadcode + +LDLIBS += -lucmx_stm32f1 +LDLIBS += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group + +CC := $(PREFIX)-gcc +LD := $(PREFIX)-gcc +AR := $(PREFIX)-ar +AS := $(PREFIX)-as +OBJCOPY := $(PREFIX)-objcopy + +all: dirtyjtag + +clean: dirtyjtag-clean ucmx-clean + +dirtyjtag: ucmx src/dirtyjtag.elf src/dirtyjtag.bin + +dirtyjtag-clean: + $(Q)$(RM) src/*.d src/*.o src/*.map src/*.bin src/*.elf + +ucmx: + $(Q)$(MAKE) -C $(UCMX_DIR) + +ucmx-clean: + $(Q)$(MAKE) -C $(UCMX_DIR) clean + +%.bin: %.elf + $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).bin + +%.elf %.map: $(OBJS) $(LD_SCRIPT) + $(Q)$(LD) $(LDFLAGS) $(ARCH_FLAGS) $(OBJS) $(LDLIBS) -o $(*).elf + +%.o: %.c + $(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $@ -c $< + +.PHONY: clean dirtyjtag dirtyjtag-clean ucmx ucmx-clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..dff1b40 --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +# DirtyJTAG + +DirtyJTAG is a JTAG adapter firmware for $2 STM32 development boards ("blue pill"/"black pill" STM32F103-based ARM boards). The DirtyJTAG project was created to find an alternative to the obsolete (but cheap) LPT Wiggler cables, and expensive USB JTAG probes. + +DirtyJTAG is dirty and dirt cheap, but is not fast nor a perfect implementation of the JTAG protocol. Yet it is a bit less than 500 sloccount lines, therefore it is easily understandable and hackable. + +DirtyJTAG is currently only supported by urjtag through custom patches (I hope getting mainline support some time soon though). + +If you prefer OpenOCD to UrJTAG, I suggest using Zoobab's fork of Versaloon firmware, which is available [on his GitHub repository](https://github.com/zoobab/versaloon). + +## How to flash DirtyJTAG on a "Blue Pill" board + +Download a pre-built version of the firmware (available [on the release page](https://github.com/jeanthomas/dirtyjtag/releases)) or build the firmware yourself (instructions provided below). Install [stlink](https://github.com/texane/stlink), then use this command : + +``` +st-flash write /path/to/dirtyjtag.bin 0x8000000 +``` + +## Pinout + +| STM32 | JTAG | +|-------|------| +| PA0 | TDI | +| PA1 | TDO | +| PA2 | TCK | +| PA3 | TMS | +| PA4 | TRST | +| PA5 | SRST | + +Pin definition can be modified in `src/jtag.c`. + +## How to patch urjtag for DirtyJTAG support + +See [urjtag4dirtyjtag](https://github.com/jeanthom/urjtag4dirtyjtag) which is a patched version of urjtag with DirtyJTAG support. I'll try to push those changes upstream some time soon. + +## DirtyJTAG compilation + +In order to compile DirtyJTAG, you will need the following software : + + * git + * ARM toolchain (I'm using Fedora's `arm-none-eabi-gcc-cs`/`arm-none-eabi-newline` packages) + * make + +Clone this repository : + +``` +git clone https://github.com/jeanthom/dirtyjtag +cd dirtyjtag +``` + +Then download unicore-mx : + +``` +git submodule init +git submodule update +``` + +Then you can build the firmware : + +``` +make bin +``` + +Your freshly compiled firmware will appear as `src/dirtyjtag.bin`. + +## Inspiration + + * [opendous-jtag](https://github.com/vfonov/opendous-jtag) + * [neroJtag](https://github.com/makestuff/neroJtag) + * [clujtag-avr](https://github.com/ClusterM/clujtag-avr) diff --git a/dirtyjtag.ld b/dirtyjtag.ld new file mode 100644 index 0000000..3cc3c0d --- /dev/null +++ b/dirtyjtag.ld @@ -0,0 +1,7 @@ +/* Generic linker script for STM32F103RBT6/STM32F103R8T6 */ +MEMORY { + rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K +} + +INCLUDE libucmx_stm32f1.ld diff --git a/src/cmd.c b/src/cmd.c new file mode 100644 index 0000000..0619f0e --- /dev/null +++ b/src/cmd.c @@ -0,0 +1,229 @@ +/* + Copyright (c) 2017 Jean THOMAS. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include + +#include "jtag.h" +#include "usb.h" +#include "cmd.h" + +#define CMD_STOP 0x00 +#define CMD_INFO 0x01 +#define CMD_FREQ 0x02 +#define CMD_XFER 0x03 +#define CMD_SETSIG 0x04 +#define CMD_GETSIG 0x05 +#define CMD_CLK 0x06 + +#define SIG_TCK (1 << 1) +#define SIG_TDI (1 << 2) +#define SIG_TDO (1 << 3) +#define SIG_TMS (1 << 4) +#define SIG_TRST (1 << 5) +#define SIG_SRST (1 << 6) + +/** + * @brief Handle CMD_INFO command + * + * CMD_INFO returns a string to the host software. This + * could be used to check DirtyJTAG firmware version + * or supported commands. As of now it is implemented + * but not usefull. + * + * @param usbd_dev USB device + */ +static void cmd_info(usbd_device *usbd_dev); + +/** + * @brief Handle CMD_FREQ command + * + * CMD_FREQ sets the clock frequency on the probe. + * Currently this does not changes anything. + * + * @param commands Command data + */ +static void cmd_freq(const uint8_t *commands); + +/** + * @brief Handle CMD_XFER command + * + * CMD_XFER reads and writes data simultaneously. + * + * @param usbd_dev USB device + * @param commands Command data + */ +static void cmd_xfer(usbd_device *usbd_dev, const uint8_t *commands); + +/** + * @brief Handle CMD_SETSIG command + * + * CMD_SETSIG set the logic state of the JTAG signals. + * + * @param commands Command data + */ +static void cmd_setsig(const uint8_t *commands); + +/** + * @brief Handle CMD_GETSIG command + * + * CMD_GETSIG gets the current signal state. + * + * @param usbd_dev USB device + */ +static void cmd_getsig(usbd_device *usbd_dev); + +/** + * @brief Handle CMD_CLK command + * + * CMD_CLK sends clock pulses with specific TMS and TDI state. + * + * @param commands Command data + */ +static void cmd_clk(const uint8_t *commands); + +uint8_t cmd_handle(usbd_device *usbd_dev, const usbd_transfer *transfer) { + uint8_t *commands; + + commands = (uint8_t*)transfer->buffer; + + while (*commands != CMD_STOP) { + switch (*commands) { + case CMD_INFO: + cmd_info(usbd_dev); + break; + + case CMD_FREQ: + cmd_freq(commands); + commands += 2; + break; + + case CMD_XFER: + cmd_xfer(usbd_dev, commands); + return 0; + break; + + case CMD_SETSIG: + cmd_setsig(commands); + commands += 2; + break; + + case CMD_GETSIG: + cmd_getsig(usbd_dev); + return 0; + break; + + case CMD_CLK: + cmd_clk(commands); + commands += 2; + break; + + default: + return 1; /* Unsupported command, halt */ + break; + } + + commands++; + } + + return 1; +} + +static void cmd_info(usbd_device *usbd_dev) { + char info_string[64] = "DJTAG1\n"; + + usb_send(usbd_dev, (uint8_t*)info_string, 64); +} + +static void cmd_freq(const uint8_t *commands) { + jtag_set_frequency((commands[1] << 8) | commands[2]); +} + +static void cmd_xfer(usbd_device *usbd_dev, const uint8_t *commands) { + uint8_t transferred_bits; + uint8_t output_buffer[32]; + + /* Fill the output buffer with zeroes */ + memset(output_buffer, 0, 32); + + /* This is the number of transfered bits in one transfer command */ + transferred_bits = commands[1]; + + jtag_transfer(transferred_bits, commands+2, output_buffer); + + /* Send the transfer response back to host */ + usb_send(usbd_dev, output_buffer, 32); +} + +static void cmd_setsig(const uint8_t *commands) { + uint8_t signal_mask, signal_status; + + signal_mask = commands[1]; + signal_status = commands[2]; + + if (signal_mask & SIG_TCK) { + jtag_set_tck(signal_status & SIG_TCK); + } + + if (signal_mask & SIG_TDI) { + jtag_set_tdi(signal_status & SIG_TDI); + } + + if (signal_mask & SIG_TMS) { + jtag_set_tms(signal_status & SIG_TMS); + } + + if (signal_mask & SIG_TRST) { + jtag_set_trst(signal_status & SIG_TRST); + } + + if (signal_mask & SIG_SRST) { + jtag_set_srst(signal_status & SIG_SRST); + } +} + +static void cmd_getsig(usbd_device *usbd_dev) { + uint8_t signal_status = 0; + + if (jtag_get_tdo()) { + signal_status |= SIG_TDO; + } + + usb_send(usbd_dev, &signal_status, 1); +} + +static void cmd_clk(const uint8_t *commands) { + uint8_t signals, clk_pulses, i; + + signals = commands[1]; + clk_pulses = commands[2]; + + /* Set TDI & TMS signals */ + jtag_set_tms(signals & SIG_TMS); + jtag_set_tdi(signals & SIG_TDI); + + /* Send clock pulses */ + for (i = 0; i < clk_pulses; i++) { + jtag_clock(); + } +} diff --git a/src/cmd.h b/src/cmd.h new file mode 100644 index 0000000..2fc34ca --- /dev/null +++ b/src/cmd.h @@ -0,0 +1,29 @@ +/* + Copyright (c) 2017 Jean THOMAS. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/** + * @brief Handle a DirtyJTAG command + * + * @param usbd_dev USB device + * @param transfer Received packet + * @return Command needs to send data back to host + */ +uint8_t cmd_handle(usbd_device *usbd_dev, const usbd_transfer *transfer); diff --git a/src/delay.c b/src/delay.c new file mode 100644 index 0000000..8802c0b --- /dev/null +++ b/src/delay.c @@ -0,0 +1,35 @@ +/* + Copyright (c) 2017 Jean THOMAS. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +void delay_us(uint32_t delay) { + (void)delay; + + asm ("mov r1, #24;" + "mul r0, r0, r1;" + "b _delaycmp;" + "_delayloop:" + "subs r0, r0, #1;" + "_delaycmp:;" + "cmp r0, #0;" + " bne _delayloop;"); +} diff --git a/src/delay.h b/src/delay.h new file mode 100644 index 0000000..04bb9cb --- /dev/null +++ b/src/delay.h @@ -0,0 +1,33 @@ +/* + Copyright (c) 2017 Jean THOMAS. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/** + * @brief Halts code executation + * + * Halts code execution for a specified amount of time + * expressed in microseconds. The function was copied + * from the superbitrf-firmware project. + * + * I haven't measured its accuracy yet. + * + * @param delay Delay in microseconds + */ +void delay_us(uint32_t delay); diff --git a/src/dirtyjtag.c b/src/dirtyjtag.c new file mode 100644 index 0000000..d68a6da --- /dev/null +++ b/src/dirtyjtag.c @@ -0,0 +1,54 @@ +/* + Copyright (c) 2017 Jean THOMAS. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include + +#include "jtag.h" +#include "usb.h" +#include "delay.h" + +int main(void) { + /* Peripherals init */ + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_GPIOB); + rcc_periph_clock_enable(RCC_GPIOC); + rcc_periph_clock_enable(RCC_AFIO); + + /* Clock init */ + rcc_clock_setup_in_hsi_out_48mhz(); + + /* Disable DirtyJTAG's own JTAG interface */ + AFIO_MAPR |= AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON; + + usb_reenumerate(); + + jtag_init(); + + /* Turn on the onboard LED */ + gpio_set_mode(GPIOC, GPIO_CNF_OUTPUT_PUSHPULL, + GPIO_MODE_OUTPUT_2_MHZ, GPIO13); + gpio_set(GPIOC, GPIO13); + + usb_init(); + + return 0; +} diff --git a/src/jtag.c b/src/jtag.c new file mode 100644 index 0000000..3840bba --- /dev/null +++ b/src/jtag.c @@ -0,0 +1,113 @@ +#include +#include + +#include "delay.h" +#include "jtag.h" + +#define JTAG_PORT GPIOA +#define JTAG_TDI GPIO0 +#define JTAG_TDO GPIO1 +#define JTAG_TCK GPIO2 +#define JTAG_TMS GPIO3 +#define JTAG_TRST GPIO4 +#define JTAG_SRST GPIO5 + +static uint32_t period = 1; + +void jtag_init(void) { + /* GPIO configuration */ + gpio_set_mode(JTAG_PORT, + GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, + JTAG_TCK | JTAG_TDI | JTAG_TMS | JTAG_SRST | JTAG_TRST); + gpio_set_mode(JTAG_PORT, + GPIO_MODE_INPUT, + GPIO_CNF_INPUT_PULL_UPDOWN, + JTAG_TDO); + + /* Set default pin state */ + gpio_clear(JTAG_PORT, JTAG_TCK | JTAG_TMS | JTAG_TDI); + gpio_set(JTAG_PORT, JTAG_TRST | JTAG_SRST); + + /* Set pull-down on TDO */ + gpio_clear(JTAG_PORT, JTAG_TDO); +} + +void jtag_set_frequency(uint32_t frequency) { + /* The period is set in microseconds */ + /* Frequency value is in kilohertz (kHz) */ + //period = 1000/frequency; + (void)frequency; +} + +void jtag_clock(void) { + delay_us(period/2); + + gpio_set(JTAG_PORT, JTAG_TCK); + + delay_us(period); + + gpio_clear(JTAG_PORT, JTAG_TCK); + delay_us(period/2); +} + +void jtag_set_tck(uint8_t value) { + if (value) { + gpio_set(JTAG_PORT, JTAG_TCK); + } else { + gpio_clear(JTAG_PORT, JTAG_TCK); + } +} + +void jtag_set_tms(uint8_t value) { + if (value) { + gpio_set(JTAG_PORT, JTAG_TMS); + } else { + gpio_clear(JTAG_PORT, JTAG_TMS); + } +} + +void jtag_set_tdi(uint8_t value) { + if (value) { + gpio_set(JTAG_PORT, JTAG_TDI); + } else { + gpio_clear(JTAG_PORT, JTAG_TDI); + } +} + +uint8_t jtag_get_tdo(void) { + return gpio_get(JTAG_PORT, JTAG_TDO) ? 1 : 0; +} + +void jtag_set_trst(uint8_t value) { + if (value) { + gpio_set(JTAG_PORT, JTAG_TRST); + } else { + gpio_clear(JTAG_PORT, JTAG_TRST); + } +} + +void jtag_set_srst(uint8_t value) { + if (value) { + gpio_set(JTAG_PORT, JTAG_SRST); + } else { + gpio_clear(JTAG_PORT, JTAG_SRST); + } +} + +void jtag_transfer(uint16_t length, const uint8_t *in, uint8_t *out) { + uint16_t i; + + /* Set TMS low during transfer */ + jtag_set_tms(0); + + /* Read TDO, then set TDI, then clock */ + for (i = 0; i < length; i++) { + out[i/8] |= jtag_get_tdo() ? (0x80 >> (i%8)) : 0 ; + + jtag_set_tdi(in[i/8] & (0x80 >> (i%8))); + + jtag_clock(); + } +} + diff --git a/src/jtag.h b/src/jtag.h new file mode 100644 index 0000000..b3f85a1 --- /dev/null +++ b/src/jtag.h @@ -0,0 +1,96 @@ +/* + Copyright (c) 2017 Jean THOMAS. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/** + * @brief Initialize JTAG + * + * Set GPIO in correct state for JTAG operation + */ +void jtag_init(void); + +/** + * @brief Set JTAG clock frequency + * + * Set JTAG clock frequency. Currently not working. + * + * @param frequency Frequency in kHz + */ +void jtag_set_frequency(uint32_t frequency); + +/** + * @brief Send a JTAG clock pulse + */ +void jtag_clock(void); + +/** + * @brief Set TCK state + * + * @param value TCK logic level + */ +void jtag_set_tck(uint8_t value); + +/** + * @brief Set TMS state + * + * @param value TMS logic level + */ +void jtag_set_tms(uint8_t value); + +/** + * @brief Set TDI state + * + * @param value TDI logic level + */ +void jtag_set_tdi(uint8_t value); + +/** + * @brief Get TDO state + * + * @return TDO logic level + */ +uint8_t jtag_get_tdo(void); + +/** + * @brief Set TRST state + * + * @param value TRST logic level + */ +void jtag_set_trst(uint8_t value); + +/** + * @brief Set SRST state + * + * @param value SRST logic level + */ +void jtag_set_srst(uint8_t value); + +/** + * @brief Read and write multiple bits + * + * Read and write multiple bits. In order to save memory, + * each bit of the in/out arrays are used to carry data. + * + * @param length Number of bits exchanged + * @param in Input data + * @param out Output data + */ +void jtag_transfer(uint16_t length, const uint8_t *in, + uint8_t *out); diff --git a/src/usb.c b/src/usb.c new file mode 100644 index 0000000..014199a --- /dev/null +++ b/src/usb.c @@ -0,0 +1,308 @@ +/* + Copyright (c) 2017 Jean THOMAS. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include + +#include "cmd.h" +#include "usb.h" + +#define DIRTYJTAG_USB_BUFFER_SIZE 64 + +/** + * @brief Registers the callback for incomming USB packet + * + * This function registers the callback which be called when + * an incomming USB packet arrives. It must be called each time + * you want to receive a USB packet. + * + * @param usbd_dev USB device + */ +static void usb_prepare_rx(usbd_device *usbd_dev); + +/** + * @brief USB RX callback + * + * This function will be called whenever a USB packet arrives. + * It will process the received packet. It will automatically + * call usb_prepare_rx() in order to receive new packets. + * + * @param usbd_dev USB device + * @param transfer Packet + * @param status Transfer status + * @param usb_id URB id + */ +static void usb_rx_callback(usbd_device *usbd_dev, const usbd_transfer *transfer, + usbd_transfer_status status, usbd_urb_id urb_id); + +/** + * @brief USB TX callback + * + * This function will be called after a packet a correctly been + * sent. It will automatically call usb_prepare_rx() in order to + * receive new packets. + * + * @param usbd_dev USB device + * @param transfer Packet + * @param status Transfer status + * @param urb_id URB id + */ +static void usb_tx_callback(usbd_device *usbd_dev, const usbd_transfer *transfer, + usbd_transfer_status status, usbd_urb_id urb_id); + +uint8_t usb_buffer[DIRTYJTAG_USB_BUFFER_SIZE]; + +static const struct usb_string_descriptor string_lang_list = { + .bLength = USB_DT_STRING_SIZE(1), + .bDescriptorType = USB_DT_STRING, + .wData = { + USB_LANGID_ENGLISH_UNITED_STATES + } +}; + +/* Generated using unicore-mx/scripts/usb-strings.py */ +static const struct usb_string_descriptor string_0 = { + .bLength = USB_DT_STRING_SIZE(9), + .bDescriptorType = USB_DT_STRING, + /* "DirtyJTAG" */ + .wData = { + 0x0044, 0x0069, 0x0072, 0x0074, 0x0079, 0x004a, 0x0054, 0x0041, + 0x0047 + } +}; + +static const struct usb_string_descriptor string_1 = { + .bLength = USB_DT_STRING_SIZE(11), + .bDescriptorType = USB_DT_STRING, + /* Jean THOMAS */ + .wData = { + 0x004a, 0x0065, 0x0061, 0x006e, 0x0020, 0x0054, 0x0048, 0x004f, + 0x004d, 0x0041, 0x0053 + } +}; + +static const struct usb_string_descriptor string_2 = { + .bLength = USB_DT_STRING_SIZE(13), + .bDescriptorType = USB_DT_STRING, + /* no serial :-) */ + .wData = { + 0x006e, 0x006f, 0x0020, 0x0073, 0x0065, 0x0072, 0x0069, 0x0061, + 0x006c, 0x0020, 0x003a, 0x002d, 0x0029 + } +}; + +static const struct usb_string_descriptor **string_data[1] = { + (const struct usb_string_descriptor *[]){&string_0, &string_1, &string_2} +}; + +static const struct usbd_info_string string = { + .lang_list = &string_lang_list, + .count = 3, + .data = string_data +}; + +const struct __attribute__((packed)) { + struct usb_config_descriptor config; + struct usb_interface_descriptor iface; + struct usb_endpoint_descriptor endp[2]; +} config_desc = { + .config = { + .bLength = USB_DT_CONFIGURATION_SIZE, + .bDescriptorType = USB_DT_CONFIGURATION, + .wTotalLength = sizeof(config_desc), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = 0x80, + .bMaxPower = 0x32, /* 100mA current consumption */ + }, + + .iface = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR, /* Custom USB class */ + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0, + }, + + .endp = { + { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = DIRTYJTAG_READ_ENDPOINT, + .bmAttributes = USB_ENDPOINT_ATTR_BULK, + .wMaxPacketSize = 64, + .bInterval = 1 + }, { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = DIRTYJTAG_WRITE_ENDPOINT, + .bmAttributes = USB_ENDPOINT_ATTR_BULK, + .wMaxPacketSize = 64, + .bInterval = 1 + } + } +}; + +const struct usb_device_descriptor dev_desc = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x0110, + .bDeviceClass = USB_CLASS_VENDOR, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = 64, + .idVendor = 0x1209, + .idProduct = 0xC0CA, + .bcdDevice = 0x0110, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 3, + .bNumConfigurations = 1 +}; + +static const struct usbd_info info = { + .device = { + .desc = &dev_desc, + .string = &string + }, + + .config = {{ + .desc = (const struct usb_config_descriptor *) &config_desc, + .string = &string + }} +}; + +static void usb_set_config(usbd_device *usbd_dev, + const struct usb_config_descriptor *cfg) { + (void)cfg; + + usbd_ep_prepare(usbd_dev, DIRTYJTAG_READ_ENDPOINT, USBD_EP_BULK, 64, + USBD_INTERVAL_NA, USBD_EP_NONE); + usbd_ep_prepare(usbd_dev, DIRTYJTAG_WRITE_ENDPOINT, USBD_EP_BULK, 64, + USBD_INTERVAL_NA, USBD_EP_NONE); + + usb_prepare_rx(usbd_dev); +} + +static void usb_control_request(usbd_device *usbd_dev, uint8_t ep, + const struct usb_setup_data *setup_data) { + (void) ep; /* assuming ep == 0 */ + + const uint8_t bmReqMask = USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT; + const uint8_t bmReqVal = USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE; + + if ((setup_data->bmRequestType & bmReqMask) != bmReqVal) { + /* Pass on to usb stack internal */ + usbd_ep0_setup(usbd_dev, setup_data); + return; + } + + usbd_ep0_stall(usbd_dev); +} + +void usb_init(void) { + usbd_device *usbd_dev; + + /* USB device initialisation */ + usbd_dev = usbd_init(USBD_STM32_FSDEV, NULL, &info); + usbd_register_set_config_callback(usbd_dev, usb_set_config); + usbd_register_setup_callback(usbd_dev, usb_control_request); + + while (1) { + usbd_poll(usbd_dev, 0); + } +} + +void usb_reenumerate(void) { + /* This hack puts D+ to LOW state to force USB enumeration. + Source : https://github.com/insane-adding-machines/unicore-mx/issues/60 */ + + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, GPIO12); + gpio_clear(GPIOA, GPIO12); +} + +void usb_send(usbd_device *usbd_dev, uint8_t *sent_buffer, uint8_t sent_size) { + const usbd_transfer transfer = { + .ep_type = USBD_EP_BULK, + .ep_addr = DIRTYJTAG_WRITE_ENDPOINT, + .ep_size = DIRTYJTAG_USB_BUFFER_SIZE, + .ep_interval = USBD_INTERVAL_NA, + .buffer = usb_buffer, + .length = sent_size, + .flags = USBD_FLAG_SHORT_PACKET, + .timeout = USBD_TIMEOUT_NEVER, + .callback = usb_tx_callback + }; + + /* Packets are copied into a buffer because the operation + is done asynchronously */ + memcpy(usb_buffer, sent_buffer, sent_size); + + usbd_transfer_submit(usbd_dev, &transfer); +} + +static void usb_prepare_rx(usbd_device *usbd_dev) { + const usbd_transfer transfer = { + .ep_type = USBD_EP_BULK, + .ep_addr = DIRTYJTAG_READ_ENDPOINT, + .ep_size = DIRTYJTAG_USB_BUFFER_SIZE, + .ep_interval = USBD_INTERVAL_NA, + .buffer = usb_buffer, + .length = DIRTYJTAG_USB_BUFFER_SIZE, + .flags = USBD_FLAG_SHORT_PACKET, + .timeout = USBD_TIMEOUT_NEVER, + .callback = usb_rx_callback + }; + + usbd_transfer_submit(usbd_dev, &transfer); +} + +static void usb_rx_callback(usbd_device *usbd_dev, const usbd_transfer *transfer, + usbd_transfer_status status, usbd_urb_id urb_id) { + (void)urb_id; + + if (status == USBD_SUCCESS) { + if (transfer->transferred) { + if (cmd_handle(usbd_dev, transfer)) { + usb_prepare_rx(usbd_dev); + } + } else { + usbd_transfer_submit(usbd_dev, transfer); /* re-submit */ + } + } +} + +static void usb_tx_callback(usbd_device *usbd_dev, const usbd_transfer *transfer, + usbd_transfer_status status, usbd_urb_id urb_id) { + (void)urb_id; + (void)transfer; + + if (status == USBD_SUCCESS) { + usb_prepare_rx(usbd_dev); + } +} diff --git a/src/usb.h b/src/usb.h new file mode 100644 index 0000000..642c066 --- /dev/null +++ b/src/usb.h @@ -0,0 +1,44 @@ +/* + Copyright (c) 2017 Jean THOMAS. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#define DIRTYJTAG_READ_ENDPOINT 0x01 +#define DIRTYJTAG_WRITE_ENDPOINT 0x82 + +/** + * @brief Initialises USB peripheral on the STM32F103 + */ +void usb_init(void); + +/** + * @brief Forces USB reenumeration by pulling data lines + */ +void usb_reenumerate(void); + +/** + * @brief Send a bulk USB packet + * + * @param usbd_dev USB device + * @param sent_buffer Data buffer + * @param size Size in bytes + */ +void usb_send(usbd_device *usbd_dev, uint8_t *sent_buffer, uint8_t size); diff --git a/unicore-mx b/unicore-mx new file mode 160000 index 0000000..a2a0605 --- /dev/null +++ b/unicore-mx @@ -0,0 +1 @@ +Subproject commit a2a06059bdce11fb24d872e8d93e4455545ad65b