Merge remote-tracking branch 'upstream/edpt_ISO_xfer' into edpt_ISO_xfer

This commit is contained in:
Reinhard Panhuber
2021-03-10 19:33:11 +01:00
149 changed files with 2305 additions and 3085 deletions

View File

@@ -396,6 +396,17 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
if ( tud_cdc_line_state_cb ) tud_cdc_line_state_cb(itf, dtr, rts);
}
break;
case CDC_REQUEST_SEND_BREAK:
if (stage == CONTROL_STAGE_SETUP)
{
tud_control_status(rhport, request);
}
else if (stage == CONTROL_STAGE_ACK)
{
TU_LOG2(" Send Break\r\n");
if ( tud_cdc_send_break_cb ) tud_cdc_send_break_cb(itf, request->wValue);
}
break;
default: return false; // stall unsupported request
}

View File

@@ -145,6 +145,9 @@ TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts);
// Invoked when line coding is change via SET_LINE_CODING
TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding);
// Invoked when received send break
TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms);
//--------------------------------------------------------------------+
// Inline Functions
//--------------------------------------------------------------------+

View File

@@ -387,6 +387,7 @@ uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint
p_midi->ep_out = ep_addr;
}
// Class Specific MIDI Stream endpoint descriptor
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);

View File

@@ -53,6 +53,7 @@ extern "C" {
#endif
#if CFG_FIFO_MUTEX
#include "osal/osal.h"
#define tu_fifo_mutex_t osal_mutex_t
#endif
@@ -108,7 +109,7 @@ static inline void tu_fifo_config_mutex(tu_fifo_t *f, tu_fifo_mutex_t write_mute
bool tu_fifo_write (tu_fifo_t* f, void const * p_data);
uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t n);
uint16_t tu_fifo_write_n_const_addr (tu_fifo_t* f, const void * data, uint16_t n);
uint16_t tu_fifo_write_n_const_addr (tu_fifo_t* f, const void * data, uint16_t n);
bool tu_fifo_read (tu_fifo_t* f, void * p_buffer);
uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t n);

View File

@@ -134,8 +134,9 @@ void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) TU_ATTR_WEAK;
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
// Submit an ISO transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes);
// Submit an transfer using fifo, When complete dcd_event_xfer_complete() is invoked to notify the stack
// This API is optional, may be useful for register-based for transferring data.
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) TU_ATTR_WEAK;
// Stall endpoint
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr);

View File

