Compare commits
14 Commits
pio-progra
...
line_codin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b6f8db41a | ||
|
|
e51647492e | ||
|
|
d13775ee72 | ||
|
|
58fa7a14cd | ||
|
|
1586ef0caa | ||
|
|
0761424821 | ||
|
|
d47b3082f8 | ||
|
|
6473166494 | ||
|
|
3a1887ff06 | ||
|
|
13b420d34c | ||
|
|
eb494103d4 | ||
|
|
7de418cce3 | ||
|
|
0746b5a844 | ||
|
|
d04ff3b472 |
@@ -17,6 +17,7 @@ add_executable(picoprobe
|
||||
src/cdc_uart.c
|
||||
src/get_serial.c
|
||||
src/sw_dp_pio.c
|
||||
src/tusb_edpt_handler.c
|
||||
)
|
||||
|
||||
target_sources(picoprobe PRIVATE
|
||||
@@ -44,6 +45,17 @@ target_compile_definitions (picoprobe PRIVATE
|
||||
PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1
|
||||
)
|
||||
|
||||
option (DEBUGPROBE "compile for the debugprobe" OFF)
|
||||
if (DEBUGPROBE)
|
||||
target_compile_definitions (picoprobe PRIVATE
|
||||
DEBUGPROBE=1
|
||||
)
|
||||
set_target_properties(picoprobe PROPERTIES
|
||||
OUTPUT_NAME "debugprobe"
|
||||
)
|
||||
endif ()
|
||||
|
||||
|
||||
target_link_libraries(picoprobe PRIVATE
|
||||
pico_multicore
|
||||
pico_stdlib
|
||||
|
||||
30
README.md
30
README.md
@@ -4,6 +4,36 @@ Picoprobe allows a Pico / RP2040 to be used as USB -> SWD and UART bridge. This
|
||||
# Documentation
|
||||
Picoprobe documentation can be found in the [Pico Getting Started Guide](https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf). See "Appendix A: Using Picoprobe".
|
||||
|
||||
# Hacking
|
||||
|
||||
For the purpose of making changes or studying of the code, you may want to compile the code yourself.
|
||||
|
||||
To compile this project firstly initialize and update the submodules:
|
||||
```
|
||||
git submodule update --init
|
||||
```
|
||||
then create and switch to the build directory:
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
```
|
||||
then run cmake and build the code:
|
||||
```
|
||||
cmake ..
|
||||
make
|
||||
```
|
||||
Done! You should now have a `picoprobe.uf2` that you can upload to your Pico in the normal way.
|
||||
|
||||
If you want to create the version that runs on the Raspberry Pi Debug Probe, then you need to invoke `cmake` in the sequence above with the `DEBUGPROBE=ON` option:
|
||||
```
|
||||
cmake -DDEBUGPROBE=ON ..
|
||||
```
|
||||
|
||||
This will build with the configuration for the Debug Probe and call the output program `debugprobe.uf2`, as opposed to `picoprobe.uf2` for the vanilla version.
|
||||
|
||||
Note that if you first ran through the whole sequence to compile for the Pico, then you don't need to start back at the top. You can just go back to the `cmake` step and start from there.
|
||||
|
||||
|
||||
# TODO
|
||||
- TinyUSB's vendor interface is FIFO-based and not packet-based. Using raw tx/rx callbacks is preferable as this stops DAP command batches from being concatenated, which confused openOCD.
|
||||
- Instead of polling, move the DAP thread to an asynchronously started/stopped one-shot operation to reduce CPU wakeups
|
||||
|
||||
@@ -460,7 +460,11 @@ __STATIC_FORCEINLINE void PIN_nTRST_OUT (uint32_t bit) {
|
||||
\return Current status of the nRESET DAP hardware I/O pin.
|
||||
*/
|
||||
__STATIC_FORCEINLINE uint32_t PIN_nRESET_IN (void) {
|
||||
#ifdef PROBE_PIN_RESET
|
||||
return probe_reset_level();
|
||||
#else
|
||||
return (0U);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** nRESET I/O pin: Set Output.
|
||||
@@ -469,7 +473,11 @@ __STATIC_FORCEINLINE uint32_t PIN_nRESET_IN (void) {
|
||||
- 1: release device hardware reset.
|
||||
*/
|
||||
__STATIC_FORCEINLINE void PIN_nRESET_OUT (uint32_t bit) {
|
||||
;
|
||||
#ifdef PROBE_PIN_RESET
|
||||
probe_assert_reset(!!bit);
|
||||
#else
|
||||
(void) bit;
|
||||
#endif
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
@@ -52,4 +52,4 @@
|
||||
|
||||
#define PROBE_PRODUCT_STRING "Debug Probe (CMSIS-DAP)"
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
/* Include CDC interface to bridge to target UART. Omit if not used. */
|
||||
#define PROBE_CDC_UART
|
||||
/* Target reset GPIO (active-low). Omit if not used.*/
|
||||
#define PROBE_PIN_RESET 0
|
||||
#define PROBE_PIN_RESET 1
|
||||
|
||||
#define PROBE_SM 0
|
||||
#define PROBE_PIN_OFFSET 12
|
||||
|
||||
@@ -35,7 +35,9 @@
|
||||
#define PROBE_PIN_SWCLK (PROBE_PIN_OFFSET + 0) // 2
|
||||
#define PROBE_PIN_SWDIO (PROBE_PIN_OFFSET + 1) // 3
|
||||
// Target reset config
|
||||
#define PROBE_PIN_RESET 0
|
||||
#if false
|
||||
#define PROBE_PIN_RESET 1
|
||||
#endif
|
||||
|
||||
// UART config
|
||||
#define PICOPROBE_UART_TX 4
|
||||
|
||||
@@ -34,8 +34,9 @@
|
||||
TaskHandle_t uart_taskhandle;
|
||||
TickType_t last_wake, interval = 100;
|
||||
|
||||
static uint8_t tx_buf[CFG_TUD_CDC_TX_BUFSIZE];
|
||||
static uint8_t rx_buf[CFG_TUD_CDC_RX_BUFSIZE];
|
||||
/* Max 1 FIFO worth of data */
|
||||
static uint8_t tx_buf[32];
|
||||
static uint8_t rx_buf[32];
|
||||
// Actually s^-1 so 25ms
|
||||
#define DEBOUNCE_MS 40
|
||||
static uint debounce_ticks = 5;
|
||||
@@ -59,6 +60,7 @@ void cdc_uart_init(void) {
|
||||
void cdc_task(void)
|
||||
{
|
||||
static int was_connected = 0;
|
||||
static uint cdc_tx_oe = 0;
|
||||
uint rx_len = 0;
|
||||
|
||||
// Consume uart fifo regardless even if not connected
|
||||
@@ -77,6 +79,9 @@ void cdc_task(void)
|
||||
rx_led_debounce = debounce_ticks;
|
||||
#endif
|
||||
written = MIN(tud_cdc_write_available(), rx_len);
|
||||
if (rx_len > written)
|
||||
cdc_tx_oe++;
|
||||
|
||||
if (written > 0) {
|
||||
tud_cdc_write(rx_buf, written);
|
||||
tud_cdc_write_flush();
|
||||
@@ -113,6 +118,7 @@ void cdc_task(void)
|
||||
} else if (was_connected) {
|
||||
tud_cdc_write_clear();
|
||||
was_connected = 0;
|
||||
cdc_tx_oe = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,6 +137,8 @@ void cdc_thread(void *ptr)
|
||||
|
||||
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding)
|
||||
{
|
||||
uart_parity_t parity;
|
||||
uint data_bits, stop_bits;
|
||||
/* Set the tick thread interval to the amount of time it takes to
|
||||
* fill up half a FIFO. Millis is too coarse for integer divide.
|
||||
*/
|
||||
@@ -139,12 +147,57 @@ void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding)
|
||||
vTaskSuspend(uart_taskhandle);
|
||||
interval = MAX(1, micros / ((1000 * 1000) / configTICK_RATE_HZ));
|
||||
debounce_ticks = MAX(1, configTICK_RATE_HZ / (interval * DEBOUNCE_MS));
|
||||
picoprobe_info("New baud rate %d micros %d interval %u\n",
|
||||
picoprobe_info("New baud rate %ld micros %ld interval %lu\n",
|
||||
line_coding->bit_rate, micros, interval);
|
||||
uart_deinit(PICOPROBE_UART_INTERFACE);
|
||||
tud_cdc_write_clear();
|
||||
tud_cdc_read_flush();
|
||||
uart_init(PICOPROBE_UART_INTERFACE, line_coding->bit_rate);
|
||||
|
||||
switch (line_coding->parity) {
|
||||
case CDC_LINE_CODING_PARITY_ODD:
|
||||
parity = UART_PARITY_ODD;
|
||||
break;
|
||||
case CDC_LINE_CODING_PARITY_EVEN:
|
||||
parity = UART_PARITY_EVEN;
|
||||
break;
|
||||
default:
|
||||
picoprobe_info("invalid parity setting %u\n", line_coding->parity);
|
||||
/* fallthrough */
|
||||
case CDC_LINE_CODING_PARITY_NONE:
|
||||
parity = UART_PARITY_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (line_coding->data_bits) {
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
data_bits = line_coding->data_bits;
|
||||
break;
|
||||
default:
|
||||
picoprobe_info("invalid data bits setting: %u\n", line_coding->data_bits);
|
||||
data_bits = 8;
|
||||
break;
|
||||
}
|
||||
|
||||
/* The PL011 only supports 1 or 2 stop bits. 1.5 stop bits is translated to 2,
|
||||
* which is safer than the alternative. */
|
||||
switch (line_coding->stop_bits) {
|
||||
case CDC_LINE_CONDING_STOP_BITS_1_5:
|
||||
case CDC_LINE_CONDING_STOP_BITS_2:
|
||||
stop_bits = 2;
|
||||
break;
|
||||
default:
|
||||
picoprobe_info("invalid stop bits setting: %u\n", line_coding->stop_bits);
|
||||
/* fallthrough */
|
||||
case CDC_LINE_CONDING_STOP_BITS_1:
|
||||
stop_bits = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
uart_set_format(PICOPROBE_UART_INTERFACE, data_bits, stop_bits, parity);
|
||||
vTaskResume(uart_taskhandle);
|
||||
}
|
||||
|
||||
|
||||
27
src/main.c
27
src/main.c
@@ -39,6 +39,7 @@
|
||||
#include "cdc_uart.h"
|
||||
#include "get_serial.h"
|
||||
#include "led.h"
|
||||
#include "tusb_edpt_handler.h"
|
||||
#include "DAP.h"
|
||||
|
||||
// UART0 for Picoprobe debug
|
||||
@@ -53,10 +54,12 @@ static uint8_t RxDataBuffer[CFG_TUD_HID_EP_BUFSIZE];
|
||||
#define TUD_TASK_PRIO (tskIDLE_PRIORITY + 2)
|
||||
#define DAP_TASK_PRIO (tskIDLE_PRIORITY + 1)
|
||||
|
||||
static TaskHandle_t dap_taskhandle, tud_taskhandle;
|
||||
TaskHandle_t dap_taskhandle, tud_taskhandle;
|
||||
|
||||
void usb_thread(void *ptr)
|
||||
{
|
||||
TickType_t wake;
|
||||
wake = xTaskGetTickCount();
|
||||
do {
|
||||
tud_task();
|
||||
#ifdef PICOPROBE_USB_CONNECTED_LED
|
||||
@@ -65,8 +68,9 @@ void usb_thread(void *ptr)
|
||||
else
|
||||
gpio_put(PICOPROBE_USB_CONNECTED_LED, 0);
|
||||
#endif
|
||||
// Trivial delay to save power
|
||||
vTaskDelay(1);
|
||||
// Go to sleep for up to a tick if nothing to do
|
||||
if (!tud_task_event_ready())
|
||||
xTaskDelayUntil(&wake, 1);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
@@ -75,22 +79,6 @@ void usb_thread(void *ptr)
|
||||
#define tud_vendor_flush(x) ((void)0)
|
||||
#endif
|
||||
|
||||
void dap_thread(void *ptr)
|
||||
{
|
||||
uint32_t resp_len;
|
||||
do {
|
||||
if (tud_vendor_available()) {
|
||||
tud_vendor_read(RxDataBuffer, sizeof(RxDataBuffer));
|
||||
resp_len = DAP_ProcessCommand(RxDataBuffer, TxDataBuffer);
|
||||
tud_vendor_write(TxDataBuffer, resp_len);
|
||||
tud_vendor_flush();
|
||||
} else {
|
||||
// Trivial delay to save power
|
||||
vTaskDelay(1);
|
||||
}
|
||||
} while (1);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
|
||||
board_init();
|
||||
@@ -99,6 +87,7 @@ int main(void) {
|
||||
tusb_init();
|
||||
|
||||
DAP_Setup();
|
||||
stdio_uart_init();
|
||||
|
||||
led_init();
|
||||
|
||||
|
||||
@@ -26,29 +26,50 @@
|
||||
#ifndef PICOPROBE_H_
|
||||
#define PICOPROBE_H_
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
#if false
|
||||
#define picoprobe_info(format,args...) printf(format, ## args)
|
||||
#define picoprobe_info(format,args...) \
|
||||
do { \
|
||||
vTaskSuspendAll(); \
|
||||
printf(format, ## args); \
|
||||
xTaskResumeAll(); \
|
||||
} while (0)
|
||||
#else
|
||||
#define picoprobe_info(format,...) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
#if false
|
||||
#define picoprobe_debug(format,args...) printf(format, ## args)
|
||||
#define picoprobe_debug(format,args...) \
|
||||
do { \
|
||||
vTaskSuspendAll(); \
|
||||
printf(format, ## args); \
|
||||
xTaskResumeAll(); \
|
||||
} while (0)
|
||||
#else
|
||||
#define picoprobe_debug(format,...) ((void)0)
|
||||
#endif
|
||||
|
||||
#if false
|
||||
#define picoprobe_dump(format,args...) printf(format, ## args)
|
||||
#define picoprobe_dump(format,args...)\
|
||||
do { \
|
||||
vTaskSuspendAll(); \
|
||||
printf(format, ## args); \
|
||||
xTaskResumeAll(); \
|
||||
} while (0)
|
||||
#else
|
||||
#define picoprobe_dump(format,...) ((void)0)
|
||||
#endif
|
||||
|
||||
// TODO tie this up with PICO_BOARD defines in the main SDK
|
||||
|
||||
#ifndef DEBUGPROBE
|
||||
#include "board_pico_config.h"
|
||||
//#include "board_debugprobe_config.h"
|
||||
#else
|
||||
#include "board_debugprobe_config.h"
|
||||
#endif
|
||||
//#include "board_example_config.h"
|
||||
|
||||
|
||||
|
||||
11
src/probe.c
11
src/probe.c
@@ -76,6 +76,15 @@ void probe_assert_reset(bool state)
|
||||
#endif
|
||||
}
|
||||
|
||||
int probe_reset_level(void)
|
||||
{
|
||||
#if defined(PROBE_PIN_RESET)
|
||||
return gpio_get(PROBE_PIN_RESET);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef enum probe_pio_command {
|
||||
CMD_WRITE = 0,
|
||||
CMD_SKIP,
|
||||
@@ -157,8 +166,8 @@ void probe_init() {
|
||||
|
||||
void probe_deinit(void)
|
||||
{
|
||||
probe_read_mode();
|
||||
if (probe.initted) {
|
||||
probe_read_mode();
|
||||
pio_sm_set_enabled(pio0, PROBE_SM, 0);
|
||||
pio_remove_program(pio0, &probe_program, probe.offset);
|
||||
probe.initted = 0;
|
||||
|
||||
@@ -46,5 +46,7 @@ void probe_write_mode(void);
|
||||
|
||||
void probe_init(void);
|
||||
void probe_deinit(void);
|
||||
void probe_assert_reset(bool state);
|
||||
int probe_reset_level(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -68,8 +68,15 @@
|
||||
#define CFG_TUD_MIDI 0
|
||||
#define CFG_TUD_VENDOR 1
|
||||
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE 64
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE 64
|
||||
/*
|
||||
* TX bufsize (actually UART RX) is oversized because the Windows CDC-ACM
|
||||
* driver submits a grand total of _one_ URB at any one time.
|
||||
* This means the application must consume the data before the next IN token
|
||||
* is issued. At high datarates this leads to huge variation in instantaneous
|
||||
* throughput on USB, so a large runway is needed.
|
||||
*/
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE 128
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE 4096
|
||||
|
||||
#define CFG_TUD_VENDOR_RX_BUFSIZE 8192
|
||||
#define CFG_TUD_VENDOR_TX_BUFSIZE 8192
|
||||
|
||||
218
src/tusb_edpt_handler.c
Normal file
218
src/tusb_edpt_handler.c
Normal file
@@ -0,0 +1,218 @@
|
||||
/**
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "tusb_edpt_handler.h"
|
||||
#include "DAP.h"
|
||||
|
||||
static uint8_t itf_num;
|
||||
static uint8_t _rhport;
|
||||
|
||||
volatile uint32_t _resp_len;
|
||||
|
||||
uint8_t _out_ep_addr;
|
||||
uint8_t _in_ep_addr;
|
||||
|
||||
buffer_t USBRequestBuffer;
|
||||
buffer_t USBResponseBuffer;
|
||||
|
||||
void dap_edpt_init(void) {
|
||||
|
||||
}
|
||||
|
||||
void dap_edpt_reset(uint8_t __unused rhport)
|
||||
{
|
||||
itf_num = 0;
|
||||
}
|
||||
|
||||
uint16_t dap_edpt_open(uint8_t __unused rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
|
||||
{
|
||||
|
||||
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass &&
|
||||
PICOPROBE_INTERFACE_SUBCLASS == itf_desc->bInterfaceSubClass &&
|
||||
PICOPROBE_INTERFACE_PROTOCOL == itf_desc->bInterfaceProtocol, 0);
|
||||
|
||||
// Initialise circular buffer indices
|
||||
USBResponseBuffer.packet_wr_idx = 0;
|
||||
USBResponseBuffer.packet_rd_idx = 0;
|
||||
USBRequestBuffer.packet_wr_idx = 0;
|
||||
USBRequestBuffer.packet_rd_idx = 0;
|
||||
|
||||
// Initialse full/empty flags
|
||||
USBResponseBuffer.wasFull = false;
|
||||
USBResponseBuffer.wasEmpty = true;
|
||||
USBRequestBuffer.wasFull = false;
|
||||
USBRequestBuffer.wasEmpty = true;
|
||||
|
||||
uint16_t const drv_len = sizeof(tusb_desc_interface_t) + (itf_desc->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
|
||||
TU_VERIFY(max_len >= drv_len, 0);
|
||||
itf_num = itf_desc->bInterfaceNumber;
|
||||
|
||||
// Initialising the OUT endpoint
|
||||
|
||||
tusb_desc_endpoint_t *edpt_desc = (tusb_desc_endpoint_t *) (itf_desc + 1);
|
||||
uint8_t ep_addr = edpt_desc->bEndpointAddress;
|
||||
|
||||
_out_ep_addr = ep_addr;
|
||||
|
||||
// The OUT endpoint requires a call to usbd_edpt_xfer to initialise the endpoint, giving tinyUSB a buffer to consume when a transfer occurs at the endpoint
|
||||
usbd_edpt_open(rhport, edpt_desc);
|
||||
usbd_edpt_xfer(rhport, ep_addr, &(USBRequestBuffer.data[USBRequestBuffer.packet_wr_idx][0]), DAP_PACKET_SIZE);
|
||||
|
||||
// Initiliasing the IN endpoint
|
||||
|
||||
edpt_desc++;
|
||||
ep_addr = edpt_desc->bEndpointAddress;
|
||||
|
||||
_in_ep_addr = ep_addr;
|
||||
|
||||
// The IN endpoint doesn't need a transfer to initialise it, as this will be done by the main loop of dap_thread
|
||||
usbd_edpt_open(rhport, edpt_desc);
|
||||
|
||||
return drv_len;
|
||||
|
||||
}
|
||||
|
||||
bool dap_edpt_control_xfer_cb(uint8_t __unused rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Manage USBResponseBuffer (request) write and USBRequestBuffer (response) read indices
|
||||
bool dap_edpt_xfer_cb(uint8_t __unused rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||
{
|
||||
const uint8_t ep_dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
if(ep_dir == TUSB_DIR_IN)
|
||||
{
|
||||
if(xferred_bytes >= 0u && xferred_bytes <= DAP_PACKET_SIZE)
|
||||
{
|
||||
USBResponseBuffer.packet_rd_idx = (USBResponseBuffer.packet_rd_idx + 1) % DAP_PACKET_COUNT;
|
||||
|
||||
// This checks that the buffer was not empty in DAP thread, which means the next buffer was not queued up for the in endpoint callback
|
||||
// So, queue up the buffer at the new read index, since we expect read to catch up to write at this point.
|
||||
// It is possible for the read index to be multiple spaces behind the write index (if the USB callbacks are lagging behind dap thread),
|
||||
// so we account for this by only setting wasEmpty to true if the next callback will empty the buffer
|
||||
if(!USBResponseBuffer.wasEmpty)
|
||||
{
|
||||
usbd_edpt_xfer(rhport, ep_addr, &(USBResponseBuffer.data[USBResponseBuffer.packet_rd_idx][0]), (uint16_t) _resp_len);
|
||||
USBResponseBuffer.wasEmpty = ((USBResponseBuffer.packet_rd_idx + 1) % DAP_PACKET_COUNT == USBResponseBuffer.packet_wr_idx);
|
||||
}
|
||||
|
||||
// Wake up DAP thread after processing the callback
|
||||
vTaskResume(dap_taskhandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
} else if(ep_dir == TUSB_DIR_OUT) {
|
||||
|
||||
if(xferred_bytes >= 0u && xferred_bytes <= DAP_PACKET_SIZE)
|
||||
{
|
||||
// Only queue the next buffer in the out callback if the buffer is not full
|
||||
// If full, we set the wasFull flag, which will be checked by dap thread
|
||||
if(!buffer_full(&USBRequestBuffer))
|
||||
{
|
||||
USBRequestBuffer.packet_wr_idx = (USBRequestBuffer.packet_wr_idx + 1) % DAP_PACKET_COUNT;
|
||||
usbd_edpt_xfer(rhport, ep_addr, &(USBRequestBuffer.data[USBRequestBuffer.packet_wr_idx][0]), DAP_PACKET_SIZE);
|
||||
USBRequestBuffer.wasFull = false;
|
||||
}
|
||||
else {
|
||||
USBRequestBuffer.wasFull = true;
|
||||
}
|
||||
|
||||
// Wake up DAP thread after processing the callback
|
||||
vTaskResume(dap_taskhandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
void dap_thread(void *ptr)
|
||||
{
|
||||
uint8_t DAPRequestBuffer[DAP_PACKET_SIZE];
|
||||
uint8_t DAPResponseBuffer[DAP_PACKET_SIZE];
|
||||
|
||||
do
|
||||
{
|
||||
while(USBRequestBuffer.packet_rd_idx != USBRequestBuffer.packet_wr_idx)
|
||||
{
|
||||
// Read a single packet from the USB buffer into the DAP Request buffer
|
||||
memcpy(DAPRequestBuffer, &(USBRequestBuffer.data[USBRequestBuffer.packet_rd_idx]), DAP_PACKET_SIZE);
|
||||
USBRequestBuffer.packet_rd_idx = (USBRequestBuffer.packet_rd_idx + 1) % DAP_PACKET_COUNT;
|
||||
|
||||
// If the buffer was full in the out callback, we need to queue up another buffer for the endpoint to consume, now that we know there is space in the buffer.
|
||||
if(USBRequestBuffer.wasFull)
|
||||
{
|
||||
vTaskSuspendAll(); // Suspend the scheduler to safely update the write index
|
||||
USBRequestBuffer.packet_wr_idx = (USBRequestBuffer.packet_wr_idx + 1) % DAP_PACKET_COUNT;
|
||||
usbd_edpt_xfer(_rhport, _out_ep_addr, &(USBRequestBuffer.data[USBRequestBuffer.packet_wr_idx][0]), DAP_PACKET_SIZE);
|
||||
USBRequestBuffer.wasFull = false;
|
||||
xTaskResumeAll();
|
||||
}
|
||||
|
||||
_resp_len = DAP_ProcessCommand(DAPRequestBuffer, DAPResponseBuffer);
|
||||
|
||||
|
||||
// Suspend the scheduler to avoid stale values/race conditions between threads
|
||||
vTaskSuspendAll();
|
||||
|
||||
if(buffer_empty(&USBResponseBuffer))
|
||||
{
|
||||
memcpy(&(USBResponseBuffer.data[USBResponseBuffer.packet_wr_idx]), DAPResponseBuffer, (uint16_t) _resp_len);
|
||||
USBResponseBuffer.packet_wr_idx = (USBResponseBuffer.packet_wr_idx + 1) % DAP_PACKET_COUNT;
|
||||
|
||||
usbd_edpt_xfer(_rhport, _in_ep_addr, &(USBResponseBuffer.data[USBResponseBuffer.packet_rd_idx][0]), (uint16_t) _resp_len);
|
||||
} else {
|
||||
|
||||
memcpy(&(USBResponseBuffer.data[USBResponseBuffer.packet_wr_idx]), DAPResponseBuffer, (uint16_t) _resp_len);
|
||||
USBResponseBuffer.packet_wr_idx = (USBResponseBuffer.packet_wr_idx + 1) % DAP_PACKET_COUNT;
|
||||
|
||||
// The In callback needs to check this flag to know when to queue up the next buffer.
|
||||
USBResponseBuffer.wasEmpty = false;
|
||||
}
|
||||
xTaskResumeAll();
|
||||
}
|
||||
|
||||
// Suspend DAP thread until it is awoken by a USB thread callback
|
||||
vTaskSuspend(dap_taskhandle);
|
||||
|
||||
} while (1);
|
||||
|
||||
}
|
||||
|
||||
usbd_class_driver_t const _dap_edpt_driver =
|
||||
{
|
||||
.init = dap_edpt_init,
|
||||
.reset = dap_edpt_reset,
|
||||
.open = dap_edpt_open,
|
||||
.control_xfer_cb = dap_edpt_control_xfer_cb,
|
||||
.xfer_cb = dap_edpt_xfer_cb,
|
||||
.sof = NULL,
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
.name = "PICOPROBE ENDPOINT"
|
||||
#endif
|
||||
};
|
||||
|
||||
// Add the custom driver to the tinyUSB stack
|
||||
usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count)
|
||||
{
|
||||
*driver_count = 1;
|
||||
return &_dap_edpt_driver;
|
||||
}
|
||||
|
||||
bool buffer_full(buffer_t *buffer)
|
||||
{
|
||||
return ((buffer->packet_wr_idx + 1) % DAP_PACKET_COUNT == buffer->packet_rd_idx);
|
||||
}
|
||||
|
||||
bool buffer_empty(buffer_t *buffer)
|
||||
{
|
||||
return (buffer->packet_wr_idx == buffer->packet_rd_idx);
|
||||
}
|
||||
41
src/tusb_edpt_handler.h
Normal file
41
src/tusb_edpt_handler.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef TUSB_EDPT_HANDLER_H
|
||||
#define TUSB_EDPT_HANDLER_H
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
#include "device/usbd_pvt.h"
|
||||
#include "DAP_config.h"
|
||||
|
||||
#define PICOPROBE_INTERFACE_SUBCLASS 0x00
|
||||
#define PICOPROBE_INTERFACE_PROTOCOL 0x00
|
||||
|
||||
typedef struct {
|
||||
uint8_t data[DAP_PACKET_COUNT][DAP_PACKET_SIZE];
|
||||
volatile uint32_t packet_wr_idx;
|
||||
volatile uint32_t packet_rd_idx;
|
||||
volatile bool wasEmpty;
|
||||
volatile bool wasFull;
|
||||
} buffer_t;
|
||||
|
||||
extern TaskHandle_t dap_taskhandle, tud_taskhandle;
|
||||
|
||||
/* Main DAP loop */
|
||||
void dap_thread(void *ptr);
|
||||
|
||||
/* Endpoint Handling */
|
||||
void picoprobe_edpt_init(void);
|
||||
uint16_t picoprobe_edpt_open(uint8_t __unused rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
|
||||
bool picoprobe_edpt_control_xfer_cb(uint8_t __unused rhport, uint8_t stage, tusb_control_request_t const *request);
|
||||
bool picoprobe_edpt_xfer_cb(uint8_t __unused rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
|
||||
/* Helper Functions */
|
||||
bool buffer_full(buffer_t *buffer);
|
||||
bool buffer_empty(buffer_t *buffer);
|
||||
|
||||
#endif
|
||||
@@ -48,7 +48,7 @@ tusb_desc_device_t const desc_device =
|
||||
|
||||
.idVendor = 0x2E8A, // Pi
|
||||
.idProduct = 0x000c, // CMSIS-DAP Debug Probe
|
||||
.bcdDevice = 0x0101, // Version 01.01
|
||||
.bcdDevice = 0x0103, // Version 01.03
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
@@ -99,7 +99,7 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf)
|
||||
|
||||
uint8_t const desc_configuration[] =
|
||||
{
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0, 100),
|
||||
// Interface 0
|
||||
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V1)
|
||||
// HID (named interface)
|
||||
@@ -242,4 +242,4 @@ TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size");
|
||||
uint8_t const * tud_descriptor_bos_cb(void)
|
||||
{
|
||||
return desc_bos;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user