2013-12-11 14:31:27 +07:00
/**************************************************************************/
/*!
@ file ohci . c
@ author hathach ( tinyusb . org )
@ section LICENSE
Software License Agreement ( BSD License )
Copyright ( c ) 2013 , hathach ( tinyusb . org )
All rights reserved .
Redistribution and use in source and binary forms , with or without
modification , are permitted provided that the following conditions are met :
1. Redistributions of source code must retain the above copyright
notice , this list of conditions and the following disclaimer .
2. Redistributions in binary form must reproduce the above copyright
notice , this list of conditions and the following disclaimer in the
documentation and / or other materials provided with the distribution .
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission .
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ' ' AS IS ' ' AND ANY
EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES
( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ;
LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
This file is part of the tinyusb stack .
*/
/**************************************************************************/
2018-03-12 22:45:35 +07:00
# include <common/tusb_common.h>
2013-12-11 14:31:27 +07:00
2018-12-07 22:01:26 +07:00
# if MODE_HOST_SUPPORTED && (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC40XX)
2013-12-11 14:31:27 +07:00
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
# include "osal/osal.h"
# include "../hcd.h"
# include "../usbh_hcd.h"
# include "ohci.h"
2018-12-07 18:49:26 +07:00
// TODO remove
# include "chip.h"
2013-12-11 14:31:27 +07:00
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
# define OHCI_REG ((ohci_registers_t *) LPC_USB_BASE)
2013-12-13 12:26:37 +07:00
enum {
OHCI_CONTROL_FUNCSTATE_RESET = 0 ,
OHCI_CONTROL_FUNCSTATE_RESUME ,
OHCI_CONTROL_FUNCSTATE_OPERATIONAL ,
OHCI_CONTROL_FUNCSTATE_SUSPEND
} ;
enum {
2013-12-18 14:57:34 +07:00
OHCI_CONTROL_CONTROL_BULK_RATIO = 3 , ///< This specifies the service ratio between Control and Bulk EDs. 0 = 1:1, 3 = 4:1
2013-12-13 12:26:37 +07:00
OHCI_CONTROL_LIST_PERIODIC_ENABLE_MASK = BIT_ ( 2 ) ,
OHCI_CONTROL_LIST_ISOCHRONOUS_ENABLE_MASK = BIT_ ( 3 ) ,
OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK = BIT_ ( 4 ) ,
OHCI_CONTROL_LIST_BULK_ENABLE_MASK = 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 = BIT_ ( 0 ) ,
OHCI_INT_WRITEBACK_DONEHEAD_MASK = BIT_ ( 1 ) ,
OHCI_INT_SOF_MASK = BIT_ ( 2 ) ,
OHCI_INT_RESUME_DETECTED_MASK = BIT_ ( 3 ) ,
OHCI_INT_UNRECOVERABLE_ERROR_MASK = BIT_ ( 4 ) ,
OHCI_INT_FRAME_OVERFLOW_MASK = BIT_ ( 5 ) ,
OHCI_INT_RHPORT_STATUS_CHANGE_MASK = BIT_ ( 6 ) ,
OHCI_INT_OWNERSHIP_CHANGE_MASK = BIT_ ( 30 ) ,
OHCI_INT_MASTER_ENABLE_MASK = BIT_ ( 31 ) ,
2013-12-16 12:35:05 +07:00
} ;
enum {
OHCI_RHPORT_CURRENT_CONNECT_STATUS_MASK = BIT_ ( 0 ) ,
OHCI_RHPORT_PORT_ENABLE_STATUS_MASK = BIT_ ( 1 ) ,
OHCI_RHPORT_PORT_SUSPEND_STATUS_MASK = BIT_ ( 2 ) ,
OHCI_RHPORT_PORT_OVER_CURRENT_INDICATOR_MASK = BIT_ ( 3 ) ,
OHCI_RHPORT_PORT_RESET_STATUS_MASK = BIT_ ( 4 ) , ///< write '1' to reset port
OHCI_RHPORT_PORT_POWER_STATUS_MASK = BIT_ ( 8 ) ,
OHCI_RHPORT_LOW_SPEED_DEVICE_ATTACHED_MASK = BIT_ ( 9 ) ,
OHCI_RHPORT_CONNECT_STATUS_CHANGE_MASK = BIT_ ( 16 ) ,
OHCI_RHPORT_PORT_ENABLE_CHANGE_MASK = BIT_ ( 17 ) ,
OHCI_RHPORT_PORT_SUSPEND_CHANGE_MASK = BIT_ ( 18 ) ,
OHCI_RHPORT_OVER_CURRENT_CHANGE_MASK = BIT_ ( 19 ) ,
OHCI_RHPORT_PORT_RESET_CHANGE_MASK = BIT_ ( 20 ) ,
OHCI_RHPORT_ALL_CHANGE_MASK = OHCI_RHPORT_CONNECT_STATUS_CHANGE_MASK | OHCI_RHPORT_PORT_ENABLE_CHANGE_MASK |
OHCI_RHPORT_PORT_SUSPEND_CHANGE_MASK | OHCI_RHPORT_OVER_CURRENT_CHANGE_MASK | OHCI_RHPORT_PORT_RESET_CHANGE_MASK
} ;
2013-12-13 12:26:37 +07:00
2013-12-16 12:35:05 +07: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 ,
2013-12-13 12:26:37 +07:00
} ;
2013-12-16 12:35:05 +07:00
enum {
OHCI_INT_ON_COMPLETE_YES = 0 ,
OHCI_INT_ON_COMPLETE_NO = BIN8 ( 111 )
} ;
2013-12-11 14:31:27 +07:00
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
2018-11-22 17:21:07 +07:00
CFG_TUSB_MEM_SECTION ATTR_ALIGNED ( 256 ) STATIC_VAR ohci_data_t ohci_data ;
2013-12-11 14:31:27 +07:00
2013-12-18 16:23:09 +07:00
static ohci_ed_t * const p_ed_head [ ] =
{
[ TUSB_XFER_CONTROL ] = & ohci_data . control [ 0 ] . ed ,
[ TUSB_XFER_BULK ] = & ohci_data . bulk_head_ed ,
2013-12-19 11:59:19 +07:00
[ TUSB_XFER_INTERRUPT ] = & ohci_data . period_head_ed ,
[ TUSB_XFER_ISOCHRONOUS ] = NULL // TODO Isochronous
2013-12-18 16:23:09 +07:00
} ;
2013-12-17 11:26:39 +07:00
static void ed_list_insert ( ohci_ed_t * p_pre , ohci_ed_t * p_ed ) ;
2013-12-18 16:23:09 +07:00
static void ed_list_remove ( ohci_ed_t * p_head , ohci_ed_t * p_ed ) ;
static ohci_ed_t * ed_list_find_previous ( ohci_ed_t const * p_head , ohci_ed_t const * p_ed ) ;
2013-12-17 11:26:39 +07:00
2013-12-11 14:31:27 +07:00
//--------------------------------------------------------------------+
// USBH-HCD API
//--------------------------------------------------------------------+
2013-12-13 12:26:37 +07:00
// Initialization according to 5.1.1.4
2018-12-07 23:28:51 +07:00
bool hcd_init ( void )
2013-12-11 14:31:27 +07:00
{
//------------- Data Structure init -------------//
2018-10-23 12:19:32 +07:00
tu_memclr ( & ohci_data , sizeof ( ohci_data_t ) ) ;
2013-12-19 11:59:19 +07:00
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 ;
}
2013-12-17 11:26:39 +07:00
ohci_data . control [ 0 ] . ed . skip = 1 ;
ohci_data . bulk_head_ed . skip = 1 ;
ohci_data . period_head_ed . skip = 1 ;
2013-12-13 12:26:37 +07:00
// 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 ;
2013-12-17 11:26:39 +07:00
OHCI_REG - > bulk_head_ed = ( uint32_t ) & ohci_data . bulk_head_ed ;
2013-12-13 12:26:37 +07:00
OHCI_REG - > hcca = ( uint32_t ) & ohci_data . hcca ;
2013-12-16 12:35:05 +07:00
OHCI_REG - > interrupt_disable = OHCI_REG - > interrupt_enable ; // disable all interrupts
2013-12-13 12:26:37 +07:00
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 |
2013-12-18 14:57:34 +07:00
OHCI_INT_UNRECOVERABLE_ERROR_MASK | /*OHCI_INT_FRAME_OVERFLOW_MASK |*/ OHCI_INT_RHPORT_STATUS_CHANGE_MASK |
2013-12-13 12:26:37 +07:00
OHCI_INT_MASTER_ENABLE_MASK ;
2013-12-11 14:31:27 +07:00
2013-12-19 12:11:13 +07:00
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
2018-12-07 23:28:51 +07:00
return true ;
2013-12-11 14:31:27 +07:00
}
//--------------------------------------------------------------------+
// PORT API
//--------------------------------------------------------------------+
void hcd_port_reset ( uint8_t hostid )
{
2018-12-07 18:49:26 +07:00
( void ) hostid ;
2013-12-16 12:35:05 +07:00
OHCI_REG - > rhport_status [ 0 ] = OHCI_RHPORT_PORT_RESET_STATUS_MASK ;
2013-12-11 14:31:27 +07:00
}
bool hcd_port_connect_status ( uint8_t hostid )
{
2018-12-07 18:49:26 +07:00
( void ) hostid ;
2013-12-16 12:35:05 +07:00
return OHCI_REG - > rhport_status_bit [ 0 ] . current_connect_status ;
2013-12-11 14:31:27 +07:00
}
tusb_speed_t hcd_port_speed_get ( uint8_t hostid )
{
2018-12-07 18:49:26 +07:00
( void ) hostid ;
2013-12-16 12:35:05 +07:00
return OHCI_REG - > rhport_status_bit [ 0 ] . low_speed_device_attached ? TUSB_SPEED_LOW : TUSB_SPEED_FULL ;
2013-12-11 14:31:27 +07:00
}
// TODO refractor abtract later
void hcd_port_unplug ( uint8_t hostid )
{
// TODO OHCI
2018-12-07 18:49:26 +07:00
( void ) hostid ;
2013-12-11 14:31:27 +07:00
}
//--------------------------------------------------------------------+
// Controller API
//--------------------------------------------------------------------+
//--------------------------------------------------------------------+
// CONTROL PIPE API
//--------------------------------------------------------------------+
2013-12-18 14:57:34 +07:00
static inline tusb_xfer_type_t ed_get_xfer_type ( ohci_ed_t const * const p_ed )
{
return ( p_ed - > endpoint_number = = 0 ) ? TUSB_XFER_CONTROL :
( p_ed - > is_iso ) ? TUSB_XFER_ISOCHRONOUS :
( p_ed - > is_interrupt_xfer ) ? TUSB_XFER_INTERRUPT : TUSB_XFER_BULK ;
}
2013-12-16 12:35:05 +07:00
static void ed_init ( ohci_ed_t * p_ed , uint8_t dev_addr , uint16_t max_packet_size , uint8_t endpoint_addr , uint8_t xfer_type , uint8_t interval )
{
2018-12-07 18:49:26 +07:00
( void ) interval ;
2013-12-16 12:35:05 +07:00
// address 0 is used as async head, which always on the list --> cannot be cleared
if ( dev_addr ! = 0 )
{
2018-10-23 12:19:32 +07:00
tu_memclr ( p_ed , sizeof ( ohci_ed_t ) ) ;
2013-12-16 12:35:05 +07:00
}
2013-12-18 14:57:34 +07:00
p_ed - > device_address = dev_addr ;
p_ed - > endpoint_number = endpoint_addr & 0x0F ;
2018-03-11 20:20:59 +07:00
p_ed - > direction = ( xfer_type = = TUSB_XFER_CONTROL ) ? OHCI_PID_SETUP : ( ( endpoint_addr & TUSB_DIR_IN_MASK ) ? OHCI_PID_IN : OHCI_PID_OUT ) ;
2018-12-07 18:49:26 +07:00
p_ed - > speed = _usbh_devices [ dev_addr ] . speed ;
2013-12-18 14:57:34 +07:00
p_ed - > is_iso = ( xfer_type = = TUSB_XFER_ISOCHRONOUS ) ? 1 : 0 ;
p_ed - > max_package_size = max_packet_size ;
2013-12-17 11:26:39 +07:00
2013-12-18 14:57:34 +07:00
p_ed - > used = 1 ;
p_ed - > is_interrupt_xfer = ( xfer_type = = TUSB_XFER_INTERRUPT ? 1 : 0 ) ;
2013-12-16 12:35:05 +07:00
}
2013-12-17 11:26:39 +07:00
static void gtd_init ( ohci_gtd_t * p_td , void * data_ptr , uint16_t total_bytes )
2013-12-16 12:35:05 +07:00
{
2018-10-23 12:19:32 +07:00
tu_memclr ( p_td , sizeof ( ohci_gtd_t ) ) ;
2013-12-16 12:35:05 +07:00
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 ;
p_td - > buffer_end = total_bytes ? ( ( ( uint8_t * ) data_ptr ) + total_bytes - 1 ) : NULL ;
}
2018-12-07 22:01:26 +07:00
2018-12-07 23:28:51 +07:00
bool hcd_pipe_control_open ( uint8_t dev_addr , uint8_t max_packet_size )
2013-12-11 14:31:27 +07:00
{
2018-12-07 22:01:26 +07:00
ohci_ed_t * p_ed = & ohci_data . control [ dev_addr ] . ed ;
2013-12-16 12:35:05 +07:00
ed_init ( p_ed , dev_addr , max_packet_size , 0 , TUSB_XFER_CONTROL , 0 ) ; // TODO binterval of control is ignored
if ( dev_addr ! = 0 )
{ // insert to control head
2013-12-18 16:23:09 +07:00
ed_list_insert ( p_ed_head [ TUSB_XFER_CONTROL ] , p_ed ) ;
2013-12-16 12:35:05 +07:00
} else
{
p_ed - > skip = 0 ; // addr0 is used as static control head --> only need to clear skip bit
}
2018-12-07 23:28:51 +07:00
return true ;
2013-12-11 14:31:27 +07:00
}
2018-12-07 23:16:06 +07:00
//bool hcd_pipe_control_xfer(uint8_t dev_addr, tusb_control_request_t const * p_request, uint8_t data[])
//{
// ohci_ed_t* const p_ed = &ohci_data.control[dev_addr].ed;
//
// ohci_gtd_t *p_setup = &ohci_data.control[dev_addr].gtd[0];
// ohci_gtd_t *p_data = p_setup + 1;
// ohci_gtd_t *p_status = p_setup + 2;
//
// //------------- SETUP Phase -------------//
// gtd_init(p_setup, (void*) p_request, 8);
// p_setup->index = dev_addr;
// p_setup->pid = OHCI_PID_SETUP;
// p_setup->data_toggle = BIN8(10); // DATA0
// p_setup->next_td = (uint32_t) p_data;
//
// //------------- DATA Phase -------------//
// if (p_request->wLength > 0)
// {
// gtd_init(p_data, data, p_request->wLength);
// p_data->index = dev_addr;
// p_data->pid = p_request->bmRequestType_bit.direction ? OHCI_PID_IN : OHCI_PID_OUT;
// p_data->data_toggle = BIN8(11); // DATA1
// }else
// {
// p_data = p_setup;
// }
// p_data->next_td = (uint32_t) p_status;
//
// //------------- STATUS Phase -------------//
// gtd_init(p_status, NULL, 0); // zero-length data
// p_status->index = dev_addr;
// p_status->pid = p_request->bmRequestType_bit.direction ? OHCI_PID_OUT : OHCI_PID_IN; // reverse direction of data phase
// p_status->data_toggle = BIN8(11); // DATA1
// p_status->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
//
// //------------- Attach TDs list to Control Endpoint -------------//
// p_ed->td_head.address = (uint32_t) p_setup;
//
// OHCI_REG->command_status_bit.control_list_filled = 1;
//
// return true;
//}
2013-12-11 14:31:27 +07:00
2018-12-07 23:28:51 +07:00
bool hcd_pipe_control_close ( uint8_t dev_addr )
2013-12-11 14:31:27 +07:00
{
2013-12-18 14:57:34 +07:00
ohci_ed_t * const p_ed = & ohci_data . control [ dev_addr ] . ed ;
if ( dev_addr = = 0 )
{ // addr0 serves as static head --> only set skip bitx
p_ed - > skip = 1 ;
} else
{
2013-12-18 16:23:09 +07:00
ed_list_remove ( p_ed_head [ ed_get_xfer_type ( p_ed ) ] , p_ed ) ;
// TODO refractor to be USBH
2018-12-07 18:49:26 +07:00
_usbh_devices [ dev_addr ] . state = TUSB_DEVICE_STATE_UNPLUG ;
2013-12-18 14:57:34 +07:00
}
2018-12-07 23:28:51 +07:00
return true ;
}
bool hcd_edpt_open ( uint8_t rhport , uint8_t dev_addr , tusb_desc_endpoint_t const * ep_desc )
{
// FIXME control only for now
( void ) rhport ;
return hcd_pipe_control_open ( dev_addr , ep_desc - > wMaxPacketSize . size ) ;
}
bool hcd_edpt_close ( uint8_t rhport , uint8_t dev_addr , uint8_t ep_addr )
{
// FIXME control only for now
( void ) rhport ;
( void ) ep_addr ;
return hcd_pipe_control_close ( dev_addr ) ;
}
bool hcd_setup_send ( uint8_t rhport , uint8_t dev_addr , uint8_t const setup_packet [ 8 ] )
{
( void ) rhport ;
ohci_ed_t * p_ed = & ohci_data . control [ dev_addr ] . ed ;
ohci_gtd_t * p_setup = & ohci_data . control [ dev_addr ] . gtd [ 0 ] ;
gtd_init ( p_setup , ( void * ) setup_packet , 8 ) ;
p_setup - > index = dev_addr ;
p_setup - > pid = OHCI_PID_SETUP ;
p_setup - > data_toggle = BIN8 ( 10 ) ; // DATA0
p_setup - > delay_interrupt = OHCI_INT_ON_COMPLETE_YES ;
//------------- Attach TDs list to Control Endpoint -------------//
p_ed - > td_head . address = ( uint32_t ) p_setup ;
OHCI_REG - > command_status_bit . control_list_filled = 1 ;
return true ;
}
bool hcd_edpt_xfer ( uint8_t rhport , uint8_t dev_addr , uint8_t ep_addr , uint8_t * buffer , uint16_t buflen )
{
( void ) rhport ;
uint8_t const epnum = edpt_number ( ep_addr ) ;
uint8_t const dir = edpt_dir ( ep_addr ) ;
// FIXME control only for now
if ( epnum = = 0 )
{
ohci_ed_t * const p_ed = & ohci_data . control [ dev_addr ] . ed ;
ohci_gtd_t * p_data = & ohci_data . control [ dev_addr ] . gtd [ 0 ] ;
gtd_init ( p_data , buffer , buflen ) ;
p_data - > index = dev_addr ;
p_data - > pid = dir ? OHCI_PID_IN : OHCI_PID_OUT ;
p_data - > data_toggle = BIN8 ( 11 ) ; // DATA1
p_data - > delay_interrupt = OHCI_INT_ON_COMPLETE_YES ;
p_ed - > td_head . address = ( uint32_t ) p_data ;
OHCI_REG - > command_status_bit . control_list_filled = 1 ;
}
return true ;
2013-12-11 14:31:27 +07:00
}
//--------------------------------------------------------------------+
// BULK/INT/ISO PIPE API
//--------------------------------------------------------------------+
2013-12-18 14:57:34 +07:00
static inline uint8_t ed_get_index ( ohci_ed_t const * const p_ed ) ATTR_PURE ATTR_ALWAYS_INLINE ;
static inline uint8_t ed_get_index ( ohci_ed_t const * const p_ed )
{
return p_ed - ohci_data . device [ p_ed - > device_address - 1 ] . ed ;
}
static inline ohci_ed_t * ed_from_pipe_handle ( pipe_handle_t pipe_hdl ) ATTR_PURE ATTR_ALWAYS_INLINE ;
static inline ohci_ed_t * ed_from_pipe_handle ( pipe_handle_t pipe_hdl )
{
return & ohci_data . device [ pipe_hdl . dev_addr - 1 ] . ed [ pipe_hdl . index ] ;
}
static inline ohci_ed_t * ed_find_free ( uint8_t dev_addr ) ATTR_PURE ATTR_ALWAYS_INLINE ;
static inline ohci_ed_t * ed_find_free ( uint8_t dev_addr )
2013-12-17 11:26:39 +07:00
{
2014-04-13 15:39:57 +07:00
for ( uint8_t i = 0 ; i < HCD_MAX_ENDPOINT ; i + + )
2013-12-17 11:26:39 +07:00
{
if ( ! ohci_data . device [ dev_addr - 1 ] . ed [ i ] . used )
{
return & ohci_data . device [ dev_addr - 1 ] . ed [ i ] ;
}
}
return NULL ;
}
2013-12-18 16:23:09 +07:00
static ohci_ed_t * ed_list_find_previous ( ohci_ed_t const * p_head , ohci_ed_t const * p_ed )
{
2018-04-10 14:31:11 +07:00
uint32_t max_loop = HCD_MAX_ENDPOINT * CFG_TUSB_HOST_DEVICE_MAX ;
2013-12-18 16:23:09 +07:00
ohci_ed_t const * p_prev = p_head ;
2018-03-28 14:49:00 +07:00
TU_ASSERT ( p_prev , NULL ) ;
2013-12-18 16:23:09 +07:00
2018-08-23 20:09:28 +07:00
while ( tu_align16 ( p_prev - > next_ed ) ! = 0 & & /* not reach null */
tu_align16 ( p_prev - > next_ed ) ! = ( uint32_t ) p_ed & & /* not found yet */
2013-12-18 16:23:09 +07:00
max_loop > 0 )
{
2018-08-23 20:09:28 +07:00
p_prev = ( ohci_ed_t const * ) tu_align16 ( p_prev - > next_ed ) ;
2013-12-18 16:23:09 +07:00
max_loop - - ;
}
2018-08-23 20:09:28 +07:00
return ( tu_align16 ( p_prev - > next_ed ) = = ( uint32_t ) p_ed ) ? ( ohci_ed_t * ) p_prev : NULL ;
2013-12-18 16:23:09 +07:00
}
2013-12-17 11:26:39 +07:00
static void ed_list_insert ( ohci_ed_t * p_pre , ohci_ed_t * p_ed )
{
2013-12-18 14:57:34 +07:00
p_ed - > next_ed | = p_pre - > next_ed ; // to reserve 4 lsb bits
p_pre - > next_ed = ( p_pre - > next_ed & 0x0FUL ) | ( ( uint32_t ) p_ed ) ;
2013-12-17 11:26:39 +07:00
}
2013-12-18 16:23:09 +07:00
static void ed_list_remove ( ohci_ed_t * p_head , ohci_ed_t * p_ed )
{
ohci_ed_t * const p_prev = ed_list_find_previous ( p_head , p_ed ) ;
2018-08-23 20:09:28 +07:00
p_prev - > next_ed = ( p_prev - > next_ed & 0x0fUL ) | tu_align16 ( p_ed - > next_ed ) ;
2013-12-18 16:23:09 +07:00
// point the removed ED's next pointer to list head to make sure HC can always safely move away from this ED
p_ed - > next_ed = ( uint32_t ) p_head ;
p_ed - > used = 0 ; // free ED
}
2018-03-23 12:32:40 +07:00
pipe_handle_t hcd_pipe_open ( uint8_t dev_addr , tusb_desc_endpoint_t const * p_endpoint_desc , uint8_t class_code )
2013-12-11 14:31:27 +07:00
{
2013-12-17 11:26:39 +07:00
pipe_handle_t const null_handle = { . dev_addr = 0 , . xfer_type = 0 , . index = 0 } ;
// TODO iso support
2018-03-28 14:49:00 +07:00
TU_ASSERT ( p_endpoint_desc - > bmAttributes . xfer ! = TUSB_XFER_ISOCHRONOUS , null_handle ) ;
2013-12-17 11:26:39 +07:00
//------------- Prepare Queue Head -------------//
ohci_ed_t * const p_ed = ed_find_free ( dev_addr ) ;
2018-03-28 14:49:00 +07:00
TU_ASSERT ( p_ed , null_handle ) ;
2013-12-17 11:26:39 +07:00
ed_init ( p_ed , dev_addr , p_endpoint_desc - > wMaxPacketSize . size , p_endpoint_desc - > bEndpointAddress ,
p_endpoint_desc - > bmAttributes . xfer , p_endpoint_desc - > bInterval ) ;
2013-12-18 14:57:34 +07:00
p_ed - > td_tail . class_code = class_code ;
2013-12-17 11:26:39 +07:00
2013-12-18 16:23:09 +07:00
ed_list_insert ( p_ed_head [ p_endpoint_desc - > bmAttributes . xfer ] , p_ed ) ;
2013-12-17 11:26:39 +07:00
return ( pipe_handle_t )
{
. dev_addr = dev_addr ,
. xfer_type = p_endpoint_desc - > bmAttributes . xfer ,
2013-12-18 14:57:34 +07:00
. index = ed_get_index ( p_ed )
2013-12-17 11:26:39 +07:00
} ;
}
static ohci_gtd_t * gtd_find_free ( uint8_t dev_addr )
{
2014-04-13 15:39:57 +07:00
for ( uint8_t i = 0 ; i < HCD_MAX_XFER ; i + + )
2013-12-17 11:26:39 +07:00
{
if ( ! ohci_data . device [ dev_addr - 1 ] . gtd [ i ] . used )
{
return & ohci_data . device [ dev_addr - 1 ] . gtd [ i ] ;
}
}
return NULL ;
}
static void td_insert_to_ed ( ohci_ed_t * p_ed , ohci_gtd_t * p_gtd )
{
// tail is always NULL
2018-08-23 20:09:28 +07:00
if ( tu_align16 ( p_ed - > td_head . address ) = = 0 )
2013-12-17 11:26:39 +07:00
{ // TD queue is empty --> head = TD
2014-03-04 15:19:50 +07:00
p_ed - > td_head . address | = ( uint32_t ) p_gtd ;
2013-12-17 11:26:39 +07:00
}
else
{ // TODO currently only support queue up to 2 TD each endpoint at a time
2018-08-23 20:09:28 +07:00
( ( ohci_gtd_t * ) tu_align16 ( p_ed - > td_head . address ) ) - > next_td = ( uint32_t ) p_gtd ;
2013-12-17 11:26:39 +07:00
}
}
static tusb_error_t pipe_queue_xfer ( pipe_handle_t pipe_hdl , uint8_t buffer [ ] , uint16_t total_bytes , bool int_on_complete )
{
2013-12-18 14:57:34 +07:00
ohci_ed_t * const p_ed = ed_from_pipe_handle ( pipe_hdl ) ;
2013-12-17 11:26:39 +07:00
if ( ! p_ed - > is_iso )
{
ohci_gtd_t * const p_gtd = gtd_find_free ( pipe_hdl . dev_addr ) ;
2018-03-28 14:49:00 +07:00
TU_ASSERT ( p_gtd , TUSB_ERROR_EHCI_NOT_ENOUGH_QTD ) ; // TODO refractor error code
2013-12-17 11:26:39 +07:00
gtd_init ( p_gtd , buffer , total_bytes ) ;
p_gtd - > index = pipe_hdl . index ;
if ( int_on_complete ) p_gtd - > delay_interrupt = OHCI_INT_ON_COMPLETE_YES ;
td_insert_to_ed ( p_ed , p_gtd ) ;
} else
{
2018-04-12 17:55:15 +07:00
TU_ASSERT_ERR ( TUSB_ERROR_NOT_SUPPORTED_YET ) ;
2013-12-17 11:26:39 +07:00
}
return TUSB_ERROR_NONE ;
2013-12-11 14:31:27 +07:00
}
tusb_error_t hcd_pipe_queue_xfer ( pipe_handle_t pipe_hdl , uint8_t buffer [ ] , uint16_t total_bytes )
{
2013-12-17 11:26:39 +07:00
return pipe_queue_xfer ( pipe_hdl , buffer , total_bytes , false ) ;
2013-12-11 14:31:27 +07:00
}
tusb_error_t hcd_pipe_xfer ( pipe_handle_t pipe_hdl , uint8_t buffer [ ] , uint16_t total_bytes , bool int_on_complete )
{
2018-12-07 18:49:26 +07:00
( void ) int_on_complete ;
2018-04-12 17:55:15 +07:00
TU_ASSERT_ERR ( pipe_queue_xfer ( pipe_hdl , buffer , total_bytes , true ) ) ;
2013-12-17 11:26:39 +07:00
2013-12-18 14:57:34 +07:00
tusb_xfer_type_t xfer_type = ed_get_xfer_type ( ed_from_pipe_handle ( pipe_hdl ) ) ;
2013-12-18 16:23:09 +07:00
if ( TUSB_XFER_BULK = = xfer_type ) OHCI_REG - > command_status_bit . bulk_list_filled = 1 ;
2013-12-17 11:26:39 +07:00
return TUSB_ERROR_NONE ;
2013-12-11 14:31:27 +07:00
}
/// pipe_close should only be called as a part of unmount/safe-remove process
2013-12-18 16:23:09 +07:00
// 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
2013-12-11 14:31:27 +07:00
tusb_error_t hcd_pipe_close ( pipe_handle_t pipe_hdl )
{
2013-12-18 16:23:09 +07:00
ohci_ed_t * const p_ed = ed_from_pipe_handle ( pipe_hdl ) ;
ed_list_remove ( p_ed_head [ ed_get_xfer_type ( p_ed ) ] , p_ed ) ;
return TUSB_ERROR_FAILED ;
2013-12-11 14:31:27 +07:00
}
bool hcd_pipe_is_busy ( pipe_handle_t pipe_hdl )
{
2013-12-18 14:57:34 +07:00
ohci_ed_t const * const p_ed = ed_from_pipe_handle ( pipe_hdl ) ;
2018-08-23 20:09:28 +07:00
return tu_align16 ( p_ed - > td_head . address ) ! = tu_align16 ( p_ed - > td_tail . address ) ;
2013-12-11 14:31:27 +07:00
}
bool hcd_pipe_is_error ( pipe_handle_t pipe_hdl )
{
2013-12-18 14:57:34 +07:00
ohci_ed_t const * const p_ed = ed_from_pipe_handle ( pipe_hdl ) ;
2014-03-04 15:19:50 +07:00
return p_ed - > td_head . halted ;
2013-12-11 14:31:27 +07:00
}
bool hcd_pipe_is_stalled ( pipe_handle_t pipe_hdl )
{
2013-12-18 14:57:34 +07:00
ohci_ed_t const * const p_ed = ed_from_pipe_handle ( pipe_hdl ) ;
2014-03-04 15:19:50 +07:00
return p_ed - > td_head . halted & & p_ed - > is_stalled ;
2013-12-11 14:31:27 +07:00
}
uint8_t hcd_pipe_get_endpoint_addr ( pipe_handle_t pipe_hdl )
{
2013-12-18 14:57:34 +07:00
ohci_ed_t const * const p_ed = ed_from_pipe_handle ( pipe_hdl ) ;
2018-03-11 20:20:59 +07:00
return p_ed - > endpoint_number | ( p_ed - > direction = = OHCI_PID_IN ? TUSB_DIR_IN_MASK : 0 ) ;
2013-12-11 14:31:27 +07:00
}
tusb_error_t hcd_pipe_clear_stall ( pipe_handle_t pipe_hdl )
{
2013-12-18 14:57:34 +07:00
ohci_ed_t * const p_ed = ed_from_pipe_handle ( pipe_hdl ) ;
p_ed - > is_stalled = 0 ;
p_ed - > td_tail . address & = 0x0Ful ; // set tail pointer back to NULL
2014-03-04 15:19:50 +07:00
p_ed - > td_head . toggle = 0 ; // reset data toggle
p_ed - > td_head . halted = 0 ;
2013-12-18 14:57:34 +07:00
if ( TUSB_XFER_BULK = = ed_get_xfer_type ( p_ed ) ) OHCI_REG - > command_status_bit . bulk_list_filled = 1 ;
return TUSB_ERROR_NONE ;
2013-12-11 14:31:27 +07:00
}
//--------------------------------------------------------------------+
// OHCI Interrupt Handler
//--------------------------------------------------------------------+
2013-12-16 12:35:05 +07:00
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_td ;
// make current's item become reverse's first item
td_head - > next_td = ( 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 ) ATTR_CONST ATTR_ALWAYS_INLINE ;
static inline bool gtd_is_control ( ohci_gtd_t const * const p_qtd )
{
return ( ( uint32_t ) p_qtd ) < ( ( uint32_t ) ohci_data . device ) ; // check ohci_data_t for memory layout
}
static inline ohci_ed_t * gtd_get_ed ( ohci_gtd_t const * const p_qtd ) ATTR_PURE ATTR_ALWAYS_INLINE ;
static inline ohci_ed_t * gtd_get_ed ( ohci_gtd_t const * const p_qtd )
{
2013-12-18 14:57:34 +07:00
if ( gtd_is_control ( p_qtd ) )
{
return & ohci_data . control [ p_qtd - > index ] . ed ;
} else
{
uint8_t dev_addr_idx = ( ( ( uint32_t ) p_qtd ) - ( ( uint32_t ) ohci_data . device ) ) / sizeof ( ohci_data . device [ 0 ] ) ;
return & ohci_data . device [ dev_addr_idx ] . ed [ p_qtd - > index ] ;
}
2013-12-16 12:35:05 +07:00
}
static inline uint32_t gtd_xfer_byte_left ( uint32_t buffer_end , uint32_t current_buffer ) ATTR_CONST ATTR_ALWAYS_INLINE ;
static inline uint32_t gtd_xfer_byte_left ( uint32_t buffer_end , uint32_t current_buffer )
{ // 5.2.9 OHCI sample code
2018-08-23 20:09:28 +07:00
return ( tu_align4k ( buffer_end ^ current_buffer ) ? 0x1000 : 0 ) +
tu_offset4k ( buffer_end ) - tu_offset4k ( current_buffer ) + 1 ;
2013-12-16 12:35:05 +07:00
}
2013-12-18 14:57:34 +07:00
static void done_queue_isr ( uint8_t hostid )
2013-12-16 12:35:05 +07:00
{
2018-12-07 18:49:26 +07:00
( void ) hostid ;
2018-04-10 14:31:11 +07:00
uint8_t max_loop = ( CFG_TUSB_HOST_DEVICE_MAX + 1 ) * ( HCD_MAX_XFER + OHCI_MAX_ITD ) ;
2013-12-16 12:35:05 +07:00
// done head is written in reversed order of completion --> need to reverse the done queue first
2018-08-23 20:09:28 +07:00
ohci_td_item_t * td_head = list_reverse ( ( ohci_td_item_t * ) tu_align16 ( ohci_data . hcca . done_head ) ) ;
2013-12-16 12:35:05 +07:00
while ( td_head ! = NULL & & max_loop > 0 )
{
// TODO check if td_head is iso td
//------------- Non ISO transfer -------------//
2014-03-13 18:43:52 +07:00
ohci_gtd_t * const p_qtd = ( ohci_gtd_t * ) td_head ;
2018-11-23 15:17:43 +07:00
xfer_result_t const event = ( p_qtd - > condition_code = = OHCI_CCODE_NO_ERROR ) ? XFER_RESULT_SUCCESS :
( p_qtd - > condition_code = = OHCI_CCODE_STALL ) ? XFER_RESULT_STALLED : XFER_RESULT_FAILED ;
2013-12-16 12:35:05 +07:00
p_qtd - > used = 0 ; // free TD
2018-11-23 15:17:43 +07:00
if ( ( p_qtd - > delay_interrupt = = OHCI_INT_ON_COMPLETE_YES ) | | ( event ! = XFER_RESULT_SUCCESS ) )
2013-12-16 12:35:05 +07:00
{
ohci_ed_t * const p_ed = gtd_get_ed ( p_qtd ) ;
uint32_t const xferred_bytes = p_qtd - > expected_bytes - gtd_xfer_byte_left ( ( uint32_t ) p_qtd - > buffer_end , ( uint32_t ) p_qtd - > current_buffer_pointer ) ;
2013-12-18 14:57:34 +07: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
2018-11-23 15:17:43 +07:00
if ( ( event ! = XFER_RESULT_SUCCESS ) )
2013-12-18 14:57:34 +07:00
{
p_ed - > td_tail . address & = 0x0Ful ;
2018-08-23 20:09:28 +07:00
p_ed - > td_tail . address | = tu_align16 ( p_ed - > td_head . address ) ; // mark halted EP as empty queue
2018-11-23 15:17:43 +07:00
if ( event = = XFER_RESULT_STALLED ) p_ed - > is_stalled = 1 ;
2013-12-18 14:57:34 +07:00
}
2013-12-16 12:35:05 +07:00
pipe_handle_t pipe_hdl =
{
. dev_addr = p_ed - > device_address ,
2013-12-18 14:57:34 +07:00
. xfer_type = ed_get_xfer_type ( p_ed ) ,
2013-12-16 12:35:05 +07:00
} ;
2013-12-18 14:57:34 +07:00
if ( pipe_hdl . xfer_type ! = TUSB_XFER_CONTROL ) pipe_hdl . index = ed_get_index ( p_ed ) ;
usbh_xfer_isr ( pipe_hdl , p_ed - > td_tail . class_code , event , xferred_bytes ) ;
2013-12-16 12:35:05 +07:00
}
td_head = ( ohci_td_item_t * ) td_head - > next_td ;
max_loop - - ;
}
}
2018-03-06 17:38:35 +07:00
void hal_hcd_isr ( uint8_t hostid )
2013-12-11 14:31:27 +07:00
{
2014-03-14 16:11:38 +07:00
uint32_t const int_en = OHCI_REG - > interrupt_enable ;
uint32_t const int_status = OHCI_REG - > interrupt_status & int_en ;
2013-12-13 12:26:37 +07:00
if ( int_status = = 0 ) return ;
2013-12-16 12:35:05 +07:00
//------------- RootHub status -------------//
2013-12-13 12:26:37 +07:00
if ( int_status & OHCI_INT_RHPORT_STATUS_CHANGE_MASK )
{
2013-12-16 12:35:05 +07:00
uint32_t const rhport_status = OHCI_REG - > rhport_status [ 0 ] & OHCI_RHPORT_ALL_CHANGE_MASK ;
2013-12-13 12:26:37 +07:00
// TODO dual port is not yet supported
2013-12-16 12:35:05 +07:00
if ( rhport_status & OHCI_RHPORT_CONNECT_STATUS_CHANGE_MASK )
2013-12-13 12:26:37 +07:00
{
2014-02-20 16:06:31 +07:00
// TODO check if remote wake-up
2013-12-13 12:26:37 +07:00
if ( OHCI_REG - > rhport_status_bit [ 0 ] . current_connect_status )
{
2014-02-20 16:06:31 +07:00
// TODO reset port immediately, without this controller will got 2-3 (debouncing connection status change)
OHCI_REG - > rhport_status [ 0 ] = OHCI_RHPORT_PORT_RESET_STATUS_MASK ;
2013-12-13 12:26:37 +07:00
usbh_hcd_rhport_plugged_isr ( 0 ) ;
} else
{
usbh_hcd_rhport_unplugged_isr ( 0 ) ;
}
}
2013-12-16 12:35:05 +07:00
if ( rhport_status & OHCI_RHPORT_PORT_SUSPEND_CHANGE_MASK )
{
}
OHCI_REG - > rhport_status [ 0 ] = rhport_status ; // acknowledge all interrupt
2013-12-13 12:26:37 +07:00
}
2013-12-16 12:35:05 +07:00
//------------- Transfer Complete -------------//
if ( int_status & OHCI_INT_WRITEBACK_DONEHEAD_MASK )
{
done_queue_isr ( hostid ) ;
}
OHCI_REG - > interrupt_status = int_status ; // Acknowledge handled interrupt
2013-12-11 14:31:27 +07:00
}
//--------------------------------------------------------------------+
// HELPER
//--------------------------------------------------------------------+
# endif