@@ -699,13 +699,21 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
// notable requests are: GET HID REPORT DESCRIPTOR, SET_INTERFACE, GET_INTERFACE
if ( !invoke_class_control(rhport, driver, p_request) )
{
// For GET_INTERFACE, it is mandatory to respond even if the class
// driver doesn't use alternate settings.
TU_VERIFY( TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type &&
TUSB_REQ_GET_INTERFACE == p_request->bRequest);
// For GET_INTERFACE and SET_INTERFACE, it is mandatory to respond even if the class
// driver doesn't use alternate settings or implement this
TU_VERIFY(TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type);
uint8_t alternate = 0;
tud_control_xfer(rhport, p_request, &alternate, 1);
if (TUSB_REQ_GET_INTERFACE == p_request->bRequest)
{
uint8_t alternate = 0;
tud_control_xfer(rhport, p_request, &alternate, 1);
}else if (TUSB_REQ_SET_INTERFACE == p_request->bRequest)
{
tud_control_status(rhport, p_request);
} else
{
return false;
}
}
}
break;
@@ -992,11 +1000,15 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
switch (event->event_id)
{
case DCD_EVENT_UNPLUGGED:
_usbd_dev.connected = 0;
_usbd_dev.addressed = 0;
_usbd_dev.cfg_num = 0;
_usbd_dev.suspended = 0;
osal_queue_send(_usbd_q, event, in_isr);
// UNPLUGGED event can be bouncing, only processing if we are currently connected
if ( _usbd_dev.connected )
{
_usbd_dev.connected = 0;
_usbd_dev.addressed = 0;
_usbd_dev.cfg_num = 0;
_usbd_dev.suspended = 0;
osal_queue_send(_usbd_q, event, in_isr);
}
break;
case DCD_EVENT_SOF:
@@ -1004,9 +1016,10 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
break;
case DCD_EVENT_SUSPEND:
// NOTE: When plugging/unplugging device, the D+/D- state are unstable and can accidentally meet the
// SUSPEND condition ( Idle for 3ms ). Some MCUs such as SAMD doesn't distinguish suspend vs disconnect as well.
// We will skip handling SUSPEND/RESUME event if not currently connected
// NOTE: When plugging/unplugging device, the D+/D- state are unstable and
// can accidentally meet the SUSPEND condition ( Bus Idle for 3ms ).
// In addition, some MCUs such as SAMD or boards that haven no VBUS detection cannot distinguish
// suspended vs disconnected. We will skip handling SUSPEND/RESUME event if not currently connected
if ( _usbd_dev.connected )
{
_usbd_dev.suspended = 1;

View File

@@ -254,6 +254,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval
//------------- MIDI -------------//
// MIDI v1.0 is based on Audio v1.0
#define TUD_MIDI_DESC_HEAD_LEN (9 + 9 + 9 + 7)
#define TUD_MIDI_DESC_HEAD(_itfnum, _stridx, _numcables) \
@@ -289,10 +290,10 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
/* MS Out Jack (External), connected to In Jack Embedded */\
9, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_OUT_JACK, MIDI_JACK_EXTERNAL, TUD_MIDI_JACKID_OUT_EXT(_cablenum), 1, TUD_MIDI_JACKID_IN_EMB(_cablenum), 1, 0
#define TUD_MIDI_DESC_EP_LEN(_numcables) (7 + 4 + (_numcables))
#define TUD_MIDI_DESC_EP_LEN(_numcables) (9 + 4 + (_numcables))
#define TUD_MIDI_DESC_EP(_epout, _epsize, _numcables) \
/* Endpoint */\
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\
/* Endpoint: Note Audio v1.0's endpoint has 9 bytes instead of 7 */\
9, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0, 0, 0, \
/* MS Endpoint (connected to embedded jack) */\
(uint8_t)(4 + (_numcables)), TUSB_DESC_CS_ENDPOINT, MIDI_CS_ENDPOINT_GENERAL, _numcables

View File

@@ -157,6 +157,7 @@ tusb_speed_t tuh_device_get_speed (uint8_t const dev_addr)
return (tusb_speed_t) _usbh_devices[dev_addr].speed;
}
#if CFG_TUSB_OS == OPT_OS_NONE
void osal_task_delay(uint32_t msec)
{
(void) msec;
@@ -164,6 +165,7 @@ void osal_task_delay(uint32_t msec)
const uint32_t start = hcd_frame_number(TUH_OPT_RHPORT);
while ( ( hcd_frame_number(TUH_OPT_RHPORT) - start ) < msec ) {}
}
#endif
//--------------------------------------------------------------------+
// CLASS-USBD API (don't require to verify parameters)

View File

@@ -66,7 +66,6 @@ typedef void (*osal_task_func_t)( void * );
//--------------------------------------------------------------------+
// OSAL Porting API
//--------------------------------------------------------------------+
//static inline void osal_task_delay(uint32_t msec);
//------------- Semaphore -------------//
static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef);

View File

@@ -34,14 +34,7 @@
//--------------------------------------------------------------------+
// TASK API
//--------------------------------------------------------------------+
//static inline void osal_task_delay(uint32_t msec)
//{
// (void) msec;
// // TODO only used by Host stack, will implement using SOF
//
//// uint32_t start = tusb_hal_millis();
//// while ( ( tusb_hal_millis() - start ) < msec ) {}
//}
//--------------------------------------------------------------------+
// Binary Semaphore API

View File

