diff --git a/.cproject b/.cproject
index a99a4ca5..3ba644ea 100644
--- a/.cproject
+++ b/.cproject
@@ -32,7 +32,10 @@
-
+
+
diff --git a/tinyusb/class/cdc_host.c b/tinyusb/class/cdc_host.c
index 81d992c2..9a3cb6ef 100644
--- a/tinyusb/class/cdc_host.c
+++ b/tinyusb/class/cdc_host.c
@@ -142,7 +142,30 @@ void cdch_isr(pipe_handle_t pipe_hdl, tusb_event_t event)
void cdch_close(uint8_t dev_addr)
{
+ tusb_error_t err1, err2, err3;
+ cdch_data_t * p_cdc = &cdch_data[dev_addr-1];
+
+ if ( pipehandle_is_valid(p_cdc->pipe_notification) )
+ {
+ err1 = hcd_pipe_close(p_cdc->pipe_notification);
+ }
+
+ if ( pipehandle_is_valid(p_cdc->pipe_in) )
+ {
+ err2 = hcd_pipe_close(p_cdc->pipe_in);
+ }
+
+ if ( pipehandle_is_valid(p_cdc->pipe_out) )
+ {
+ err3 = hcd_pipe_close(p_cdc->pipe_out);
+ }
+
+ memclr_(p_cdc, sizeof(cdch_data_t));
+
+ ASSERT(err1 == TUSB_ERROR_NONE &&
+ err2 == TUSB_ERROR_NONE &&
+ err3 == TUSB_ERROR_NONE, (void) 0 );
}
#endif
diff --git a/tinyusb/core/tusb_types.h b/tinyusb/core/tusb_types.h
index ac3bb879..34a11052 100644
--- a/tinyusb/core/tusb_types.h
+++ b/tinyusb/core/tusb_types.h
@@ -67,7 +67,7 @@ typedef enum {
TUSB_XFER_ISOCHRONOUS ,
TUSB_XFER_BULK ,
TUSB_XFER_INTERRUPT
-}tusb_transfer_type_t;
+}tusb_xfer_type_t;
typedef enum {
TUSB_DIR_HOST_TO_DEV = 0,
@@ -198,6 +198,8 @@ typedef enum tusb_device_state_{
typedef enum {
TUSB_EVENT_XFER_COMPLETE,
TUSB_EVENT_XFER_ERROR,
+ TUSB_EVENT_XFER_STALLED,
+
TUSB_EVENT_INTERFACE_OPEN,
TUSB_EVENT_INTERFACE_CLOSE,
diff --git a/tinyusb/host/ehci/ehci.c b/tinyusb/host/ehci/ehci.c
index 783e50ad..f30c9844 100644
--- a/tinyusb/host/ehci/ehci.c
+++ b/tinyusb/host/ehci/ehci.c
@@ -88,12 +88,17 @@ STATIC_ INLINE_ ehci_link_t* get_period_head(uint8_t hostid, uint8_t interval_ms
STATIC_ INLINE_ ehci_qhd_t* get_control_qhd(uint8_t dev_addr) ATTR_ALWAYS_INLINE ATTR_PURE ATTR_WARN_UNUSED_RESULT;
STATIC_ INLINE_ ehci_qtd_t* get_control_qtds(uint8_t dev_addr) ATTR_ALWAYS_INLINE ATTR_PURE ATTR_WARN_UNUSED_RESULT;
-static inline uint8_t qhd_get_index(ehci_qhd_t * p_qhd) ATTR_ALWAYS_INLINE ATTR_PURE;
+static inline uint8_t qhd_get_index(ehci_qhd_t const * p_qhd) ATTR_ALWAYS_INLINE ATTR_PURE;
+static inline ehci_qhd_t* qhd_next(ehci_qhd_t const * p_qhd) ATTR_ALWAYS_INLINE ATTR_PURE;
static inline ehci_qhd_t* qhd_find_free (uint8_t dev_addr) ATTR_PURE ATTR_ALWAYS_INLINE;
STATIC_ INLINE_ ehci_qhd_t* qhd_get_from_pipe_handle(pipe_handle_t pipe_hdl) ATTR_PURE ATTR_ALWAYS_INLINE;
+static inline pipe_handle_t qhd_create_pipe_handle(ehci_qhd_t const * p_qhd, tusb_xfer_type_t xfer_type) ATTR_PURE ATTR_ALWAYS_INLINE;
+
static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, uint16_t max_packet_size, uint8_t endpoint_addr, uint8_t xfer_type, uint8_t interval);
+
STATIC_ INLINE_ ehci_qtd_t* qtd_find_free(uint8_t dev_addr) ATTR_PURE ATTR_ALWAYS_INLINE;
+static inline ehci_qtd_t* qtd_next(ehci_qtd_t const * p_qtd ) ATTR_PURE ATTR_ALWAYS_INLINE;
static inline void qtd_insert_to_qhd(ehci_qhd_t *p_qhd, ehci_qtd_t *p_qtd_new) ATTR_ALWAYS_INLINE;
static inline void qtd_remove_1st_from_qhd(ehci_qhd_t *p_qhd) ATTR_ALWAYS_INLINE;
static void qtd_init(ehci_qtd_t* p_qtd, uint32_t data_ptr, uint16_t total_bytes);
@@ -424,7 +429,7 @@ tusb_error_t hcd_pipe_close(pipe_handle_t pipe_hdl)
// async list needs async advance handshake to make sure host controller has released cached data
// period list queue element is guarantee to be free in the next frame (1 ms)
- p_qhd->is_removing = 1;
+ p_qhd->is_removing = 1; // TODO redundant, only apply to control queue head
if ( pipe_hdl.xfer_type == TUSB_XFER_BULK )
{
@@ -453,6 +458,9 @@ bool hcd_pipe_is_idle(pipe_handle_t pipe_hdl)
//--------------------------------------------------------------------+
// EHCI Interrupt Handler
//--------------------------------------------------------------------+
+
+// async_advance is handshake between sw stack & ehci controller where ehci free all memory from an deleted queue head.
+// In tinyusb, queue head is only removed when device is unplugged. So only control queue head is checked if removing
void async_advance_isr(ehci_qhd_t * const async_head)
{
// TODO do we need to close addr0
@@ -506,7 +514,8 @@ void port_connect_status_change_isr(uint8_t hostid)
}
}
-void qhd_xfer_complete_isr(ehci_qhd_t * p_qhd, tusb_transfer_type_t xfer_type)
+//void qtd_xfer_process_isr(ehci_qtd_t * p_qtd, )
+void qhd_xfer_complete_isr(ehci_qhd_t * p_qhd, tusb_xfer_type_t xfer_type)
{
// free all TDs from the head td to the first active TD
while(p_qhd->p_qtd_list_head != NULL && !p_qhd->p_qtd_list_head->active)
@@ -520,16 +529,8 @@ void qhd_xfer_complete_isr(ehci_qhd_t * p_qhd, tusb_transfer_type_t xfer_type)
if (is_ioc) // end of request
{
- pipe_handle_t pipe_hdl = {
- .dev_addr = p_qhd->device_address,
- .xfer_type = xfer_type
- };
- if (TUSB_XFER_CONTROL != xfer_type) // qhd index for control is meaningless
- {
- pipe_hdl.index = qhd_get_index(p_qhd);
- }
-
- usbh_xfer_isr( pipe_hdl, p_qhd->class_code, TUSB_EVENT_XFER_COMPLETE); // call USBH callback
+ usbh_xfer_isr( qhd_create_pipe_handle(p_qhd, xfer_type),
+ p_qhd->class_code, TUSB_EVENT_XFER_COMPLETE); // call USBH callback
}
}
}
@@ -545,7 +546,7 @@ void async_list_xfer_complete_isr(ehci_qhd_t * const async_head)
qhd_xfer_complete_isr(p_qhd,
p_qhd->endpoint_number != 0 ? TUSB_XFER_BULK : TUSB_XFER_CONTROL);
}
- p_qhd = (ehci_qhd_t*) align32(p_qhd->next.address);
+ p_qhd = qhd_next(p_qhd);
max_loop++;
}while(p_qhd != async_head && max_loop < EHCI_MAX_QHD); // async list traversal, stop if loop around
// TODO abstract max loop guard for async
@@ -591,15 +592,23 @@ void period_list_xfer_complete_isr(uint8_t hostid, uint8_t interval_ms)
void xfer_error_isr(uint8_t hostid)
{
//------------- async list -------------//
+ uint8_t max_loop = 0;
ehci_qhd_t * const async_head = get_async_head(hostid);
ehci_qhd_t *p_qhd = async_head;
do
{
// current qhd has error in transaction
- if (p_qhd->qtd_overlay.buffer_err || p_qhd->qtd_overlay.babble_err || p_qhd->qtd_overlay.xact_err ||
+ if ( (p_qhd->device_address != 0 && p_qhd->qtd_overlay.halted) || // addr0 cannot be protocol STALL
+ p_qhd->qtd_overlay.buffer_err ||p_qhd->qtd_overlay.babble_err || p_qhd->qtd_overlay.xact_err
//p_qhd->qtd_overlay.non_hs_period_missed_uframe || p_qhd->qtd_overlay.pingstate_err TODO split transaction error
- (p_qhd->device_address != 0 && p_qhd->qtd_overlay.halted) ) // addr0 cannot be protocol STALL
+ )
{
+
+ if ( !p_qhd->qtd_overlay.buffer_err && !p_qhd->qtd_overlay.babble_err && !p_qhd->qtd_overlay.xact_err)
+ { // no error bits are set, endpoint is halted due to STALL handshake
+
+ }
+
hal_debugger_breakpoint();
p_qhd->p_qtd_list_head->used = 0; // free QTD
@@ -618,8 +627,9 @@ void xfer_error_isr(uint8_t hostid)
usbh_xfer_isr( pipe_hdl, p_qhd->class_code, TUSB_EVENT_XFER_ERROR); // call USBH callback
}
- p_qhd = (ehci_qhd_t*) align32(p_qhd->next.address);
- }while(p_qhd != async_head); // async list traversal, stop if loop around
+ p_qhd = qhd_next(p_qhd);
+ max_loop++;
+ }while(p_qhd != async_head && max_loop < EHCI_MAX_QHD); // async list traversal, stop if loop around
//------------- TODO period list -------------//
}
@@ -753,16 +763,36 @@ static inline ehci_qhd_t* qhd_find_free (uint8_t dev_addr)
return (index < EHCI_MAX_QHD) ? &ehci_data.device[relative_address].qhd[index] : NULL;
}
-static inline uint8_t qhd_get_index(ehci_qhd_t * p_qhd)
+static inline uint8_t qhd_get_index(ehci_qhd_t const * p_qhd)
{
return p_qhd - ehci_data.device[p_qhd->device_address-1].qhd;
}
+static inline ehci_qhd_t* qhd_next(ehci_qhd_t const * p_qhd)
+{
+ return (ehci_qhd_t*) align32(p_qhd->next.address);
+}
+
STATIC_ INLINE_ ehci_qhd_t* qhd_get_from_pipe_handle(pipe_handle_t pipe_hdl)
{
return &ehci_data.device[ pipe_hdl.dev_addr-1 ].qhd[ pipe_hdl.index ];
}
+static inline pipe_handle_t qhd_create_pipe_handle(ehci_qhd_t const * p_qhd, tusb_xfer_type_t xfer_type)
+{
+ pipe_handle_t pipe_hdl = {
+ .dev_addr = p_qhd->device_address,
+ .xfer_type = xfer_type
+ };
+
+ // TODO Isochronous transfer support
+ if (TUSB_XFER_CONTROL != xfer_type) // qhd index for control is meaningless
+ {
+ pipe_hdl.index = qhd_get_index(p_qhd);
+ }
+
+ return pipe_hdl;
+}
//------------- TD helper -------------//
STATIC_ INLINE_ ehci_qtd_t* qtd_find_free(uint8_t dev_addr)
@@ -776,6 +806,11 @@ STATIC_ INLINE_ ehci_qtd_t* qtd_find_free(uint8_t dev_addr)
return (index < EHCI_MAX_QTD) ? &ehci_data.device[dev_addr-1].qtd[index] : NULL;
}
+static inline ehci_qtd_t* qtd_next(ehci_qtd_t const * p_qtd )
+{
+ return (ehci_qtd_t*) align32(p_qtd->next.address);
+}
+
static inline void qtd_remove_1st_from_qhd(ehci_qhd_t *p_qhd)
{
if (p_qhd->p_qtd_list_head == p_qhd->p_qtd_list_tail) // last TD --> make it NULL
@@ -783,7 +818,7 @@ static inline void qtd_remove_1st_from_qhd(ehci_qhd_t *p_qhd)
p_qhd->p_qtd_list_head = p_qhd->p_qtd_list_tail = NULL;
}else
{
- p_qhd->p_qtd_list_head = (ehci_qtd_t*) align32(p_qhd->p_qtd_list_head->next.address);
+ p_qhd->p_qtd_list_head = qtd_next( p_qhd->p_qtd_list_head );
}
}
@@ -896,9 +931,9 @@ static ehci_link_t* list_find_previous_item(ehci_link_t* p_head, ehci_link_t* p_
{
ehci_link_t *p_prev = p_head;
uint32_t max_loop = 0;
- while( (align32(p_prev->address) != (uint32_t) p_head) &&
- (align32(p_prev->address) != (uint32_t) p_current) &&
- !p_prev->terminate &&
+ while( (align32(p_prev->address) != (uint32_t) p_head) &&
+ (align32(p_prev->address) != (uint32_t) p_current) &&
+ !p_prev->terminate &&
max_loop < EHCI_MAX_QHD)
{
p_prev = (ehci_link_t*) align32(p_prev->address);