diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index d05032d00..74cac965d 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -177,6 +177,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool tu_is_aligned64(uint64_t value) { retur //------------- Mathematics -------------// TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_div_ceil(uint32_t v, uint32_t d) { return TU_DIV_CEIL(v, d); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_round_up(uint32_t v, uint32_t f) { return tu_div_ceil(v, f) * f; } // log2 of a value is its MSB's position // TODO use clz TODO remove diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index bb0a2f3fb..af08b549d 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -46,11 +46,8 @@ /*------------------------------------------------------------------*/ /* Low level controller *------------------------------------------------------------------*/ -static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type); -static void hw_set_endpoint_control_reg(struct hw_endpoint* ep, uint dpram_offset); - // Init these in dcd_init -static uint8_t* next_buffer_ptr = NULL; +static uint8_t* next_buffer_ptr; // 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]; @@ -70,115 +67,28 @@ TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_addr( // Allocate from the USB buffer space (max 3840 bytes) static void hw_endpoint_alloc(struct hw_endpoint* ep, size_t size) { - static uint8_t *end; - // determine buffer end - if (end == NULL){ - end = &usb_dpram->epx_data[0] + 0xF00; - } - // determine first next_buffer_ptr if necessary - if (next_buffer_ptr == NULL){ - next_buffer_ptr = &usb_dpram->epx_data[0]; + // round up size to multiple of 64 + size = tu_round_up(ep->wMaxPacketSize, 64); + + // double buffered Bulk endpoint + if (ep->transfer_type == TUSB_XFER_BULK) { + size *= 2u; } + // assign buffer ep->hw_data_buf = next_buffer_ptr; next_buffer_ptr += size; - hard_assert(next_buffer_ptr < end); - + hard_assert(next_buffer_ptr < usb_dpram->epx_data + sizeof(usb_dpram->epx_data)); pico_info(" Allocated %d bytes (0x%p)\r\n", size, ep->hw_data_buf); } -// allocate endpoint and fill endpoint control registers -static void hw_endpoint_alloc_and_control(struct hw_endpoint* ep, uint8_t transfer_type) { - // size must be multiple of 64 - uint size = tu_div_ceil(ep->wMaxPacketSize, 64) * 64u; - // double buffered Bulk endpoint - if (transfer_type == TUSB_XFER_BULK) { - size *= 2u; - } - ep->transfer_type = transfer_type; - hw_endpoint_alloc(ep, size); - - uint dpram_offset = hw_data_offset(ep->hw_data_buf); - pico_info(" Allocated %d bytes at offset 0x%x (0x%p)\r\n", size, dpram_offset, ep->hw_data_buf); - - hw_set_endpoint_control_reg(ep, dpram_offset); -} - -static void hw_set_endpoint_control_reg(struct hw_endpoint* ep, uint dpram_offset) { - // Fill in endpoint control register with buffer offset - uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint) ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; +// Enable endpoint +TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_enable(struct hw_endpoint* ep) { + uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint) ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->hw_data_buf); *ep->endpoint_control = reg; } -// New API: Allocate packet buffer used by ISO endpoints -// Some MCU need manual packet buffer allocation, we allocate the largest size to avoid clustering -bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { - (void) rhport; - assert(rhport == 0); - struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); - // size must be multiple of 64 - uint16_t size = (uint16_t)tu_div_ceil(largest_packet_size, 64) * 64u; - ep->wMaxPacketSize = size; - hw_endpoint_alloc(ep, size); - return true; -} - -// New API: Configure and enable an ISO endpoint according to descriptor -bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) { - (void) rhport; - assert(rhport == 0); - const uint8_t ep_addr = ep_desc->bEndpointAddress; - const uint16_t mps = ep_desc->wMaxPacketSize; - uint16_t size = (uint16_t)tu_div_ceil(mps, 64) * 64u; - - // init w/o allocate - hw_endpoint_init(ep_addr, size, TUSB_XFER_ISOCHRONOUS); - - // Fill in endpoint control register with buffer offset - struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); - uint dpram_offset = hw_data_offset(ep->hw_data_buf); - hw_set_endpoint_control_reg(ep, dpram_offset); - return true; -} - -static void hw_endpoint_close(uint8_t ep_addr) { - struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); - // Clear hardware registers and then zero the struct - // Clears endpoint enable - *ep->endpoint_control = 0; - // Clears buffer available, etc - *ep->buffer_control = 0; - // Clear any endpoint state - memset(ep, 0, sizeof(struct hw_endpoint)); - - // Reclaim buffer space if all endpoints are closed - bool reclaim_buffers = true; - for (uint8_t i = 1; i < USB_MAX_ENDPOINTS; i++) { - if (hw_endpoint_get_by_num(i, TUSB_DIR_OUT)->hw_data_buf != NULL || - hw_endpoint_get_by_num(i, TUSB_DIR_IN)->hw_data_buf != NULL) { - reclaim_buffers = false; - break; - } - } - if (reclaim_buffers) { - next_buffer_ptr = &usb_dpram->epx_data[0]; - } -} - -// Legacy init called by dcd_init (which does allocation) -static void hw_endpoint_init_and_alloc(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { - struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); - uint16_t size = (uint16_t) tu_div_ceil(wMaxPacketSize, 64) * 64u; - // size must be multiple of 64 - hw_endpoint_init(ep_addr, size, transfer_type); - const uint8_t num = tu_edpt_number(ep_addr); - if (num != 0) { - // alloc a buffer and fill in endpoint control register - hw_endpoint_alloc_and_control(ep, transfer_type); - } -} - // main processing for dcd_edpt_iso_activate static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); @@ -221,6 +131,18 @@ static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t } } +// Init, allocate buffer and enable endpoint +static void hw_endpoint_open(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { + struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); + hw_endpoint_init(ep_addr, wMaxPacketSize, transfer_type); + const uint8_t num = tu_edpt_number(ep_addr); + if (num != 0) { + // EP0 is already enabled + hw_endpoint_alloc(ep, ep->wMaxPacketSize); + hw_endpoint_enable(ep); + } +} + static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); hw_endpoint_xfer_start(ep, buffer, total_bytes); @@ -446,8 +368,8 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // Init control endpoints tu_memclr(hw_endpoints[0], 2 * sizeof(hw_endpoint_t)); - hw_endpoint_init_and_alloc(0x0, 64, TUSB_XFER_CONTROL); - hw_endpoint_init_and_alloc(0x80, 64, TUSB_XFER_CONTROL); + hw_endpoint_open(0x0, 64, TUSB_XFER_CONTROL); + hw_endpoint_open(0x80, 64, TUSB_XFER_CONTROL); // Init non-control endpoints reset_non_control_endpoints(); @@ -552,9 +474,34 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* req } } -bool dcd_edpt_open(__unused uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) { - assert(rhport == 0); - hw_endpoint_init_and_alloc(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), desc_edpt->bmAttributes.xfer); +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) { + (void) rhport; + const uint8_t xfer_type = desc_edpt->bmAttributes.xfer; + TU_VERIFY(xfer_type != TUSB_XFER_ISOCHRONOUS); + hw_endpoint_open(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), xfer_type); + return true; +} + +// New API: Allocate packet buffer used by ISO endpoints +// Some MCU need manual packet buffer allocation, we allocate the largest size to avoid clustering +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void) rhport; + struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); + hw_endpoint_init(ep_addr, largest_packet_size, TUSB_XFER_ISOCHRONOUS); + hw_endpoint_alloc(ep, largest_packet_size); + return true; +} + +// New API: Configure and enable an ISO endpoint according to descriptor +bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) { + (void) rhport; + const uint8_t ep_addr = ep_desc->bEndpointAddress; + // Fill in endpoint control register with buffer offset + struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); + TU_ASSERT(ep->hw_data_buf != NULL); // must be inited and buffer allocated + ep->wMaxPacketSize = ep_desc->wMaxPacketSize; + + hw_endpoint_enable(ep); return true; } @@ -599,12 +546,6 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { } } -void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { - (void) rhport; - pico_trace("dcd_edpt_close %02x\r\n", ep_addr); - hw_endpoint_close(ep_addr); -} - void __tusb_irq_path_func(dcd_int_handler)(uint8_t rhport) { (void) rhport; dcd_rp2040_irq();