From 1b17bc04f964301857abb80e9a9ed46f02803fc1 Mon Sep 17 00:00:00 2001 From: pschatzmann Date: Tue, 15 Oct 2024 22:22:24 +0200 Subject: [PATCH 1/2] RP2040 New ISO API --- src/class/video/video_device.c | 2 +- src/common/tusb_mcu.h | 1 + src/portable/raspberrypi/rp2040/dcd_rp2040.c | 99 ++++++++++++++++---- 3 files changed, 81 insertions(+), 21 deletions(-) diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c index 70dbb5b9e9..ec41e0991c 100644 --- a/src/class/video/video_device.c +++ b/src/class/video/video_device.c @@ -1307,7 +1307,7 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin #ifdef TUP_DCD_EDPT_ISO_ALLOC /* Allocate ISO endpoints */ uint16_t ep_size = 0; - uint16_t ep_addr = 0; + uint8_t ep_addr = 0; uint8_t const *p_desc = (uint8_t const*)itf_desc + stm->desc.beg; uint8_t const *p_desc_end = (uint8_t const*)itf_desc + stm->desc.end; while (p_desc < p_desc_end) { diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h index c77afecad0..c643560283 100644 --- a/src/common/tusb_mcu.h +++ b/src/common/tusb_mcu.h @@ -366,6 +366,7 @@ // Raspberry Pi //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_RP2040) + #define TUP_DCD_EDPT_ISO_ALLOC #define TUP_DCD_ENDPOINT_MAX 16 #define TU_ATTR_FAST_FUNC __attribute__((section(".time_critical.tinyusb"))) diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index 2e177d5cbf..bb0a2f3fbd 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -46,9 +46,11 @@ /*------------------------------------------------------------------*/ /* 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; +static uint8_t* next_buffer_ptr = NULL; // 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]; @@ -66,31 +68,82 @@ TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_addr( return hw_endpoint_get_by_num(num, dir); } -static void _hw_endpoint_alloc(struct hw_endpoint* ep, uint8_t transfer_type) { +// 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]; + } + // assign buffer + ep->hw_data_buf = next_buffer_ptr; + next_buffer_ptr += size; + + hard_assert(next_buffer_ptr < end); + + 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); - ep->hw_data_buf = next_buffer_ptr; - next_buffer_ptr += size; - - assert(((uintptr_t) next_buffer_ptr & 0b111111u) == 0); uint dpram_offset = hw_data_offset(ep->hw_data_buf); - hard_assert(hw_data_offset(next_buffer_ptr) <= USB_DPRAM_MAX); - pico_info(" Allocated %d bytes at offset 0x%x (0x%p)\r\n", size, dpram_offset, ep->hw_data_buf); - // Fill in endpoint control register with buffer offset - uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint) transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; + 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; *ep->endpoint_control = reg; } -static void _hw_endpoint_close(struct hw_endpoint* ep) { +// 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; @@ -113,11 +166,20 @@ static void _hw_endpoint_close(struct hw_endpoint* ep) { } } -static void hw_endpoint_close(uint8_t ep_addr) { +// 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); - _hw_endpoint_close(ep); + 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); @@ -156,9 +218,6 @@ static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t } else { ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].out; } - - // alloc a buffer and fill in endpoint control register - _hw_endpoint_alloc(ep, transfer_type); } } @@ -387,8 +446,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(0x0, 64, TUSB_XFER_CONTROL); - hw_endpoint_init(0x80, 64, TUSB_XFER_CONTROL); + hw_endpoint_init_and_alloc(0x0, 64, TUSB_XFER_CONTROL); + hw_endpoint_init_and_alloc(0x80, 64, TUSB_XFER_CONTROL); // Init non-control endpoints reset_non_control_endpoints(); @@ -495,7 +554,7 @@ 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(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), desc_edpt->bmAttributes.xfer); + hw_endpoint_init_and_alloc(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), desc_edpt->bmAttributes.xfer); return true; } From 7f61a5a43b4637afc323f6e63cc59330f1b1e5a6 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 28 Nov 2024 15:56:47 +0700 Subject: [PATCH 2/2] made change per reviews, remove dcd_edpt_close(), rename and move thing around --- src/common/tusb_common.h | 1 + src/portable/raspberrypi/rp2040/dcd_rp2040.c | 167 ++++++------------- 2 files changed, 55 insertions(+), 113 deletions(-) diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index d05032d001..74cac965d4 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 bb0a2f3fbd..af08b549dc 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]; - } - // assign buffer - ep->hw_data_buf = next_buffer_ptr; - next_buffer_ptr += size; - - hard_assert(next_buffer_ptr < end); - - pico_info(" Allocated %d bytes (0x%p)\r\n", size, ep->hw_data_buf); -} + // round up size to multiple of 64 + size = tu_round_up(ep->wMaxPacketSize, 64); -// 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) { + if (ep->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); + // assign buffer + ep->hw_data_buf = next_buffer_ptr; + next_buffer_ptr += size; - hw_set_endpoint_control_reg(ep, dpram_offset); + 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); } -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();