| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * ehci.c | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Created on: Dec 2, 2012 | 
					
						
							|  |  |  |  *      Author: hathach | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Software License Agreement (BSD License) | 
					
						
							|  |  |  |  * Copyright (c) 2013, hathach (tinyusb.net) | 
					
						
							|  |  |  |  * 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. The name of the author may not be used to endorse or promote products | 
					
						
							|  |  |  |  *    derived from this software without specific prior written permission. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 tiny usb stack. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "common/common.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-04 17:07:14 +07:00
										 |  |  | #if MODE_HOST_SUPPORTED && (MCU == MCU_LPC43XX || MCU == MCU_LPC18XX)
 | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | // INCLUDE
 | 
					
						
							|  |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | #include "hal/hal.h"
 | 
					
						
							|  |  |  | #include "osal/osal.h"
 | 
					
						
							|  |  |  | #include "common/timeout_timer.h"
 | 
					
						
							| 
									
										
										
										
											2013-03-05 14:26:36 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "../hcd.h"
 | 
					
						
							|  |  |  | #include "../usbh_hcd.h"
 | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | #include "ehci.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | // MACRO CONSTANT TYPEDEF
 | 
					
						
							|  |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | // INTERNAL OBJECT & FUNCTION DECLARATION
 | 
					
						
							|  |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | STATIC_ ehci_data_t ehci_data TUSB_CFG_ATTR_USBRAM; | 
					
						
							|  |  |  | STATIC_ ehci_link_t period_frame_list0[EHCI_FRAMELIST_SIZE] ATTR_ALIGNED(4096) TUSB_CFG_ATTR_USBRAM; | 
					
						
							| 
									
										
										
										
											2013-03-04 16:21:41 +07:00
										 |  |  | #if CONTROLLER_HOST_NUMBER > 1
 | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | STATIC_ ehci_link_t period_frame_list1[EHCI_FRAMELIST_SIZE] ATTR_ALIGNED(4096) TUSB_CFG_ATTR_USBRAM; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-04 15:01:33 +07:00
										 |  |  | //------------- Validation -------------//
 | 
					
						
							|  |  |  | STATIC_ASSERT( ALIGN_OF(period_frame_list0) == 4096, "Period Framelist must be 4k alginment"); | 
					
						
							| 
									
										
										
										
											2013-03-04 16:21:41 +07:00
										 |  |  | #if CONTROLLER_HOST_NUMBER > 1
 | 
					
						
							| 
									
										
										
										
											2013-03-04 15:01:33 +07:00
										 |  |  | STATIC_ASSERT( ALIGN_OF(period_frame_list1) == 4096, "Period Framelist must be 4k alginment"); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | // IMPLEMENTATION
 | 
					
						
							|  |  |  | //--------------------------------------------------------------------+
 | 
					
						
							| 
									
										
										
										
											2013-03-03 15:32:04 +07:00
										 |  |  | STATIC_ INLINE_ ehci_registers_t* const get_operational_register(uint8_t hostid) ATTR_PURE ATTR_ALWAYS_INLINE ATTR_WARN_UNUSED_RESULT; | 
					
						
							|  |  |  | STATIC_ INLINE_ ehci_registers_t* const get_operational_register(uint8_t hostid) | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-03 15:32:04 +07:00
										 |  |  |   return (ehci_registers_t* const) (hostid ? (&LPC_USB1->USBCMD_H) : (&LPC_USB0->USBCMD_H) ); | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-03 15:32:04 +07:00
										 |  |  | STATIC_ INLINE_ ehci_link_t* const get_period_frame_list(uint8_t list_idx) ATTR_PURE ATTR_ALWAYS_INLINE ATTR_WARN_UNUSED_RESULT; | 
					
						
							|  |  |  | STATIC_ INLINE_ ehci_link_t* const get_period_frame_list(uint8_t list_idx) | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-04 16:21:41 +07:00
										 |  |  | #if CONTROLLER_HOST_NUMBER > 1
 | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  |   return list_idx ? period_frame_list1 : period_frame_list0; // TODO more than 2 controller
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |   return period_frame_list0; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-04 18:01:05 +07:00
										 |  |  | STATIC_ INLINE_ uint8_t hostid_to_data_idx(uint8_t hostid) ATTR_ALWAYS_INLINE ATTR_CONST ATTR_WARN_UNUSED_RESULT; | 
					
						
							|  |  |  | STATIC_ INLINE_ uint8_t hostid_to_data_idx(uint8_t hostid) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   #if (CONTROLLER_HOST_NUMBER == 1) && (TUSB_CFG_CONTROLLER1_MODE & TUSB_MODE_HOST)
 | 
					
						
							|  |  |  |     (void) hostid; | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  |   #else
 | 
					
						
							|  |  |  |     return hostid; | 
					
						
							|  |  |  |   #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | STATIC_ INLINE_ ehci_qhd_t* const get_async_head(uint8_t hostid) ATTR_ALWAYS_INLINE ATTR_PURE ATTR_WARN_UNUSED_RESULT; | 
					
						
							|  |  |  | STATIC_ INLINE_ ehci_qhd_t* const get_async_head(uint8_t hostid) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-06 14:25:26 +07:00
										 |  |  |   return &ehci_data.async_head[ hostid_to_data_idx(hostid) ]; | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-03 15:32:04 +07:00
										 |  |  | STATIC_ INLINE_ ehci_qhd_t* const get_period_head(uint8_t hostid) ATTR_ALWAYS_INLINE ATTR_PURE ATTR_WARN_UNUSED_RESULT; | 
					
						
							|  |  |  | STATIC_ INLINE_ ehci_qhd_t* const get_period_head(uint8_t hostid) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-06 14:25:26 +07:00
										 |  |  |   return &ehci_data.period_head[ hostid_to_data_idx(hostid) ]; | 
					
						
							| 
									
										
										
										
											2013-03-03 15:32:04 +07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  | tusb_error_t hcd_controller_init(uint8_t hostid) ATTR_WARN_UNUSED_RESULT; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | // USBH-HCD API
 | 
					
						
							|  |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | tusb_error_t hcd_init(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-07 17:59:07 +07:00
										 |  |  |   // oops, ehci_qtd_t:used must be at Reserved places in EHCI specs
 | 
					
						
							|  |  |  |   ASSERT(offsetof(ehci_qtd_t, used) == 16, TUSB_ERROR_HCD_FAILED); // TODO can be removed after an THOROUGH checked
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  |   //------------- Data Structure init -------------//
 | 
					
						
							|  |  |  |   memclr_(&ehci_data, sizeof(ehci_data_t)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-04 18:01:05 +07:00
										 |  |  |   #if (TUSB_CFG_CONTROLLER0_MODE & TUSB_MODE_HOST)
 | 
					
						
							|  |  |  |     ASSERT_STATUS (hcd_controller_init(0)); | 
					
						
							|  |  |  |   #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   #if (TUSB_CFG_CONTROLLER1_MODE & TUSB_MODE_HOST)
 | 
					
						
							|  |  |  |     ASSERT_STATUS (hcd_controller_init(1)); | 
					
						
							|  |  |  |   #endif
 | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return TUSB_ERROR_NONE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-10 17:51:53 +07:00
										 |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | // EHCI Interrupt Handler
 | 
					
						
							|  |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | static inline uint8_t get_qhd_index(ehci_qhd_t * p_qhd) ATTR_ALWAYS_INLINE ATTR_PURE; | 
					
						
							|  |  |  | static inline uint8_t get_qhd_index(ehci_qhd_t * p_qhd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return p_qhd - ehci_data.device[p_qhd->device_address].qhd; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void async_list_process_isr(ehci_qhd_t * const async_head, ehci_registers_t * const regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   ehci_qhd_t *p_qhd = async_head; | 
					
						
							|  |  |  |   do | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     if ( p_qhd->qtd_overlay.halted ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       // TODO invoke some error callback if not async head
 | 
					
						
							|  |  |  |     } else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       // 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) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         // TODO check halted TD
 | 
					
						
							|  |  |  |         if (p_qhd->p_qtd_list_head->int_on_complete) // end of request
 | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           pipe_handle_t pipe_hdl = { .dev_addr = p_qhd->device_address }; | 
					
						
							|  |  |  |           if (p_qhd->endpoint_number) // if not Control, can only be Bulk
 | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             pipe_hdl.xfer_type = TUSB_XFER_BULK; | 
					
						
							|  |  |  |             pipe_hdl.index = get_qhd_index(p_qhd); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           usbh_isr( pipe_hdl, p_qhd->class_code); // call USBH call back
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         p_qhd->p_qtd_list_head->used = 0; // free QTD
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (p_qhd->p_qtd_list_head == p_qhd->p_qtd_list_tail) // last TD --> make it NULL
 | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           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 = (ehci_qhd_t*) align32(p_qhd->next.address); | 
					
						
							|  |  |  |   }while(p_qhd != async_head); // stop if loop around
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //------------- Host Controller Driver's Interrupt Handler -------------//
 | 
					
						
							|  |  |  | // TODO this isr is not properly go through TDD
 | 
					
						
							| 
									
										
										
										
											2013-03-04 18:46:02 +07:00
										 |  |  | void hcd_isr(uint8_t hostid) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-10 17:51:53 +07:00
										 |  |  |   ehci_registers_t* const regs = get_operational_register(hostid); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   uint32_t int_status = regs->usb_sts & regs->usb_int_enable; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (int_status == 0) | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (int_status & EHCI_INT_MASK_ERROR) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     // TODO something going wrong
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   //------------- some QTD/SITD/ITD with IOC set is completed -------------//
 | 
					
						
							|  |  |  |   if (int_status & EHCI_INT_MASK_NXP_ASYNC) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     async_list_process_isr(get_async_head(hostid), regs); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (int_status & EHCI_INT_MASK_NXP_PERIODIC) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (int_status & EHCI_INT_MASK_PORT_CHANGE) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2013-03-04 18:46:02 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-10 17:51:53 +07:00
										 |  |  |   if (int_status & EHCI_INT_MASK_ASYNC_ADVANCE) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   regs->usb_sts |= regs->usb_sts; // Acknowledge interrupt & clear it
 | 
					
						
							| 
									
										
										
										
											2013-03-04 18:46:02 +07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | // Controller API
 | 
					
						
							|  |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | tusb_error_t hcd_controller_init(uint8_t hostid) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-03 15:32:04 +07:00
										 |  |  |   ehci_registers_t* const regs = get_operational_register(hostid); | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   //------------- CTRLDSSEGMENT Register (skip) -------------//
 | 
					
						
							|  |  |  |   //------------- USB INT Register -------------//
 | 
					
						
							|  |  |  |   regs->usb_int_enable = 0;                 // 1. disable all the interrupt
 | 
					
						
							|  |  |  |   regs->usb_sts        = EHCI_INT_MASK_ALL; // 2. clear all status
 | 
					
						
							|  |  |  |   regs->usb_int_enable = | 
					
						
							| 
									
										
										
										
											2013-03-10 17:51:53 +07:00
										 |  |  |       EHCI_INT_MASK_ERROR | EHCI_INT_MASK_PORT_CHANGE | 
					
						
							|  |  |  |       | EHCI_INT_MASK_ASYNC_ADVANCE | EHCI_INT_MASK_NXP_ASYNC | 
					
						
							|  |  |  | #if EHCI_PERIODIC_LIST
 | 
					
						
							|  |  |  |       | EHCI_INT_MASK_NXP_PERIODIC | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | #endif
 | 
					
						
							|  |  |  |       ; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   //------------- Asynchronous List -------------//
 | 
					
						
							|  |  |  |   ehci_qhd_t * const async_head = get_async_head(hostid); | 
					
						
							|  |  |  |   memclr_(async_head, sizeof(ehci_qhd_t)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-06 11:24:20 +07:00
										 |  |  |   async_head->next.address                    = (uint32_t) async_head; // circular list, next is itself
 | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  |   async_head->next.type                       = EHCI_QUEUE_ELEMENT_QHD; | 
					
						
							|  |  |  |   async_head->head_list_flag                  = 1; | 
					
						
							| 
									
										
										
										
											2013-03-06 11:24:20 +07:00
										 |  |  |   async_head->qtd_overlay.halted              = 1; // inactive most of time
 | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   regs->async_list_base = (uint32_t) async_head; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   //------------- Periodic List -------------//
 | 
					
						
							| 
									
										
										
										
											2013-03-03 15:32:04 +07:00
										 |  |  | #if EHCI_PERIODIC_LIST
 | 
					
						
							|  |  |  |   ehci_link_t * const framelist  = get_period_frame_list(hostid); | 
					
						
							|  |  |  |   ehci_qhd_t * const period_head = get_period_head(hostid); | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-03 15:32:04 +07:00
										 |  |  |   uint32_t i; | 
					
						
							|  |  |  |   for(i=0; i<EHCI_FRAMELIST_SIZE; i++) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     framelist[i].address = (uint32_t) period_head; | 
					
						
							|  |  |  |     framelist[i].type    = EHCI_QUEUE_ELEMENT_QHD; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-09 14:38:35 +07:00
										 |  |  |   period_head->interrupt_smask    = 1; // queue head in period list must have smask non-zero
 | 
					
						
							|  |  |  |   period_head->next.terminate     = 1; | 
					
						
							|  |  |  |   period_head->qtd_overlay.halted = 1; // dummy node, always inactive
 | 
					
						
							| 
									
										
										
										
											2013-03-03 15:32:04 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-09 14:38:35 +07:00
										 |  |  |   regs->periodic_list_base        = (uint32_t) framelist; | 
					
						
							| 
									
										
										
										
											2013-03-03 15:32:04 +07:00
										 |  |  | #else
 | 
					
						
							|  |  |  |   regs->periodic_list_base = 0; | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2013-03-04 00:46:40 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   //------------- TT Control (NXP only) -------------//
 | 
					
						
							|  |  |  |   regs->tt_control = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  |   //------------- USB CMD Register -------------//
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-04 00:46:40 +07:00
										 |  |  |   regs->usb_cmd = BIT_(EHCI_USBCMD_POS_ASYNC_ENABLE) | 
					
						
							|  |  |  | #if EHCI_PERIODIC_LIST
 | 
					
						
							|  |  |  |                   | BIT_(EHCI_USBCMD_POS_PERIOD_ENABLE) | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |                   | ((EHCI_CFG_FRAMELIST_SIZE_BITS & BIN8(011)) << EHCI_USBCMD_POS_FRAMELIST_SZIE) | 
					
						
							|  |  |  |                   | ((EHCI_CFG_FRAMELIST_SIZE_BITS >> 2) << EHCI_USBCMD_POS_NXP_FRAMELIST_SIZE_MSB); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  |   //------------- ConfigFlag Register (skip) -------------//
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return TUSB_ERROR_NONE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | tusb_error_t hcd_controller_stop(uint8_t hostid) ATTR_WARN_UNUSED_RESULT; | 
					
						
							|  |  |  | tusb_error_t hcd_controller_stop(uint8_t hostid) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-03 15:32:04 +07:00
										 |  |  |   ehci_registers_t* const regs = get_operational_register(hostid); | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  |   timeout_timer_t timeout; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   regs->usb_cmd_bit.run_stop = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   timeout_set(&timeout, 16); // USB Spec: controller has to stop within 16 uframe
 | 
					
						
							|  |  |  |   while( regs->usb_sts_bit.hc_halted == 0 && !timeout_expired(&timeout)) {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return timeout_expired(&timeout) ? TUSB_ERROR_OSAL_TIMEOUT : TUSB_ERROR_NONE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-05 14:26:36 +07:00
										 |  |  | //TODO host/device mode must be set immediately after a reset
 | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | tusb_error_t hcd_controller_reset(uint8_t hostid) ATTR_WARN_UNUSED_RESULT; | 
					
						
							|  |  |  | tusb_error_t hcd_controller_reset(uint8_t hostid) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-03 15:32:04 +07:00
										 |  |  |   ehci_registers_t* const regs = get_operational_register(hostid); | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  |   timeout_timer_t timeout; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (regs->usb_sts_bit.hc_halted == 0) // need to stop before reset
 | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     ASSERT_STATUS( hcd_controller_stop(hostid) ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   regs->usb_cmd_bit.reset = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   timeout_set(&timeout, 16); // should not take longer the time to stop controller
 | 
					
						
							|  |  |  |   while( regs->usb_cmd_bit.reset && !timeout_expired(&timeout)) {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return timeout_expired(&timeout) ? TUSB_ERROR_OSAL_TIMEOUT : TUSB_ERROR_NONE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | // PORT API
 | 
					
						
							|  |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | // PIPE API
 | 
					
						
							|  |  |  | //--------------------------------------------------------------------+
 | 
					
						
							| 
									
										
										
										
											2013-03-09 15:43:14 +07:00
										 |  |  | static void init_qhd(ehci_qhd_t *p_qhd, uint8_t dev_addr, uint16_t max_packet_size, uint8_t endpoint_addr, uint8_t xfer_type); | 
					
						
							| 
									
										
										
										
											2013-03-06 16:24:32 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-06 03:03:59 +07:00
										 |  |  | static inline ehci_qhd_t* const 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; | 
					
						
							| 
									
										
										
										
											2013-03-06 00:29:10 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 16:06:28 +07:00
										 |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | // CONTROL PIPE API
 | 
					
						
							|  |  |  | //--------------------------------------------------------------------+
 | 
					
						
							| 
									
										
										
										
											2013-03-10 17:51:53 +07:00
										 |  |  | // TODO subject to pure function
 | 
					
						
							|  |  |  | static void init_qtd(ehci_qtd_t* p_qtd, uint32_t data_ptr, uint16_t total_bytes) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   memclr_(p_qtd, sizeof(ehci_qtd_t)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   p_qtd->used                = 1; | 
					
						
							| 
									
										
										
										
											2013-03-07 16:40:01 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-10 17:51:53 +07:00
										 |  |  |   p_qtd->next.terminate      = 1; // init to null
 | 
					
						
							|  |  |  |   p_qtd->alternate.terminate = 1; // not used, always set to terminated
 | 
					
						
							|  |  |  |   p_qtd->active              = 1; | 
					
						
							|  |  |  |   p_qtd->cerr                = 3; // TODO 3 consecutive errors tolerance
 | 
					
						
							|  |  |  |   p_qtd->data_toggle         = 0; | 
					
						
							|  |  |  |   p_qtd->total_bytes         = total_bytes; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   p_qtd->buffer[0]           = data_ptr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for(uint8_t i=1; i<5; i++) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     p_qtd->buffer[i] |= align4k( p_qtd->buffer[i-1] ) + 4096; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-03-07 16:40:01 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t new_type) ATTR_ALWAYS_INLINE; | 
					
						
							|  |  |  | static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t new_type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   new->address = current->address; | 
					
						
							|  |  |  |   current->address = (uint32_t) new; | 
					
						
							|  |  |  |   current->type = new_type; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-10 17:51:53 +07:00
										 |  |  | static inline void insert_qtd_to_qhd(ehci_qhd_t *p_qhd, ehci_qtd_t *p_qtd_new) ATTR_ALWAYS_INLINE; | 
					
						
							|  |  |  | static inline void insert_qtd_to_qhd(ehci_qhd_t *p_qhd, ehci_qtd_t *p_qtd_new) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (p_qhd->p_qtd_list_head == NULL) // empty list
 | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     p_qhd->p_qtd_list_head               = p_qhd->p_qtd_list_tail = p_qtd_new; | 
					
						
							|  |  |  |     p_qhd->qtd_overlay.next.address      = (uint32_t) p_qhd->p_qtd_list_head; | 
					
						
							|  |  |  |   }else | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     p_qhd->p_qtd_list_tail->next.address = (uint32_t) p_qtd_new; | 
					
						
							|  |  |  |     p_qhd->p_qtd_list_tail               = p_qtd_new; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-06 00:29:10 +07:00
										 |  |  | tusb_error_t  hcd_pipe_control_open(uint8_t dev_addr, uint8_t max_packet_size) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-06 03:03:59 +07:00
										 |  |  |   ehci_qhd_t * const p_qhd = get_control_qhd(dev_addr); | 
					
						
							| 
									
										
										
										
											2013-03-06 00:29:10 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-09 15:43:14 +07:00
										 |  |  |   init_qhd(p_qhd, dev_addr, max_packet_size, 0, TUSB_XFER_CONTROL); | 
					
						
							| 
									
										
										
										
											2013-03-05 14:26:36 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (dev_addr != 0) | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2013-03-09 13:11:02 +07:00
										 |  |  |     //------------- insert to async list -------------//
 | 
					
						
							|  |  |  |     // TODO might need to to disable async list first
 | 
					
						
							| 
									
										
										
										
											2013-03-07 16:40:01 +07:00
										 |  |  |     list_insert( (ehci_link_t*) get_async_head(usbh_device_info_pool[dev_addr].core_id), | 
					
						
							|  |  |  |                  (ehci_link_t*) p_qhd, EHCI_QUEUE_ELEMENT_QHD); | 
					
						
							| 
									
										
										
										
											2013-03-05 14:26:36 +07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return TUSB_ERROR_NONE; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-03-05 15:23:12 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-06 03:03:59 +07:00
										 |  |  | tusb_error_t  hcd_pipe_control_xfer(uint8_t dev_addr, tusb_std_request_t const * p_request, uint8_t data[]) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   ehci_qhd_t * const p_qhd = get_control_qhd(dev_addr); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-09 14:38:35 +07:00
										 |  |  |   ehci_qtd_t *p_setup      = get_control_qtds(dev_addr); | 
					
						
							|  |  |  |   ehci_qtd_t *p_data       = p_setup + 1; | 
					
						
							|  |  |  |   ehci_qtd_t *p_status     = p_setup + 2; | 
					
						
							| 
									
										
										
										
											2013-03-06 03:03:59 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-06 16:24:32 +07:00
										 |  |  |   //------------- SETUP Phase -------------//
 | 
					
						
							| 
									
										
										
										
											2013-03-09 15:43:14 +07:00
										 |  |  |   init_qtd(p_setup, (uint32_t) p_request, 8); | 
					
						
							| 
									
										
										
										
											2013-03-09 14:19:40 +07:00
										 |  |  |   p_setup->pid          = EHCI_PID_SETUP; | 
					
						
							| 
									
										
										
										
											2013-03-06 16:24:32 +07:00
										 |  |  |   p_setup->next.address = (uint32_t) p_data; | 
					
						
							| 
									
										
										
										
											2013-03-10 17:51:53 +07:00
										 |  |  |   insert_qtd_to_qhd(p_qhd, p_setup); | 
					
						
							| 
									
										
										
										
											2013-03-06 03:03:59 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-06 16:24:32 +07:00
										 |  |  |   //------------- DATA Phase -------------//
 | 
					
						
							| 
									
										
										
										
											2013-03-06 03:03:59 +07:00
										 |  |  |   if (p_request->wLength > 0) | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2013-03-09 15:43:14 +07:00
										 |  |  |     init_qtd(p_data, (uint32_t) data, p_request->wLength); | 
					
						
							| 
									
										
										
										
											2013-03-06 16:24:32 +07:00
										 |  |  |     p_data->data_toggle = 1; | 
					
						
							|  |  |  |     p_data->pid         = p_request->bmRequestType.direction ? EHCI_PID_IN : EHCI_PID_OUT; | 
					
						
							| 
									
										
										
										
											2013-03-10 17:51:53 +07:00
										 |  |  |     insert_qtd_to_qhd(p_qhd, p_data); | 
					
						
							| 
									
										
										
										
											2013-03-06 03:03:59 +07:00
										 |  |  |   }else | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     p_data = p_setup; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2013-03-06 15:39:37 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-06 03:03:59 +07:00
										 |  |  |   p_data->next.address = (uint32_t) p_status; | 
					
						
							| 
									
										
										
										
											2013-03-06 15:39:37 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-06 16:24:32 +07:00
										 |  |  |   //------------- STATUS Phase -------------//
 | 
					
						
							| 
									
										
										
										
											2013-03-09 15:43:14 +07:00
										 |  |  |   init_qtd(p_status, 0, 0); // zero-length data
 | 
					
						
							| 
									
										
										
										
											2013-03-06 16:24:32 +07:00
										 |  |  |   p_status->int_on_complete = 1; | 
					
						
							|  |  |  |   p_status->data_toggle     = 1; | 
					
						
							|  |  |  |   p_status->pid             = p_request->bmRequestType.direction ? EHCI_PID_OUT : EHCI_PID_IN; // reverse direction of data phase
 | 
					
						
							|  |  |  |   p_status->next.terminate  = 1; | 
					
						
							| 
									
										
										
										
											2013-03-10 17:51:53 +07:00
										 |  |  |   insert_qtd_to_qhd(p_qhd, p_status); | 
					
						
							| 
									
										
										
										
											2013-03-06 03:03:59 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return TUSB_ERROR_NONE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 16:06:28 +07:00
										 |  |  | tusb_error_t  hcd_pipe_control_close(uint8_t dev_addr) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-09 13:11:02 +07:00
										 |  |  |   //------------- TODO pipe handle validate -------------//
 | 
					
						
							| 
									
										
										
										
											2013-03-07 16:06:28 +07:00
										 |  |  |   ehci_qhd_t * const p_qhd = get_control_qhd(dev_addr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   p_qhd->qtd_overlay.halted = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // TODO remove from async list
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return TUSB_ERROR_NONE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | // BULK/INT/ISO PIPE API
 | 
					
						
							|  |  |  | //--------------------------------------------------------------------+
 | 
					
						
							| 
									
										
										
										
											2013-03-09 21:37:49 +07:00
										 |  |  | pipe_handle_t hcd_pipe_open(uint8_t dev_addr, tusb_descriptor_endpoint_t const * p_endpoint_desc, uint8_t class_code) | 
					
						
							| 
									
										
										
										
											2013-03-07 16:06:28 +07:00
										 |  |  | { | 
					
						
							|  |  |  |   pipe_handle_t const null_handle = { .dev_addr = 0, .xfer_type = 0, .index = 0 }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 16:10:16 +07:00
										 |  |  |   if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) | 
					
						
							|  |  |  |     return null_handle; // TODO not support ISO yet
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 16:06:28 +07:00
										 |  |  |   //------------- find a free queue head -------------//
 | 
					
						
							|  |  |  |   uint8_t index=0; | 
					
						
							|  |  |  |   while( index<EHCI_MAX_QHD && ehci_data.device[dev_addr].qhd[index].used ) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     index++; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   ASSERT( index < EHCI_MAX_QHD, null_handle); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   ehci_qhd_t * const p_qhd = &ehci_data.device[dev_addr].qhd[index]; | 
					
						
							| 
									
										
										
										
											2013-03-09 15:43:14 +07:00
										 |  |  |   init_qhd(p_qhd, dev_addr, p_endpoint_desc->wMaxPacketSize, p_endpoint_desc->bEndpointAddress, p_endpoint_desc->bmAttributes.xfer); | 
					
						
							| 
									
										
										
										
											2013-03-09 21:37:49 +07:00
										 |  |  |   p_qhd->class_code = class_code; | 
					
						
							| 
									
										
										
										
											2013-03-07 16:06:28 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   ehci_qhd_t * list_head; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_BULK) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     // TODO might need to to disable async list first
 | 
					
						
							|  |  |  |     list_head = get_async_head(usbh_device_info_pool[dev_addr].core_id); | 
					
						
							|  |  |  |   }else if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_INTERRUPT) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     // TODO might need to to disable period list first
 | 
					
						
							|  |  |  |     list_head = get_period_head(usbh_device_info_pool[dev_addr].core_id); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   //------------- insert to async/period list -------------//
 | 
					
						
							| 
									
										
										
										
											2013-03-07 16:40:01 +07:00
										 |  |  |   list_insert( (ehci_link_t*) list_head, | 
					
						
							|  |  |  |                (ehci_link_t*) p_qhd, EHCI_QUEUE_ELEMENT_QHD); | 
					
						
							| 
									
										
										
										
											2013-03-07 16:06:28 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return (pipe_handle_t) { .dev_addr = dev_addr, .xfer_type = p_endpoint_desc->bmAttributes.xfer, .index = index}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-09 13:11:02 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-09 14:19:40 +07:00
										 |  |  | tusb_error_t  hcd_pipe_xfer(pipe_handle_t pipe_hdl, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete) | 
					
						
							| 
									
										
										
										
											2013-03-07 17:59:07 +07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-09 13:11:02 +07:00
										 |  |  |   //------------- TODO pipe handle validate -------------//
 | 
					
						
							| 
									
										
										
										
											2013-03-07 19:54:00 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 17:59:07 +07:00
										 |  |  |   //------------- find a free qtd -------------//
 | 
					
						
							| 
									
										
										
										
											2013-03-07 19:54:00 +07:00
										 |  |  |   uint8_t index=0; | 
					
						
							|  |  |  |   while( index<EHCI_MAX_QTD && ehci_data.device[pipe_hdl.dev_addr].qtd[index].used ) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     index++; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   ASSERT( index < EHCI_MAX_QTD, TUSB_ERROR_EHCI_NOT_ENOUGH_QTD); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   //------------- set up QTD -------------//
 | 
					
						
							| 
									
										
										
										
											2013-03-09 13:11:02 +07:00
										 |  |  |   ehci_qhd_t *p_qhd = &ehci_data.device[pipe_hdl.dev_addr].qhd[pipe_hdl.index]; | 
					
						
							| 
									
										
										
										
											2013-03-07 19:54:00 +07:00
										 |  |  |   ehci_qtd_t *p_qtd = &ehci_data.device[pipe_hdl.dev_addr].qtd[index]; | 
					
						
							| 
									
										
										
										
											2013-03-09 13:11:02 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-09 15:43:14 +07:00
										 |  |  |   init_qtd(p_qtd, (uint32_t) buffer, total_bytes); | 
					
						
							| 
									
										
										
										
											2013-03-07 19:54:00 +07:00
										 |  |  |   p_qtd->pid = p_qhd->pid_non_control; | 
					
						
							| 
									
										
										
										
											2013-03-09 14:19:40 +07:00
										 |  |  |   p_qtd->int_on_complete = int_on_complete ? 1 : 0; | 
					
						
							| 
									
										
										
										
											2013-03-07 19:54:00 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-09 13:11:02 +07:00
										 |  |  |   //------------- insert TD to TD list -------------//
 | 
					
						
							|  |  |  |   insert_qtd_to_qhd(p_qhd, p_qtd); | 
					
						
							| 
									
										
										
										
											2013-03-07 17:59:07 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return TUSB_ERROR_NONE; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-03-06 03:03:59 +07:00
										 |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | // HELPER
 | 
					
						
							|  |  |  | //--------------------------------------------------------------------+
 | 
					
						
							|  |  |  | static inline ehci_qhd_t* const get_control_qhd(uint8_t dev_addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return (dev_addr == 0) ? | 
					
						
							|  |  |  |       get_async_head( usbh_device_info_pool[dev_addr].core_id ) : | 
					
						
							|  |  |  |       &ehci_data.device[dev_addr].control.qhd; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | static inline ehci_qtd_t* get_control_qtds(uint8_t dev_addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return (dev_addr == 0) ? | 
					
						
							| 
									
										
										
										
											2013-03-07 16:06:28 +07:00
										 |  |  |       ehci_data.addr0_qtd : | 
					
						
							| 
									
										
										
										
											2013-03-06 03:03:59 +07:00
										 |  |  |       ehci_data.device[ dev_addr ].control.qtd; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 16:40:01 +07:00
										 |  |  | // TODO subject to pure function
 | 
					
						
							| 
									
										
										
										
											2013-03-09 15:43:14 +07:00
										 |  |  | static void init_qhd(ehci_qhd_t *p_qhd, uint8_t dev_addr, uint16_t max_packet_size, uint8_t endpoint_addr, uint8_t xfer_type) | 
					
						
							| 
									
										
										
										
											2013-03-06 03:03:59 +07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-06 22:21:42 +07:00
										 |  |  |   memclr_(p_qhd, sizeof(ehci_qhd_t)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-06 11:24:20 +07:00
										 |  |  |   p_qhd->device_address          = dev_addr; | 
					
						
							|  |  |  |   p_qhd->inactive_next_xact      = 0; | 
					
						
							|  |  |  |   p_qhd->endpoint_number         = endpoint_addr & 0x0F; | 
					
						
							|  |  |  |   p_qhd->endpoint_speed          = usbh_device_info_pool[dev_addr].speed; | 
					
						
							|  |  |  |   p_qhd->data_toggle_control     = (xfer_type == TUSB_XFER_CONTROL) ? 1 : 0; | 
					
						
							|  |  |  |   p_qhd->head_list_flag          = (dev_addr == 0) ? 1 : 0; // addr0's endpoint is the static asyn list head
 | 
					
						
							|  |  |  |   p_qhd->max_package_size        = max_packet_size; | 
					
						
							| 
									
										
										
										
											2013-03-06 03:03:59 +07:00
										 |  |  |   p_qhd->non_hs_control_endpoint = ((TUSB_XFER_CONTROL == xfer_type) && (usbh_device_info_pool[dev_addr].speed != TUSB_SPEED_HIGH) )  ? 1 : 0; | 
					
						
							| 
									
										
										
										
											2013-03-06 11:24:20 +07:00
										 |  |  |   p_qhd->nak_count_reload        = 0; | 
					
						
							| 
									
										
										
										
											2013-03-06 03:03:59 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-06 22:21:42 +07:00
										 |  |  |   // Bulk/Control -> smask = cmask = 0
 | 
					
						
							|  |  |  |   if (TUSB_XFER_INTERRUPT == xfer_type) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     // Highspeed: schedule every uframe (1 us interval); Full/Low: schedule only 1st frame
 | 
					
						
							|  |  |  |     p_qhd->interrupt_smask         = (TUSB_SPEED_HIGH == usbh_device_info_pool[dev_addr].speed) ? 0xFF : 0x01; | 
					
						
							|  |  |  |     // Highspeed: ignored by Host Controller, Full/Low: 4.12.2.1 (EHCI) case 1 schedule complete split at 2,3,4 uframe
 | 
					
						
							|  |  |  |     p_qhd->non_hs_interrupt_cmask  = BIN8(11100); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-06 11:24:20 +07:00
										 |  |  |   p_qhd->hub_address             = usbh_device_info_pool[dev_addr].hub_addr; | 
					
						
							|  |  |  |   p_qhd->hub_port                = usbh_device_info_pool[dev_addr].hub_port; | 
					
						
							|  |  |  |   p_qhd->mult                    = 1; // TODO not use high bandwidth/park mode yet
 | 
					
						
							| 
									
										
										
										
											2013-03-06 03:03:59 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-06 11:24:20 +07:00
										 |  |  |   //------------- active, but no TD list -------------//
 | 
					
						
							|  |  |  |   p_qhd->qtd_overlay.halted              = 0; | 
					
						
							| 
									
										
										
										
											2013-03-06 03:03:59 +07:00
										 |  |  |   p_qhd->qtd_overlay.next.terminate      = 1; | 
					
						
							|  |  |  |   p_qhd->qtd_overlay.alternate.terminate = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   //------------- HCD Management Data -------------//
 | 
					
						
							|  |  |  |   p_qhd->used            = 1; | 
					
						
							| 
									
										
										
										
											2013-03-09 13:11:02 +07:00
										 |  |  |   p_qhd->p_qtd_list_head = NULL; | 
					
						
							|  |  |  |   p_qhd->p_qtd_list_tail = NULL; | 
					
						
							| 
									
										
										
										
											2013-03-06 03:03:59 +07:00
										 |  |  |   p_qhd->pid_non_control = (endpoint_addr & 0x80) ? EHCI_PID_IN : EHCI_PID_OUT; // PID for TD under this endpoint
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-03 12:31:18 +07:00
										 |  |  | #endif
 |