2020-01-14 23:30:39 -05:00
/*
* The MIT License ( MIT )
*
* Copyright ( c ) 2019 Ha Thach ( tinyusb . org )
*
* 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 .
*/
2021-08-23 17:00:41 +07:00
# include "host/hcd_attr.h"
2020-01-14 23:30:39 -05:00
2021-08-23 17:00:41 +07:00
# if TUSB_OPT_HOST_ENABLED && defined(HCD_ATTR_OHCI)
2021-02-23 23:09:48 +07:00
2020-01-14 23:30:39 -05:00
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
# include "osal/osal.h"
2021-02-23 23:09:48 +07:00
# include "host/hcd.h"
2020-01-14 23:30:39 -05:00
# include "ohci.h"
// TODO remove
# include "chip.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
# define OHCI_REG ((ohci_registers_t *) LPC_USB_BASE)
enum {
OHCI_CONTROL_FUNCSTATE_RESET = 0 ,
OHCI_CONTROL_FUNCSTATE_RESUME ,
OHCI_CONTROL_FUNCSTATE_OPERATIONAL ,
OHCI_CONTROL_FUNCSTATE_SUSPEND
} ;
enum {
OHCI_CONTROL_CONTROL_BULK_RATIO = 3 , ///< This specifies the service ratio between Control and Bulk EDs. 0 = 1:1, 3 = 4:1
OHCI_CONTROL_LIST_PERIODIC_ENABLE_MASK = TU_BIT ( 2 ) ,
OHCI_CONTROL_LIST_ISOCHRONOUS_ENABLE_MASK = TU_BIT ( 3 ) ,
OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK = TU_BIT ( 4 ) ,
OHCI_CONTROL_LIST_BULK_ENABLE_MASK = TU_BIT ( 5 ) ,
} ;
enum {
OHCI_FMINTERVAL_FI = 0x2EDF , // 7.3.1 nominal (reset) value
OHCI_FMINTERVAL_FSMPS = ( 6 * ( OHCI_FMINTERVAL_FI - 210 ) ) / 7 , // 5.4 calculated based on maximum overhead + bit stuffing
} ;
enum {
OHCI_PERIODIC_START = 0x3E67
} ;
enum {
OHCI_INT_SCHEDULING_OVERUN_MASK = TU_BIT ( 0 ) ,
OHCI_INT_WRITEBACK_DONEHEAD_MASK = TU_BIT ( 1 ) ,
OHCI_INT_SOF_MASK = TU_BIT ( 2 ) ,
OHCI_INT_RESUME_DETECTED_MASK = TU_BIT ( 3 ) ,
OHCI_INT_UNRECOVERABLE_ERROR_MASK = TU_BIT ( 4 ) ,
OHCI_INT_FRAME_OVERFLOW_MASK = TU_BIT ( 5 ) ,
OHCI_INT_RHPORT_STATUS_CHANGE_MASK = TU_BIT ( 6 ) ,
OHCI_INT_OWNERSHIP_CHANGE_MASK = TU_BIT ( 30 ) ,
OHCI_INT_MASTER_ENABLE_MASK = TU_BIT ( 31 ) ,
} ;
enum {
2021-05-31 18:41:08 +07:00
RHPORT_CURRENT_CONNECT_STATUS_MASK = TU_BIT ( 0 ) ,
RHPORT_PORT_ENABLE_STATUS_MASK = TU_BIT ( 1 ) ,
RHPORT_PORT_SUSPEND_STATUS_MASK = TU_BIT ( 2 ) ,
RHPORT_PORT_OVER_CURRENT_INDICATOR_MASK = TU_BIT ( 3 ) ,
RHPORT_PORT_RESET_STATUS_MASK = TU_BIT ( 4 ) , ///< write '1' to reset port
RHPORT_PORT_POWER_STATUS_MASK = TU_BIT ( 8 ) ,
RHPORT_LOW_SPEED_DEVICE_ATTACHED_MASK = TU_BIT ( 9 ) ,
RHPORT_CONNECT_STATUS_CHANGE_MASK = TU_BIT ( 16 ) ,
RHPORT_PORT_ENABLE_CHANGE_MASK = TU_BIT ( 17 ) ,
RHPORT_PORT_SUSPEND_CHANGE_MASK = TU_BIT ( 18 ) ,
RHPORT_OVER_CURRENT_CHANGE_MASK = TU_BIT ( 19 ) ,
RHPORT_PORT_RESET_CHANGE_MASK = TU_BIT ( 20 ) ,
RHPORT_ALL_CHANGE_MASK = RHPORT_CONNECT_STATUS_CHANGE_MASK | RHPORT_PORT_ENABLE_CHANGE_MASK |
RHPORT_PORT_SUSPEND_CHANGE_MASK | RHPORT_OVER_CURRENT_CHANGE_MASK | RHPORT_PORT_RESET_CHANGE_MASK
2020-01-14 23:30:39 -05:00
} ;
enum {
OHCI_CCODE_NO_ERROR = 0 ,
OHCI_CCODE_CRC = 1 ,
OHCI_CCODE_BIT_STUFFING = 2 ,
OHCI_CCODE_DATA_TOGGLE_MISMATCH = 3 ,
OHCI_CCODE_STALL = 4 ,
OHCI_CCODE_DEVICE_NOT_RESPONDING = 5 ,
OHCI_CCODE_PID_CHECK_FAILURE = 6 ,
OHCI_CCODE_UNEXPECTED_PID = 7 ,
OHCI_CCODE_DATA_OVERRUN = 8 ,
OHCI_CCODE_DATA_UNDERRUN = 9 ,
OHCI_CCODE_BUFFER_OVERRUN = 12 ,
OHCI_CCODE_BUFFER_UNDERRUN = 13 ,
OHCI_CCODE_NOT_ACCESSED = 14 ,
} ;
enum {
OHCI_INT_ON_COMPLETE_YES = 0 ,
OHCI_INT_ON_COMPLETE_NO = TU_BIN8 ( 111 )
} ;
2021-05-31 18:16:07 +07:00
enum {
GTD_DT_TOGGLE_CARRY = 0 ,
GTD_DT_DATA0 = TU_BIT ( 1 ) | 0 ,
GTD_DT_DATA1 = TU_BIT ( 1 ) | 1 ,
} ;
enum {
PID_SETUP = 0 ,
PID_OUT ,
PID_IN ,
} ;
enum {
PID_FROM_TD = 0 ,
} ;
2020-01-14 23:30:39 -05:00
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED ( 256 ) static ohci_data_t ohci_data ;
static ohci_ed_t * const p_ed_head [ ] =
{
[ TUSB_XFER_CONTROL ] = & ohci_data . control [ 0 ] . ed ,
[ TUSB_XFER_BULK ] = & ohci_data . bulk_head_ed ,
[ TUSB_XFER_INTERRUPT ] = & ohci_data . period_head_ed ,
[ TUSB_XFER_ISOCHRONOUS ] = NULL // TODO Isochronous
} ;
static void ed_list_insert ( ohci_ed_t * p_pre , ohci_ed_t * p_ed ) ;
static void ed_list_remove_by_addr ( ohci_ed_t * p_head , uint8_t dev_addr ) ;
//--------------------------------------------------------------------+
// USBH-HCD API
//--------------------------------------------------------------------+
// Initialization according to 5.1.1.4
2021-02-23 19:45:53 +07:00
bool hcd_init ( uint8_t rhport )
2020-01-14 23:30:39 -05:00
{
2021-02-23 19:45:53 +07:00
( void ) rhport ;
2020-01-14 23:30:39 -05:00
//------------- Data Structure init -------------//
tu_memclr ( & ohci_data , sizeof ( ohci_data_t ) ) ;
for ( uint8_t i = 0 ; i < 32 ; i + + )
{ // assign all interrupt pointes to period head ed
ohci_data . hcca . interrupt_table [ i ] = ( uint32_t ) & ohci_data . period_head_ed ;
}
ohci_data . control [ 0 ] . ed . skip = 1 ;
ohci_data . bulk_head_ed . skip = 1 ;
ohci_data . period_head_ed . skip = 1 ;
// reset controller
OHCI_REG - > command_status_bit . controller_reset = 1 ;
while ( OHCI_REG - > command_status_bit . controller_reset ) { } // should not take longer than 10 us
//------------- init ohci registers -------------//
OHCI_REG - > control_head_ed = ( uint32_t ) & ohci_data . control [ 0 ] . ed ;
OHCI_REG - > bulk_head_ed = ( uint32_t ) & ohci_data . bulk_head_ed ;
OHCI_REG - > hcca = ( uint32_t ) & ohci_data . hcca ;
OHCI_REG - > interrupt_disable = OHCI_REG - > interrupt_enable ; // disable all interrupts
OHCI_REG - > interrupt_status = OHCI_REG - > interrupt_status ; // clear current set bits
OHCI_REG - > interrupt_enable = OHCI_INT_WRITEBACK_DONEHEAD_MASK | OHCI_INT_RESUME_DETECTED_MASK |
2020-05-22 21:45:34 +07:00
OHCI_INT_UNRECOVERABLE_ERROR_MASK | OHCI_INT_FRAME_OVERFLOW_MASK | OHCI_INT_RHPORT_STATUS_CHANGE_MASK |
2020-01-14 23:30:39 -05:00
OHCI_INT_MASTER_ENABLE_MASK ;
OHCI_REG - > control | = OHCI_CONTROL_CONTROL_BULK_RATIO | OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK |
OHCI_CONTROL_LIST_BULK_ENABLE_MASK | OHCI_CONTROL_LIST_PERIODIC_ENABLE_MASK ; // TODO Isochronous
OHCI_REG - > frame_interval = ( OHCI_FMINTERVAL_FSMPS < < 16 ) | OHCI_FMINTERVAL_FI ;
OHCI_REG - > periodic_start = ( OHCI_FMINTERVAL_FI * 9 ) / 10 ; // Periodic start is 90% of frame interval
OHCI_REG - > control_bit . hc_functional_state = OHCI_CONTROL_FUNCSTATE_OPERATIONAL ; // make HC's state to operational state TODO use this to suspend (save power)
OHCI_REG - > rh_status_bit . local_power_status_change = 1 ; // set global power for ports
return true ;
}
2021-05-31 18:16:07 +07:00
uint32_t hcd_frame_number ( uint8_t rhport )
2020-05-22 21:45:34 +07:00
{
( void ) rhport ;
2021-05-31 18:16:07 +07:00
return ( ohci_data . frame_number_hi < < 16 ) | OHCI_REG - > frame_number ;
2020-05-22 21:45:34 +07:00
}
2020-01-14 23:30:39 -05:00
//--------------------------------------------------------------------+
// PORT API
//--------------------------------------------------------------------+
void hcd_port_reset ( uint8_t hostid )
{
( void ) hostid ;
2021-05-31 18:41:08 +07:00
OHCI_REG - > rhport_status [ 0 ] = RHPORT_PORT_RESET_STATUS_MASK ;
2020-01-14 23:30:39 -05:00
}
bool hcd_port_connect_status ( uint8_t hostid )
{
( void ) hostid ;
return OHCI_REG - > rhport_status_bit [ 0 ] . current_connect_status ;
}
tusb_speed_t hcd_port_speed_get ( uint8_t hostid )
{
( void ) hostid ;
return OHCI_REG - > rhport_status_bit [ 0 ] . low_speed_device_attached ? TUSB_SPEED_LOW : TUSB_SPEED_FULL ;
}
// endpoints are tied to an address, which only reclaim after a long delay when enumerating
// thus there is no need to make sure ED is not in HC's cahed as it will not for sure
void hcd_device_close ( uint8_t rhport , uint8_t dev_addr )
{
// TODO OHCI
( void ) rhport ;
// addr0 serves as static head --> only set skip bit
if ( dev_addr = = 0 )
{
ohci_data . control [ 0 ] . ed . skip = 1 ;
} else
{
// remove control
ed_list_remove_by_addr ( p_ed_head [ TUSB_XFER_CONTROL ] , dev_addr ) ;
// remove bulk
ed_list_remove_by_addr ( p_ed_head [ TUSB_XFER_BULK ] , dev_addr ) ;
// remove interrupt
ed_list_remove_by_addr ( p_ed_head [ TUSB_XFER_INTERRUPT ] , dev_addr ) ;
// TODO remove ISO
}
}
//--------------------------------------------------------------------+
// Controller API
//--------------------------------------------------------------------+
//--------------------------------------------------------------------+
2021-05-31 16:34:07 +07:00
// List Helper
2020-01-14 23:30:39 -05:00
//--------------------------------------------------------------------+
static inline tusb_xfer_type_t ed_get_xfer_type ( ohci_ed_t const * const p_ed )
{
2021-05-31 18:16:07 +07:00
return ( p_ed - > ep_number = = 0 ) ? TUSB_XFER_CONTROL :
( p_ed - > is_iso ) ? TUSB_XFER_ISOCHRONOUS :
( p_ed - > is_interrupt_xfer ) ? TUSB_XFER_INTERRUPT : TUSB_XFER_BULK ;
2020-01-14 23:30:39 -05:00
}
2021-05-31 18:16:07 +07:00
static void ed_init ( ohci_ed_t * p_ed , uint8_t dev_addr , uint16_t ep_size , uint8_t ep_addr , uint8_t xfer_type , uint8_t interval )
2020-01-14 23:30:39 -05:00
{
( void ) interval ;
// address 0 is used as async head, which always on the list --> cannot be cleared
if ( dev_addr ! = 0 )
{
tu_memclr ( p_ed , sizeof ( ohci_ed_t ) ) ;
}
2021-08-23 17:00:41 +07:00
hcd_devtree_info_t devtree_info ;
hcd_devtree_get_info ( dev_addr , & devtree_info ) ;
2021-05-31 18:16:07 +07:00
p_ed - > dev_addr = dev_addr ;
p_ed - > ep_number = ep_addr & 0x0F ;
p_ed - > pid = ( xfer_type = = TUSB_XFER_CONTROL ) ? PID_FROM_TD : ( tu_edpt_dir ( ep_addr ) ? PID_IN : PID_OUT ) ;
2021-08-23 17:00:41 +07:00
p_ed - > speed = devtree_info . speed ;
2020-01-14 23:30:39 -05:00
p_ed - > is_iso = ( xfer_type = = TUSB_XFER_ISOCHRONOUS ) ? 1 : 0 ;
2021-05-31 18:16:07 +07:00
p_ed - > max_packet_size = ep_size ;
2020-01-14 23:30:39 -05:00
p_ed - > used = 1 ;
p_ed - > is_interrupt_xfer = ( xfer_type = = TUSB_XFER_INTERRUPT ? 1 : 0 ) ;
}
2021-05-31 18:16:07 +07:00
static void gtd_init ( ohci_gtd_t * p_td , uint8_t * data_ptr , uint16_t total_bytes )
2020-01-14 23:30:39 -05:00
{
tu_memclr ( p_td , sizeof ( ohci_gtd_t ) ) ;
p_td - > used = 1 ;
p_td - > expected_bytes = total_bytes ;
p_td - > buffer_rounding = 1 ; // less than queued length is not a error
p_td - > delay_interrupt = OHCI_INT_ON_COMPLETE_NO ;
p_td - > condition_code = OHCI_CCODE_NOT_ACCESSED ;
p_td - > current_buffer_pointer = data_ptr ;
2021-05-31 18:16:07 +07:00
p_td - > buffer_end = total_bytes ? ( data_ptr + total_bytes - 1 ) : data_ptr ;
2020-01-14 23:30:39 -05:00
}
2020-09-06 11:49:00 +07:00
static ohci_ed_t * ed_from_addr ( uint8_t dev_addr , uint8_t ep_addr )
2020-01-14 23:30:39 -05:00
{
if ( tu_edpt_number ( ep_addr ) = = 0 ) return & ohci_data . control [ dev_addr ] . ed ;
ohci_ed_t * ed_pool = ohci_data . ed_pool ;
for ( uint32_t i = 0 ; i < HCD_MAX_ENDPOINT ; i + + )
{
if ( ( ed_pool [ i ] . dev_addr = = dev_addr ) & &
2021-05-31 18:16:07 +07:00
ep_addr = = tu_edpt_addr ( ed_pool [ i ] . ep_number , ed_pool [ i ] . pid = = PID_IN ) )
2020-01-14 23:30:39 -05:00
{
return & ed_pool [ i ] ;
}
}
return NULL ;
}
2020-09-06 11:49:00 +07:00
static ohci_ed_t * ed_find_free ( void )
2020-01-14 23:30:39 -05:00
{
ohci_ed_t * ed_pool = ohci_data . ed_pool ;
for ( uint8_t i = 0 ; i < HCD_MAX_ENDPOINT ; i + + )
{
if ( ! ed_pool [ i ] . used ) return & ed_pool [ i ] ;
}
return NULL ;
}
static void ed_list_insert ( ohci_ed_t * p_pre , ohci_ed_t * p_ed )
{
p_ed - > next = p_pre - > next ;
p_pre - > next = ( uint32_t ) p_ed ;
}
static void ed_list_remove_by_addr ( ohci_ed_t * p_head , uint8_t dev_addr )
{
ohci_ed_t * p_prev = p_head ;
while ( p_prev - > next )
{
ohci_ed_t * ed = ( ohci_ed_t * ) p_prev - > next ;
if ( ed - > dev_addr = = dev_addr )
{
// unlink ed
p_prev - > next = ed - > next ;
// point the removed ED's next pointer to list head to make sure HC can always safely move away from this ED
ed - > next = ( uint32_t ) p_head ;
ed - > used = 0 ;
}
// check next valid since we could remove it
if ( p_prev - > next ) p_prev = ( ohci_ed_t * ) p_prev - > next ;
}
}
2021-05-31 16:34:07 +07:00
static ohci_gtd_t * gtd_find_free ( void )
{
for ( uint8_t i = 0 ; i < HCD_MAX_XFER ; i + + )
{
if ( ! ohci_data . gtd_pool [ i ] . used ) return & ohci_data . gtd_pool [ i ] ;
}
return NULL ;
}
static void td_insert_to_ed ( ohci_ed_t * p_ed , ohci_gtd_t * p_gtd )
{
// tail is always NULL
if ( tu_align16 ( p_ed - > td_head . address ) = = 0 )
{ // TD queue is empty --> head = TD
p_ed - > td_head . address | = ( uint32_t ) p_gtd ;
}
else
{ // TODO currently only support queue up to 2 TD each endpoint at a time
( ( ohci_gtd_t * ) tu_align16 ( p_ed - > td_head . address ) ) - > next = ( uint32_t ) p_gtd ;
}
}
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
2020-01-14 23:30:39 -05:00
bool hcd_edpt_open ( uint8_t rhport , uint8_t dev_addr , tusb_desc_endpoint_t const * ep_desc )
{
2021-05-31 16:34:07 +07:00
( void ) rhport ;
2020-01-14 23:30:39 -05:00
// TODO iso support
TU_ASSERT ( ep_desc - > bmAttributes . xfer ! = TUSB_XFER_ISOCHRONOUS ) ;
//------------- Prepare Queue Head -------------//
ohci_ed_t * p_ed ;
if ( ep_desc - > bEndpointAddress = = 0 )
{
p_ed = & ohci_data . control [ dev_addr ] . ed ;
} else
{
p_ed = ed_find_free ( ) ;
}
TU_ASSERT ( p_ed ) ;
2021-10-24 13:11:21 +07:00
ed_init ( p_ed , dev_addr , tu_edpt_packet_size ( ep_desc ) , ep_desc - > bEndpointAddress ,
2020-01-14 23:30:39 -05:00
ep_desc - > bmAttributes . xfer , ep_desc - > bInterval ) ;
// control of dev0 is used as static async head
if ( dev_addr = = 0 )
{
p_ed - > skip = 0 ; // only need to clear skip bit
return true ;
}
ed_list_insert ( p_ed_head [ ep_desc - > bmAttributes . xfer ] , p_ed ) ;
return true ;
}
2021-05-31 18:16:07 +07:00
bool hcd_setup_send ( uint8_t rhport , uint8_t dev_addr , uint8_t const setup_packet [ 8 ] )
{
( void ) rhport ;
ohci_ed_t * ed = & ohci_data . control [ dev_addr ] . ed ;
ohci_gtd_t * qtd = & ohci_data . control [ dev_addr ] . gtd ;
gtd_init ( qtd , ( uint8_t * ) setup_packet , 8 ) ;
qtd - > index = dev_addr ;
qtd - > pid = PID_SETUP ;
qtd - > data_toggle = GTD_DT_DATA0 ;
qtd - > delay_interrupt = 0 ;
//------------- Attach TDs list to Control Endpoint -------------//
ed - > td_head . address = ( uint32_t ) qtd ;
OHCI_REG - > command_status_bit . control_list_filled = 1 ;
return true ;
}
2021-05-31 16:34:07 +07:00
bool hcd_edpt_xfer ( uint8_t rhport , uint8_t dev_addr , uint8_t ep_addr , uint8_t * buffer , uint16_t buflen )
2020-01-14 23:30:39 -05:00
{
2021-05-31 16:34:07 +07:00
( void ) rhport ;
2020-01-14 23:30:39 -05:00
2021-05-31 16:34:07 +07:00
uint8_t const epnum = tu_edpt_number ( ep_addr ) ;
uint8_t const dir = tu_edpt_dir ( ep_addr ) ;
2020-01-14 23:30:39 -05:00
2021-05-31 16:34:07 +07:00
if ( epnum = = 0 )
{
ohci_ed_t * ed = & ohci_data . control [ dev_addr ] . ed ;
ohci_gtd_t * gtd = & ohci_data . control [ dev_addr ] . gtd ;
2020-01-14 23:30:39 -05:00
2021-05-31 16:34:07 +07:00
gtd_init ( gtd , buffer , buflen ) ;
2020-01-14 23:30:39 -05:00
2021-05-31 16:34:07 +07:00
gtd - > index = dev_addr ;
2021-05-31 18:16:07 +07:00
gtd - > pid = dir ? PID_IN : PID_OUT ;
gtd - > data_toggle = GTD_DT_DATA1 ; // Both Data and Ack stage start with DATA1
gtd - > delay_interrupt = 0 ;
2020-01-14 23:30:39 -05:00
2021-05-31 16:34:07 +07:00
ed - > td_head . address = ( uint32_t ) gtd ;
2020-01-14 23:30:39 -05:00
2021-05-31 16:34:07 +07:00
OHCI_REG - > command_status_bit . control_list_filled = 1 ;
} else
{
ohci_ed_t * ed = ed_from_addr ( dev_addr , ep_addr ) ;
ohci_gtd_t * gtd = gtd_find_free ( ) ;
2020-01-14 23:30:39 -05:00
2021-05-31 16:34:07 +07:00
TU_ASSERT ( gtd ) ;
2020-01-14 23:30:39 -05:00
2021-05-31 16:34:07 +07:00
gtd_init ( gtd , buffer , buflen ) ;
gtd - > index = ed - ohci_data . ed_pool ;
2021-05-31 18:16:07 +07:00
gtd - > delay_interrupt = 0 ;
2020-01-14 23:30:39 -05:00
2021-05-31 16:34:07 +07:00
td_insert_to_ed ( ed , gtd ) ;
2020-01-14 23:30:39 -05:00
2021-05-31 16:34:07 +07:00
tusb_xfer_type_t xfer_type = ed_get_xfer_type ( ed_from_addr ( dev_addr , ep_addr ) ) ;
if ( TUSB_XFER_BULK = = xfer_type ) OHCI_REG - > command_status_bit . bulk_list_filled = 1 ;
}
2020-01-14 23:30:39 -05:00
return true ;
}
bool hcd_edpt_clear_stall ( uint8_t dev_addr , uint8_t ep_addr )
{
ohci_ed_t * const p_ed = ed_from_addr ( dev_addr , ep_addr ) ;
p_ed - > is_stalled = 0 ;
p_ed - > td_tail & = 0x0Ful ; // set tail pointer back to NULL
p_ed - > td_head . toggle = 0 ; // reset data toggle
p_ed - > td_head . halted = 0 ;
if ( TUSB_XFER_BULK = = ed_get_xfer_type ( p_ed ) ) OHCI_REG - > command_status_bit . bulk_list_filled = 1 ;
return true ;
}
//--------------------------------------------------------------------+
// OHCI Interrupt Handler
//--------------------------------------------------------------------+
static ohci_td_item_t * list_reverse ( ohci_td_item_t * td_head )
{
ohci_td_item_t * td_reverse_head = NULL ;
while ( td_head ! = NULL )
{
uint32_t next = td_head - > next ;
// make current's item become reverse's first item
td_head - > next = ( uint32_t ) td_reverse_head ;
td_reverse_head = td_head ;
td_head = ( ohci_td_item_t * ) next ; // advance to next item
}
return td_reverse_head ;
}
static inline bool gtd_is_control ( ohci_gtd_t const * const p_qtd )
{
return ( ( uint32_t ) p_qtd ) < ( ( uint32_t ) ohci_data . gtd_pool ) ; // check ohci_data_t for memory layout
}
static inline ohci_ed_t * gtd_get_ed ( ohci_gtd_t const * const p_qtd )
{
if ( gtd_is_control ( p_qtd ) )
{
return & ohci_data . control [ p_qtd - > index ] . ed ;
} else
{
return & ohci_data . ed_pool [ p_qtd - > index ] ;
}
}
static inline uint32_t gtd_xfer_byte_left ( uint32_t buffer_end , uint32_t current_buffer )
2021-05-31 18:16:07 +07:00
{
// 5.2.9 OHCI sample code
// CBP is 0 mean all data is transferred
if ( current_buffer = = 0 ) return 0 ;
2020-01-14 23:30:39 -05:00
return ( tu_align4k ( buffer_end ^ current_buffer ) ? 0x1000 : 0 ) +
tu_offset4k ( buffer_end ) - tu_offset4k ( current_buffer ) + 1 ;
}
static void done_queue_isr ( uint8_t hostid )
{
( void ) hostid ;
// done head is written in reversed order of completion --> need to reverse the done queue first
ohci_td_item_t * td_head = list_reverse ( ( ohci_td_item_t * ) tu_align16 ( ohci_data . hcca . done_head ) ) ;
while ( td_head ! = NULL )
{
// TODO check if td_head is iso td
//------------- Non ISO transfer -------------//
2021-05-31 18:16:07 +07:00
ohci_gtd_t * const qtd = ( ohci_gtd_t * ) td_head ;
xfer_result_t const event = ( qtd - > condition_code = = OHCI_CCODE_NO_ERROR ) ? XFER_RESULT_SUCCESS :
( qtd - > condition_code = = OHCI_CCODE_STALL ) ? XFER_RESULT_STALLED : XFER_RESULT_FAILED ;
2020-01-14 23:30:39 -05:00
2021-05-31 18:16:07 +07:00
qtd - > used = 0 ; // free TD
if ( ( qtd - > delay_interrupt = = OHCI_INT_ON_COMPLETE_YES ) | | ( event ! = XFER_RESULT_SUCCESS ) )
2020-01-14 23:30:39 -05:00
{
2021-05-31 18:16:07 +07:00
ohci_ed_t * const ed = gtd_get_ed ( qtd ) ;
2020-01-14 23:30:39 -05:00
2021-05-31 18:16:07 +07:00
uint32_t const xferred_bytes = qtd - > expected_bytes - gtd_xfer_byte_left ( ( uint32_t ) qtd - > buffer_end , ( uint32_t ) qtd - > current_buffer_pointer ) ;
2020-01-14 23:30:39 -05:00
// NOTE Assuming the current list is BULK and there is no other EDs in the list has queued TDs.
// When there is a error resulting this ED is halted, and this EP still has other queued TD
// --> the Bulk list only has this halted EP queueing TDs (remaining)
// --> Bulk list will be considered as not empty by HC !!! while there is no attempt transaction on this list
// --> HC will not process Control list (due to service ratio when Bulk list not empty)
// To walk-around this, the halted ED will have TailP = HeadP (empty list condition), when clearing halt
// the TailP must be set back to NULL for processing remaining TDs
if ( ( event ! = XFER_RESULT_SUCCESS ) )
{
2021-05-31 18:16:07 +07:00
ed - > td_tail & = 0x0Ful ;
ed - > td_tail | = tu_align16 ( ed - > td_head . address ) ; // mark halted EP as empty queue
if ( event = = XFER_RESULT_STALLED ) ed - > is_stalled = 1 ;
2020-01-14 23:30:39 -05:00
}
2021-05-31 18:16:07 +07:00
uint8_t dir = ( ed - > ep_number = = 0 ) ? ( qtd - > pid = = PID_IN ) : ( ed - > pid = = PID_IN ) ;
hcd_event_xfer_complete ( ed - > dev_addr , tu_edpt_addr ( ed - > ep_number , dir ) , xferred_bytes , event , true ) ;
2020-01-14 23:30:39 -05:00
}
td_head = ( ohci_td_item_t * ) td_head - > next ;
}
}
2020-09-03 13:12:09 +07:00
void hcd_int_handler ( uint8_t hostid )
2020-01-14 23:30:39 -05:00
{
uint32_t const int_en = OHCI_REG - > interrupt_enable ;
uint32_t const int_status = OHCI_REG - > interrupt_status & int_en ;
if ( int_status = = 0 ) return ;
2020-05-22 21:45:34 +07:00
// Frame number overflow
if ( int_status & OHCI_INT_FRAME_OVERFLOW_MASK )
{
ohci_data . frame_number_hi + + ;
}
2020-01-14 23:30:39 -05:00
//------------- RootHub status -------------//
if ( int_status & OHCI_INT_RHPORT_STATUS_CHANGE_MASK )
{
2021-05-31 18:41:08 +07:00
uint32_t const rhport_status = OHCI_REG - > rhport_status [ 0 ] & RHPORT_ALL_CHANGE_MASK ;
2020-01-14 23:30:39 -05:00
// TODO dual port is not yet supported
2021-05-31 18:41:08 +07:00
if ( rhport_status & RHPORT_CONNECT_STATUS_CHANGE_MASK )
2020-01-14 23:30:39 -05:00
{
// TODO check if remote wake-up
if ( OHCI_REG - > rhport_status_bit [ 0 ] . current_connect_status )
{
// TODO reset port immediately, without this controller will got 2-3 (debouncing connection status change)
2021-05-31 18:41:08 +07:00
OHCI_REG - > rhport_status [ 0 ] = RHPORT_PORT_RESET_STATUS_MASK ;
2020-09-05 15:45:03 +07:00
hcd_event_device_attach ( hostid , true ) ;
2020-01-14 23:30:39 -05:00
} else
{
2020-09-05 15:45:03 +07:00
hcd_event_device_remove ( hostid , true ) ;
2020-01-14 23:30:39 -05:00
}
}
2021-05-31 18:41:08 +07:00
if ( rhport_status & RHPORT_PORT_SUSPEND_CHANGE_MASK )
2020-01-14 23:30:39 -05:00
{
}
OHCI_REG - > rhport_status [ 0 ] = rhport_status ; // acknowledge all interrupt
}
//------------- Transfer Complete -------------//
2021-05-31 18:16:07 +07:00
if ( int_status & OHCI_INT_WRITEBACK_DONEHEAD_MASK )
2020-01-14 23:30:39 -05:00
{
done_queue_isr ( hostid ) ;
}
OHCI_REG - > interrupt_status = int_status ; // Acknowledge handled interrupt
}
//--------------------------------------------------------------------+
// HELPER
//--------------------------------------------------------------------+
# endif