@@ -39,12 +39,10 @@
//--------------------------------------------------------------------+
// TASK API
//--------------------------------------------------------------------+
#ifndef RP2040_USB_HOST_MODE
static inline void osal_task_delay(uint32_t msec)
{
sleep_ms(msec);
sleep_ms(msec);
}
#endif
//--------------------------------------------------------------------+
// Binary Semaphore API
@@ -145,7 +143,7 @@ static inline bool osal_queue_receive(osal_queue_t qhdl, void* data)
// TODO: revisit... docs say that mutexes are never used from IRQ context,
// however osal_queue_recieve may be. therefore my assumption is that
// the fifo mutex is not populated for queues used from an IRQ context
assert(!qhdl->ff.mutex);
//assert(!qhdl->ff.mutex);
_osal_q_lock(qhdl);
bool success = tu_fifo_read(&qhdl->ff, data);
@@ -159,7 +157,7 @@ static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in
// TODO: revisit... docs say that mutexes are never used from IRQ context,
// however osal_queue_recieve may be. therefore my assumption is that
// the fifo mutex is not populated for queues used from an IRQ context
assert(!qhdl->ff.mutex);
//assert(!qhdl->ff.mutex);
_osal_q_lock(qhdl);
bool success = tu_fifo_write(&qhdl->ff, data);

View File

