implement msc device class

usbd auto stall control for not supported return from class control request
usbd implement xfer isr callback mechanism
DCD
- implement dcd multiple qtd support
- dcd dcd_pipe_stall
- implement dcd_pipe_queue_xfer
- xfer_complete_isr
- flush control endpoint if received new setup while previous transfer is not complete
change msc_cmd_block_wrapper_t flags field to dir
force full speed for easy testing

NOTEs: somehow unable to get endpoint IN interrupt with ioc
This commit is contained in:
hathach
2013-11-01 12:11:26 +07:00
parent c760c69d51
commit 3a54ad4c0d
28 changed files with 821 additions and 243 deletions

View File

@@ -57,102 +57,10 @@
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
#define DCD_QHD_MAX 12
#define QTD_INVALID 0x01
#define CONTROL_ENDOINT_SIZE 64
#define DCD_QTD_MAX 12
#define DCD_QTD_PER_QHD_MAX 2 // maximum number of qtd that are linked into one queue head at a time
typedef struct {
// Word 0: Next QTD Pointer
uint32_t next; ///< Next link pointer This field contains the physical memory address of the next dTD to be processed
// Word 1: qTQ Token
uint32_t : 3 ;
volatile uint32_t xact_err : 1 ;
uint32_t : 1 ;
volatile uint32_t buffer_err : 1 ;
volatile uint32_t halted : 1 ;
volatile uint32_t active : 1 ;
uint32_t : 2 ;
uint32_t iso_mult_override : 2 ; ///< This field can be used for transmit ISOs to override the MULT field in the dQH. This field must be zero for all packet types that are not transmit-ISO.
uint32_t : 3 ;
uint32_t int_on_complete : 1 ;
volatile uint32_t total_bytes : 15 ;
uint32_t : 0 ;
// Word 2-6: Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page
uint32_t buffer[5]; ///< buffer1 has frame_n for TODO Isochronous
//------------- -------------//
uint32_t reserved;
} dcd_qtd_t;
STATIC_ASSERT( sizeof(dcd_qtd_t) == 32, "size is not correct");
typedef struct {
// Word 0: Capabilities and Characteristics
uint32_t : 15 ; ///< Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated by the USB variable length protocol where N is computed using Max_packet_length and the Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 - Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed.
uint32_t int_on_setup : 1 ; ///< Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is set in response to a setup being received.
uint32_t max_package_size : 11 ; ///< This directly corresponds to the maximum packet size of the associated endpoint (wMaxPacketSize)
uint32_t : 2 ;
uint32_t zero_length_termination : 1 ; ///< This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on transfers that are equal in length to a multiple Max_packet_length.
uint32_t iso_mult : 2 ; ///<
uint32_t : 0 ;
// Word 1: Current qTD Pointer
volatile uint32_t qtd_addr;
// Word 2-9: Transfer Overlay
volatile dcd_qtd_t qtd_overlay;
// Word 10-11: Setup request (control OUT only)
volatile tusb_control_request_t setup_request;
//--------------------------------------------------------------------+
/// Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes
/// thus there are 16 bytes padding free that we can make use of.
//--------------------------------------------------------------------+
uint8_t reserved[16];
} ATTR_ALIGNED(64) dcd_qhd_t;
STATIC_ASSERT( sizeof(dcd_qhd_t) == 64, "size is not correct");
typedef struct {
dcd_qhd_t qhd[DCD_QHD_MAX]; ///< Must be at 2K alignment
dcd_qtd_t qtd[DCD_QHD_MAX] ATTR_ALIGNED(32);
}dcd_data_t;
ATTR_ALIGNED(2048) dcd_data_t dcd_data TUSB_CFG_ATTR_USBRAM;
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
//--------------------------------------------------------------------+
// USBD-DCD API
//--------------------------------------------------------------------+
tusb_error_t dcd_controller_reset(uint8_t coreid)
{
volatile uint32_t * p_reg_usbcmd;
p_reg_usbcmd = (coreid ? &LPC_USB1->USBCMD_D : &LPC_USB0->USBCMD_D);
// NXP chip powered with non-host mode --> sts bit is not correctly reflected
(*p_reg_usbcmd) |= BIT_(1); // TODO refractor reset controller
// timeout_timer_t timeout;
// timeout_set(&timeout, 2); // should not take longer the time to stop controller
while( ((*p_reg_usbcmd) & BIT_(1)) /*&& !timeout_expired(&timeout)*/) {}
//
// return timeout_expired(&timeout) ? TUSB_ERROR_OSAL_TIMEOUT : TUSB_ERROR_NONE;
return TUSB_ERROR_NONE;
}
void dcd_controller_connect(uint8_t coreid)
{
volatile uint32_t * p_reg_usbcmd = (coreid ? &LPC_USB1->USBCMD_D : &LPC_USB0->USBCMD_D);
(*p_reg_usbcmd) |= BIT_(0);
}
#define QTD_NEXT_INVALID 0x01
/*---------- ENDPTCTRL ----------*/
enum {
@@ -182,9 +90,105 @@ enum {
INT_MASK_NAK = BIT_(16)
};
typedef struct {
// Word 0: Next QTD Pointer
uint32_t next; ///< Next link pointer This field contains the physical memory address of the next dTD to be processed
// Word 1: qTQ Token
uint32_t : 3 ;
volatile uint32_t xact_err : 1 ;
uint32_t : 1 ;
volatile uint32_t buffer_err : 1 ;
volatile uint32_t halted : 1 ;
volatile uint32_t active : 1 ;
uint32_t : 2 ;
uint32_t iso_mult_override : 2 ; ///< This field can be used for transmit ISOs to override the MULT field in the dQH. This field must be zero for all packet types that are not transmit-ISO.
uint32_t : 3 ;
uint32_t int_on_complete : 1 ;
volatile uint32_t total_bytes : 15 ;
uint32_t : 0 ;
// Word 2-6: Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page
uint32_t buffer[5]; ///< buffer1 has frame_n for TODO Isochronous
//------------- DCD Area -------------//
uint16_t expected_bytes;
uint8_t used;
uint8_t reserved;
} dcd_qtd_t;
STATIC_ASSERT( sizeof(dcd_qtd_t) == 32, "size is not correct");
typedef struct {
// Word 0: Capabilities and Characteristics
uint32_t : 15 ; ///< Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated by the USB variable length protocol where N is computed using Max_packet_length and the Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 - Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed.
uint32_t int_on_setup : 1 ; ///< Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is set in response to a setup being received.
uint32_t max_package_size : 11 ; ///< This directly corresponds to the maximum packet size of the associated endpoint (wMaxPacketSize)
uint32_t : 2 ;
uint32_t zero_length_termination : 1 ; ///< This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on transfers that are equal in length to a multiple Max_packet_length.
uint32_t iso_mult : 2 ; ///<
uint32_t : 0 ;
// Word 1: Current qTD Pointer
volatile uint32_t qtd_addr;
// Word 2-9: Transfer Overlay
volatile dcd_qtd_t qtd_overlay;
// Word 10-11: Setup request (control OUT only)
volatile tusb_control_request_t setup_request;
//--------------------------------------------------------------------+
/// Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes
/// thus there are 16 bytes padding free that we can make use of.
//--------------------------------------------------------------------+
uint8_t class_code; // Class code that endpoint belongs to
uint8_t xfer_type;
uint8_t list_qtd_idx[DCD_QTD_PER_QHD_MAX];
uint8_t reserved[14-DCD_QTD_PER_QHD_MAX];
} ATTR_ALIGNED(64) dcd_qhd_t;
STATIC_ASSERT( sizeof(dcd_qhd_t) == 64, "size is not correct");
typedef struct {
dcd_qhd_t qhd[DCD_QHD_MAX]; ///< Must be at 2K alignment
dcd_qtd_t qtd[DCD_QTD_MAX] ATTR_ALIGNED(32);
}dcd_data_t;
ATTR_ALIGNED(2048) dcd_data_t dcd_data TUSB_CFG_ATTR_USBRAM;
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
//--------------------------------------------------------------------+
// CONTROLLER API
//--------------------------------------------------------------------+
tusb_error_t dcd_controller_reset(uint8_t coreid)
{
volatile uint32_t * p_reg_usbcmd;
p_reg_usbcmd = (coreid ? &LPC_USB1->USBCMD_D : &LPC_USB0->USBCMD_D);
// NXP chip powered with non-host mode --> sts bit is not correctly reflected
(*p_reg_usbcmd) |= BIT_(1); // TODO refractor reset controller
// timeout_timer_t timeout;
// timeout_set(&timeout, 2); // should not take longer the time to stop controller
while( ((*p_reg_usbcmd) & BIT_(1)) /*&& !timeout_expired(&timeout)*/) {}
//
// return timeout_expired(&timeout) ? TUSB_ERROR_OSAL_TIMEOUT : TUSB_ERROR_NONE;
return TUSB_ERROR_NONE;
}
void dcd_controller_connect(uint8_t coreid)
{
volatile uint32_t * p_reg_usbcmd = (coreid ? &LPC_USB1->USBCMD_D : &LPC_USB0->USBCMD_D);
(*p_reg_usbcmd) |= BIT_(0);
}
void dcd_controller_set_address(uint8_t coreid, uint8_t dev_addr)
{
LPC_USB0->DEVICEADDR = (dev_addr << 25) | BIT_(24);
@@ -226,8 +230,8 @@ void bus_reset(uint8_t coreid)
//------------- Set up Control Endpoints (0 OUT, 1 IN) -------------//
dcd_data.qhd[0].zero_length_termination = dcd_data.qhd[1].zero_length_termination = 1;
dcd_data.qhd[0].max_package_size = dcd_data.qhd[1].max_package_size = CONTROL_ENDOINT_SIZE;
dcd_data.qhd[0].qtd_overlay.next = dcd_data.qhd[1].qtd_overlay.next = QTD_INVALID;
dcd_data.qhd[0].max_package_size = dcd_data.qhd[1].max_package_size = TUSB_CFG_DEVICE_CONTROL_ENDOINT_SIZE;
dcd_data.qhd[0].qtd_overlay.next = dcd_data.qhd[1].qtd_overlay.next = QTD_NEXT_INVALID;
dcd_data.qhd[0].int_on_setup = 1; // OUT only
}
@@ -247,39 +251,41 @@ tusb_error_t dcd_init(void)
}
//--------------------------------------------------------------------+
// PIPE API
// PIPE HELPER
//--------------------------------------------------------------------+
static inline uint8_t endpoint_to_pos(uint8_t logical_endpoint, tusb_direction_t dir) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint8_t endpoint_to_pos(uint8_t logical_endpoint, tusb_direction_t dir)
{
return logical_endpoint + (dir == TUSB_DIR_HOST_TO_DEV ? 0 : 16);
static inline uint8_t edpt_pos2phy(uint8_t pos) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint8_t edpt_pos2phy(uint8_t pos)
{ // 0-5 --> OUT, 16-21 IN
return (pos < DCD_QHD_MAX/2) ? (2*pos) : (2*(pos-16)+1);
}
static inline uint8_t endpoint_phy2pos(uint8_t physical_endpoint) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint8_t endpoint_phy2pos(uint8_t physical_endpoint)
static inline uint8_t edpt_phy2pos(uint8_t physical_endpoint) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint8_t edpt_phy2pos(uint8_t physical_endpoint)
{
return physical_endpoint/2 + ( (physical_endpoint%2) ? 16 : 0);
}
static inline uint8_t endpoint_log2phy(uint8_t logical_endpoint, tusb_direction_t dir) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint8_t endpoint_log2phy(uint8_t logical_endpoint, tusb_direction_t dir)
{
return 2*logical_endpoint + (dir == TUSB_DIR_DEV_TO_HOST ? 1 : 0);
}
static inline uint8_t endpoint_addr2phy(uint8_t endpoint_addr) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint8_t endpoint_addr2phy(uint8_t endpoint_addr)
static inline uint8_t edpt_addr2phy(uint8_t endpoint_addr) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint8_t edpt_addr2phy(uint8_t endpoint_addr)
{
return 2*(endpoint_addr & 0x0F) + ((endpoint_addr & TUSB_DIR_DEV_TO_HOST_MASK) ? 1 : 0);
}
static inline uint8_t edpt_phy2log(uint8_t physical_endpoint) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint8_t edpt_phy2log(uint8_t physical_endpoint)
{
return physical_endpoint/2;
}
static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes)
{
memclr_(p_qtd, sizeof(dcd_qtd_t));
p_qtd->next = QTD_INVALID;
p_qtd->used = 1;
p_qtd->next = QTD_NEXT_INVALID;
p_qtd->active = 1;
p_qtd->total_bytes = total_bytes;
p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes;
if (data_ptr != NULL)
{
@@ -291,23 +297,39 @@ static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes)
}
}
// retval 0: invalid
static inline uint8_t qtd_find_free(uint8_t coreid) ATTR_PURE ATTR_ALWAYS_INLINE;
static inline uint8_t qtd_find_free(uint8_t coreid)
{
for(uint8_t i=2; i<DCD_QTD_MAX; i++)
{ // exclude control's qtd
if ( dcd_data.qtd[i].used == 0) return i;
}
return 0;
}
//--------------------------------------------------------------------+
// CONTROL PIPE API
//--------------------------------------------------------------------+
void dcd_pipe_control_stall(uint8_t coreid)
{
LPC_USB0->ENDPTCTRL0 |= (ENDPTCTRL_MASK_STALL << 16); // stall Control IN
}
// control transfer does not need to use qtd find function
tusb_error_t dcd_pipe_control_xfer(uint8_t coreid, tusb_direction_t dir, void * buffer, uint16_t length)
{
uint8_t const endpoint_data = (dir == TUSB_DIR_DEV_TO_HOST) ? 1 : 0; // IN xfer --> data phase on Control IN, other Control OUT
ASSERT_FALSE(dcd_data.qhd[0].qtd_overlay.active || dcd_data.qhd[1].qtd_overlay.active, TUSB_ERROR_FAILED);
//------------- Data Phase -------------//
if (length)
{
dcd_qtd_t* p_data = &dcd_data.qtd[0];
qtd_init(p_data, buffer, length);
dcd_data.qhd[endpoint_data].qtd_overlay.next = (uint32_t) p_data;
LPC_USB0->ENDPTPRIME |= BIT_( endpoint_phy2pos(endpoint_data) );
}
//------------- Status Phase (other endpoint, opposite direction) -------------//
@@ -315,12 +337,26 @@ tusb_error_t dcd_pipe_control_xfer(uint8_t coreid, tusb_direction_t dir, void *
qtd_init(p_status, NULL, 0); // zero length xfer
dcd_data.qhd[1 - endpoint_data].qtd_overlay.next = (uint32_t) p_status;
LPC_USB0->ENDPTPRIME |= BIT_( endpoint_phy2pos(1 - endpoint_data) );
//------------- Prime Endpoint -------------//
LPC_USB0->ENDPTPRIME |= BIT_( edpt_phy2pos(1 - endpoint_data) ) |
(length ? BIT_( edpt_phy2pos(endpoint_data) ) : 0) ;
return TUSB_ERROR_NONE;
}
endpoint_handle_t dcd_pipe_open(uint8_t coreid, tusb_descriptor_endpoint_t const * p_endpoint_desc)
//--------------------------------------------------------------------+
// BULK/INTERRUPT/ISOCHRONOUS PIPE API
//--------------------------------------------------------------------+
tusb_error_t dcd_pipe_stall(endpoint_handle_t edpt_hdl)
{
volatile uint32_t * reg_control = (&LPC_USB0->ENDPTCTRL0) + edpt_phy2log(edpt_hdl.index);
(*reg_control) |= ENDPTCTRL_MASK_STALL << (edpt_hdl.index & 0x01 ? 16 : 0);
return TUSB_ERROR_NONE;
}
endpoint_handle_t dcd_pipe_open(uint8_t coreid, tusb_descriptor_endpoint_t const * p_endpoint_desc, uint8_t class_code)
{
// TODO USB1 only has 4 non-control enpoint (USB0 has 5)
endpoint_handle_t const null_handle = { .coreid = 0, .xfer_type = 0, .index = 0 };
@@ -336,53 +372,123 @@ endpoint_handle_t dcd_pipe_open(uint8_t coreid, tusb_descriptor_endpoint_t const
ASSERT_FALSE( (*reg_control) & (ENDPTCTRL_MASK_ENABLE << (dir ? 16 : 0)), null_handle ); // endpoint must not be already enabled
//------------- Prepare Queue Head -------------//
uint8_t ep_idx = endpoint_addr2phy(p_endpoint_desc->bEndpointAddress);
uint8_t ep_idx = edpt_addr2phy(p_endpoint_desc->bEndpointAddress);
dcd_qhd_t * p_qhd = &dcd_data.qhd[ep_idx];
memclr_(p_qhd, sizeof(dcd_qhd_t));
p_qhd->class_code = class_code;
p_qhd->xfer_type = p_endpoint_desc->bmAttributes.xfer;
p_qhd->zero_length_termination = 1;
p_qhd->max_package_size = p_endpoint_desc->wMaxPacketSize.size;
p_qhd->qtd_overlay.next = QTD_INVALID;
p_qhd->max_package_size = p_endpoint_desc->wMaxPacketSize.size;
p_qhd->qtd_overlay.next = QTD_NEXT_INVALID;
(*reg_control) |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_MASK_ENABLE | ENDPTCTRL_MASK_TOGGLE_RESET) << (dir ? 16 : 0);
return (endpoint_handle_t) { .coreid = coreid, .xfer_type = p_endpoint_desc->bmAttributes.xfer, .index = ep_idx };
}
STATIC_ INLINE_ dcd_qhd_t* qhd_get_from_endpoint_handle(endpoint_handle_t edpt_hdl) ATTR_PURE ATTR_ALWAYS_INLINE;
STATIC_ INLINE_ dcd_qhd_t* qhd_get_from_endpoint_handle(endpoint_handle_t edpt_hdl)
{
return &dcd_data.qhd[edpt_hdl.index];
return (endpoint_handle_t) { .coreid = coreid, .xfer_type = p_endpoint_desc->bmAttributes.xfer, .index = ep_idx, .class_code = class_code};
}
bool dcd_pipe_is_busy(endpoint_handle_t edpt_hdl)
{
dcd_qhd_t* p_qhd = qhd_get_from_endpoint_handle(edpt_hdl);
dcd_qhd_t* p_qhd = &dcd_data.qhd[edpt_hdl.index];
// LPC_USB0->ENDPTSTAT & endpoint_phy2pos(edpt_hdl.index)
return !p_qhd->qtd_overlay.halted && p_qhd->qtd_overlay.active;
}
tusb_error_t dcd_pipe_xfer(endpoint_handle_t edpt_hdl, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete)
// add only, controller virtually cannot know
static tusb_error_t pipe_add_xfer(endpoint_handle_t edpt_hdl, void * buffer, uint16_t total_bytes, bool int_on_complete)
{
dcd_qhd_t* p_qhd = qhd_get_from_endpoint_handle(edpt_hdl);
dcd_qtd_t* p_qtd = &dcd_data.qtd[edpt_hdl.index]; // TODO allocate qtd
ASSERT(edpt_hdl.xfer_type != TUSB_XFER_ISOCHRONOUS, TUSB_ERROR_NOT_SUPPORTED_YET);
uint8_t qtd_idx = qtd_find_free(edpt_hdl.coreid);
ASSERT(qtd_idx != 0, TUSB_ERROR_DCD_NOT_ENOUGH_QTD);
dcd_qhd_t* p_qhd = &dcd_data.qhd[edpt_hdl.index];
dcd_qtd_t* p_qtd = &dcd_data.qtd[qtd_idx];
//------------- Find free slot in qhd's array list -------------//
uint8_t free_slot;
for(free_slot=0; free_slot < DCD_QTD_PER_QHD_MAX; free_slot++)
{
if ( p_qhd->list_qtd_idx[free_slot] == 0 ) break; // found free slot
}
ASSERT(free_slot < DCD_QTD_PER_QHD_MAX, TUSB_ERROR_DCD_NOT_ENOUGH_QTD);
p_qhd->list_qtd_idx[free_slot] = qtd_idx; // add new qtd to qhd's array list
//------------- Prepare qtd -------------//
qtd_init(p_qtd, buffer, total_bytes);
p_qtd->int_on_complete = int_on_complete;
p_qhd->qtd_overlay.next = (uint32_t) p_qtd;
if ( free_slot > 0 ) dcd_data.qtd[ p_qhd->list_qtd_idx[free_slot-1] ].next = (uint32_t) p_qtd;
LPC_USB0->ENDPTPRIME |= BIT_( endpoint_phy2pos(edpt_hdl.index) ) ;
return TUSB_ERROR_NONE;
}
tusb_error_t dcd_pipe_queue_xfer(endpoint_handle_t edpt_hdl, void * buffer, uint16_t total_bytes)
{
return pipe_add_xfer( edpt_hdl, buffer, total_bytes, false);
}
tusb_error_t dcd_pipe_xfer(endpoint_handle_t edpt_hdl, void* buffer, uint16_t total_bytes, bool int_on_complete)
{
ASSERT_STATUS ( pipe_add_xfer(edpt_hdl, buffer, total_bytes, int_on_complete) );
dcd_qhd_t* p_qhd = &dcd_data.qhd[ edpt_hdl.index ];
dcd_qtd_t* p_qtd = &dcd_data.qtd[ p_qhd->list_qtd_idx[0] ];
p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // attach head QTD to QHD start transferring
LPC_USB0->ENDPTPRIME |= BIT_( edpt_phy2pos(edpt_hdl.index) ) ;
return TUSB_ERROR_NONE;
}
//------------- Device Controller Driver's Interrupt Handler -------------//
void xfer_complete_isr(uint8_t coreid, uint8_t reg_complete)
{
if (reg_complete & BIT_(3+16))
{
hal_debugger_breakpoint();
}
// TODO currently exclude control
for(uint8_t ep_idx = 2; ep_idx < DCD_QHD_MAX; ep_idx++)
{
if ( BIT_TEST_(reg_complete, edpt_phy2pos(ep_idx)) )
{ // 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set
dcd_qhd_t * p_qhd = &dcd_data.qhd[ep_idx];
endpoint_handle_t edpt_hdl =
{
.coreid = coreid,
.xfer_type = p_qhd->xfer_type,
.index = ep_idx,
.class_code = p_qhd->class_code
};
// retire all QTDs in array list, up to 1st still-active QTD
while( p_qhd->list_qtd_idx[0] != 0 )
{
dcd_qtd_t * p_qtd = &dcd_data.qtd[ p_qhd->list_qtd_idx[0] ];
if (p_qtd->active) break; // stop immediately if found still-active QTD and shift array list
//------------- Free QTD and shift array list -------------//
p_qtd->used = 0; // free QTD
memmove(p_qhd->list_qtd_idx, p_qhd->list_qtd_idx+1, DCD_QTD_PER_QHD_MAX-1);
if (p_qtd->int_on_complete)
{
tusb_event_t event = ( p_qtd->xact_err || p_qtd->halted || p_qtd->buffer_err ) ? TUSB_EVENT_XFER_ERROR : TUSB_EVENT_XFER_COMPLETE;
usbd_xfer_isr(edpt_hdl, event, p_qtd->expected_bytes - p_qtd->total_bytes); // only number of bytes in the IOC qtd
}
}
}
}
}
void dcd_isr(uint8_t coreid)
{
uint32_t int_status = LPC_USB0->USBSTS_D;
@@ -405,13 +511,28 @@ void dcd_isr(uint8_t coreid)
tusb_control_request_t control_request = dcd_data.qhd[0].setup_request;
LPC_USB0->ENDPTSETUPSTAT = LPC_USB0->ENDPTSETUPSTAT;
//------------- Flush if previous transfer is not done -------------//
if (dcd_data.qhd[0].qtd_overlay.active || dcd_data.qhd[1].qtd_overlay.active)
{
do
{
LPC_USB0->ENDPTFLUSH = BIT_(0) | BIT_(16);
while(LPC_USB0->ENDPTFLUSH) {} // TODO refractor later
}while( LPC_USB0->ENDPTSTAT & (BIT_(0) | BIT_(16)) );
dcd_data.qhd[0].qtd_overlay.active = dcd_data.qhd[1].qtd_overlay.active = 0;
}
usbd_setup_received_isr(coreid, &control_request);
}
if (LPC_USB0->ENDPTCOMPLETE)
{
// hal_debugger_breakpoint();
LPC_USB0->ENDPTCOMPLETE = LPC_USB0->ENDPTCOMPLETE;
uint32_t edpt_complete = LPC_USB0->ENDPTCOMPLETE;
LPC_USB0->ENDPTCOMPLETE = edpt_complete; // acknowledge
xfer_complete_isr(coreid, edpt_complete);
}
}