Compare commits

..

1 Commits

Author SHA1 Message Date
Jonathan Bell
0b6f8db41a cdc_uart: support databits, stopbits and parity setup 2023-09-18 16:17:29 +01:00
4 changed files with 150 additions and 234 deletions

View File

@@ -68,9 +68,6 @@
#define PICOPROBE_UART_RX 5
#define PICOPROBE_UART_INTERFACE uart1
#define PICOPROBE_UART_BAUDRATE 115200
/* Flow control - some or all of these can be omitted if not used */
#define PICOPROBE_UART_RTS 9
#define PICOPROBE_UART_DTR 10
#endif
/* LED config - some or all of these can be omitted if not used */

View File

@@ -55,17 +55,6 @@ void cdc_uart_init(void) {
gpio_set_pulls(PICOPROBE_UART_TX, 1, 0);
gpio_set_pulls(PICOPROBE_UART_RX, 1, 0);
uart_init(PICOPROBE_UART_INTERFACE, PICOPROBE_UART_BAUDRATE);
#ifdef PICOPROBE_UART_RTS
gpio_init(PICOPROBE_UART_RTS);
gpio_set_dir(PICOPROBE_UART_RTS, GPIO_OUT);
gpio_put(PICOPROBE_UART_RTS, 1);
#endif
#ifdef PICOPROBE_UART_DTR
gpio_init(PICOPROBE_UART_DTR);
gpio_set_dir(PICOPROBE_UART_DTR, GPIO_OUT);
gpio_put(PICOPROBE_UART_DTR, 1);
#endif
}
void cdc_task(void)
@@ -214,13 +203,6 @@ void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding)
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
{
#ifdef PICOPROBE_UART_RTS
gpio_put(PICOPROBE_UART_RTS, !rts);
#endif
#ifdef PICOPROBE_UART_DTR
gpio_put(PICOPROBE_UART_DTR, !dtr);
#endif
/* CDC drivers use linestate as a bodge to activate/deactivate the interface.
* Resume our UART polling on activate, stop on deactivate */
if (!dtr && !rts) {

View File

@@ -12,270 +12,207 @@ static uint8_t _rhport;
volatile uint32_t _resp_len;
static uint8_t _out_ep_addr;
static uint8_t _in_ep_addr;
uint8_t _out_ep_addr;
uint8_t _in_ep_addr;
static buffer_t USBRequestBuffer;
static buffer_t USBResponseBuffer;
static uint8_t DAPRequestBuffer[DAP_PACKET_SIZE];
static uint8_t DAPResponseBuffer[DAP_PACKET_SIZE];
#define WR_IDX(x) (x.wptr % DAP_PACKET_COUNT)
#define RD_IDX(x) (x.rptr % DAP_PACKET_COUNT)
#define WR_SLOT_PTR(x) &(x.data[WR_IDX(x)][0])
#define RD_SLOT_PTR(x) &(x.data[RD_IDX(x)][0])
bool buffer_full(buffer_t *buffer)
{
return ((buffer->wptr + 1) % DAP_PACKET_COUNT == buffer->rptr);
}
bool buffer_empty(buffer_t *buffer)
{
return (buffer->wptr == buffer->rptr);
}
buffer_t USBRequestBuffer;
buffer_t USBResponseBuffer;
void dap_edpt_init(void) {
}
void dap_edpt_reset(uint8_t __unused rhport)
{
itf_num = 0;
itf_num = 0;
}
char * dap_cmd_string[] = {
[ID_DAP_Info ] = "DAP_Info",
[ID_DAP_HostStatus ] = "DAP_HostStatus",
[ID_DAP_Connect ] = "DAP_Connect",
[ID_DAP_Disconnect ] = "DAP_Disconnect",
[ID_DAP_TransferConfigure ] = "DAP_TransferConfigure",
[ID_DAP_Transfer ] = "DAP_Transfer",
[ID_DAP_TransferBlock ] = "DAP_TransferBlock",
[ID_DAP_TransferAbort ] = "DAP_TransferAbort",
[ID_DAP_WriteABORT ] = "DAP_WriteABORT",
[ID_DAP_Delay ] = "DAP_Delay",
[ID_DAP_ResetTarget ] = "DAP_ResetTarget",
[ID_DAP_SWJ_Pins ] = "DAP_SWJ_Pins",
[ID_DAP_SWJ_Clock ] = "DAP_SWJ_Clock",
[ID_DAP_SWJ_Sequence ] = "DAP_SWJ_Sequence",
[ID_DAP_SWD_Configure ] = "DAP_SWD_Configure",
[ID_DAP_SWD_Sequence ] = "DAP_SWD_Sequence",
[ID_DAP_JTAG_Sequence ] = "DAP_JTAG_Sequence",
[ID_DAP_JTAG_Configure ] = "DAP_JTAG_Configure",
[ID_DAP_JTAG_IDCODE ] = "DAP_JTAG_IDCODE",
[ID_DAP_SWO_Transport ] = "DAP_SWO_Transport",
[ID_DAP_SWO_Mode ] = "DAP_SWO_Mode",
[ID_DAP_SWO_Baudrate ] = "DAP_SWO_Baudrate",
[ID_DAP_SWO_Control ] = "DAP_SWO_Control",
[ID_DAP_SWO_Status ] = "DAP_SWO_Status",
[ID_DAP_SWO_ExtendedStatus ] = "DAP_SWO_ExtendedStatus",
[ID_DAP_SWO_Data ] = "DAP_SWO_Data",
[ID_DAP_QueueCommands ] = "DAP_QueueCommands",
[ID_DAP_ExecuteCommands ] = "DAP_ExecuteCommands",
};
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);
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;
// Initialise circular buffer indices
USBResponseBuffer.wptr = 0;
USBResponseBuffer.rptr = 0;
USBRequestBuffer.wptr = 0;
USBRequestBuffer.rptr = 0;
// Initialse full/empty flags
USBResponseBuffer.wasFull = false;
USBResponseBuffer.wasEmpty = true;
USBRequestBuffer.wasFull = false;
USBRequestBuffer.wasEmpty = true;
// 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;
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
// Initialising the OUT endpoint
tusb_desc_endpoint_t *edpt_desc = (tusb_desc_endpoint_t *) (itf_desc + 1);
uint8_t ep_addr = edpt_desc->bEndpointAddress;
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;
_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);
// 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, WR_SLOT_PTR(USBRequestBuffer), DAP_PACKET_SIZE);
// Initiliasing the IN endpoint
// Initiliasing the IN endpoint
edpt_desc++;
ep_addr = edpt_desc->bEndpointAddress;
edpt_desc++;
ep_addr = edpt_desc->bEndpointAddress;
_in_ep_addr = ep_addr;
_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);
// 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;
return drv_len;
}
bool dap_edpt_control_xfer_cb(uint8_t __unused rhport, uint8_t stage, tusb_control_request_t const *request)
{
return false;
{
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);
{
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.rptr++;
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, RD_SLOT_PTR(USBResponseBuffer), (uint16_t) _resp_len);
USBResponseBuffer.wasEmpty = (USBResponseBuffer.rptr + 1) == USBResponseBuffer.wptr;
}
// 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;
}
// Wake up DAP thread after processing the callback
vTaskResume(dap_taskhandle);
return true;
}
return false;
return false;
} else if(ep_dir == TUSB_DIR_OUT) {
} 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;
}
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.wptr++;
usbd_edpt_xfer(rhport, ep_addr, WR_SLOT_PTR(USBRequestBuffer), DAP_PACKET_SIZE);
USBRequestBuffer.wasFull = false;
}
else {
USBRequestBuffer.wasFull = true;
}
// Wake up DAP thread after processing the callback
vTaskResume(dap_taskhandle);
return true;
}
// Wake up DAP thread after processing the callback
vTaskResume(dap_taskhandle);
return true;
}
return false;
}
else return false;
return false;
}
else return false;
}
void dap_thread(void *ptr)
{
uint32_t n;
do
{
while(USBRequestBuffer.rptr != USBRequestBuffer.wptr)
{
/*
* Atomic command support - buffer QueueCommands, but don't process them
* until a non-QueueCommands packet is seen.
*/
n = USBRequestBuffer.rptr;
while (USBRequestBuffer.data[n % DAP_PACKET_COUNT][0] == ID_DAP_QueueCommands) {
picoprobe_info("%u %u DAP queued cmd %s len %02x\n",
USBRequestBuffer.wptr, USBRequestBuffer.rptr,
dap_cmd_string[USBRequestBuffer.data[n % DAP_PACKET_COUNT][0]], USBRequestBuffer.data[n % DAP_PACKET_COUNT][1]);
USBRequestBuffer.data[n % DAP_PACKET_COUNT][0] = ID_DAP_ExecuteCommands;
n++;
while (n == USBRequestBuffer.wptr) {
/* Need yield in a loop here, as IN callbacks will also wake the thread */
picoprobe_info("DAP wait\n");
vTaskSuspend(dap_taskhandle);
}
}
// Read a single packet from the USB buffer into the DAP Request buffer
memcpy(DAPRequestBuffer, RD_SLOT_PTR(USBRequestBuffer), DAP_PACKET_SIZE);
picoprobe_info("%u %u DAP cmd %s len %02x\n",
USBRequestBuffer.wptr, USBRequestBuffer.rptr,
dap_cmd_string[DAPRequestBuffer[0]], DAPRequestBuffer[1]);
USBRequestBuffer.rptr++;
uint8_t DAPRequestBuffer[DAP_PACKET_SIZE];
uint8_t DAPResponseBuffer[DAP_PACKET_SIZE];
// 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.wptr++;
usbd_edpt_xfer(_rhport, _out_ep_addr, WR_SLOT_PTR(USBRequestBuffer), DAP_PACKET_SIZE);
USBRequestBuffer.wasFull = false;
xTaskResumeAll();
}
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;
_resp_len = DAP_ExecuteCommand(DAPRequestBuffer, DAPResponseBuffer);
picoprobe_info("%u %u DAP resp %s\n",
USBResponseBuffer.wptr, USBResponseBuffer.rptr,
dap_cmd_string[DAPResponseBuffer[0]]);
// 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();
// Suspend the scheduler to avoid stale values/race conditions between threads
vTaskSuspendAll();
if(buffer_empty(&USBResponseBuffer))
{
memcpy(WR_SLOT_PTR(USBResponseBuffer), DAPResponseBuffer, (uint16_t) _resp_len);
USBResponseBuffer.wptr++;
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, RD_SLOT_PTR(USBResponseBuffer), (uint16_t) _resp_len);
} else {
usbd_edpt_xfer(_rhport, _in_ep_addr, &(USBResponseBuffer.data[USBResponseBuffer.packet_rd_idx][0]), (uint16_t) _resp_len);
} else {
memcpy(WR_SLOT_PTR(USBResponseBuffer), DAPResponseBuffer, (uint16_t) _resp_len);
USBResponseBuffer.wptr++;
// 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);
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
.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;
*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);
}

View File

@@ -16,11 +16,11 @@
#define PICOPROBE_INTERFACE_PROTOCOL 0x00
typedef struct {
uint8_t data[DAP_PACKET_COUNT][DAP_PACKET_SIZE];
volatile uint32_t wptr;
volatile uint32_t rptr;
volatile bool wasEmpty;
volatile bool wasFull;
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;
@@ -38,4 +38,4 @@ bool picoprobe_edpt_xfer_cb(uint8_t __unused rhport, uint8_t ep_addr, xfer_resul
bool buffer_full(buffer_t *buffer);
bool buffer_empty(buffer_t *buffer);
#endif
#endif