From f67a57d2baf6caad5e9dc4ba5049595f2ebeb512 Mon Sep 17 00:00:00 2001 From: graham sanderson Date: Tue, 19 Jan 2021 22:34:21 -0600 Subject: [PATCH] Initial Release --- CMakeLists.txt | 24 ++++ pico_sdk_import.cmake | 64 ++++++++++ src/cdc_uart.c | 44 +++++++ src/cdc_uart.h | 7 ++ src/main.c | 56 +++++++++ src/picoprobe_config.h | 34 ++++++ src/probe.c | 263 +++++++++++++++++++++++++++++++++++++++++ src/probe.h | 7 ++ src/probe.pio | 24 ++++ src/tusb_config.h | 81 +++++++++++++ src/usb_descriptors.c | 148 +++++++++++++++++++++++ 11 files changed, 752 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 pico_sdk_import.cmake create mode 100755 src/cdc_uart.c create mode 100755 src/cdc_uart.h create mode 100644 src/main.c create mode 100755 src/picoprobe_config.h create mode 100755 src/probe.c create mode 100755 src/probe.h create mode 100755 src/probe.pio create mode 100644 src/tusb_config.h create mode 100644 src/usb_descriptors.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f04c8b7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.12) + +include(pico_sdk_import.cmake) + +project(picoprobe) + +pico_sdk_init() + +add_executable(picoprobe + src/main.c + src/usb_descriptors.c + src/probe.c + src/cdc_uart.c +) + +set(DBG_PIN_COUNT=4) + +pico_generate_pio_header(picoprobe ${CMAKE_CURRENT_LIST_DIR}/src/probe.pio) + +target_include_directories(picoprobe PRIVATE src) + +target_link_libraries(picoprobe PRIVATE pico_stdlib tinyusb_device tinyusb_board hardware_pio) + +pico_add_extra_outputs(picoprobe) diff --git a/pico_sdk_import.cmake b/pico_sdk_import.cmake new file mode 100644 index 0000000..f63ee3f --- /dev/null +++ b/pico_sdk_import.cmake @@ -0,0 +1,64 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# todo document + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the PICO SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + ) + if (NOT pico_sdk) + message("Downloading PICO SDK") + FetchContent_Populate(pico_sdk) + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "PICO SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the PICO SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the PICO SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/src/cdc_uart.c b/src/cdc_uart.c new file mode 100755 index 0000000..6c1c009 --- /dev/null +++ b/src/cdc_uart.c @@ -0,0 +1,44 @@ +#include + +#include "tusb.h" + +#include "picoprobe_config.h" + +void cdc_uart_init(void) { + gpio_set_function(PICOPROBE_UART_TX, GPIO_FUNC_UART); + gpio_set_function(PICOPROBE_UART_RX, GPIO_FUNC_UART); + uart_init(uart1, 115200); +} + +#define MAX_UART_PKT 64 +void cdc_task(void) { + uint8_t rx_buf[MAX_UART_PKT]; + uint8_t tx_buf[MAX_UART_PKT]; + + // Consume uart fifo regardless even if not connected + uint rx_len = 0; + while(uart_is_readable(uart1) && (rx_len < MAX_UART_PKT)) { + rx_buf[rx_len++] = uart_getc(uart1); + } + + if (tud_cdc_connected()) { + // Do we have anything to display on the host's terminal? + if (rx_len) { + for (uint i = 0; i < rx_len; i++) { + tud_cdc_write_char(rx_buf[i]); + } + tud_cdc_write_flush(); + } + + if (tud_cdc_available()) { + // Is there any data from the host for us to tx + uint tx_len = tud_cdc_read(tx_buf, sizeof(tx_buf)); + uart_write_blocking(uart1, tx_buf, tx_len); + } + } +} + +void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding) { + picoprobe_info("New baud rate %d\n", line_coding->bit_rate); + uart_init(uart1, line_coding->bit_rate); +} \ No newline at end of file diff --git a/src/cdc_uart.h b/src/cdc_uart.h new file mode 100755 index 0000000..445da37 --- /dev/null +++ b/src/cdc_uart.h @@ -0,0 +1,7 @@ +#ifndef CDC_UART_H +#define CDC_UART_H + +void cdc_uart_init(void); +void cdc_task(void); + +#endif \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..9e54962 --- /dev/null +++ b/src/main.c @@ -0,0 +1,56 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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 "bsp/board.h" +#include "tusb.h" + +#include "picoprobe_config.h" +#include "probe.h" +#include "cdc_uart.h" + +// UART0 for Picoprobe debug +// UART1 for picoprobe to target device + +int main(void) { + + board_init(); + cdc_uart_init(); + tusb_init(); + probe_init(); + + picoprobe_info("Welcome to Picoprobe!\n"); + + while (1) { + tud_task(); // tinyusb device task + cdc_task(); + probe_task(); + } + + return 0; +} \ No newline at end of file diff --git a/src/picoprobe_config.h b/src/picoprobe_config.h new file mode 100755 index 0000000..f34c165 --- /dev/null +++ b/src/picoprobe_config.h @@ -0,0 +1,34 @@ +#ifndef PICOPROBE_H_ +#define PICOPROBE_H_ + +#if true +#define picoprobe_info(format,args...) printf(format, ## args) +#else +#define picoprobe_info(format,...) ((void)0) +#endif + + +#if false +#define picoprobe_debug(format,args...) printf(format, ## args) +#else +#define picoprobe_debug(format,...) ((void)0) +#endif + +#if false +#define picoprobe_dump(format,args...) printf(format, ## args) +#else +#define picoprobe_dump(format,...) ((void)0) +#endif + + +// PIO config +#define PROBE_SM 0 +#define PROBE_PIN_OFFSET 2 +#define PROBE_PIN_SWCLK PROBE_PIN_OFFSET + 0 // 2 +#define PROBE_PIN_SWDIO PROBE_PIN_OFFSET + 1 // 3 + +// UART config +#define PICOPROBE_UART_TX 4 +#define PICOPROBE_UART_RX 5 + +#endif \ No newline at end of file diff --git a/src/probe.c b/src/probe.c new file mode 100755 index 0000000..f0cd2b3 --- /dev/null +++ b/src/probe.c @@ -0,0 +1,263 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include +#include + +#include "picoprobe_config.h" +#include "probe.pio.h" +#include "tusb.h" + +#define DIV_ROUND_UP(m, n) (((m) + (n) - 1) / (n)) + +// Only want to set / clear one gpio per event so go up in powers of 2 +enum _dbg_pins { + DBG_PIN_WRITE = 1, + DBG_PIN_WRITE_WAIT = 2, + DBG_PIN_READ = 4, + DBG_PIN_PKT = 8, +}; + +CU_REGISTER_DEBUG_PINS(probe_timing) + +// Uncomment to enable debug +//CU_SELECT_DEBUG_PINS(probe_timing) + +#define PROBE_BUF_SIZE 8192 +struct _probe { + // Total length + uint tx_len; + // Data back to host + uint8_t tx_buf[PROBE_BUF_SIZE]; + + // CMD / Data RX'd from + uint rx_len; + uint8_t rx_buf[PROBE_BUF_SIZE]; + + // PIO offset + uint offset; +}; + +static struct _probe probe; + +enum PROBE_CMDS { + PROBE_INVALID = 0, // Invalid command + PROBE_WRITE_BITS = 1, // Host wants us to write bits + PROBE_READ_BITS = 2, // Host wants us to read bits + PROBE_SET_FREQ = 3, // Set TCK + PROBE_RESET = 4, // Reset all state +}; + +struct __attribute__((__packed__)) probe_cmd_hdr { + uint8_t id; + uint8_t cmd; + uint32_t bits; +}; + +struct __attribute__((__packed__)) probe_pkt_hdr { + uint32_t total_packet_length; +}; + +void probe_set_swclk_freq(uint freq_khz) { + picoprobe_info("Set swclk freq %dKHz\n", freq_khz); + uint clk_sys_freq_khz = clock_get_hz(clk_sys) / 1000; + // Worked out with saleae + uint32_t divider = clk_sys_freq_khz / freq_khz / 2; + pio_sm_set_clkdiv_int_frac(pio0, PROBE_SM, divider, 0); +} + +static inline void probe_write_bits(uint bit_count, uint8_t data_byte) { + DEBUG_PINS_SET(probe_timing, DBG_PIN_WRITE); + pio_sm_put_blocking(pio0, PROBE_SM, bit_count - 1); + pio_sm_put_blocking(pio0, PROBE_SM, data_byte); + DEBUG_PINS_SET(probe_timing, DBG_PIN_WRITE_WAIT); + picoprobe_dump("Write %d bits 0x%x\n", bit_count, data_byte); + // Wait for pio to push garbage to rx fifo so we know it has finished sending + pio_sm_get_blocking(pio0, PROBE_SM); + DEBUG_PINS_CLR(probe_timing, DBG_PIN_WRITE_WAIT); + DEBUG_PINS_CLR(probe_timing, DBG_PIN_WRITE); +} + +static inline uint8_t probe_read_bits(uint bit_count) { + DEBUG_PINS_SET(probe_timing, DBG_PIN_READ); + pio_sm_put_blocking(pio0, PROBE_SM, bit_count - 1); + uint32_t data = pio_sm_get_blocking(pio0, PROBE_SM); + uint8_t data_shifted = data >> 24; + + if (bit_count < 8) { + data_shifted = data_shifted >> 8-bit_count; + } + + picoprobe_dump("Read %d bits 0x%x (shifted 0x%x)\n", bit_count, data, data_shifted); + DEBUG_PINS_CLR(probe_timing, DBG_PIN_READ); + return data_shifted; +} + +static void probe_read_mode(void) { + pio_sm_exec(pio0, PROBE_SM, pio_encode_jmp(probe.offset + probe_offset_in_posedge)); + while(pio0->dbg_padoe & (1 << PROBE_PIN_SWDIO)); +} + +static void probe_write_mode(void) { + pio_sm_exec(pio0, PROBE_SM, pio_encode_jmp(probe.offset + probe_offset_out_negedge)); + while(!(pio0->dbg_padoe & (1 << PROBE_PIN_SWDIO))); +} + +void probe_init() { + // Funcsel pins + pio_gpio_init(pio0, PROBE_PIN_SWCLK); + pio_gpio_init(pio0, PROBE_PIN_SWDIO); + // Make sure SWDIO has a pullup on it. Idle state is high + gpio_pull_up(PROBE_PIN_SWDIO); + + uint offset = pio_add_program(pio0, &probe_program); + probe.offset = offset; + + pio_sm_config sm_config = probe_program_get_default_config(offset); + + // Set SWCLK as a sideset pin + sm_config_set_sideset_pins(&sm_config, PROBE_PIN_SWCLK); + + // Set SWDIO offset + sm_config_set_out_pins(&sm_config, PROBE_PIN_SWDIO, 1); + sm_config_set_set_pins(&sm_config, PROBE_PIN_SWDIO, 1); + sm_config_set_in_pins(&sm_config, PROBE_PIN_SWDIO); + + // Set SWD and SWDIO pins as output to start. This will be set in the sm + pio_sm_set_consecutive_pindirs(pio0, PROBE_SM, PROBE_PIN_OFFSET, 2, true); + + // shift output right, autopull off, autopull threshold + sm_config_set_out_shift(&sm_config, true, false, 0); + // shift input right as swd data is lsb first, autopush off + sm_config_set_in_shift(&sm_config, true, false, 0); + + // Init SM with config + pio_sm_init(pio0, PROBE_SM, offset, &sm_config); + + // Set up divisor + probe_set_swclk_freq(1000); + + // Enable SM + pio_sm_set_enabled(pio0, PROBE_SM, 1); + + // Jump to write program + probe_write_mode(); +} + +void probe_handle_read(uint total_bits) { + picoprobe_debug("Read %d bits\n", total_bits); + probe_read_mode(); + + uint chunk; + uint bits = total_bits; + while (bits > 0) { + if (bits > 8) { + chunk = 8; + } else { + chunk = bits; + } + probe.tx_buf[probe.tx_len] = probe_read_bits(chunk); + probe.tx_len++; + // Decrement remaining bits + bits -= chunk; + } +} + +void probe_handle_write(uint8_t *data, uint total_bits) { + picoprobe_debug("Write %d bits\n", total_bits); + probe_write_mode(); + + uint chunk; + uint bits = total_bits; + while (bits > 0) { + if (bits > 8) { + chunk = 8; + } else { + chunk = bits; + } + + probe_write_bits(chunk, *data++); + bits -= chunk; + } +} + +void probe_prepare_read_header(struct probe_cmd_hdr *hdr) { + // We have a read so need to prefix the data with the cmd header + if (probe.tx_len == 0) { + // Reserve some space for probe_pkt_hdr + probe.tx_len += sizeof(struct probe_pkt_hdr); + } + + memcpy((void*)&probe.tx_buf[probe.tx_len], hdr, sizeof(struct probe_cmd_hdr)); + probe.tx_len += sizeof(struct probe_cmd_hdr); +} + +void probe_handle_pkt(void) { + uint8_t *pkt = &probe.rx_buf[0] + sizeof(struct probe_pkt_hdr); + uint remaining = probe.rx_len - sizeof(struct probe_pkt_hdr); + + DEBUG_PINS_SET(probe_timing, DBG_PIN_PKT); + + picoprobe_debug("Processing packet of length %d\n", probe.rx_len); + + probe.tx_len = 0; + while (remaining) { + struct probe_cmd_hdr *hdr = (struct probe_cmd_hdr*)pkt; + uint data_bytes = DIV_ROUND_UP(hdr->bits, 8); + pkt += sizeof(struct probe_cmd_hdr); + remaining -= sizeof(struct probe_cmd_hdr); + + if (hdr->cmd == PROBE_WRITE_BITS) { + uint8_t *data = pkt; + probe_handle_write(data, hdr->bits); + pkt += data_bytes; + remaining -= data_bytes; + } else if (hdr->cmd == PROBE_READ_BITS) { + probe_prepare_read_header(hdr); + probe_handle_read(hdr->bits); + } else if (hdr->cmd == PROBE_SET_FREQ) { + probe_set_swclk_freq(hdr->bits); + } else if (hdr->cmd == PROBE_RESET) { + // TODO: Is there anything to do after a reset? + // tx len and rx len should already be 0 + ; + } + } + probe.rx_len = 0; + + if (probe.tx_len) { + // Fill in total packet length before sending + struct probe_pkt_hdr *tx_hdr = (struct probe_pkt_hdr*)&probe.tx_buf[0]; + tx_hdr->total_packet_length = probe.tx_len; + tud_vendor_write(&probe.tx_buf[0], probe.tx_len); + picoprobe_debug("Picoprobe wrote %d response bytes\n", probe.tx_len); + } + probe.tx_len = 0; + + DEBUG_PINS_CLR(probe_timing, DBG_PIN_PKT); +} + +// USB bits +void probe_task(void) { + if ( tud_vendor_available() ) { + uint count = tud_vendor_read(&probe.rx_buf[probe.rx_len], 64); + if (count == 0) { + return; + } + probe.rx_len += count; + } + + if (probe.rx_len >= sizeof(struct probe_pkt_hdr)) { + struct probe_pkt_hdr *pkt_hdr = (struct probe_pkt_hdr*)&probe.rx_buf[0]; + if (pkt_hdr->total_packet_length == probe.rx_len) { + probe_handle_pkt(); + } + } +} \ No newline at end of file diff --git a/src/probe.h b/src/probe.h new file mode 100755 index 0000000..b6269c9 --- /dev/null +++ b/src/probe.h @@ -0,0 +1,7 @@ +#ifndef PROBE_H_ +#define PROBE_H_ + +void probe_task(void); +void probe_init(void); + +#endif \ No newline at end of file diff --git a/src/probe.pio b/src/probe.pio new file mode 100755 index 0000000..f27b1b2 --- /dev/null +++ b/src/probe.pio @@ -0,0 +1,24 @@ +.program probe +.side_set 1 opt + +public out_negedge: + set pindirs, 1 side 0x0 ; Init OE clock 0 + pull ; Pull number of bits to shift -1 from tx fifo and put into output shift register + mov x, osr ; mov bits to shift -1 from output shift register into x + pull ; Pull data to shift out +out_negedge_bitloop: + out pins, 1 side 0x0 ; clock data out on falling edge + jmp x-- out_negedge_bitloop side 0x1 ; data is present for posedge + set pins, 0 side 0x0 ; Drive data low + push ; Push to rx fifo just so processor knows when done + jmp out_negedge ; Wait for next transaction + +public in_posedge: + set pindirs, 0 side 0x0 ; INIT IE clock 0 + pull ; Pull number of bits to shift -1 from tx fifo and put into output shift register + mov x, osr ; mov bits to shift -1 from output shift register into x into x +in_posedge_bitloop: + in pins, 1 side 0x1 ; Generate posedge and read data + jmp x-- in_posedge_bitloop side 0x0 ; + push ; Push to rx fifo when done + jmp in_posedge ; Jump back to start diff --git a/src/tusb_config.h b/src/tusb_config.h new file mode 100644 index 0000000..b4dc45c --- /dev/null +++ b/src/tusb_config.h @@ -0,0 +1,81 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU + #error CFG_TUSB_MCU must be defined +#endif + +#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_PICO +#endif + +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_HID 0 +#define CFG_TUD_CDC 1 +#define CFG_TUD_MSC 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 1 + +#define CFG_TUD_CDC_RX_BUFSIZE 64 +#define CFG_TUD_CDC_TX_BUFSIZE 64 + +#define CFG_TUD_VENDOR_RX_BUFSIZE 8192 +#define CFG_TUD_VENDOR_TX_BUFSIZE 8192 + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c new file mode 100644 index 0000000..eddfa7a --- /dev/null +++ b/src/usb_descriptors.c @@ -0,0 +1,148 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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 "tusb.h" + + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ +tusb_desc_device_t const desc_device = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0110, // // USB Specification version 1.1 + .bDeviceClass = 0x00, // Each interface specifies its own + .bDeviceSubClass = 0x00, // Each interface specifies its own + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = 0x2E8A, // Pi + .idProduct = 0x0004, // Picoprobe + .bcdDevice = 0x0100, // Version 01.00 + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + .bNumConfigurations = 0x01 +}; + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const * tud_descriptor_device_cb(void) +{ + return (uint8_t const *) &desc_device; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +enum +{ + ITF_NUM_CDC_COM, + ITF_NUM_CDC_DATA, + ITF_NUM_PROBE, + ITF_NUM_TOTAL +}; + +#define CDC_NOTIFICATION_EP_NUM 0x81 +#define CDC_DATA_OUT_EP_NUM 0x02 +#define CDC_DATA_IN_EP_NUM 0x83 +#define PROBE_OUT_EP_NUM 0x04 +#define PROBE_IN_EP_NUM 0x85 + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_VENDOR_DESC_LEN) + +uint8_t const desc_configuration[] = +{ + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + + // Interface 0 + 1 + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_COM, 0, CDC_NOTIFICATION_EP_NUM, 64, CDC_DATA_OUT_EP_NUM, CDC_DATA_IN_EP_NUM, 64), + + // Interface 2 + TUD_VENDOR_DESCRIPTOR(ITF_NUM_PROBE, 0, PROBE_OUT_EP_NUM, PROBE_IN_EP_NUM, 64) + +}; + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_descriptor_configuration_cb(uint8_t index) +{ + (void) index; // for multiple configurations + return desc_configuration; +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +char const* string_desc_arr [] = +{ + (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) + "Raspberry Pi", // 1: Manufacturer + "Picoprobe", // 2: Product + "123456", // 3: Serials, should use chip ID +}; + +static uint16_t _desc_str[32]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) +{ + (void) langid; + + uint8_t chr_count; + + if ( index == 0) + { + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + }else + { + // Convert ASCII string into UTF-16 + + if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; + + const char* str = string_desc_arr[index]; + + // Cap at max char + chr_count = strlen(str); + if ( chr_count > 31 ) chr_count = 31; + + for(uint8_t i=0; i