-
Notifications
You must be signed in to change notification settings - Fork 305
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
326 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,313 @@ | ||
/* | ||
* Copyright (c) 2025, sakumisu | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
#include "usbh_core.h" | ||
#include "usbh_hub.h" | ||
#include "hardware/resets.h" | ||
#include "hardware/irq.h" | ||
#include "hardware/structs/usb.h" | ||
|
||
#define usb_hw_set hw_set_alias(usb_hw) | ||
#define usb_hw_clear hw_clear_alias(usb_hw) | ||
|
||
struct rp2040_pipe { | ||
uint8_t chidx; | ||
bool inuse; | ||
volatile uint32_t *endpoint_control; /*!< Endpoint control register */ | ||
volatile uint32_t *buffer_control; /*!< Buffer control register */ | ||
uint8_t *data_buffer; /*!< Buffer pointer in usb dpram */ | ||
uint32_t buffer_size; /*!< Buffer size */ | ||
usb_osal_sem_t waitsem; | ||
struct usbh_urb *urb; | ||
}; | ||
|
||
struct rp2040_hcd { | ||
volatile bool port_csc; | ||
volatile bool port_pec; | ||
volatile bool port_pe; | ||
struct rp2040_pipe pipe_pool[1 + CONFIG_USBHOST_PIPE_NUM]; | ||
} g_rp2040_hcd[CONFIG_USBHOST_MAX_BUS]; | ||
|
||
void rp2040_usbh_irq(void); | ||
|
||
/** | ||
* @brief Take a buffer pointer located in the USB RAM and return as an offset of the RAM. | ||
* | ||
* @param buf | ||
* @return uint32_t | ||
*/ | ||
static inline uint32_t usb_buffer_offset(volatile uint8_t *buf) | ||
{ | ||
return (uint32_t)buf ^ (uint32_t)usbh_dpram; | ||
} | ||
|
||
static inline uint8_t usbh_get_port_speed(void) | ||
{ | ||
return (usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS) >> USB_SIE_STATUS_SPEED_LSB; | ||
} | ||
|
||
int usb_hc_init(struct usbh_bus *bus) | ||
{ | ||
uint8_t *next_buffer_ptr; | ||
|
||
memset(&g_rp2040_hcd[bus->hcd.hcd_id], 0, sizeof(struct rp2040_hcd)); | ||
|
||
for (uint8_t i = 0; i <= CONFIG_USBHOST_PIPE_NUM; i++) { | ||
g_rp2040_hcd[bus->hcd.hcd_id].pipe_pool[i].waitsem = usb_osal_sem_create(0); | ||
} | ||
|
||
g_rp2040_hcd[bus->hcd.hcd_id].pipe_pool[0].endpoint_control = &usbh_dpram->epx_ctrl; | ||
g_rp2040_hcd[bus->hcd.hcd_id].pipe_pool[0].buffer_control = &usbh_dpram->epx_buf_ctrl; | ||
g_rp2040_hcd[bus->hcd.hcd_id].pipe_pool[0].data_buffer = &usbh_dpram->epx_data[0]; | ||
|
||
next_buffer_ptr = &usb_dpram->epx_data[64 * 2]; | ||
|
||
for (uint8_t i = 1; i <= CONFIG_USBHOST_PIPE_NUM; i++) { | ||
g_rp2040_hcd[bus->hcd.hcd_id].pipe_pool[i].chidx = i; | ||
g_rp2040_hcd[bus->hcd.hcd_id].pipe_pool[i].endpoint_control = &usbh_dpram->int_ep_ctrl[i].ctrl; | ||
g_rp2040_hcd[bus->hcd.hcd_id].pipe_pool[i].buffer_control = &usbh_dpram->int_ep_buffer_ctrl[i].ctrl; | ||
g_rp2040_hcd[bus->hcd.hcd_id].pipe_pool[i].data_buffer = next_buffer_ptr; | ||
g_rp2040_hcd[bus->hcd.hcd_id].pipe_pool[i].buffer_size = (64 * 2); | ||
next_buffer_ptr += (64 * 2); | ||
} | ||
|
||
// Reset usb controller | ||
reset_unreset_block_num_wait_blocking(RESET_USBCTRL); | ||
|
||
// Remove shared irq if it was previously added so as not to fill up shared irq slots | ||
irq_remove_handler(USBCTRL_IRQ, rp2040_usbh_irq); | ||
|
||
irq_add_shared_handler(USBCTRL_IRQ, rp2040_usbh_irq, PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY); | ||
|
||
/*!< Clear any previous state just in case */ | ||
memset(usb_hw, 0, sizeof(*usb_hw)); | ||
memset(usbh_dpram, 0, sizeof(*usbh_dpram)); | ||
|
||
/*!< 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 | ||
usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS; | ||
|
||
// Enable the USB controller in device mode. | ||
usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS | USB_MAIN_CTRL_HOST_NDEVICE_BITS; | ||
|
||
usb_hw->sie_ctrl = USB_SIE_CTRL_SOF_EN_BITS | | ||
USB_SIE_CTRL_KEEP_ALIVE_EN_BITS | | ||
USB_SIE_CTRL_PULLDOWN_EN_BITS | | ||
USB_SIE_CTRL_EP0_INT_1BUF_BITS; | ||
|
||
// Enable USB interrupt at processor | ||
irq_set_enabled(USBCTRL_IRQ, true); | ||
|
||
usb_hw->inte = USB_INTE_BUFF_STATUS_BITS | | ||
USB_INTE_HOST_CONN_DIS_BITS | | ||
USB_INTE_HOST_RESUME_BITS | | ||
USB_INTE_STALL_BITS | | ||
USB_INTE_TRANS_COMPLETE_BITS | | ||
USB_INTE_ERROR_RX_TIMEOUT_BITS | | ||
USB_INTE_ERROR_DATA_SEQ_BITS; | ||
return 0; | ||
} | ||
|
||
int usb_hc_deinit(struct usbh_bus *bus) | ||
{ | ||
// Enable USB interrupt at processor | ||
irq_set_enabled(USBCTRL_IRQ, false); | ||
|
||
// Remove shared irq if it was previously added so as not to fill up shared irq slots | ||
irq_remove_handler(USBCTRL_IRQ, rp2040_usbh_irq); | ||
|
||
return 0; | ||
} | ||
|
||
uint16_t usbh_get_frame_number(struct usbh_bus *bus) | ||
{ | ||
return 0; | ||
} | ||
|
||
int usbh_roothub_control(struct usbh_bus *bus, struct usb_setup_packet *setup, uint8_t *buf) | ||
{ | ||
uint8_t nports; | ||
uint8_t port; | ||
uint32_t status; | ||
|
||
nports = CONFIG_USBHOST_MAX_RHPORTS; | ||
port = setup->wIndex; | ||
if (setup->bmRequestType & USB_REQUEST_RECIPIENT_DEVICE) { | ||
switch (setup->bRequest) { | ||
case HUB_REQUEST_CLEAR_FEATURE: | ||
switch (setup->wValue) { | ||
case HUB_FEATURE_HUB_C_LOCALPOWER: | ||
break; | ||
case HUB_FEATURE_HUB_C_OVERCURRENT: | ||
break; | ||
default: | ||
return -USB_ERR_INVAL; | ||
} | ||
break; | ||
case HUB_REQUEST_SET_FEATURE: | ||
switch (setup->wValue) { | ||
case HUB_FEATURE_HUB_C_LOCALPOWER: | ||
break; | ||
case HUB_FEATURE_HUB_C_OVERCURRENT: | ||
break; | ||
default: | ||
return -USB_ERR_INVAL; | ||
} | ||
break; | ||
case HUB_REQUEST_GET_DESCRIPTOR: | ||
break; | ||
case HUB_REQUEST_GET_STATUS: | ||
memset(buf, 0, 4); | ||
break; | ||
default: | ||
break; | ||
} | ||
} else if (setup->bmRequestType & USB_REQUEST_RECIPIENT_OTHER) { | ||
switch (setup->bRequest) { | ||
case HUB_REQUEST_CLEAR_FEATURE: | ||
if (!port || port > nports) { | ||
return -USB_ERR_INVAL; | ||
} | ||
|
||
switch (setup->wValue) { | ||
case HUB_PORT_FEATURE_ENABLE: | ||
break; | ||
case HUB_PORT_FEATURE_SUSPEND: | ||
case HUB_PORT_FEATURE_C_SUSPEND: | ||
break; | ||
case HUB_PORT_FEATURE_POWER: | ||
break; | ||
case HUB_PORT_FEATURE_C_CONNECTION: | ||
g_rp2040_hcd[bus->hcd.hcd_id].port_csc = 0; | ||
break; | ||
case HUB_PORT_FEATURE_C_ENABLE: | ||
g_rp2040_hcd[bus->hcd.hcd_id].port_pec = 0; | ||
break; | ||
case HUB_PORT_FEATURE_C_OVER_CURREN: | ||
break; | ||
case HUB_PORT_FEATURE_C_RESET: | ||
break; | ||
default: | ||
return -USB_ERR_INVAL; | ||
} | ||
break; | ||
case HUB_REQUEST_SET_FEATURE: | ||
if (!port || port > nports) { | ||
return -USB_ERR_INVAL; | ||
} | ||
|
||
switch (setup->wValue) { | ||
case HUB_PORT_FEATURE_SUSPEND: | ||
break; | ||
case HUB_PORT_FEATURE_POWER: | ||
break; | ||
case HUB_PORT_FEATURE_RESET: | ||
break; | ||
|
||
default: | ||
return -USB_ERR_INVAL; | ||
} | ||
break; | ||
case HUB_REQUEST_GET_STATUS: | ||
if (!port || port > nports) { | ||
return -USB_ERR_INVAL; | ||
} | ||
|
||
status = 0; | ||
if (g_rp2040_hcd[bus->hcd.hcd_id].port_csc) { | ||
status |= (1 << HUB_PORT_FEATURE_C_CONNECTION); | ||
} | ||
if (g_rp2040_hcd[bus->hcd.hcd_id].port_pec) { | ||
status |= (1 << HUB_PORT_FEATURE_C_ENABLE); | ||
} | ||
|
||
if (g_rp2040_hcd[bus->hcd.hcd_id].port_pe) { | ||
status |= (1 << HUB_PORT_FEATURE_CONNECTION); | ||
status |= (1 << HUB_PORT_FEATURE_ENABLE); | ||
if (usbh_get_port_speed() == USB_SPEED_LOW) { | ||
status |= (1 << HUB_PORT_FEATURE_LOWSPEED); | ||
} | ||
} | ||
|
||
status |= (1 << HUB_PORT_FEATURE_POWER); | ||
memcpy(buf, &status, 4); | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
return 0; | ||
} | ||
|
||
int usbh_submit_urb(struct usbh_urb *urb) | ||
{ | ||
return -USB_ERR_NOTSUPP; | ||
} | ||
|
||
int usbh_kill_urb(struct usbh_urb *urb) | ||
{ | ||
return -USB_ERR_NOTSUPP; | ||
} | ||
|
||
void USBH_IRQHandler(uint8_t busid) | ||
{ | ||
uint32_t status = usb_hw->ints; | ||
uint32_t handled = 0; | ||
struct usbh_bus *bus; | ||
|
||
bus = &g_usbhost_bus[busid]; | ||
|
||
if (status & USB_INTS_HOST_CONN_DIS_BITS) { | ||
handled |= USB_INTS_HOST_CONN_DIS_BITS; | ||
usb_hw_clear->sie_status = USB_SIE_STATUS_SPEED_BITS; | ||
if (usbh_get_port_speed()) { | ||
g_rp2040_hcd[bus->hcd.hcd_id].port_csc = 1; | ||
g_rp2040_hcd[bus->hcd.hcd_id].port_pec = 1; | ||
g_rp2040_hcd[bus->hcd.hcd_id].port_pe = 1; | ||
bus->hcd.roothub.int_buffer[0] = (1 << 1); | ||
usbh_hub_thread_wakeup(&bus->hcd.roothub); | ||
} else { | ||
g_rp2040_hcd[bus->hcd.hcd_id].port_csc = 1; | ||
g_rp2040_hcd[bus->hcd.hcd_id].port_pec = 1; | ||
g_rp2040_hcd[bus->hcd.hcd_id].port_pe = 0; | ||
bus->hcd.roothub.int_buffer[0] = (1 << 1); | ||
usbh_hub_thread_wakeup(&bus->hcd.roothub); | ||
} | ||
} | ||
|
||
if (status & USB_INTS_STALL_BITS) { | ||
handled |= USB_INTS_STALL_BITS; | ||
usb_hw_clear->sie_status = USB_SIE_STATUS_STALL_REC_BITS; | ||
} | ||
|
||
if (status & USB_INTS_BUFF_STATUS_BITS) { | ||
handled |= USB_INTS_BUFF_STATUS_BITS; | ||
} | ||
|
||
if (status & USB_INTS_TRANS_COMPLETE_BITS) { | ||
handled |= USB_INTS_TRANS_COMPLETE_BITS; | ||
usb_hw_clear->sie_status = USB_SIE_STATUS_TRANS_COMPLETE_BITS; | ||
} | ||
|
||
if (status & USB_INTS_ERROR_RX_TIMEOUT_BITS) { | ||
handled |= USB_INTS_ERROR_RX_TIMEOUT_BITS; | ||
usb_hw_clear->sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS; | ||
} | ||
|
||
if (status & USB_INTS_ERROR_DATA_SEQ_BITS) { | ||
usb_hw_clear->sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS; | ||
} | ||
|
||
if (status ^ handled) { | ||
USB_LOG_ERR("Unhandled IRQ 0x%x\n", (uint)(status ^ handled)); | ||
} | ||
} | ||
|
||
void rp2040_usbh_irq(void) | ||
{ | ||
USBH_IRQHandler(0); | ||
} |