@@ -35,7 +35,8 @@
#include "pico/fix/rp2040_usb_device_enumeration.h"
#endif
#include "osal/osal.h"
#include "common/tusb_fifo.h"
#include "device/dcd.h"
/*------------------------------------------------------------------*/
@@ -48,23 +49,24 @@
// Init these in dcd_init
static uint8_t *next_buffer_ptr;
// Endpoints 0-15, direction 0 for out and 1 for in.
static struct hw_endpoint hw_endpoints[16][2] = {0};
// USB_MAX_ENDPOINTS Endpoints, direction TUSB_DIR_OUT for out and TUSB_DIR_IN for in.
static struct hw_endpoint hw_endpoints[USB_MAX_ENDPOINTS][2] = {0};
static inline struct hw_endpoint *hw_endpoint_get_by_num(uint8_t num, uint8_t in)
static inline struct hw_endpoint *hw_endpoint_get_by_num(uint8_t num, tusb_dir_t dir)
{
return &hw_endpoints[num][in];
return &hw_endpoints[num][dir];
}
static struct hw_endpoint *hw_endpoint_get_by_addr(uint8_t ep_addr)
{
uint8_t num = tu_edpt_number(ep_addr);
uint8_t in = (ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0;
return hw_endpoint_get_by_num(num, in);
tusb_dir_t dir = tu_edpt_dir(ep_addr);
return hw_endpoint_get_by_num(num, dir);
}
static void _hw_endpoint_alloc(struct hw_endpoint *ep)
{
uint size = TU_MIN(64, ep->wMaxPacketSize);
uint16_t size = tu_min16(64, ep->wMaxPacketSize);
// Assumes single buffered for now
ep->hw_data_buf = next_buffer_ptr;
@@ -99,15 +101,13 @@ static void _hw_endpoint_alloc(struct hw_endpoint *ep)
*ep->endpoint_control = reg;
}
static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t ep_addr, uint wMaxPacketSize, uint8_t transfer_type)
static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type)
{
uint8_t num = tu_edpt_number(ep_addr);
bool in = ep_addr & TUSB_DIR_IN_MASK;
const uint8_t num = tu_edpt_number(ep_addr);
const tusb_dir_t dir = tu_edpt_dir(ep_addr);
ep->ep_addr = ep_addr;
ep->in = in;
// For device, IN is a tx transfer and OUT is an rx transfer
ep->rx = in == false;
ep->num = num;
ep->rx = (dir == TUSB_DIR_OUT);
// Response to a setup packet on EP0 starts with pid of 1
ep->next_pid = num == 0 ? 1u : 0u;
@@ -131,7 +131,7 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t ep_addr, uint wMax
ep->transfer_type = transfer_type;
// Every endpoint has a buffer control register in dpram
if (ep->in)
if (dir == TUSB_DIR_IN)
{
ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].in;
}
@@ -143,7 +143,7 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t ep_addr, uint wMax
// Clear existing buffer control state
*ep->buffer_control = 0;
if (ep->num == 0)
if (num == 0)
{
// EP0 has no endpoint control register because
// the buffer offsets are fixed
@@ -155,7 +155,7 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t ep_addr, uint wMax
else
{
// Set the endpoint control register (starts at EP1, hence num-1)
if (in)
if (dir == TUSB_DIR_IN)
{
ep->endpoint_control = &usb_dpram->ep_ctrl[num-1].in;
}
@@ -194,7 +194,7 @@ static void hw_endpoint_close(uint8_t ep_addr)
}
#endif
static void hw_endpoint_init(uint8_t ep_addr, uint wMaxPacketSize, uint8_t bmAttributes)
static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t bmAttributes)
{
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
_hw_endpoint_init(ep, ep_addr, wMaxPacketSize, bmAttributes);
@@ -259,10 +259,10 @@ static void ep0_0len_status(void)
static void _hw_endpoint_stall(struct hw_endpoint *ep)
{
assert(!ep->stalled);
if (ep->num == 0)
if (tu_edpt_number(ep->ep_addr) == 0)
{
// A stall on EP0 has to be armed so it can be cleared on the next setup packet
usb_hw_set->ep_stall_arm = ep->in ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS;
usb_hw_set->ep_stall_arm = (tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS;
}
_hw_endpoint_buffer_control_set_mask32(ep, USB_BUF_CTRL_STALL);
ep->stalled = true;
@@ -276,10 +276,10 @@ static void hw_endpoint_stall(uint8_t ep_addr)
static void _hw_endpoint_clear_stall(struct hw_endpoint *ep)
{
if (ep->num == 0)
if (tu_edpt_number(ep->ep_addr) == 0)
{
// Probably already been cleared but no harm
usb_hw_clear->ep_stall_arm = ep->in ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS;
usb_hw_clear->ep_stall_arm = (tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS;
}
_hw_endpoint_buffer_control_clear_mask32(ep, USB_BUF_CTRL_STALL);
ep->stalled = false;
@@ -313,18 +313,67 @@ static void dcd_rp2040_irq(void)
hw_handle_buff_status();
}
// SE0 for 2 us or more, usually together with Bus Reset
if (status & USB_INTS_DEV_CONN_DIS_BITS)
{
handled |= USB_INTS_DEV_CONN_DIS_BITS;
if ( usb_hw->sie_status & USB_SIE_STATUS_CONNECTED_BITS )
{
// Connected: nothing to do
}else
{
// Disconnected
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true);
}
usb_hw_clear->sie_status = USB_SIE_STATUS_CONNECTED_BITS;
}
// SE0 for 2.5 us or more
if (status & USB_INTS_BUS_RESET_BITS)
{
pico_trace("BUS RESET (addr %d -> %d)\n", assigned_address, 0);
pico_trace("BUS RESET\n");
usb_hw->dev_addr_ctrl = 0;
handled |= USB_INTS_BUS_RESET_BITS;
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
usb_hw_clear->sie_status = USB_SIE_STATUS_BUS_RESET_BITS;
#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
rp2040_usb_device_enumeration_fix();
// Only run enumeration walk-around if pull up is enabled
if ( usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS )
{
rp2040_usb_device_enumeration_fix();
}
#endif
}
#if 0
// TODO Enable SUSPEND & RESUME interrupt and test later on with/without VBUS detection
/* Note from pico datasheet 4.1.2.6.4 (v1.2)
* If you enable the suspend interrupt, it is likely you will see a suspend interrupt when
* the device is first connected but the bus is idle. The bus can be idle for a few ms before
* the host begins sending start of frame packets. You will also see a suspend interrupt
* when the device is disconnected if you do not have a VBUS detect circuit connected. This is
* because without VBUS detection, it is impossible to tell the difference between
* being disconnected and suspended.
*/
if (status & USB_INTS_DEV_SUSPEND_BITS)
{
handled |= USB_INTS_DEV_SUSPEND_BITS;
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
usb_hw_clear->sie_status = USB_SIE_STATUS_SUSPENDED_BITS;
}
if (status & USB_INTS_DEV_RESUME_FROM_HOST_BITS)
{
handled |= USB_INTS_DEV_RESUME_FROM_HOST_BITS;
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
usb_hw_clear->sie_status = USB_SIE_STATUS_RESUME_BITS;
}
#endif
if (status ^ handled)
{
panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
@@ -365,8 +414,10 @@ void dcd_init (uint8_t rhport)
// Enable individual controller IRQS here. Processor interrupt enable will be used
// for the global interrupt enable...
// TODO Enable SUSPEND & RESUME interrupt
usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS;
usb_hw->inte = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS;
usb_hw->inte = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS |
USB_INTS_DEV_CONN_DIS_BITS /* | USB_INTS_DEV_SUSPEND_BITS | USB_INTS_DEV_RESUME_FROM_HOST_BITS */ ;
dcd_connect(rhport);
}
@@ -394,8 +445,9 @@ void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
void dcd_remote_wakeup(uint8_t rhport)
{
pico_info("dcd_remote_wakeup %d is not supported yet\n", rhport);
pico_info("dcd_remote_wakeup %d\n", rhport);
assert(rhport == 0);
usb_hw_set->sie_ctrl = USB_SIE_CTRL_RESUME_BITS;
}
// disconnect by disabling internal pull-up resistor on D+/D-

View File

@@ -291,14 +291,16 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t
assert(ep->buffer_control);
assert(ep->hw_data_buf);
uint8_t num = tu_edpt_number(ep_addr);
uint8_t const num = tu_edpt_number(ep_addr);
tusb_dir_t const dir = tu_edpt_dir(ep_addr);
bool in = ep_addr & TUSB_DIR_IN_MASK;
ep->ep_addr = ep_addr;
ep->dev_addr = dev_addr;
ep->in = in;
// For host, IN to host == RX, anything else rx == false
ep->rx = in == true;
ep->num = num;
ep->rx = (dir == TUSB_DIR_IN);
// Response to a setup packet on EP0 starts with pid of 1
ep->next_pid = num == 0 ? 1u : 0u;
ep->wMaxPacketSize = wMaxPacketSize;
@@ -327,9 +329,9 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t
// device address
// endpoint number / direction
// preamble
uint32_t reg = dev_addr | (ep->num << USB_ADDR_ENDP1_ENDPOINT_LSB);
uint32_t reg = dev_addr | (num << USB_ADDR_ENDP1_ENDPOINT_LSB);
// Assert the interrupt endpoint is IN_TO_HOST
assert(ep->in);
assert(dir == TUSB_DIR_IN);
if (need_pre(dev_addr))
{
@@ -363,9 +365,10 @@ static void hw_endpoint_init(uint8_t dev_addr, const tusb_desc_endpoint_t *ep_de
//--------------------------------------------------------------------+
// HCD API
//--------------------------------------------------------------------+
bool hcd_init(void)
bool hcd_init(uint8_t rhport)
{
pico_trace("hcd_init\n");
pico_trace("hcd_init %d\n", rhport);
assert(rhport == 0);
// Reset any previous state
rp2040_usb_init();
@@ -461,7 +464,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
if (ep == &epx) {
// That has set up buffer control, endpoint control etc
// for host we have to initiate the transfer
usb_hw->dev_addr_ctrl = dev_addr | ep->num << USB_ADDR_ENDP_ENDPOINT_LSB;
usb_hw->dev_addr_ctrl = dev_addr | (tu_edpt_number(ep_addr) << USB_ADDR_ENDP_ENDPOINT_LSB);
uint32_t flags = USB_SIE_CTRL_START_TRANS_BITS | sie_ctrl_base;
flags |= ep->rx ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS;
// Set pre if we are a low speed device on full speed hub

View File

@@ -30,7 +30,6 @@
#include <stdlib.h>
#include "rp2040_usb.h"
#include "hardware/clocks.h"
// Direction strings for debug
const char *ep_dir_string[] = {
@@ -44,10 +43,12 @@ static inline void _hw_endpoint_lock_update(struct hw_endpoint *ep, int delta) {
// sense to have worker and IRQ on same core, however I think using critsec is about equivalent.
}
#if TUSB_OPT_HOST_ENABLED
static inline void _hw_endpoint_update_last_buf(struct hw_endpoint *ep)
{
ep->last_buf = ep->len + ep->transfer_size == ep->total_len;
}
#endif
void rp2040_usb_init(void)
{
@@ -59,8 +60,11 @@ void rp2040_usb_init(void)
memset(usb_hw, 0, sizeof(*usb_hw));
memset(usb_dpram, 0, sizeof(*usb_dpram));
// Mux to phy
// Mux the controller to the onboard usb phy
usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS;
// Force VBUS detect so the device thinks it is plugged into a host
// TODO support VBUs detect
usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS;
}
@@ -68,7 +72,9 @@ void hw_endpoint_reset_transfer(struct hw_endpoint *ep)
{
ep->stalled = false;
ep->active = false;
#if TUSB_OPT_HOST_ENABLED
ep->sent_setup = false;
#endif
ep->total_len = 0;
ep->len = 0;
ep->transfer_size = 0;
@@ -84,12 +90,12 @@ void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_m
value |= or_mask;
if (or_mask & USB_BUF_CTRL_AVAIL) {
if (*ep->buffer_control & USB_BUF_CTRL_AVAIL) {
panic("ep %d %s was already available", ep->num, ep_dir_string[ep->in]);
panic("ep %d %s was already available", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
}
*ep->buffer_control = value & ~USB_BUF_CTRL_AVAIL;
// 12 cycle delay.. (should be good for 48*12Mhz = 576Mhz)
// Don't need delay in host mode as host is in charge
#ifndef RP2040_USB_HOST_MODE
#if !TUSB_OPT_HOST_ENABLED
__asm volatile (
"b 1f\n"
"1: b 1f\n"
@@ -122,6 +128,7 @@ void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep)
val |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID;
ep->next_pid ^= 1u;
#if TUSB_OPT_HOST_ENABLED
// Is this the last buffer? Only really matters for host mode. Will trigger
// the trans complete irq but also stop it polling. We only really care about
// trans complete for setup packets being sent
@@ -130,6 +137,7 @@ void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep)
pico_trace("Last buf (%d bytes left)\n", ep->transfer_size);
val |= USB_BUF_CTRL_LAST;
}
#endif
// Finally, write to buffer_control which will trigger the transfer
// the next time the controller polls this dpram address
@@ -141,11 +149,11 @@ void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep)
void _hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len)
{
_hw_endpoint_lock_update(ep, 1);
pico_trace("Start transfer of total len %d on ep %d %s\n", total_len, ep->num, ep_dir_string[ep->in]);
pico_trace("Start transfer of total len %d on ep %d %s\n", total_len, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
if (ep->active)
{
// TODO: Is this acceptable for interrupt packets?
pico_warn("WARN: starting new transfer on already active ep %d %s\n", ep->num, ep_dir_string[ep->in]);
pico_warn("WARN: starting new transfer on already active ep %d %s\n", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
hw_endpoint_reset_transfer(ep);
}
@@ -153,12 +161,14 @@ void _hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t t
// Fill in info now that we're kicking off the hw
ep->total_len = total_len;
ep->len = 0;
ep->transfer_size = tu_min32(total_len, ep->wMaxPacketSize);
ep->transfer_size = tu_min16(total_len, ep->wMaxPacketSize);
ep->active = true;
ep->user_buf = buffer;
#if TUSB_OPT_HOST_ENABLED
// Recalculate if this is the last buffer
_hw_endpoint_update_last_buf(ep);
ep->buf_sel = 0;
#endif
_hw_endpoint_start_next_buffer(ep);
_hw_endpoint_lock_update(ep, -1);
@@ -172,9 +182,9 @@ void _hw_endpoint_xfer_sync(struct hw_endpoint *ep)
// Get the buffer state and amount of bytes we have
// transferred
uint32_t buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep);
uint transferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK;
uint16_t transferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK;
#ifdef RP2040_USB_HOST_MODE
#if TUSB_OPT_HOST_ENABLED
// tag::host_buf_sel_fix[]
if (ep->buf_sel == 1)
{
@@ -223,16 +233,18 @@ bool _hw_endpoint_xfer_continue(struct hw_endpoint *ep)
// Part way through a transfer
if (!ep->active)
{
panic("Can't continue xfer on inactive ep %d %s", ep->num, ep_dir_string);
panic("Can't continue xfer on inactive ep %d %s", tu_edpt_number(ep->ep_addr), ep_dir_string);
}
// Update EP struct from hardware state
_hw_endpoint_xfer_sync(ep);
// Now we have synced our state with the hardware. Is there more data to transfer?
uint remaining_bytes = ep->total_len - ep->len;
ep->transfer_size = tu_min32(remaining_bytes, ep->wMaxPacketSize);
uint16_t remaining_bytes = ep->total_len - ep->len;
ep->transfer_size = tu_min16(remaining_bytes, ep->wMaxPacketSize);
#if TUSB_OPT_HOST_ENABLED
_hw_endpoint_update_last_buf(ep);
#endif
// Can happen because of programmer error so check for it
if (ep->len > ep->total_len)
@@ -244,7 +256,7 @@ bool _hw_endpoint_xfer_continue(struct hw_endpoint *ep)
if (ep->len == ep->total_len)
{
pico_trace("Completed transfer of %d bytes on ep %d %s\n",
ep->len, ep->num, ep_dir_string[ep->in]);
ep->len, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
// Notify caller we are done so it can notify the tinyusb
// stack
_hw_endpoint_lock_update(ep, -1);
@@ -263,7 +275,7 @@ bool _hw_endpoint_xfer_continue(struct hw_endpoint *ep)
void _hw_endpoint_xfer(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len, bool start)
{
// Trace
pico_trace("hw_endpoint_xfer ep %d %s", ep->num, ep_dir_string[ep->in]);
pico_trace("hw_endpoint_xfer ep %d %s", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
pico_trace(" total_len %d, start=%d\n", total_len, start);
assert(ep->configured);

View File

@@ -42,10 +42,6 @@ struct hw_endpoint
{
// Is this a valid struct
bool configured;
// EP direction
bool in;
// EP num (not including direction)
uint8_t num;
// Transfer direction (i.e. IN is rx for host but tx for device)
// allows us to common up transfer functions
@@ -67,28 +63,30 @@ struct hw_endpoint
// Current transfer information
bool active;
uint total_len;
uint len;
uint16_t total_len;
uint16_t len;
// Amount of data with the hardware
uint transfer_size;
uint16_t transfer_size;
// User buffer in main memory
uint8_t *user_buf;
// Data needed from EP descriptor
uint16_t wMaxPacketSize;
// Interrupt, bulk, etc
uint8_t transfer_type;
#if TUSB_OPT_HOST_ENABLED
// Only needed for host mode
bool last_buf;
// HOST BUG. Host will incorrect write status to top half of buffer
// control register when doing transfers > 1 packet
uint8_t buf_sel;
// User buffer in main memory
uint8_t *user_buf;
// Data needed from EP descriptor
uint wMaxPacketSize;
// Interrupt, bulk, etc
uint8_t transfer_type;
// Only needed for host
uint8_t dev_addr;
bool sent_setup;
// If interrupt endpoint
uint8_t interrupt_num;
#endif
};
void rp2040_usb_init(void);