2021-01-19 19:52:07 -06:00
|
|
|
/*
|
|
|
|
* The MIT License (MIT)
|
|
|
|
*
|
|
|
|
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
* THE SOFTWARE.
|
|
|
|
*
|
|
|
|
* This file is part of the TinyUSB stack.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "tusb_option.h"
|
|
|
|
|
2022-04-08 13:36:05 +07:00
|
|
|
#if CFG_TUD_ENABLED && (CFG_TUSB_MCU == OPT_MCU_RP2040) && !CFG_TUD_RPI_PIO_USB
|
2021-01-19 19:52:07 -06:00
|
|
|
|
|
|
|
#include "pico.h"
|
2023-03-10 14:01:51 -05:00
|
|
|
#include "hardware/sync.h"
|
2021-01-19 19:52:07 -06:00
|
|
|
#include "rp2040_usb.h"
|
|
|
|
|
|
|
|
#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
|
|
|
|
#include "pico/fix/rp2040_usb_device_enumeration.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "device/dcd.h"
|
|
|
|
|
2021-08-11 20:06:57 +07:00
|
|
|
// Current implementation force vbus detection as always present, causing device think it is always plugged into host.
|
|
|
|
// Therefore it cannot detect disconnect event, mistaken it as suspend.
|
|
|
|
// Note: won't work if change to 0 (for now)
|
|
|
|
#define FORCE_VBUS_DETECT 1
|
|
|
|
|
2021-01-19 19:52:07 -06:00
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
/* Low level controller
|
|
|
|
*------------------------------------------------------------------*/
|
|
|
|
// Init these in dcd_init
|
2024-11-28 15:56:47 +07:00
|
|
|
static uint8_t* next_buffer_ptr;
|
2021-01-19 19:52:07 -06:00
|
|
|
|
2021-02-25 09:15:42 -06:00
|
|
|
// USB_MAX_ENDPOINTS Endpoints, direction TUSB_DIR_OUT for out and TUSB_DIR_IN for in.
|
2021-08-12 15:40:26 +07:00
|
|
|
static struct hw_endpoint hw_endpoints[USB_MAX_ENDPOINTS][2];
|
2021-01-19 19:52:07 -06:00
|
|
|
|
2022-06-30 15:45:04 +07:00
|
|
|
// SOF may be used by remote wakeup as RESUME, this indicate whether SOF is actually used by usbd
|
|
|
|
static bool _sof_enable = false;
|
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_num(uint8_t num, tusb_dir_t dir) {
|
2021-08-11 20:06:57 +07:00
|
|
|
return &hw_endpoints[num][dir];
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
2024-03-13 11:41:58 +07:00
|
|
|
TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_addr(uint8_t ep_addr) {
|
2021-08-11 20:06:57 +07:00
|
|
|
uint8_t num = tu_edpt_number(ep_addr);
|
|
|
|
tusb_dir_t dir = tu_edpt_dir(ep_addr);
|
|
|
|
return hw_endpoint_get_by_num(num, dir);
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
2021-02-23 12:06:41 -06:00
|
|
|
|
2024-10-15 22:22:24 +02:00
|
|
|
// Allocate from the USB buffer space (max 3840 bytes)
|
|
|
|
static void hw_endpoint_alloc(struct hw_endpoint* ep, size_t size) {
|
2024-11-28 15:56:47 +07:00
|
|
|
// round up size to multiple of 64
|
|
|
|
size = tu_round_up(ep->wMaxPacketSize, 64);
|
2024-10-15 22:22:24 +02:00
|
|
|
|
2021-08-12 15:40:26 +07:00
|
|
|
// double buffered Bulk endpoint
|
2024-11-28 15:56:47 +07:00
|
|
|
if (ep->transfer_type == TUSB_XFER_BULK) {
|
2021-06-13 18:30:26 +07:00
|
|
|
size *= 2u;
|
|
|
|
}
|
2021-06-13 17:19:14 +07:00
|
|
|
|
2024-11-28 15:56:47 +07:00
|
|
|
// assign buffer
|
|
|
|
ep->hw_data_buf = next_buffer_ptr;
|
|
|
|
next_buffer_ptr += size;
|
2021-06-13 17:19:14 +07:00
|
|
|
|
2024-11-28 15:56:47 +07:00
|
|
|
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);
|
2024-10-15 22:22:24 +02:00
|
|
|
}
|
2021-06-13 17:19:14 +07:00
|
|
|
|
2024-11-28 15:56:47 +07:00
|
|
|
// 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);
|
2021-06-13 17:19:14 +07:00
|
|
|
*ep->endpoint_control = reg;
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
2024-10-15 22:22:24 +02:00
|
|
|
// main processing for dcd_edpt_iso_activate
|
2024-03-05 19:43:37 +07:00
|
|
|
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);
|
2021-08-12 00:11:04 +07:00
|
|
|
|
2021-08-12 00:12:15 +07:00
|
|
|
const uint8_t num = tu_edpt_number(ep_addr);
|
|
|
|
const tusb_dir_t dir = tu_edpt_dir(ep_addr);
|
2021-06-13 16:16:25 +07:00
|
|
|
|
2021-08-12 00:12:15 +07:00
|
|
|
ep->ep_addr = ep_addr;
|
2021-06-13 16:16:25 +07:00
|
|
|
|
2021-08-12 00:12:15 +07:00
|
|
|
// For device, IN is a tx transfer and OUT is an rx transfer
|
|
|
|
ep->rx = (dir == TUSB_DIR_OUT);
|
2021-06-13 16:16:25 +07:00
|
|
|
|
2021-09-01 19:42:40 +07:00
|
|
|
ep->next_pid = 0u;
|
2021-08-12 00:12:15 +07:00
|
|
|
ep->wMaxPacketSize = wMaxPacketSize;
|
|
|
|
ep->transfer_type = transfer_type;
|
2021-01-19 19:52:07 -06:00
|
|
|
|
2021-08-12 00:12:15 +07:00
|
|
|
// Every endpoint has a buffer control register in dpram
|
2024-03-05 19:43:37 +07:00
|
|
|
if (dir == TUSB_DIR_IN) {
|
2021-08-12 00:12:15 +07:00
|
|
|
ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].in;
|
2024-03-05 19:43:37 +07:00
|
|
|
} else {
|
2021-08-12 00:12:15 +07:00
|
|
|
ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].out;
|
|
|
|
}
|
2021-01-19 19:52:07 -06:00
|
|
|
|
2021-08-12 00:12:15 +07:00
|
|
|
// Clear existing buffer control state
|
|
|
|
*ep->buffer_control = 0;
|
2021-01-19 19:52:07 -06:00
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
if (num == 0) {
|
2021-09-01 16:52:27 +07:00
|
|
|
// EP0 has no endpoint control register because the buffer offsets are fixed
|
2021-08-12 00:12:15 +07:00
|
|
|
ep->endpoint_control = NULL;
|
2021-01-19 19:52:07 -06:00
|
|
|
|
2021-08-12 15:40:26 +07:00
|
|
|
// Buffer offset is fixed (also double buffered)
|
2021-08-12 00:12:15 +07:00
|
|
|
ep->hw_data_buf = (uint8_t*) &usb_dpram->ep0_buf_a[0];
|
2024-03-05 19:43:37 +07:00
|
|
|
} else {
|
2021-08-12 00:12:15 +07:00
|
|
|
// Set the endpoint control register (starts at EP1, hence num-1)
|
2024-03-05 19:43:37 +07:00
|
|
|
if (dir == TUSB_DIR_IN) {
|
2021-08-12 00:12:15 +07:00
|
|
|
ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].in;
|
2024-03-05 19:43:37 +07:00
|
|
|
} else {
|
2021-08-12 00:12:15 +07:00
|
|
|
ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].out;
|
|
|
|
}
|
|
|
|
}
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
2024-11-28 15:56:47 +07:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
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);
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
2025-03-05 22:26:28 +07:00
|
|
|
static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) {
|
|
|
|
// Abort any pending transfer
|
|
|
|
// Due to Errata RP2040-E2: ABORT flag is only applicable for B2 and later (unusable for B0, B1).
|
|
|
|
// Which means we are not guaranteed to safely abort pending transfer on B0 and B1.
|
|
|
|
const uint8_t dir = tu_edpt_dir(ep->ep_addr);
|
|
|
|
const uint8_t epnum = tu_edpt_number(ep->ep_addr);
|
|
|
|
const uint32_t abort_mask = TU_BIT((epnum << 1) | (dir ? 0 : 1));
|
|
|
|
if (rp2040_chip_version() >= 2) {
|
|
|
|
usb_hw_set->abort = abort_mask;
|
|
|
|
while ((usb_hw->abort_done & abort_mask) != abort_mask) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t buf_ctrl = USB_BUF_CTRL_SEL; // reset to buffer 0
|
|
|
|
if (ep->next_pid) {
|
|
|
|
buf_ctrl |= USB_BUF_CTRL_DATA1_PID;
|
|
|
|
}
|
|
|
|
|
|
|
|
_hw_endpoint_buffer_control_set_value32(ep, buf_ctrl);
|
|
|
|
hw_endpoint_reset_transfer(ep);
|
|
|
|
|
|
|
|
if (rp2040_chip_version() >= 2) {
|
|
|
|
usb_hw_clear->abort_done = abort_mask;
|
|
|
|
usb_hw_clear->abort = abort_mask;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
static void __tusb_irq_path_func(hw_handle_buff_status)(void) {
|
|
|
|
uint32_t remaining_buffers = usb_hw->buf_status;
|
|
|
|
pico_trace("buf_status = 0x%08lx\r\n", remaining_buffers);
|
|
|
|
uint bit = 1u;
|
|
|
|
for (uint8_t i = 0; remaining_buffers && i < USB_MAX_ENDPOINTS * 2; i++) {
|
|
|
|
if (remaining_buffers & bit) {
|
|
|
|
// clear this in advance
|
|
|
|
usb_hw_clear->buf_status = bit;
|
|
|
|
|
|
|
|
// IN transfer for even i, OUT transfer for odd i
|
|
|
|
struct hw_endpoint* ep = hw_endpoint_get_by_num(i >> 1u, (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN);
|
|
|
|
|
|
|
|
// Continue xfer
|
|
|
|
bool done = hw_endpoint_xfer_continue(ep);
|
|
|
|
if (done) {
|
|
|
|
// Notify
|
|
|
|
dcd_event_xfer_complete(0, ep->ep_addr, ep->xferred_len, XFER_RESULT_SUCCESS, true);
|
|
|
|
hw_endpoint_reset_transfer(ep);
|
|
|
|
}
|
|
|
|
remaining_buffers &= ~bit;
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
2024-03-05 19:43:37 +07:00
|
|
|
bit <<= 1u;
|
|
|
|
}
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
2024-03-13 11:41:58 +07:00
|
|
|
TU_ATTR_ALWAYS_INLINE static inline void reset_ep0(void) {
|
|
|
|
// If we have finished this transfer on EP0 set pid back to 1 for next
|
|
|
|
// setup transfer. Also clear a stall in case
|
|
|
|
for (uint8_t dir = 0; dir < 2; dir++) {
|
|
|
|
struct hw_endpoint* ep = hw_endpoint_get_by_num(0, dir);
|
2025-03-05 22:26:28 +07:00
|
|
|
ep->next_pid = 1u;
|
2024-03-13 11:41:58 +07:00
|
|
|
if (ep->active) {
|
2025-03-05 22:26:28 +07:00
|
|
|
hw_endpoint_abort_xfer(ep); // Abort any pending transfer per USB specs
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
2024-03-13 11:41:58 +07:00
|
|
|
}
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
static void __tusb_irq_path_func(reset_non_control_endpoints)(void) {
|
2021-09-01 19:42:40 +07:00
|
|
|
// Disable all non-control
|
2024-03-05 19:43:37 +07:00
|
|
|
for (uint8_t i = 0; i < USB_MAX_ENDPOINTS - 1; i++) {
|
2021-09-01 19:42:40 +07:00
|
|
|
usb_dpram->ep_ctrl[i].in = 0;
|
|
|
|
usb_dpram->ep_ctrl[i].out = 0;
|
|
|
|
}
|
2021-01-19 19:52:07 -06:00
|
|
|
|
2021-09-01 19:42:40 +07:00
|
|
|
// clear non-control hw endpoints
|
2024-03-05 19:43:37 +07:00
|
|
|
tu_memclr(hw_endpoints[1], sizeof(hw_endpoints) - 2 * sizeof(hw_endpoint_t));
|
2021-12-04 16:04:48 +02:00
|
|
|
|
2021-12-07 15:35:30 +02:00
|
|
|
// reclaim buffer space
|
2021-09-01 19:42:40 +07:00
|
|
|
next_buffer_ptr = &usb_dpram->epx_data[0];
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
static void __tusb_irq_path_func(dcd_rp2040_irq)(void) {
|
2023-01-31 14:48:11 +07:00
|
|
|
uint32_t const status = usb_hw->ints;
|
|
|
|
uint32_t handled = 0;
|
2021-01-19 19:52:07 -06:00
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
if (status & USB_INTF_DEV_SOF_BITS) {
|
2023-01-31 14:48:11 +07:00
|
|
|
bool keep_sof_alive = false;
|
2022-06-30 15:45:04 +07:00
|
|
|
|
2023-01-31 14:48:11 +07:00
|
|
|
handled |= USB_INTF_DEV_SOF_BITS;
|
2022-06-30 15:45:04 +07:00
|
|
|
|
2023-01-05 13:36:51 +00:00
|
|
|
#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
|
2023-01-31 19:03:31 +07:00
|
|
|
// Errata 15 workaround for Device Bulk-In endpoint
|
2023-01-31 14:48:11 +07:00
|
|
|
e15_last_sof = time_us_32();
|
2022-03-07 23:04:47 +07:00
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
for (uint8_t i = 0; i < USB_MAX_ENDPOINTS; i++) {
|
|
|
|
struct hw_endpoint* ep = hw_endpoint_get_by_num(i, TUSB_DIR_IN);
|
2021-12-08 12:33:30 +02:00
|
|
|
|
2023-01-31 14:48:11 +07:00
|
|
|
// Active Bulk IN endpoint requires SOF
|
2024-03-05 19:43:37 +07:00
|
|
|
if ((ep->transfer_type == TUSB_XFER_BULK) && ep->active) {
|
2023-01-31 14:48:11 +07:00
|
|
|
keep_sof_alive = true;
|
2021-09-01 19:42:40 +07:00
|
|
|
|
2023-01-31 14:48:11 +07:00
|
|
|
hw_endpoint_lock_update(ep, 1);
|
2021-09-01 19:42:40 +07:00
|
|
|
|
2023-01-05 13:36:51 +00:00
|
|
|
// Deferred enable?
|
2024-03-05 19:43:37 +07:00
|
|
|
if (ep->pending) {
|
2023-01-05 13:36:51 +00:00
|
|
|
ep->pending = 0;
|
2023-01-31 14:48:11 +07:00
|
|
|
hw_endpoint_start_next_buffer(ep);
|
2021-03-02 23:24:36 +07:00
|
|
|
}
|
|
|
|
|
2023-01-05 13:36:51 +00:00
|
|
|
hw_endpoint_lock_update(ep, -1);
|
|
|
|
}
|
2021-03-02 23:24:36 +07:00
|
|
|
}
|
2021-08-11 20:06:57 +07:00
|
|
|
#endif
|
2021-03-02 23:24:36 +07:00
|
|
|
|
2023-01-31 14:48:11 +07:00
|
|
|
// disable SOF interrupt if it is used for RESUME in remote wakeup
|
2024-03-05 19:43:37 +07:00
|
|
|
if (!keep_sof_alive && !_sof_enable) usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
|
2022-06-30 15:45:04 +07:00
|
|
|
|
2023-01-31 14:48:11 +07:00
|
|
|
dcd_event_sof(0, usb_hw->sof_rd & USB_SOF_RD_BITS, true);
|
|
|
|
}
|
2022-03-07 23:04:47 +07:00
|
|
|
|
2023-01-31 14:48:11 +07:00
|
|
|
// xfer events are handled before setup req. So if a transfer completes immediately
|
|
|
|
// before closing the EP, the events will be delivered in same order.
|
2024-03-05 19:43:37 +07:00
|
|
|
if (status & USB_INTS_BUFF_STATUS_BITS) {
|
2023-01-31 14:48:11 +07:00
|
|
|
handled |= USB_INTS_BUFF_STATUS_BITS;
|
|
|
|
hw_handle_buff_status();
|
|
|
|
}
|
2021-12-08 12:33:30 +02:00
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
if (status & USB_INTS_SETUP_REQ_BITS) {
|
2023-01-31 14:48:11 +07:00
|
|
|
handled |= USB_INTS_SETUP_REQ_BITS;
|
2024-03-05 19:43:37 +07:00
|
|
|
uint8_t const* setup = remove_volatile_cast(uint8_t const*, &usb_dpram->setup_packet);
|
2021-09-01 19:42:40 +07:00
|
|
|
|
2023-01-31 14:48:11 +07:00
|
|
|
// reset pid to both 1 (data and ack)
|
2024-03-13 11:41:58 +07:00
|
|
|
reset_ep0();
|
2021-09-01 19:42:40 +07:00
|
|
|
|
2023-01-31 14:48:11 +07:00
|
|
|
// Pass setup packet to tiny usb
|
|
|
|
dcd_event_setup_received(0, setup, true);
|
|
|
|
usb_hw_clear->sie_status = USB_SIE_STATUS_SETUP_REC_BITS;
|
|
|
|
}
|
2021-01-19 19:52:07 -06:00
|
|
|
|
2021-08-11 20:06:57 +07:00
|
|
|
#if FORCE_VBUS_DETECT == 0
|
2023-01-31 14:48:11 +07:00
|
|
|
// Since we force VBUS detect On, device will always think it is connected and
|
|
|
|
// couldn't distinguish between disconnect and suspend
|
|
|
|
if (status & USB_INTS_DEV_CONN_DIS_BITS)
|
|
|
|
{
|
|
|
|
handled |= USB_INTS_DEV_CONN_DIS_BITS;
|
2021-03-02 23:24:36 +07:00
|
|
|
|
2023-01-31 14:48:11 +07:00
|
|
|
if ( usb_hw->sie_status & USB_SIE_STATUS_CONNECTED_BITS )
|
|
|
|
{
|
|
|
|
// Connected: nothing to do
|
|
|
|
}else
|
2021-03-04 18:36:18 +07:00
|
|
|
{
|
2023-01-31 14:48:11 +07:00
|
|
|
// Disconnected
|
|
|
|
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true);
|
2021-03-02 23:24:36 +07:00
|
|
|
}
|
2021-08-11 20:29:39 +07:00
|
|
|
|
2023-01-31 14:48:11 +07:00
|
|
|
usb_hw_clear->sie_status = USB_SIE_STATUS_CONNECTED_BITS;
|
|
|
|
}
|
2021-08-11 20:06:57 +07:00
|
|
|
#endif
|
2021-03-02 23:24:36 +07:00
|
|
|
|
2023-01-31 14:48:11 +07:00
|
|
|
// SE0 for 2.5 us or more (will last at least 10ms)
|
2024-03-05 19:43:37 +07:00
|
|
|
if (status & USB_INTS_BUS_RESET_BITS) {
|
2023-07-02 10:53:08 +02:00
|
|
|
pico_trace("BUS RESET\r\n");
|
2021-08-11 20:29:39 +07:00
|
|
|
|
2023-01-31 14:48:11 +07:00
|
|
|
handled |= USB_INTS_BUS_RESET_BITS;
|
2021-08-11 20:29:39 +07:00
|
|
|
|
2023-01-31 14:48:11 +07:00
|
|
|
usb_hw->dev_addr_ctrl = 0;
|
|
|
|
reset_non_control_endpoints();
|
|
|
|
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
|
|
|
|
usb_hw_clear->sie_status = USB_SIE_STATUS_BUS_RESET_BITS;
|
2021-03-04 18:36:18 +07:00
|
|
|
|
|
|
|
#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
|
2023-01-31 19:03:31 +07:00
|
|
|
// Only run enumeration workaround if pull up is enabled
|
2024-03-05 19:43:37 +07:00
|
|
|
if (usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS) rp2040_usb_device_enumeration_fix();
|
2021-03-04 18:36:18 +07:00
|
|
|
#endif
|
2023-01-31 14:48:11 +07:00
|
|
|
}
|
2021-03-04 18:36:18 +07:00
|
|
|
|
2023-01-31 14:48:11 +07:00
|
|
|
/* 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.
|
|
|
|
*/
|
2024-03-05 19:43:37 +07:00
|
|
|
if (status & USB_INTS_DEV_SUSPEND_BITS) {
|
2023-01-31 14:48:11 +07:00
|
|
|
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;
|
|
|
|
}
|
2021-03-02 23:24:36 +07:00
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
if (status & USB_INTS_DEV_RESUME_FROM_HOST_BITS) {
|
2023-01-31 14:48:11 +07:00
|
|
|
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;
|
|
|
|
}
|
2021-03-02 23:24:36 +07:00
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
if (status ^ handled) {
|
2023-01-31 14:48:11 +07:00
|
|
|
panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
|
|
|
|
}
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#define USB_INTS_ERROR_BITS ( \
|
|
|
|
USB_INTS_ERROR_DATA_SEQ_BITS | \
|
|
|
|
USB_INTS_ERROR_BIT_STUFF_BITS | \
|
|
|
|
USB_INTS_ERROR_CRC_BITS | \
|
|
|
|
USB_INTS_ERROR_RX_OVERFLOW_BITS | \
|
|
|
|
USB_INTS_ERROR_RX_TIMEOUT_BITS)
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
/* Controller API
|
|
|
|
*------------------------------------------------------------------*/
|
2021-08-11 20:06:57 +07:00
|
|
|
|
2023-03-10 14:39:57 +07:00
|
|
|
// older SDK
|
|
|
|
#ifndef PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY
|
|
|
|
#define PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY 0xff
|
|
|
|
#endif
|
|
|
|
|
2024-10-14 19:42:22 +07:00
|
|
|
bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
|
2024-10-14 18:27:52 +07:00
|
|
|
(void) rh_init;
|
2021-08-11 20:06:57 +07:00
|
|
|
assert(rhport == 0);
|
2021-01-19 19:52:07 -06:00
|
|
|
|
2024-03-13 11:41:58 +07:00
|
|
|
TU_LOG(2, "Chip Version B%u\r\n", rp2040_chip_version());
|
|
|
|
|
2021-08-11 20:06:57 +07:00
|
|
|
// Reset hardware to default state
|
|
|
|
rp2040_usb_init();
|
2021-01-19 19:52:07 -06:00
|
|
|
|
2021-08-11 20:06:57 +07:00
|
|
|
#if FORCE_VBUS_DETECT
|
|
|
|
// Force VBUS detect so the device thinks it is plugged into a host
|
|
|
|
usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS;
|
|
|
|
#endif
|
2021-01-19 19:52:07 -06:00
|
|
|
|
2022-06-17 09:13:40 -05:00
|
|
|
irq_add_shared_handler(USBCTRL_IRQ, dcd_rp2040_irq, PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY);
|
2021-08-11 20:06:57 +07:00
|
|
|
|
2021-09-01 19:42:40 +07:00
|
|
|
// Init control endpoints
|
2024-03-05 19:43:37 +07:00
|
|
|
tu_memclr(hw_endpoints[0], 2 * sizeof(hw_endpoint_t));
|
2024-11-28 15:56:47 +07:00
|
|
|
hw_endpoint_open(0x0, 64, TUSB_XFER_CONTROL);
|
|
|
|
hw_endpoint_open(0x80, 64, TUSB_XFER_CONTROL);
|
2021-09-01 19:42:40 +07:00
|
|
|
|
|
|
|
// Init non-control endpoints
|
|
|
|
reset_non_control_endpoints();
|
2021-08-11 20:06:57 +07:00
|
|
|
|
|
|
|
// Initializes the USB peripheral for device mode and enables it.
|
|
|
|
// Don't need to enable the pull up here. Force VBUS
|
|
|
|
usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS;
|
|
|
|
|
|
|
|
// Enable individual controller IRQS here. Processor interrupt enable will be used
|
|
|
|
// for the global interrupt enable...
|
|
|
|
// Note: Force VBUS detect cause disconnection not detectable
|
|
|
|
usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS;
|
2024-03-05 19:43:37 +07:00
|
|
|
usb_hw->inte = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS |
|
|
|
|
USB_INTS_DEV_SUSPEND_BITS | USB_INTS_DEV_RESUME_FROM_HOST_BITS |
|
|
|
|
(FORCE_VBUS_DETECT ? 0 : USB_INTS_DEV_CONN_DIS_BITS);
|
2021-08-11 20:06:57 +07:00
|
|
|
|
|
|
|
dcd_connect(rhport);
|
2024-10-14 19:42:22 +07:00
|
|
|
return true;
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
2024-04-08 22:07:56 +07:00
|
|
|
bool dcd_deinit(uint8_t rhport) {
|
2024-04-08 22:22:00 +07:00
|
|
|
(void) rhport;
|
2023-01-02 17:09:45 +01:00
|
|
|
|
|
|
|
reset_non_control_endpoints();
|
|
|
|
irq_remove_handler(USBCTRL_IRQ, dcd_rp2040_irq);
|
|
|
|
|
|
|
|
// reset usb hardware into initial state
|
|
|
|
reset_block(RESETS_RESET_USBCTRL_BITS);
|
|
|
|
unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
|
2024-04-08 22:07:56 +07:00
|
|
|
|
|
|
|
return true;
|
2023-01-02 17:09:45 +01:00
|
|
|
}
|
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
void dcd_int_enable(__unused uint8_t rhport) {
|
|
|
|
assert(rhport == 0);
|
|
|
|
irq_set_enabled(USBCTRL_IRQ, true);
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
void dcd_int_disable(__unused uint8_t rhport) {
|
|
|
|
assert(rhport == 0);
|
|
|
|
irq_set_enabled(USBCTRL_IRQ, false);
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
void dcd_set_address(__unused uint8_t rhport, __unused uint8_t dev_addr) {
|
2021-08-12 00:11:04 +07:00
|
|
|
assert(rhport == 0);
|
2021-01-19 19:52:07 -06:00
|
|
|
|
2021-08-12 00:11:04 +07:00
|
|
|
// Can't set device address in hardware until status xfer has complete
|
|
|
|
// Send 0len complete response on EP0 IN
|
|
|
|
hw_endpoint_xfer(0x80, NULL, 0);
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
void dcd_remote_wakeup(__unused uint8_t rhport) {
|
2022-06-30 15:45:04 +07:00
|
|
|
pico_info("dcd_remote_wakeup %d\n", rhport);
|
|
|
|
assert(rhport == 0);
|
|
|
|
|
|
|
|
// since RESUME interrupt is not triggered if we are the one initiate
|
|
|
|
// briefly enable SOF to notify usbd when bus is ready
|
|
|
|
usb_hw_set->inte = USB_INTS_DEV_SOF_BITS;
|
|
|
|
usb_hw_set->sie_ctrl = USB_SIE_CTRL_RESUME_BITS;
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// disconnect by disabling internal pull-up resistor on D+/D-
|
2024-03-05 19:43:37 +07:00
|
|
|
void dcd_disconnect(__unused uint8_t rhport) {
|
2021-08-31 17:41:08 +07:00
|
|
|
(void) rhport;
|
|
|
|
usb_hw_clear->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// connect by enabling internal pull-up resistor on D+/D-
|
2024-03-05 19:43:37 +07:00
|
|
|
void dcd_connect(__unused uint8_t rhport) {
|
2021-08-31 17:41:08 +07:00
|
|
|
(void) rhport;
|
|
|
|
usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
void dcd_sof_enable(uint8_t rhport, bool en) {
|
2022-03-07 23:04:47 +07:00
|
|
|
(void) rhport;
|
|
|
|
|
2022-06-30 15:45:04 +07:00
|
|
|
_sof_enable = en;
|
2022-03-07 23:04:47 +07:00
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
if (en) {
|
2022-06-30 15:45:04 +07:00
|
|
|
usb_hw_set->inte = USB_INTS_DEV_SOF_BITS;
|
2024-03-05 19:43:37 +07:00
|
|
|
}
|
|
|
|
#if !TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
|
|
|
|
else {
|
2023-01-05 13:36:51 +00:00
|
|
|
// Don't clear immediately if the SOF workaround is in use.
|
|
|
|
// The SOF handler will conditionally disable the interrupt.
|
2022-06-30 15:45:04 +07:00
|
|
|
usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
|
2022-03-07 23:04:47 +07:00
|
|
|
}
|
2024-03-05 19:43:37 +07:00
|
|
|
#endif
|
2022-03-07 23:04:47 +07:00
|
|
|
}
|
|
|
|
|
2021-01-19 19:52:07 -06:00
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
/* DCD Endpoint port
|
|
|
|
*------------------------------------------------------------------*/
|
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) {
|
2021-06-13 18:30:26 +07:00
|
|
|
(void) rhport;
|
2021-01-19 19:52:07 -06:00
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
|
|
|
|
request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
|
|
|
|
request->bRequest == TUSB_REQ_SET_ADDRESS) {
|
2021-06-13 18:30:26 +07:00
|
|
|
usb_hw->dev_addr_ctrl = (uint8_t) request->wValue;
|
|
|
|
}
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
2024-11-28 15:56:47 +07:00
|
|
|
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;
|
2025-03-05 22:26:28 +07:00
|
|
|
struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_desc->bEndpointAddress);
|
|
|
|
TU_ASSERT(ep->hw_data_buf != NULL); // must be inited and allocated previously
|
2025-01-04 22:20:46 +01:00
|
|
|
|
2025-03-05 22:26:28 +07:00
|
|
|
if (ep->active) {
|
|
|
|
hw_endpoint_abort_xfer(ep); // abort any pending transfer
|
|
|
|
}
|
2025-01-04 22:20:46 +01:00
|
|
|
|
2024-11-28 15:56:47 +07:00
|
|
|
ep->wMaxPacketSize = ep_desc->wMaxPacketSize;
|
|
|
|
hw_endpoint_enable(ep);
|
2024-03-05 19:43:37 +07:00
|
|
|
return true;
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
void dcd_edpt_close_all(uint8_t rhport) {
|
2021-08-26 17:02:24 +07:00
|
|
|
(void) rhport;
|
2021-09-01 19:42:40 +07:00
|
|
|
|
|
|
|
// may need to use EP Abort
|
|
|
|
reset_non_control_endpoints();
|
2021-08-26 17:02:24 +07:00
|
|
|
}
|
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
bool dcd_edpt_xfer(__unused uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
|
|
|
|
assert(rhport == 0);
|
|
|
|
hw_endpoint_xfer(ep_addr, buffer, total_bytes);
|
|
|
|
return true;
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
|
2021-09-01 16:52:27 +07:00
|
|
|
(void) rhport;
|
2021-08-11 20:29:39 +07:00
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
if (tu_edpt_number(ep_addr) == 0) {
|
2021-08-11 20:29:39 +07:00
|
|
|
// A stall on EP0 has to be armed so it can be cleared on the next setup packet
|
2024-03-05 19:43:37 +07:00
|
|
|
usb_hw_set->ep_stall_arm = (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS
|
|
|
|
: USB_EP_STALL_ARM_EP0_OUT_BITS;
|
2021-08-11 20:29:39 +07:00
|
|
|
}
|
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
|
2021-08-11 20:29:39 +07:00
|
|
|
|
2021-09-01 16:52:27 +07:00
|
|
|
// stall and clear current pending buffer
|
|
|
|
// may need to use EP_ABORT
|
|
|
|
_hw_endpoint_buffer_control_set_value32(ep, USB_BUF_CTRL_STALL);
|
2021-01-19 19:52:07 -06:00
|
|
|
}
|
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
|
2021-09-01 16:52:27 +07:00
|
|
|
(void) rhport;
|
2021-01-19 19:52:07 -06:00
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
if (tu_edpt_number(ep_addr)) {
|
|
|
|
struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
|
2021-08-11 20:29:39 +07:00
|
|
|
|
2021-09-01 16:52:27 +07:00
|
|
|
// clear stall also reset toggle to DATA0, ready for next transfer
|
|
|
|
ep->next_pid = 0;
|
|
|
|
_hw_endpoint_buffer_control_clear_mask32(ep, USB_BUF_CTRL_STALL);
|
2021-08-11 20:29:39 +07:00
|
|
|
}
|
|
|
|
}
|
2021-01-19 19:52:07 -06:00
|
|
|
|
2024-03-05 19:43:37 +07:00
|
|
|
void __tusb_irq_path_func(dcd_int_handler)(uint8_t rhport) {
|
2021-08-11 20:29:39 +07:00
|
|
|
(void) rhport;
|
|
|
|
dcd_rp2040_irq();
|
2021-01-23 20:21:58 -06:00
|
|
|
}
|
|
|
|
|
2021-01-19 19:52:07 -06:00
|
|
|
#endif
|