2014-03-12 14:08:52 +07:00
/**************************************************************************/
/*!
@ file dcd_lpc175x_6x . 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 .
*/
/**************************************************************************/
# include "tusb_option.h"
2018-07-23 15:25:45 +07:00
# if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X)
2014-03-12 14:08:52 +07:00
2018-03-28 13:44:39 +07:00
# include "device/dcd.h"
2014-03-12 14:08:52 +07:00
# include "dcd_lpc175x_6x.h"
2018-11-27 20:48:46 +07:00
# include "LPC17xx.h"
2014-03-12 14:08:52 +07:00
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
# define DCD_QHD_MAX 32
# define DCD_QTD_MAX 32 // TODO scale with configure
2018-11-27 20:48:46 +07:00
typedef struct ATTR_ALIGNED ( 4 )
{
//------------- Word 0 -------------//
uint32_t next ;
//------------- Word 1 -------------//
uint16_t mode : 2 ; // either 00 normal or 01 ATLE(auto length extraction)
uint16_t next_valid : 1 ;
uint16_t int_on_complete : 1 ; ///< make use of reserved bit
uint16_t isochronous : 1 ; // is an iso endpoint
uint16_t max_packet_size : 11 ;
volatile uint16_t buffer_length ;
//------------- Word 2 -------------//
volatile uint32_t buffer_addr ;
//------------- Word 3 -------------//
volatile uint16_t retired : 1 ; // initialized to zero
volatile uint16_t status : 4 ;
volatile uint16_t iso_last_packet_valid : 1 ;
volatile uint16_t atle_lsb_extracted : 1 ; // used in ATLE mode
volatile uint16_t atle_msb_extracted : 1 ; // used in ATLE mode
volatile uint16_t atle_message_length_position : 6 ; // used in ATLE mode
uint16_t : 2 ;
volatile uint16_t present_count ; // The number of bytes transferred by the DMA engine. The DMA engine updates this field after completing each packet transfer.
//------------- Word 4 -------------//
// uint32_t iso_packet_size_addr; // iso only, can be omitted for non-iso
} dcd_dma_descriptor_t ;
TU_VERIFY_STATIC ( sizeof ( dcd_dma_descriptor_t ) = = 16 , " size is not correct " ) ; // TODO not support ISO for now
typedef struct
{
// must be 128 byte aligned
volatile dcd_dma_descriptor_t * udca [ DCD_QHD_MAX ] ;
2014-03-12 14:08:52 +07:00
2018-11-27 20:48:46 +07:00
// each endpoints can have up to 2 DD queued at a time
// TODO DMA does not support control transfer (0-1 are not used, offset to reduce memory)
dcd_dma_descriptor_t dd [ DCD_QTD_MAX ] [ 2 ] ;
2014-03-12 14:08:52 +07:00
2018-11-27 20:48:46 +07:00
struct
{
2018-11-27 21:51:02 +07:00
uint8_t * out_buffer ;
uint8_t out_bytes ;
volatile bool out_received ; // indicate if data is already received in endpoint
2018-11-27 20:48:46 +07:00
2018-11-27 21:51:02 +07:00
uint8_t in_bytes ;
} control ;
2014-03-12 14:08:52 +07:00
2018-11-27 20:48:46 +07:00
} dcd_data_t ;
2014-03-12 14:08:52 +07:00
2018-11-27 21:53:36 +07:00
CFG_TUSB_MEM_SECTION ATTR_ALIGNED ( 128 ) static dcd_data_t _dcd ;
2018-11-27 20:48:46 +07:00
//--------------------------------------------------------------------+
// SIE Command
//--------------------------------------------------------------------+
static void sie_cmd_code ( sie_cmdphase_t phase , uint8_t code_data )
{
LPC_USB - > USBDevIntClr = ( DEV_INT_COMMAND_CODE_EMPTY_MASK | DEV_INT_COMMAND_DATA_FULL_MASK ) ;
LPC_USB - > USBCmdCode = ( phase < < 8 ) | ( code_data < < 16 ) ;
uint32_t const wait_flag = ( phase = = SIE_CMDPHASE_READ ) ? DEV_INT_COMMAND_DATA_FULL_MASK : DEV_INT_COMMAND_CODE_EMPTY_MASK ;
# ifndef _TEST_
while ( ( LPC_USB - > USBDevIntSt & wait_flag ) = = 0 ) ; // TODO blocking forever potential
# endif
LPC_USB - > USBDevIntClr = wait_flag ;
}
static void sie_write ( uint8_t cmd_code , uint8_t data_len , uint8_t data )
{
sie_cmd_code ( SIE_CMDPHASE_COMMAND , cmd_code ) ;
if ( data_len )
{
sie_cmd_code ( SIE_CMDPHASE_WRITE , data ) ;
}
}
static uint32_t sie_read ( uint8_t cmd_code , uint8_t data_len )
{
// TODO multiple read
sie_cmd_code ( SIE_CMDPHASE_COMMAND , cmd_code ) ;
sie_cmd_code ( SIE_CMDPHASE_READ , cmd_code ) ;
return LPC_USB - > USBCmdData ;
}
2014-03-12 14:08:52 +07:00
//--------------------------------------------------------------------+
// PIPE HELPER
//--------------------------------------------------------------------+
2018-11-27 20:48:46 +07:00
static inline uint8_t edpt_addr2phy ( uint8_t ep_addr )
2014-03-12 14:08:52 +07:00
{
2018-11-27 20:48:46 +07:00
return 2 * ( ep_addr & 0x0F ) + ( ( ep_addr & TUSB_DIR_IN_MASK ) ? 1 : 0 ) ;
2014-03-12 14:08:52 +07:00
}
static inline void edpt_set_max_packet_size ( uint8_t ep_id , uint16_t max_packet_size )
2018-11-27 20:48:46 +07:00
{
// follows example in 11.10.4.2
2014-03-12 14:08:52 +07:00
LPC_USB - > USBReEp | = BIT_ ( ep_id ) ;
LPC_USB - > USBEpInd = ep_id ; // select index before setting packet size
LPC_USB - > USBMaxPSize = max_packet_size ;
while ( ( LPC_USB - > USBDevIntSt & DEV_INT_ENDPOINT_REALIZED_MASK ) = = 0 ) { } // TODO can be omitted
LPC_USB - > USBDevIntClr = DEV_INT_ENDPOINT_REALIZED_MASK ;
}
2018-11-27 20:48:46 +07:00
2014-03-12 14:08:52 +07:00
//--------------------------------------------------------------------+
2018-11-27 21:51:02 +07:00
// CONTROLLER API
2014-03-12 14:08:52 +07:00
//--------------------------------------------------------------------+
static void bus_reset ( void )
{
// step 7 : slave mode set up
LPC_USB - > USBEpIntClr = 0xFFFFFFFF ; // clear all pending interrupt
LPC_USB - > USBDevIntClr = 0xFFFFFFFF ; // clear all pending interrupt
LPC_USB - > USBEpIntEn = ( uint32_t ) BIN8 ( 11 ) ; // control endpoint cannot use DMA, non-control all use DMA
LPC_USB - > USBEpIntPri = 0 ; // same priority for all endpoint
// step 8 : DMA set up
LPC_USB - > USBEpDMADis = 0xFFFFFFFF ; // firstly disable all dma
LPC_USB - > USBDMARClr = 0xFFFFFFFF ; // clear all pending interrupt
LPC_USB - > USBEoTIntClr = 0xFFFFFFFF ;
LPC_USB - > USBNDDRIntClr = 0xFFFFFFFF ;
LPC_USB - > USBSysErrIntClr = 0xFFFFFFFF ;
2018-11-27 21:53:36 +07:00
tu_memclr ( & _dcd , sizeof ( dcd_data_t ) ) ;
2014-03-12 14:08:52 +07:00
}
2018-03-28 13:47:58 +07:00
bool dcd_init ( uint8_t rhport )
2014-03-12 14:08:52 +07:00
{
2018-03-23 12:17:47 +07:00
( void ) rhport ;
2018-03-02 22:46:36 +07:00
2014-03-12 14:08:52 +07:00
//------------- user manual 11.13 usb device controller initialization -------------// LPC_USB->USBEpInd = 0;
// step 6 : set up control endpoint
2018-04-12 13:23:52 +07:00
edpt_set_max_packet_size ( 0 , CFG_TUD_ENDOINT0_SIZE ) ;
edpt_set_max_packet_size ( 1 , CFG_TUD_ENDOINT0_SIZE ) ;
2014-03-12 14:08:52 +07:00
bus_reset ( ) ;
LPC_USB - > USBDevIntEn = ( DEV_INT_DEVICE_STATUS_MASK | DEV_INT_ENDPOINT_SLOW_MASK | DEV_INT_ERROR_MASK ) ;
2018-11-27 21:53:36 +07:00
LPC_USB - > USBUDCAH = ( uint32_t ) _dcd . udca ;
2014-03-12 14:08:52 +07:00
LPC_USB - > USBDMAIntEn = ( DMA_INT_END_OF_XFER_MASK | DMA_INT_ERROR_MASK ) ;
sie_write ( SIE_CMDCODE_DEVICE_STATUS , 1 , 1 ) ; // connect
2018-03-13 16:33:23 +07:00
NVIC_EnableIRQ ( USB_IRQn ) ;
2014-03-12 14:08:52 +07:00
return TUSB_ERROR_NONE ;
}
2018-03-28 13:47:58 +07:00
void dcd_connect ( uint8_t rhport )
2014-03-12 14:08:52 +07:00
{
2018-03-23 12:17:47 +07:00
( void ) rhport ;
2014-03-12 14:08:52 +07:00
sie_write ( SIE_CMDCODE_DEVICE_STATUS , 1 , 1 ) ;
}
2018-03-28 13:47:58 +07:00
void dcd_set_address ( uint8_t rhport , uint8_t dev_addr )
2014-03-12 14:08:52 +07:00
{
2018-03-23 12:17:47 +07:00
( void ) rhport ;
2014-03-12 14:08:52 +07:00
sie_write ( SIE_CMDCODE_SET_ADDRESS , 1 , 0x80 | dev_addr ) ; // 7th bit is : device_enable
}
2018-03-28 13:47:58 +07:00
void dcd_set_config ( uint8_t rhport , uint8_t config_num )
2014-03-12 14:08:52 +07:00
{
2018-03-23 12:17:47 +07:00
( void ) rhport ;
2018-03-02 22:53:26 +07:00
( void ) config_num ;
2014-03-12 14:08:52 +07:00
sie_write ( SIE_CMDCODE_CONFIGURE_DEVICE , 1 , 1 ) ;
}
//--------------------------------------------------------------------+
2018-11-27 21:51:02 +07:00
// CONTROL HELPER
2014-03-12 14:08:52 +07:00
//--------------------------------------------------------------------+
2018-11-27 21:51:02 +07:00
static inline uint8_t byte2dword ( uint8_t bytes )
2014-03-12 14:08:52 +07:00
{
2018-11-27 21:51:02 +07:00
// length in dwords
return ( bytes + 3 ) / 4 ;
2014-03-12 14:08:52 +07:00
}
2018-11-27 21:51:02 +07:00
static void control_ep_write ( void const * buffer , uint8_t len )
2014-03-12 14:08:52 +07:00
{
2018-11-27 21:51:02 +07:00
uint32_t const * buf32 = ( uint32_t const * ) buffer ;
2014-03-12 14:08:52 +07:00
LPC_USB - > USBCtrl = USBCTRL_WRITE_ENABLE_MASK ; // logical endpoint = 0
2018-11-27 21:51:02 +07:00
LPC_USB - > USBTxPLen = ( uint32_t ) len ;
2014-03-12 14:08:52 +07:00
2018-11-27 21:51:02 +07:00
for ( uint8_t count = 0 ; count < byte2dword ( len ) ; count + + )
2014-03-12 14:08:52 +07:00
{
2018-11-27 21:51:02 +07:00
LPC_USB - > USBTxData = * buf32 ; // NOTE: cortex M3 have no problem with alignment
buf32 + + ;
2014-03-12 14:08:52 +07:00
}
2018-11-27 21:51:02 +07:00
LPC_USB - > USBCtrl = 0 ;
2014-03-12 14:08:52 +07:00
// select control IN & validate the endpoint
sie_write ( SIE_CMDCODE_ENDPOINT_SELECT + 1 , 0 , 0 ) ;
sie_write ( SIE_CMDCODE_BUFFER_VALIDATE , 0 , 0 ) ;
}
2018-11-27 21:51:02 +07:00
static uint8_t control_ep_read ( void * buffer , uint8_t len )
2014-03-12 14:08:52 +07:00
{
LPC_USB - > USBCtrl = USBCTRL_READ_ENABLE_MASK ; // logical endpoint = 0
while ( ( LPC_USB - > USBRxPLen & USBRXPLEN_PACKET_READY_MASK ) = = 0 ) { } // TODO blocking, should have timeout
2018-11-27 21:51:02 +07:00
len = tu_min8 ( len , ( uint8_t ) ( LPC_USB - > USBRxPLen & USBRXPLEN_PACKET_LENGTH_MASK ) ) ;
uint32_t * buf32 = ( uint32_t * ) buffer ;
2018-11-27 20:48:46 +07:00
2018-11-27 21:51:02 +07:00
for ( uint8_t count = 0 ; count < byte2dword ( len ) ; count + + )
2014-03-12 14:08:52 +07:00
{
2018-11-27 21:51:02 +07:00
* buf32 = LPC_USB - > USBRxData ;
buf32 + + ;
2014-03-12 14:08:52 +07:00
}
LPC_USB - > USBCtrl = 0 ;
// select control OUT & clear the endpoint
sie_write ( SIE_CMDCODE_ENDPOINT_SELECT + 0 , 0 , 0 ) ;
sie_write ( SIE_CMDCODE_BUFFER_CLEAR , 0 , 0 ) ;
2018-11-27 21:51:02 +07:00
return len ;
2014-03-12 14:08:52 +07:00
}
//--------------------------------------------------------------------+
2018-11-27 20:48:46 +07:00
// DCD Endpoint Port
2014-03-12 14:08:52 +07:00
//--------------------------------------------------------------------+
2018-11-27 20:48:46 +07:00
bool dcd_edpt_open ( uint8_t rhport , tusb_desc_endpoint_t const * p_endpoint_desc )
2014-03-12 14:08:52 +07:00
{
2018-03-23 12:17:47 +07:00
( void ) rhport ;
2014-03-12 14:08:52 +07:00
// TODO refractor to universal pipe open validation function
2018-11-27 20:48:46 +07:00
// if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) return null_handle; // TODO not support ISO yet
// TU_ASSERT (p_endpoint_desc->wMaxPacketSize.size <= 64, null_handle); // TODO ISO can be 1023, but ISO not supported now
2014-03-12 14:08:52 +07:00
uint8_t ep_id = edpt_addr2phy ( p_endpoint_desc - > bEndpointAddress ) ;
//------------- Realize Endpoint with Max Packet Size -------------//
edpt_set_max_packet_size ( ep_id , p_endpoint_desc - > wMaxPacketSize . size ) ;
//------------- first DD prepare -------------//
2018-11-27 21:53:36 +07:00
dcd_dma_descriptor_t * const p_dd = & _dcd . dd [ ep_id ] [ 0 ] ;
2018-10-23 12:19:32 +07:00
tu_memclr ( p_dd , sizeof ( dcd_dma_descriptor_t ) ) ;
2014-03-12 14:08:52 +07:00
2018-11-27 20:48:46 +07:00
p_dd - > isochronous = ( p_endpoint_desc - > bmAttributes . xfer = = TUSB_XFER_ISOCHRONOUS ) ? 1 : 0 ;
2014-03-12 14:08:52 +07:00
p_dd - > max_packet_size = p_endpoint_desc - > wMaxPacketSize . size ;
2018-11-27 20:48:46 +07:00
p_dd - > retired = 1 ; // inactive at first
2014-03-12 14:08:52 +07:00
2018-11-27 21:53:36 +07:00
_dcd . udca [ ep_id ] = p_dd ; // hook to UDCA
2014-03-12 14:08:52 +07:00
sie_write ( SIE_CMDCODE_ENDPOINT_SET_STATUS + ep_id , 1 , 0 ) ; // clear all endpoint status
2018-11-27 20:48:46 +07:00
return true ;
2014-03-12 14:08:52 +07:00
}
2018-11-27 20:48:46 +07:00
bool dcd_edpt_busy ( uint8_t rhport , uint8_t ep_addr )
2014-03-12 14:08:52 +07:00
{
2018-11-27 20:48:46 +07:00
( void ) rhport ;
uint8_t ep_id = edpt_addr2phy ( ep_addr ) ;
2018-11-27 21:53:36 +07:00
return ( _dcd . udca [ ep_id ] ! = NULL & & ! _dcd . udca [ ep_id ] - > retired ) ;
2014-03-12 14:08:52 +07:00
}
2018-11-27 20:48:46 +07:00
void dcd_edpt_stall ( uint8_t rhport , uint8_t ep_addr )
2014-03-12 14:08:52 +07:00
{
2018-11-27 20:48:46 +07:00
( void ) rhport ;
2018-11-27 21:51:02 +07:00
if ( edpt_number ( ep_addr ) = = 0 )
2018-11-27 20:48:46 +07:00
{
sie_write ( SIE_CMDCODE_ENDPOINT_SET_STATUS + 0 , 1 , SIE_SET_ENDPOINT_STALLED_MASK | SIE_SET_ENDPOINT_CONDITION_STALLED_MASK ) ;
} else
{
uint8_t ep_id = edpt_addr2phy ( ep_addr ) ;
sie_write ( SIE_CMDCODE_ENDPOINT_SET_STATUS + ep_id , 1 , SIE_SET_ENDPOINT_STALLED_MASK ) ;
}
2014-03-12 14:08:52 +07:00
}
2018-03-28 13:47:58 +07:00
void dcd_edpt_clear_stall ( uint8_t rhport , uint8_t ep_addr )
2014-03-12 14:08:52 +07:00
{
2018-11-27 20:48:46 +07:00
uint8_t ep_id = edpt_addr2phy ( ep_addr ) ;
2014-03-12 14:08:52 +07:00
sie_write ( SIE_CMDCODE_ENDPOINT_SET_STATUS + ep_id , 1 , 0 ) ;
}
2018-11-27 20:48:46 +07:00
bool dcd_edpt_stalled ( uint8_t rhport , uint8_t ep_addr )
{
// TODO implement later
return false ;
}
2014-03-12 14:08:52 +07:00
void dd_xfer_init ( dcd_dma_descriptor_t * p_dd , void * buffer , uint16_t total_bytes )
{
p_dd - > next = 0 ;
2018-11-27 20:48:46 +07:00
p_dd - > next_valid = 0 ;
2014-03-12 14:08:52 +07:00
p_dd - > buffer_addr = ( uint32_t ) buffer ;
p_dd - > buffer_length = total_bytes ;
p_dd - > status = DD_STATUS_NOT_SERVICED ;
p_dd - > iso_last_packet_valid = 0 ;
p_dd - > present_count = 0 ;
}
2018-11-27 20:48:46 +07:00
//tusb_error_t dcd_edpt_queue_xfer(edpt_hdl_t edpt_hdl, uint8_t * buffer, uint16_t total_bytes)
//{ // NOTE for sure the qhd has no dds
// dcd_dma_descriptor_t* const p_fixed_dd = &dcd_data.dd[edpt_hdl.index][0]; // always queue with the fixed DD
//
// dd_xfer_init(p_fixed_dd, buffer, total_bytes);
// p_fixed_dd->is_retired = 1;
// p_fixed_dd->int_on_complete = 0;
//
// return TUSB_ERROR_NONE;
//}
2018-11-27 21:51:02 +07:00
static bool control_xact ( uint8_t rhport , uint8_t dir , uint8_t * buffer , uint8_t len )
2018-11-27 20:48:46 +07:00
{
( void ) rhport ;
if ( dir )
{
2018-11-27 21:53:36 +07:00
_dcd . control . in_bytes = len ;
2018-11-27 21:51:02 +07:00
control_ep_write ( buffer , len ) ;
2018-11-27 20:48:46 +07:00
} else
{
2018-11-27 21:53:36 +07:00
if ( _dcd . control . out_received )
2018-11-27 21:51:02 +07:00
{
// Already received the DATA OUT packet
2018-11-27 21:53:36 +07:00
_dcd . control . out_received = false ;
_dcd . control . out_buffer = NULL ;
_dcd . control . out_bytes = 0 ;
2014-03-12 14:08:52 +07:00
2018-11-27 21:51:02 +07:00
uint8_t received = control_ep_read ( buffer , len ) ;
dcd_event_xfer_complete ( 0 , 0 , received , XFER_RESULT_SUCCESS , true ) ;
} else
{
2018-11-27 21:53:36 +07:00
_dcd . control . out_buffer = buffer ;
_dcd . control . out_bytes = len ;
2018-11-27 21:51:02 +07:00
}
2018-11-27 20:48:46 +07:00
}
2014-03-12 14:08:52 +07:00
2018-11-27 20:48:46 +07:00
return true ;
2014-03-12 14:08:52 +07:00
}
2018-11-27 20:48:46 +07:00
bool dcd_edpt_xfer ( uint8_t rhport , uint8_t ep_addr , uint8_t * buffer , uint16_t total_bytes )
2014-03-12 14:08:52 +07:00
{
2018-11-27 21:51:02 +07:00
uint8_t const epnum = edpt_number ( ep_addr ) ;
uint8_t const dir = edpt_dir ( ep_addr ) ;
2018-11-27 20:48:46 +07:00
// Control transfer is not DMA support, and must be done in slave mode
if ( epnum = = 0 )
{
2018-11-27 21:51:02 +07:00
return control_xact ( rhport , dir , buffer , ( uint8_t ) total_bytes ) ;
2018-11-27 20:48:46 +07:00
}
uint8_t ep_id = edpt_addr2phy ( ep_addr ) ;
2018-11-27 21:53:36 +07:00
dcd_dma_descriptor_t * const p_first_dd = & _dcd . dd [ ep_id ] [ 0 ] ;
2014-03-12 14:08:52 +07:00
//------------- fixed DD is already queued a xfer -------------//
if ( p_first_dd - > buffer_length )
{
// setup new dd
2018-11-27 21:53:36 +07:00
dcd_dma_descriptor_t * const p_dd = & _dcd . dd [ ep_id ] [ 1 ] ;
2018-10-23 12:19:32 +07:00
tu_memclr ( p_dd , sizeof ( dcd_dma_descriptor_t ) ) ;
2014-03-12 14:08:52 +07:00
dd_xfer_init ( p_dd , buffer , total_bytes ) ;
2018-11-27 21:51:02 +07:00
p_dd - > max_packet_size = p_first_dd - > max_packet_size ;
2018-11-27 20:48:46 +07:00
p_dd - > isochronous = p_first_dd - > isochronous ;
2018-11-27 21:51:02 +07:00
p_dd - > int_on_complete = true ;
2014-03-12 14:08:52 +07:00
// hook to fixed dd
2018-11-27 21:51:02 +07:00
p_first_dd - > next = ( uint32_t ) p_dd ;
2018-11-27 20:48:46 +07:00
p_first_dd - > next_valid = 1 ;
2014-03-12 14:08:52 +07:00
}
//------------- fixed DD is free -------------//
else
{
dd_xfer_init ( p_first_dd , buffer , total_bytes ) ;
2018-11-27 20:48:46 +07:00
p_first_dd - > int_on_complete = true ;
2014-03-12 14:08:52 +07:00
}
2018-11-27 20:48:46 +07:00
p_first_dd - > retired = 0 ; // activate xfer
2018-11-27 21:53:36 +07:00
_dcd . udca [ ep_id ] = p_first_dd ;
2018-11-27 20:48:46 +07:00
LPC_USB - > USBEpDMAEn = BIT_ ( ep_id ) ;
2014-03-12 14:08:52 +07:00
2018-11-27 20:48:46 +07:00
if ( ep_id % 2 )
{
// endpoint IN need to actively raise DMA request
LPC_USB - > USBDMARSet = BIT_ ( ep_id ) ;
2014-03-12 14:08:52 +07:00
}
2018-11-27 20:48:46 +07:00
return true ;
}
//--------------------------------------------------------------------+
// ISR
//--------------------------------------------------------------------+
static void endpoint_non_control_isr ( uint32_t eot_int )
{
for ( uint8_t ep_id = 2 ; ep_id < DCD_QHD_MAX ; ep_id + + )
{
if ( BIT_TEST_ ( eot_int , ep_id ) )
{
2018-11-27 21:53:36 +07:00
dcd_dma_descriptor_t * const p_first_dd = & _dcd . dd [ ep_id ] [ 0 ] ;
dcd_dma_descriptor_t * const p_last_dd = _dcd . dd [ ep_id ] + ( p_first_dd - > next_valid ? 1 : 0 ) ; // Maximum is 2 QTD are queued in an endpoint
2018-11-27 20:48:46 +07:00
// only handle when Controller already finished the last DD
2018-11-27 21:53:36 +07:00
if ( _dcd . udca [ ep_id ] = = p_last_dd )
2018-11-27 20:48:46 +07:00
{
2018-11-27 21:53:36 +07:00
_dcd . udca [ ep_id ] = p_first_dd ; // UDCA currently points to the last DD, change to the fixed DD
2018-11-27 20:48:46 +07:00
p_first_dd - > buffer_length = 0 ; // buffer length is used to determined if first dd is queued in pipe xfer function
if ( p_last_dd - > int_on_complete )
{
uint8_t result = ( p_last_dd - > status = = DD_STATUS_NORMAL | | p_last_dd - > status = = DD_STATUS_DATA_UNDERUN ) ? XFER_RESULT_SUCCESS : XFER_RESULT_FAILED ;
// report only xferred bytes in the IOC qtd
uint8_t const ep_addr = ( ep_id / 2 ) | ( ( ep_id & 0x01 ) ? TUSB_DIR_IN_MASK : 0 ) ;
dcd_event_xfer_complete ( 0 , ep_addr , p_last_dd - > present_count , result , true ) ;
}
}
}
}
}
static void endpoint_control_isr ( void )
{
uint32_t const interrupt_enable = LPC_USB - > USBEpIntEn ;
uint32_t const endpoint_int_status = LPC_USB - > USBEpIntSt & interrupt_enable ;
// LPC_USB->USBEpIntClr = endpoint_int_status; // acknowledge interrupt TODO cannot immediately acknowledge setup packet
dcd_event_t event = { . rhport = 0 } ;
//------------- Setup Received-------------//
if ( ( endpoint_int_status & BIT_ ( 0 ) ) & &
( sie_read ( SIE_CMDCODE_ENDPOINT_SELECT + 0 , 1 ) & SIE_SELECT_ENDPOINT_SETUP_RECEIVED_MASK ) )
{
( void ) sie_read ( SIE_CMDCODE_ENDPOINT_SELECT_CLEAR_INTERRUPT + 0 , 1 ) ; // clear setup bit
uint8_t setup_packet [ 8 ] ;
2018-11-27 21:51:02 +07:00
control_ep_read ( setup_packet , 8 ) ; // TODO read before clear setup above
2018-11-27 20:48:46 +07:00
dcd_event_setup_received ( 0 , setup_packet , true ) ;
}
else if ( endpoint_int_status & 0x03 )
{
2018-11-27 21:51:02 +07:00
// Control out complete
if ( endpoint_int_status & BIT_ ( 0 ) )
2018-11-27 20:48:46 +07:00
{
2018-11-27 21:53:36 +07:00
if ( _dcd . control . out_buffer )
2018-11-27 21:51:02 +07:00
{
// software queued transfer previously
2018-11-27 21:53:36 +07:00
uint8_t received = control_ep_read ( _dcd . control . out_buffer , _dcd . control . out_bytes ) ;
2018-11-27 21:51:02 +07:00
2018-11-27 21:53:36 +07:00
_dcd . control . out_buffer = NULL ;
_dcd . control . out_bytes = 0 ;
2018-11-27 21:51:02 +07:00
dcd_event_xfer_complete ( 0 , 0 , received , XFER_RESULT_SUCCESS , true ) ;
} else
{
// mark as received
2018-11-27 21:53:36 +07:00
_dcd . control . out_received = true ;
2018-11-27 21:51:02 +07:00
}
}
// Control In complete
if ( endpoint_int_status & BIT_ ( 1 ) )
2018-11-27 20:48:46 +07:00
{
2018-11-27 21:53:36 +07:00
dcd_event_xfer_complete ( 0 , TUSB_DIR_IN_MASK , _dcd . control . in_bytes , XFER_RESULT_SUCCESS , true ) ;
2018-11-27 20:48:46 +07:00
}
}
LPC_USB - > USBEpIntClr = endpoint_int_status ; // acknowledge interrupt TODO cannot immediately acknowledge setup packet
}
void hal_dcd_isr ( uint8_t rhport )
{
( void ) rhport ;
uint32_t const device_int_enable = LPC_USB - > USBDevIntEn ;
uint32_t const device_int_status = LPC_USB - > USBDevIntSt & device_int_enable ;
LPC_USB - > USBDevIntClr = device_int_status ; // Acknowledge handled interrupt
//------------- usb bus event -------------//
if ( device_int_status & DEV_INT_DEVICE_STATUS_MASK )
{
uint8_t const dev_status_reg = sie_read ( SIE_CMDCODE_DEVICE_STATUS , 1 ) ;
if ( dev_status_reg & SIE_DEV_STATUS_RESET_MASK )
{
bus_reset ( ) ;
dcd_event_bus_signal ( rhport , DCD_EVENT_BUS_RESET , true ) ;
}
if ( dev_status_reg & SIE_DEV_STATUS_CONNECT_CHANGE_MASK )
{ // device is disconnected, require using VBUS (P1_30)
dcd_event_bus_signal ( rhport , DCD_EVENT_UNPLUGGED , true ) ;
}
if ( dev_status_reg & SIE_DEV_STATUS_SUSPEND_CHANGE_MASK )
{
if ( dev_status_reg & SIE_DEV_STATUS_SUSPEND_MASK )
{
dcd_event_bus_signal ( rhport , DCD_EVENT_SUSPENDED , true ) ;
}
// else
// { // resume signal
// dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
// }
// }
}
}
//------------- Control Endpoint (Slave Mode) -------------//
if ( device_int_status & DEV_INT_ENDPOINT_SLOW_MASK )
{
endpoint_control_isr ( ) ;
}
//------------- Non-Control Endpoint (DMA Mode) -------------//
uint32_t const dma_int_enable = LPC_USB - > USBDMAIntEn ;
uint32_t const dma_int_status = LPC_USB - > USBDMAIntSt & dma_int_enable ;
if ( dma_int_status & DMA_INT_END_OF_XFER_MASK )
{
uint32_t eot_int = LPC_USB - > USBEoTIntSt ;
LPC_USB - > USBEoTIntClr = eot_int ; // acknowledge interrupt source
endpoint_non_control_isr ( eot_int ) ;
}
if ( device_int_status & DEV_INT_ERROR_MASK | | dma_int_status & DMA_INT_ERROR_MASK )
{
uint32_t error_status = sie_read ( SIE_CMDCODE_READ_ERROR_STATUS , 1 ) ;
( void ) error_status ;
// TU_ASSERT(false, (void) 0);
}
2014-03-12 14:08:52 +07:00
}
# endif