2021-01-20 02:52:07 +01:00
/*
* The MIT License ( MIT )
*
* Copyright ( c ) 2020 Raspberry Pi ( Trading ) Ltd .
2021-06-11 13:26:41 +02:00
* Copyright ( c ) 2021 Ha Thach ( tinyusb . org ) for Double Buffered
2021-01-20 02:52:07 +01:00
*
* 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-03-02 06:22:20 +01:00
# if CFG_TUH_ENABLED && (CFG_TUSB_MCU == OPT_MCU_RP2040) && !CFG_TUH_RPI_PIO_USB
2021-01-20 02:52:07 +01:00
# include "pico.h"
# include "rp2040_usb.h"
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
# include "osal/osal.h"
# include "host/hcd.h"
# include "host/usbh.h"
2022-03-01 17:55:53 +01:00
// port 0 is native USB port, other is counted as software PIO
# define RHPORT_NATIVE 0
2021-01-20 02:52:07 +01:00
//--------------------------------------------------------------------+
// Low level rp2040 controller functions
//--------------------------------------------------------------------+
# ifndef PICO_USB_HOST_INTERRUPT_ENDPOINTS
# define PICO_USB_HOST_INTERRUPT_ENDPOINTS (USB_MAX_ENDPOINTS - 1)
# endif
static_assert ( PICO_USB_HOST_INTERRUPT_ENDPOINTS < = USB_MAX_ENDPOINTS , " " ) ;
// Host mode uses one shared endpoint register for non-interrupt endpoint
2021-06-10 17:00:59 +02:00
static struct hw_endpoint ep_pool [ 1 + PICO_USB_HOST_INTERRUPT_ENDPOINTS ] ;
# define epx (ep_pool[0])
2021-01-20 02:52:07 +01:00
2021-06-10 17:00:59 +02:00
# define usb_hw_set hw_set_alias(usb_hw)
2021-01-20 02:52:07 +01:00
# define usb_hw_clear hw_clear_alias(usb_hw)
// Flags we set by default in sie_ctrl (we add other bits on top)
2021-06-10 17:00:59 +02:00
enum {
2021-08-11 15:06:57 +02:00
SIE_CTRL_BASE = 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
2021-06-10 17:00:59 +02:00
} ;
2021-01-20 02:52:07 +01:00
static struct hw_endpoint * get_dev_ep ( uint8_t dev_addr , uint8_t ep_addr )
{
2021-06-13 08:18:32 +02:00
uint8_t num = tu_edpt_number ( ep_addr ) ;
if ( num = = 0 ) return & epx ;
2021-06-10 17:00:59 +02:00
2021-06-13 08:18:32 +02:00
for ( uint32_t i = 1 ; i < TU_ARRAY_SIZE ( ep_pool ) ; i + + )
{
struct hw_endpoint * ep = & ep_pool [ i ] ;
if ( ep - > configured & & ( ep - > dev_addr = = dev_addr ) & & ( ep - > ep_addr = = ep_addr ) ) return ep ;
}
2021-06-10 17:00:59 +02:00
2021-06-13 08:18:32 +02:00
return NULL ;
2021-01-20 02:52:07 +01:00
}
2022-06-08 11:08:27 +02:00
TU_ATTR_ALWAYS_INLINE static inline uint8_t dev_speed ( void )
2021-01-20 02:52:07 +01:00
{
return ( usb_hw - > sie_status & USB_SIE_STATUS_SPEED_BITS ) > > USB_SIE_STATUS_SPEED_LSB ;
}
static bool need_pre ( uint8_t dev_addr )
{
// If this device is different to the speed of the root device
// (i.e. is a low speed device on a full speed hub) then need pre
2021-08-24 07:30:57 +02:00
return hcd_port_speed_get ( 0 ) ! = tuh_speed_get ( dev_addr ) ;
2021-01-20 02:52:07 +01:00
}
2022-06-16 18:03:53 +02:00
static void __tusb_irq_path_func ( hw_xfer_complete ) ( struct hw_endpoint * ep , xfer_result_t xfer_result )
2021-01-20 02:52:07 +01:00
{
// Mark transfer as done before we tell the tinyusb stack
uint8_t dev_addr = ep - > dev_addr ;
uint8_t ep_addr = ep - > ep_addr ;
2021-06-11 12:58:29 +02:00
uint xferred_len = ep - > xferred_len ;
2021-01-20 02:52:07 +01:00
hw_endpoint_reset_transfer ( ep ) ;
2021-06-11 12:05:49 +02:00
hcd_event_xfer_complete ( dev_addr , ep_addr , xferred_len , xfer_result , true ) ;
2021-01-20 02:52:07 +01:00
}
2022-06-16 18:03:53 +02:00
static void __tusb_irq_path_func ( _handle_buff_status_bit ) ( uint bit , struct hw_endpoint * ep )
2021-01-20 02:52:07 +01:00
{
usb_hw_clear - > buf_status = bit ;
2022-01-13 12:17:08 +01:00
// EP may have been stalled?
assert ( ep - > active ) ;
2021-06-11 12:34:51 +02:00
bool done = hw_endpoint_xfer_continue ( ep ) ;
2021-01-20 02:52:07 +01:00
if ( done )
{
hw_xfer_complete ( ep , XFER_RESULT_SUCCESS ) ;
}
}
2022-06-16 18:03:53 +02:00
static void __tusb_irq_path_func ( hw_handle_buff_status ) ( void )
2021-01-20 02:52:07 +01:00
{
uint32_t remaining_buffers = usb_hw - > buf_status ;
pico_trace ( " buf_status 0x%08x \n " , remaining_buffers ) ;
// Check EPX first
uint bit = 0 b1 ;
if ( remaining_buffers & bit )
{
remaining_buffers & = ~ bit ;
struct hw_endpoint * ep = & epx ;
2021-06-11 12:05:49 +02:00
uint32_t ep_ctrl = * ep - > endpoint_control ;
if ( ep_ctrl & EP_CTRL_DOUBLE_BUFFERED_BITS )
{
2021-06-11 13:53:56 +02:00
TU_LOG ( 3 , " Double Buffered: " ) ;
2021-06-11 12:05:49 +02:00
} else
{
2021-06-11 13:53:56 +02:00
TU_LOG ( 3 , " Single Buffered: " ) ;
2021-06-11 12:05:49 +02:00
}
2021-06-11 13:53:56 +02:00
TU_LOG_HEX ( 3 , ep_ctrl ) ;
2021-06-11 12:05:49 +02:00
2021-01-20 02:52:07 +01:00
_handle_buff_status_bit ( bit , ep ) ;
}
2022-11-04 10:14:35 +01:00
// Check "interrupt" (asynchronous) endpoints for both IN and OUT
2021-01-20 02:52:07 +01:00
for ( uint i = 1 ; i < = USB_HOST_INTERRUPT_ENDPOINTS & & remaining_buffers ; i + + )
{
2022-11-04 10:14:35 +01:00
// EPX is bit 0 & 1
2022-04-17 02:30:03 +02:00
// IEP1 IN is bit 2
// IEP1 OUT is bit 3
// IEP2 IN is bit 4
// IEP2 OUT is bit 5
// IEP3 IN is bit 6
// IEP3 OUT is bit 7
2021-01-20 02:52:07 +01:00
// etc
2022-11-04 10:14:35 +01:00
for ( uint j = 0 ; j < 2 ; j + + )
{
2022-04-17 02:30:03 +02:00
bit = 1 < < ( i * 2 + j ) ;
if ( remaining_buffers & bit )
{
remaining_buffers & = ~ bit ;
_handle_buff_status_bit ( bit , & ep_pool [ i ] ) ;
}
2021-01-20 02:52:07 +01:00
}
}
if ( remaining_buffers )
{
panic ( " Unhandled buffer %d \n " , remaining_buffers ) ;
}
}
2022-06-16 18:03:53 +02:00
static void __tusb_irq_path_func ( hw_trans_complete ) ( void )
2021-01-20 02:52:07 +01:00
{
2021-06-13 10:27:20 +02:00
if ( usb_hw - > sie_ctrl & USB_SIE_CTRL_SEND_SETUP_BITS )
{
pico_trace ( " Sent setup packet \n " ) ;
2021-11-05 10:39:31 +01:00
struct hw_endpoint * ep = & epx ;
assert ( ep - > active ) ;
2022-01-13 12:17:08 +01:00
// Set transferred length to 8 for a setup packet
ep - > xferred_len = 8 ;
2021-06-13 10:27:20 +02:00
hw_xfer_complete ( ep , XFER_RESULT_SUCCESS ) ;
}
else
{
// Don't care. Will handle this in buff status
return ;
}
2021-01-20 02:52:07 +01:00
}
2022-06-16 18:03:53 +02:00
static void __tusb_irq_path_func ( hcd_rp2040_irq ) ( void )
2021-01-20 02:52:07 +01:00
{
uint32_t status = usb_hw - > ints ;
uint32_t handled = 0 ;
if ( status & USB_INTS_HOST_CONN_DIS_BITS )
{
handled | = USB_INTS_HOST_CONN_DIS_BITS ;
if ( dev_speed ( ) )
{
2022-03-01 17:55:53 +01:00
hcd_event_device_attach ( RHPORT_NATIVE , true ) ;
2021-01-20 02:52:07 +01:00
}
else
{
2022-03-01 17:55:53 +01:00
hcd_event_device_remove ( RHPORT_NATIVE , true ) ;
2021-01-20 02:52:07 +01:00
}
// Clear speed change interrupt
usb_hw_clear - > sie_status = USB_SIE_STATUS_SPEED_BITS ;
}
2022-01-13 12:17:08 +01:00
if ( status & USB_INTS_STALL_BITS )
{
// We have rx'd a stall from the device
// NOTE THIS SHOULD HAVE PRIORITY OVER BUFF_STATUS
// AND TRANS_COMPLETE as the stall is an alternative response
// to one of those events
pico_trace ( " Stall REC \n " ) ;
handled | = USB_INTS_STALL_BITS ;
usb_hw_clear - > sie_status = USB_SIE_STATUS_STALL_REC_BITS ;
hw_xfer_complete ( & epx , XFER_RESULT_STALLED ) ;
}
2021-06-11 13:26:41 +02:00
if ( status & USB_INTS_BUFF_STATUS_BITS )
{
handled | = USB_INTS_BUFF_STATUS_BITS ;
TU_LOG ( 2 , " Buffer complete \n " ) ;
hw_handle_buff_status ( ) ;
}
2021-01-20 02:52:07 +01:00
if ( status & USB_INTS_TRANS_COMPLETE_BITS )
{
handled | = USB_INTS_TRANS_COMPLETE_BITS ;
usb_hw_clear - > sie_status = USB_SIE_STATUS_TRANS_COMPLETE_BITS ;
2021-06-10 17:00:59 +02:00
TU_LOG ( 2 , " Transfer complete \n " ) ;
2021-01-20 02:52:07 +01:00
hw_trans_complete ( ) ;
}
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 ;
2021-09-01 11:52:27 +02:00
TU_LOG ( 3 , " Seq Error: [0] = 0x%04u [1] = 0x%04x \r \n " , tu_u32_low16 ( * epx . buffer_control ) , tu_u32_high16 ( * epx . buffer_control ) ) ;
2021-01-20 02:52:07 +01:00
panic ( " Data Seq Error \n " ) ;
}
if ( status ^ handled )
{
panic ( " Unhandled IRQ 0x%x \n " , ( uint ) ( status ^ handled ) ) ;
}
}
2022-06-16 18:03:53 +02:00
void __tusb_irq_path_func ( hcd_int_handler ) ( uint8_t rhport )
2022-06-08 11:08:27 +02:00
{
( void ) rhport ;
hcd_rp2040_irq ( ) ;
}
2021-01-20 02:52:07 +01:00
static struct hw_endpoint * _next_free_interrupt_ep ( void )
{
struct hw_endpoint * ep = NULL ;
2021-06-10 17:00:59 +02:00
for ( uint i = 1 ; i < TU_ARRAY_SIZE ( ep_pool ) ; i + + )
2021-01-20 02:52:07 +01:00
{
2021-06-10 17:00:59 +02:00
ep = & ep_pool [ i ] ;
2021-01-20 02:52:07 +01:00
if ( ! ep - > configured )
{
// Will be configured by _hw_endpoint_init / _hw_endpoint_allocate
2022-06-27 12:11:24 +02:00
ep - > interrupt_num = ( uint8_t ) ( i - 1 ) ;
2021-01-20 02:52:07 +01:00
return ep ;
}
}
return ep ;
}
static struct hw_endpoint * _hw_endpoint_allocate ( uint8_t transfer_type )
{
struct hw_endpoint * ep = NULL ;
2021-06-10 17:00:59 +02:00
2022-04-17 02:30:03 +02:00
if ( transfer_type ! = TUSB_XFER_CONTROL )
2021-01-20 02:52:07 +01:00
{
2022-11-04 10:14:35 +01:00
// Note: even though datasheet name these "Interrupt" endpoints. These are actually
// "Asynchronous" endpoints and can be used for other type such as: Bulk (ISO need confirmation)
2021-01-20 02:52:07 +01:00
ep = _next_free_interrupt_ep ( ) ;
2022-04-17 02:30:03 +02:00
pico_info ( " Allocate %s ep %d \n " , tu_edpt_type_str ( transfer_type ) , ep - > interrupt_num ) ;
2021-01-20 02:52:07 +01:00
assert ( ep ) ;
ep - > buffer_control = & usbh_dpram - > int_ep_buffer_ctrl [ ep - > interrupt_num ] . ctrl ;
ep - > endpoint_control = & usbh_dpram - > int_ep_ctrl [ ep - > interrupt_num ] . ctrl ;
2021-06-11 12:05:49 +02:00
// 0 for epx (double buffered): TODO increase to 1024 for ISO
// 2x64 for intep0
// 3x64 for intep1
2021-01-20 02:52:07 +01:00
// etc
2021-06-11 12:05:49 +02:00
ep - > hw_data_buf = & usbh_dpram - > epx_data [ 64 * ( ep - > interrupt_num + 2 ) ] ;
2021-01-20 02:52:07 +01:00
}
else
{
ep = & epx ;
ep - > buffer_control = & usbh_dpram - > epx_buf_ctrl ;
ep - > endpoint_control = & usbh_dpram - > epx_ctrl ;
ep - > hw_data_buf = & usbh_dpram - > epx_data [ 0 ] ;
}
2021-06-10 17:00:59 +02:00
2021-01-20 02:52:07 +01:00
return ep ;
}
2022-06-27 12:11:24 +02:00
static void _hw_endpoint_init ( struct hw_endpoint * ep , uint8_t dev_addr , uint8_t ep_addr , uint16_t wMaxPacketSize , uint8_t transfer_type , uint8_t bmInterval )
2021-01-20 02:52:07 +01:00
{
// Already has data buffer, endpoint control, and buffer control allocated at this point
assert ( ep - > endpoint_control ) ;
assert ( ep - > buffer_control ) ;
assert ( ep - > hw_data_buf ) ;
2021-03-02 08:54:12 +01:00
uint8_t const num = tu_edpt_number ( ep_addr ) ;
tusb_dir_t const dir = tu_edpt_dir ( ep_addr ) ;
2021-01-20 02:52:07 +01:00
ep - > ep_addr = ep_addr ;
ep - > dev_addr = dev_addr ;
2021-03-02 08:54:12 +01:00
2021-01-20 02:52:07 +01:00
// For host, IN to host == RX, anything else rx == false
2021-03-02 08:54:12 +01:00
ep - > rx = ( dir = = TUSB_DIR_IN ) ;
2021-01-20 02:52:07 +01:00
// Response to a setup packet on EP0 starts with pid of 1
2021-06-11 12:05:49 +02:00
ep - > next_pid = ( num = = 0 ? 1u : 0u ) ;
2021-01-20 02:52:07 +01:00
ep - > wMaxPacketSize = wMaxPacketSize ;
ep - > transfer_type = transfer_type ;
2021-05-10 16:09:09 +02:00
pico_trace ( " hw_endpoint_init dev %d ep %d %s xfer %d \n " , ep - > dev_addr , tu_edpt_number ( ep - > ep_addr ) , ep_dir_string [ tu_edpt_dir ( ep - > ep_addr ) ] , ep - > transfer_type ) ;
pico_trace ( " dev %d ep %d %s setup buffer @ 0x%p \n " , ep - > dev_addr , tu_edpt_number ( ep - > ep_addr ) , ep_dir_string [ tu_edpt_dir ( ep - > ep_addr ) ] , ep - > hw_data_buf ) ;
2021-01-20 02:52:07 +01:00
uint dpram_offset = hw_data_offset ( ep - > hw_data_buf ) ;
// Bits 0-5 should be 0
assert ( ! ( dpram_offset & 0 b111111 ) ) ;
// Fill in endpoint control register with buffer offset
uint32_t ep_reg = EP_CTRL_ENABLE_BITS
| EP_CTRL_INTERRUPT_PER_BUFFER
| ( ep - > transfer_type < < EP_CTRL_BUFFER_TYPE_LSB )
| dpram_offset ;
2022-06-27 12:11:24 +02:00
if ( bmInterval )
{
ep_reg | = ( uint32_t ) ( ( bmInterval - 1 ) < < EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB ) ;
}
2021-01-20 02:52:07 +01:00
* ep - > endpoint_control = ep_reg ;
pico_trace ( " endpoint control (0x%p) <- 0x%x \n " , ep - > endpoint_control , ep_reg ) ;
ep - > configured = true ;
2022-04-17 02:30:03 +02:00
if ( ep ! = & epx )
2021-01-20 02:52:07 +01:00
{
2022-04-17 02:30:03 +02:00
// Endpoint has its own addr_endp and interrupt bits to be setup!
2022-11-04 10:14:35 +01:00
// This is an interrupt/async endpoint. so need to set up ADDR_ENDP register with:
// - device address
// - endpoint number / direction
// - preamble
2022-06-27 12:11:24 +02:00
uint32_t reg = ( uint32_t ) ( dev_addr | ( num < < USB_ADDR_ENDP1_ENDPOINT_LSB ) ) ;
2022-02-23 13:25:01 +01:00
if ( dir = = TUSB_DIR_OUT )
{
reg | = USB_ADDR_ENDP1_INTEP_DIR_BITS ;
}
2021-01-20 02:52:07 +01:00
if ( need_pre ( dev_addr ) )
{
reg | = USB_ADDR_ENDP1_INTEP_PREAMBLE_BITS ;
}
usb_hw - > int_ep_addr_ctrl [ ep - > interrupt_num ] = reg ;
// Finally, enable interrupt that endpoint
usb_hw_set - > int_ep_ctrl = 1 < < ( ep - > interrupt_num + 1 ) ;
// If it's an interrupt endpoint we need to set up the buffer control
// register
}
}
//--------------------------------------------------------------------+
// HCD API
//--------------------------------------------------------------------+
2021-02-25 16:48:19 +01:00
bool hcd_init ( uint8_t rhport )
2021-01-20 02:52:07 +01:00
{
2022-06-27 12:11:24 +02:00
( void ) rhport ;
pico_trace ( " hcd_init %d \n " , rhport ) ;
assert ( rhport = = 0 ) ;
2021-01-20 02:52:07 +01:00
2022-06-27 12:11:24 +02:00
// Reset any previous state
rp2040_usb_init ( ) ;
2021-01-20 02:52:07 +01:00
2022-06-27 12:11:24 +02:00
// Force VBUS detect to always present, for now we assume vbus is always provided (without using VBUS En)
usb_hw - > pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS ;
2021-08-11 15:06:57 +02:00
2022-09-07 22:10:44 +02:00
// Remove shared irq if it was previously added so as not to fill up shared irq slots
irq_remove_handler ( USBCTRL_IRQ , hcd_rp2040_irq ) ;
2021-01-20 02:52:07 +01:00
2022-06-27 12:11:24 +02:00
irq_add_shared_handler ( USBCTRL_IRQ , hcd_rp2040_irq , PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY ) ;
2021-01-20 02:52:07 +01:00
2022-06-27 12:11:24 +02:00
// clear epx and interrupt eps
memset ( & ep_pool , 0 , sizeof ( ep_pool ) ) ;
2021-01-20 02:52:07 +01:00
2022-06-27 12:11:24 +02:00
// Enable in host mode with SOF / Keep alive on
usb_hw - > main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS | USB_MAIN_CTRL_HOST_NDEVICE_BITS ;
usb_hw - > sie_ctrl = SIE_CTRL_BASE ;
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 ;
2021-01-20 02:52:07 +01:00
2022-06-27 12:11:24 +02:00
return true ;
2021-01-20 02:52:07 +01:00
}
void hcd_port_reset ( uint8_t rhport )
{
2022-06-27 12:11:24 +02:00
( void ) rhport ;
pico_trace ( " hcd_port_reset \n " ) ;
assert ( rhport = = 0 ) ;
// TODO: Nothing to do here yet. Perhaps need to reset some state?
2021-01-20 02:52:07 +01:00
}
2022-03-02 06:33:47 +01:00
void hcd_port_reset_end ( uint8_t rhport )
{
( void ) rhport ;
2021-01-20 02:52:07 +01:00
}
bool hcd_port_connect_status ( uint8_t rhport )
{
2022-06-27 12:11:24 +02:00
( void ) rhport ;
pico_trace ( " hcd_port_connect_status \n " ) ;
assert ( rhport = = 0 ) ;
return usb_hw - > sie_status & USB_SIE_STATUS_SPEED_BITS ;
2021-01-20 02:52:07 +01:00
}
tusb_speed_t hcd_port_speed_get ( uint8_t rhport )
{
2022-06-27 12:11:24 +02:00
( void ) rhport ;
assert ( rhport = = 0 ) ;
// TODO: Should enumval this register
switch ( dev_speed ( ) )
{
case 1 :
return TUSB_SPEED_LOW ;
case 2 :
return TUSB_SPEED_FULL ;
default :
panic ( " Invalid speed \n " ) ;
return TUSB_SPEED_INVALID ;
}
2021-01-20 02:52:07 +01:00
}
// Close all opened endpoint belong to this device
void hcd_device_close ( uint8_t rhport , uint8_t dev_addr )
{
2021-11-30 11:36:52 +01:00
pico_trace ( " hcd_device_close %d \n " , dev_addr ) ;
( void ) rhport ;
2021-06-10 18:29:02 +02:00
2021-11-30 11:36:52 +01:00
if ( dev_addr = = 0 ) return ;
for ( size_t i = 1 ; i < TU_ARRAY_SIZE ( ep_pool ) ; i + + )
{
hw_endpoint_t * ep = & ep_pool [ i ] ;
if ( ep - > dev_addr = = dev_addr & & ep - > configured )
2021-11-12 23:58:51 +01:00
{
2021-11-30 11:36:52 +01:00
// in case it is an interrupt endpoint, disable it
usb_hw_clear - > int_ep_ctrl = ( 1 < < ( ep - > interrupt_num + 1 ) ) ;
usb_hw - > int_ep_addr_ctrl [ ep - > interrupt_num ] = 0 ;
// unconfigure the endpoint
ep - > configured = false ;
* ep - > endpoint_control = 0 ;
* ep - > buffer_control = 0 ;
hw_endpoint_reset_transfer ( ep ) ;
2021-11-12 23:58:51 +01:00
}
2021-11-30 11:36:52 +01:00
}
2021-01-20 02:52:07 +01:00
}
2021-06-10 18:29:02 +02:00
uint32_t hcd_frame_number ( uint8_t rhport )
{
( void ) rhport ;
return usb_hw - > sof_rd ;
}
2021-01-20 02:52:07 +01:00
void hcd_int_enable ( uint8_t rhport )
{
2022-06-27 12:11:24 +02:00
( void ) rhport ;
assert ( rhport = = 0 ) ;
irq_set_enabled ( USBCTRL_IRQ , true ) ;
2021-01-20 02:52:07 +01:00
}
void hcd_int_disable ( uint8_t rhport )
{
2022-06-27 12:11:24 +02:00
( void ) rhport ;
// todo we should check this is disabling from the correct core; note currently this is never called
assert ( rhport = = 0 ) ;
irq_set_enabled ( USBCTRL_IRQ , false ) ;
2021-01-20 02:52:07 +01:00
}
2021-06-11 12:05:49 +02:00
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
2021-06-10 18:29:02 +02:00
bool hcd_edpt_open ( uint8_t rhport , uint8_t dev_addr , tusb_desc_endpoint_t const * ep_desc )
{
( void ) rhport ;
pico_trace ( " hcd_edpt_open dev_addr %d, ep_addr %d \n " , dev_addr , ep_desc - > bEndpointAddress ) ;
// Allocated differently based on if it's an interrupt endpoint or not
struct hw_endpoint * ep = _hw_endpoint_allocate ( ep_desc - > bmAttributes . xfer ) ;
2022-06-27 12:11:24 +02:00
TU_ASSERT ( ep ) ;
2021-06-10 18:29:02 +02:00
_hw_endpoint_init ( ep ,
dev_addr ,
ep_desc - > bEndpointAddress ,
2021-10-24 08:11:21 +02:00
tu_edpt_packet_size ( ep_desc ) ,
2021-06-10 18:29:02 +02:00
ep_desc - > bmAttributes . xfer ,
ep_desc - > bInterval ) ;
return true ;
}
2021-01-20 02:52:07 +01:00
bool hcd_edpt_xfer ( uint8_t rhport , uint8_t dev_addr , uint8_t ep_addr , uint8_t * buffer , uint16_t buflen )
{
2021-06-10 18:29:02 +02:00
( void ) rhport ;
2021-06-10 17:00:59 +02:00
pico_trace ( " hcd_edpt_xfer dev_addr %d, ep_addr 0x%x, len %d \n " , dev_addr , ep_addr , buflen ) ;
2021-01-20 02:52:07 +01:00
2021-06-10 18:29:02 +02:00
uint8_t const ep_num = tu_edpt_number ( ep_addr ) ;
tusb_dir_t const ep_dir = tu_edpt_dir ( ep_addr ) ;
2021-01-20 02:52:07 +01:00
// Get appropriate ep. Either EPX or interrupt endpoint
struct hw_endpoint * ep = get_dev_ep ( dev_addr , ep_addr ) ;
2022-11-04 09:42:50 +01:00
2022-06-27 12:11:24 +02:00
TU_ASSERT ( ep ) ;
2021-01-20 02:52:07 +01:00
2022-01-13 12:17:08 +01:00
// EP should be inactive
assert ( ! ep - > active ) ;
2021-01-20 02:52:07 +01:00
2021-06-13 10:27:20 +02:00
// Control endpoint can change direction 0x00 <-> 0x80
if ( ep_addr ! = ep - > ep_addr )
2021-01-20 02:52:07 +01:00
{
2021-06-13 10:27:20 +02:00
assert ( ep_num = = 0 ) ;
// Direction has flipped on endpoint control so re init it but with same properties
_hw_endpoint_init ( ep , dev_addr , ep_addr , ep - > wMaxPacketSize , ep - > transfer_type , 0 ) ;
2021-01-20 02:52:07 +01:00
}
// If a normal transfer (non-interrupt) then initiate using
// sie ctrl registers. Otherwise interrupt ep registers should
// already be configured
if ( ep = = & epx ) {
2021-06-11 12:34:51 +02:00
hw_endpoint_xfer_start ( ep , buffer , buflen ) ;
2021-06-11 12:05:49 +02:00
2021-01-20 02:52:07 +01:00
// That has set up buffer control, endpoint control etc
// for host we have to initiate the transfer
2022-06-27 12:11:24 +02:00
usb_hw - > dev_addr_ctrl = ( uint32_t ) ( dev_addr | ( ep_num < < USB_ADDR_ENDP_ENDPOINT_LSB ) ) ;
2021-06-10 18:29:02 +02:00
2021-06-13 08:18:32 +02:00
uint32_t flags = USB_SIE_CTRL_START_TRANS_BITS | SIE_CTRL_BASE |
2021-06-10 18:29:02 +02:00
( ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS ) ;
2021-01-20 02:52:07 +01:00
// Set pre if we are a low speed device on full speed hub
flags | = need_pre ( dev_addr ) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0 ;
2021-06-10 18:29:02 +02:00
2021-01-20 02:52:07 +01:00
usb_hw - > sie_ctrl = flags ;
2021-06-11 12:05:49 +02:00
} else
{
2021-06-11 12:34:51 +02:00
hw_endpoint_xfer_start ( ep , buffer , buflen ) ;
2021-01-20 02:52:07 +01:00
}
return true ;
}
bool hcd_setup_send ( uint8_t rhport , uint8_t dev_addr , uint8_t const setup_packet [ 8 ] )
{
2021-06-10 18:29:02 +02:00
( void ) rhport ;
2021-01-20 02:52:07 +01:00
// Copy data into setup packet buffer
2022-06-27 16:05:41 +02:00
for ( uint8_t i = 0 ; i < 8 ; i + + )
{
usbh_dpram - > setup_packet [ i ] = setup_packet [ i ] ;
}
2021-01-20 02:52:07 +01:00
// Configure EP0 struct with setup info for the trans complete
struct hw_endpoint * ep = _hw_endpoint_allocate ( 0 ) ;
2022-06-27 12:11:24 +02:00
TU_ASSERT ( ep ) ;
2021-06-10 17:00:59 +02:00
2022-01-13 12:17:08 +01:00
// EPX should be inactive
assert ( ! ep - > active ) ;
2021-06-10 17:00:59 +02:00
2021-01-20 02:52:07 +01:00
// EP0 out
_hw_endpoint_init ( ep , dev_addr , 0x00 , ep - > wMaxPacketSize , 0 , 0 ) ;
assert ( ep - > configured ) ;
2021-06-10 17:00:59 +02:00
2021-06-11 12:58:29 +02:00
ep - > remaining_len = 8 ;
2021-06-10 17:00:59 +02:00
ep - > active = true ;
2021-01-20 02:52:07 +01:00
// Set device address
usb_hw - > dev_addr_ctrl = dev_addr ;
2021-06-10 17:00:59 +02:00
2021-01-20 02:52:07 +01:00
// Set pre if we are a low speed device on full speed hub
2021-06-13 08:18:32 +02:00
uint32_t const flags = SIE_CTRL_BASE | USB_SIE_CTRL_SEND_SETUP_BITS | USB_SIE_CTRL_START_TRANS_BITS |
2021-06-10 17:00:59 +02:00
( need_pre ( dev_addr ) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0 ) ;
2021-01-20 02:52:07 +01:00
usb_hw - > sie_ctrl = flags ;
2021-06-10 17:00:59 +02:00
2021-01-20 02:52:07 +01:00
return true ;
}
bool hcd_edpt_clear_stall ( uint8_t dev_addr , uint8_t ep_addr )
{
2021-06-10 18:29:02 +02:00
( void ) dev_addr ;
2021-06-11 12:05:49 +02:00
( void ) ep_addr ;
2021-06-10 18:29:02 +02:00
2021-01-20 02:52:07 +01:00
panic ( " hcd_clear_stall " ) ;
return true ;
}
# endif