/* * Copyright (c) 2015 - 2016, Freescale Semiconductor, Inc. * Copyright 2016 NXP * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, this list * of conditions and the following disclaimer. * * o 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. * * o Neither the name of the copyright holder 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 AND CONTRIBUTORS "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 OR CONTRIBUTORS 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. */ #include #if ((defined USB_HOST_CONFIG_EHCI) && (USB_HOST_CONFIG_EHCI > 0U)) #include "usb_host.h" #include "usb_host_hci.h" #include "usb_host_devices.h" #include "fsl_device_registers.h" #include "usb_host_ehci.h" #include "usb_phy.h" #if ((defined USB_HOST_CONFIG_COMPLIANCE_TEST) && (USB_HOST_CONFIG_COMPLIANCE_TEST)) #include "usb_host.h" #endif /******************************************************************************* * Definitions ******************************************************************************/ #if defined(USB_STACK_USE_DEDICATED_RAM) && (USB_STACK_USE_DEDICATED_RAM > 0U) #error The SOC does not suppoort dedicated RAM case. #endif #define USB_HOST_EHCI_BANDWIDTH_DELAY (3500U) #define USB_HOST_EHCI_BANDWIDTH_HUB_LS_SETUP (333U) #define USB_HOST_EHCI_BANDWIDTH_FRAME_TOTOAL_TIME (900U) #if ((defined USB_HOST_CONFIG_COMPLIANCE_TEST) && (USB_HOST_CONFIG_COMPLIANCE_TEST)) #define USB_HOST_EHCI_TEST_DESCRIPTOR_LENGTH (18U) #define USB_HOST_EHCI_PORTSC_PTC_J_STATE (0x01U) #define USB_HOST_EHCI_PORTSC_PTC_K_STATE (0x02U) #define USB_HOST_EHCI_PORTSC_PTC_SE0_NAK (0x03U) #define USB_HOST_EHCI_PORTSC_PTC_PACKET (0x04U) #define USB_HOST_EHCI_PORTSC_PTC_FORCE_ENABLE_HS (0x05U) #define USB_HOST_EHCI_PORTSC_PTC_FORCE_ENABLE_FS (0x06U) #define USB_HOST_EHCI_PORTSC_PTC_FORCE_ENABLE_LS (0x07U) #endif /******************************************************************************* * Prototypes ******************************************************************************/ /*! * @brief compute data bandwidth time. * * @param speed data speed. * @param pipeType data type. * @param direction data direction. * @param dataLength data length. * *@return time value. */ static uint32_t USB_HostBandwidthComputeTime(uint8_t speed, uint8_t pipeType, uint8_t direction, uint32_t dataLength); /*! * @brief compute current allocated bandwidth when ehci work as full-speed or low-speed host. * * @param ehciInstance ehci instance pointer. * @param frameIndex frame index. * @param frameBandwidths return frame bandwidth data. */ static void USB_HostBandwidthFslsHostComputeCurrent(usb_host_ehci_instance_t *ehciInstance, uint16_t frameIndex, uint16_t *frameBandwidth); /*! * @brief compute current hub's allocated FS/LS bandwidth when ehci work as hi-speed host. * * @param ehciInstance ehci instance pointer. * @param hubNumber hub address. * @param frameIndex frame index. * @param frameBandwidths return frame bandwidth data. */ static void USB_HostBandwidthHsHostComputeCurrentFsls(usb_host_ehci_instance_t *ehciInstance, uint32_t hubNumber, uint16_t frameIndex, uint8_t frameBandwidths[8]); /*! * @brief compute current allocated HS bandwidth when ehci work as hi-speed host. * * @param ehciInstance ehci instance pointer. * @param frameIndex frame index. * @param frameBandwidths return frame bandwidth data. */ static void USB_HostBandwidthHsHostComputeCurrentHsAll(usb_host_ehci_instance_t *ehciInstance, uint16_t frameIndex, uint8_t frameBandwidths[8]); /*! * @brief allocate HS bandwidth when host work as high-speed host. * * @param ehciInstance ehci instance pointer. * @param uframeInterval micro-frame interval. * @param timeData time for allocating. * @param uframe_index_out return start uframe index. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostBandwidthHsHostAllocateHsCommon(usb_host_ehci_instance_t *ehciInstance, uint16_t uframeInterval, uint16_t timeData, uint16_t *uframeIndexOut); /*! * @brief allocate HS interrupt bandwidth when host work as high-speed host. * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe pointer. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostBandwidthHsHostAllocateInterrupt(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer); /*! * @brief allocate bandwidth when host work as full-speed or low-speed host. * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe pointer. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostBandwidthFslsHostAllocate(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer); /*! * @brief get the 2 power value of uint8_t. * * @param value input uint8_t value. */ static uint8_t USB_HostEhciGet2PowerValue(uint8_t value); /*! * @brief memory zero. * * @param buffer buffer pointer. * @param length buffer length. */ static void USB_HostEhciZeroMem(uint32_t *buffer, uint32_t length); /*! * @brief host ehci delay. * * @param ehciIpBase ehci ip base address. * @param ms millisecond. */ static void USB_HostEhciDelay(USBHS_Type *ehciIpBase, uint32_t ms); /*! * @brief host ehci start async schedule. * * @param ehciInstance ehci instance pointer. */ static void USB_HostEhciStartAsync(usb_host_ehci_instance_t *ehciInstance); /*! * @brief host ehci stop async schedule. * * @param ehciInstance ehci instance pointer. */ static void USB_HostEhciStopAsync(usb_host_ehci_instance_t *ehciInstance); /*! * @brief host ehci start periodic schedule. * * @param ehciInstance ehci instance pointer. */ static void USB_HostEhciStartPeriodic(usb_host_ehci_instance_t *ehciInstance); /*! * @brief host ehci stop periodic schedule. * * @param ehciInstance ehci instance pointer. */ static void USB_HostEhciStopPeriodic(usb_host_ehci_instance_t *ehciInstance); /*! * @brief initialize the qtd for one transfer. * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe pointer. * @param transfer transfer information. * *@return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostEhciQhQtdListInit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer, usb_host_transfer_t *transfer); /*! * @brief release the qtd list. * * @param ehciInstance ehci instance pointer. * @param ehciQtdStart qtd list start pointer. * @param ehciQtdEnd qtd list end pointer. * *@return the transfer's length. */ static uint32_t USB_HostEhciQtdListRelease(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_qtd_t *ehciQtdStart, usb_host_ehci_qtd_t *ehciQtdEnd); /*! * @brief de-initialize qh's linking qtd list. * 1. remove qtd from qh; 2. remove transfer from qh; 3. release qtd; 4. transfer callback. * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe. * *@return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostEhciQhQtdListDeinit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer); /*! * @brief de-initialize transfer's linking qtd list. * 1. stop this qh schedule; 2. remove qtd from qh; 3. remove transfer from qh; 4. release qtd; 5. transfer callback; 6. *start this qh schedule. * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe pointer. * @param transfer transfer information. * *@return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostEhciTransferQtdListDeinit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer, usb_host_transfer_t *transfer); /*! * @brief initialize QH when opening one control, bulk or interrupt pipe. * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe pointer. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostEhciQhInit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer); /*! * @brief de-initialize QH when closing one control, bulk or interrupt pipe. * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe pointer. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostEhciQhDeinit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer); /*! * @brief add qh to one frame entry. * * @param ehciInstance ehci instance pointer. * @param entryPointerValue entry pointer value. * @param framePos frame index. * @param uframeInterval micro-frame interval. */ static void USB_HostEhciAddQhToFrame(usb_host_ehci_instance_t *ehciInstance, uint32_t entryPointerValue, uint16_t framePos, uint16_t uframeInterval); /*! * @brief remove entry from frame list. * * @param ehciInstance ehci instance pointer. * @param entryPointerValue entry pointer value. * @param framePos frame index. */ static void USB_HostEhciRemoveFromFrame(usb_host_ehci_instance_t *ehciInstance, uint32_t entryPointerValue, uint16_t framePos); #if ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD)) /*! * @brief add sitd array to the frame list. * * @param ehciInstance ehci instance pointer. * @param entryPointerValue entry pointer value. * @param startEntryPointer sitd entry pointer. */ static void USB_HostEhciLinkSitd(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer, void *startEntryPointer); /*! * @brief initialize sitd array for one transfer. * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe pointer. * @param transfer transfer information. */ static usb_status_t USB_HostEhciSitdArrayInit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer, usb_host_transfer_t *transfer); /*! * @brief release sitd list. * * @param ehciInstance ehci instance pointer. * @param startSitdPointer start sitd pointer. * @param endSitdPointer end sitd pointer. * * @return transfer's result length. */ static uint32_t USB_HostEhciSitdArrayRelease(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_sitd_t *startSitdPointer, usb_host_ehci_sitd_t *endSitdPointer); /*! * @brief de-initialize sitd list. * 1. remove transfer; 2. remove sitd from frame list and release sitd; 3. transfer callback * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe pointer. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostEhciSitdArrayDeinit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer); #endif /* USB_HOST_CONFIG_EHCI_MAX_SITD */ #if ((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) /*! * @brief compute the frame index when inserting itd. * * @param ehciInstance ehci instance pointer. * @param lastLinkUframe last inserted micro-frame. * @param startUframe start micro-frame. * @param uframeInterval micro-frame interval. * * @return frame index */ static uint32_t USB_HostEhciGetItdLinkFrame(usb_host_ehci_instance_t *ehciInstance, uint32_t lastLinkUframe, uint16_t startUframe, uint16_t uframeInterval); /*! * @brief initialize itd list for one transfer. * 1. initialize itd list; 2. insert itd to frame list. * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe pointer. * @param transfer transfer information. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostEhciItdArrayInit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer, usb_host_transfer_t *transfer); /*! * @brief release itd list. * * @param ehciInstance ehci instance pointer. * @param startItdPointer start itd pointer. * @param endItdPointer end itd pointer. * * @return transfer's result length. */ static uint32_t USB_HostEhciItdArrayRelease(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_itd_t *startItdPointer, usb_host_ehci_itd_t *endItdPointer); /*! * @brief de-initialize itd list. * 1. remove transfer; 2. remove itd from frame list and release itd; 3. transfer callback * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe pointer. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostEhciItdArrayDeinit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer); #endif /* USB_HOST_CONFIG_EHCI_MAX_ITD */ /*! * @brief open control or bulk pipe. * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe pointer. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostEhciOpenControlBulk(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer); /*! * @brief close control or bulk pipe. * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe pointer. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostEhciCloseControlBulk(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer); /*! * @brief open interrupt pipe. * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe pointer. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostEhciOpenInterrupt(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer); /*! * @brief close interrupt pipe. * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe pointer. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostEhciCloseInterrupt(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer); #if (((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) || \ ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD))) /*! * @brief open iso pipe. * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe pointer. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostEhciOpenIso(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer); /*! * @brief close iso pipe. * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe pointer. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostEhciCloseIso(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer); /*! * @brief allocate HS iso bandwidth when host work as high-speed host. * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe pointer. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostBandwidthHsHostAllocateIso(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer); #endif /*! * @brief reset ehci ip. * * @param ehciInstance ehci instance pointer. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostEhciResetIP(usb_host_ehci_instance_t *ehciInstance); /*! * @brief start ehci ip. * * @param ehciInstance ehci instance pointer. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostEhciStartIP(usb_host_ehci_instance_t *ehciInstance); /*! * @brief cancel pipe's transfers. * * @param ehciInstance ehci instance pointer. * @param ehciPipePointer ehci pipe pointer. * @param transfer the canceling transfer. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostEhciCancelPipe(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer, usb_host_transfer_t *transfer); /*! * @brief control ehci bus. * * @param ehciInstance ehci instance pointer. * @param bus_control control code. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostEhciControlBus(usb_host_ehci_instance_t *ehciInstance, uint8_t busControl); /*! * @brief ehci transaction done process function. * * @param ehciInstance ehci instance pointer. */ void USB_HostEhciTransactionDone(usb_host_ehci_instance_t *ehciInstance); /*! * @brief ehci port change interrupt process function. * * @param ehciInstance ehci instance pointer. */ static void USB_HostEhciPortChange(usb_host_ehci_instance_t *ehciInstance); /*! * @brief ehci timer0 interrupt process function. * cancel control/bulk transfer that time out. * * @param ehciInstance ehci instance pointer. */ static void USB_HostEhciTimer0(usb_host_ehci_instance_t *ehciInstance); #if ((defined(USB_HOST_CONFIG_LOW_POWER_MODE)) && (USB_HOST_CONFIG_LOW_POWER_MODE > 0U)) /*! * @brief ehci timer1 interrupt process function. * cancel control/bulk transfer that time out. * * @param ehciInstance ehci instance pointer. */ static void USB_HostEhciTimer1(usb_host_ehci_instance_t *ehciInstance); #endif #if ((defined USB_HOST_CONFIG_COMPLIANCE_TEST) && (USB_HOST_CONFIG_COMPLIANCE_TEST)) /*! * @brief suspend bus. * * @param ehciInstance ehci instance pointer. */ static void USB_HostEhciSuspendBus(usb_host_ehci_instance_t *ehciInstance); /*! * @brief resume bus. * * @param ehciInstance ehci instance pointer. */ static void USB_HostEhciResumeBus(usb_host_ehci_instance_t *ehciInstance); extern usb_status_t USB_HostStandardSetGetDescriptor(usb_host_device_instance_t *deviceInstance, usb_host_transfer_t *transfer, void *param); #endif /* USB_HOST_CONFIG_COMPLIANCE_TEST */ /******************************************************************************* * Variables ******************************************************************************/ /* EHCI controller driver instances. */ #if (USB_HOST_CONFIG_EHCI == 1U) USB_RAM_ADDRESS_ALIGNMENT(4096) USB_CONTROLLER_DATA static uint8_t s_UsbHostEhciFrameList1[USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE * 4]; #define USB_HOST_EHCI_FRAME_LIST_ARRAY \ { \ &s_UsbHostEhciFrameList1[0] \ } USB_RAM_ADDRESS_ALIGNMENT(64) USB_CONTROLLER_DATA static usb_host_ehci_data_t s_UsbHostEhciData1; #define USB_HOST_EHCI_DATA_ARRAY \ { \ &s_UsbHostEhciData1 \ } #elif(USB_HOST_CONFIG_EHCI == 2U) USB_RAM_ADDRESS_ALIGNMENT(4096) USB_CONTROLLER_DATA static uint8_t s_UsbHostEhciFrameList1[USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE * 4]; USB_RAM_ADDRESS_ALIGNMENT(4096) USB_CONTROLLER_DATA static uint8_t s_UsbHostEhciFrameList2[USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE * 4]; #define USB_HOST_EHCI_FRAME_LIST_ARRAY \ { \ &s_UsbHostEhciFrameList1[0], &s_UsbHostEhciFrameList2[0] \ } USB_RAM_ADDRESS_ALIGNMENT(64) USB_CONTROLLER_DATA static usb_host_ehci_data_t s_UsbHostEhciData1; USB_RAM_ADDRESS_ALIGNMENT(64) USB_CONTROLLER_DATA static usb_host_ehci_data_t s_UsbHostEhciData2; #define USB_HOST_EHCI_DATA_ARRAY \ { \ &s_UsbHostEhciData1, &s_UsbHostEhciData2 \ } #else #error "Please increase the instance count." #endif static uint8_t s_SlotMaxBandwidth[8] = {125, 125, 125, 125, 125, 125, 50, 0}; /******************************************************************************* * Code ******************************************************************************/ /*! * @brief EHCI NC get USB NC bass address. * * This function is used to get USB NC bass address. * * @param[in] controllerId EHCI controller ID; See the #usb_controller_index_t. * * @retval USB NC bass address. */ #if (defined(USB_HOST_CONFIG_LOW_POWER_MODE) && (USB_HOST_CONFIG_LOW_POWER_MODE > 0U)) #if (defined(FSL_FEATURE_SOC_USBNC_COUNT) && (FSL_FEATURE_SOC_USBNC_COUNT > 0U)) void *USB_EhciNCGetBase(uint8_t controllerId) { void *usbNCBase = NULL; #if ((defined FSL_FEATURE_SOC_USBNC_COUNT) && (FSL_FEATURE_SOC_USBNC_COUNT > 0U)) uint32_t instance; uint32_t newinstance = 0; uint32_t usbnc_base_temp[] = USBNC_BASE_ADDRS; uint32_t usbnc_base[] = USBNC_BASE_ADDRS; if (controllerId < kUSB_ControllerEhci0) { return NULL; } controllerId = controllerId - kUSB_ControllerEhci0; for (instance = 0; instance < (sizeof(usbnc_base_temp) / sizeof(usbnc_base_temp[0])); instance++) { if (usbnc_base_temp[instance]) { usbnc_base[newinstance++] = usbnc_base_temp[instance]; } } if (controllerId > newinstance) { return NULL; } usbNCBase = (void *)usbnc_base[controllerId]; #endif return usbNCBase; } #endif #endif #if ((defined USB_HOST_CONFIG_COMPLIANCE_TEST) && (USB_HOST_CONFIG_COMPLIANCE_TEST)) usb_status_t USB_HostEhciTestSetMode(usb_host_ehci_instance_t *ehciInstance, uint32_t testMode) { uint32_t ehciPortSC; ehciPortSC = ehciInstance->ehciIpBase->PORTSC1; ehciPortSC &= ~((uint32_t)USBHS_PORTSC1_PTC_MASK); /* clear test mode bits */ ehciPortSC |= (testMode << USBHS_PORTSC1_PTC_SHIFT); /* set test mode bits */ ehciInstance->ehciIpBase->PORTSC1 = ehciPortSC; return kStatus_USB_Success; } static void USB_HostEhciTestSuspendResume(usb_host_ehci_instance_t *ehciInstance) { uint8_t timeCount; timeCount = 15; /* 15s */ while (timeCount--) { USB_HostEhciDelay(ehciInstance->ehciIpBase, 1000U); } USB_HostEhciSuspendBus(ehciInstance); timeCount = 15; /* 15s */ while (timeCount--) { USB_HostEhciDelay(ehciInstance->ehciIpBase, 1000U); } USB_HostEhciResumeBus(ehciInstance); } static void USB_HostEhciTestCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status) { USB_HostFreeTransfer(param, transfer); } static void USB_HostEhciTestSingleStepGetDeviceDesc(usb_host_ehci_instance_t *ehciInstance, usb_device_handle deviceHandle) { usb_host_process_descriptor_param_t getDescriptorParam; usb_host_device_instance_t *deviceInstance = (usb_host_device_instance_t *)deviceHandle; usb_host_transfer_t *transfer; uint8_t timeCount; /* disable periodic shedule */ USB_HostEhciStopPeriodic(ehciInstance); timeCount = 15; /* 15s */ while (timeCount--) { USB_HostEhciDelay(ehciInstance->ehciIpBase, 1000U); } /* malloc one transfer */ if (USB_HostMallocTransfer(ehciInstance->hostHandle, &transfer) != kStatus_USB_Success) { #ifdef HOST_ECHO usb_echo("allocate transfer error\r\n"); #endif return; } getDescriptorParam.descriptorLength = sizeof(usb_descriptor_device_t); getDescriptorParam.descriptorLength = 18; getDescriptorParam.descriptorBuffer = (uint8_t *)&deviceInstance->deviceDescriptor; getDescriptorParam.descriptorType = USB_DESCRIPTOR_TYPE_DEVICE; getDescriptorParam.descriptorIndex = 0; getDescriptorParam.languageId = 0; transfer->callbackFn = USB_HostEhciTestCallback; transfer->callbackParam = ehciInstance->hostHandle; transfer->setupPacket->bmRequestType = USB_REQUEST_TYPE_DIR_IN; transfer->setupPacket->bRequest = USB_REQUEST_STANDARD_GET_DESCRIPTOR; transfer->setupPacket->wIndex = 0; transfer->setupPacket->wLength = 0; transfer->setupPacket->wValue = 0; USB_HostStandardSetGetDescriptor(deviceInstance, transfer, &getDescriptorParam); } static usb_status_t USB_HostEhciSingleStepQtdListInit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer, usb_host_transfer_t *transfer, uint8_t setupPhase) { volatile usb_host_ehci_qh_t *vltQhPointer; usb_host_ehci_qtd_t *qtdPointer = NULL; volatile uint32_t *entryPointer; uint32_t qtdNumber; uint32_t dataLength; uint32_t dataAddress; uint8_t index; /* compute the qtd number */ qtdNumber = 1; vltQhPointer = (volatile usb_host_ehci_qh_t *)ehciPipePointer->ehciQh; /* get qtd list */ USB_HostEhciLock(); if (qtdNumber <= ehciInstance->ehciQtdNumber) { ehciInstance->ehciQtdNumber -= qtdNumber; qtdPointer = NULL; do { if (qtdPointer != NULL) { qtdPointer->nextQtdPointer = (uint32_t)ehciInstance->ehciQtdHead; } qtdPointer = ehciInstance->ehciQtdHead; ehciInstance->ehciQtdHead = (usb_host_ehci_qtd_t *)qtdPointer->nextQtdPointer; qtdPointer->nextQtdPointer = 0; } while (--qtdNumber); } else { USB_HostEhciUnlock(); return kStatus_USB_Error; } USB_HostEhciUnlock(); /* int qTD */ if (setupPhase == 1) /* setup transaction qtd init */ { qtdPointer->alternateNextQtdPointer = EHCI_HOST_T_INVALID_VALUE; /* dt: need set; ioc: 0; C_Page: 0; PID Code: SETUP; Status: Active */ qtdPointer->transferResults[0] = qtdPointer->transferResults[1] = 0; qtdPointer->transferResults[0] = ((0x00000000 << EHCI_HOST_QTD_DT_SHIFT) | (8 << EHCI_HOST_QTD_TOTAL_BYTES_SHIFT) | (EHCI_HOST_PID_SETUP << EHCI_HOST_QTD_PID_CODE_SHIFT) | (EHCI_HOST_QTD_STATUS_ACTIVE_MASK)); dataAddress = (uint32_t)(transfer->setupPacket); qtdPointer->transferResults[1] = dataAddress; /* current offset is set too */ /* set buffer pointer no matter data length */ for (index = 0; index < 4; ++index) { qtdPointer->bufferPointers[index] = ((dataAddress + (index + 1) * 4 * 1024) & 0xFFFFF000); } } else if (setupPhase == 2) /* data transaction qtd */ { dataLength = transfer->transferLength; if (dataLength != 0) { qtdPointer->alternateNextQtdPointer = EHCI_HOST_T_INVALID_VALUE; /* dt: need set; ioc: 0; C_Page: 0; PID Code: IN/OUT; Status: Active */ qtdPointer->transferResults[0] = qtdPointer->transferResults[1] = 0; qtdPointer->transferResults[0] = ((0x00000001U << EHCI_HOST_QTD_DT_SHIFT) | (dataLength << EHCI_HOST_QTD_TOTAL_BYTES_SHIFT) | (EHCI_HOST_PID_IN << EHCI_HOST_QTD_PID_CODE_SHIFT) | (EHCI_HOST_QTD_STATUS_ACTIVE_MASK)); dataAddress = (uint32_t)(transfer->transferBuffer); qtdPointer->transferResults[1] = dataAddress; /* current offset is set too */ /* set buffer pointer no matter data length */ for (index = 0; index < 4; ++index) { qtdPointer->bufferPointers[index] = ((dataAddress + (index + 1) * 4 * 1024) & 0xFFFFF000); } } } else if (setupPhase == 3) { /* status transaction qtd */ qtdPointer->alternateNextQtdPointer = EHCI_HOST_T_INVALID_VALUE; /* dt: dont care; ioc: 1; C_Page: 0; PID Code: IN/OUT; Status: Active */ qtdPointer->transferResults[0] = qtdPointer->transferResults[1] = 0; qtdPointer->transferResults[0] = ((0x00000001U << EHCI_HOST_QTD_DT_SHIFT) | (EHCI_HOST_PID_OUT << EHCI_HOST_QTD_PID_CODE_SHIFT) | (EHCI_HOST_QTD_IOC_MASK) | (EHCI_HOST_QTD_STATUS_ACTIVE_MASK)); qtdPointer->nextQtdPointer |= EHCI_HOST_T_INVALID_VALUE; } qtdPointer->nextQtdPointer |= EHCI_HOST_T_INVALID_VALUE; qtdPointer->transferResults[0] |= EHCI_HOST_QTD_IOC_MASK; /* set IOC */ /* save qtd to transfer */ transfer->union1.unitHead = (uint32_t)qtdPointer; transfer->union2.unitTail = (uint32_t)qtdPointer; /* link transfer to qh */ transfer->next = NULL; if (vltQhPointer->ehciTransferHead == NULL) { transfer->next = NULL; vltQhPointer->ehciTransferHead = vltQhPointer->ehciTransferTail = transfer; } else { transfer->next = NULL; vltQhPointer->ehciTransferTail->next = transfer; vltQhPointer->ehciTransferTail = transfer; } USB_HostEhciLock(); /* link qtd to qh (link to end) */ entryPointer = &(vltQhPointer->nextQtdPointer); dataAddress = *entryPointer; /* dataAddress variable means entry value here */ while ((dataAddress) && (!(dataAddress & EHCI_HOST_T_INVALID_VALUE))) { entryPointer = (volatile uint32_t *)dataAddress; dataAddress = *entryPointer; } *entryPointer = (uint32_t)qtdPointer; USB_HostEhciUnlock(); USB_HostEhciStartAsync(ehciInstance); return kStatus_USB_Success; } static void USB_HostEhciTestSingleStepGetDeviceDescData(usb_host_ehci_instance_t *ehciInstance, usb_device_handle deviceHandle) { static uint8_t buffer[USB_HOST_EHCI_TEST_DESCRIPTOR_LENGTH]; usb_host_device_instance_t *deviceInstance = (usb_host_device_instance_t *)deviceHandle; usb_host_transfer_t *transfer; uint8_t timeCount; USB_HostEhciStopPeriodic(ehciInstance); if (USB_HostMallocTransfer(ehciInstance->hostHandle, &transfer) != kStatus_USB_Success) { return; } transfer->callbackFn = USB_HostEhciTestCallback; transfer->callbackParam = ehciInstance->hostHandle; transfer->setupPacket->bmRequestType = USB_REQUEST_TYPE_DIR_IN; transfer->setupPacket->bRequest = USB_REQUEST_STANDARD_GET_DESCRIPTOR; transfer->setupPacket->wLength = USB_SHORT_TO_LITTLE_ENDIAN(USB_HOST_EHCI_TEST_DESCRIPTOR_LENGTH); transfer->setupPacket->wValue = USB_SHORT_TO_LITTLE_ENDIAN((uint16_t)((uint16_t)USB_DESCRIPTOR_TYPE_DEVICE << 8)); transfer->setupPacket->wIndex = 0; USB_HostEhciSingleStepQtdListInit(ehciInstance, (usb_host_ehci_pipe_t *)(deviceInstance->controlPipe), transfer, 1); timeCount = 15; /* 15s */ while (timeCount--) { USB_HostEhciDelay(ehciInstance->ehciIpBase, 1000U); } if (USB_HostMallocTransfer(ehciInstance->hostHandle, &transfer) != kStatus_USB_Success) { return; } transfer->callbackFn = USB_HostEhciTestCallback; transfer->callbackParam = ehciInstance->hostHandle; transfer->transferBuffer = buffer; transfer->transferLength = USB_HOST_EHCI_TEST_DESCRIPTOR_LENGTH; USB_HostEhciSingleStepQtdListInit(ehciInstance, (usb_host_ehci_pipe_t *)(deviceInstance->controlPipe), transfer, 2); if (USB_HostMallocTransfer(ehciInstance->hostHandle, &transfer) != kStatus_USB_Success) { return; } transfer->callbackFn = USB_HostEhciTestCallback; transfer->callbackParam = ehciInstance->hostHandle; transfer->transferBuffer = NULL; transfer->transferLength = 0; USB_HostEhciSingleStepQtdListInit(ehciInstance, (usb_host_ehci_pipe_t *)(deviceInstance->controlPipe), transfer, 3); timeCount = 15; /* 15s */ while (timeCount--) { USB_HostEhciDelay(ehciInstance->ehciIpBase, 1000U); } usb_echo("test_single_step_get_dev_desc_data finished\r\n"); return; } void USB_HostEhciTestModeInit(usb_device_handle deviceHandle) { uint32_t productId; usb_host_device_instance_t *deviceInstance = (usb_host_device_instance_t *)deviceHandle; usb_host_ehci_instance_t *ehciInstance = (usb_host_ehci_instance_t *)(((usb_host_instance_t *)(deviceInstance->hostHandle))->controllerHandle); USB_HostHelperGetPeripheralInformation(deviceHandle, kUSB_HostGetDevicePID, &productId); usb_echo("usb host ehci test mode init product id:0x%x\r\n", productId); switch (productId) { case 0x0101U: USB_HostEhciTestSetMode(ehciInstance, USB_HOST_EHCI_PORTSC_PTC_SE0_NAK); break; case 0x0102U: USB_HostEhciTestSetMode(ehciInstance, USB_HOST_EHCI_PORTSC_PTC_J_STATE); break; case 0x0103U: USB_HostEhciTestSetMode(ehciInstance, USB_HOST_EHCI_PORTSC_PTC_K_STATE); break; case 0x0104U: USB_HostEhciTestSetMode(ehciInstance, USB_HOST_EHCI_PORTSC_PTC_PACKET); break; case 0x0105U: usb_echo("set test mode FORCE_ENALBE_HS\r\n"); USB_HostEhciTestSetMode(ehciInstance, USB_HOST_EHCI_PORTSC_PTC_FORCE_ENABLE_HS); break; case 0x0106U: USB_HostEhciTestSuspendResume(ehciInstance); break; case 0x0107U: usb_echo("start test SINGLE_STEP_GET_DEV_DESC\r\n"); USB_HostEhciTestSingleStepGetDeviceDesc(ehciInstance, deviceHandle); break; case 0x0108U: usb_echo("start test SINGLE_STEP_GET_DEV_DESC_DATA\r\n"); USB_HostEhciTestSingleStepGetDeviceDescData(ehciInstance, deviceHandle); break; default: break; } return; } static void USB_HostEhciSuspendBus(usb_host_ehci_instance_t *ehciInstance) { uint32_t ehciPortSC; USB_HostEhciLock(); ehciPortSC = ehciInstance->ehciIpBase->PORTSC1; if (ehciPortSC & USBHS_PORTSC1_PE_MASK) { ehciPortSC = ehciInstance->ehciIpBase->PORTSC1; ehciPortSC &= (uint32_t)(~EHCI_PORTSC1_W1_BITS); ehciInstance->ehciIpBase->PORTSC1 = (ehciPortSC | USBHS_PORTSC1_SUSP_MASK); } USB_HostEhciUnlock(); } static void USB_HostEhciResumeBus(usb_host_ehci_instance_t *ehciInstance) { uint32_t ehciPortSC; USB_HostEhciLock(); /* Resume port */ ehciPortSC = ehciInstance->ehciIpBase->PORTSC1; if (ehciPortSC & USBHS_PORTSC1_PE_MASK) { ehciPortSC &= (uint32_t)(~EHCI_PORTSC1_W1_BITS); ehciInstance->ehciIpBase->PORTSC1 = (ehciPortSC | USBHS_PORTSC1_FPR_MASK); } USB_HostEhciUnlock(); } #endif static uint32_t USB_HostBandwidthComputeTime(uint8_t speed, uint8_t pipeType, uint8_t direction, uint32_t dataLength) { uint32_t result = (3167 + ((1000 * dataLength) * 7U * 8U / 6U)) / 1000; if (pipeType == USB_ENDPOINT_ISOCHRONOUS) /* iso */ { if (speed == USB_SPEED_HIGH) { result = 38 * 8 * 2083 + 2083 * result + USB_HOST_EHCI_BANDWIDTH_DELAY; } else if (speed == USB_SPEED_FULL) { if (direction == USB_IN) { result = 7268000 + 83540 * result + USB_HOST_EHCI_BANDWIDTH_DELAY; } else { result = 6265000 + 83540 * result + USB_HOST_EHCI_BANDWIDTH_DELAY; } } else { } } else /* interrupt */ { if (speed == USB_SPEED_HIGH) { result = 55 * 8 * 2083 + 2083 * result + USB_HOST_EHCI_BANDWIDTH_DELAY; } else if (speed == USB_SPEED_FULL) { result = 9107000 + 83540 * result + USB_HOST_EHCI_BANDWIDTH_DELAY; } else if (speed == USB_SPEED_LOW) { if (direction == USB_IN) { result = 64060000 + 2000 * USB_HOST_EHCI_BANDWIDTH_HUB_LS_SETUP + 676670 * result + USB_HOST_EHCI_BANDWIDTH_DELAY; } else { result = 6265000 + 83540 * result + USB_HOST_EHCI_BANDWIDTH_DELAY; } } else { } } result /= 1000000; if (result == 0) { result = 1; } return result; } static void USB_HostBandwidthFslsHostComputeCurrent(usb_host_ehci_instance_t *ehciInstance, uint16_t frameIndex, uint16_t *frameBandwidth) { usb_host_ehci_pipe_t *ehciPipePointer; /* clear the bandwidth */ *frameBandwidth = 0; ehciPipePointer = ehciInstance->ehciRunningPipeList; while (ehciPipePointer != NULL) { /* only compute iso and interrupt pipe */ if ((ehciPipePointer->pipeCommon.pipeType == USB_ENDPOINT_ISOCHRONOUS) || (ehciPipePointer->pipeCommon.pipeType == USB_ENDPOINT_INTERRUPT)) { /* does pipe allocate bandwidth in frameIndex frame? note: interval is power of 2. */ if ((frameIndex >= ehciPipePointer->startFrame) && (!((uint32_t)(frameIndex - ehciPipePointer->startFrame) & (uint32_t)(ehciPipePointer->pipeCommon.interval - 1)))) { *frameBandwidth += ehciPipePointer->dataTime; } } ehciPipePointer = (usb_host_ehci_pipe_t *)ehciPipePointer->pipeCommon.next; } } static void USB_HostBandwidthHsHostComputeCurrentFsls(usb_host_ehci_instance_t *ehciInstance, uint32_t hubNumber, uint16_t frameIndex, uint8_t frameBandwidths[8]) { usb_host_ehci_pipe_t *ehciPipePointer; uint8_t index; uint32_t deviceInfo; for (index = 0; index < 8; ++index) { frameBandwidths[index] = 0; } ehciPipePointer = ehciInstance->ehciRunningPipeList; while (ehciPipePointer != NULL) { /* only compute iso and interrupt pipe */ if ((ehciPipePointer->pipeCommon.pipeType == USB_ENDPOINT_ISOCHRONOUS) || (ehciPipePointer->pipeCommon.pipeType == USB_ENDPOINT_INTERRUPT)) { /* compute FS/LS bandwidth that blong to same high-speed hub, because FS/LS bandwidth is allocated from * first parent high-speed hub */ USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceHSHubNumber, &deviceInfo); if (deviceInfo != hubNumber) { ehciPipePointer = (usb_host_ehci_pipe_t *)ehciPipePointer->pipeCommon.next; continue; } USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceSpeed, &deviceInfo); if (deviceInfo == USB_SPEED_HIGH) { ehciPipePointer = (usb_host_ehci_pipe_t *)ehciPipePointer->pipeCommon.next; continue; } /* does pipe allocate bandwidth in frameIndex frame? note: interval is power of 2. */ if ((frameIndex >= ehciPipePointer->startFrame) && (!((uint32_t)(frameIndex - ehciPipePointer->startFrame) & (uint32_t)(ehciPipePointer->pipeCommon.interval - 1)))) { if (ehciPipePointer->pipeCommon.pipeType == USB_ENDPOINT_ISOCHRONOUS) /* iso bandwidth is allocated once */ { frameBandwidths[ehciPipePointer->startUframe + 1] += ehciPipePointer->dataTime; } else /* iso bandwidth is allocated three times */ { frameBandwidths[ehciPipePointer->startUframe + 1] += ehciPipePointer->dataTime; frameBandwidths[ehciPipePointer->startUframe + 2] += ehciPipePointer->dataTime; frameBandwidths[ehciPipePointer->startUframe + 3] += ehciPipePointer->dataTime; } } } ehciPipePointer = (usb_host_ehci_pipe_t *)ehciPipePointer->pipeCommon.next; } for (index = 0; index < 7; ++index) /* */ { if (frameBandwidths[index] > s_SlotMaxBandwidth[index]) { frameBandwidths[index + 1] += (frameBandwidths[index] - s_SlotMaxBandwidth[index]); frameBandwidths[index] = s_SlotMaxBandwidth[index]; } } } static void USB_HostBandwidthHsHostComputeCurrentHsAll(usb_host_ehci_instance_t *ehciInstance, uint16_t frameIndex, uint8_t frameBandwidths[8]) { usb_host_ehci_pipe_t *ehciPipePointer; uint8_t index; uint32_t deviceInfo; uint16_t frameInterval; for (index = 0; index < 8; ++index) { frameBandwidths[index] = 0; } ehciPipePointer = ehciInstance->ehciRunningPipeList; while (ehciPipePointer != NULL) { /* only compute iso and interrupt pipe */ if ((ehciPipePointer->pipeCommon.pipeType == USB_ENDPOINT_ISOCHRONOUS) || (ehciPipePointer->pipeCommon.pipeType == USB_ENDPOINT_INTERRUPT)) { frameInterval = ehciPipePointer->pipeCommon.interval; USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceSpeed, &deviceInfo); if (deviceInfo == USB_SPEED_HIGH) /* high-speed data bandwidth */ { /* frameInterval means micro-frame here */ if (frameIndex >= ehciPipePointer->startFrame) { if ((frameInterval > 8) && (frameIndex * 8 - ehciPipePointer->startFrame * 8 >= ehciPipePointer->startUframe)) { if (!((uint32_t)(frameIndex * 8 - ehciPipePointer->startFrame * 8 - ehciPipePointer->startUframe) & (uint32_t)(frameInterval - 1))) { frameBandwidths[ehciPipePointer->startUframe] += ehciPipePointer->dataTime; } } else { for (index = ehciPipePointer->startUframe; index < 8; index += frameInterval) { frameBandwidths[index] += ehciPipePointer->dataTime; } } } } else /* full-speed split bandwidth */ { if ((frameIndex >= ehciPipePointer->startFrame) && (!((uint32_t)(frameIndex - ehciPipePointer->startFrame) & (uint32_t)(frameInterval - 1)))) { for (index = 0; index < 8; ++index) { if ((uint32_t)(ehciPipePointer->uframeSmask) & (uint32_t)(0x01 << index)) /* start-split micro-frames */ { frameBandwidths[index] += ehciPipePointer->startSplitTime; } if ((uint32_t)(ehciPipePointer->uframeCmask) & (uint32_t)(0x01 << index)) /* complete-split micro-frames */ { frameBandwidths[index] += ehciPipePointer->completeSplitTime; } } } } } ehciPipePointer = (usb_host_ehci_pipe_t *)ehciPipePointer->pipeCommon.next; } for (index = 0; index < 7; ++index) /* */ { if (frameBandwidths[index] > s_SlotMaxBandwidth[index]) { frameBandwidths[index + 1] += (frameBandwidths[index] - s_SlotMaxBandwidth[index]); frameBandwidths[index] = s_SlotMaxBandwidth[index]; } } } /*! * @brief allocate HS bandwidth when host work as high-speed host. * * @param ehciInstance ehci instance pointer. * @param uframeInterval micro-frame interval. * @param timeData time for allocating. * @param uframeIndexOut return start uframe index. * * @return kStatus_USB_Success or error codes. */ static usb_status_t USB_HostBandwidthHsHostAllocateHsCommon(usb_host_ehci_instance_t *ehciInstance, uint16_t uframeInterval, uint16_t timeData, uint16_t *uframeIndexOut) { uint16_t uframeIntervalIndex; uint16_t uframeIndex; uint16_t frameIndex; uint8_t frameTimes[8]; frameIndex = 0; USB_HostBandwidthHsHostComputeCurrentHsAll( ehciInstance, frameIndex, frameTimes); /* compute the allocated bandwidths in the frameIndex frame */ for (uframeIntervalIndex = 0; (uframeIntervalIndex < uframeInterval); ++uframeIntervalIndex) /* start micro-frame */ { /* for all the micro-frame in interval uframeInterval */ for (uframeIndex = uframeIntervalIndex; uframeIndex < (USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE * 8); uframeIndex += uframeInterval) { if (frameIndex != (uframeIndex >> 3)) { frameIndex = (uframeIndex >> 3); USB_HostBandwidthHsHostComputeCurrentHsAll( ehciInstance, frameIndex, frameTimes); /* compute the allocated bandwidths in the new frameIndex frame */ } if (frameTimes[uframeIndex & 0x0007] + timeData > s_SlotMaxBandwidth[(uframeIndex & 0x0007)]) /* micro-frame has enough idle bandwidth? */ { break; /* fail */ } } if (uframeIndex >= (USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE * 8)) /* success? */ { break; } } if (uframeIntervalIndex < uframeInterval) { *uframeIndexOut = (uframeIntervalIndex); return kStatus_USB_Success; } else { return kStatus_USB_Error; } } #if (((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) || \ ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD))) static usb_status_t USB_HostBandwidthHsHostAllocateIso(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer) { usb_status_t status; uint32_t deviceInfo; uint32_t hubNumber; uint16_t uframeIntervalIndex = 0; uint16_t frameIntervalIndex = 0; uint16_t frameIndex; uint16_t timeCompleteSplit; uint16_t timeStartSplit; uint32_t timeData; uint8_t SsCsNumber = 0; uint16_t frameInterval; uint8_t frameTimes[8]; uint8_t allocateOk = 1; uint8_t index; USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceSpeed, &deviceInfo); timeData = USB_HostBandwidthComputeTime( deviceInfo, USB_ENDPOINT_ISOCHRONOUS, ehciPipePointer->pipeCommon.direction, ehciPipePointer->pipeCommon.maxPacketSize * ehciPipePointer->pipeCommon.numberPerUframe); /* pipe is high-speed */ if (deviceInfo == USB_SPEED_HIGH) { uframeIntervalIndex = 0; status = USB_HostBandwidthHsHostAllocateHsCommon(ehciInstance, ehciPipePointer->uframeInterval, timeData, &uframeIntervalIndex); if (status == kStatus_USB_Success) { ehciPipePointer->startFrame = (uframeIntervalIndex / 8); ehciPipePointer->startUframe = (uframeIntervalIndex & 0x0007); ehciPipePointer->dataTime = timeData; return kStatus_USB_Success; } } else /* pipe is full-speed or low-speed */ { USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetHubThinkTime, &deviceInfo); /* deviceInfo variable means hub think time */ timeData += (deviceInfo * 7 / (6 * 12)); USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceHSHubNumber, &hubNumber); frameInterval = ehciPipePointer->pipeCommon.interval; /* compute start-split and complete-split bandwidth */ if (ehciPipePointer->pipeCommon.direction == USB_OUT) { timeStartSplit = USB_HostBandwidthComputeTime(USB_SPEED_HIGH, USB_ENDPOINT_ISOCHRONOUS, USB_OUT, ehciPipePointer->pipeCommon.maxPacketSize); timeCompleteSplit = 0; } else { timeStartSplit = USB_HostBandwidthComputeTime(USB_SPEED_HIGH, USB_ENDPOINT_ISOCHRONOUS, USB_IN, 1); timeCompleteSplit = USB_HostBandwidthComputeTime(USB_SPEED_HIGH, USB_ENDPOINT_ISOCHRONOUS, USB_IN, ehciPipePointer->pipeCommon.maxPacketSize); } /* note: bandwidth must put in one frame */ for (uframeIntervalIndex = 0; uframeIntervalIndex <= 5; ++uframeIntervalIndex) /* uframe interval */ { for (frameIntervalIndex = 0; frameIntervalIndex < frameInterval; ++frameIntervalIndex) /* frame interval */ { allocateOk = 1; for (frameIndex = frameIntervalIndex; frameIndex < USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE; frameIndex += frameInterval) /* check all the frames */ { /* compute start-split and complete-split number */ SsCsNumber = (ehciPipePointer->pipeCommon.maxPacketSize + 187) / 188; /* ss number for iso out; cs number for iso in */ if (ehciPipePointer->pipeCommon.direction == USB_OUT) /* ISO OUT */ { if (uframeIntervalIndex + SsCsNumber > 8) { allocateOk = 0; } } else { if (uframeIntervalIndex + 2 + SsCsNumber > 8) /* ISO IN: there are two micro-frame interval between start-split and complete-split */ { allocateOk = 0; } } if (allocateOk) { /* allocate start-split and complete-split bandwidth */ USB_HostBandwidthHsHostComputeCurrentHsAll(ehciInstance, frameIndex, frameTimes); if (ehciPipePointer->pipeCommon.direction == USB_OUT) /* ISO OUT */ { index = uframeIntervalIndex; for (; index < (uframeIntervalIndex + SsCsNumber); ++index) { if (frameTimes[index] + timeStartSplit > s_SlotMaxBandwidth[index]) { allocateOk = 0; break; } } } else /* ISO IN */ { index = uframeIntervalIndex; if (frameTimes[index] + timeStartSplit > s_SlotMaxBandwidth[index]) { allocateOk = 0; } if (allocateOk) { index = uframeIntervalIndex + 2; /* there are two micro-frames interval between start-split and complete-split */ for (; index < (uframeIntervalIndex + 2 + SsCsNumber); ++index) { if (frameTimes[index] + timeCompleteSplit > s_SlotMaxBandwidth[index]) { allocateOk = 0; break; } } } } } /* allocate data bandwidth */ if (allocateOk) { USB_HostBandwidthHsHostComputeCurrentFsls(ehciInstance, hubNumber, frameIndex, frameTimes); index = uframeIntervalIndex + 1; /* timeData bandwidth start position */ /* iso must occupy all the uframe bandwidth */ { deviceInfo = timeData; /* note: deviceInfo variable means bandwidth here */ while ((index < 8) && (deviceInfo > s_SlotMaxBandwidth[index])) { if (frameTimes[index] > 0) { allocateOk = 0; break; } else { deviceInfo -= s_SlotMaxBandwidth[index]; } ++index; } } } if (allocateOk) { /* data bandwidth can be put in the frame? */ index = uframeIntervalIndex + 1; /* timeData bandwidth start position */ frameTimes[index] += timeData; for (; index < 7; ++index) { if (frameTimes[index] > s_SlotMaxBandwidth[index]) { frameTimes[index + 1] += (frameTimes[index] - s_SlotMaxBandwidth[index]); frameTimes[index] = s_SlotMaxBandwidth[index]; } else { break; } } if (frameTimes[index] > s_SlotMaxBandwidth[index]) { allocateOk = 0; } } if (allocateOk) { break; } } if (allocateOk) { break; } } if (allocateOk) { break; } } if (allocateOk) { ehciPipePointer->startFrame = frameIntervalIndex; ehciPipePointer->startUframe = uframeIntervalIndex; ehciPipePointer->dataTime = timeData; ehciPipePointer->startSplitTime = timeStartSplit; ehciPipePointer->completeSplitTime = timeCompleteSplit; if (ehciPipePointer->pipeCommon.direction == USB_OUT) { index = uframeIntervalIndex; for (; index < (uframeIntervalIndex + SsCsNumber); ++index) { ehciPipePointer->uframeSmask = (uint32_t)ehciPipePointer->uframeSmask | (uint32_t)(0x01 << index); } } else { index = uframeIntervalIndex; ehciPipePointer->uframeSmask = (uint32_t)ehciPipePointer->uframeSmask | (uint32_t)(0x01 << index); index = uframeIntervalIndex + 2; for (; index < (uframeIntervalIndex + 2 + SsCsNumber); ++index) { ehciPipePointer->uframeCmask = (uint32_t)ehciPipePointer->uframeCmask | (uint32_t)(0x01 << index); } } return kStatus_USB_Success; } } return kStatus_USB_Error; } #endif static usb_status_t USB_HostBandwidthHsHostAllocateInterrupt(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer) { usb_status_t status; uint32_t deviceInfo; uint32_t hubNumber; uint16_t uframeIntervalIndex = 0; uint16_t frameIntervalIndex = 0; uint16_t frameIndex; uint16_t timeCompleteSplit; uint16_t timeStartSplit; uint32_t timeData; uint8_t SsCsNumber; uint16_t frameInterval; uint8_t frameTimes[8]; uint8_t allocateOk = 1; uint8_t index; USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceSpeed, &deviceInfo); timeData = USB_HostBandwidthComputeTime( deviceInfo, USB_ENDPOINT_INTERRUPT, ehciPipePointer->pipeCommon.direction, ehciPipePointer->pipeCommon.maxPacketSize * ehciPipePointer->pipeCommon.numberPerUframe); /* pipe is high-speed */ if (deviceInfo == USB_SPEED_HIGH) { uframeIntervalIndex = 0; status = USB_HostBandwidthHsHostAllocateHsCommon(ehciInstance, ehciPipePointer->uframeInterval, timeData, &uframeIntervalIndex); if (status == kStatus_USB_Success) { ehciPipePointer->startFrame = (uframeIntervalIndex / 8); ehciPipePointer->startUframe = (uframeIntervalIndex & 0x0007); /* for HS interrupt start transaction position */ if (ehciPipePointer->uframeInterval >= 8) { ehciPipePointer->uframeSmask = (0x01 << ehciPipePointer->startUframe); } else { ehciPipePointer->uframeSmask = 0x00u; for (index = ehciPipePointer->startUframe; index < 8; index += ehciPipePointer->uframeInterval) { ehciPipePointer->uframeSmask |= (0x01U << index); } } ehciPipePointer->dataTime = timeData; return kStatus_USB_Success; } } else /* pipe is full-speed or low-speed */ { USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetHubThinkTime, &deviceInfo); timeData += (deviceInfo * 7 / (6 * 12)); USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceHSHubNumber, &hubNumber); frameInterval = ehciPipePointer->pipeCommon.interval; SsCsNumber = 3; /* complete split number */ /* compute start-split and complete-split bandwidth */ if (ehciPipePointer->pipeCommon.direction == USB_OUT) { timeStartSplit = USB_HostBandwidthComputeTime(USB_SPEED_HIGH, USB_ENDPOINT_INTERRUPT, USB_OUT, ehciPipePointer->pipeCommon.maxPacketSize) + USB_HostBandwidthComputeTime(USB_SPEED_HIGH, USB_ENDPOINT_INTERRUPT, USB_OUT, 1); timeCompleteSplit = USB_HostBandwidthComputeTime(USB_SPEED_HIGH, USB_ENDPOINT_INTERRUPT, USB_OUT, 0); } else { timeStartSplit = USB_HostBandwidthComputeTime(USB_SPEED_HIGH, USB_ENDPOINT_INTERRUPT, USB_IN, 1); timeCompleteSplit = USB_HostBandwidthComputeTime(USB_SPEED_HIGH, USB_ENDPOINT_INTERRUPT, USB_IN, ehciPipePointer->pipeCommon.maxPacketSize) + USB_HostBandwidthComputeTime(USB_SPEED_HIGH, USB_ENDPOINT_INTERRUPT, USB_IN, 0); } /* note: bandwidth must put in one frame */ for (uframeIntervalIndex = 0; uframeIntervalIndex <= 4; ++uframeIntervalIndex) /* uframe interval */ { for (frameIntervalIndex = 0; frameIntervalIndex < frameInterval; ++frameIntervalIndex) /* frame interval */ { allocateOk = 1; for (frameIndex = frameIntervalIndex; frameIndex < USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE; frameIndex += frameInterval) /* check all the frames */ { /* allocate data bandwidth */ USB_HostBandwidthHsHostComputeCurrentFsls(ehciInstance, hubNumber, frameIndex, frameTimes); index = uframeIntervalIndex + 1; for (; index <= (uframeIntervalIndex + 3); ++index) /* data bandwidth number is 3. uframeIntervalIndex don't exceed 4, so index cannot exceed 7 */ { if (frameTimes[index] + timeData > s_SlotMaxBandwidth[index]) { allocateOk = 0; break; } } if (allocateOk) { USB_HostBandwidthHsHostComputeCurrentHsAll(ehciInstance, frameIndex, frameTimes); /* allocate start_split bandwidth */ if (frameTimes[uframeIntervalIndex] + timeStartSplit > s_SlotMaxBandwidth[uframeIntervalIndex]) { allocateOk = 0; } if (allocateOk) { /* allocate complete_split bandwidth */ index = uframeIntervalIndex + 2; /* complete-split number is normal 3. When uframeIntervalIndex is 4, complete-split number * is 2. */ for (; (index <= (uframeIntervalIndex + 1 + SsCsNumber)) && (index < 8); ++index) { if (frameTimes[index] + timeCompleteSplit > s_SlotMaxBandwidth[index]) { allocateOk = 0; break; } } } } if (!allocateOk) { break; /* allocate fail */ } } if (allocateOk) { break; } } if (allocateOk) { break; } } if (allocateOk) { ehciPipePointer->startFrame = frameIntervalIndex; ehciPipePointer->startUframe = uframeIntervalIndex; ehciPipePointer->uframeSmask = (0x01 << ehciPipePointer->startUframe); ehciPipePointer->uframeCmask = 0; index = uframeIntervalIndex + 2; for (; (index <= (uframeIntervalIndex + 1 + SsCsNumber)) && (index < 8); ++index) { ehciPipePointer->uframeCmask = (uint32_t)ehciPipePointer->uframeCmask | (uint32_t)(0x01 << index); } ehciPipePointer->dataTime = timeData; ehciPipePointer->startSplitTime = timeStartSplit; ehciPipePointer->completeSplitTime = timeCompleteSplit; return kStatus_USB_Success; } } return kStatus_USB_BandwidthFail; } static usb_status_t USB_HostBandwidthFslsHostAllocate(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer) { uint32_t FslsTime = 0; uint32_t speed = 0; uint16_t uframeIntervalIndex; uint16_t frameIndex; uint16_t frameInterval; uint16_t frameTime; USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetHubThinkTime, &FslsTime); FslsTime += (FslsTime * 7 / (6 * 12)); USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceSpeed, &speed); FslsTime = FslsTime + USB_HostBandwidthComputeTime(speed, ehciPipePointer->pipeCommon.pipeType, ehciPipePointer->pipeCommon.direction, ehciPipePointer->pipeCommon.maxPacketSize); frameInterval = ehciPipePointer->pipeCommon.interval; for (uframeIntervalIndex = 0; uframeIntervalIndex < ehciPipePointer->uframeInterval; ++uframeIntervalIndex) /* uframeIntervalIndex can exceed 8 */ { for (frameIndex = (uframeIntervalIndex >> 3); frameIndex < USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE; frameIndex += frameInterval) { USB_HostBandwidthFslsHostComputeCurrent(ehciInstance, frameIndex, &frameTime); if (frameTime + FslsTime > USB_HOST_EHCI_BANDWIDTH_FRAME_TOTOAL_TIME) { break; } } if (frameIndex >= USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE) { break; } } if (uframeIntervalIndex < ehciPipePointer->uframeInterval) { ehciPipePointer->startFrame = (uframeIntervalIndex >> 3); ehciPipePointer->startUframe = (uframeIntervalIndex & 0x0007); ehciPipePointer->uframeSmask = 0; /* useless */ ehciPipePointer->uframeCmask = 0; ehciPipePointer->dataTime = FslsTime; return kStatus_USB_Success; } return kStatus_USB_BandwidthFail; } static uint8_t USB_HostEhciGet2PowerValue(uint8_t value) { if ((value == 0) || (value == 1)) { return value; } if (value & 0xf0) { if (value & 0x80) { return 128; } else if (value & 0x40) { return 64; } else if (value & 0x20) { return 32; } else { return 16; } } else { if (value & 0x08) { return 8; } else if (value & 0x04) { return 4; } else if (value & 0x02) { return 2; } else { return 1; } } } static void USB_HostEhciZeroMem(uint32_t *buffer, uint32_t length) { /* note: the zero unit is uint32_t */ while (length--) { *buffer = 0; buffer++; } } static void USB_HostEhciDelay(USBHS_Type *ehciIpBase, uint32_t ms) { /* note: the max delay time cannot exceed half of max value (0x4000) */ int32_t sofStart; int32_t SofEnd; uint32_t distance; sofStart = (int32_t)(ehciIpBase->FRINDEX & EHCI_MAX_UFRAME_VALUE); do { SofEnd = (int32_t)(ehciIpBase->FRINDEX & EHCI_MAX_UFRAME_VALUE); distance = (uint32_t)(SofEnd - sofStart + EHCI_MAX_UFRAME_VALUE + 1); } while ((distance & EHCI_MAX_UFRAME_VALUE) < (ms * 8)); /* compute the distance between sofStart and SofEnd */ } static void USB_HostEhciStartAsync(usb_host_ehci_instance_t *ehciInstance) { uint32_t stateSync; if (!(ehciInstance->ehciIpBase->USBSTS & USBHS_USBSTS_AS_MASK)) { /* the status must be same when change USBCMD->ASE */ do { stateSync = ((ehciInstance->ehciIpBase->USBSTS & USBHS_USBSTS_AS_MASK) | (ehciInstance->ehciIpBase->USBCMD & USBHS_USBCMD_ASE_MASK)); } while ((stateSync == USBHS_USBSTS_AS_MASK) || (stateSync == USBHS_USBCMD_ASE_MASK)); ehciInstance->ehciIpBase->ASYNCLISTADDR = (uint32_t)(ehciInstance->shedFirstQh); ehciInstance->ehciIpBase->USBCMD |= USBHS_USBCMD_ASE_MASK; while (!(ehciInstance->ehciIpBase->USBSTS & USBHS_USBSTS_AS_MASK)) { } } } static void USB_HostEhciStopAsync(usb_host_ehci_instance_t *ehciInstance) { uint32_t stateSync; /* the status must be same when change USBCMD->ASE */ do { stateSync = ((ehciInstance->ehciIpBase->USBSTS & USBHS_USBSTS_AS_MASK) | (ehciInstance->ehciIpBase->USBCMD & USBHS_USBCMD_ASE_MASK)); } while ((stateSync == USBHS_USBSTS_AS_MASK) || (stateSync == USBHS_USBCMD_ASE_MASK)); ehciInstance->ehciIpBase->USBCMD &= (uint32_t)(~(uint32_t)USBHS_USBCMD_ASE_MASK); /* disable async schedule */ while (ehciInstance->ehciIpBase->USBSTS & USBHS_USBSTS_AS_MASK) { } } static void USB_HostEhciStartPeriodic(usb_host_ehci_instance_t *ehciInstance) { uint32_t stateSync; if (!(ehciInstance->ehciIpBase->USBSTS & USBHS_USBSTS_PS_MASK)) { /* the status must be same when change USBCMD->PSE */ do { stateSync = ((ehciInstance->ehciIpBase->USBSTS & USBHS_USBSTS_PS_MASK) | (ehciInstance->ehciIpBase->USBCMD & USBHS_USBCMD_PSE_MASK)); } while ((stateSync == USBHS_USBSTS_PS_MASK) || (stateSync == USBHS_USBCMD_PSE_MASK)); ehciInstance->ehciIpBase->PERIODICLISTBASE = (uint32_t)(ehciInstance->ehciFrameList); if (!(ehciInstance->ehciIpBase->USBCMD & USBHS_USBCMD_PSE_MASK)) { ehciInstance->ehciIpBase->USBCMD |= USBHS_USBCMD_PSE_MASK; /* start periodic schedule */ } while (!(ehciInstance->ehciIpBase->USBSTS & USBHS_USBSTS_PS_MASK)) { } } return; } static void USB_HostEhciStopPeriodic(usb_host_ehci_instance_t *ehciInstance) { uint32_t stateSync; /* the status must be same when change USBCMD->PSE */ do { stateSync = ((ehciInstance->ehciIpBase->USBSTS & USBHS_USBSTS_PS_MASK) | (ehciInstance->ehciIpBase->USBCMD & USBHS_USBCMD_PSE_MASK)); } while ((stateSync == USBHS_USBSTS_PS_MASK) || (stateSync == USBHS_USBCMD_PSE_MASK)); ehciInstance->ehciIpBase->USBCMD &= (~USBHS_USBCMD_PSE_MASK); /* stop periodic schedule */ while (ehciInstance->ehciIpBase->USBSTS & USBHS_USBSTS_PS_MASK) { } } static usb_status_t USB_HostEhciQhQtdListInit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer, usb_host_transfer_t *transfer) { volatile usb_host_ehci_qh_t *vltQhPointer; usb_host_ehci_qtd_t *qtdPointer = NULL; usb_host_ehci_qtd_t *BaseQtdPointer = NULL; volatile uint32_t *entryPointer; uint32_t qtdNumber; uint32_t dataLength; uint32_t dataAddress; uint32_t endAddress; uint8_t index; /* compute the qtd number */ if (ehciPipePointer->pipeCommon.pipeType == USB_ENDPOINT_CONTROL) { /* assume setup data don't exceed one qtd data size, one qtd can transfer least 16k data */ if (transfer->transferLength == 0) { qtdNumber = 2; } else { qtdNumber = 3; } } else { qtdNumber = (((transfer->transferLength) & 0xFFFFC000U) >> 14) + (((transfer->transferLength) & 0x00003FFF) ? 1 : 0); } vltQhPointer = (volatile usb_host_ehci_qh_t *)ehciPipePointer->ehciQh; /* get qtd list */ USB_HostEhciLock(); if (qtdNumber <= ehciInstance->ehciQtdNumber) { ehciInstance->ehciQtdNumber -= qtdNumber; BaseQtdPointer = ehciInstance->ehciQtdHead; qtdPointer = NULL; do { if (qtdPointer != NULL) { qtdPointer->nextQtdPointer = (uint32_t)ehciInstance->ehciQtdHead; } qtdPointer = ehciInstance->ehciQtdHead; ehciInstance->ehciQtdHead = (usb_host_ehci_qtd_t *)qtdPointer->nextQtdPointer; qtdPointer->nextQtdPointer = 0; } while (--qtdNumber); if (ehciInstance->ehciQtdNumber == 0) { ehciInstance->ehciQtdTail = NULL; } } else { USB_HostEhciUnlock(); return kStatus_USB_Error; } USB_HostEhciUnlock(); /* int qTD list */ if (ehciPipePointer->pipeCommon.pipeType == USB_ENDPOINT_CONTROL) { /* setup transaction qtd */ qtdPointer = BaseQtdPointer; qtdPointer->alternateNextQtdPointer = EHCI_HOST_T_INVALID_VALUE; /* dt: need set; ioc: 0; C_Page: 0; PID Code: SETUP; Status: Active */ qtdPointer->transferResults[0] = qtdPointer->transferResults[1] = 0; qtdPointer->transferResults[0] = ((0x00000000 << EHCI_HOST_QTD_DT_SHIFT) | (8 << EHCI_HOST_QTD_TOTAL_BYTES_SHIFT) | (EHCI_HOST_PID_SETUP << EHCI_HOST_QTD_PID_CODE_SHIFT) | (EHCI_HOST_QTD_STATUS_ACTIVE_MASK)); dataAddress = ((uint32_t)transfer->setupPacket); qtdPointer->transferResults[1] = dataAddress; /* current offset is set too */ /* set buffer pointer no matter data length */ for (index = 0; index < 4; ++index) { qtdPointer->bufferPointers[index] = ((dataAddress + (index + 1) * 4 * 1024) & 0xFFFFF000U); } /* data transaction qtd */ dataLength = transfer->transferLength; if (dataLength != 0) { qtdPointer = (usb_host_ehci_qtd_t *)(qtdPointer->nextQtdPointer); qtdPointer->alternateNextQtdPointer = EHCI_HOST_T_INVALID_VALUE; /* dt: need set; ioc: 0; C_Page: 0; PID Code: IN/OUT; Status: Active */ qtdPointer->transferResults[0] = qtdPointer->transferResults[1] = 0; if (transfer->direction == USB_OUT) { qtdPointer->transferResults[0] = ((0x00000001U << EHCI_HOST_QTD_DT_SHIFT) | (dataLength << EHCI_HOST_QTD_TOTAL_BYTES_SHIFT) | (EHCI_HOST_PID_OUT << EHCI_HOST_QTD_PID_CODE_SHIFT) | (EHCI_HOST_QTD_STATUS_ACTIVE_MASK)); } else { qtdPointer->transferResults[0] = ((0x00000001U << EHCI_HOST_QTD_DT_SHIFT) | (dataLength << EHCI_HOST_QTD_TOTAL_BYTES_SHIFT) | (EHCI_HOST_PID_IN << EHCI_HOST_QTD_PID_CODE_SHIFT) | (EHCI_HOST_QTD_STATUS_ACTIVE_MASK)); } dataAddress = (uint32_t)transfer->transferBuffer; qtdPointer->transferResults[1] = dataAddress; /* current offset is set too */ /* set buffer pointer no matter data length */ for (index = 0; index < 4; ++index) { qtdPointer->bufferPointers[index] = ((dataAddress + (index + 1) * 4 * 1024) & 0xFFFFF000U); } } /* status transaction qtd */ qtdPointer = (usb_host_ehci_qtd_t *)(qtdPointer->nextQtdPointer); qtdPointer->alternateNextQtdPointer = EHCI_HOST_T_INVALID_VALUE; /* dt: dont care; ioc: 1; C_Page: 0; PID Code: IN/OUT; Status: Active */ qtdPointer->transferResults[0] = qtdPointer->transferResults[1] = 0; if ((dataLength == 0) || (transfer->direction == USB_OUT)) { qtdPointer->transferResults[0] = ((0x00000001U << EHCI_HOST_QTD_DT_SHIFT) | (EHCI_HOST_PID_IN << EHCI_HOST_QTD_PID_CODE_SHIFT) | (EHCI_HOST_QTD_IOC_MASK) | (EHCI_HOST_QTD_STATUS_ACTIVE_MASK)); } else { qtdPointer->transferResults[0] = ((0x00000001U << EHCI_HOST_QTD_DT_SHIFT) | (EHCI_HOST_PID_OUT << EHCI_HOST_QTD_PID_CODE_SHIFT) | (EHCI_HOST_QTD_IOC_MASK) | (EHCI_HOST_QTD_STATUS_ACTIVE_MASK)); } qtdPointer->nextQtdPointer |= EHCI_HOST_T_INVALID_VALUE; } else { dataLength = transfer->transferLength; dataAddress = (uint32_t)transfer->transferBuffer; qtdPointer = BaseQtdPointer; while (1) { endAddress = dataAddress + (16 * 1024); if (endAddress > (uint32_t)(transfer->transferBuffer + transfer->transferLength)) { endAddress = (uint32_t)(transfer->transferBuffer + transfer->transferLength); } qtdPointer->alternateNextQtdPointer = EHCI_HOST_T_INVALID_VALUE; /* dt: set; ioc: 0; C_Page: 0; PID Code: IN/OUT; Status: Active */ qtdPointer->transferResults[0] = qtdPointer->transferResults[1] = 0; if (transfer->direction == USB_OUT) { qtdPointer->transferResults[0] = (((endAddress - dataAddress) << EHCI_HOST_QTD_TOTAL_BYTES_SHIFT) | ((uint32_t)ehciPipePointer->pipeCommon.nextdata01 << EHCI_HOST_QTD_DT_SHIFT) | (EHCI_HOST_QTD_CERR_MAX_VALUE << EHCI_HOST_QTD_CERR_SHIFT) | (EHCI_HOST_PID_OUT << EHCI_HOST_QTD_PID_CODE_SHIFT) | (EHCI_HOST_QTD_STATUS_ACTIVE_MASK)); } else { qtdPointer->transferResults[0] = (((endAddress - dataAddress) << EHCI_HOST_QTD_TOTAL_BYTES_SHIFT) | ((uint32_t)ehciPipePointer->pipeCommon.nextdata01 << EHCI_HOST_QTD_DT_SHIFT) | (EHCI_HOST_QTD_CERR_MAX_VALUE << EHCI_HOST_QTD_CERR_SHIFT) | (EHCI_HOST_PID_IN << EHCI_HOST_QTD_PID_CODE_SHIFT) | (EHCI_HOST_QTD_STATUS_ACTIVE_MASK)); } qtdPointer->transferResults[1] = dataAddress; /* current offset is set too */ /* set buffer pointer no matter data length */ for (index = 0; index < 4; ++index) { qtdPointer->bufferPointers[index] = ((dataAddress + (index + 1) * 4 * 1024) & 0xFFFFF000U); } dataAddress = endAddress; /* for next qtd */ if (qtdPointer->nextQtdPointer == 0) { break; } qtdPointer = (usb_host_ehci_qtd_t *)(qtdPointer->nextQtdPointer); } qtdPointer->nextQtdPointer |= EHCI_HOST_T_INVALID_VALUE; qtdPointer->transferResults[0] |= EHCI_HOST_QTD_IOC_MASK; /* last one set IOC */ } /* save qtd to transfer */ transfer->union1.unitHead = (uint32_t)BaseQtdPointer; transfer->union2.unitTail = (uint32_t)qtdPointer; /* link transfer to qh */ transfer->next = NULL; if (vltQhPointer->ehciTransferHead == NULL) { transfer->next = NULL; vltQhPointer->ehciTransferHead = vltQhPointer->ehciTransferTail = transfer; } else { transfer->next = NULL; vltQhPointer->ehciTransferTail->next = transfer; vltQhPointer->ehciTransferTail = transfer; } USB_HostEhciLock(); /* link qtd to qh (link to end) */ entryPointer = &(vltQhPointer->nextQtdPointer); dataAddress = *entryPointer; /* dataAddress variable means entry value here */ while ((dataAddress) && (!(dataAddress & EHCI_HOST_T_INVALID_VALUE))) { entryPointer = (volatile uint32_t *)dataAddress; dataAddress = *entryPointer; } *entryPointer = (uint32_t)BaseQtdPointer; USB_HostEhciUnlock(); USB_HostEhciStartAsync(ehciInstance); return kStatus_USB_Success; } static uint32_t USB_HostEhciQtdListRelease(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_qtd_t *ehciQtdStart, usb_host_ehci_qtd_t *ehciQtdEnd) { uint32_t length = 0; usb_host_ehci_qtd_t *qtdPointer; ehciQtdEnd->nextQtdPointer = 0; /* compute remaining length */ qtdPointer = ehciQtdStart; while (qtdPointer != ehciQtdEnd) { length += ((qtdPointer->transferResults[0] & EHCI_HOST_QTD_TOTAL_BYTES_MASK) >> EHCI_HOST_QTD_TOTAL_BYTES_SHIFT); qtdPointer = (usb_host_ehci_qtd_t *)qtdPointer->nextQtdPointer; } qtdPointer = ehciQtdEnd; length += ((qtdPointer->transferResults[0] & EHCI_HOST_QTD_TOTAL_BYTES_MASK) >> EHCI_HOST_QTD_TOTAL_BYTES_SHIFT); /* put releasing qtd to idle qtd list */ USB_HostEhciLock(); if (ehciInstance->ehciQtdNumber == 0) { ehciInstance->ehciQtdHead = ehciQtdStart; ehciInstance->ehciQtdTail = ehciQtdEnd; } else { ehciInstance->ehciQtdTail->nextQtdPointer = (uint32_t)ehciQtdStart; ehciInstance->ehciQtdTail = ehciQtdEnd; } while (ehciQtdStart != ehciQtdEnd) { ehciInstance->ehciQtdNumber++; ehciQtdStart = (usb_host_ehci_qtd_t *)ehciQtdStart->nextQtdPointer; } ehciInstance->ehciQtdNumber++; USB_HostEhciUnlock(); return length; } static usb_status_t USB_HostEhciQhQtdListDeinit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer) { volatile usb_host_ehci_qh_t *vltQhPointer; usb_host_transfer_t *transfer; usb_host_transfer_t *nextTransfer; uint8_t needStop = 0; vltQhPointer = (volatile usb_host_ehci_qh_t *)ehciPipePointer->ehciQh; USB_HostEhciLock(); /* this API is called from APP, the host task may occupy to access the same resource */ /* remove qtd from qh */ if ((!((uint32_t)vltQhPointer->nextQtdPointer & EHCI_HOST_T_INVALID_VALUE)) || (!((uint32_t)vltQhPointer->currentQtdPointer & EHCI_HOST_T_INVALID_VALUE))) { /* need stop async schedule */ if ((!(vltQhPointer->horizontalLinkPointer & EHCI_HOST_T_INVALID_VALUE)) && (ehciPipePointer->pipeCommon.pipeType != USB_ENDPOINT_INTERRUPT)) { needStop = 1; } if (needStop) { USB_HostEhciStopAsync(ehciInstance); } vltQhPointer->currentQtdPointer = EHCI_HOST_T_INVALID_VALUE; /* invalid current qtd */ vltQhPointer->nextQtdPointer = EHCI_HOST_T_INVALID_VALUE; /* invalid next qtd */ vltQhPointer->transferOverlayResults[0] &= (~EHCI_HOST_QTD_STATUS_MASK); /* clear error status */ if (needStop) { USB_HostEhciStartAsync(ehciInstance); } } /* remove transfer from the QH transfer list */ transfer = vltQhPointer->ehciTransferHead; vltQhPointer->ehciTransferHead = vltQhPointer->ehciTransferTail = NULL; USB_HostEhciUnlock(); /* release qtd and transfer callback*/ while (transfer != NULL) { nextTransfer = transfer->next; /* the transfer is released when call back */ transfer->transferSofar = USB_HostEhciQtdListRelease(ehciInstance, (usb_host_ehci_qtd_t *)(transfer->union1.unitHead), (usb_host_ehci_qtd_t *)(transfer->union2.unitTail)); transfer->transferSofar = (transfer->transferLength < transfer->transferSofar) ? 0 : (transfer->transferLength - transfer->transferSofar); transfer->callbackFn(transfer->callbackParam, transfer, kStatus_USB_TransferCancel); transfer = nextTransfer; } return kStatus_USB_Success; } static usb_status_t USB_HostEhciTransferQtdListDeinit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer, usb_host_transfer_t *transfer) { volatile usb_host_ehci_qh_t *vltQhPointer; usb_host_transfer_t *preSearchTransfer; uint32_t qhNextQtdValue; uint32_t qtdPointerEntry; uint32_t *searchQtdEntryPointer; vltQhPointer = (volatile usb_host_ehci_qh_t *)ehciPipePointer->ehciQh; USB_HostEhciLock(); /* this API is called from APP, the host task may occupy to access the same resource */ /* remove qtd from qh */ qhNextQtdValue = (uint32_t)vltQhPointer->currentQtdPointer; qtdPointerEntry = *((uint32_t *)qhNextQtdValue + 2); /* note: qtdPointerEntry means qtd status */ if ((qhNextQtdValue & EHCI_HOST_T_INVALID_VALUE) || (!(qtdPointerEntry & EHCI_HOST_QTD_STATUS_ACTIVE_MASK))) { qhNextQtdValue = (uint32_t)vltQhPointer->nextQtdPointer; } if (!(qhNextQtdValue & EHCI_HOST_T_INVALID_VALUE)) /* there is pending qtd in the qh */ { /* this qh don't schedule temporarily */ if (ehciPipePointer->pipeCommon.pipeType != USB_ENDPOINT_INTERRUPT) { USB_HostEhciStopAsync(ehciInstance); } vltQhPointer->currentQtdPointer |= EHCI_HOST_T_INVALID_VALUE; /* invalid current qtd */ vltQhPointer->nextQtdPointer |= EHCI_HOST_T_INVALID_VALUE; /* invalid next qtd */ if (ehciPipePointer->pipeCommon.pipeType != USB_ENDPOINT_INTERRUPT) { USB_HostEhciStartAsync(ehciInstance); } /* remove qtd from qh one by one */ qtdPointerEntry = transfer->union1.unitHead; while (1) { /* search qh's qtd list for qtdPointerEntry */ searchQtdEntryPointer = &qhNextQtdValue; while (!((*searchQtdEntryPointer) & EHCI_HOST_T_INVALID_VALUE)) { if ((*searchQtdEntryPointer) == qtdPointerEntry) { *searchQtdEntryPointer = *((uint32_t *)qtdPointerEntry); /* remove the qtd from qh */ break; } else { searchQtdEntryPointer = (uint32_t *)(*searchQtdEntryPointer); } } if (qtdPointerEntry == transfer->union2.unitTail) { break; } qtdPointerEntry = *((uint32_t *)qtdPointerEntry); } } /* remove transfer from the QH transfer list */ preSearchTransfer = vltQhPointer->ehciTransferHead; if (preSearchTransfer == transfer) { vltQhPointer->ehciTransferHead = preSearchTransfer->next; } else { while (preSearchTransfer != NULL) { if (preSearchTransfer->next == transfer) { preSearchTransfer->next = transfer->next; break; } else { preSearchTransfer = preSearchTransfer->next; } } } USB_HostEhciUnlock(); /* release qtd and callback */ transfer->transferSofar = USB_HostEhciQtdListRelease(ehciInstance, (usb_host_ehci_qtd_t *)(transfer->union1.unitHead), (usb_host_ehci_qtd_t *)(transfer->union2.unitTail)); transfer->transferSofar = (transfer->transferLength < transfer->transferSofar) ? 0 : (transfer->transferLength - transfer->transferSofar); transfer->callbackFn(transfer->callbackParam, transfer, kStatus_USB_TransferCancel); /* start this qh schedule */ vltQhPointer->transferOverlayResults[0] &= (~EHCI_HOST_QTD_STATUS_MASK); /* clear error status */ if ((qhNextQtdValue != 0) && (!(qhNextQtdValue & EHCI_HOST_T_INVALID_VALUE))) { vltQhPointer->nextQtdPointer = qhNextQtdValue; } return kStatus_USB_Success; } static usb_status_t USB_HostEhciQhInit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer) { usb_host_ehci_qh_t *qhPointer = NULL; uint32_t address, speed, portNumber, hubNumber; uint32_t controlBits1 = 0; uint32_t controlBits2 = 0; /* get qh */ USB_HostEhciLock(); if (ehciInstance->ehciQhList != NULL) { qhPointer = (usb_host_ehci_qh_t *)ehciInstance->ehciQhList; ehciInstance->ehciQhList = (usb_host_ehci_qh_t *)(ehciInstance->ehciQhList->horizontalLinkPointer & EHCI_HOST_POINTER_ADDRESS_MASK); } USB_HostEhciUnlock(); if (qhPointer == NULL) { #ifdef HOST_EHCO usb_echo("get qh error\r\n"); #endif return kStatus_USB_Error; } ehciPipePointer->ehciQh = (void *)qhPointer; /* initialize qh */ USB_HostEhciZeroMem((uint32_t *)qhPointer, sizeof(usb_host_ehci_qh_t) / 4); qhPointer->horizontalLinkPointer = EHCI_HOST_T_INVALID_VALUE; qhPointer->currentQtdPointer = EHCI_HOST_T_INVALID_VALUE; qhPointer->nextQtdPointer = EHCI_HOST_T_INVALID_VALUE; qhPointer->alternateNextQtdPointer = EHCI_HOST_T_INVALID_VALUE; qhPointer->ehciPipePointer = ehciPipePointer; qhPointer->timeOutLabel = 0; qhPointer->timeOutValue = USB_HOST_EHCI_CONTROL_BULK_TIME_OUT_VALUE; USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceSpeed, &speed); /* initialize staticEndpointStates[0] */ if (ehciPipePointer->pipeCommon.pipeType == USB_ENDPOINT_INTERRUPT) { /* Software should set the RL field to zero if the queue head is an interrupt endpoint. */ controlBits1 |= ((0U << EHCI_HOST_QH_RL_SHIFT) & EHCI_HOST_QH_RL_MASK); } else { if (ehciPipePointer->pipeCommon.nakCount >= 16) { controlBits1 |= ((15U << EHCI_HOST_QH_RL_SHIFT) & EHCI_HOST_QH_RL_MASK); } else { controlBits1 |= (((uint32_t)ehciPipePointer->pipeCommon.nakCount << EHCI_HOST_QH_RL_SHIFT) & EHCI_HOST_QH_RL_MASK); } } if (ehciPipePointer->pipeCommon.pipeType == USB_ENDPOINT_CONTROL) { if (speed != USB_SPEED_HIGH) { controlBits1 |= (1 << EHCI_HOST_QH_C_SHIFT); } controlBits1 |= (1 << EHCI_HOST_QH_DTC_SHIFT); } controlBits1 |= ((uint32_t)ehciPipePointer->pipeCommon.maxPacketSize << EHCI_HOST_QH_MAX_PACKET_LENGTH_SHIFT); controlBits1 |= (speed << EHCI_HOST_QH_EPS_SHIFT); controlBits1 |= ((uint32_t)ehciPipePointer->pipeCommon.endpointAddress << EHCI_HOST_QH_ENDPT_SHIFT); USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceAddress, &address); controlBits1 |= (address << EHCI_HOST_QH_DEVICE_ADDRESS_SHIFT); qhPointer->staticEndpointStates[0] = controlBits1; if (speed == USB_SPEED_HIGH) { controlBits2 |= ((uint32_t)ehciPipePointer->pipeCommon.numberPerUframe << EHCI_HOST_QH_MULT_SHIFT); } else { controlBits2 |= (0x00000001U << EHCI_HOST_QH_MULT_SHIFT); } /*initialize staticEndpointStates[1] */ if (speed != USB_SPEED_HIGH) { USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceHSHubNumber, &hubNumber); USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceHSHubPort, &portNumber); } else { USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceHubNumber, &hubNumber); USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDevicePortNumber, &portNumber); } controlBits2 |= (portNumber << EHCI_HOST_QH_PORT_NUMBER_SHIFT); controlBits2 |= (hubNumber << EHCI_HOST_QH_HUB_ADDR_SHIFT); controlBits2 |= ((uint32_t)ehciPipePointer->uframeCmask << EHCI_HOST_QH_UFRAME_CMASK_SHIFT); controlBits2 |= ((uint32_t)ehciPipePointer->uframeSmask << EHCI_HOST_QH_UFRAME_SMASK_SHIFT); qhPointer->staticEndpointStates[1] = controlBits2; return kStatus_USB_Success; } static usb_status_t USB_HostEhciQhDeinit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer) { usb_host_ehci_qh_t *qhPointer; qhPointer = (usb_host_ehci_qh_t *)ehciPipePointer->ehciQh; /* de-initialize qtd from qh */ USB_HostEhciQhQtdListDeinit(ehciInstance, ehciPipePointer); /* release QH */ USB_HostEhciLock(); qhPointer->horizontalLinkPointer = (uint32_t)ehciInstance->ehciQhList; ehciInstance->ehciQhList = qhPointer; USB_HostEhciUnlock(); return kStatus_USB_Success; } static void USB_HostEhciAddQhToFrame(usb_host_ehci_instance_t *ehciInstance, uint32_t entryPointerValue, uint16_t framePos, uint16_t uframeInterval) { volatile uint32_t *frameEntryPointer; uint32_t frameEntryValue; /* search for the inserting point by interval */ frameEntryPointer = (volatile uint32_t *)(&((uint32_t *)ehciInstance->ehciFrameList)[framePos]); while (frameEntryPointer) { frameEntryValue = *frameEntryPointer; if (frameEntryValue & EHCI_HOST_T_INVALID_VALUE) { /* insert into the end */ *((uint32_t *)entryPointerValue) = EHCI_HOST_T_INVALID_VALUE; *frameEntryPointer = (entryPointerValue | EHCI_HOST_POINTER_TYPE_QH); break; } if ((frameEntryValue & EHCI_HOST_POINTER_ADDRESS_MASK) == entryPointerValue) { return; /* has inserted */ } if (((frameEntryValue & EHCI_HOST_POINTER_TYPE_MASK) == EHCI_HOST_POINTER_TYPE_QH) && (((usb_host_ehci_qh_t *)(frameEntryValue & EHCI_HOST_POINTER_ADDRESS_MASK)) ->ehciPipePointer->uframeInterval <= uframeInterval)) { /* insert into this point */ *((uint32_t *)entryPointerValue) = frameEntryValue; *frameEntryPointer = (entryPointerValue | EHCI_HOST_POINTER_TYPE_QH); return; } else { frameEntryPointer = (volatile uint32_t *)(frameEntryValue & EHCI_HOST_POINTER_ADDRESS_MASK); } } } static void USB_HostEhciRemoveFromFrame(usb_host_ehci_instance_t *ehciInstance, uint32_t entryPointerValue, uint16_t framePos) { volatile uint32_t *frameEntryPointer; uint32_t frameEntryValue; /* search for the qh/itd/sitd entry */ frameEntryPointer = (volatile uint32_t *)(&((uint32_t *)ehciInstance->ehciFrameList)[framePos]); while (frameEntryPointer) { frameEntryValue = *frameEntryPointer; if (frameEntryValue & EHCI_HOST_T_INVALID_VALUE) { return; } if ((frameEntryValue & EHCI_HOST_POINTER_ADDRESS_MASK) == entryPointerValue) { /* remove the entry */ *frameEntryPointer = *((uint32_t *)entryPointerValue); break; } else { frameEntryPointer = (volatile uint32_t *)(frameEntryValue & EHCI_HOST_POINTER_ADDRESS_MASK); } } } #if ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD)) static void USB_HostEhciLinkSitd(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer, void *startEntryPointer) { usb_host_ehci_iso_t *isoPointer = (usb_host_ehci_iso_t *)ehciPipePointer->ehciQh; usb_host_ehci_sitd_t *sitdPointer; uint32_t distance; uint32_t frameInterval; int32_t shouldLinkFrame; int32_t currentFrame; frameInterval = (ehciPipePointer->uframeInterval >> 3); if (isoPointer->lastLinkFrame == 0xFFFF) /* first link */ { currentFrame = ((ehciInstance->ehciIpBase->FRINDEX & EHCI_MAX_UFRAME_VALUE) >> 3); currentFrame = ((uint32_t)(currentFrame + USB_HOST_EHCI_ISO_BOUNCE_FRAME_NUMBER) & (EHCI_MAX_UFRAME_VALUE >> 3)); /* add USB_HOST_EHCI_ISO_BOUNCE_FRAME_NUMBER */ /* frame should align with interval */ currentFrame -= ehciPipePointer->startFrame; currentFrame = ((uint32_t)(currentFrame + frameInterval - 1) & (~(frameInterval - 1))); /* frameInterval is power of 2 */ currentFrame += ehciPipePointer->startFrame; } else { shouldLinkFrame = isoPointer->lastLinkFrame + frameInterval; /* continuous next should link frame */ if (shouldLinkFrame > (int32_t)(EHCI_MAX_UFRAME_VALUE >> 3)) { shouldLinkFrame = shouldLinkFrame - ((EHCI_MAX_UFRAME_VALUE >> 3) + 1); } currentFrame = ((ehciInstance->ehciIpBase->FRINDEX & EHCI_MAX_UFRAME_VALUE) >> 3); distance = ((shouldLinkFrame - currentFrame + (EHCI_MAX_UFRAME_VALUE >> 3) + 1) & (EHCI_MAX_UFRAME_VALUE >> 3)); /* get the distance from shouldLinkFrame to currentFrame */ /* shouldLinkFrame has add frameInterval, think about the align with interval, so here add (frameInterval * * 2) */ if ((distance <= (USB_HOST_EHCI_ISO_BOUNCE_FRAME_NUMBER + frameInterval * 2)) && (distance > 0)) { currentFrame = shouldLinkFrame; } else /* re-link */ { currentFrame = ((uint32_t)(currentFrame + USB_HOST_EHCI_ISO_BOUNCE_FRAME_NUMBER) & (EHCI_MAX_UFRAME_VALUE >> 3)); if (currentFrame > (int32_t)(EHCI_MAX_UFRAME_VALUE >> 3)) { currentFrame = currentFrame - ((EHCI_MAX_UFRAME_VALUE >> 3) + 1); } /* frame should align with interval */ currentFrame -= ehciPipePointer->startFrame; currentFrame = ((uint32_t)(currentFrame + frameInterval - 1) & (~(frameInterval - 1))); currentFrame += ehciPipePointer->startFrame; } } if (currentFrame >= (int32_t)USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE) /* frame turn around */ { shouldLinkFrame = (currentFrame - USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE); /* shouldLinkFrame means inserted frame position */ } else { shouldLinkFrame = currentFrame; /* shouldLinkFrame means inserted frame position */ } sitdPointer = (usb_host_ehci_sitd_t *)startEntryPointer; while (sitdPointer) { sitdPointer->frameEntryIndex = shouldLinkFrame; /* add to frame list head */ sitdPointer->nextLinkPointer = ((uint32_t *)ehciInstance->ehciFrameList)[shouldLinkFrame]; ((uint32_t *)ehciInstance->ehciFrameList)[shouldLinkFrame] = ((uint32_t)sitdPointer | EHCI_HOST_POINTER_TYPE_SITD); if (sitdPointer->nextSitdIndex == 0xFF) /* 0xFF is invalid value */ { break; } sitdPointer = &(ehciInstance->ehciSitdIndexBase[sitdPointer->nextSitdIndex]); /* next sitd */ shouldLinkFrame += frameInterval; currentFrame += frameInterval; if (shouldLinkFrame >= (int32_t)USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE) { shouldLinkFrame = (shouldLinkFrame - USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE); } } if (currentFrame > (int32_t)(EHCI_MAX_UFRAME_VALUE >> 3)) { currentFrame = currentFrame - ((EHCI_MAX_UFRAME_VALUE >> 3) + 1); } isoPointer->lastLinkFrame = currentFrame; /* save the last link frame value */ } static usb_status_t USB_HostEhciSitdArrayInit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer, usb_host_transfer_t *transfer) { usb_host_ehci_iso_t *isoPointer; uint32_t sitdNumber = 0; usb_host_ehci_sitd_t *sitdPointer; uint32_t dataLength = 0; uint32_t sitdLength = 0; uint32_t dataBufferValue; uint32_t hubNumber; uint32_t portNumber; uint32_t address; uint32_t tmp; uint8_t index; USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceAddress, &address); sitdNumber = ((transfer->transferLength - 1 + (ehciPipePointer->pipeCommon.maxPacketSize)) / (ehciPipePointer->pipeCommon.maxPacketSize)); /* get sitd array */ tmp = ehciPipePointer - ehciInstance->ehciPipeIndexBase; /* pipe index */ /* USB_HostEhciLock(); */ if (ehciInstance->ehciSitdNumber >= sitdNumber) { sitdPointer = ehciInstance->ehciSitdList; transfer->union1.unitHead = (uint32_t)sitdPointer; for (index = 1; index < sitdNumber; ++index) { sitdPointer->nextSitdIndex = (((usb_host_ehci_sitd_t *)sitdPointer->nextLinkPointer) - ehciInstance->ehciSitdIndexBase); sitdPointer = (usb_host_ehci_sitd_t *)sitdPointer->nextLinkPointer; } sitdPointer->nextSitdIndex = 0xFF; ehciInstance->ehciSitdList = (usb_host_ehci_sitd_t *)sitdPointer->nextLinkPointer; ehciInstance->ehciSitdNumber -= sitdNumber; } else { /* USB_HostEhciUnlock(); */ return kStatus_USB_Error; } /* USB_HostEhciUnlock(); */ transfer->union2.unitTail = (uint32_t)sitdPointer; /* initialize sitd array */ USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceHubNumber, &hubNumber); USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDevicePortNumber, &portNumber); sitdPointer = (usb_host_ehci_sitd_t *)transfer->union1.unitHead; dataLength = transfer->transferLength; while (sitdNumber--) { USB_HostEhciZeroMem((uint32_t *)sitdPointer, 7); sitdLength = dataLength; if (sitdLength > ehciPipePointer->pipeCommon.maxPacketSize) { sitdLength = ehciPipePointer->pipeCommon.maxPacketSize; } dataBufferValue = (uint32_t)(transfer->transferBuffer + (transfer->transferLength - dataLength)); dataLength -= sitdLength; /* update left data length */ sitdPointer->transferResults[1] = dataBufferValue; sitdPointer->transferResults[2] = ((dataBufferValue + 4 * 1024) & 0xFFFFF000U); sitdPointer->endpointStates[0] = (((uint32_t)ehciPipePointer->pipeCommon.direction << EHCI_HOST_SITD_DIRECTION_SHIFT) | (portNumber << EHCI_HOST_SITD_PORT_NUMBER_SHIFT) | (hubNumber << EHCI_HOST_SITD_HUB_ADDR_SHIFT) | ((uint32_t)ehciPipePointer->pipeCommon.endpointAddress << EHCI_HOST_SITD_ENDPT_SHIFT) | (address << EHCI_HOST_SITD_DEVICE_ADDRESS_SHIFT)); sitdPointer->transferResults[0] = ((sitdLength << EHCI_HOST_SITD_TOTAL_BYTES_SHIFT) | (EHCI_HOST_SITD_STATUS_ACTIVE_MASK)); if (ehciInstance->firstDeviceSpeed == USB_SPEED_HIGH) { sitdPointer->endpointStates[1] = (((uint32_t)ehciPipePointer->uframeCmask << EHCI_HOST_SITD_CMASK_SHIFT) | ((uint32_t)ehciPipePointer->uframeSmask << EHCI_HOST_SITD_SMASK_SHIFT)); tmp = (sitdLength + 187) / 188; if (tmp > 1) { sitdPointer->transferResults[2] |= (0x01 << EHCI_HOST_SITD_TP_SHIFT); /* for iso split */ } else { sitdPointer->transferResults[2] |= (0x00 << EHCI_HOST_SITD_TP_SHIFT); /* for iso split */ } sitdPointer->transferResults[2] |= (tmp << EHCI_HOST_SITD_TCOUNT_SHIFT); /* for iso split */ } sitdPointer->backPointer = EHCI_HOST_T_INVALID_VALUE; sitdPointer = (ehciInstance->ehciSitdIndexBase + sitdPointer->nextSitdIndex); } sitdPointer = (usb_host_ehci_sitd_t *)transfer->union2.unitTail; sitdPointer->transferResults[0] |= (1U << EHCI_HOST_SITD_IOC_SHIFT); /* last set IOC */ /* link transfer to usb_host_ehci_iso_t transfer list */ isoPointer = (usb_host_ehci_iso_t *)ehciPipePointer->ehciQh; USB_HostEhciLock(); if (isoPointer->ehciTransferHead == NULL) { transfer->next = NULL; isoPointer->ehciTransferHead = isoPointer->ehciTransferTail = transfer; } else { transfer->next = NULL; isoPointer->ehciTransferTail->next = transfer; isoPointer->ehciTransferTail = transfer; } USB_HostEhciUnlock(); /* link itd to frame list (note: initialize frameEntryIndex)*/ USB_HostEhciLinkSitd(ehciInstance, ehciPipePointer, (void *)transfer->union1.unitHead); return kStatus_USB_Success; } static uint32_t USB_HostEhciSitdArrayRelease(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_sitd_t *startSitdPointer, usb_host_ehci_sitd_t *endSitdPointer) { usb_host_ehci_sitd_t *sitdPointer = startSitdPointer; uint32_t leftLength = 0; /* remove itd from frame list */ while (1) { /* record the transfer's result length */ leftLength += ((sitdPointer->transferResults[0] & EHCI_HOST_SITD_TOTAL_BYTES_MASK) >> EHCI_HOST_SITD_TOTAL_BYTES_SHIFT); USB_HostEhciRemoveFromFrame(ehciInstance, (uint32_t)sitdPointer, sitdPointer->frameEntryIndex); /* remove from the inserted frame list */ /* release itd */ /* USB_HostEhciLock(); */ sitdPointer->nextLinkPointer = (uint32_t)ehciInstance->ehciSitdList; ehciInstance->ehciSitdList = sitdPointer; ehciInstance->ehciSitdNumber++; /* USB_HostEhciUnlock(); */ if (sitdPointer == endSitdPointer) { break; } sitdPointer = &(ehciInstance->ehciSitdIndexBase[sitdPointer->nextSitdIndex]); } return leftLength; } static usb_status_t USB_HostEhciSitdArrayDeinit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer) { usb_host_ehci_iso_t *isoPointer = (usb_host_ehci_iso_t *)ehciPipePointer->ehciQh; usb_host_transfer_t *transfer; usb_host_transfer_t *nextTransfer; /* firstly remove the transfer (because host task may occupy to access the resource) */ USB_HostEhciLock(); transfer = isoPointer->ehciTransferHead; isoPointer->ehciTransferHead = isoPointer->ehciTransferTail = NULL; USB_HostEhciUnlock(); while (transfer != NULL) { nextTransfer = transfer->next; /* remove sitd from frame list and release itd */ transfer->transferSofar = transfer->transferLength - USB_HostEhciSitdArrayRelease(ehciInstance, (usb_host_ehci_sitd_t *)transfer->union1.unitHead, (usb_host_ehci_sitd_t *)transfer->union2.unitTail); /* transfer callback */ transfer->callbackFn(transfer->callbackParam, transfer, kStatus_USB_TransferCancel); /* next transfer */ transfer = nextTransfer; } return kStatus_USB_Success; } #endif /* USB_HOST_CONFIG_EHCI_MAX_SITD */ #if ((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) static uint32_t USB_HostEhciGetItdLinkFrame(usb_host_ehci_instance_t *ehciInstance, uint32_t lastLinkUframe, uint16_t startUframe, uint16_t uframeInterval) { int32_t shouldLinkUframe; int32_t currentUframe; int32_t distance; if (lastLinkUframe != 0xFFFF) { shouldLinkUframe = lastLinkUframe + uframeInterval; if (shouldLinkUframe > (int32_t)EHCI_MAX_UFRAME_VALUE) { shouldLinkUframe = shouldLinkUframe - (EHCI_MAX_UFRAME_VALUE + 1); } currentUframe = (ehciInstance->ehciIpBase->FRINDEX & EHCI_MAX_UFRAME_VALUE); distance = ((shouldLinkUframe - currentUframe + EHCI_MAX_UFRAME_VALUE + 1) & EHCI_MAX_UFRAME_VALUE); /* get the distance */ /* shouldLinkUframe has add uframeInterval, think about the align with interval, so here add (uframeInterval * * 2) */ if ((distance <= (int32_t)(USB_HOST_EHCI_ISO_BOUNCE_UFRAME_NUMBER + (uframeInterval * 2))) && (distance > 2)) { currentUframe = shouldLinkUframe; } else /* re-link */ { currentUframe = ((uint32_t)(currentUframe + USB_HOST_EHCI_ISO_BOUNCE_UFRAME_NUMBER) & EHCI_MAX_UFRAME_VALUE); if (currentUframe > (int32_t)EHCI_MAX_UFRAME_VALUE) { currentUframe = currentUframe - (EHCI_MAX_UFRAME_VALUE + 1); } /* uframe should align with interval */ currentUframe -= startUframe; currentUframe = ((uint32_t)(currentUframe + uframeInterval - 1) & (~((uint32_t)uframeInterval - 1))); /* uframeInterval is power of 2 */ currentUframe += startUframe; } } else { currentUframe = (ehciInstance->ehciIpBase->FRINDEX & EHCI_MAX_UFRAME_VALUE); currentUframe = ((uint32_t)(currentUframe + USB_HOST_EHCI_ISO_BOUNCE_UFRAME_NUMBER) & EHCI_MAX_UFRAME_VALUE); /* uframe should align with interval */ currentUframe -= startUframe; currentUframe = ((uint32_t)(currentUframe + uframeInterval - 1) & (~((uint32_t)uframeInterval - 1))); /* uframeInterval is power of 2 */ currentUframe += startUframe; } return currentUframe; } static usb_status_t USB_HostEhciItdArrayInit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer, usb_host_transfer_t *transfer) { usb_host_ehci_iso_t *isoPointer; usb_host_ehci_itd_t *itdPointer = NULL; usb_host_ehci_itd_t *tmpItdPointer; uint32_t dataLength; /* the remaining data for sending */ uint32_t transactionLength; /* the initializing transaction descriptor data length */ uint32_t itdBufferValue; uint32_t itdBufferBaseValue; /* for calculating PG value */ uint32_t address; uint32_t lastShouldLinkUframe; uint32_t linkUframe; uint32_t minDataPerItd = ehciPipePointer->pipeCommon.numberPerUframe * ehciPipePointer->pipeCommon.maxPacketSize; uint8_t maxItdNumber; uint8_t index = 0; isoPointer = (usb_host_ehci_iso_t *)ehciPipePointer->ehciQh; USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceAddress, &address); /* max needed itd number, the actual needed number may be less because micro-frame interval may be less than 8 */ maxItdNumber = ((transfer->transferLength - 1 + minDataPerItd) / minDataPerItd); if (ehciPipePointer->uframeInterval < 8) { maxItdNumber = ((maxItdNumber * ehciPipePointer->uframeInterval + 7) / 8) + 1; } if (maxItdNumber > ehciInstance->ehciItdNumber) { return kStatus_USB_Error; } /* link transfer to usb_host_ehci_iso_t transfer list */ transfer->next = NULL; /* USB_HostEhciLock(); */ if (isoPointer->ehciTransferHead == NULL) { isoPointer->ehciTransferHead = isoPointer->ehciTransferTail = transfer; } else { isoPointer->ehciTransferTail->next = transfer; isoPointer->ehciTransferTail = transfer; } /* USB_HostEhciUnlock(); */ dataLength = transfer->transferLength; transfer->union1.unitHead = (uint32_t)NULL; /* get the link micro-frame */ lastShouldLinkUframe = USB_HostEhciGetItdLinkFrame( ehciInstance, isoPointer->lastLinkFrame, (uint16_t)((ehciPipePointer->startFrame << 3) + ehciPipePointer->startUframe), ehciPipePointer->uframeInterval); if (lastShouldLinkUframe > EHCI_MAX_UFRAME_VALUE) { linkUframe = lastShouldLinkUframe - (EHCI_MAX_UFRAME_VALUE + 1); } else { linkUframe = lastShouldLinkUframe; } while (dataLength) { /* get one idle itd */ tmpItdPointer = ehciInstance->ehciItdList; ehciInstance->ehciItdList = (usb_host_ehci_itd_t *)tmpItdPointer->nextLinkPointer; ehciInstance->ehciItdNumber -= 1; if (tmpItdPointer == NULL) { return kStatus_USB_Error; /* this should not reach */ } tmpItdPointer->nextItdPointer = NULL; /* use the itd */ if (transfer->union1.unitHead == (uint32_t)NULL) /* first itd */ { transfer->union1.unitHead = (uint32_t)tmpItdPointer; } else /* link itd list */ { itdPointer->nextItdPointer = tmpItdPointer; } itdPointer = tmpItdPointer; /* itd has been set to all zero when releasing */ itdBufferBaseValue = itdBufferValue = (uint32_t)(transfer->transferBuffer + (transfer->transferLength - dataLength)); for (index = 0; index < 7; ++index) { itdPointer->bufferPointers[index] = ((itdBufferBaseValue + (index * 4 * 1024)) & 0xFFFFF000U); } /* initialize iTD common fields */ itdPointer->bufferPointers[0] |= (((uint32_t)ehciPipePointer->pipeCommon.endpointAddress << EHCI_HOST_ITD_ENDPT_SHIFT) | (address << EHCI_HOST_ITD_DEVICE_ADDRESS_SHIFT)); itdPointer->bufferPointers[1] |= (((uint32_t)ehciPipePointer->pipeCommon.direction << EHCI_HOST_ITD_DIRECTION_SHIFT) | ((uint32_t)ehciPipePointer->pipeCommon.maxPacketSize << EHCI_HOST_ITD_MAX_PACKET_SIZE_SHIFT)); itdPointer->bufferPointers[2] |= (ehciPipePointer->pipeCommon.numberPerUframe); /* initialize transaction descriptors */ for (index = (linkUframe & 0x0007); index < 8; index += ehciPipePointer->uframeInterval) { transactionLength = ((dataLength > minDataPerItd) ? minDataPerItd : dataLength); /* initialize the uframeIndex's transaction descriptor in itd */ itdPointer->transactions[index] = ((EHCI_HOST_ITD_STATUS_ACTIVE_MASK) | (transactionLength << EHCI_HOST_ITD_TRANSACTION_LEN_SHIFT) | ((((itdBufferValue & 0xFFFFF000U) - (itdBufferBaseValue & 0xFFFFF000U)) >> EHCI_HOST_ITD_BUFFER_POINTER_SHIFT) << EHCI_HOST_ITD_PG_SHIFT) | (itdBufferValue & EHCI_HOST_ITD_TRANSACTION_OFFSET_MASK)); dataLength -= transactionLength; itdBufferValue += transactionLength; if (dataLength <= 0) { break; } } } transfer->union2.unitTail = (uint32_t)itdPointer; itdPointer->transactions[index] |= (1 << EHCI_HOST_ITD_IOC_SHIFT); /* last set IOC */ /* link itd to frame list (note: initialize frameEntryIndex)*/ while (itdPointer) { itdPointer->frameEntryIndex = linkUframe; /* add to frame head */ itdPointer->nextLinkPointer = ((uint32_t *)ehciInstance->ehciFrameList)[linkUframe >> 3]; *(uint32_t *)((uint32_t *)ehciInstance->ehciFrameList)[linkUframe >> 3] = ((uint32_t)itdPointer | EHCI_HOST_POINTER_TYPE_ITD); itdPointer = itdPointer->nextItdPointer; if (itdPointer == NULL) { break; } linkUframe += ehciPipePointer->uframeInterval; lastShouldLinkUframe += ehciPipePointer->uframeInterval; if (linkUframe >= (USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE << 3)) { linkUframe = (linkUframe - (USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE << 3)); } } if (lastShouldLinkUframe > EHCI_MAX_UFRAME_VALUE) { lastShouldLinkUframe = lastShouldLinkUframe - (EHCI_MAX_UFRAME_VALUE + 1); } isoPointer->lastLinkFrame = lastShouldLinkUframe; return kStatus_USB_Success; } static uint32_t USB_HostEhciItdArrayRelease(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_itd_t *startItdPointer, usb_host_ehci_itd_t *endItdPointer) { usb_host_ehci_itd_t *itdPointer = startItdPointer; uint8_t index; uint32_t doneLength = 0; /* remove itd from frame list */ while (1) { /* record the transfer's result length */ for (index = 0; index < 8; ++index) { doneLength += ((itdPointer->transactions[index] & EHCI_HOST_ITD_TRANSACTION_LEN_MASK) >> EHCI_HOST_ITD_TRANSACTION_LEN_SHIFT); } USB_HostEhciRemoveFromFrame(ehciInstance, (uint32_t)itdPointer, itdPointer->frameEntryIndex); /* remove from the inserted frame list */ /* release itd */ /* USB_HostEhciLock(); */ USB_HostEhciZeroMem((uint32_t *)itdPointer, sizeof(usb_host_ehci_itd_t) >> 2); itdPointer->nextLinkPointer = (uint32_t)ehciInstance->ehciItdList; ehciInstance->ehciItdList = itdPointer; ehciInstance->ehciItdNumber++; /* USB_HostEhciUnlock(); */ if (itdPointer == endItdPointer) { break; } itdPointer = itdPointer->nextItdPointer; } return doneLength; } static usb_status_t USB_HostEhciItdArrayDeinit(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer) { usb_host_ehci_iso_t *isoPointer = (usb_host_ehci_iso_t *)ehciPipePointer->ehciQh; usb_host_transfer_t *transfer; usb_host_transfer_t *nextTransfer; uint32_t doneLength = 0; /* firstly remove the transfer (because host task may occupy to access the resource) */ USB_HostEhciLock(); transfer = isoPointer->ehciTransferHead; isoPointer->ehciTransferHead = isoPointer->ehciTransferTail = NULL; USB_HostEhciUnlock(); while (transfer != NULL) { nextTransfer = transfer->next; doneLength = 0; /* remove itd from frame list and release itd */ doneLength = USB_HostEhciItdArrayRelease(ehciInstance, (usb_host_ehci_itd_t *)transfer->union1.unitHead, (usb_host_ehci_itd_t *)transfer->union2.unitTail); /* transfer callback */ if (ehciPipePointer->pipeCommon.direction == USB_OUT) { doneLength = transfer->transferLength; } transfer->transferSofar = doneLength; transfer->callbackFn(transfer->callbackParam, transfer, kStatus_USB_TransferCancel); /* next transfer */ transfer = nextTransfer; } return kStatus_USB_Success; } #endif /* USB_HOST_CONFIG_EHCI_MAX_ITD */ static usb_status_t USB_HostEhciOpenControlBulk(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer) { usb_host_ehci_qh_t *qhPointer; if (USB_HostEhciQhInit(ehciInstance, ehciPipePointer) != kStatus_USB_Success) /* initialize control/bulk qh */ { return kStatus_USB_Error; } qhPointer = (usb_host_ehci_qh_t *)ehciPipePointer->ehciQh; /* add qh to async */ qhPointer->horizontalLinkPointer = ehciInstance->shedFirstQh->horizontalLinkPointer; ehciInstance->shedFirstQh->horizontalLinkPointer = ((uint32_t)qhPointer | EHCI_HOST_POINTER_TYPE_QH); return kStatus_USB_Success; } static usb_status_t USB_HostEhciCloseControlBulk(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer) { volatile usb_host_ehci_qh_t *vltPrevQhPointer; uint32_t horizontalLinkValue; /* remove qh from async schedule */ if ((ehciInstance->shedFirstQh->horizontalLinkPointer & EHCI_HOST_POINTER_ADDRESS_MASK) == (uint32_t)ehciPipePointer->ehciQh) /* the removing qh is the first qh in the async list */ { USB_HostEhciStopAsync(ehciInstance); ehciInstance->shedFirstQh->horizontalLinkPointer = ((usb_host_ehci_qh_t *)ehciPipePointer->ehciQh)->horizontalLinkPointer; USB_HostEhciStartAsync(ehciInstance); } else { /* search for the removing qh from the async list */ vltPrevQhPointer = ehciInstance->shedFirstQh; while (vltPrevQhPointer != NULL) { horizontalLinkValue = vltPrevQhPointer->horizontalLinkPointer; if ((horizontalLinkValue & EHCI_HOST_T_INVALID_VALUE) || ((horizontalLinkValue & EHCI_HOST_POINTER_ADDRESS_MASK) == (uint32_t)ehciPipePointer->ehciQh) || ((horizontalLinkValue & EHCI_HOST_POINTER_ADDRESS_MASK) == (uint32_t)ehciInstance->shedFirstQh)) { break; } vltPrevQhPointer = (volatile usb_host_ehci_qh_t *)(horizontalLinkValue & EHCI_HOST_POINTER_ADDRESS_MASK); } /* remove the qh from async list */ if ((vltPrevQhPointer != NULL) && (!(horizontalLinkValue & EHCI_HOST_T_INVALID_VALUE)) && ((horizontalLinkValue & EHCI_HOST_POINTER_ADDRESS_MASK) == (uint32_t)ehciPipePointer->ehciQh)) { USB_HostEhciStopAsync(ehciInstance); vltPrevQhPointer->horizontalLinkPointer = ((usb_host_ehci_qh_t *)ehciPipePointer->ehciQh)->horizontalLinkPointer; USB_HostEhciStartAsync(ehciInstance); } } ((usb_host_ehci_qh_t *)ehciPipePointer->ehciQh)->horizontalLinkPointer = EHCI_HOST_T_INVALID_VALUE; /* invalid next qh link */ return USB_HostEhciQhDeinit(ehciInstance, ehciPipePointer); /* de-initialize qh and release qh */ } static usb_status_t USB_HostEhciOpenInterrupt(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer) { usb_status_t status = kStatus_USB_Success; uint32_t frameIndex; /* allocate bandwidth */ if (ehciInstance->firstDeviceSpeed == USB_SPEED_HIGH) { status = USB_HostBandwidthHsHostAllocateInterrupt(ehciInstance, ehciPipePointer); /* host works as high-speed */ } else { status = USB_HostBandwidthFslsHostAllocate(ehciInstance, ehciPipePointer); /* host works as full-speed or low-speed */ } if (status != kStatus_USB_Success) { return status; } if (USB_HostEhciQhInit(ehciInstance, ehciPipePointer) != kStatus_USB_Success) { return kStatus_USB_Error; } /* insert QH to frame list */ for (frameIndex = ehciPipePointer->startFrame; frameIndex < USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE; frameIndex += (ehciPipePointer->uframeInterval / 8)) { USB_HostEhciAddQhToFrame(ehciInstance, (uint32_t)ehciPipePointer->ehciQh, frameIndex, ehciPipePointer->uframeInterval); } return kStatus_USB_Success; } static usb_status_t USB_HostEhciCloseInterrupt(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer) { uint32_t frameIndex; /* remove from frame list */ for (frameIndex = ehciPipePointer->startFrame; frameIndex < USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE; frameIndex += (ehciPipePointer->uframeInterval / 8)) { USB_HostEhciRemoveFromFrame(ehciInstance, (uint32_t)ehciPipePointer->ehciQh, frameIndex); } ((usb_host_ehci_qh_t *)ehciPipePointer->ehciQh)->horizontalLinkPointer |= EHCI_HOST_T_INVALID_VALUE; /* invalid next qh link */ return USB_HostEhciQhDeinit(ehciInstance, ehciPipePointer); /* de-initilaze qh and release qh */ } #if (((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) || \ ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD))) static usb_status_t USB_HostEhciOpenIso(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer) { usb_host_ehci_iso_t *isoPointer; usb_status_t status = kStatus_USB_Success; if (ehciInstance->firstDeviceSpeed == USB_SPEED_HIGH) { status = USB_HostBandwidthHsHostAllocateIso( ehciInstance, ehciPipePointer); /* allocate iso bandwidth when host works as high-speed */ } else { status = USB_HostBandwidthFslsHostAllocate( ehciInstance, ehciPipePointer); /* allocate iso bandwidth when host works as full-speed or low-speed */ } if (status != kStatus_USB_Success) { return status; } /* get usb_host_ehci_iso_t */ if (ehciInstance->ehciIsoList == NULL) { return kStatus_USB_Error; } USB_HostEhciLock(); isoPointer = ehciInstance->ehciIsoList; ehciInstance->ehciIsoList = ehciInstance->ehciIsoList->next; USB_HostEhciUnlock(); isoPointer->lastLinkFrame = 0xFFFF; ehciPipePointer->ehciQh = isoPointer; return status; } static usb_status_t USB_HostEhciCloseIso(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer) { usb_host_ehci_iso_t *isoPointer; uint32_t speed; isoPointer = (usb_host_ehci_iso_t *)ehciPipePointer->ehciQh; if (isoPointer->ehciTransferHead != NULL) { USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceSpeed, &speed); if (speed == USB_SPEED_HIGH) { #if ((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) USB_HostEhciItdArrayDeinit(ehciInstance, ehciPipePointer); /* de-initialize itd list and free them */ #endif } else { #if ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD)) USB_HostEhciSitdArrayDeinit(ehciInstance, ehciPipePointer); /* de-initialize sitd list and free them */ #endif } } /* release usb_host_ehci_iso_t */ USB_HostEhciLock(); isoPointer->next = ehciInstance->ehciIsoList; ehciInstance->ehciIsoList = isoPointer; USB_HostEhciUnlock(); return kStatus_USB_Success; } #endif static usb_status_t USB_HostEhciResetIP(usb_host_ehci_instance_t *ehciInstance) { /* reset controller */ ehciInstance->ehciIpBase->USBCMD = USBHS_USBCMD_RST_MASK; while (ehciInstance->ehciIpBase->USBCMD & USBHS_USBCMD_RST_MASK) { } /* set host mode */ #if (ENDIANNESS == USB_LITTLE_ENDIAN) ehciInstance->ehciIpBase->USBMODE |= 0x03; #else ehciInstance->ehciIpBase->USBMODE |= (0x03 | (0x01 << USBHS_USBMODE_ES_SHIFT)); #endif /* check frame list size */ if (!(ehciInstance->ehciIpBase->HCCPARAMS & USBHS_HCCPARAMS_PFL_MASK)) { #if ((USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE < 8) || (USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE > 1024)) return kStatus_USB_Error; #endif #if (USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE & (USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE - 1)) return kStatus_USB_Error; /* frame size must be 1024/512/256/128/64/32/16/8 */ #endif } return kStatus_USB_Success; } static usb_status_t USB_HostEhciStartIP(usb_host_ehci_instance_t *ehciInstance) { uint32_t tmp = 0; if (ehciInstance->ehciIpBase->HCSPARAMS & USBHS_HCSPARAMS_PPC_MASK) /* Ports have power port switches */ { /* only has one port */ tmp = ehciInstance->ehciIpBase->PORTSC1; tmp &= (~EHCI_PORTSC1_W1_BITS); ehciInstance->ehciIpBase->PORTSC1 = (tmp | USBHS_PORTSC1_PP_MASK); /* turn on port power */ } /* set frame list size */ if (ehciInstance->ehciIpBase->HCCPARAMS & USBHS_HCCPARAMS_PFL_MASK) { #if (USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE <= 64) ehciInstance->ehciIpBase->USBCMD |= (USBHS_USBCMD_FS2_MASK); #if (USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE == 64) ehciInstance->ehciIpBase->USBCMD |= (0x00 << USBHS_USBCMD_FS_SHIFT); #elif(USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE == 32) ehciInstance->ehciIpBase->USBCMD |= (0x01 << USBHS_USBCMD_FS_SHIFT); #elif(USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE == 16) ehciInstance->ehciIpBase->USBCMD |= (0x02 << USBHS_USBCMD_FS_SHIFT); #elif(USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE == 8) ehciInstance->ehciIpBase->USBCMD |= (0x03 << USBHS_USBCMD_FS_SHIFT); #endif #else #if (USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE == 1024) ehciInstance->ehciIpBase->USBCMD |= (0x00 << USBHS_USBCMD_FS_SHIFT); #elif(USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE == 512) ehciInstance->ehciIpBase->USBCMD |= (0x01 << USBHS_USBCMD_FS_SHIFT); #elif(USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE == 256) ehciInstance->ehciIpBase->USBCMD |= (0x02 << USBHS_USBCMD_FS_SHIFT); #elif(USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE == 128) ehciInstance->ehciIpBase->USBCMD |= (0x03 << USBHS_USBCMD_FS_SHIFT); #endif #endif } /* start the controller */ ehciInstance->ehciIpBase->USBCMD = USBHS_USBCMD_RS_MASK; /* set timer0 */ ehciInstance->ehciIpBase->GPTIMER0LD = (300 * 1000 - 1); /* 100ms */ /* enable interrupt (USB interrupt enable + USB error interrupt enable + port change detect enable + system error * enable + interrupt on async advance enable) + general purpos Timer 0 Interrupt enable */ ehciInstance->ehciIpBase->USBINTR |= (0x1000037); return kStatus_USB_Success; } static usb_status_t USB_HostEhciCancelPipe(usb_host_ehci_instance_t *ehciInstance, usb_host_ehci_pipe_t *ehciPipePointer, usb_host_transfer_t *transfer) { usb_host_ehci_qh_t *qhPointer; #if (((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) || \ ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD))) usb_host_ehci_iso_t *isoPointer; uint32_t speed; #endif uint8_t cancelPipe = 0; switch (ehciPipePointer->pipeCommon.pipeType) { case USB_ENDPOINT_BULK: case USB_ENDPOINT_CONTROL: case USB_ENDPOINT_INTERRUPT: qhPointer = (usb_host_ehci_qh_t *)ehciPipePointer->ehciQh; if (qhPointer->ehciTransferHead == NULL) /* there is no transfer to cancel */ { return kStatus_USB_Success; } if (transfer != NULL) { if ((qhPointer->ehciTransferHead == transfer) && (qhPointer->ehciTransferHead == qhPointer->ehciTransferTail)) /* only has this one transfer */ { cancelPipe = 1; } else { cancelPipe = 0; } } else { cancelPipe = 1; } if (cancelPipe == 1) /* cancel all pipe */ { USB_HostEhciQhQtdListDeinit(ehciInstance, ehciPipePointer); /* release all the qtd */ } else /* cancel one transfer */ { USB_HostEhciTransferQtdListDeinit(ehciInstance, ehciPipePointer, transfer); } break; #if (((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) || \ ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD))) case USB_ENDPOINT_ISOCHRONOUS: isoPointer = (usb_host_ehci_iso_t *)ehciPipePointer->ehciQh; if (isoPointer->ehciTransferHead == NULL) /* there is no transfer to cancel */ { return kStatus_USB_Success; } /* cancel all pipe, don't implement canceling transfer for iso */ USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceSpeed, &speed); if (speed == USB_SPEED_HIGH) { #if ((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) USB_HostEhciItdArrayDeinit(ehciInstance, ehciPipePointer); /* de-initialize itd */ #endif } else { #if ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD)) USB_HostEhciSitdArrayDeinit(ehciInstance, ehciPipePointer); /* de-initialize sitd */ #endif } break; #endif default: break; } return kStatus_USB_Success; } static usb_status_t USB_HostEhciControlBus(usb_host_ehci_instance_t *ehciInstance, uint8_t busControl) { usb_status_t status = kStatus_USB_Success; uint32_t portScRegister; switch (busControl) { case kUSB_HostBusReset: /* reset port */ portScRegister = ehciInstance->ehciIpBase->PORTSC1; portScRegister &= (~EHCI_PORTSC1_W1_BITS); ehciInstance->ehciIpBase->PORTSC1 = (portScRegister | USBHS_PORTSC1_PR_MASK); while (ehciInstance->ehciIpBase->PORTSC1 & USBHS_PORTSC1_PR_MASK) { } break; case kUSB_HostBusRestart: ehciInstance->deviceAttached = kEHCIDeviceDetached; ehciInstance->ehciIpBase->USBINTR |= (USBHS_USBINTR_PCE_MASK); /* enable ehci port change interrupt */ break; case kUSB_HostBusEnableAttach: /* enable device attach */ if (ehciInstance->deviceAttached == kEHCIDeviceDetached) { ehciInstance->ehciIpBase->USBINTR |= (USBHS_USBINTR_PCE_MASK); /* enable ehci port change interrupt */ } break; case kUSB_HostBusDisableAttach: /* disable device attach */ ehciInstance->ehciIpBase->USBINTR &= (~USBHS_USBINTR_PCE_MASK); /* disable ehci port change interrupt */ break; #if ((defined(USB_HOST_CONFIG_LOW_POWER_MODE)) && (USB_HOST_CONFIG_LOW_POWER_MODE > 0U)) case kUSB_HostBusSuspend: if (ehciInstance->ehciIpBase->PORTSC1 && USBHS_PORTSC1_CCS_MASK) { /* set timer1 */ ehciInstance->ehciIpBase->GPTIMER1LD = (1 * 1000); /* 1ms */ ehciInstance->ehciIpBase->GPTIMER1CTL |= (USBHS_GPTIMER0CTL_RUN_MASK | USBHS_GPTIMER0CTL_MODE_MASK | USBHS_GPTIMER0CTL_RST_MASK); USB_HostEhciStopAsync(ehciInstance); USB_HostEhciStopPeriodic(ehciInstance); while (ehciInstance->ehciIpBase->USBSTS & (USBHS_USBSTS_PS_MASK | USBHS_USBSTS_AS_MASK)) { __ASM("nop"); } ehciInstance->ehciIpBase->PORTSC1 &= ~USBHS_PORTSC1_WKCN_MASK; ehciInstance->ehciIpBase->PORTSC1 |= USBHS_PORTSC1_WKDS_MASK; ehciInstance->ehciIpBase->PORTSC1 |= (USBHS_PORTSC1_SUSP_MASK); /* Suspend the device */ ehciInstance->matchTick = 0U; ehciInstance->ehciIpBase->USBINTR |= (USBHS_USBINTR_TIE1_MASK); ehciInstance->busSuspendStatus = kBus_EhciStartSuspend; } else { status = kStatus_USB_Error; } break; case kUSB_HostBusResume: ehciInstance->ehciIpBase->PORTSC1 &= ~(USBHS_PORTSC1_SUSP_MASK); /* Clear Suspend bit */ ehciInstance->ehciIpBase->PORTSC1 &= ~USBHS_PORTSC1_PHCD_MASK; if (ehciInstance->deviceAttached != kEHCIDeviceDetached) { ehciInstance->busSuspendStatus = kBus_EhciStartResume; #if (defined(FSL_FEATURE_SOC_USBNC_COUNT) && (FSL_FEATURE_SOC_USBNC_COUNT > 0U)) ehciInstance->registerNcBase->USB_OTGn_CTRL &= ~USBNC_USB_OTGn_CTRL_WIE_MASK; #else ehciInstance->ehciIpBase->USBGENCTRL &= ~USBHS_USBGENCTRL_WU_IE_MASK; #endif ehciInstance->ehciIpBase->USBCMD |= (USBHS_USBCMD_RS_MASK); ehciInstance->ehciIpBase->PORTSC1 |= (USBHS_PORTSC1_FPR_MASK); /* Resume the device */ } else { status = kStatus_USB_Error; } break; #endif default: status = kStatus_USB_Error; break; } return status; } void USB_HostEhciTransactionDone(usb_host_ehci_instance_t *ehciInstance) { /* process async QH */ usb_host_ehci_pipe_t *ehciPipePointer; usb_host_ehci_pipe_t *ehciClearPipePointer = NULL; volatile usb_host_ehci_qh_t *vltQhPointer; volatile usb_host_ehci_qtd_t *vltQtdPointer; usb_host_transfer_t *transfer; usb_host_transfer_t *nextTransfer; uint32_t qtdStatus = 0; #if ((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) volatile usb_host_ehci_itd_t *vltItdPointer; uint8_t index = 0; #endif #if ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD)) volatile usb_host_ehci_sitd_t *vltSitdPointer; #endif #if (((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) || \ ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD))) usb_host_ehci_iso_t *isoPointer; uint32_t dataLength; uint32_t speed; #endif ehciPipePointer = ehciInstance->ehciRunningPipeList; /* check all the running pipes */ while (ehciPipePointer != NULL) { switch (ehciPipePointer->pipeCommon.pipeType) { case USB_ENDPOINT_BULK: case USB_ENDPOINT_INTERRUPT: case USB_ENDPOINT_CONTROL: vltQhPointer = (volatile usb_host_ehci_qh_t *)ehciPipePointer->ehciQh; /* pipe's qh */ transfer = vltQhPointer->ehciTransferHead; /* qh's transfer */ while (transfer != NULL) { nextTransfer = transfer->next; /* normal case */ vltQtdPointer = (volatile usb_host_ehci_qtd_t *)transfer->union2.unitTail; if ((vltQtdPointer->transferResults[0] & (EHCI_HOST_QTD_IOC_MASK)) && (!(vltQtdPointer->transferResults[0] & EHCI_HOST_QTD_STATUS_ACTIVE_MASK))) /* transfer is done */ { qtdStatus = (vltQtdPointer->transferResults[0] & EHCI_HOST_QTD_STATUS_ERROR_MASK); transfer->transferSofar = USB_HostEhciQtdListRelease(ehciInstance, (usb_host_ehci_qtd_t *)(transfer->union1.unitHead), (usb_host_ehci_qtd_t *)(transfer->union2.unitTail)); transfer->transferSofar = (transfer->transferLength < transfer->transferSofar) ? 0 : (transfer->transferLength - transfer->transferSofar); vltQhPointer->ehciTransferHead = transfer->next; vltQhPointer->timeOutLabel = 0; vltQhPointer->timeOutValue = USB_HOST_EHCI_CONTROL_BULK_TIME_OUT_VALUE; if (qtdStatus) /* has errors */ { if (!(vltQhPointer->transferOverlayResults[0] & EHCI_HOST_QTD_STATUS_ACTIVE_MASK)) { vltQhPointer->transferOverlayResults[0] &= (~EHCI_HOST_QTD_STATUS_MASK); /* clear error status */ } if (qtdStatus & EHCI_HOST_QH_STATUS_NOSTALL_ERROR_MASK) { transfer->callbackFn(transfer->callbackParam, transfer, kStatus_USB_TransferFailed); /* transfer fail */ } else { transfer->callbackFn(transfer->callbackParam, transfer, kStatus_USB_TransferStall); /* transfer stall */ } } else { if ((ehciPipePointer->pipeCommon.pipeType == USB_ENDPOINT_CONTROL) && (transfer->setupPacket->bRequest == USB_REQUEST_STANDARD_CLEAR_FEATURE) && (transfer->setupPacket->bmRequestType == USB_REQUEST_TYPE_RECIPIENT_ENDPOINT) && ((USB_SHORT_FROM_LITTLE_ENDIAN(transfer->setupPacket->wValue) & 0x00FFu) == USB_REQUEST_STANDARD_FEATURE_SELECTOR_ENDPOINT_HALT)) { ehciClearPipePointer = ehciInstance->ehciRunningPipeList; while (ehciClearPipePointer != NULL) { /* only compute bulk and interrupt pipe */ if (((ehciClearPipePointer->pipeCommon.endpointAddress | (ehciClearPipePointer->pipeCommon.direction << USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_SHIFT)) == (uint8_t)(USB_SHORT_FROM_LITTLE_ENDIAN(transfer->setupPacket->wIndex))) && (ehciClearPipePointer->pipeCommon.deviceHandle == ehciPipePointer->pipeCommon.deviceHandle)) { break; } ehciClearPipePointer = (usb_host_ehci_pipe_t *)ehciClearPipePointer->pipeCommon.next; } if ((ehciClearPipePointer != NULL) && ((ehciClearPipePointer->pipeCommon.pipeType == USB_ENDPOINT_INTERRUPT) || (ehciClearPipePointer->pipeCommon.pipeType == USB_ENDPOINT_BULK))) { ((volatile usb_host_ehci_qh_t *)(ehciClearPipePointer->ehciQh)) ->transferOverlayResults[0] &= (~EHCI_HOST_QTD_DT_MASK); } } transfer->callbackFn(transfer->callbackParam, transfer, kStatus_USB_Success); /* transfer success */ } } else if ((!(vltQhPointer->transferOverlayResults[0] & EHCI_HOST_QTD_STATUS_ACTIVE_MASK)) && (vltQhPointer->transferOverlayResults[0] & EHCI_HOST_QH_STATUS_ERROR_MASK)) /* there is error and transfer is done */ { qtdStatus = (vltQhPointer->transferOverlayResults[0] & EHCI_HOST_QH_STATUS_ERROR_MASK); vltQtdPointer = (volatile usb_host_ehci_qtd_t *)(vltQhPointer->currentQtdPointer); if (((uint32_t)vltQtdPointer & EHCI_HOST_T_INVALID_VALUE) || (vltQtdPointer == NULL)) /* the error status is unreasonable */ { vltQhPointer->transferOverlayResults[0] &= (~EHCI_HOST_QTD_STATUS_MASK); /* clear error status */ } else { /* remove qtd from qh */ while ((vltQtdPointer != NULL) && (!(vltQtdPointer->transferResults[0] & EHCI_HOST_QTD_IOC_MASK))) /* find the IOC qtd */ { vltQtdPointer = (volatile usb_host_ehci_qtd_t *)vltQtdPointer->nextQtdPointer; } vltQhPointer->nextQtdPointer = EHCI_HOST_T_INVALID_VALUE; vltQhPointer->currentQtdPointer = EHCI_HOST_T_INVALID_VALUE; vltQhPointer->transferOverlayResults[0] &= (~EHCI_HOST_QTD_STATUS_MASK); /* clear error status */ if (vltQtdPointer != NULL) { vltQhPointer->nextQtdPointer = vltQtdPointer->nextQtdPointer; } transfer->transferSofar = USB_HostEhciQtdListRelease( ehciInstance, (usb_host_ehci_qtd_t *)(transfer->union1.unitHead), (usb_host_ehci_qtd_t *)(transfer->union2.unitTail)); transfer->transferSofar = (transfer->transferLength < transfer->transferSofar) ? 0 : (transfer->transferLength - transfer->transferSofar); vltQhPointer->ehciTransferHead = transfer->next; vltQhPointer->timeOutLabel = 0; vltQhPointer->timeOutValue = USB_HOST_EHCI_CONTROL_BULK_TIME_OUT_VALUE; if (qtdStatus & EHCI_HOST_QH_STATUS_NOSTALL_ERROR_MASK) { transfer->callbackFn(transfer->callbackParam, transfer, kStatus_USB_TransferFailed); /* transfer fail */ } else { transfer->callbackFn(transfer->callbackParam, transfer, kStatus_USB_TransferStall); /* transfer stall */ } } } else { break; } transfer = nextTransfer; } break; #if (((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) || \ ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD))) case USB_ENDPOINT_ISOCHRONOUS: qtdStatus = 0; /* qtdStatus means break here, because there is only one break in while for misra */ isoPointer = (usb_host_ehci_iso_t *)ehciPipePointer->ehciQh; /* pipe's usb_host_ehci_iso_t */ transfer = isoPointer->ehciTransferHead; /* usb_host_ehci_iso_t's transfer */ while (transfer != NULL) { nextTransfer = transfer->next; USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceSpeed, &speed); if (speed == USB_SPEED_HIGH) { #if ((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) vltItdPointer = (volatile usb_host_ehci_itd_t *)(transfer->union2.unitTail); /* transfer's last itd */ for (index = 0; index < 8; ++index) { if (vltItdPointer->transactions[index] & EHCI_HOST_ITD_STATUS_ACTIVE_MASK) { break; } } if (index == 8) /* transfer is done */ { /* remove itd from frame list and release itd */ dataLength = USB_HostEhciItdArrayRelease(ehciInstance, (usb_host_ehci_itd_t *)transfer->union1.unitHead, (usb_host_ehci_itd_t *)transfer->union2.unitTail); transfer->transferSofar = dataLength; isoPointer->ehciTransferHead = transfer->next; transfer->callbackFn(transfer->callbackParam, transfer, kStatus_USB_Success); /* transfer callback success */ /* TODO: iso callback error */ } else { qtdStatus = 1; /* break */ } #endif } else { #if ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD)) vltSitdPointer = (volatile usb_host_ehci_sitd_t *)(transfer->union2.unitTail); /* transfer's last sitd */ if (!(vltSitdPointer->transferResults[0] & EHCI_HOST_SITD_STATUS_ACTIVE_MASK)) /* transfer is done */ { /* remove sitd from frame list and release itd */ dataLength = USB_HostEhciSitdArrayRelease( ehciInstance, (usb_host_ehci_sitd_t *)transfer->union1.unitHead, (usb_host_ehci_sitd_t *)transfer->union2.unitTail); transfer->transferSofar = dataLength; isoPointer->ehciTransferHead = transfer->next; transfer->callbackFn(transfer->callbackParam, transfer, kStatus_USB_Success); /* transfer callback success */ /* TODO: iso callback error */ } else { qtdStatus = 1; /* break */ } #endif } if (qtdStatus == 1) { break; } transfer = nextTransfer; } break; #endif default: break; } ehciPipePointer = (usb_host_ehci_pipe_t *)ehciPipePointer->pipeCommon.next; } } static void USB_HostEhciPortChange(usb_host_ehci_instance_t *ehciInstance) { /* note: only has one port */ uint32_t portScRegister = ehciInstance->ehciIpBase->PORTSC1; int32_t sofStart = 0; int32_t sofCount = 0; uint32_t index; if (portScRegister & USBHS_PORTSC1_CSC_MASK) /* connection status change */ { sofStart = (int32_t)(ehciInstance->ehciIpBase->FRINDEX & EHCI_MAX_UFRAME_VALUE); /* process CSC bit */ while (1) { portScRegister = ehciInstance->ehciIpBase->PORTSC1; if (portScRegister & USBHS_PORTSC1_CSC_MASK) { /* clear csc bit */ portScRegister = ehciInstance->ehciIpBase->PORTSC1; portScRegister &= (~EHCI_PORTSC1_W1_BITS); ehciInstance->ehciIpBase->PORTSC1 = (portScRegister | USBHS_PORTSC1_CSC_MASK); } sofCount = (int32_t)(ehciInstance->ehciIpBase->FRINDEX & EHCI_MAX_UFRAME_VALUE); if (((sofCount - sofStart + EHCI_MAX_UFRAME_VALUE + 1) & EHCI_MAX_UFRAME_VALUE) > (1 * 8)) /* delay 1ms to clear CSC */ { break; } } } /* process CCS bit */ portScRegister = ehciInstance->ehciIpBase->PORTSC1; if (portScRegister & USBHS_PORTSC1_CCS_MASK) /* process attach */ { if ((ehciInstance->deviceAttached == kEHCIDevicePhyAttached) || (ehciInstance->deviceAttached == kEHCIDeviceAttached)) { return; } #if ((defined(USB_HOST_CONFIG_LOW_POWER_MODE)) && (USB_HOST_CONFIG_LOW_POWER_MODE > 0U)) ehciInstance->busSuspendStatus = kBus_EhciIdle; ehciInstance->ehciIpBase->USBINTR &= ~(USBHS_USBINTR_TIE1_MASK); #endif for (index = 0; index < USB_HOST_EHCI_PORT_CONNECT_DEBOUNCE_DELAY; ++index) { USB_HostEhciDelay(ehciInstance->ehciIpBase, 1); if (!(ehciInstance->ehciIpBase->PORTSC1 & USBHS_PORTSC1_CCS_MASK)) { break; } } if (index < USB_HOST_EHCI_PORT_CONNECT_DEBOUNCE_DELAY) /* CCS is cleared */ { ehciInstance->deviceAttached = kEHCIDeviceDetached; return; } /* reset port */ portScRegister = ehciInstance->ehciIpBase->PORTSC1; portScRegister &= (~EHCI_PORTSC1_W1_BITS); ehciInstance->ehciIpBase->PORTSC1 = (portScRegister | USBHS_PORTSC1_PR_MASK); while (ehciInstance->ehciIpBase->PORTSC1 & USBHS_PORTSC1_PR_MASK) { } ehciInstance->firstDeviceSpeed = ((ehciInstance->ehciIpBase->PORTSC1 & USBHS_PORTSC1_PSPD_MASK) >> USBHS_PORTSC1_PSPD_SHIFT); /* enable ehci phy disconnection */ if (ehciInstance->firstDeviceSpeed == USB_SPEED_HIGH) { USB_EhcihostPhyDisconnectDetectCmd(ehciInstance->controllerId, 1); } /* wait for reset */ USB_HostEhciDelay(ehciInstance->ehciIpBase, USB_HOST_EHCI_PORT_RESET_DELAY); /* process attach */ USB_OsaEventSet(ehciInstance->taskEventHandle, EHCI_TASK_EVENT_DEVICE_ATTACH); /* gpt timer start */ ehciInstance->ehciIpBase->GPTIMER0CTL |= (USBHS_GPTIMER0CTL_RUN_MASK | USBHS_GPTIMER0CTL_MODE_MASK | USBHS_GPTIMER0CTL_RST_MASK); ehciInstance->deviceAttached = kEHCIDevicePhyAttached; } else { if ((ehciInstance->deviceAttached == kEHCIDevicePhyAttached) || (ehciInstance->deviceAttached == kEHCIDeviceAttached)) { #if ((defined(USB_HOST_CONFIG_LOW_POWER_MODE)) && (USB_HOST_CONFIG_LOW_POWER_MODE > 0U)) ehciInstance->busSuspendStatus = kBus_EhciIdle; ehciInstance->ehciIpBase->USBINTR &= ~(USBHS_USBINTR_TIE1_MASK); #endif /* disable ehci phy disconnection */ USB_EhcihostPhyDisconnectDetectCmd(ehciInstance->controllerId, 0); /* disable async and periodic */ USB_HostEhciStopAsync(ehciInstance); USB_HostEhciStopPeriodic(ehciInstance); USB_OsaEventSet(ehciInstance->taskEventHandle, EHCI_TASK_EVENT_DEVICE_DETACH); } } } static void USB_HostEhciTimer0(usb_host_ehci_instance_t *ehciInstance) { volatile usb_host_ehci_qh_t *vltQhPointer; volatile usb_host_ehci_qtd_t *vltQtdPointer; usb_host_transfer_t *transfer; uint32_t backValue; volatile uint32_t *totalBytesAddress = NULL; usb_host_ehci_pipe_t *ehciPipePointer = ehciInstance->ehciRunningPipeList; uint8_t timeoutLabel; while (ehciPipePointer != NULL) { switch (ehciPipePointer->pipeCommon.pipeType) { case USB_ENDPOINT_BULK: case USB_ENDPOINT_CONTROL: vltQhPointer = (volatile usb_host_ehci_qh_t *)ehciPipePointer->ehciQh; /* pipe's qh */ transfer = vltQhPointer->ehciTransferHead; /* qh's transfer */ if ((transfer != NULL)) /* there is transfering data */ { timeoutLabel = 0; if (ehciInstance->deviceAttached != kEHCIDeviceAttached) { vltQtdPointer = (volatile usb_host_ehci_qtd_t *)transfer->union2.unitTail; vltQhPointer->nextQtdPointer = EHCI_HOST_T_INVALID_VALUE; /* invalid next qtd */ vltQhPointer->transferOverlayResults[0] &= (~EHCI_HOST_QTD_STATUS_MASK); /* clear error status */ timeoutLabel = 1; } else { if (vltQhPointer->transferOverlayResults[0] & EHCI_HOST_QTD_STATUS_ACTIVE_MASK) { vltQtdPointer = (volatile usb_host_ehci_qtd_t *)vltQhPointer->currentQtdPointer; totalBytesAddress = &(vltQhPointer->transferOverlayResults[0]); } else { vltQtdPointer = (volatile usb_host_ehci_qtd_t *)transfer->union2.unitTail; totalBytesAddress = ((uint32_t *)vltQtdPointer + 2); } backValue = (((*totalBytesAddress) & EHCI_HOST_QTD_TOTAL_BYTES_MASK) >> EHCI_HOST_QTD_TOTAL_BYTES_SHIFT); /* backValue is used for total bytes to transfer */ if (vltQhPointer->timeOutLabel != backValue) /* use total bytes to reflect the time out */ { vltQhPointer->timeOutValue = USB_HOST_EHCI_CONTROL_BULK_TIME_OUT_VALUE; vltQhPointer->timeOutLabel = backValue; } else { /* time out when the total bytes don't change for the duration * USB_HOST_EHCI_CONTROL_BULK_TIME_OUT_VALUE */ (vltQhPointer->timeOutValue)--; if (vltQhPointer->timeOutValue == 0) { /* stop the qh schedule */ USB_HostEhciStopAsync(ehciInstance); if (backValue != (((*totalBytesAddress) & EHCI_HOST_QTD_TOTAL_BYTES_MASK) >> EHCI_HOST_QTD_TOTAL_BYTES_SHIFT)) { USB_HostEhciStartAsync(ehciInstance); } else { vltQhPointer->nextQtdPointer = EHCI_HOST_T_INVALID_VALUE; /* invalid next qtd */ vltQhPointer->transferOverlayResults[0] &= (~EHCI_HOST_QTD_STATUS_MASK); /* clear error status */ USB_HostEhciStartAsync(ehciInstance); timeoutLabel = 1; } } } } if (timeoutLabel == 1) { /* remove qtd from qh */ while ((vltQtdPointer != NULL) && (!(vltQtdPointer->transferResults[0] & EHCI_HOST_QTD_IOC_MASK)) && (vltQtdPointer != (usb_host_ehci_qtd_t *)vltQhPointer->ehciTransferTail)) { vltQtdPointer = (volatile usb_host_ehci_qtd_t *)vltQtdPointer->nextQtdPointer; } if ((vltQtdPointer != NULL) && (!(vltQtdPointer->nextQtdPointer & EHCI_HOST_T_INVALID_VALUE))) { vltQhPointer->nextQtdPointer = vltQtdPointer->nextQtdPointer; /* start qh if there are other qtd that don't belong to the transfer */ } transfer->transferSofar = USB_HostEhciQtdListRelease(ehciInstance, (usb_host_ehci_qtd_t *)(transfer->union1.unitHead), (usb_host_ehci_qtd_t *)(transfer->union2.unitTail)); transfer->transferSofar = (transfer->transferLength < transfer->transferSofar) ? 0 : (transfer->transferLength - transfer->transferSofar); vltQhPointer->ehciTransferHead = transfer->next; vltQhPointer->timeOutValue = USB_HOST_EHCI_CONTROL_BULK_TIME_OUT_VALUE; transfer->callbackFn(transfer->callbackParam, transfer, kStatus_USB_TransferFailed); } } break; default: break; } ehciPipePointer = (usb_host_ehci_pipe_t *)ehciPipePointer->pipeCommon.next; } } #if ((defined(USB_HOST_CONFIG_LOW_POWER_MODE)) && (USB_HOST_CONFIG_LOW_POWER_MODE > 0U)) static void USB_HostEhciTimer1(usb_host_ehci_instance_t *ehciInstance) { if (ehciInstance->deviceAttached != kEHCIDeviceDetached) { if (kBus_EhciStartSuspend == ehciInstance->busSuspendStatus) { usb_host_instance_t *hostPointer = (usb_host_instance_t *)ehciInstance->hostHandle; if (0 == ehciInstance->matchTick) { ehciInstance->matchTick = hostPointer->hwTick; } else { if ((hostPointer->hwTick - ehciInstance->matchTick) >= 5) { ehciInstance->ehciIpBase->USBCMD &= ~USBHS_USBCMD_RS_MASK; ehciInstance->ehciIpBase->USBSTS |= USBHS_USBSTS_SRI_MASK; #if (defined(FSL_FEATURE_SOC_USBNC_COUNT) && (FSL_FEATURE_SOC_USBNC_COUNT > 0U)) #if 0 ehciInstance->registerPhyBase->CTRL |= USBPHY_CTRL_ENVBUSCHG_WKUP_MASK | USBPHY_CTRL_ENIDCHG_WKUP_MASK | USBPHY_CTRL_ENDPDMCHG_WKUP_MASK | USBPHY_CTRL_ENIRQRESUMEDETECT_MASK ; #endif #endif ehciInstance->ehciIpBase->PORTSC1 |= USBHS_PORTSC1_PHCD_MASK; ehciInstance->registerPhyBase->PWD = 0xFFFFFFFFU; while (ehciInstance->registerPhyBase->CTRL & (USBPHY_CTRL_UTMI_SUSPENDM_MASK)) { __ASM("nop"); } #if (defined(FSL_FEATURE_SOC_USBNC_COUNT) && (FSL_FEATURE_SOC_USBNC_COUNT > 0U)) ehciInstance->registerNcBase->USB_OTGn_CTRL |= USBNC_USB_OTGn_CTRL_WKUP_ID_EN_MASK | USBNC_USB_OTGn_CTRL_WKUP_VBUS_EN_MASK | USBNC_USB_OTGn_CTRL_WKUP_DPDM_EN_MASK; ehciInstance->registerNcBase->USB_OTGn_CTRL |= USBNC_USB_OTGn_CTRL_WIE_MASK; #else ehciInstance->ehciIpBase->USBGENCTRL = USBHS_USBGENCTRL_WU_IE_MASK; #endif ehciInstance->registerPhyBase->CTRL |= USBPHY_CTRL_CLKGATE_MASK; hostPointer->deviceCallback(hostPointer->suspendedDevice, NULL, kUSB_HostEventSuspended); /* call host callback function */ ehciInstance->busSuspendStatus = kBus_EhciSuspended; } } } else if (kBus_EhciStartResume == ehciInstance->busSuspendStatus) { usb_host_instance_t *hostPointer = (usb_host_instance_t *)ehciInstance->hostHandle; if (!(ehciInstance->ehciIpBase->PORTSC1 & USBHS_PORTSC1_FPR_MASK)) { ehciInstance->ehciIpBase->PORTSC1 &= ~USBHS_PORTSC1_WKDS_MASK; if (ehciInstance->ehciIpBase->PORTSC1 & USBHS_PORTSC1_CCS_MASK) { USB_HostEhciStartAsync(ehciInstance); USB_HostEhciStartPeriodic(ehciInstance); } hostPointer->deviceCallback(hostPointer->suspendedDevice, NULL, kUSB_HostEventResumed); /* call host callback function */ hostPointer->suspendedDevice = NULL; ehciInstance->busSuspendStatus = kBus_EhciIdle; ehciInstance->ehciIpBase->USBINTR &= ~(USBHS_USBINTR_TIE1_MASK); } } else { } } else { ehciInstance->busSuspendStatus = kBus_EhciIdle; ehciInstance->ehciIpBase->USBINTR &= ~(USBHS_USBINTR_TIE1_MASK); } } #endif usb_status_t USB_HostEhciCreate(uint8_t controllerId, usb_host_handle upperLayerHandle, usb_host_controller_handle *controllerHandle) { uint32_t index = 0; usb_osa_status_t osaStatus; usb_host_ehci_instance_t *ehciInstance; uint32_t usbhsBaseAddrs[] = USBHS_BASE_ADDRS; usb_host_ehci_data_t *usbHostEhciData[] = USB_HOST_EHCI_DATA_ARRAY; uint8_t *usbHostEhciFrameList[] = USB_HOST_EHCI_FRAME_LIST_ARRAY; uint32_t *framePointer; if ((uint32_t)(controllerId - kUSB_ControllerEhci0) >= (sizeof(usbhsBaseAddrs) / sizeof(usbhsBaseAddrs[0]))) { return kStatus_USB_ControllerNotFound; } *controllerHandle = NULL; ehciInstance = (usb_host_ehci_instance_t *)USB_OsaMemoryAllocate( sizeof(usb_host_ehci_instance_t)); /* malloc host ehci instance */ if (ehciInstance == NULL) { return kStatus_USB_AllocFail; } ehciInstance->controllerId = controllerId; ehciInstance->hostHandle = upperLayerHandle; ehciInstance->deviceAttached = kEHCIDeviceDetached; ehciInstance->ehciIpBase = (USBHS_Type *) usbhsBaseAddrs[controllerId - kUSB_ControllerEhci0]; /* operate ehci ip through the base address */ #if ((defined(USB_HOST_CONFIG_LOW_POWER_MODE)) && (USB_HOST_CONFIG_LOW_POWER_MODE > 0U)) ehciInstance->busSuspendStatus = kBus_EhciIdle; #if (defined(USB_HOST_CONFIG_LOW_POWER_MODE) && (USB_HOST_CONFIG_LOW_POWER_MODE > 0U)) ehciInstance->registerPhyBase = (USBPHY_Type *)USB_EhciPhyGetBase(controllerId); #if (defined(FSL_FEATURE_SOC_USBNC_COUNT) && (FSL_FEATURE_SOC_USBNC_COUNT > 0U)) ehciInstance->registerNcBase = (USBNC_Type *)USB_EhciNCGetBase(controllerId); #endif #endif #endif if (USB_HostEhciResetIP(ehciInstance) != kStatus_USB_Success) /* reset ehci ip */ { USB_OsaMemoryFree(ehciInstance); return kStatus_USB_Error; } /* initialize ehci frame list */ ehciInstance->ehciFrameList = usbHostEhciFrameList[ehciInstance->controllerId - kUSB_ControllerEhci0]; /* initialize ehci units */ ehciInstance->ehciUnitBase = (uint32_t *)(usbHostEhciData[ehciInstance->controllerId - kUSB_ControllerEhci0]); /* initialize qh/qtd/itd/sitd/iso list */ ehciInstance->ehciQhList = (usb_host_ehci_qh_t *)((uint32_t)(ehciInstance->ehciUnitBase)); ehciInstance->ehciQtdHead = (usb_host_ehci_qtd_t *)((uint32_t)ehciInstance->ehciQhList + (sizeof(usb_host_ehci_qh_t) * USB_HOST_CONFIG_EHCI_MAX_QH)); ehciInstance->ehciItdList = (usb_host_ehci_itd_t *)((uint32_t)ehciInstance->ehciQtdHead + (sizeof(usb_host_ehci_qtd_t) * USB_HOST_CONFIG_EHCI_MAX_QTD)); ehciInstance->ehciSitdList = ehciInstance->ehciSitdIndexBase = (usb_host_ehci_sitd_t *)((uint32_t)ehciInstance->ehciItdList + (sizeof(usb_host_ehci_itd_t) * USB_HOST_CONFIG_EHCI_MAX_ITD)); ehciInstance->ehciIsoList = (usb_host_ehci_iso_t *)((uint32_t)ehciInstance->ehciSitdList + (sizeof(usb_host_ehci_sitd_t) * USB_HOST_CONFIG_EHCI_MAX_SITD)); ehciInstance->ehciPipeIndexBase = (usb_host_ehci_pipe_t *)((uint32_t)ehciInstance->ehciIsoList + (sizeof(usb_host_ehci_iso_t) * USB_HOST_EHCI_ISO_NUMBER)); for (index = 1; index < USB_HOST_CONFIG_EHCI_MAX_QH; ++index) { ehciInstance->ehciQhList[index - 1].horizontalLinkPointer = (uint32_t)(&ehciInstance->ehciQhList[index]); } ehciInstance->ehciQhList[USB_HOST_CONFIG_EHCI_MAX_QH - 1].horizontalLinkPointer = (uint32_t)NULL; for (index = 1; index < USB_HOST_CONFIG_EHCI_MAX_QTD; ++index) { ehciInstance->ehciQtdHead[index - 1].nextQtdPointer = (uint32_t)(&ehciInstance->ehciQtdHead[index]); } ehciInstance->ehciQtdNumber = USB_HOST_CONFIG_EHCI_MAX_QTD; ehciInstance->ehciQtdHead[USB_HOST_CONFIG_EHCI_MAX_QTD - 1].nextQtdPointer = (uint32_t)NULL; ehciInstance->ehciQtdTail = &ehciInstance->ehciQtdHead[USB_HOST_CONFIG_EHCI_MAX_QTD - 1]; #if ((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) for (index = 1; index < USB_HOST_CONFIG_EHCI_MAX_ITD; ++index) { ehciInstance->ehciItdList[index - 1].nextLinkPointer = (uint32_t)(&ehciInstance->ehciItdList[index]); } ehciInstance->ehciItdNumber = USB_HOST_CONFIG_EHCI_MAX_ITD; ehciInstance->ehciItdList[USB_HOST_CONFIG_EHCI_MAX_ITD - 1].nextLinkPointer = (uint32_t)NULL; #endif /* USB_HOST_CONFIG_EHCI_MAX_ITD */ #if ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD)) for (index = 1; index < USB_HOST_CONFIG_EHCI_MAX_SITD; ++index) { ehciInstance->ehciSitdList[index - 1].nextLinkPointer = (uint32_t)(&ehciInstance->ehciSitdList[index]); } ehciInstance->ehciSitdNumber = USB_HOST_CONFIG_EHCI_MAX_SITD; ehciInstance->ehciSitdList[USB_HOST_CONFIG_EHCI_MAX_SITD - 1].nextLinkPointer = (uint32_t)NULL; #endif /* USB_HOST_CONFIG_EHCI_MAX_SITD */ #if ((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) for (index = 1; index < USB_HOST_EHCI_ISO_NUMBER; ++index) { ehciInstance->ehciIsoList[index - 1].next = &ehciInstance->ehciIsoList[index]; } ehciInstance->ehciIsoList[USB_HOST_EHCI_ISO_NUMBER - 1].next = NULL; #endif /* initialize pipes */ ehciInstance->ehciPipeList = ehciInstance->ehciPipeIndexBase; for (index = 1; index < USB_HOST_CONFIG_MAX_PIPES; ++index) { ehciInstance->ehciPipeList[index - 1].pipeCommon.next = (usb_host_pipe_t *)&ehciInstance->ehciPipeList[index]; } /* initialize mutext */ osaStatus = USB_OsaMutexCreate(&ehciInstance->ehciMutex); if (osaStatus != kStatus_USB_OSA_Success) { #ifdef HOST_ECHO usb_echo("ehci mutex init fail\r\n"); #endif USB_OsaMemoryFree(ehciInstance); return kStatus_USB_Error; } /* initialize task event */ osaStatus = USB_OsaEventCreate(&ehciInstance->taskEventHandle, 1); if (osaStatus != kStatus_USB_OSA_Success) { #ifdef HOST_ECHO usb_echo("ehci event init fail\r\n"); #endif USB_OsaMutexDestroy(ehciInstance->ehciMutex); USB_OsaMemoryFree(ehciInstance); return kStatus_USB_Error; } /* initialize first qh */ ehciInstance->shedFirstQh = ehciInstance->ehciQhList; ehciInstance->ehciQhList = (usb_host_ehci_qh_t *)(ehciInstance->ehciQhList->horizontalLinkPointer & EHCI_HOST_POINTER_ADDRESS_MASK); ehciInstance->shedFirstQh->staticEndpointStates[0] |= (1 << EHCI_HOST_QH_H_SHIFT); /* first qh */ ehciInstance->shedFirstQh->horizontalLinkPointer = EHCI_HOST_T_INVALID_VALUE; ehciInstance->shedFirstQh->currentQtdPointer = EHCI_HOST_T_INVALID_VALUE; ehciInstance->shedFirstQh->nextQtdPointer = EHCI_HOST_T_INVALID_VALUE; ehciInstance->shedFirstQh->alternateNextQtdPointer = EHCI_HOST_T_INVALID_VALUE; ehciInstance->shedFirstQh->horizontalLinkPointer = (uint32_t)((uint32_t)(ehciInstance->shedFirstQh) | EHCI_HOST_POINTER_TYPE_QH); /* initialize periodic list */ framePointer = (uint32_t *)ehciInstance->ehciFrameList; for (index = 0; index < USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE; ++index) { framePointer[index] = EHCI_HOST_T_INVALID_VALUE; } USB_HostEhciStartIP(ehciInstance); /* start ehci ip */ *controllerHandle = ehciInstance; return kStatus_USB_Success; } usb_status_t USB_HostEhciDestory(usb_host_controller_handle controllerHandle) { usb_host_ehci_instance_t *ehciInstance = (usb_host_ehci_instance_t *)controllerHandle; /* disable all interrupts */ ehciInstance->ehciIpBase->USBINTR = 0; /* stop the controller */ ehciInstance->ehciIpBase->USBCMD = 0; /* free memory */ USB_OsaMutexDestroy(ehciInstance->ehciMutex); USB_OsaEventDestroy(ehciInstance->taskEventHandle); USB_OsaMemoryFree(ehciInstance); return kStatus_USB_Success; } usb_status_t USB_HostEhciOpenPipe(usb_host_controller_handle controllerHandle, usb_host_pipe_handle *pipeHandle, usb_host_pipe_init_t *pipeInit) { usb_host_ehci_pipe_t *ehciPipePointer = NULL; usb_status_t status; uint32_t speed; usb_host_ehci_instance_t *ehciInstance = (usb_host_ehci_instance_t *)controllerHandle; /* get one pipe */ USB_HostEhciLock(); if (ehciInstance->ehciPipeList != NULL) { ehciPipePointer = ehciInstance->ehciPipeList; ehciInstance->ehciPipeList = (usb_host_ehci_pipe_t *)ehciPipePointer->pipeCommon.next; } USB_HostEhciUnlock(); if (ehciPipePointer == NULL) { #ifdef HOST_ECHO usb_echo("ehci open pipe failed\r\n"); #endif return kStatus_USB_Busy; } /* initialize pipe informations */ USB_HostEhciZeroMem((uint32_t *)ehciPipePointer, sizeof(usb_host_ehci_pipe_t) / 4); ehciPipePointer->pipeCommon.deviceHandle = pipeInit->devInstance; ehciPipePointer->pipeCommon.endpointAddress = pipeInit->endpointAddress; ehciPipePointer->pipeCommon.direction = pipeInit->direction; ehciPipePointer->pipeCommon.interval = pipeInit->interval; ehciPipePointer->pipeCommon.maxPacketSize = pipeInit->maxPacketSize; ehciPipePointer->pipeCommon.pipeType = pipeInit->pipeType; ehciPipePointer->pipeCommon.numberPerUframe = pipeInit->numberPerUframe; if (ehciPipePointer->pipeCommon.numberPerUframe == 0) { ehciPipePointer->pipeCommon.numberPerUframe = 1; } ehciPipePointer->pipeCommon.nakCount = pipeInit->nakCount; ehciPipePointer->pipeCommon.nextdata01 = 0; ehciPipePointer->ehciQh = NULL; USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceSpeed, &speed); if (ehciPipePointer->pipeCommon.pipeType == USB_ENDPOINT_ISOCHRONOUS) { ehciPipePointer->pipeCommon.interval = (1 << (ehciPipePointer->pipeCommon.interval - 1)); /* iso interval is the power of 2 */ } else if (ehciPipePointer->pipeCommon.pipeType == USB_ENDPOINT_INTERRUPT) { if (speed == USB_SPEED_HIGH) { ehciPipePointer->pipeCommon.interval = (1 << (ehciPipePointer->pipeCommon.interval - 1)); /* HS interrupt interval is the power of 2 */ } else { ehciPipePointer->pipeCommon.interval = USB_HostEhciGet2PowerValue( ehciPipePointer->pipeCommon .interval); /* FS/LS interrupt interval should be the power of 2, it is used for ehci bandwidth */ } } else { } /* save the micro-frame interval, it is convenient for the interval process */ if (speed == USB_SPEED_HIGH) { ehciPipePointer->uframeInterval = ehciPipePointer->pipeCommon.interval; } else { ehciPipePointer->uframeInterval = 8 * ehciPipePointer->pipeCommon.interval; } /* open pipe */ switch (ehciPipePointer->pipeCommon.pipeType) { case USB_ENDPOINT_CONTROL: case USB_ENDPOINT_BULK: status = USB_HostEhciOpenControlBulk(ehciInstance, ehciPipePointer); break; #if (((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) || \ ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD))) case USB_ENDPOINT_ISOCHRONOUS: status = USB_HostEhciOpenIso(ehciInstance, ehciPipePointer); break; #endif case USB_ENDPOINT_INTERRUPT: status = USB_HostEhciOpenInterrupt(ehciInstance, ehciPipePointer); break; default: status = kStatus_USB_Error; break; } if (status != kStatus_USB_Success) { /* release pipe */ USB_HostEhciLock(); ehciPipePointer->pipeCommon.next = (usb_host_pipe_t *)ehciInstance->ehciPipeList; ehciInstance->ehciPipeList = ehciPipePointer; USB_HostEhciUnlock(); return status; } /* add pipe to run pipe list */ USB_HostEhciLock(); ehciPipePointer->pipeCommon.next = (usb_host_pipe_t *)ehciInstance->ehciRunningPipeList; ehciInstance->ehciRunningPipeList = ehciPipePointer; USB_HostEhciUnlock(); *pipeHandle = ehciPipePointer; return status; } usb_status_t USB_HostEhciClosePipe(usb_host_controller_handle controllerHandle, usb_host_pipe_handle pipeHandle) { usb_host_ehci_instance_t *ehciInstance = (usb_host_ehci_instance_t *)controllerHandle; usb_host_ehci_pipe_t *ehciPipePointer = (usb_host_ehci_pipe_t *)pipeHandle; usb_host_pipe_t *prevPointer = NULL; switch (ehciPipePointer->pipeCommon.pipeType) { case USB_ENDPOINT_BULK: case USB_ENDPOINT_CONTROL: USB_HostEhciCloseControlBulk(ehciInstance, ehciPipePointer); break; case USB_ENDPOINT_INTERRUPT: USB_HostEhciCloseInterrupt(ehciInstance, ehciPipePointer); break; #if (((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) || \ ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD))) case USB_ENDPOINT_ISOCHRONOUS: USB_HostEhciCloseIso(ehciInstance, ehciPipePointer); break; #endif default: break; } /* delete pipe from run pipe list */ USB_HostEhciLock(); prevPointer = (usb_host_pipe_t *)ehciInstance->ehciRunningPipeList; if (prevPointer == (usb_host_pipe_t *)ehciPipePointer) { ehciInstance->ehciRunningPipeList = (usb_host_ehci_pipe_t *)(prevPointer->next); } else { while (prevPointer != NULL) { if (prevPointer->next == (usb_host_pipe_t *)ehciPipePointer) { prevPointer->next = ehciPipePointer->pipeCommon.next; break; } else { prevPointer = prevPointer->next; } } } USB_HostEhciUnlock(); /* release pipe */ USB_HostEhciLock(); ehciPipePointer->pipeCommon.next = (usb_host_pipe_t *)ehciInstance->ehciPipeList; ehciInstance->ehciPipeList = ehciPipePointer; USB_HostEhciUnlock(); return kStatus_USB_Success; } usb_status_t USB_HostEhciWritePipe(usb_host_controller_handle controllerHandle, usb_host_pipe_handle pipeHandle, usb_host_transfer_t *transfer) { usb_host_ehci_instance_t *ehciInstance = (usb_host_ehci_instance_t *)controllerHandle; usb_host_ehci_pipe_t *ehciPipePointer = (usb_host_ehci_pipe_t *)pipeHandle; usb_status_t status = kStatus_USB_Success; #if (((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) || \ ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD))) uint32_t speed; #endif switch (ehciPipePointer->pipeCommon.pipeType) { case USB_ENDPOINT_BULK: case USB_ENDPOINT_CONTROL: case USB_ENDPOINT_INTERRUPT: status = USB_HostEhciQhQtdListInit(ehciInstance, ehciPipePointer, transfer); /* initialize qtd for control/bulk transfer */ break; #if (((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) || \ ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD))) case USB_ENDPOINT_ISOCHRONOUS: USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceSpeed, &speed); if (speed == USB_SPEED_HIGH) { #if ((defined USB_HOST_CONFIG_EHCI_MAX_ITD) && (USB_HOST_CONFIG_EHCI_MAX_ITD)) status = USB_HostEhciItdArrayInit(ehciInstance, ehciPipePointer, transfer); /* initialize itd for iso transfer */ #endif } else { #if ((defined USB_HOST_CONFIG_EHCI_MAX_SITD) && (USB_HOST_CONFIG_EHCI_MAX_SITD)) status = USB_HostEhciSitdArrayInit(ehciInstance, ehciPipePointer, transfer); /* initialize sitd for iso transfer */ #endif } break; #endif default: break; } return status; } usb_status_t USB_HostEhciReadpipe(usb_host_controller_handle controllerHandle, usb_host_pipe_handle pipeHandle, usb_host_transfer_t *transfer) { return USB_HostEhciWritePipe(controllerHandle, pipeHandle, transfer); /* same as write */ } usb_status_t USB_HostEhciIoctl(usb_host_controller_handle controllerHandle, uint32_t ioctlEvent, void *ioctlParam) { usb_status_t status = kStatus_USB_Success; usb_host_ehci_instance_t *ehciInstance = (usb_host_ehci_instance_t *)controllerHandle; usb_host_cancel_param_t *param; usb_host_ehci_pipe_t *ehciPipePointer; volatile usb_host_ehci_qh_t *vltQhPointer; uint32_t deviceAddress; if (controllerHandle == NULL) { return kStatus_USB_InvalidHandle; } switch (ioctlEvent) { case kUSB_HostCancelTransfer: /* cancel pipe or one transfer */ param = (usb_host_cancel_param_t *)ioctlParam; status = USB_HostEhciCancelPipe(ehciInstance, (usb_host_ehci_pipe_t *)param->pipeHandle, param->transfer); break; case kUSB_HostBusControl: /* bus control */ status = USB_HostEhciControlBus(ehciInstance, *((uint8_t *)ioctlParam)); break; case kUSB_HostGetFrameNumber: /* get frame number */ *((uint32_t *)ioctlParam) = ((ehciInstance->ehciIpBase->FRINDEX & EHCI_MAX_UFRAME_VALUE) >> 3); break; case kUSB_HostUpdateControlEndpointAddress: ehciPipePointer = (usb_host_ehci_pipe_t *)ioctlParam; vltQhPointer = (volatile usb_host_ehci_qh_t *)ehciPipePointer->ehciQh; /* update address */ USB_HostHelperGetPeripheralInformation(ehciPipePointer->pipeCommon.deviceHandle, kUSB_HostGetDeviceAddress, &deviceAddress); vltQhPointer->staticEndpointStates[0] |= deviceAddress; break; case kUSB_HostUpdateControlPacketSize: ehciPipePointer = (usb_host_ehci_pipe_t *)ioctlParam; vltQhPointer = (volatile usb_host_ehci_qh_t *)ehciPipePointer->ehciQh; USB_HostEhciLock(); if (ehciInstance->ehciIpBase->USBSTS & USBHS_USBSTS_AS_MASK) { USB_HostEhciStopAsync(ehciInstance); /* update max packet size */ vltQhPointer->staticEndpointStates[0] = (((vltQhPointer->staticEndpointStates[0]) & (~EHCI_HOST_QH_MAX_PACKET_LENGTH_MASK)) | ((uint32_t)ehciPipePointer->pipeCommon.maxPacketSize << EHCI_HOST_QH_MAX_PACKET_LENGTH_SHIFT)); USB_HostEhciStartAsync(ehciInstance); } else { /* update max packet size */ vltQhPointer->staticEndpointStates[0] = (((vltQhPointer->staticEndpointStates[0]) & (~EHCI_HOST_QH_MAX_PACKET_LENGTH_MASK)) | ((uint32_t)ehciPipePointer->pipeCommon.maxPacketSize << EHCI_HOST_QH_MAX_PACKET_LENGTH_SHIFT)); } USB_HostEhciUnlock(); break; default: break; } return status; } void USB_HostEhciTaskFunction(void *hostHandle) { usb_host_ehci_instance_t *ehciInstance; uint32_t bitSet; usb_device_handle deviceHandle; if (hostHandle == NULL) { return; } ehciInstance = (usb_host_ehci_instance_t *)((usb_host_instance_t *)hostHandle)->controllerHandle; if (USB_OsaEventWait(ehciInstance->taskEventHandle, 0xFF, 0, 0, &bitSet) == kStatus_USB_OSA_Success) /* wait all event */ { if (bitSet & EHCI_TASK_EVENT_PORT_CHANGE) /* port change */ { USB_HostEhciPortChange(ehciInstance); } if (bitSet & EHCI_TASK_EVENT_TIMER0) /* timer0 */ { USB_HostEhciTimer0(ehciInstance); } #if ((defined(USB_HOST_CONFIG_LOW_POWER_MODE)) && (USB_HOST_CONFIG_LOW_POWER_MODE > 0U)) if (bitSet & EHCI_TASK_EVENT_TIMER1) /* timer1 */ { USB_HostEhciTimer1(ehciInstance); } #endif if (ehciInstance->deviceAttached == kEHCIDeviceAttached) { if (bitSet & EHCI_TASK_EVENT_TRANSACTION_DONE) /* transaction done */ { USB_HostEhciTransactionDone(ehciInstance); } if (bitSet & EHCI_TASK_EVENT_DEVICE_DETACH) /* device detach */ { ehciInstance->ehciIpBase->USBINTR &= (~USBHS_USBINTR_PCE_MASK); /* disable attach, enable when the detach process is done */ ehciInstance->deviceAttached = kEHCIDeviceDetached; USB_HostDetachDevice(ehciInstance->hostHandle, 0, 0); } } else if (ehciInstance->deviceAttached != kEHCIDeviceAttached) { if (bitSet & EHCI_TASK_EVENT_DEVICE_ATTACH) /* device is attached */ { USB_HostEhciStartAsync(ehciInstance); USB_HostEhciStartPeriodic(ehciInstance); if (USB_HostAttachDevice(ehciInstance->hostHandle, ehciInstance->firstDeviceSpeed, 0, 0, 1, &deviceHandle) == kStatus_USB_Success) { ehciInstance->deviceAttached = kEHCIDeviceAttached; } } } else { } } } void USB_HostEhciIsrFunction(void *hostHandle) { usb_host_ehci_instance_t *ehciInstance; static uint32_t interruptStatus = 0; if (hostHandle == NULL) { return; } ehciInstance = (usb_host_ehci_instance_t *)((usb_host_instance_t *)hostHandle)->controllerHandle; #if ((defined(USB_HOST_CONFIG_LOW_POWER_MODE)) && (USB_HOST_CONFIG_LOW_POWER_MODE > 0U)) #if (defined(FSL_FEATURE_SOC_USBNC_COUNT) && (FSL_FEATURE_SOC_USBNC_COUNT > 0U)) if (ehciInstance->registerNcBase->USB_OTGn_CTRL & USBNC_USB_OTGn_CTRL_WIE_MASK) { usb_host_instance_t *hostPointer = (usb_host_instance_t *)ehciInstance->hostHandle; ehciInstance->registerNcBase->USB_OTGn_CTRL &= ~USBNC_USB_OTGn_CTRL_WIE_MASK; hostPointer->deviceCallback(hostPointer->suspendedDevice, NULL, kUSB_HostEventDetectResume); /* call host callback function */ while (!(ehciInstance->registerNcBase->USB_OTGn_PHY_CTRL_0 & USBNC_USB_OTGn_PHY_CTRL_0_UTMI_CLK_VLD_MASK)) { } if (ehciInstance->ehciIpBase->PORTSC1 & USBHS_PORTSC1_CCS_MASK) { USB_HostEhciStartAsync(ehciInstance); USB_HostEhciStartPeriodic(ehciInstance); } ehciInstance->ehciIpBase->USBCMD |= (USBHS_USBCMD_RS_MASK); if ((kBus_EhciSuspended == ehciInstance->busSuspendStatus)) { /* ehciInstance->ehciIpBase->PORTSC1 |= USBHS_PORTSC1_FPR_MASK; */ ehciInstance->busSuspendStatus = kBus_EhciStartResume; } else { } } else { } #else if (ehciInstance->ehciIpBase->USBGENCTRL & USBHS_USBGENCTRL_WU_IE_MASK) { usb_host_instance_t *hostPointer = (usb_host_instance_t *)ehciInstance->hostHandle; hostPointer->deviceCallback(hostPointer->suspendedDevice, NULL, kUSB_HostEventDetectResume); /* call host callback function */ while (!(USBPHY->PLL_SIC & USBPHY_PLL_SIC_PLL_LOCK_MASK)) { } ehciInstance->ehciIpBase->USBGENCTRL |= USBHS_USBGENCTRL_WU_INT_CLR_MASK; ehciInstance->ehciIpBase->USBGENCTRL &= ~USBHS_USBGENCTRL_WU_IE_MASK; if (ehciInstance->ehciIpBase->PORTSC1 & USBHS_PORTSC1_CCS_MASK) { USB_HostEhciStartAsync(ehciInstance); USB_HostEhciStartPeriodic(ehciInstance); } ehciInstance->ehciIpBase->USBCMD |= (USBHS_USBCMD_RS_MASK); if ((kBus_EhciSuspended == ehciInstance->busSuspendStatus)) { ehciInstance->busSuspendStatus = kBus_EhciStartResume; /*ehciInstance->ehciIpBase->PORTSC1 |= USBHS_PORTSC1_FPR_MASK; */ } else { } } else { } #endif /* FSL_FEATURE_SOC_USBNC_COUNT */ #endif /* USB_HOST_CONFIG_LOW_POWER_MODE */ interruptStatus = ehciInstance->ehciIpBase->USBSTS; interruptStatus &= ehciInstance->ehciIpBase->USBINTR; while (interruptStatus) /* there are usb interrupts */ { ehciInstance->ehciIpBase->USBSTS = interruptStatus; /* clear interrupt */ if (interruptStatus & USBHS_USBSTS_SRI_MASK) /* SOF interrupt */ { } if (interruptStatus & USBHS_USBSTS_SEI_MASK) /* system error interrupt */ { } if ((interruptStatus & USBHS_USBSTS_UI_MASK) || (interruptStatus & USBHS_USBSTS_UEI_MASK)) /* USB interrupt or USB error interrupt */ { USB_OsaEventSet(ehciInstance->taskEventHandle, EHCI_TASK_EVENT_TRANSACTION_DONE); } if (interruptStatus & USBHS_USBSTS_PCI_MASK) /* port change detect interrupt */ { #if ((defined(USB_HOST_CONFIG_LOW_POWER_MODE)) && (USB_HOST_CONFIG_LOW_POWER_MODE > 0U)) usb_host_instance_t *hostPointer = (usb_host_instance_t *)ehciInstance->hostHandle; if (ehciInstance->ehciIpBase->PORTSC1 & USBHS_PORTSC1_FPR_MASK) { if (kBus_EhciStartSuspend == ehciInstance->busSuspendStatus) { if (ehciInstance->ehciIpBase->PORTSC1 & USBHS_PORTSC1_CCS_MASK) { USB_HostEhciStartAsync(ehciInstance); USB_HostEhciStartPeriodic(ehciInstance); } hostPointer->deviceCallback(hostPointer->suspendedDevice, NULL, kUSB_HostEventNotSuspended); /* call host callback function */ hostPointer->suspendedDevice = NULL; ehciInstance->busSuspendStatus = kBus_EhciIdle; ehciInstance->ehciIpBase->USBINTR &= ~(USBHS_USBINTR_TIE1_MASK); } else { } } #endif USB_OsaEventSet(ehciInstance->taskEventHandle, EHCI_TASK_EVENT_PORT_CHANGE); } if (interruptStatus & USBHS_USBSTS_TI0_MASK) /* timer 0 interrupt */ { USB_OsaEventSet(ehciInstance->taskEventHandle, EHCI_TASK_EVENT_TIMER0); } #if ((defined(USB_HOST_CONFIG_LOW_POWER_MODE)) && (USB_HOST_CONFIG_LOW_POWER_MODE > 0U)) if (interruptStatus & USBHS_USBSTS_TI1_MASK) /* timer 1 interrupt */ { USB_OsaEventSet(ehciInstance->taskEventHandle, EHCI_TASK_EVENT_TIMER1); } #endif interruptStatus = ehciInstance->ehciIpBase->USBSTS; interruptStatus &= ehciInstance->ehciIpBase->USBINTR; } } #endif /* USB_HOST_CONFIG_EHCI */