5677 lines
267 KiB
C
5677 lines
267 KiB
C
/*
|
|
*********************************************************************************************************
|
|
* uC/USB-Host
|
|
* The Embedded USB Host Stack
|
|
*
|
|
* Copyright 2004-2021 Silicon Laboratories Inc. www.silabs.com
|
|
*
|
|
* SPDX-License-Identifier: APACHE-2.0
|
|
*
|
|
* This software is subject to an open source license and is distributed by
|
|
* Silicon Laboratories Inc. pursuant to the terms of the Apache License,
|
|
* Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
|
|
*
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* NOTICE
|
|
*
|
|
* Semidrive modified this file to adapt it for ssdk platform.
|
|
* The modifications are only intended for use with Semidrive chips.
|
|
* Copyright of all the modifications belongs to Semidrive Semiconductor.
|
|
*
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
*
|
|
* GENERIC EHCI DRIVER
|
|
*
|
|
* Filename : usbh_ehci.c
|
|
* Version : V3.42.01
|
|
*********************************************************************************************************
|
|
* Note(s) : (1) Streaming for Bulk and Interrupt transfers not implemented.
|
|
*
|
|
* (2) With an appropriate BSP, this device driver also will support the EHCI module on
|
|
* the following MCUs:
|
|
*
|
|
* Freescale i.MX6
|
|
* Freescale i.MX25
|
|
* NXP LPC185x series
|
|
* NXP LPC183x series
|
|
* NXP LPC182x series
|
|
* NXP LPC435x series
|
|
* NXP LPC433x series
|
|
* NXP LPC432x series
|
|
* Xilinx Zynq-7000 Soc
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* INCLUDE FILES
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#define USBH_EHCI_MODULE
|
|
#define MICRIUM_SOURCE
|
|
#include "../../Source/usbh_hub.h"
|
|
#include "usbh_hcd_ehci.h"
|
|
#include <cpu_cache.h>
|
|
#include <usb_bsp.h>
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* LOCAL DEFINES
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#define EHCI_HCD_GENERIC 0u
|
|
#define EHCI_HCD_SYNOPSYS 1u
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI OPERATIONAL REGISTERS
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#define USBCMD (p_ehci->HcOperReg->USBCmd) /* USB Command Register */
|
|
#define USBSTATUS (p_ehci->HcOperReg->USBSts) /* USB Status Register */
|
|
#define USBINT (p_ehci->HcOperReg->USBIntr) /* USB Interrupt Enable Register */
|
|
#define FRAMEIX (p_ehci->HcOperReg->FrameIx) /* Frame Index Register */
|
|
#define CTRLDSSEG (p_ehci->HcOperReg->CtrlDSSeg) /* Control Data Structure Segment Register */
|
|
#define PERIODICLISTBASE (p_ehci->HcOperReg->PeriodicListBase)/* Periodic Frame List Base Address Register */
|
|
#define ASYNCLISTADDR (p_ehci->HcOperReg->AsyncListAddr) /* Current Asynchronous List Address Register */
|
|
#define CFGFLAG (p_ehci->HcOperReg->CfgFlag) /* Configure Flag Register */
|
|
#define PORTSC(i) (p_ehci->HcOperReg->PortSC[(i)]) /* Port Status and Control Register */
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI SYNOPSYS OPERATIONAL REGISTERS
|
|
*********************************************************************************************************
|
|
*/
|
|
/* USB mode Register */
|
|
#define EHCI_SYNOPSYS_USBMODE (* (CPU_REG32 *)(((CPU_REG08 *)p_ehci->HcCapReg) + 0x00000A8))
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* BITMASKS FOR EHCI CAPABILITY REGISTER FIELDS
|
|
*********************************************************************************************************
|
|
*/
|
|
/* ---------------- HCSPARAMS Register ---------------- */
|
|
#define EHCI_HCSPARAMS_RD_DPN 0x00F00000u /* Debug Port Number */
|
|
#define EHCI_HCSPARAMS_RD_PI 0x00010000u /* Port Indicators */
|
|
#define EHCI_HCSPARAMS_RD_NCC 0x0000F000u /* Number of Companion Controllers */
|
|
#define EHCI_HCSPARAMS_RD_NPCC 0x00000F00u /* Number of Ports per Companion Controller */
|
|
#define EHCI_HCSPARAMS_RD_PRR 0x00000080u /* Port Routing Rules */
|
|
#define EHCI_HCSPARAMS_RD_PPC 0x00000010u /* Port Power Control */
|
|
#define EHCI_HCSPARAMS_RD_NP 0x0000000Fu /* Number of Ports */
|
|
|
|
/* ---------------- HCCPARAMS Register ---------------- */
|
|
#define EHCI_HCCPARAMS_RD_EECP 0x0000FF00u /* EHCI Extended Capabilities Pointer */
|
|
#define EHCI_HCCPARAMS_RD_IST 0x000000F0u /* Isochronous Scheduling Threshold */
|
|
#define EHCI_HCCPARAMS_RD_ASPC 0x00000004u /* Asynchronous Schedule Park Capability */
|
|
#define EHCI_HCCPARAMS_RD_PFLF 0x00000002u /* Programmable Frame List Flag */
|
|
#define EHCI_HCCPARAMS_RD_64BAC 0x00000001u /* 64-Bit Addressing Capability */
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* READ BITMASKS FOR EHCI OPERATIONAL REGISTER FIELDS
|
|
*********************************************************************************************************
|
|
*/
|
|
/* ------------------ USBCMD Register ----------------- */
|
|
#define EHCI_USBCMD_RD_ITC 0x00FF0000u /* Interrupt Threshold Control */
|
|
#define EHCI_USBCMD_RD_ASPME 0x00000800u /* Asynchronous Schedule Park Mode Enable */
|
|
#define EHCI_USBCMD_RD_ACPMC 0x00000300u /* Asynchronous Schedule Park Mode Count */
|
|
#define EHCI_USBCMD_RD_LHCR 0x00000080u /* Light Host Controller Reset */
|
|
#define EHCI_USBCMD_RD_IOAAD 0x00000040u /* Interrupt On Async Advance Doorbell */
|
|
#define EHCI_USBCMD_RD_ASE 0x00000020u /* Asynchronous Schedule Enable */
|
|
#define EHCI_USBCMD_RD_PSE 0x00000010u /* Periodic Schedule Enable */
|
|
#define EHCI_USBCMD_RD_FLS_1024 0x00000000u /* 1024 Frame List Size */
|
|
#define EHCI_USBCMD_RD_FLS_512 0x00000004u /* 512 Frame List Size */
|
|
#define EHCI_USBCMD_RD_FLS_256 0x00000008u /* 256 Frame List Size */
|
|
#define EHCI_USBCMD_RD_FLS 0x0000000Cu /* Frame List Size */
|
|
#define EHCI_USBCMD_RD_HCR 0x00000002u /* Host Controller Reset */
|
|
#define EHCI_USBCMD_RD_RS 0x00000001u /* Run/Stop */
|
|
|
|
/* ------------------ USBSTS Register ----------------- */
|
|
#define EHCI_USBSTS_RD_ASS 0x00008000u /* Asynchronous Schedule Status */
|
|
#define EHCI_USBSTS_RD_PSS 0x00004000u /* Periodic Schedule Status */
|
|
#define EHCI_USBSTS_RD_RECL 0x00002000u /* Reclamation */
|
|
#define EHCI_USBSTS_RD_HC_HAL 0x00001000u /* HC Halted */
|
|
#define EHCI_USBSTS_RD_IOAA 0x00000020u /* Interrupt On Async Advance */
|
|
#define EHCI_USBSTS_RD_HSE 0x00000010u /* Host System Error */
|
|
#define EHCI_USBSTS_RD_FLR 0x00000008u /* Frame List Rollover */
|
|
#define EHCI_USBSTS_RD_PCD 0x00000004u /* Port Change Detect */
|
|
#define EHCI_USBSTS_RD_USBEI 0x00000002u /* USB Error Interrupt */
|
|
#define EHCI_USBSTS_RD_USBI 0x00000001u /* USB Interrupt */
|
|
|
|
/* ----------------- USBINT Register ----------------- */
|
|
#define EHCI_USBINTR_RD_IOAAE 0x00000020u /* Interrupt On Async Advance Enable */
|
|
#define EHCI_USBINTR_RD_HSEE 0x00000010u /* Host System Error Enable */
|
|
#define EHCI_USBINTR_RD_FLRE 0x00000008u /* Frame List Rollover Enable */
|
|
#define EHCI_USBINTR_RD_PCIE 0x00000004u /* Port Change Interrupt enable */
|
|
#define EHCI_USBINTR_RD_USBEIE 0x00000002u /* USB Error Interrupt Enable */
|
|
#define EHCI_USBINTR_RD_USBIE 0x00000001u /* USB Interrupt Enable */
|
|
|
|
/* ----------------- FRAMEIX Register ----------------- */
|
|
#define EHCI_FRINDEX_RD_FI 0x00003FFFu /* Frame Index */
|
|
|
|
/* ------------- PERIODICLISTBASE Register ------------ */
|
|
#define EHCI_PERIODICLIST_RD_BA 0xFFFFF000u /* Base Address */
|
|
|
|
/* -------------- ASYNCLISTADDR Register -------------- */
|
|
#define EHCI_ASYNCLISTADDR_RD_LPL 0xFFFFFFE0u /* Link Pointer Low */
|
|
|
|
/* ---------------- CFGFLAG Register --------------- */
|
|
#define EHCI_CONFIGFLAG_RD_CF 0x00000001u /* Configure Flag */
|
|
|
|
/* ------------------ PORTSC Register ----------------- */
|
|
#define EHCI_PORTSC_WKOC_RD_E 0x00400000u /* Wake on Over Current Enable */
|
|
#define EHCI_PORTSC_WKDSCNNT_RD_E 0x00200000u /* Wake on Disconnect Enable */
|
|
#define EHCI_PORTSC_WKCNNT_RD_E 0x00100000u /* Wake on Connect Enable */
|
|
#define EHCI_PORTSC_RD_PTC 0x000F0000u /* Port Test Control */
|
|
#define EHCI_PORTSC_RD_PIC 0x0000C000u /* Port Indicator Control */
|
|
#define EHCI_PORTSC_RD_PO 0x00002000u /* Port Owner */
|
|
#define EHCI_PORTSC_RD_PP 0x00001000u /* Port Power */
|
|
#define EHCI_PORTSC_RD_LS 0x00000C00u /* Line Status */
|
|
#define EHCI_PORTSC_RD_PR 0x00000100u /* Port Reset */
|
|
#define EHCI_PORTSC_RD_SUSP 0x00000080u /* Suspend */
|
|
#define EHCI_PORTSC_RD_FPR 0x00000040u /* Force Port Resume */
|
|
#define EHCI_PORTSC_RD_OCC 0x00000020u /* Over Current Change */
|
|
#define EHCI_PORTSC_RD_OCA 0x00000010u /* Over Current Active */
|
|
#define EHCI_PORTSC_RD_PEDC 0x00000008u /* Port Enable/Disable Change */
|
|
#define EHCI_PORTSC_RD_PED 0x00000004u /* Port Enabled/Disabled */
|
|
#define EHCI_PORTSC_RD_CSC 0x00000002u /* Connect Status Change */
|
|
#define EHCI_PORTSC_RD_CCS 0x00000001u /* Current Connect Status */
|
|
|
|
/* Port speed bit (specific to Synopsys USB 2.0 Host IP)*/
|
|
#define EHCI_SYNOPSYS_PORTSC_RD_PSPD_MASK (DEF_BIT_26 | DEF_BIT_27)
|
|
#define EHCI_SYNOPSYS_PORTSC_RD_PSPD_FS DEF_BIT_NONE
|
|
#define EHCI_SYNOPSYS_PORTSC_RD_PSPD_HS DEF_BIT_27
|
|
#define EHCI_SYNOPSYS_PORTSC_RD_PSPD_LS DEF_BIT_26
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* WRITE BITMASKS FOR EHCI OPERATIONAL REGISTER FIELDS
|
|
*********************************************************************************************************
|
|
*/
|
|
/* ------------------ USBCMD Register ----------------- */
|
|
#define EHCI_USBCMD_WR_ITC_1MF 0x00010000u /* Issue interrupts for every 1 Micro Frame */
|
|
#define EHCI_USBCMD_WR_ITC_2MF 0x00020000u /* Issue interrupts for every 2 Micro Frames */
|
|
#define EHCI_USBCMD_WR_ITC_4MF 0x00040000u /* Issue interrupts for every 4 Micro Frames */
|
|
#define EHCI_USBCMD_WR_ITC_8MF 0x00080000u /* Issue interrupts for every 8 Micro Frames */
|
|
#define EHCI_USBCMD_WR_ITC_16MF 0x00100000u /* Issue interrupts for every 16 Micro Frames */
|
|
#define EHCI_USBCMD_WR_ITC_32MF 0x00200000u /* Issue interrupts for every 32 Micro Frames */
|
|
#define EHCI_USBCMD_WR_ITC_64MF 0x00400000u /* Issue interrupts for every 64 Micro Frames */
|
|
#define EHCI_USBCMD_WR_ASPME 0x00000800u /* Asynchronous Park Mode Enable */
|
|
#define EHCI_USBCMD_WR_LHCR 0x00000080u /* Light Host Controller Reset */
|
|
#define EHCI_USBCMD_WR_IOAAD 0x00000040u /* Interrupt On Async Advance Doorbell */
|
|
#define EHCI_USBCMD_WR_ASE 0x00000020u /* Asynchronous Schedule Enable */
|
|
#define EHCI_USBCMD_WR_PSE 0x00000010u /* Periodic Schedule Enable */
|
|
#define EHCI_USBCMD_WR_FLS_1024 0x00000000u /* Frame List Size 1024 elements */
|
|
#define EHCI_USBCMD_WR_FLS_512 0x00000004u /* Frame List Size 512 elements */
|
|
#define EHCI_USBCMD_WR_FLS_256 0x00000008u /* Frame List Size 256 elements */
|
|
#define EHCI_USBCMD_WR_HCR 0x00000002u /* Host Controller Reset */
|
|
#define EHCI_USBCMD_WR_RS 0x00000001u /* Run/Stop */
|
|
|
|
/* ------------------ USBSTS Register ----------------- */
|
|
#define EHCI_USBSTS_WR_ASS 0x00008000u /* Asynchronous Schedule Status */
|
|
#define EHCI_USBSTS_WR_PSS 0x00004000u /* Periodic Schedule Status */
|
|
#define EHCI_USBSTS_WR_RECL 0x00002000u /* Reclamation */
|
|
#define EHCI_USBSTS_WR_HC_HAL 0x00001000u /* HC Halted */
|
|
#define EHCI_USBSTS_WR_IOAA 0x00000020u /* Interrupt On Async Advance */
|
|
#define EHCI_USBSTS_WR_HSE 0x00000010u /* Host System Error */
|
|
#define EHCI_USBSTS_WR_FLR 0x00000008u /* Frame List Roll over */
|
|
#define EHCI_USBSTS_WR_PCD 0x00000004u /* Port Change Detect */
|
|
#define EHCI_USBSTS_WR_USBEI 0x00000002u /* USB Error Interrupt */
|
|
#define EHCI_USBSTS_WR_USBI 0x00000001u /* USB Interrupt */
|
|
|
|
/* ----------------- USBINT Register ----------------- */
|
|
#define EHCI_USBINTR_WR_IOAAE 0x00000020u /* Interrupt On Async Advance Enable */
|
|
#define EHCI_USBINTR_WR_HSEE 0x00000010u /* Host System Error Enable */
|
|
#define EHCI_USBINTR_WR_FLRE 0x00000008u /* Frame List Rollover Enable */
|
|
#define EHCI_USBINTR_WR_PCIE 0x00000004u /* Port Change Interrupt enable */
|
|
#define EHCI_USBINTR_WR_USBEIE 0x00000002u /* USB Error Interrupt Enable */
|
|
#define EHCI_USBINTR_WR_USBIE 0x00000001u /* USB Interrupt Enable */
|
|
|
|
/* ----------------- FRAMEIX Register ----------------- */
|
|
#define EHCI_FRINDEX_WR_FI_1024 0x00000000u /* Frame Index */
|
|
#define EHCI_FRINDEX_WR_FI_512 0x00001000u /* Frame Index */
|
|
#define EHCI_FRINDEX_WR_FI_256 0x00002000u /* Frame Index */
|
|
|
|
/* ---------------- CFGFLAG Register --------------- */
|
|
#define EHCI_CONFIGFLAG_WR_CF 0x00000001u /* Configure Flag */
|
|
|
|
/* ------------------ PORTSC Register ----------------- */
|
|
#define EHCI_PORTSC_WR_WKOC_E 0x00400000u /* Wakeon Over Current Enable */
|
|
#define EHCI_PORTSC_WR_WKDSCNNT_E 0x00200000u /* Wakeon Disconnect Enable */
|
|
#define EHCI_PORTSC_WR_WKCNNT_E 0x00100000u /* Wakeon Connect Enable */
|
|
#define EHCI_PORTSC_WR_PTC_DIS 0x00000000u /* Port Test Control */
|
|
#define EHCI_PORTSC_WR_PTC_J 0x00010000u /* Port Test Control */
|
|
#define EHCI_PORTSC_WR_PTC_K 0x00020000u /* Port Test Control */
|
|
#define EHCI_PORTSC_WR_PTC_SE0_NAK 0x00030000u /* Port Test Control */
|
|
#define EHCI_PORTSC_WR_PTC_P 0x00040000u /* Port Test Control */
|
|
#define EHCI_PORTSC_WR_PTC_FE 0x00050000u /* Port Test Control */
|
|
#define EHCI_PORTSC_WR_PIC_OFF 0x00000000u /* Port Indicator Control */
|
|
#define EHCI_PORTSC_WR_PIC_AMB 0x00004000u /* Port Indicator Control */
|
|
#define EHCI_PORTSC_WR_PIC_GRE 0x00008000u /* Port Indicator Control */
|
|
#define EHCI_PORTSC_WR_PO 0x00002000u /* Port Owner */
|
|
#define EHCI_PORTSC_WR_PP_OFF 0x00000000u /* Port Power */
|
|
#define EHCI_PORTSC_WR_PP_ON 0x00001000u /* Port Power */
|
|
#define EHCI_PORTSC_WR_PR 0x00000100u /* Port Reset */
|
|
#define EHCI_PORTSC_WR_SUSP 0x00000080u /* Suspend */
|
|
#define EHCI_PORTSC_WR_FPR 0x00000040u /* Force Port Resume */
|
|
#define EHCI_PORTSC_WR_OCC 0x00000020u /* Over Current Change */
|
|
#define EHCI_PORTSC_WR_OCA 0x00000010u /* Over Current Active */
|
|
#define EHCI_PORTSC_WR_PEDC 0x00000008u /* Port Enable/Disable Change */
|
|
#define EHCI_PORTSC_WR_PED 0x00000004u /* Port Enabled/Disabled */
|
|
#define EHCI_PORTSC_WR_CSC 0x00000002u /* Connect Status Change */
|
|
|
|
/* ------------------ USBMODE Register ---------------- */
|
|
#define EHCI_SYNOPSYS_USBMODE_WR_CM_HOST (DEF_BIT_00 | DEF_BIT_01)
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* OFFSETS FOR BIT FIELDS
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#define O_ITD_T 0u /* Terminate */
|
|
#define O_ITD_TYP 1u /* QH/iTD/siTD/FSTN Select */
|
|
#define O_ITD_LP 5u /* Link Pointer */
|
|
#define O_ITD_OFFSET 0u /* Transaction Offset */
|
|
#define O_ITD_PG 12u /* Page Select */
|
|
#define O_ITD_IOC 15u /* Interrupt On Complete */
|
|
#define O_ITD_LENGTH 16u /* Transaction Length */
|
|
#define O_ITD_STS 28u /* Status */
|
|
#define O_ITD_STS_ACTIVE 0x8u
|
|
#define O_ITD_STS_DBE 0x4u
|
|
#define O_ITD_STS_BD 0x2u
|
|
#define O_ITD_STS_XACTERR 0x1u
|
|
#define O_ITD_DEVADD 0u /* Device Address */
|
|
#define O_ITD_ENDPT 8u /* Endpoint Number */
|
|
#define O_ITD_BUFPTR 12u /* Buffer Pointer */
|
|
#define O_ITD_MPS 0u /* Maximum Packet Size */
|
|
#define O_ITD_DIR 11u /* Direction */
|
|
#define O_ITD_MULTI 0u /* Multi */
|
|
#define O_SITD_NLP 5u
|
|
#define O_SITD_T 0u /* Terminate */
|
|
#define O_SITD_TYP 2u /* QH/iTD/siTD/FSTN Select */
|
|
#define O_SITD_LP 5u /* Link Pointer */
|
|
#define O_SITD_DEVADD 0u /* Device Address */
|
|
#define O_SITD_ENDPT 8u /* Endpoint Number */
|
|
#define O_SITD_HUBADD 16u /* Hub Address */
|
|
#define O_SITD_PN 24u /* Port Number */
|
|
#define O_SITD_DIR 31u /* Direction */
|
|
#define O_SITD_SMASK 0u /* Split Complete Mask */
|
|
#define O_SITD_CMASK 8u /* Split Start Mask */
|
|
#define O_SITD_STS 0u /* Status of the transaction executed by the HC */
|
|
#define O_SITD_TP 3u /* Transaction position */
|
|
#define O_SITD_TCOUNT 0u /* Transaction count */
|
|
#define O_SITD_STS_ACTIVE 0x80u /* Active */
|
|
#define O_SITD_STS_ERR 0x40u /* Transaction translator error */
|
|
#define O_SITD_STS_DBE 0x20u /* Data buffer error */
|
|
#define O_SITD_STS_BD 0x10u /* Babble detected */
|
|
#define O_SITD_STS_XACT_ERR 0x08u /* Transaction error */
|
|
#define O_SITD_STS_MMF 0x04u /* Missed micro frame */
|
|
#define O_SITD_STS_STS 0x02u /* Split transaction state */
|
|
#define O_SITD_CSPMASK 8u /* Complete Split Progress Mask */
|
|
#define O_SITD_TBTT 16u /* Total Bytes To Transfer */
|
|
#define O_SITD_PS 30u /* Page Select */
|
|
#define O_SITD_IOC 31u /* Interrupt On Complete */
|
|
#define O_SITD_CO 0u /* Current Offset */
|
|
#define O_SITD_BPL 12u /* Buffer Pointer List */
|
|
#define O_SITD_TP 3u /* Transaction Position */
|
|
#define O_SITD_TC 0u /* Transaction Count */
|
|
#define O_SITD_T 0u /* Terminate */
|
|
#define O_SITD_BP 5u /* Back Pointer */
|
|
#define O_QTD_T 0u /* Terminate */
|
|
#define O_QTD_NTEP 5u /* Next Transfer Element Pointer */
|
|
#define O_QTD_ANTEP 5u /* Alternate Next Transfer Element Pointer */
|
|
#define O_QTD_STS 0u /* Status */
|
|
#define O_QTD_PID 8u /* PID Code */
|
|
#define O_QTD_CERR 10u /* Error Counter */
|
|
#define O_QTD_CP 12u /* Current Page */
|
|
#define O_QTD_IOC 15u /* Interrupt On Complete */
|
|
#define O_QTD_TBTT 16u /* Total Bytes To Transfer */
|
|
#define O_QTD_DT 31u /* Data Toggle */
|
|
#define O_QTD_SFD 0u /* Status Field Description */
|
|
#define O_QTD_CO 0u /* Current Offset */
|
|
#define O_QTD_BPL 12u /* Buffer Pointer List */
|
|
#define O_QH_T 0u /* Terminate */
|
|
#define O_QH_TYP 1u /* QH/iTD/siTD/FSTN Select */
|
|
#define O_QH_QHHLP 5u /* Queue Head Horizontal Link Pointer */
|
|
#define O_QH_DEVADD 0u /* Device Address */
|
|
#define O_QH_I 7u /* Inactive on Next Transaction */
|
|
#define O_QH_ENDPT 8u /* Endpoint Number */
|
|
#define O_QH_EPS 12u /* Endpoint Speed */
|
|
#define O_QH_DTC 14u /* Data Toggle Control */
|
|
#define O_QH_H 15u /* Head of Reclamation List Flag */
|
|
#define O_QH_MPL 16u /* Maximum Packet Length */
|
|
#define O_QH_C 27u /* Control Endpoint Flag */
|
|
#define O_QH_RL 28u /* Next Count Reload */
|
|
#define O_QH_SMASK 0u /* Interrupt Schedule Mask */
|
|
#define O_QH_CMASK 8u /* Split Completion Mask */
|
|
#define O_QH_HUBADD 16u /* Hub Address */
|
|
#define O_QH_PN 23u /* Port Number */
|
|
#define O_QH_HBPM 30u /* High Bandwidth Pipe Multifier */
|
|
#define O_QH_CETDLP 5u /* Current Element Transaction Descriptor Link Pointer */
|
|
#define O_QH_NAKCNT 1u /* Nak Counter */
|
|
#define O_QH_DT 31u /* Data Toggle */
|
|
#define O_QH_IOC 15u /* Interrupt On Complete */
|
|
#define O_QH_EC 10u /* Error Counter */
|
|
#define O_QH_PS 0u /* Ping State */
|
|
#define O_QH_STCSP 0u /* Split Transaction Complete Split Progress */
|
|
#define O_QH_STFT 0u /* Split Transaction Frame Tag */
|
|
#define O_QH_SBYTES 5u /* S-Bytes */
|
|
#define O_QH_STS_ACTIVE 0x80u /* Active */
|
|
#define O_QH_STS_HALTED 0x40u /* Halted */
|
|
#define O_QH_STS_DBE 0x20u /* Data Buffer Error */
|
|
#define O_QH_STS_BD 0x10u /* Babble Detected */
|
|
#define O_QH_STS_XACT_ERR 0x08u /* Transaction Error */
|
|
#define O_QH_STS_MMF 0x04u /* Missed Micro Frame */
|
|
#define O_QH_STS_STS 0x02u /* Split Transaction State */
|
|
#define O_QH_STS_PE 0x01u /* Ping State */
|
|
#define O_FSTN_T 0u /* Terminate */
|
|
#define O_FSTN_TYP 1u /* QH/iTD/siTD/FSTN Select */
|
|
#define O_FSTN_NPLP 5u /* Normal Path Link Pointer */
|
|
#define O_FSTN_BPLP 5u /* Back Path Link Pointer */
|
|
#define S_MASK_1MICROFRM 0xFFu /* S mask for 1 Micro Frame interval */
|
|
#define S_MASK_2MICROFRM 0x55u /* S mask for 2 Micro Frame interval */
|
|
#define S_MASK_4MICROFRM 0x11u /* S mask for 3 Micro Frame interval */
|
|
#define S_MASK_8MICROFRM 0x01u /* S mask for 8 or > 8 Micro Frame interval */
|
|
|
|
#define S_MASK_SPLIT_0_MICROFRM 0x01u
|
|
#define S_MASK_SPLIT_01_MICROFRM 0x03u
|
|
#define S_MASK_SPLIT_012_MICROFRM 0x07u
|
|
#define S_MASK_SPLIT_0123_MICROFRM 0x0Fu
|
|
#define S_MASK_SPLIT_01234_MICROFRM 0x1Fu
|
|
#define S_MASK_SPLIT_012345_MICROFRM 0x3Fu
|
|
|
|
#define C_MASK_SPLIT_0_MICROFRM 0xFEu
|
|
#define C_MASK_SPLIT_01_MICROFRM 0xF8u
|
|
#define C_MASK_SPLIT_012_MICROFRM 0xF0u
|
|
#define C_MASK_SPLIT_0123_MICROFRM 0xE0u
|
|
#define C_MASK_SPLIT_01234_MICROFRM 0xC0u
|
|
#define C_MASK_SPLIT_012345_MICROFRM 0x80u
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* DATA STRUCTURE FIELD DEFINITIONS
|
|
*********************************************************************************************************
|
|
*/
|
|
/* ------------------- Common Fields ------------------ */
|
|
#define DWORD1_T DEF_BIT_00
|
|
#define DWORD1_T_VALID 0u /* T-bit Field in DWORD1 = 0 (Valid) */
|
|
#define DWORD1_T_INVALID 1u /* T-bit Field in DWORD1 = 1 (Invalid) */
|
|
#define DWORD1_TYP_ITD 0u /* Type Field in DWORD1 = 0 (iTD) */
|
|
#define DWORD1_TYP_QH 1u /* Type Field in DWORD1 = 1 (QH) */
|
|
#define DWORD1_TYP_SITD 2u /* Type Field in DWORD1 = 2 (siTD) */
|
|
#define DWORD1_TYP_FSTN 3u /* Type Field in DWORD1 = 3 (FSTN) */
|
|
|
|
/* ----------------- QueueHead Fields ----------------- */
|
|
#define DWORD2_QH_AS_I 1u /* I-Field in DWORD2 = 1, HC set 'Active' bit to 0. Used for Split transaction. */
|
|
#define DWORD2_QH_EPS_FS 0u /* EPS Field in DWORD2 = 0 for Full-Speed Device */
|
|
#define DWORD2_QH_EPS_LS 1u /* EPS Field in DWORD2 = 1 for Low-Speed Device */
|
|
#define DWORD2_QH_EPS_HS 2u /* EPS Field in DWORD2 = 2 for High-Speed Device */
|
|
#define DWORD2_QH_DTC_QH 0u /* Preserve DT bit in QH.Ignore DT bit from qTD */
|
|
#define DWORD2_QH_DTC_QTD 1u /* DT bit comes from qTD */
|
|
#define DWORD2_QH_R_H 1u /* QH is Head of Reclamation List */
|
|
#define DWORD2_QH_C 1u /* Split Transaction Control Endpoint Flag */
|
|
#define DWORD3_QH_PS_CSPLIT_UFRAME_2345 0x3Cu /* Split Transaction: CSPLIT sent in uFrame 2, 3, 4 ... */
|
|
/* ...or 5 from the Host-Frame (1ms) */
|
|
#define DWORD3_QH_PS_SSPLIT_UFRAME_0 0x01u /* Split Transaction: SSPLIT sent in uFrame 0 from ... */
|
|
/* ...the Host-Frame (1ms) */
|
|
#define DWORD3_QH_AS_SMASK 0u /* Interrupt Schedule Mask = 0: Asynchronous Schedule */
|
|
#define DWORD3_QH_HBPM_1 1u /* One Transaction should be issued to the End-Point */
|
|
#define DWORD3_QH_HBPM_2 2u /* Two Transactions should be issued to the End-Point */
|
|
#define DWORD3_QH_HBPM_3 3u /* Three Transactions should be issued to the End-Point */
|
|
|
|
/* --- Queue Element Transfer Descriptor(qTD) Fields -- */
|
|
#define DWORD3_QTD_PIDC_OUT 0u /* PID Code Field in DWORD3 = 0 (OUT Packet) */
|
|
#define DWORD3_QTD_PIDC_IN 1u /* PID Code Field in DWORD3 = 1 (IN Packet) */
|
|
#define DWORD3_QTD_PIDC_SETUP 2u /* PID Code Field in DWORD3 = 2 (SETUP Packet) */
|
|
|
|
/* ------- Isoc Transfer Descriptor (iTD) Fields ------ */
|
|
#define DWORDx_ITD_IOC DEF_BIT_15
|
|
#define DWORDx_ITD_STATUS_ACTIVE DEF_BIT_31
|
|
|
|
/* --- Split Transaction Isoc Transfer Descriptor (siTD) Fields -- */
|
|
#define DWORD3_SITD_STATUS_ACTIVE DEF_BIT_07
|
|
#define DWORD3_SITD_IOC DEF_BIT_31
|
|
#define DWORD1_SITD_IO_OUT 0u
|
|
#define DWORD1_SITD_IO_IN 1u
|
|
#define DWORD6_SITD_TP_ALL 0u
|
|
#define DWORD6_SITD_TP_BEGIN 1u
|
|
#define DWORD6_SITD_TP_MID 2u
|
|
#define DWORD6_SITD_TP_END 3u
|
|
|
|
#define DWORD1_ITD_IO_OUT 0u
|
|
#define DWORD1_ITD_IO_IN 1u
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* DATA STRUCTURE FIELD SHIFTS
|
|
*********************************************************************************************************
|
|
*/
|
|
/* Common Fields */
|
|
#define HOR_LNK_PTR_PTR(x) ( (x) << O_QH_QHHLP )
|
|
#define HOR_LNK_PTR_TYP(x) ( (x) << O_QH_TYP )
|
|
#define HOR_LNK_PTR_T(x) ( (x) << O_QH_T )
|
|
|
|
/* QueueHead Fields */
|
|
#define QH_EPCHAR_DEVADD(x) ( (x) << O_QH_DEVADD )
|
|
#define QH_EPCHAR_I(x) ( (x) << O_QH_I )
|
|
#define QH_EPCHAR_ENDPT(x) ( (x) << O_QH_ENDPT )
|
|
#define QH_EPCHAR_EPS(x) ( (x) << O_QH_EPS )
|
|
#define QH_EPCHAR_DTC(x) ( (x) << O_QH_DTC )
|
|
#define QH_EPCHAR_H(x) ( (x) << O_QH_H )
|
|
#define QH_EPCHAR_MPL(x) ( (x) << O_QH_MPL )
|
|
#define QH_EPCHAR_C(x) ( (x) << O_QH_C )
|
|
#define QH_EPCHAR_RL(x) ( (x) << O_QH_RL )
|
|
#define QH_EPCAP_SMASK(x) ( (x) << O_QH_SMASK )
|
|
#define QH_EPCAP_CMASK(x) ( (x) << O_QH_CMASK )
|
|
#define QH_EPCAP_HUBADD(x) ( (x) << O_QH_HUBADD )
|
|
#define QH_EPCAP_PN(x) ( (x) << O_QH_PN )
|
|
#define QH_EPCAP_HBPM(x) ( (x) << O_QH_HBPM )
|
|
#define QH_CETDLP(x) ( (x) << O_QH_CETDLP )
|
|
#define QH_OVERLAY_NAKCNT(x) ( (x) << O_QH_NAKCNT )
|
|
#define QH_OVERLAY_PS(x) ( (x) << O_QH_PS )
|
|
#define QH_OVERLAY_EC(x) ( (x) << O_QH_EC )
|
|
#define QH_OVERLAY_IOC(x) ( (x) << O_QH_IOC )
|
|
#define QH_OVERLAY_DT(x) ( (x) << O_QH_DT )
|
|
#define QH_OVERLAY_STCSP(x) ( (x) << O_QH_STCSP )
|
|
#define QH_OVERLAY_SBYTES(x) ( (x) << O_QH_SBYTES )
|
|
#define QH_OVERLAY_STFT(x) ( (x) << O_QH_STFT )
|
|
|
|
/* Queue Element Transfer Descriptor Fields */
|
|
#define QTD_N_QTD_PTR_NTEP(x) ( (x) << O_QTD_NTEP )
|
|
#define QTD_N_QTD_PTR_T(x) ( (x) << O_QTD_T )
|
|
#define QTD_ALT_PTR_ANTEP(x) ( (x) << O_QTD_ANTEP )
|
|
#define QTD_ALT_QTD_PTR_T(x) ( (x) << O_QTD_T )
|
|
#define QTD_TOKEN_STS(x) ( (x) << O_QTD_STS )
|
|
#define QTD_TOKEN_PID(x) ( (x) << O_QTD_PID )
|
|
#define QTD_TOKEN_CERR(x) ( (x) << O_QTD_CERR )
|
|
#define QTD_TOKEN_CP(x) ( (x) << O_QTD_CP )
|
|
#define QTD_TOKEN_IOC(x) ( (x) << O_QTD_IOC )
|
|
#define QTD_TOKEN_TBTT(x) ( (x) << O_QTD_TBTT )
|
|
#define QTD_TOKEN_DT(x) ( (x) << O_QTD_DT )
|
|
#define QTD_BPPL_CO(x) ( (x) << O_QTD_CO )
|
|
#define QTD_BPPL_BPL(x) ( (x) << O_QTD_BPL )
|
|
|
|
|
|
#define SITD_DWORD0_NXT_LINK_PTR(x) ( (x) << O_SITD_NLP)
|
|
#define SITD_DWORD0_TYP(x) ( (x) << O_SITD_TYP)
|
|
#define SITD_DWORD0_T(x) ( (x) << O_SITD_T)
|
|
|
|
#define SITD_EPCHAR_DEVADD(x) ( (x) << O_SITD_DEVADD)
|
|
#define SITD_EPCHAR_ENDPT(x) ( (x) << O_SITD_ENDPT)
|
|
#define SITD_EPCHAR_HUBADD(x) ( (x) << O_SITD_HUBADD)
|
|
#define SITD_EPCHAR_PN(x) ( (x) << O_SITD_PN)
|
|
#define SITD_EPCHAR_DIR(x) ( (x) << O_SITD_DIR)
|
|
#define SITD_EPCHAR_SMASK(x) ( (x) << O_SITD_SMASK)
|
|
#define SITD_STSCTRL_IOC(x) ( (x) << O_SITD_IOC)
|
|
#define SITD_STSCTRL_STS(x) ( (x) << O_SITD_STS)
|
|
#define SITD_BUGPAGE1_TP(x) ( (x) << O_SITD_TP)
|
|
#define SITD_BUGPAGE1_TCOUNT(x) ( (x) << O_SITD_TCOUNT)
|
|
|
|
#define ITD_DWORD0_TYP(x) ( (x) << O_ITD_TYP)
|
|
#define ITD_DWORD0_T(x) ( (x) << O_ITD_T)
|
|
#define ITD_BUF_PG_PTR_LIST_DEVADD(x) ( (x) << O_ITD_DEVADD)
|
|
#define ITD_BUF_PG_PTR_LIST_ENDPT(x) ( (x) << O_ITD_ENDPT)
|
|
#define ITD_BUF_PG_PTR_LIST_MPS(x) ( (x) << O_ITD_MPS)
|
|
#define ITD_BUF_PG_PTR_LIST_IO(x) ( (x) << O_ITD_DIR)
|
|
#define ITD_BUF_PG_PTR_LIST_MULT(x) ( (x) << O_ITD_MULTI)
|
|
#define ITD_BUF_PG_PTR_LIST_BUF_PTR(x) ( (x) << O_ITD_BUFPTR)
|
|
#define ITD_STSCTRL_STS(x) ( (x) << O_ITD_STS)
|
|
#define ITD_STSCTRL_XACT_LEN(x) ( (x) << O_ITD_LENGTH)
|
|
#define ITD_STSCTRL_PG(x) ( (x) << O_ITD_PG)
|
|
#define ITD_STSCTRL_XACT_OFFSET(x) ( (x) << O_ITD_OFFSET)
|
|
#define ITD_STSCTRL_IOC(x) ( (x) << O_ITD_IOC)
|
|
|
|
/* ----------------- ALIGNMENT MACROS ----------------- */
|
|
#define DEF_ALIGN(x, a) ((CPU_INT32U)(x) % (a) ? (a) - ((CPU_INT32U)(x) % (a)) +\
|
|
(CPU_INT32U)(x) : (CPU_INT32U)(x))
|
|
|
|
#define USB_ALIGNED(x, a) (void *)USBH_OS_BusToVir((void *)DEF_ALIGN(USBH_OS_VirToBus((void *)(x)), (a)))
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* LOCAL CONSTANTS
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* LOCAL DATA TYPES
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* LOCAL TABLES
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* LOCAL GLOBAL VARIABLES
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static CPU_INT32U EHCI_BranchArray[256];
|
|
#endif
|
|
|
|
#if (CPU_CFG_CACHE_MGMT_EN == DEF_ENABLED)
|
|
extern CPU_INT32U CPU_Cache_Linesize;
|
|
#endif
|
|
|
|
|
|
/*
|
|
**********************************************************************************************************
|
|
* LOCAL FUNCTION PROTOTYPES
|
|
**********************************************************************************************************
|
|
*/
|
|
|
|
/* --------------- DRIVER API FUNCTIONS --------------- */
|
|
static void EHCI_SynopsysInit (USBH_HC_DRV *p_hc_drv,
|
|
USBH_ERR *p_err);
|
|
|
|
static void EHCI_Init (USBH_HC_DRV *p_hc_drv,
|
|
USBH_ERR *p_err);
|
|
|
|
static void EHCI_InitHandler (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U ehci_drv_type,
|
|
USBH_ERR *p_err);
|
|
|
|
static void EHCI_Start (USBH_HC_DRV *p_hc_drv,
|
|
USBH_ERR *p_err);
|
|
|
|
static void EHCI_Stop (USBH_HC_DRV *p_hc_drv,
|
|
USBH_ERR *p_err);
|
|
|
|
static USBH_DEV_SPD EHCI_SpdGet (USBH_HC_DRV *p_hc_drv,
|
|
USBH_ERR *p_err);
|
|
|
|
static void EHCI_Suspend (USBH_HC_DRV *p_hc_drv,
|
|
USBH_ERR *p_err);
|
|
|
|
static void EHCI_Resume (USBH_HC_DRV *p_hc_drv,
|
|
USBH_ERR *p_err);
|
|
|
|
static CPU_INT32U EHCI_FrameNbrGet (USBH_HC_DRV *p_hc_drv,
|
|
USBH_ERR *p_err);
|
|
|
|
static void EHCI_EP_Open (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
USBH_ERR *p_err);
|
|
|
|
static void EHCI_EP_Close (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
USBH_ERR *p_err);
|
|
|
|
static void EHCI_EP_Abort (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
USBH_ERR *p_err);
|
|
|
|
static CPU_BOOLEAN EHCI_IsHalt_EP (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
USBH_ERR *p_err);
|
|
|
|
static void EHCI_URB_Submit (USBH_HC_DRV *p_hc_drv,
|
|
USBH_URB *p_urb,
|
|
USBH_ERR *p_err);
|
|
|
|
static void EHCI_URB_Complete (USBH_HC_DRV *p_hc_drv,
|
|
USBH_URB *p_urb,
|
|
USBH_ERR *p_err);
|
|
|
|
static void EHCI_URB_Abort (USBH_HC_DRV *p_hc_drv,
|
|
USBH_URB *p_urb,
|
|
USBH_ERR *p_err);
|
|
|
|
/* ---------------- INTERNAL FUNCTIONS ---------------- */
|
|
static void EHCI_ISR (void *p_data);
|
|
|
|
#if (EHCI_CFG_ONRESET_EN == DEF_ENABLED)
|
|
static void EHCI_OnReset (USBH_DEV *p_dev);
|
|
#endif
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static void EHCI_PeriodicOrderPrepare(CPU_INT32U idx,
|
|
CPU_INT32U power,
|
|
CPU_INT32U list_size);
|
|
#endif
|
|
|
|
static USBH_ERR EHCI_DMA_Init (USBH_HC_DRV *p_hc_drv);
|
|
|
|
static void EHCI_CapRegRead (EHCI_DEV *p_ehci,
|
|
EHCI_CAP *p_cap);
|
|
|
|
static void EHCI_QTD_Clr (EHCI_QTD *p_qtd);
|
|
|
|
static void EHCI_QH_Clr (EHCI_QH *p_qh);
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static void EHCI_EP_DescClr (EHCI_ISOC_EP_DESC *p_ep_desc);
|
|
|
|
static void EHCI_SITD_Clr (EHCI_SITD *p_sitd);
|
|
#endif
|
|
|
|
static EHCI_QTD *EHCI_QTDListPrepare (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
USBH_URB *p_urb,
|
|
CPU_INT08U *p_buf,
|
|
CPU_INT32U buf_len,
|
|
USBH_ERR *p_err);
|
|
|
|
static CPU_INT32U EHCI_QTDRemove (USBH_HC_DRV *p_hc_drv,
|
|
EHCI_QH *p_qh);
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static USBH_ERR EHCI_PeriodicListInit (USBH_HC_DRV *p_hc_drv);
|
|
#endif
|
|
|
|
static USBH_ERR EHCI_AsyncListInit (USBH_HC_DRV *p_hc_drv);
|
|
|
|
static USBH_ERR EHCI_AsyncEP_Open (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
USBH_DEV *p_dev);
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static USBH_ERR EHCI_IntrEP_Open (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
USBH_DEV *p_dev);
|
|
|
|
static USBH_ERR EHCI_IsocEP_Open (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep);
|
|
|
|
static void EHCI_BW_Update (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
void *p_data,
|
|
CPU_BOOLEAN bw_use);
|
|
|
|
static USBH_ERR EHCI_BW_Get (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
void *p_data);
|
|
|
|
static USBH_ERR EHCI_SITDListPrepare (USBH_HC_DRV *p_hc_drv,
|
|
USBH_DEV *p_dev,
|
|
USBH_EP *p_ep,
|
|
EHCI_ISOC_EP_DESC *p_ep_desc,
|
|
USBH_URB *p_urb,
|
|
CPU_INT08U *p_buf);
|
|
|
|
static USBH_ERR EHCI_ITDListPrepare (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
EHCI_ISOC_EP_DESC *p_ep_desc,
|
|
USBH_URB *p_urb,
|
|
CPU_INT08U *p_buf,
|
|
CPU_INT32U buf_len);
|
|
|
|
static void EHCI_ITD_Clr (EHCI_ITD *p_itd);
|
|
#endif
|
|
|
|
static USBH_ERR EHCI_AsyncEP_Close (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep);
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static CPU_INT32U EHCI_SITDDone (USBH_HC_DRV *p_hc_drv,
|
|
EHCI_ISOC_EP_DESC *p_ep_desc,
|
|
CPU_INT08U dev_addr,
|
|
CPU_INT08U ep_addr,
|
|
USBH_URB *p_urb);
|
|
|
|
static CPU_INT32U EHCI_ITDDone (USBH_HC_DRV *p_hc_drv,
|
|
EHCI_ISOC_EP_DESC *p_ep_desc,
|
|
CPU_INT08U dev_addr,
|
|
CPU_INT08U ep_addr,
|
|
USBH_URB *p_urb);
|
|
#endif
|
|
|
|
static void EHCI_QHDone (USBH_HC_DRV *p_hc_drv,
|
|
EHCI_QH *p_qh);
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static void EHCI_IntrEPInsert (USBH_HC_DRV *p_hc_drv,
|
|
EHCI_QH *p_qh_to_insert);
|
|
|
|
static USBH_ERR EHCI_IntrEP_Close (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep);
|
|
|
|
static USBH_ERR EHCI_IsocEP_Close (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep);
|
|
#endif
|
|
|
|
/* -------------- ROOT HUB API FUNCTIONS -------------- */
|
|
static CPU_BOOLEAN EHCI_PortStatusGet (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr,
|
|
USBH_HUB_PORT_STATUS *p_port_status);
|
|
|
|
static CPU_BOOLEAN EHCI_HubDescGet (USBH_HC_DRV *p_hc_drv,
|
|
void *p_buf,
|
|
CPU_INT08U buf_len);
|
|
|
|
static CPU_BOOLEAN EHCI_PortEnSet (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr);
|
|
|
|
static CPU_BOOLEAN EHCI_PortEnClr (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr);
|
|
|
|
static CPU_BOOLEAN EHCI_PortEnChngClr (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr);
|
|
|
|
static CPU_BOOLEAN EHCI_PortPwrSet (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr);
|
|
|
|
static CPU_BOOLEAN EHCI_PortPwrClr (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr);
|
|
|
|
static CPU_BOOLEAN EHCI_PortResetSet (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr);
|
|
|
|
static CPU_BOOLEAN EHCI_PortResetChngClr (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr);
|
|
|
|
static CPU_BOOLEAN EHCI_PortSuspendClr (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr);
|
|
|
|
static CPU_BOOLEAN EHCI_PortConnChngClr (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr);
|
|
|
|
static CPU_BOOLEAN EHCI_PCD_IntEn (USBH_HC_DRV *p_hc_drv);
|
|
|
|
static CPU_BOOLEAN EHCI_PCD_IntDis (USBH_HC_DRV *p_hc_drv);
|
|
|
|
|
|
static CPU_BOOLEAN EHCI_PortPwrModeGet (EHCI_DEV *p_ehci);
|
|
|
|
static USBH_ERR EHCI_PortSuspendSet (EHCI_DEV *p_ehci,
|
|
CPU_INT32U port_nbr);
|
|
|
|
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* LOCAL CONFIGURATION ERRORS
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* INITIALIZED GLOBAL VARIABLES
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
USBH_HC_DRV_API EHCI_DrvAPI = {
|
|
EHCI_Init,
|
|
EHCI_Start,
|
|
EHCI_Stop,
|
|
EHCI_SpdGet,
|
|
EHCI_Suspend,
|
|
EHCI_Resume,
|
|
EHCI_FrameNbrGet,
|
|
|
|
EHCI_EP_Open,
|
|
EHCI_EP_Close,
|
|
EHCI_EP_Abort,
|
|
EHCI_IsHalt_EP,
|
|
|
|
EHCI_URB_Submit,
|
|
EHCI_URB_Complete,
|
|
EHCI_URB_Abort
|
|
};
|
|
|
|
USBH_HC_DRV_API EHCI_DrvAPI_Synopsys = {
|
|
EHCI_SynopsysInit,
|
|
EHCI_Start,
|
|
EHCI_Stop,
|
|
EHCI_SpdGet,
|
|
EHCI_Suspend,
|
|
EHCI_Resume,
|
|
EHCI_FrameNbrGet,
|
|
|
|
EHCI_EP_Open,
|
|
EHCI_EP_Close,
|
|
EHCI_EP_Abort,
|
|
EHCI_IsHalt_EP,
|
|
|
|
EHCI_URB_Submit,
|
|
EHCI_URB_Complete,
|
|
EHCI_URB_Abort
|
|
};
|
|
|
|
USBH_HC_RH_API EHCI_RH_API = {
|
|
EHCI_PortStatusGet,
|
|
EHCI_HubDescGet,
|
|
|
|
EHCI_PortEnSet,
|
|
EHCI_PortEnClr,
|
|
EHCI_PortEnChngClr,
|
|
|
|
EHCI_PortPwrSet,
|
|
EHCI_PortPwrClr,
|
|
|
|
EHCI_PortResetSet,
|
|
EHCI_PortResetChngClr,
|
|
|
|
EHCI_PortSuspendClr,
|
|
EHCI_PortConnChngClr,
|
|
|
|
EHCI_PCD_IntEn,
|
|
EHCI_PCD_IntDis
|
|
};
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
* GLOBAL FUNCTIONS
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_Init()
|
|
*
|
|
* Description : Initialize generic EHCI host controller.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to Host controller driver structure
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function
|
|
* USBH_ERR_NONE HCD initialized successfully.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Return(s) : None
|
|
*
|
|
* Note(s) : None
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void EHCI_Init (USBH_HC_DRV *p_hc_drv,
|
|
USBH_ERR *p_err)
|
|
{
|
|
EHCI_InitHandler(p_hc_drv, EHCI_HCD_GENERIC, p_err);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_SynopsysInit()
|
|
*
|
|
* Description : Initialize EHCI host controller for MCUs that contain the Synopsys USB 2.0 Host Atlantic
|
|
* IP.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to Host controller driver structure
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function
|
|
* USBH_ERR_NONE HCD initialized successfully.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Return(s) : None
|
|
*
|
|
* Note(s) : None
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void EHCI_SynopsysInit (USBH_HC_DRV *p_hc_drv,
|
|
USBH_ERR *p_err)
|
|
{
|
|
EHCI_InitHandler(p_hc_drv, EHCI_HCD_SYNOPSYS, p_err);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_InitHandler()
|
|
*
|
|
* Description : Initialize EHCI host controller, issue EHC hardware reset, initialize periodic
|
|
* frame list size, initialize asynchronous and periodic lists, run host controller,
|
|
* and enable interrupts
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to Host controller driver structure
|
|
*
|
|
* ehci_drv_type EHCI driver type:
|
|
*
|
|
* EHCI_HCD_GENERIC Generic EHCI driver.
|
|
* EHCI_HCD_SYNOPSYS EHCI driver for MCU containing the Synopsys
|
|
* USB 2.0 Host Atlantic controller IP.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function
|
|
* USBH_ERR_NONE HCD initialized successfully.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Return(s) : None
|
|
*
|
|
* Note(s) : None
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void EHCI_InitHandler (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U ehci_drv_type,
|
|
USBH_ERR *p_err)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
CPU_REG32 usb_cmd;
|
|
CPU_SIZE_T octets_reqd;
|
|
CPU_ADDR base_addr;
|
|
LIB_ERR err_lib;
|
|
USBH_HC_CFG *p_hc_cfg;
|
|
USBH_HC_BSP_API *p_bsp_api;
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
CPU_INT16U frame_nbr;
|
|
CPU_INT08U micro_frame_nbr;
|
|
#endif
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)Mem_HeapAlloc(sizeof(EHCI_DEV),
|
|
sizeof(CPU_ALIGN),
|
|
&octets_reqd,
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
*p_err = USBH_ERR_ALLOC;
|
|
return;
|
|
}
|
|
|
|
Mem_Clr(p_ehci, sizeof(EHCI_DEV));
|
|
|
|
p_hc_drv->DataPtr = (void *)p_ehci;
|
|
p_ehci->HC_Started = DEF_FALSE;
|
|
p_hc_cfg = p_hc_drv->HC_CfgPtr;
|
|
p_bsp_api = p_hc_drv->BSP_API_Ptr;
|
|
|
|
if ((p_bsp_api != (USBH_HC_BSP_API *)0) &&
|
|
(p_bsp_api->Init != 0)) {
|
|
p_bsp_api->Init(p_hc_drv, p_err);
|
|
if (*p_err != USBH_ERR_NONE) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
base_addr = p_hc_cfg->BaseAddr;
|
|
p_ehci->HcCapReg = (EHCI_CAP_REG *)base_addr; /* EHCI Capability registers base address. */
|
|
EHCI_CapRegRead(p_ehci, &p_ehci->HcCap);
|
|
/* EHCI Operational registers base address. */
|
|
p_ehci->HcOperReg = (EHCI_OPER_REG *)((CPU_INT32U)base_addr + p_ehci->HcCap.CapLen);
|
|
|
|
*p_err = EHCI_DMA_Init(p_hc_drv); /* Initialize memory pool. */
|
|
if (*p_err != USBH_ERR_NONE) {
|
|
return;
|
|
}
|
|
|
|
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
|
|
USBH_PRINT_LOG("EHCI Applying Hardware Reset...\r\n");
|
|
#endif
|
|
|
|
USBCMD = EHCI_USBCMD_RD_HCR; /* Apply hardware reset. */
|
|
do {
|
|
usb_cmd = USBCMD;
|
|
} while ((usb_cmd & EHCI_USBCMD_RD_HCR) != 0u); /* Wait for the reset completion. */
|
|
|
|
p_ehci->DrvType = ehci_drv_type;
|
|
if (p_ehci->DrvType == EHCI_HCD_SYNOPSYS) { /* Set ctrlr in host mode. */
|
|
CPU_INT32U reg_val;
|
|
|
|
|
|
reg_val = EHCI_SYNOPSYS_USBMODE;
|
|
DEF_BIT_SET(reg_val, EHCI_SYNOPSYS_USBMODE_WR_CM_HOST);
|
|
EHCI_SYNOPSYS_USBMODE = reg_val;
|
|
}
|
|
|
|
#if (EHCI_CFG_ONRESET_EN == DEF_ENABLED)
|
|
EHCI_OnReset(p_dev);
|
|
#endif
|
|
|
|
USBSTATUS = USBSTATUS;
|
|
if ((USBSTATUS & EHCI_USBSTS_RD_HC_HAL) == 0u) {
|
|
*p_err = USBH_ERR_HC_INIT;
|
|
return;
|
|
}
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
for (frame_nbr = 0u; frame_nbr < 256u; frame_nbr++) { /* Initialize the array used for BW allocation */
|
|
for (micro_frame_nbr = 0u; micro_frame_nbr < 8u; micro_frame_nbr++) {
|
|
p_ehci->MaxPeriodicBWArr[frame_nbr][micro_frame_nbr] = 3072u;
|
|
}
|
|
}
|
|
|
|
EHCI_PeriodicOrderPrepare(0u, 7u, 256u);
|
|
EHCI_PeriodicListInit(p_hc_drv);
|
|
#endif
|
|
|
|
EHCI_AsyncListInit(p_hc_drv);
|
|
|
|
p_ehci->NbrPorts = p_ehci->HcCap.HCSParams & EHCI_HCSPARAMS_RD_NP;
|
|
|
|
USBINT = DEF_BIT_NONE;
|
|
|
|
*p_err = USBH_ERR_NONE;
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_Start()
|
|
*
|
|
* Description : Start EHCI Host controller
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function
|
|
* USBH_ERR_NONE HCD start successful.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Return(s) : None
|
|
*
|
|
* Note(s) : None
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void EHCI_Start (USBH_HC_DRV *p_hc_drv,
|
|
USBH_ERR *p_err)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
USBH_HC_BSP_API *p_bsp_api;
|
|
CPU_REG32 usb_cmd;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
p_bsp_api = p_hc_drv->BSP_API_Ptr;
|
|
|
|
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
|
|
USBH_PRINT_LOG("EHCI Enabling interrupts...\r\n");
|
|
#endif
|
|
|
|
if ((p_bsp_api != (USBH_HC_BSP_API *)0) &&
|
|
(p_bsp_api->ISR_Reg != 0)) {
|
|
p_bsp_api->ISR_Reg(EHCI_ISR, p_err);
|
|
|
|
if (*p_err != USBH_ERR_NONE) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
p_ehci->HC_Started = DEF_TRUE;
|
|
|
|
CFGFLAG = EHCI_CONFIGFLAG_WR_CF; /* Route all ports to EHCI */
|
|
|
|
usb_cmd = USBCMD;
|
|
DEF_BIT_SET(usb_cmd, EHCI_USBCMD_RD_FLS_256);
|
|
DEF_BIT_SET(usb_cmd, DEF_BIT_09 | DEF_BIT_08);
|
|
DEF_BIT_SET(usb_cmd, EHCI_USBCMD_RD_RS);
|
|
USBCMD = usb_cmd;
|
|
|
|
/* Enable all the required interrupts. */
|
|
USBINT |= EHCI_USBINTR_WR_USBIE | /* USB Interrupt Enable. */
|
|
EHCI_USBINTR_WR_USBEIE | /* USB Error Interrupt Enable. */
|
|
EHCI_USBINTR_WR_HSEE | /* Host System Error Enable. */
|
|
EHCI_USBINTR_WR_FLRE | /* Frame List Rollover Enable. */
|
|
EHCI_USBINTR_WR_IOAAE | /* Interrupt on Async Advance Enable. */
|
|
EHCI_USBINTR_WR_PCIE;
|
|
|
|
*p_err = USBH_ERR_NONE;
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_Stop()
|
|
*
|
|
* Description : Stop EHCI Host controller
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function
|
|
* USBH_ERR_NONE HCD stopped successfully.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Return(s) : USBH_ERR_NOT_SUPPORTED
|
|
*
|
|
* Note(s) : None
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void EHCI_Stop (USBH_HC_DRV *p_hc_drv,
|
|
USBH_ERR *p_err)
|
|
{
|
|
(void)p_hc_drv;
|
|
|
|
*p_err = USBH_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_SpdGet()
|
|
*
|
|
* Description : Returns Host Controller Speed
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function
|
|
* USBH_ERR_NONE Host controller speed retrieved successfuly.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Return(s) : Host controller speed.
|
|
*
|
|
* Note(s) : None
|
|
*
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static USBH_DEV_SPD EHCI_SpdGet (USBH_HC_DRV *p_hc_drv,
|
|
USBH_ERR *p_err)
|
|
{
|
|
(void)p_hc_drv;
|
|
|
|
*p_err = USBH_ERR_NONE;
|
|
return (USBH_DEV_SPD_HIGH); /* EHCI controller supports HS. */
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_Suspend()
|
|
*
|
|
* Description : Suspend Host controller
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function
|
|
* USBH_ERR_NONE HCD suspend successful.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Return(s) : None
|
|
*
|
|
* Note(s) : None
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void EHCI_Suspend (USBH_HC_DRV *p_hc_drv,
|
|
USBH_ERR *p_err)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
CPU_INT08U port_nbr;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
|
|
for (port_nbr = 1u; port_nbr <= p_ehci->NbrPorts; port_nbr++) {
|
|
EHCI_PortSuspendSet(p_ehci, port_nbr);
|
|
}
|
|
|
|
USBCMD &= ~EHCI_USBCMD_RD_RS;
|
|
|
|
*p_err = USBH_ERR_NONE;
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_Resume()
|
|
*
|
|
* Description : Resume Host controller
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function
|
|
* USBH_ERR_NONE HCD resume successful.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Return(s) : None
|
|
*
|
|
* Note(s) : None
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void EHCI_Resume (USBH_HC_DRV *p_hc_drv,
|
|
USBH_ERR *p_err)
|
|
{
|
|
CPU_INT08U port_nbr;
|
|
EHCI_DEV *p_ehci;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
|
|
while ((USBSTATUS & EHCI_USBSTS_RD_HC_HAL) == 0u) {
|
|
;
|
|
}
|
|
|
|
USBCMD |= EHCI_USBCMD_RD_RS;
|
|
|
|
for (port_nbr = 1u; port_nbr <= p_ehci->NbrPorts; port_nbr++) {
|
|
EHCI_PortSuspendClr(p_hc_drv, port_nbr);
|
|
}
|
|
|
|
*p_err = USBH_ERR_NONE;
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_FrameNbrGet()
|
|
*
|
|
* Description : Retrieve current frame number.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function
|
|
* USBH_ERR_NONE HC frame number retrieved successfuly.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Return(s) : Frame number.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_INT32U EHCI_FrameNbrGet (USBH_HC_DRV *p_hc_drv,
|
|
USBH_ERR *p_err)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
CPU_INT32U frame_nbr;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
frame_nbr = FRAMEIX;
|
|
frame_nbr = (frame_nbr & 0x000007F8u) >> 3u; /* Bit[10..3] = current frame number */
|
|
|
|
*p_err = USBH_ERR_NONE;
|
|
|
|
return (frame_nbr);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_EP_Open()
|
|
*
|
|
* Description : Create queue head structure for the given endpoint
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_ep Pointer to endpoint structure
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function
|
|
* USBH_ERR_NONE Endpoint open successfully.
|
|
* USBH_ERR_EP_INVALID_TYPE, If p_hc_drv, or p_ep is 0
|
|
* Specific error code otherwise.
|
|
*
|
|
* Return(s) : None
|
|
*
|
|
* Note(s) : (1) Handle Cache Coherency for the p_ehci->AsyncQHHead data structure
|
|
*
|
|
* (2) See USB2.0 specification, section 9.6.6. Interval for polling endpoint for data
|
|
* transfers can be obtained from the equataion 2 POW (bInterval - 1)
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void EHCI_EP_Open (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
USBH_ERR *p_err)
|
|
{
|
|
CPU_INT08U ep_type;
|
|
|
|
|
|
ep_type = USBH_EP_TypeGet(p_ep);
|
|
|
|
switch(ep_type) {
|
|
case USBH_EP_TYPE_CTRL:
|
|
case USBH_EP_TYPE_BULK:
|
|
*p_err = EHCI_AsyncEP_Open(p_hc_drv,
|
|
p_ep,
|
|
p_ep->DevPtr);
|
|
break;
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
case USBH_EP_TYPE_INTR:
|
|
*p_err = EHCI_IntrEP_Open(p_hc_drv,
|
|
p_ep,
|
|
p_ep->DevPtr);
|
|
break;
|
|
|
|
case USBH_EP_TYPE_ISOC:
|
|
*p_err = EHCI_IsocEP_Open(p_hc_drv, p_ep);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
*p_err = USBH_ERR_EP_INVALID_TYPE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_EP_Close()
|
|
*
|
|
* Description : Close the endpoint by unlinking the EHCI queue head
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_ep Pointer to endpoint structure.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function
|
|
* USBH_ERR_NONE Endpoint closed successfully.
|
|
* USBH_ERR_EP_INVALID_TYPE, If p_hc_drv, or p_ep is 0
|
|
* Specific error code otherwise.
|
|
*
|
|
* Return(s) : None
|
|
*
|
|
* Note(s) : None
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void EHCI_EP_Close (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
USBH_ERR *p_err)
|
|
{
|
|
CPU_INT08U ep_type;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
ep_type = USBH_EP_TypeGet(p_ep);
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
|
|
switch (ep_type) {
|
|
case USBH_EP_TYPE_CTRL:
|
|
case USBH_EP_TYPE_BULK:
|
|
*p_err = EHCI_AsyncEP_Close(p_hc_drv, p_ep);
|
|
break;
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
case USBH_EP_TYPE_INTR:
|
|
*p_err = EHCI_IntrEP_Close(p_hc_drv, p_ep);
|
|
break;
|
|
|
|
case USBH_EP_TYPE_ISOC:
|
|
*p_err = EHCI_IsocEP_Close(p_hc_drv, p_ep);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
*p_err = USBH_ERR_EP_INVALID_TYPE;
|
|
break;
|
|
}
|
|
|
|
CPU_CRITICAL_EXIT();
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_EP_Abort()
|
|
*
|
|
* Description : Abort all pending URBs in the queue head.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_ep Pointer to endpoint structure.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function
|
|
* USBH_ERR_NONE Endpoint abort successfully.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Return(s) : None
|
|
*
|
|
* Note(s) : None
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void EHCI_EP_Abort (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
USBH_ERR *p_err)
|
|
{
|
|
(void)p_hc_drv;
|
|
(void)p_ep;
|
|
|
|
*p_err = USBH_ERR_NONE;
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_IsHaltEP()
|
|
*
|
|
* Description : Retrieve endpoint halt state.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_ep Pointer to endpoint structure.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function
|
|
* USBH_ERR_NONE If successful.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Return(s) : DEF_TRUE, if halted.
|
|
* DEF_FALSE, otherwise.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN EHCI_IsHalt_EP (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
USBH_ERR *p_err)
|
|
{
|
|
(void)p_hc_drv;
|
|
(void)p_ep;
|
|
|
|
*p_err = USBH_ERR_NONE;
|
|
|
|
return (DEF_FALSE);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_URB_Submit()
|
|
*
|
|
* Description : Insert the QTD list head into the appropriate QH.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_urb Pointer to URB structure.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function
|
|
* USBH_ERR_NONE URB submitted successfuly.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Return(s) : None
|
|
*
|
|
* Note(s) : (1) When the CPU cache is enabled, this code ensures that the buffer start address is
|
|
* aligned on the cache line size. If not, the nearest address from the initial buffer
|
|
* start address is computed. This address is aligned on the cache line. The number of
|
|
* octets to flush or invalidate will be increased accordingly to take into account
|
|
* the buffer size plus the address adjustment.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void EHCI_URB_Submit (USBH_HC_DRV *p_hc_drv,
|
|
USBH_URB *p_urb,
|
|
USBH_ERR *p_err)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
EHCI_QH *p_qh;
|
|
EHCI_QTD *p_head_qtd;
|
|
CPU_INT08U ep_type;
|
|
LIB_ERR err_lib;
|
|
USBH_HC_CFG *p_hc_cfg;
|
|
USBH_EP *p_ep;
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
EHCI_ISOC_EP_DESC *p_ep_desc;
|
|
USBH_DEV *p_dev;
|
|
#endif
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
p_hc_cfg = p_hc_drv->HC_CfgPtr;
|
|
p_ep = p_urb->EP_Ptr;
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
p_dev = p_ep->DevPtr;
|
|
#endif
|
|
ep_type = USBH_EP_TypeGet(p_ep);
|
|
|
|
/* ----------- DATA BUF FROM DEDICATED MEM ------------ */
|
|
if ((p_hc_cfg->DedicatedMemAddr != (CPU_ADDR)0) &&
|
|
(p_hc_cfg->DataBufFromSysMemEn == DEF_DISABLED)) {
|
|
|
|
if (ep_type == USBH_EP_TYPE_ISOC) {
|
|
if (p_urb->UserBufLen > p_hc_cfg->DataBufMaxLen) {
|
|
*p_err = USBH_ERR_ALLOC;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (p_urb->UserBufLen != 0u) {
|
|
|
|
p_urb->DMA_BufPtr = Mem_PoolBlkGet(&p_ehci->BufPool,
|
|
p_hc_cfg->DataBufMaxLen,
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
*p_err = USBH_ERR_ALLOC;
|
|
return;
|
|
}
|
|
|
|
p_urb->DMA_BufLen = DEF_MIN(p_urb->UserBufLen,
|
|
p_hc_cfg->DataBufMaxLen);
|
|
|
|
if ((p_urb->Token == USBH_TOKEN_OUT ) ||
|
|
(p_urb->Token == USBH_TOKEN_SETUP)) {
|
|
|
|
Mem_Copy(p_urb->DMA_BufPtr,
|
|
p_urb->UserBufPtr,
|
|
p_urb->DMA_BufLen);
|
|
|
|
CPU_DCACHE_RANGE_FLUSH(p_urb->DMA_BufPtr, p_urb->DMA_BufLen);
|
|
} else {
|
|
CPU_DCACHE_RANGE_FLUSHINV(p_urb->DMA_BufPtr, p_urb->DMA_BufLen);
|
|
}
|
|
}
|
|
} else { /* ------------- DATA BUF FROM SYSTEM MEM ------------- */
|
|
#if (CPU_CFG_CACHE_MGMT_EN == DEF_ENABLED)
|
|
CPU_INT08U *p_cache_aligned_buf_addr = DEF_NULL;
|
|
CPU_INT32U len;
|
|
CPU_INT08U remainder;
|
|
#endif
|
|
|
|
p_urb->DMA_BufPtr = p_urb->UserBufPtr;
|
|
p_urb->DMA_BufLen = p_urb->UserBufLen;
|
|
|
|
#if (CPU_CFG_CACHE_MGMT_EN == DEF_ENABLED) /* See Note #1. */
|
|
remainder = (CPU_INT08U)(((CPU_INT32U)p_urb->DMA_BufPtr) % CONFIG_ARCH_CACHE_LINE);
|
|
if (remainder != 0u) {
|
|
p_cache_aligned_buf_addr = ((CPU_INT08U *)p_urb->DMA_BufPtr) - remainder;
|
|
len = p_urb->DMA_BufLen + remainder;
|
|
} else {
|
|
p_cache_aligned_buf_addr = (CPU_INT08U *)p_urb->DMA_BufPtr;
|
|
len = p_urb->DMA_BufLen;
|
|
}
|
|
|
|
if (((p_urb->Token == USBH_TOKEN_OUT ) ||
|
|
(p_urb->Token == USBH_TOKEN_SETUP)) &&
|
|
(p_urb->DMA_BufLen != 0u)) {
|
|
|
|
CPU_DCACHE_RANGE_FLUSH(p_cache_aligned_buf_addr, len);
|
|
} else {
|
|
CPU_DCACHE_RANGE_FLUSHINV(p_cache_aligned_buf_addr, len);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if ((ep_type == USBH_EP_TYPE_CTRL) ||
|
|
(ep_type == USBH_EP_TYPE_BULK) ||
|
|
(ep_type == USBH_EP_TYPE_INTR)) {
|
|
|
|
p_qh = (EHCI_QH *)p_ep->ArgPtr;
|
|
|
|
p_head_qtd = EHCI_QTDListPrepare(p_hc_drv,
|
|
p_ep,
|
|
p_urb,
|
|
(p_urb->DMA_BufLen) ? (CPU_INT08U *)(p_urb->DMA_BufPtr) : (CPU_INT08U *)0,
|
|
p_urb->DMA_BufLen,
|
|
p_err);
|
|
if (p_head_qtd == 0) {
|
|
return;
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_qh->QTDHead = (CPU_INT32U)p_head_qtd;
|
|
p_qh->QHNxtQTDPtr = (CPU_INT32U)USBH_OS_VirToBus((void *)p_head_qtd);
|
|
CPU_DCACHE_RANGE_FLUSH(p_qh, sizeof(EHCI_QH));
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
}
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
else {
|
|
p_ep_desc = (EHCI_ISOC_EP_DESC *)p_ep->ArgPtr;
|
|
|
|
if (p_ep->DevSpd == USBH_DEV_SPD_FULL) {
|
|
*p_err = EHCI_SITDListPrepare(p_hc_drv,
|
|
p_dev,
|
|
p_ep,
|
|
p_ep_desc,
|
|
p_urb,
|
|
(p_urb->DMA_BufLen) ? (CPU_INT08U *)(p_urb->DMA_BufPtr) : (CPU_INT08U *)0);
|
|
} else {
|
|
*p_err = EHCI_ITDListPrepare(p_hc_drv,
|
|
p_ep,
|
|
p_ep_desc,
|
|
p_urb,
|
|
(p_urb->DMA_BufLen) ? (CPU_INT08U *)(p_urb->DMA_BufPtr) : (CPU_INT08U *)0,
|
|
p_urb->DMA_BufLen);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_URB_Complete()
|
|
*
|
|
* Description : Transfer received data to application buffer, and release DMA buffer, if DMA is enabled.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_urb Pointer to URB structure.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function
|
|
* USBH_ERR_NONE URB completed successfuly.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Return(s) : None
|
|
*
|
|
* Note(s) : (1) See Note #1 in function 'EHCI_URB_Submit()'.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void EHCI_URB_Complete (USBH_HC_DRV *p_hc_drv,
|
|
USBH_URB *p_urb,
|
|
USBH_ERR *p_err)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
LIB_ERR err_lib;
|
|
USBH_HC_CFG *p_hc_cfg;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
p_hc_cfg = p_hc_drv->HC_CfgPtr;
|
|
/* ----------- DATA BUF FROM DEDICATED MEM ------------ */
|
|
if ((p_hc_cfg->DedicatedMemAddr != (CPU_ADDR)0) &&
|
|
(p_hc_cfg->DataBufFromSysMemEn == DEF_DISABLED)) {
|
|
|
|
if ((p_urb->UserBufPtr != p_urb->DMA_BufPtr) &&
|
|
(p_urb->DMA_BufPtr != (void *)0 )) {
|
|
|
|
if ((p_urb->Token == USBH_TOKEN_IN) &&
|
|
(p_urb->XferLen != 0u )) {
|
|
#if (CPU_CFG_CACHE_MGMT_EN == DEF_ENABLED)
|
|
CPU_INT08U *p_cache_aligned_buf_addr = DEF_NULL;
|
|
CPU_INT32U len;
|
|
CPU_INT08U remainder;
|
|
#endif
|
|
|
|
Mem_Copy(p_urb->UserBufPtr,
|
|
p_urb->DMA_BufPtr,
|
|
p_urb->XferLen);
|
|
|
|
#if (CPU_CFG_CACHE_MGMT_EN == DEF_ENABLED) /* See Note #1. */
|
|
remainder = (CPU_INT08U)(((CPU_INT32U)p_urb->UserBufPtr) % CONFIG_ARCH_CACHE_LINE);
|
|
if (remainder != 0u) {
|
|
p_cache_aligned_buf_addr = ((CPU_INT08U *)p_urb->UserBufPtr) - remainder;
|
|
len = p_urb->XferLen + remainder;
|
|
} else {
|
|
p_cache_aligned_buf_addr = (CPU_INT08U *)p_urb->UserBufPtr;
|
|
len = p_urb->XferLen;
|
|
}
|
|
#endif
|
|
CPU_DCACHE_RANGE_FLUSH(p_cache_aligned_buf_addr, len);
|
|
}
|
|
|
|
Mem_PoolBlkFree(&p_ehci->BufPool,
|
|
p_urb->DMA_BufPtr,
|
|
&err_lib);
|
|
}
|
|
|
|
} else { /* ------------- DATA BUF FROM SYSTEM MEM ------------- */
|
|
|
|
if ((p_urb->Token == USBH_TOKEN_IN) &&
|
|
(p_urb->XferLen != 0u )) {
|
|
|
|
CPU_DCACHE_RANGE_INV(p_urb->DMA_BufPtr, p_urb->XferLen);
|
|
}
|
|
}
|
|
|
|
*p_err = USBH_ERR_NONE;
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_URB_Abort()
|
|
*
|
|
* Description : Abort pending transfer.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_urb Pointer to URB structure.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function
|
|
* USBH_ERR_NONE URB aborted successfuly.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Return(s) : None
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void EHCI_URB_Abort (USBH_HC_DRV *p_hc_drv,
|
|
USBH_URB *p_urb,
|
|
USBH_ERR *p_err)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
USBH_HC_CFG *p_hc_cfg;
|
|
LIB_ERR err_lib;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
p_hc_cfg = p_hc_drv->HC_CfgPtr;
|
|
p_urb->Err = USBH_ERR_URB_ABORT;
|
|
|
|
if (p_hc_cfg->DedicatedMemAddr != (CPU_ADDR)0) {
|
|
|
|
if (p_urb->DMA_BufPtr != (void *)0) {
|
|
|
|
Mem_PoolBlkFree(&p_ehci->BufPool,
|
|
p_urb->DMA_BufPtr,
|
|
&err_lib);
|
|
}
|
|
}
|
|
|
|
*p_err = USBH_ERR_NONE;
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_AsyncEP_Open()
|
|
*
|
|
* Description : Open a Control or Bulk Endpoint
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_ep Pointer to endpoint structure
|
|
*
|
|
* p_dev Pointer to device structure
|
|
*
|
|
* Return(s) : USBH_ERR_NONE If successful
|
|
*
|
|
* Note(s) : qH structure and fields. For more details, see section 3.6 (EHCI spec).
|
|
*
|
|
* ---------------------------------------------------------------------------------------
|
|
* |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0|
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Queue Head Horizontal Link Pointer | 0 |T|
|
|
* ---------------------------------------------------------------------------------------
|
|
* | RL |C | Maximum Packet Length |H |dtc|EPS | EndPt |I| Device Addr |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Mult | Port Number | Hub Addr | uFrame C-mask | uFrame S-mask |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Current qTD Pointer | 0 |
|
|
* ---------------------------------------------------------------------------------------
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static USBH_ERR EHCI_AsyncEP_Open (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
USBH_DEV *p_dev)
|
|
{
|
|
EHCI_QH *p_new_qh;
|
|
EHCI_DEV *p_ehci;
|
|
CPU_INT08U ep_nbr;
|
|
CPU_INT08U ep_type;
|
|
CPU_INT16U ep_max_pkt_size;
|
|
LIB_ERR err_lib;
|
|
USBH_DEV *ptemp_dev;
|
|
CPU_INT08U retry;
|
|
USBH_ERR err;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
/* Allocate memory for a queue head */
|
|
p_new_qh = (EHCI_QH *)Mem_PoolBlkGet(&p_ehci->HC_QHPool,
|
|
sizeof(EHCI_QH),
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
EHCI_QH_Clr(p_new_qh);
|
|
|
|
p_new_qh->EPPtr = p_ep;
|
|
p_ep->ArgPtr = (void *)p_new_qh;
|
|
ep_nbr = USBH_EP_LogNbrGet(p_ep);
|
|
ep_type = USBH_EP_TypeGet(p_ep);
|
|
ep_max_pkt_size = USBH_EP_MaxPktSizeGet(p_ep);
|
|
|
|
p_new_qh->QHHorLinkPtr = HOR_LNK_PTR_TYP(DWORD1_TYP_QH);
|
|
p_new_qh->QHEpCapChar[0] = QH_EPCHAR_DEVADD(p_ep->DevAddr) |
|
|
QH_EPCHAR_I(0) |
|
|
QH_EPCHAR_ENDPT(ep_nbr) |
|
|
((ep_type == USBH_EP_TYPE_CTRL) ? QH_EPCHAR_DTC(DWORD2_QH_DTC_QTD) :
|
|
QH_EPCHAR_DTC(DWORD2_QH_DTC_QH)) |
|
|
QH_EPCHAR_H(0u) |
|
|
QH_EPCHAR_MPL(ep_max_pkt_size) |
|
|
QH_EPCHAR_C(0u) |
|
|
QH_EPCHAR_RL(0xFu);
|
|
|
|
switch (p_ep->DevSpd) {
|
|
|
|
case USBH_DEV_SPD_LOW: /* For low speed devices, use split transactions */
|
|
if (ep_type == USBH_EP_TYPE_CTRL) {
|
|
p_new_qh->QHEpCapChar[0] |= QH_EPCHAR_C(DWORD2_QH_C);
|
|
}
|
|
|
|
p_new_qh->QHEpCapChar[0] |= QH_EPCHAR_EPS(DWORD2_QH_EPS_LS);
|
|
/* Search Hub 2.0 the nearest of LS dev. */
|
|
ptemp_dev = p_dev;
|
|
while (ptemp_dev->HubDevPtr->DevSpd != USBH_DEV_SPD_HIGH) {
|
|
ptemp_dev = ptemp_dev->HubDevPtr;
|
|
}
|
|
/* Set Hub addr of the nearest Hub 2.0 and port nbr... */
|
|
/* ...of the Hub 2.0 to which the dev is attached. */
|
|
if (ptemp_dev->HubDevPtr->IsRootHub == DEF_NO) {
|
|
p_new_qh->QHEpCapChar[1] = QH_EPCAP_HUBADD(ptemp_dev->HubDevPtr->DevAddr) |
|
|
QH_EPCAP_PN(ptemp_dev->PortNbr);
|
|
} else {
|
|
p_new_qh->QHEpCapChar[1] = QH_EPCAP_HUBADD(0u) |
|
|
QH_EPCAP_PN(0u);
|
|
}
|
|
break;
|
|
|
|
case USBH_DEV_SPD_FULL: /* For full speed devices, use split transactions */
|
|
if (ep_type == USBH_EP_TYPE_CTRL) {
|
|
p_new_qh->QHEpCapChar[0] |= QH_EPCHAR_C(DWORD2_QH_C);
|
|
}
|
|
|
|
p_new_qh->QHEpCapChar[0] |= QH_EPCHAR_EPS(DWORD2_QH_EPS_FS);
|
|
/* Search Hub 2.0 the nearest of FS dev. */
|
|
ptemp_dev = p_dev;
|
|
while (ptemp_dev->HubDevPtr->DevSpd != USBH_DEV_SPD_HIGH) {
|
|
ptemp_dev = ptemp_dev->HubDevPtr;
|
|
}
|
|
/* Set Hub addr of the nearest Hub 2.0 and port nbr... */
|
|
/* ...of the Hub 2.0 to which the dev is attached. */
|
|
if (ptemp_dev->HubDevPtr->IsRootHub == DEF_NO) {
|
|
p_new_qh->QHEpCapChar[1] = QH_EPCAP_HUBADD(ptemp_dev->HubDevPtr->DevAddr) |
|
|
QH_EPCAP_PN(ptemp_dev->PortNbr);
|
|
} else {
|
|
p_new_qh->QHEpCapChar[1] = QH_EPCAP_HUBADD(0u) |
|
|
QH_EPCAP_PN(0u);
|
|
}
|
|
break;
|
|
|
|
case USBH_DEV_SPD_HIGH:
|
|
p_new_qh->QHEpCapChar[0] |= QH_EPCHAR_EPS(DWORD2_QH_EPS_HS);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
p_new_qh->QHEpCapChar[1] |= QH_EPCAP_HBPM(DWORD3_QH_HBPM_1) |
|
|
QH_EPCAP_SMASK(0u);
|
|
p_new_qh->QHCurQTDPtr = (CPU_INT32U)0;
|
|
p_new_qh->QHNxtQTDPtr = (CPU_INT32U)0x00000001;
|
|
p_new_qh->QHAltNxtQTDPtr = (CPU_INT32U)0x00000001;
|
|
p_new_qh->QHToken = (CPU_INT32U)0;
|
|
p_new_qh->QHBufPagePtrList[0] = (CPU_INT32U)0;
|
|
p_new_qh->QHBufPagePtrList[1] = (CPU_INT32U)0;
|
|
p_new_qh->QHBufPagePtrList[2] = (CPU_INT32U)0;
|
|
p_new_qh->QHBufPagePtrList[3] = (CPU_INT32U)0;
|
|
p_new_qh->QHBufPagePtrList[4] = (CPU_INT32U)0;
|
|
CPU_DCACHE_RANGE_INV(p_ehci->AsyncQHHead, sizeof(EHCI_QH));
|
|
p_new_qh->QHHorLinkPtr |= (CPU_INT32U)(p_ehci->AsyncQHHead->QHHorLinkPtr & 0xFFFFFFE0);
|
|
|
|
CPU_DCACHE_RANGE_FLUSH(p_new_qh, sizeof(EHCI_QH));
|
|
|
|
USBCMD &= ~EHCI_USBCMD_RD_ASE; /* Disable async list processing */
|
|
retry = 100u;
|
|
while ((USBSTATUS & EHCI_USBSTS_RD_ASS) != 0u) { /* Wait until the async list processing is disabled */
|
|
retry--;
|
|
if (retry == 0) {
|
|
Mem_PoolBlkFree( &p_ehci->HC_QHPool,
|
|
(void *)p_new_qh,
|
|
&err_lib);
|
|
|
|
p_ep->ArgPtr = (void *)0;
|
|
err = USBH_ERR_EP_ALLOC;
|
|
return (err);
|
|
}
|
|
USBH_OS_DlyMS(1u);
|
|
}
|
|
/* Insert new queue head */
|
|
p_ehci->AsyncQHHead->QHHorLinkPtr = (CPU_INT32U)USBH_OS_VirToBus(p_new_qh) |
|
|
HOR_LNK_PTR_TYP(DWORD1_TYP_QH) |
|
|
HOR_LNK_PTR_T(DWORD1_T_VALID);
|
|
|
|
CPU_DCACHE_RANGE_FLUSH(p_ehci->AsyncQHHead, sizeof(EHCI_QH));
|
|
|
|
USBCMD |= EHCI_USBCMD_WR_ASE; /* Enable async list processing */
|
|
|
|
retry = 100u;
|
|
while ((USBSTATUS & EHCI_USBSTS_RD_ASS) == 0u) { /* Wait till async list processing is enabled */
|
|
retry--;
|
|
if (retry == 0u) {
|
|
Mem_PoolBlkFree( &p_ehci->HC_QHPool,
|
|
(void *)p_new_qh,
|
|
&err_lib);
|
|
|
|
p_ep->ArgPtr = (void *)0;
|
|
err = USBH_ERR_EP_ALLOC;
|
|
return (err);
|
|
}
|
|
USBH_OS_DlyMS(1u);
|
|
}
|
|
|
|
USBCMD |= EHCI_USBCMD_WR_IOAAD;
|
|
|
|
err = USBH_ERR_NONE;
|
|
return (err);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_IntrEP_Open()
|
|
*
|
|
* Description : Open an Interrupt endpoint
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_ep Pointer to endpoint structure
|
|
*
|
|
* p_dev Pointer to device structure
|
|
*
|
|
* Return(s) : USBH_ERR_NONE If successful
|
|
*
|
|
* Note(s) : qH structure and fields. For more details, see section 3.6 (EHCI spec).
|
|
*
|
|
* ---------------------------------------------------------------------------------------
|
|
* |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0|
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Queue Head Horizontal Link Pointer | 0 |T|
|
|
* ---------------------------------------------------------------------------------------
|
|
* | RL |C | Maximum Packet Length |H |dtc|EPS | EndPt |I| Device Addr |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Mult | Port Number | Hub Addr | uFrame C-mask | uFrame S-mask |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Current qTD Pointer | 0 |
|
|
* ---------------------------------------------------------------------------------------
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static USBH_ERR EHCI_IntrEP_Open (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
USBH_DEV *p_dev)
|
|
{
|
|
EHCI_QH *p_new_qh;
|
|
EHCI_DEV *p_ehci;
|
|
CPU_INT08U ep_nbr;
|
|
CPU_INT16U ep_max_pkt_size;
|
|
USBH_ERR err;
|
|
LIB_ERR err_lib;
|
|
CPU_INT16U nbr_of_transaction_per_uframe;
|
|
USBH_DEV *ptemp_dev;
|
|
EHCI_INTR_INFO *p_intr_info;
|
|
EHCI_INTR_INFO *p_temp_intr_info;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
/* Allocate memory for a queue head */
|
|
p_new_qh = (EHCI_QH *)Mem_PoolBlkGet(&p_ehci->HC_QHPool,
|
|
sizeof(EHCI_QH),
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
EHCI_QH_Clr(p_new_qh); /* Clear QH structure */
|
|
|
|
p_new_qh->EPPtr = p_ep;
|
|
p_ep->ArgPtr = (void *)p_new_qh;
|
|
ep_nbr = USBH_EP_LogNbrGet(p_ep);
|
|
ep_max_pkt_size = USBH_EP_MaxPktSizeGet(p_ep);
|
|
|
|
p_new_qh->QHHorLinkPtr = HOR_LNK_PTR_TYP(DWORD1_TYP_QH) |
|
|
HOR_LNK_PTR_T(DWORD1_T_INVALID);
|
|
|
|
|
|
p_new_qh->QHEpCapChar[0] = QH_EPCHAR_DEVADD(p_ep->DevAddr) |/* USB Device address */
|
|
QH_EPCHAR_ENDPT(ep_nbr) |/* Endpoint number */
|
|
QH_EPCHAR_DTC(DWORD2_QH_DTC_QH) |/* Use toggle bit from QH */
|
|
QH_EPCHAR_H(0u) |/* For interrut endpoint H-bit must be zero */
|
|
QH_EPCHAR_MPL(ep_max_pkt_size) |/* Endpoint max packet size */
|
|
QH_EPCHAR_C(0u) |/* C bit must be zero for non-control endpoints */
|
|
QH_EPCHAR_RL(0x00u); /* Reload NAK Cnt */
|
|
|
|
switch (p_ep->DevSpd) {
|
|
case USBH_DEV_SPD_LOW: /* For low speed devices, use split transactions */
|
|
/* Endpoint is low-speed. */
|
|
p_new_qh->QHEpCapChar[0] |= QH_EPCHAR_EPS(DWORD2_QH_EPS_LS);
|
|
|
|
/* Search Hub 2.0 the nearest of LS dev. */
|
|
ptemp_dev = p_dev;
|
|
while (ptemp_dev->HubDevPtr->DevSpd != USBH_DEV_SPD_HIGH) {
|
|
ptemp_dev = ptemp_dev->HubDevPtr;
|
|
}
|
|
/* Set Hub addr of the nearest Hub 2.0 and port nbr... */
|
|
/* ...of the Hub 2.0 to which the dev is attached. */
|
|
if (ptemp_dev->HubDevPtr->IsRootHub == DEF_NO) {
|
|
p_new_qh->QHEpCapChar[1] = QH_EPCAP_HUBADD(ptemp_dev->HubDevPtr->DevAddr) |
|
|
QH_EPCAP_PN(ptemp_dev->PortNbr);
|
|
} else {
|
|
p_new_qh->QHEpCapChar[1] = QH_EPCAP_HUBADD(0u) |
|
|
QH_EPCAP_PN(0u);
|
|
}
|
|
break;
|
|
|
|
case USBH_DEV_SPD_FULL: /* For full speed devices, use split transactions */
|
|
/* Endpoint is full-speed. */
|
|
p_new_qh->QHEpCapChar[0] |= QH_EPCHAR_EPS(DWORD2_QH_EPS_FS);
|
|
ptemp_dev = p_dev;
|
|
/* Search Hub 2.0 the nearest of LS dev. */
|
|
while (ptemp_dev->HubDevPtr->DevSpd != USBH_DEV_SPD_HIGH) {
|
|
ptemp_dev = ptemp_dev->HubDevPtr;
|
|
}
|
|
/* Set Hub addr of the nearest Hub 2.0 and port nbr... */
|
|
/* ...of the Hub 2.0 to which the dev is attached. */
|
|
if (ptemp_dev->HubDevPtr->IsRootHub == DEF_NO) {
|
|
p_new_qh->QHEpCapChar[1] = QH_EPCAP_HUBADD(ptemp_dev->HubDevPtr->DevAddr) |
|
|
QH_EPCAP_PN(ptemp_dev->PortNbr);
|
|
} else {
|
|
p_new_qh->QHEpCapChar[1] = QH_EPCAP_HUBADD(0u) |
|
|
QH_EPCAP_PN(0u);
|
|
}
|
|
break;
|
|
|
|
case USBH_DEV_SPD_HIGH:
|
|
p_new_qh->QHEpCapChar[0] |= QH_EPCHAR_EPS(DWORD2_QH_EPS_HS);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
nbr_of_transaction_per_uframe = (p_ep->Desc.wMaxPacketSize & USBH_NBR_TRANSACTION_PER_UFRAME) >> 11;
|
|
|
|
if (nbr_of_transaction_per_uframe == USBH_3_TRANSACTION_PER_UFRAME) {
|
|
p_new_qh->QHEpCapChar[1] |= (CPU_INT32U) QH_EPCAP_HBPM(DWORD3_QH_HBPM_3);
|
|
} else if (nbr_of_transaction_per_uframe == USBH_2_TRANSACTION_PER_UFRAME) {
|
|
p_new_qh->QHEpCapChar[1] |= (CPU_INT32U) QH_EPCAP_HBPM(DWORD3_QH_HBPM_2);
|
|
} else {
|
|
p_new_qh->QHEpCapChar[1] |= (CPU_INT32U) QH_EPCAP_HBPM(DWORD3_QH_HBPM_1);
|
|
}
|
|
|
|
p_new_qh->QHCurQTDPtr = (CPU_INT32U)0u;
|
|
p_new_qh->QHNxtQTDPtr = (CPU_INT32U)0x00000001u;
|
|
p_new_qh->QHAltNxtQTDPtr = (CPU_INT32U)0x00000001u;
|
|
p_new_qh->QHToken = (CPU_INT32U)0u;
|
|
p_new_qh->QHBufPagePtrList[0] = (CPU_INT32U)0u;
|
|
p_new_qh->QHBufPagePtrList[1] = (CPU_INT32U)0u;
|
|
p_new_qh->QHBufPagePtrList[2] = (CPU_INT32U)0u;
|
|
p_new_qh->QHBufPagePtrList[3] = (CPU_INT32U)0u;
|
|
p_new_qh->QHBufPagePtrList[4] = (CPU_INT32U)0u;
|
|
|
|
CPU_DCACHE_RANGE_FLUSH(p_new_qh, sizeof(EHCI_QH));
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
|
|
err = EHCI_BW_Get ( p_hc_drv,
|
|
p_ep,
|
|
(void *)p_new_qh);
|
|
|
|
if (err != USBH_ERR_NONE) {
|
|
Mem_PoolBlkFree( &p_ehci->HC_QHPool,
|
|
(void *)p_new_qh,
|
|
&err_lib);
|
|
|
|
p_ep->ArgPtr = (void *)0;
|
|
|
|
CPU_CRITICAL_EXIT();
|
|
return (err);
|
|
}
|
|
|
|
p_new_qh->QHEpCapChar[1] |= p_new_qh->SMask |
|
|
DWORD3_QH_PS_CSPLIT_UFRAME_2345 << 8;
|
|
|
|
CPU_DCACHE_RANGE_FLUSH(p_new_qh, sizeof(EHCI_QH));
|
|
|
|
EHCI_BW_Update( p_hc_drv,
|
|
p_ep,
|
|
(void *)p_new_qh,
|
|
DEF_TRUE);
|
|
|
|
|
|
p_intr_info = (EHCI_INTR_INFO *)Mem_PoolBlkGet(&p_ehci->IntrInfoPool,
|
|
sizeof(EHCI_INTR_INFO),
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
CPU_DCACHE_RANGE_INV(p_new_qh, sizeof(EHCI_QH));
|
|
p_intr_info->IntrPlaceholderIx = p_new_qh->BWStartFrame; /* Save placeholder index in QHLists array. */
|
|
p_intr_info->FrameInterval = p_new_qh->FrameInterval; /* Save polling interval list to which belongs qH. */
|
|
p_intr_info->EpPtr = p_ep;
|
|
p_intr_info->NxtIntrInfo = (EHCI_INTR_INFO *)0;
|
|
|
|
if (p_ehci->HeadIntrInfo == 0) { /* First Intr EP opened. */
|
|
p_ehci->HeadIntrInfo = p_intr_info; /* Init Intr info queue head ptr. */
|
|
} else { /* Other Intr EP opened. */
|
|
p_temp_intr_info = p_ehci->HeadIntrInfo; /* Retrieve the 1st Intr info. */
|
|
while (p_temp_intr_info->NxtIntrInfo != 0) { /* Find end of Intr info queue. */
|
|
p_temp_intr_info = p_temp_intr_info->NxtIntrInfo;
|
|
}
|
|
p_temp_intr_info->NxtIntrInfo = p_intr_info; /* Insert new Intr info at end of the queue. */
|
|
}
|
|
|
|
EHCI_IntrEPInsert(p_hc_drv, p_new_qh);
|
|
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
return (USBH_ERR_NONE);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_IsocEP_Open()
|
|
*
|
|
* Description : Open an Isochronous endpoint
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_ep Pointer to endpoint structure
|
|
*
|
|
* Return(s) : USBH_ERR_NONE If successful
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static USBH_ERR EHCI_IsocEP_Open (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep)
|
|
{
|
|
EHCI_ISOC_EP_DESC *p_ep_desc;
|
|
EHCI_ISOC_EP_DESC *p_temp_ep_desc;
|
|
EHCI_DEV *p_ehci;
|
|
USBH_ERR err;
|
|
LIB_ERR err_lib;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
p_ep_desc = (EHCI_ISOC_EP_DESC *)Mem_PoolBlkGet(&p_ehci->HC_Isoc_EP_DescPool,
|
|
sizeof(EHCI_ISOC_EP_DESC),
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
EHCI_EP_DescClr(p_ep_desc);
|
|
|
|
if (p_ehci->HeadIsocEPDesc == 0) { /* First Isochronous EP opened */
|
|
p_ehci->HeadIsocEPDesc = p_ep_desc; /* Init the Isochronous queue Head pointer */
|
|
} else { /* Other Isochronous EP opened */
|
|
p_temp_ep_desc = p_ehci->HeadIsocEPDesc; /* Retrieve the 1st Isoc EP */
|
|
while (p_temp_ep_desc->NxtEPDesc) { /* Find the end of the Isoc EP queue */
|
|
p_temp_ep_desc = p_temp_ep_desc->NxtEPDesc;
|
|
}
|
|
p_temp_ep_desc->NxtEPDesc = p_ep_desc; /* Insert the new Isoc EP at the end of the queue */
|
|
}
|
|
|
|
p_ep_desc->EPPtr = p_ep;
|
|
p_ep->ArgPtr = (void *)p_ep_desc;
|
|
p_ep_desc->FrameInterval = 1u; /* Add the Isoc EP to the 1 ms list */
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
err = EHCI_BW_Get ( p_hc_drv,
|
|
p_ep,
|
|
(void *)p_ep_desc);
|
|
|
|
if (err != USBH_ERR_NONE) {
|
|
Mem_PoolBlkFree( &p_ehci->HC_QHPool,
|
|
(void *)p_ep_desc,
|
|
&err_lib);
|
|
|
|
p_ep->ArgPtr = (void *)0;
|
|
|
|
CPU_CRITICAL_EXIT();
|
|
return (err);
|
|
}
|
|
|
|
EHCI_BW_Update( p_hc_drv,
|
|
p_ep,
|
|
(void *)p_ep_desc,
|
|
DEF_TRUE);
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
return (USBH_ERR_NONE);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_AsyncEP_Close()
|
|
*
|
|
* Description : Close the endpoint by unlinking the EHCI queue head
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_ep Pointer to endpoint structure
|
|
*
|
|
* Return(s) : USBH_ERR_NONE If endpoint was closed
|
|
*
|
|
* Note(s) : (1) Interrupt on Async Advance Doorbell bit in the USBCMD register allows software
|
|
* to inform the host controller that something has been removed from its asynchronous
|
|
* schedule. For more details about the doorbell mechanism, see section
|
|
* 4.8.2 Removing Queue Heads from Asynchronous Schedule (EHCI spec document).
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static USBH_ERR EHCI_AsyncEP_Close (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
EHCI_QH *p_qh_to_remove;
|
|
EHCI_QH *p_temp_qh;
|
|
CPU_INT32U qh_hor_link_ptr_temp;
|
|
CPU_INT32U qh_bus_addr;
|
|
CPU_INT32U async_qh_head_bus_addr;
|
|
LIB_ERR err_lib;
|
|
CPU_INT32U retry;
|
|
USBH_ERR err;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
/* ------------- (1) SEARCH QH TO REMOVE -------------- */
|
|
/* Retrieve the QH associated with this EP. */
|
|
p_qh_to_remove = (EHCI_QH *)p_ep->ArgPtr;
|
|
qh_bus_addr = (CPU_INT32U)USBH_OS_VirToBus((void *)p_qh_to_remove);
|
|
/* Retrieve the QH at the head of the Async Schedule. */
|
|
p_temp_qh = p_ehci->AsyncQHHead;
|
|
async_qh_head_bus_addr = (CPU_INT32U)USBH_OS_VirToBus((void *)p_temp_qh);
|
|
|
|
CPU_DCACHE_RANGE_INV(p_qh_to_remove, sizeof(EHCI_QH));
|
|
CPU_DCACHE_RANGE_INV(p_temp_qh, sizeof(EHCI_QH));
|
|
|
|
/* Mask Typ bits-field and T-bit. */
|
|
qh_hor_link_ptr_temp = p_temp_qh->QHHorLinkPtr & 0xFFFFFFE0;
|
|
/* Find QH which references the QH to remove. */
|
|
while ((qh_hor_link_ptr_temp != (CPU_INT32U)qh_bus_addr ) &&
|
|
(qh_hor_link_ptr_temp != (CPU_INT32U)async_qh_head_bus_addr)) {
|
|
|
|
p_temp_qh = (EHCI_QH *)USBH_OS_BusToVir((void *)(qh_hor_link_ptr_temp));
|
|
|
|
CPU_DCACHE_RANGE_INV(p_temp_qh, sizeof(EHCI_QH));
|
|
|
|
qh_hor_link_ptr_temp = p_temp_qh->QHHorLinkPtr & 0xFFFFFFE0;
|
|
}
|
|
|
|
if (qh_hor_link_ptr_temp == async_qh_head_bus_addr) {
|
|
return (USBH_ERR_EP_FREE); /* The QH to remove was not found in the Async Schedule.*/
|
|
}
|
|
|
|
/* --------- (2) REMOVE QH FROM ASYNC LIST ------------ */
|
|
USBCMD &= ~EHCI_USBCMD_RD_ASE; /* Disable the Async list processing. */
|
|
|
|
retry = 100u;
|
|
while ((USBSTATUS & EHCI_USBSTS_RD_ASS) != 0u) { /* Wait until the async list processing is disabled */
|
|
retry--;
|
|
if (retry == 0u) {
|
|
err = USBH_ERR_EP_FREE;
|
|
return (err);
|
|
}
|
|
USBH_OS_DlyMS(1u);
|
|
}
|
|
|
|
p_temp_qh->QHHorLinkPtr = p_qh_to_remove->QHHorLinkPtr; /* Remove the QH from the Async list. */
|
|
CPU_DCACHE_RANGE_FLUSH(p_temp_qh, sizeof(EHCI_QH));
|
|
|
|
EHCI_QTDRemove(p_hc_drv, p_qh_to_remove); /* Remove all QTDs attached to QH to remove. */
|
|
/* Free the QH just removed. */
|
|
Mem_PoolBlkFree( &p_ehci->HC_QHPool,
|
|
(void *)p_qh_to_remove,
|
|
&err_lib);
|
|
p_ep->ArgPtr = (void *)0;
|
|
|
|
USBCMD |= EHCI_USBCMD_WR_ASE; /* Enable async list processing */
|
|
|
|
retry = 100u;
|
|
while ((USBSTATUS & EHCI_USBSTS_RD_ASS) == 0u) { /* Wait until async schedule is enabled. */
|
|
retry--;
|
|
if (retry == 0u) {
|
|
err = USBH_ERR_EP_FREE;
|
|
return (err);
|
|
}
|
|
USBH_OS_DlyMS(1u);
|
|
}
|
|
|
|
USBCMD |= EHCI_USBCMD_WR_IOAAD; /* Ring the doorbell. See Note #1. */
|
|
|
|
err = USBH_ERR_NONE;
|
|
return (err);
|
|
}
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_IntrEP_Close()
|
|
*
|
|
* Description : Close the endpoint by unlinking the EHCI queue head
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_ep Pointer to endpoint structure
|
|
*
|
|
* Return(s) : USBH_ERR_NONE If endpoint was closed
|
|
*
|
|
* Note(s) : None
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static USBH_ERR EHCI_IntrEP_Close (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
EHCI_QH *p_qh_to_remove;
|
|
EHCI_QH *p_parent_qh;
|
|
CPU_INT08U bw_start_frame;
|
|
USBH_ERR err;
|
|
LIB_ERR err_lib;
|
|
EHCI_INTR_INFO *p_intr_info_to_remove;
|
|
EHCI_INTR_INFO *p_prev_intr_info = DEF_NULL;
|
|
|
|
|
|
err = USBH_ERR_NONE;
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
p_qh_to_remove = (EHCI_QH *)p_ep->ArgPtr;
|
|
|
|
CPU_DCACHE_RANGE_INV(p_qh_to_remove, sizeof(EHCI_QH));
|
|
|
|
bw_start_frame = p_qh_to_remove->BWStartFrame;
|
|
p_parent_qh = p_ehci->QHLists[bw_start_frame];
|
|
p_parent_qh = (EHCI_QH *) USBH_OS_BusToVir((void *)p_parent_qh);
|
|
|
|
CPU_DCACHE_RANGE_INV(p_parent_qh, sizeof(EHCI_QH));
|
|
|
|
while (((p_parent_qh->QHHorLinkPtr & 0x01u) == 0u) &&
|
|
((p_parent_qh->QHHorLinkPtr & 0xFFFFFFE0u) != (CPU_INT32U)p_qh_to_remove)) {
|
|
|
|
p_parent_qh = (EHCI_QH *)USBH_OS_BusToVir((void *)(p_parent_qh->QHHorLinkPtr & 0xFFFFFFE0u));
|
|
|
|
CPU_DCACHE_RANGE_INV(p_parent_qh, sizeof(EHCI_QH));
|
|
}
|
|
|
|
if ((p_parent_qh->QHHorLinkPtr & 0x01u) != 0u) {
|
|
err = USBH_ERR_EP_FREE;
|
|
} else {
|
|
p_parent_qh->QHHorLinkPtr = p_qh_to_remove->QHHorLinkPtr;
|
|
CPU_DCACHE_RANGE_FLUSH(p_parent_qh, sizeof(EHCI_QH));
|
|
}
|
|
|
|
EHCI_QTDRemove(p_hc_drv, p_qh_to_remove); /* Remove all QTDs attached to this QH */
|
|
|
|
EHCI_BW_Update( p_hc_drv, /* Update bandwidth allocation. */
|
|
p_ep,
|
|
(void *)p_qh_to_remove,
|
|
DEF_FALSE);
|
|
|
|
Mem_PoolBlkFree( &p_ehci->HC_QHPool,
|
|
(void *)p_qh_to_remove,
|
|
&err_lib);
|
|
|
|
p_intr_info_to_remove = p_ehci->HeadIntrInfo; /* Find Intr info struct to remove from queue. */
|
|
/* Search until end of the Intr info queue. */
|
|
while (p_intr_info_to_remove != (EHCI_INTR_INFO *)0) {
|
|
|
|
if ((p_intr_info_to_remove->IntrPlaceholderIx == bw_start_frame) &&
|
|
(p_intr_info_to_remove->EpPtr == p_ep )) {
|
|
break;
|
|
}
|
|
p_prev_intr_info = p_intr_info_to_remove; /* Keep ref to prev Intr info struct. */
|
|
/* Get next Intr info struct. */
|
|
p_intr_info_to_remove = p_intr_info_to_remove->NxtIntrInfo;
|
|
}
|
|
|
|
if (p_intr_info_to_remove != (EHCI_INTR_INFO *)0) {
|
|
if (p_intr_info_to_remove == p_ehci->HeadIntrInfo) {
|
|
p_ehci->HeadIntrInfo = p_ehci->HeadIntrInfo->NxtIntrInfo;
|
|
} else {
|
|
p_prev_intr_info->NxtIntrInfo = p_intr_info_to_remove->NxtIntrInfo;
|
|
}
|
|
|
|
Mem_PoolBlkFree( &p_ehci->IntrInfoPool,
|
|
(void *)p_intr_info_to_remove,
|
|
&err_lib);
|
|
}
|
|
|
|
return (err);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_IsocEP_Close()
|
|
*
|
|
* Description : Close the endpoint by unlinking the EHCI queue head
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_ep Pointer to endpoint structure
|
|
*
|
|
* Return(s) : USBH_ERR_NONE If endpoint was closed
|
|
*
|
|
* Note(s) : None
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static USBH_ERR EHCI_IsocEP_Close (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep)
|
|
{
|
|
EHCI_ISOC_EP_DESC *p_ep_desc_to_close;
|
|
EHCI_ISOC_EP_DESC *p_temp_ep_desc;
|
|
EHCI_ITD *p_itd;
|
|
EHCI_SITD *p_sitd;
|
|
EHCI_DEV *p_ehci;
|
|
CPU_INT08U dev_addr;
|
|
CPU_INT08U ep_addr;
|
|
LIB_ERR err_lib;
|
|
CPU_BOOLEAN isoc_ep_desc_found;
|
|
USBH_URB *p_urb;
|
|
EHCI_ISOC_EP_URB *p_urb_info;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
p_ep_desc_to_close = (EHCI_ISOC_EP_DESC *)p_ep->ArgPtr;
|
|
/* (1) Find the Isoc EP to close in the EHCI Isoc queue */
|
|
p_temp_ep_desc = p_ehci->HeadIsocEPDesc;
|
|
if (p_temp_ep_desc == p_ep_desc_to_close) { /* If the Isoc EP to close is the head of the queue... */
|
|
p_ehci->HeadIsocEPDesc = p_ep_desc_to_close->NxtEPDesc; /* Remove the Isoc EP to close from the Isoc queue */
|
|
|
|
} else { /* Search into the Isoc queue */
|
|
isoc_ep_desc_found = DEF_FALSE;
|
|
while (p_temp_ep_desc->NxtEPDesc != 0) { /* Search until the end of the Isoc queue */
|
|
if (p_temp_ep_desc->NxtEPDesc == p_ep_desc_to_close) {
|
|
isoc_ep_desc_found = DEF_TRUE;
|
|
break;
|
|
}
|
|
p_temp_ep_desc = p_temp_ep_desc->NxtEPDesc; /* Get the next Isoc desc */
|
|
}
|
|
|
|
if (isoc_ep_desc_found == DEF_FALSE) {
|
|
return (USBH_ERR_EP_NOT_FOUND);
|
|
}
|
|
/* Remove the Isoc EP to close from the Isoc queue */
|
|
p_temp_ep_desc->NxtEPDesc = p_ep_desc_to_close->NxtEPDesc;
|
|
}
|
|
|
|
/* (2) Clear any iTD or siTD scheduled for this EP */
|
|
p_urb = &p_ep->URB;
|
|
|
|
while (p_urb != 0) { /* Browse every URB scheduled for this EP */
|
|
|
|
if(p_urb->ArgPtr != 0) {
|
|
|
|
p_urb_info = (EHCI_ISOC_EP_URB *)p_urb->ArgPtr;
|
|
|
|
if (p_ep->DevSpd == USBH_DEV_SPD_HIGH) {
|
|
p_itd = (EHCI_ITD *)p_urb_info->iTD_Addr;
|
|
|
|
CPU_DCACHE_RANGE_INV(p_itd, sizeof(EHCI_ITD));
|
|
dev_addr = p_itd->ITDBufPagePtrList[0] & 0x0000007Fu;
|
|
ep_addr = (p_itd->ITDBufPagePtrList[0] & 0x00000F00u) >> 8u;
|
|
|
|
EHCI_ITDDone (p_hc_drv, /* Unschedule the iTD(s) of this URB */
|
|
p_ep_desc_to_close,
|
|
dev_addr,
|
|
ep_addr,
|
|
p_urb);
|
|
} else {
|
|
|
|
p_sitd = (EHCI_SITD *)p_urb_info->iTD_Addr;
|
|
|
|
CPU_DCACHE_RANGE_INV(p_sitd, sizeof(EHCI_SITD));
|
|
dev_addr = p_sitd->SITDEpCapChar[0] & 0x0000007Fu;
|
|
ep_addr = (p_sitd->SITDEpCapChar[0] & 0x00000F00u) >> 8u;
|
|
|
|
EHCI_SITDDone (p_hc_drv, /* Unschedule the siTD(s) of this URB */
|
|
p_ep_desc_to_close,
|
|
dev_addr,
|
|
ep_addr,
|
|
p_urb);
|
|
}
|
|
}
|
|
p_urb = p_urb->AsyncURB_NxtPtr; /* Get the next URB scheduled for this EP */
|
|
}
|
|
|
|
EHCI_BW_Update( p_hc_drv,
|
|
p_ep,
|
|
(void *)p_ep_desc_to_close,
|
|
DEF_FALSE);
|
|
|
|
Mem_PoolBlkFree( &p_ehci->HC_Isoc_EP_DescPool,
|
|
(void *)p_ep_desc_to_close,
|
|
&err_lib);
|
|
|
|
return (USBH_ERR_NONE);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_QTDListPrepare()
|
|
*
|
|
* Description : Prepare a QTD list and fills the elements of each QTD with appropriate values.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_ep Pointer to endpoint structure
|
|
*
|
|
* p_urb Pointer to URB structure.
|
|
*
|
|
* p_buf Pointer to the buffer.
|
|
*
|
|
* buf_len Number of bytes to transfer.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function
|
|
* USBH_ERR_NONE Endpoint closed successfully.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Return(s) : Pointer to the head of the QTD list.
|
|
*
|
|
* Note(s) : qTD structure and fields. iTD is a 32-bytes structure which must be aligned on a 32-byte
|
|
* boundary. For more details, see section 3.5 (EHCI spec)
|
|
*
|
|
* ---------------------------------------------------------------------------------------
|
|
* |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0|
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Next qTD Pointer | 0 |T|
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Alternate Next qTD Pointer | 0 |T|
|
|
* ---------------------------------------------------------------------------------------
|
|
* |dt| Total Bytes to Transfer |io| C_Page |Cerr |PID| Status |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Buffer Pointer (page 0) | Current Offset |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Buffer Pointer (page 1) | Reserved |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Buffer Pointer (page 2) | Reserved |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Buffer Pointer (page 3) | Reserved |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Buffer Pointer (page 4) | Reserved |
|
|
* ---------------------------------------------------------------------------------------
|
|
*
|
|
* (1) Section 3.5 (EHCI spec), one qTD structure is used to transfer up to 20480 (5*4096) bytes.
|
|
*
|
|
* (2) Alternate Next qTD Pointer (2nd DWord of qTD) is used to support hardware-only advance
|
|
* of the data stream to the next client buffer on short packet. To be more explicit the
|
|
* host controller will always use this pointer when the current qTD is retired due
|
|
* to short packet. Alternate Next qTD Pointer applies to IN direction only.
|
|
* See Section 3.5.2 (EHCI spec) for more details about Alternate Next qTD Pointer.
|
|
*
|
|
* (3) See section 4.10.6 for more details about the Buffer Pointer List use when the buffer
|
|
* associated with the transfer spans more than one physical page.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static EHCI_QTD *EHCI_QTDListPrepare (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
USBH_URB *p_urb,
|
|
CPU_INT08U *p_buf,
|
|
CPU_INT32U buf_len,
|
|
USBH_ERR *p_err)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
EHCI_QTD *p_new_qtd;
|
|
EHCI_QTD *p_head_qtd;
|
|
EHCI_QTD *p_temp_qtd;
|
|
CPU_INT32U qtd_token;
|
|
CPU_INT08U ep_type;
|
|
CPU_INT16U ep_max_pkt_size;
|
|
CPU_INT32U qtd_toggle;
|
|
CPU_INT32U token;
|
|
CPU_INT08U *p_buf_page;
|
|
CPU_INT08U i;
|
|
CPU_BOOLEAN rtn_flag;
|
|
CPU_INT32U qtd_totbytes;
|
|
CPU_INT32U rem;
|
|
CPU_INT32U buf_page_max;
|
|
CPU_INT32U buf_page;
|
|
LIB_ERR err_lib;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
qtd_toggle = 0u;
|
|
token = 0u;
|
|
ep_type = USBH_EP_TypeGet(p_ep);
|
|
ep_max_pkt_size = USBH_EP_MaxPktSizeGet(p_ep);
|
|
|
|
if (ep_type == USBH_EP_TYPE_CTRL) {
|
|
if (p_urb->Token == USBH_TOKEN_SETUP) {
|
|
token = DWORD3_QTD_PIDC_SETUP;
|
|
} else {
|
|
qtd_toggle = O_QTD_DT; /* Data toggle is 1 for Data and Status phases */
|
|
}
|
|
}
|
|
|
|
if (p_urb->Token == USBH_TOKEN_OUT) { /* Set the direction of the transfer */
|
|
token = DWORD3_QTD_PIDC_OUT;
|
|
} else if (p_urb->Token == USBH_TOKEN_IN) {
|
|
token = DWORD3_QTD_PIDC_IN;
|
|
} else {
|
|
/* Empty Else Statement */
|
|
}
|
|
|
|
p_buf = (CPU_INT08U *)USBH_OS_VirToBus((void *)p_buf);
|
|
p_buf_page = p_buf;
|
|
p_new_qtd = (EHCI_QTD *)0;
|
|
p_head_qtd = (EHCI_QTD *)0;
|
|
rtn_flag = DEF_FALSE;
|
|
|
|
while ((p_buf_page < (p_buf + buf_len)) || /* Initialize one or several qTDs for total xfer's size */
|
|
(buf_len == 0u )) { /* See Note #1 */
|
|
/* Get a qTD structure */
|
|
p_temp_qtd = (EHCI_QTD *)Mem_PoolBlkGet(&p_ehci->HC_QTDPool,
|
|
sizeof(EHCI_QTD),
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
*p_err = USBH_ERR_ALLOC;
|
|
return ((EHCI_QTD *)0);
|
|
}
|
|
|
|
EHCI_QTD_Clr(p_temp_qtd); /* Clear every field of the qTD to have a known state */
|
|
CPU_DCACHE_RANGE_FLUSH(p_temp_qtd, sizeof(EHCI_QTD));
|
|
|
|
if (p_new_qtd) { /* Next qTD. */
|
|
/* Set Next qTD Pointer. */
|
|
p_new_qtd->QTDNxtPtr = (CPU_INT32U)USBH_OS_VirToBus(p_temp_qtd);
|
|
p_new_qtd->QTDAltNxtPtr = 0x00000001u; /* Set Alternate Next qTD Pointer (see Note #2). */
|
|
CPU_DCACHE_RANGE_FLUSH(p_new_qtd, sizeof(EHCI_QTD));
|
|
p_new_qtd = p_temp_qtd; /* qTD struct gotten. */
|
|
|
|
} else { /* 1st qTD. */
|
|
p_head_qtd = p_temp_qtd;
|
|
p_new_qtd = p_temp_qtd;
|
|
}
|
|
|
|
/* Init Buffer Pointer (Page 0) + Current Offset */
|
|
p_new_qtd->QTDBufPagePtrList[0] = (CPU_INT32U)p_buf_page;
|
|
|
|
buf_page_max = (((CPU_INT32U)p_buf_page + 0x1000u) & 0xFFFFF000u) - (CPU_INT32U)p_buf_page;
|
|
buf_page = (p_buf + buf_len) - p_buf_page;
|
|
qtd_totbytes = DEF_MIN(buf_page, buf_page_max);
|
|
|
|
/* Init Buffer Pointer List if buffer spans more ... */
|
|
/* ... than one physical page (see Note #3). */
|
|
for (i = 1u; i <= 4u; i++) { /* Init Buffer Pointer (Page 1 to 4) */
|
|
/* Find the next closest 4K-page boundary ahead. */
|
|
p_buf_page = (CPU_INT08U *)(((CPU_INT32U)p_buf_page + 0x1000u) & 0xFFFFF000u);
|
|
|
|
if (p_buf_page < (p_buf + buf_len)) { /* If buffer spans a new 4K-page boundary. */
|
|
/* Set page ptr to ref start of the subsequent 4K page. */
|
|
p_new_qtd->QTDBufPagePtrList[i] = (CPU_INT32U)p_buf_page;
|
|
qtd_totbytes += DEF_MIN(((p_buf + buf_len) - p_buf_page), 0x1000);
|
|
|
|
} else { /* All the transfer size has been described... */
|
|
rtn_flag = DEF_TRUE;
|
|
break; /* ... quit the loop. */
|
|
}
|
|
|
|
}
|
|
|
|
if (rtn_flag == DEF_TRUE) {
|
|
/* Init the qTD token */
|
|
qtd_token = QTD_TOKEN_STS(1 << 7u) | /* Status field. Active bit to '1'. */
|
|
QTD_TOKEN_PID(token) | /* PID code */
|
|
QTD_TOKEN_CERR(3u) | /* Error Counter */
|
|
QTD_TOKEN_CP(0u) | /* Current Page */
|
|
QTD_TOKEN_TBTT(qtd_totbytes) | /* Total Bytes to Transfer */
|
|
QTD_TOKEN_DT(qtd_toggle); /* Data Toggle */
|
|
|
|
if ((p_ep->DevSpd == USBH_DEV_SPD_HIGH) &&
|
|
(p_urb->Token == USBH_TOKEN_OUT)) {
|
|
qtd_token |= QTD_TOKEN_STS(1u);
|
|
}
|
|
p_new_qtd->QTDToken = qtd_token; /* Prepare qTD with the parameters */
|
|
break;
|
|
|
|
} else { /* The transfer's size requires more qTDs, update the...*/
|
|
/* ... size remaining to describe by qTD(s). */
|
|
p_buf_page += 0x1000;
|
|
|
|
if (p_buf_page < (p_buf + buf_len)) {
|
|
rem = ((CPU_INT32U)p_buf_page - p_new_qtd->QTDBufPagePtrList[0]) % ep_max_pkt_size;
|
|
qtd_totbytes -= rem;
|
|
p_buf_page -= rem;
|
|
}
|
|
}
|
|
/* Init the qTD token */
|
|
qtd_token = QTD_TOKEN_STS(1u << 7u) | /* Status field. Active bit to '1'. */
|
|
QTD_TOKEN_PID(token) | /* PID code */
|
|
QTD_TOKEN_CERR(3u) | /* Error Counter */
|
|
QTD_TOKEN_CP(0u) | /* Current Page */
|
|
QTD_TOKEN_TBTT(qtd_totbytes) | /* Total Bytes to Transfer */
|
|
QTD_TOKEN_DT(qtd_toggle); /* Data Toggle */
|
|
|
|
p_new_qtd->QTDToken = qtd_token; /* Prepare qTD with the parameters */
|
|
CPU_DCACHE_RANGE_FLUSH(p_new_qtd, sizeof(EHCI_QTD));
|
|
}
|
|
|
|
if (p_new_qtd == (EHCI_QTD *)0) {
|
|
*p_err = USBH_ERR_NULL_PTR;
|
|
return ((EHCI_QTD *)0);
|
|
}
|
|
/* Finalize init for last qTD. */
|
|
p_new_qtd->QTDToken |= QTD_TOKEN_IOC(1u); /* Interrupt On Completion for last qTD */
|
|
p_new_qtd->QTDNxtPtr |= QTD_N_QTD_PTR_T(1u); /* Set Terminate bit */
|
|
p_new_qtd->QTDAltNxtPtr |= QTD_ALT_QTD_PTR_T(1u);
|
|
CPU_DCACHE_RANGE_FLUSH(p_new_qtd, sizeof(EHCI_QTD));
|
|
|
|
*p_err = USBH_ERR_NONE;
|
|
|
|
return (p_head_qtd);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_SITDListPrepare()
|
|
*
|
|
* Description : Prepare a QTD list and fills the elements of each QTD with appropriate values.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_dev Pointer to device structure
|
|
*
|
|
* p_ep Pointer to endpoint structure
|
|
*
|
|
* p_ep_desc Pointer to endpoint descriptor
|
|
*
|
|
* p_urb Pointer to URB structure.
|
|
*
|
|
* p_buf Pointer to the buffer.
|
|
*
|
|
*
|
|
* Return(s) : Pointer to the head of the QTD list.
|
|
*
|
|
* Note(s) : siTD structure and fields. siTD is a 28-bytes structure which must be aligned on a 32-byte
|
|
* boundary. For more details, see section 3.4 (EHCI spec)
|
|
*
|
|
* ---------------------------------------------------------------------------------------
|
|
* |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0|
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Next qTD Pointer | 0 |Typ|T|
|
|
* ---------------------------------------------------------------------------------------
|
|
* |I/O| Port Number |R | Hub Addr | R | EndPt |R| Device Addr |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Reserved | uFrame C-mask | uFrame S-mask |
|
|
* ---------------------------------------------------------------------------------------
|
|
* |ioc|P| Reserved | Total Bytes to Transfer | uFrame C-prog-mask | Status |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Buffer Pointer (Page 0) | Current Offset |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Buffer Pointer (Page 1) | Reserved |TP |T-cnt|
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Back Pointer | 0 |T|
|
|
* ---------------------------------------------------------------------------------------
|
|
*
|
|
* (1) For a split transaction, any isochronous OUT full-speed transaction is subdivided into
|
|
* multiple start-splits, each with a data payload of 188 bytes or less.
|
|
* For more information, See USB 2.0 specfication:
|
|
* section 11.18.1 Best Case Full-Speed Budget
|
|
* section 11.18.4 Host Split Transaction Scheduling Requirements
|
|
* section 11.21.3 Isochronous OUT Sequencing
|
|
*
|
|
* Table 4-14 (EHCI spec) gives details about the initial conditions for OUT siTD's
|
|
* TP and T-count Fields
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static USBH_ERR EHCI_SITDListPrepare (USBH_HC_DRV *p_hc_drv,
|
|
USBH_DEV *p_dev,
|
|
USBH_EP *p_ep,
|
|
EHCI_ISOC_EP_DESC *p_ep_desc,
|
|
USBH_URB *p_urb,
|
|
CPU_INT08U *p_buf)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
EHCI_SITD *p_new_sitd;
|
|
CPU_INT32U buf_page;
|
|
CPU_INT08U ep_nbr;
|
|
CPU_INT32U token;
|
|
CPU_INT32U i;
|
|
CPU_INT16U frame_len; /* Number of bytes to be transferred in this frame */
|
|
CPU_INT32U frame_nbr; /* Frame number where this SITD to be placed */
|
|
CPU_INT16U frame_interval; /* Interval of this endpoint */
|
|
CPU_INT32U *p_hw_desc;
|
|
LIB_ERR err_lib;
|
|
CPU_INT08U t_count;
|
|
EHCI_ISOC_EP_URB *p_urb_info;
|
|
USBH_ERR err;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
ep_nbr = USBH_EP_LogNbrGet(p_ep);
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
|
|
if (p_urb->Token == USBH_TOKEN_OUT) { /* Set the EP direction */
|
|
token = DWORD1_SITD_IO_OUT;
|
|
} else if (p_urb->Token == USBH_TOKEN_IN) {
|
|
token = DWORD1_SITD_IO_IN;
|
|
} else {
|
|
token = (CPU_INT32U)0;
|
|
}
|
|
|
|
p_buf = (CPU_INT08U *)USBH_OS_VirToBus((void *)p_buf);
|
|
buf_page = (CPU_INT32U )p_buf;
|
|
frame_interval = p_ep_desc->FrameInterval; /* siTD belongs to the 1 ms Frame list */
|
|
|
|
for (i = 0u; i < p_urb->IsocDescPtr->NbrFrm; i++) { /* Initialize the frame error array */
|
|
p_urb->IsocDescPtr->FrmErr[i] = USBH_ERR_NONE;
|
|
}
|
|
|
|
if(p_urb->IsocDescPtr->StartFrm == 0u) {
|
|
frame_nbr = EHCI_FrameNbrGet(p_hc_drv, &err) + 8u; /* Start this xfer immediately after current frame nbr */
|
|
} else {
|
|
frame_nbr = p_urb->IsocDescPtr->StartFrm + 8u; /* Start this xfer at the caller specified frame number */
|
|
}
|
|
|
|
frame_nbr %= 256u; /* Keep the Periodic Frame List index between 0 and 255 */
|
|
|
|
p_ep_desc->AppStartFrame = frame_nbr; /* Save the index */
|
|
p_ep_desc->NbrFrame = p_urb->IsocDescPtr->NbrFrm;
|
|
|
|
p_urb_info = (EHCI_ISOC_EP_URB *)Mem_PoolBlkGet(&p_ehci->HC_Isoc_EP_URBPool,
|
|
sizeof(EHCI_ISOC_EP_URB),
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
CPU_CRITICAL_EXIT();
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
p_urb_info->AppStartFrame = p_ep_desc->AppStartFrame;
|
|
p_urb_info->NbrFrame = p_ep_desc->NbrFrame;
|
|
|
|
/* Prepare 1 or more siTD for this Isoc transfer */
|
|
for (i = 0u; i < p_urb->IsocDescPtr->NbrFrm; i++) {
|
|
|
|
frame_nbr %= 256u; /* Keep the Periodic Frame List index between 0 and 255 */
|
|
|
|
/* Get a new siTD struct */
|
|
p_new_sitd = (EHCI_SITD *)Mem_PoolBlkGet(&p_ehci->HC_ITDPool,
|
|
sizeof(EHCI_SITD),
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
CPU_CRITICAL_EXIT();
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
EHCI_SITD_Clr(p_new_sitd);
|
|
/* Init siTD EP Capabilities/Characteristics */
|
|
p_new_sitd->SITDEpCapChar[0] = (SITD_EPCHAR_DIR(token) |
|
|
SITD_EPCHAR_PN(p_dev->PortNbr) |
|
|
SITD_EPCHAR_HUBADD(p_dev->HubDevPtr->DevAddr) |
|
|
SITD_EPCHAR_ENDPT(ep_nbr) |
|
|
SITD_EPCHAR_DEVADD(p_ep->DevAddr));
|
|
|
|
if (p_urb->Token == USBH_TOKEN_IN) { /* Only isochronous in transfers have C-Mask */
|
|
p_new_sitd->SITDEpCapChar[1] = p_ep_desc->SMask;
|
|
p_new_sitd->SITDEpCapChar[1] |= ((CPU_INT32U)p_ep_desc->CMask) << 8u;
|
|
}
|
|
|
|
frame_len = p_urb->IsocDescPtr->FrmLen[i]; /* Size of transaction for this frame */
|
|
p_new_sitd->SITDStsCtrl = (frame_len << 16u) | /* Total bytes to transfer */
|
|
O_SITD_STS_ACTIVE; /* Enable execution of Isoc split transaction by HC */
|
|
/* Set pointer to buffer data (Page 0) */
|
|
p_new_sitd->SITDBufPagePtrList[0] = (CPU_INT32U)buf_page;
|
|
/* If buf data crosses 4K page, set Buffer Ptr (Page 1) */
|
|
if (((buf_page + frame_len) & 0xFFFFF000u) == ((buf_page + 0x1000u) & 0xFFFFF000u)) {
|
|
p_new_sitd->SITDBufPagePtrList[1] = (buf_page + frame_len) & 0xFFFFF000u;
|
|
}
|
|
|
|
if (p_urb->Token == USBH_TOKEN_OUT) { /* For Isoc OUT, set TP and T-count fields. See Note#1 */
|
|
if (frame_len <= 188) { /* If data payload for this transaction <= 188 bytes */
|
|
/* Only 1 SSPLIT required. Mark it with an ALL */
|
|
p_new_sitd->SITDBufPagePtrList[1] |= SITD_BUGPAGE1_TP(DWORD6_SITD_TP_ALL);
|
|
} else {
|
|
/* Several SSPLITs required. Mark the 1st one with BEGIN*/
|
|
p_new_sitd->SITDBufPagePtrList[1] |= SITD_BUGPAGE1_TP(DWORD6_SITD_TP_BEGIN);
|
|
}
|
|
|
|
t_count = p_ep_desc->TCnt; /* Number of SSPLITs for this OUT transaction */
|
|
if (t_count < 7) { /* T-Count must NOT be larger than 6 */
|
|
p_new_sitd->SITDBufPagePtrList[1] |= SITD_BUGPAGE1_TCOUNT(t_count);
|
|
}
|
|
|
|
p_new_sitd->SITDEpCapChar[1] |= p_ep_desc->SMask; /* Set every bit required for the nbr of SSPLITs */
|
|
}
|
|
|
|
if (i == (p_urb->IsocDescPtr->NbrFrm - 1u)) { /* If this is the last siTD for this Isoc xfer */
|
|
p_new_sitd->SITDStsCtrl |= (CPU_INT32U)SITD_STSCTRL_IOC(1); /* Interrupt On Completion for last siTD */
|
|
p_ep_desc->TDTailPtr = (void *)p_new_sitd;
|
|
p_urb_info->iTD_Addr = (CPU_INT32U)p_new_sitd; /* Save the last siD associated with this URB */
|
|
p_urb->ArgPtr = (void *) p_urb_info;
|
|
}
|
|
|
|
/* Get the data struct at index 'frame_nbr' in PFL */
|
|
p_hw_desc = (CPU_INT32U *)USBH_OS_BusToVir((void *)(p_ehci->PeriodicListBase[frame_nbr] & 0xFFFFFFE0u));
|
|
CPU_DCACHE_RANGE_INV(p_hw_desc, sizeof(CPU_INT32U));
|
|
/* Find the last siTD at this entry position */
|
|
while ((*p_hw_desc & 0x06u) != HOR_LNK_PTR_TYP(DWORD1_TYP_QH)) {
|
|
|
|
p_hw_desc = (CPU_INT32U *)USBH_OS_BusToVir((void *)(*p_hw_desc & 0xFFFFFFE0u));
|
|
CPU_DCACHE_RANGE_INV(p_hw_desc, sizeof(CPU_INT32U));
|
|
}
|
|
|
|
p_new_sitd->SITDNxtLinkPtr = *p_hw_desc; /* Store the gotten data struct in siTD Next Link Ptr */
|
|
CPU_DCACHE_RANGE_FLUSH(p_new_sitd, sizeof(EHCI_SITD));
|
|
|
|
*p_hw_desc = HOR_LNK_PTR_T(DWORD1_T_INVALID); /* Invalidate siTD Next Link Ptr so HC ignores it */
|
|
/* Insert the new siTD after the gotten data struct */
|
|
*p_hw_desc |= (CPU_INT32U)USBH_OS_VirToBus((void *)p_new_sitd) |
|
|
HOR_LNK_PTR_TYP(DWORD1_TYP_SITD);
|
|
|
|
*p_hw_desc &= 0xFFFFFFFEu; /* Validate Next Link Ptr pointed to siTD to insert */
|
|
CPU_DCACHE_RANGE_FLUSH(p_hw_desc, sizeof(CPU_INT32U));
|
|
|
|
buf_page += frame_len;
|
|
frame_nbr += frame_interval;
|
|
}
|
|
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
return (USBH_ERR_NONE);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_ITDListPrepare()
|
|
*
|
|
* Description : Prepare a ITD list and fills the elements of each ITD with appropriate values.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_ep Pointer to endpoint structure
|
|
*
|
|
* p_ep_desc Pointer to endpoint descriptor
|
|
*
|
|
* p_urb Pointer to URB structure.
|
|
*
|
|
* p_buf Pointer to the buffer.
|
|
*
|
|
* buf_len Number of bytes to transfer.
|
|
*
|
|
* Return(s) : Pointer to the head of the ITD list.
|
|
*
|
|
* Note(s) : iTD structure and fields. iTD is a 64-bytes structure which must be aligned on a 32-byte
|
|
* boundary. For more details, see section 3.3 (EHCI spec)
|
|
*
|
|
* ---------------------------------------------------------------------------------------
|
|
* |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0|
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Next qTD Pointer | 0 |Typ|T|
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Status | Transaction 0 Length |io| PG | Transaction 0 Offset |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Status | Transaction 1 Length |io| PG | Transaction 1 Offset |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Status | Transaction 2 Length |io| PG | Transaction 2 Offset |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Status | Transaction 3 Length |io| PG | Transaction 3 Offset |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Status | Transaction 4 Length |io| PG | Transaction 4 Offset |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Status | Transaction 5 Length |io| PG | Transaction 5 Offset |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Status | Transaction 6 Length |io| PG | Transaction 6 Offset |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Status | Transaction 7 Length |io| PG | Transaction 7 Offset |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Buffer Pointer (Page 0) | EP Addr |R| Device Addr |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Buffer Pointer (Page 1) |I/O| Maximum Packet Size |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Buffer Pointer (Page 2) | Reserved |Mlt|
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Buffer Pointer (Page 3) | Reserved |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Buffer Pointer (Page 4) | Reserved |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Buffer Pointer (Page 5) | Reserved |
|
|
* ---------------------------------------------------------------------------------------
|
|
* | Buffer Pointer (Page 6) | Reserved |
|
|
* ---------------------------------------------------------------------------------------
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static USBH_ERR EHCI_ITDListPrepare (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
EHCI_ISOC_EP_DESC *p_ep_desc,
|
|
USBH_URB *p_urb,
|
|
CPU_INT08U *p_buf,
|
|
CPU_INT32U buf_len)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
EHCI_ITD *p_new_itd; /* Pointer to new iTD structure */
|
|
CPU_INT32U *p_hw_desc;
|
|
CPU_INT08U active_trans[10]; /* Array of active transactions */
|
|
CPU_INT32U array_index; /* Active Transactiona array index */
|
|
CPU_INT32U buf_page; /* Buffer Page */
|
|
CPU_INT32U buf_ptr = 0u; /* Buffer Pointer */
|
|
CPU_INT08U buf_ptr_page_nbr; /* Buffer Pointer Page Number */
|
|
CPU_INT32U buf_start_addr = 0u; /* Buffer Start Address */
|
|
CPU_INT08U ep_nbr; /* Endpoint Number */
|
|
CPU_INT16U ep_max_pkt_size; /* Endpoint Max Packet Size */
|
|
CPU_INT32U i; /* For loop index */
|
|
CPU_INT32U j; /* For loop index */
|
|
CPU_INT32U k; /* For loop index */
|
|
CPU_INT08U ioc_bit = 0u; /* Interrupt on Complete bit */
|
|
CPU_INT16U frame_interval; /* Interval of this endpoint */
|
|
CPU_INT16U frame_nbr; /* Frame number where this iTD is to be placed */
|
|
LIB_ERR err_lib;
|
|
CPU_INT08U micro_frame_nbr; /* Microframe number */
|
|
CPU_INT16U mult_value; /* Mult value */
|
|
CPU_INT16U nbr_of_transaction_per_uframe; /* Number of transactions per uframe from EP desc */
|
|
CPU_INT16U nbr_of_transaction_per_iTD; /* Number of transactions per iTD */
|
|
CPU_INT16U nbr_of_transaction; /* Number of transactions */
|
|
CPU_INT16U nbr_of_iTDs_for_xfer; /* Number of iTDs for transfer */
|
|
CPU_INT08U page_nbr; /* Page Number */
|
|
CPU_INT32U token; /* I/O Token */
|
|
CPU_INT08U transaction_per_octo_mult_rem;
|
|
CPU_INT08U transaction_per_mult_rem;
|
|
CPU_INT08U transaction_shift;
|
|
CPU_INT16U xact_len; /* Exact Transaction Length */
|
|
CPU_INT16U xact_offset; /* Exact Transaction Offset */
|
|
CPU_INT32U xfer_remaining_len; /* Transfer Remaining Length */
|
|
CPU_INT16U max_transaction_len; /* Maximum Transaction Length */
|
|
CPU_INT32U xfer_elapsed_len; /* Transfer Elapsed Length */
|
|
EHCI_ISOC_EP_URB *p_urb_info;
|
|
USBH_ERR err;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
|
|
if (p_urb->Token == USBH_TOKEN_OUT) { /* Determine I/O Token direction */
|
|
token = DWORD1_ITD_IO_OUT;
|
|
} else if (p_urb->Token == USBH_TOKEN_IN) {
|
|
token = DWORD1_ITD_IO_IN;
|
|
} else {
|
|
token = (CPU_INT32U)0;
|
|
}
|
|
|
|
ep_nbr = USBH_EP_LogNbrGet(p_ep);
|
|
ep_max_pkt_size = USBH_EP_MaxPktSizeGet(p_ep);
|
|
p_buf = (CPU_INT08U *)USBH_OS_VirToBus((void *)p_buf);
|
|
buf_page = (CPU_INT32U )p_buf;
|
|
xfer_remaining_len = buf_len;
|
|
|
|
for (j = 0u; j < 10u; j++){
|
|
active_trans[j] = 0x00u; /* Clear all values in active transaction array */
|
|
}
|
|
|
|
nbr_of_transaction = p_urb->IsocDescPtr->NbrFrm;
|
|
nbr_of_transaction_per_uframe = (p_ep->Desc.wMaxPacketSize & USBH_NBR_TRANSACTION_PER_UFRAME) >> 11u ;
|
|
mult_value = nbr_of_transaction_per_uframe +1u ; /* Determine the Mult field for iTD structure */
|
|
max_transaction_len = ep_max_pkt_size * mult_value;
|
|
|
|
/* Determine the number of iTD for transfer */
|
|
transaction_per_octo_mult_rem = nbr_of_transaction % (mult_value * 8u);
|
|
if (transaction_per_octo_mult_rem == 0){
|
|
nbr_of_iTDs_for_xfer = (nbr_of_transaction / (mult_value * 8u));
|
|
} else {
|
|
nbr_of_iTDs_for_xfer = (nbr_of_transaction / (mult_value * 8u)) + 1u;
|
|
}
|
|
/* Determine the number of transactions per iTD */
|
|
transaction_per_mult_rem = nbr_of_transaction % mult_value;
|
|
if (transaction_per_mult_rem == 0){
|
|
nbr_of_transaction_per_iTD = nbr_of_transaction / mult_value;
|
|
} else {
|
|
nbr_of_transaction_per_iTD = (nbr_of_transaction / mult_value) + 1u;
|
|
}
|
|
|
|
/* Left shift nbr of trans in active transaction array */
|
|
for (k = 0u; k < nbr_of_transaction_per_iTD; k++){
|
|
array_index = k / 8u;
|
|
transaction_shift = k % 8u;
|
|
active_trans[array_index] |= 1 << transaction_shift;
|
|
}
|
|
|
|
frame_interval = p_ep_desc->FrameInterval; /* iTD belongs to the 1 ms Frame list */
|
|
|
|
if(p_urb->IsocDescPtr->StartFrm == 0u) {
|
|
frame_nbr = EHCI_FrameNbrGet(p_hc_drv, &err) + 8u; /* Start this xfer immediately after current frame nbr */
|
|
} else {
|
|
frame_nbr = p_urb->IsocDescPtr->StartFrm + 8u; /* Start this xfer at the caller specified frame number */
|
|
}
|
|
frame_nbr %= 256; /* Keep the Periodic Frame List index between 0 and 255 */
|
|
|
|
p_ep_desc->AppStartFrame = frame_nbr;
|
|
p_ep_desc->NbrFrame = p_urb->IsocDescPtr->NbrFrm;
|
|
|
|
p_urb_info = (EHCI_ISOC_EP_URB *)Mem_PoolBlkGet(&p_ehci->HC_Isoc_EP_URBPool,
|
|
sizeof(EHCI_ISOC_EP_URB),
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
CPU_CRITICAL_EXIT();
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
p_urb_info->AppStartFrame = p_ep_desc->AppStartFrame;
|
|
p_urb_info->NbrFrame = p_ep_desc->NbrFrame;
|
|
|
|
/* ----------- iTD Structure Initialization ---------- */
|
|
/* Init each iTD that composed for the transfer */
|
|
for (i = 0u; i < nbr_of_iTDs_for_xfer; i++) {
|
|
|
|
p_new_itd = (EHCI_ITD *)Mem_PoolBlkGet(&p_ehci->HC_ITDPool,
|
|
sizeof(EHCI_ITD),
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
CPU_CRITICAL_EXIT();
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
EHCI_ITD_Clr(p_new_itd); /* Clear the new iTD structure */
|
|
|
|
p_new_itd->ITDBufPagePtrList[0] = ITD_BUF_PG_PTR_LIST_DEVADD(p_ep->DevAddr) |
|
|
ITD_BUF_PG_PTR_LIST_ENDPT(ep_nbr);
|
|
|
|
p_new_itd->ITDBufPagePtrList[1] = ITD_BUF_PG_PTR_LIST_MPS(ep_max_pkt_size) |
|
|
ITD_BUF_PG_PTR_LIST_IO(token);
|
|
|
|
p_new_itd->ITDBufPagePtrList[2] = ITD_BUF_PG_PTR_LIST_MULT(mult_value);
|
|
|
|
page_nbr = 0u;
|
|
buf_ptr_page_nbr = 0u;
|
|
xfer_elapsed_len = 0u;
|
|
|
|
for (micro_frame_nbr = 0u; micro_frame_nbr < 8u; micro_frame_nbr++) {
|
|
|
|
if ((active_trans[i] & (1 << micro_frame_nbr)) != 0u) {
|
|
|
|
/* Determine the Transaction Length for iTD structure */
|
|
if (xfer_remaining_len > max_transaction_len) {
|
|
xact_len = max_transaction_len;
|
|
} else {
|
|
xact_len = xfer_remaining_len;
|
|
}
|
|
/* Calculate the Transaction Offset for iTD structure */
|
|
xact_offset = (buf_page + xfer_elapsed_len) & 0x00000FFFu;
|
|
|
|
/* For the 1st microframe nbr, determine the Buffer */
|
|
/* Pointer for iTD structure and buffer start address */
|
|
if (micro_frame_nbr == 0){
|
|
buf_ptr = buf_page & 0xFFFFF000u;
|
|
buf_start_addr = buf_ptr | xact_offset;
|
|
}
|
|
|
|
if (buf_start_addr > (buf_ptr + 0x1000u)){ /* If buffer start addr is greater than 4096 boundary */
|
|
buf_ptr += 0x1000u; /* Increment Buffer Pointer by 4096 */
|
|
page_nbr++; /* Increment Page Number for iTD structure */
|
|
buf_ptr_page_nbr++; /* Increment buffer pointer page number */
|
|
}
|
|
/* Store the Status, Transaction Length, Page Number */
|
|
/* and Transaction Offset in iTD structure's Status and*/
|
|
/* Control field for this microframe */
|
|
p_new_itd->ITDStsAndCntrl[micro_frame_nbr] = ITD_STSCTRL_STS(O_ITD_STS_ACTIVE) |
|
|
ITD_STSCTRL_XACT_LEN(xact_len) |
|
|
ITD_STSCTRL_PG(page_nbr) |
|
|
ITD_STSCTRL_XACT_OFFSET(xact_offset);
|
|
|
|
/* Store the Buffer Pointer in iTD structure's Buffer */
|
|
/* Page Pointer field for this page number ensuring it */
|
|
if (buf_ptr_page_nbr < 7u) { /* doesn't go beyond the array limit */
|
|
p_new_itd->ITDBufPagePtrList[buf_ptr_page_nbr] |= buf_ptr;
|
|
} else {
|
|
CPU_CRITICAL_EXIT();
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
buf_start_addr += xact_len;
|
|
ioc_bit = micro_frame_nbr;
|
|
xfer_remaining_len -= xact_len;
|
|
xfer_elapsed_len += xact_len;
|
|
}
|
|
}
|
|
if (i == (nbr_of_iTDs_for_xfer - 1)) { /* For the itd is the last iTD for this transfer */
|
|
p_new_itd->ITDStsAndCntrl[ioc_bit] |= (CPU_INT32U)ITD_STSCTRL_IOC(1); /* Set IOC bit for last iTD */
|
|
p_ep_desc->TDTailPtr = (void *) p_new_itd; /* Save the last iTD of this xfer */
|
|
p_urb_info->iTD_Addr = (CPU_INT32U)p_new_itd; /* Save the last iTD associated with this URB */
|
|
p_urb->ArgPtr = (void *) p_urb_info;
|
|
}
|
|
|
|
/* ----------- Isochronous EP Insertion ------------- */
|
|
p_hw_desc = (CPU_INT32U *)USBH_OS_BusToVir((void *)(p_ehci->PeriodicListBase[frame_nbr] & 0xFFFFFFE0));
|
|
CPU_DCACHE_RANGE_INV(p_hw_desc, sizeof(CPU_INT32U));
|
|
|
|
/* While Type in Next Link Pointer is not QH */
|
|
/* go to next pointer */
|
|
while ((*p_hw_desc & 0x06u) != HOR_LNK_PTR_TYP(DWORD1_TYP_QH)) {
|
|
|
|
p_hw_desc = (CPU_INT32U *)USBH_OS_BusToVir((void *)*p_hw_desc);
|
|
CPU_DCACHE_RANGE_INV(p_hw_desc, sizeof(CPU_INT32U));
|
|
}
|
|
|
|
p_new_itd->ITDNxtLinkPtr = *p_hw_desc;
|
|
CPU_DCACHE_RANGE_FLUSH(p_new_itd, sizeof(EHCI_ITD));
|
|
|
|
*p_hw_desc = HOR_LNK_PTR_T(DWORD1_T_INVALID); /* Set to invalid so that insertion is not compromised. */
|
|
|
|
*p_hw_desc |= (CPU_INT32U)USBH_OS_VirToBus((void *)p_new_itd) |
|
|
HOR_LNK_PTR_TYP(DWORD1_TYP_ITD); /* Set insertion Type to iTD */
|
|
|
|
*p_hw_desc &= 0xFFFFFFFEu; /* Set to valid when insertion is done. */
|
|
CPU_DCACHE_RANGE_FLUSH(p_hw_desc, sizeof(CPU_INT32U));
|
|
|
|
frame_nbr += frame_interval;
|
|
}
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
return (USBH_ERR_NONE);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_ISR()
|
|
*
|
|
* Description : EHCI interrupt service routine.
|
|
*
|
|
* Argument(s) : p_data Pointer to host controller driver structure.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void EHCI_ISR (void *p_data)
|
|
{
|
|
|
|
CPU_INT32U int_status;
|
|
CPU_INT32U int_en;
|
|
EHCI_DEV *p_ehci;
|
|
EHCI_QH *p_qh;
|
|
USBH_HC_DRV *p_hc_drv;
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
CPU_INT32U bytes_to_xfer;
|
|
CPU_INT32U frame_interval;
|
|
CPU_INT16U index;
|
|
CPU_INT08U ep_addr;
|
|
CPU_INT08U dev_addr;
|
|
EHCI_ITD *p_itd;
|
|
EHCI_SITD *p_sitd;
|
|
EHCI_ISOC_EP_DESC *p_ep_desc;
|
|
EHCI_ISOC_EP_URB *p_urb_info;
|
|
EHCI_INTR_INFO *p_intr_info;
|
|
EHCI_QH *p_intr_qh_placeholder;
|
|
USBH_EP *p_ep;
|
|
USBH_URB *p_urb;
|
|
USBH_URB *p_urb_previous = DEF_NULL;
|
|
#endif
|
|
|
|
|
|
p_hc_drv = (USBH_HC_DRV *)p_data;
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
int_status = USBSTATUS;
|
|
int_en = USBINT;
|
|
int_status &= int_en;
|
|
USBSTATUS = int_status; /* Clear the interrupt status register */
|
|
|
|
if (int_status == 0u) {
|
|
return;
|
|
}
|
|
|
|
if ((int_status & EHCI_USBSTS_RD_HSE) != 0u) { /* ----------- (1) HOST SYSTEM ERROR INT -------------- */
|
|
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
|
|
USBH_PRINT_LOG("Host System Error => HC halted\r\n");
|
|
#endif
|
|
}
|
|
|
|
if ((int_status & EHCI_USBSTS_RD_PCD) != 0u) { /* ----------- (2) PORT CHANGE DETECT INT ------------- */
|
|
USBH_HUB_RH_Event(p_hc_drv->RH_DevPtr);
|
|
}
|
|
|
|
if (((int_status & EHCI_USBSTS_RD_USBI) != 0u) || /* ---------- (3) USB INT or USB ERROR INT ------------ */
|
|
((int_status & EHCI_USBSTS_RD_USBEI) != 0u)) {
|
|
|
|
/* (1) Control and Bulk qTD processing */
|
|
CPU_DCACHE_RANGE_INV(p_ehci->AsyncQHHead, sizeof(EHCI_QH));
|
|
p_qh = (EHCI_QH *)USBH_OS_BusToVir((void *)(p_ehci->AsyncQHHead->QHHorLinkPtr & 0xFFFFFFE0));
|
|
CPU_DCACHE_RANGE_INV(p_qh, sizeof(EHCI_QH));
|
|
|
|
while (p_qh != (EHCI_QH *)p_ehci->AsyncQHHead) { /* Search in the async list until async head is found */
|
|
|
|
if (p_qh->QHCurQTDPtr != 0u) {
|
|
EHCI_QHDone(p_hc_drv, p_qh);
|
|
}
|
|
|
|
p_qh = (EHCI_QH *)USBH_OS_BusToVir((void *)(p_qh->QHHorLinkPtr & 0xFFFFFFE0));
|
|
CPU_DCACHE_RANGE_INV(p_qh, sizeof(EHCI_QH));
|
|
}
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
/* (2) Interrupt qTD processing */
|
|
p_intr_info = p_ehci->HeadIntrInfo;
|
|
while (p_intr_info != 0) { /* Browse Intr info list = active Intr placeholder. */
|
|
/* Get placeholder containing opened Intr ep(s). */
|
|
p_intr_qh_placeholder = p_ehci->QHLists[p_intr_info->IntrPlaceholderIx];
|
|
/* Get polling interval of this placeholder. */
|
|
frame_interval = p_intr_info->FrameInterval;
|
|
|
|
/* T-bit = 0 => QH Horizontal link ptr is valid */
|
|
if (DEF_BIT_IS_SET(p_intr_qh_placeholder->QHHorLinkPtr, DWORD1_T) == DEF_NO) {
|
|
|
|
CPU_DCACHE_RANGE_INV(p_intr_qh_placeholder, sizeof(EHCI_QH));
|
|
p_qh = (EHCI_QH *)USBH_OS_BusToVir((void *)(p_intr_qh_placeholder->QHHorLinkPtr & 0xFFFFFFE0));
|
|
CPU_DCACHE_RANGE_INV(p_qh, sizeof(EHCI_QH));
|
|
|
|
/* Search polling interval list matching to open qH. */
|
|
while (p_qh->FrameInterval != frame_interval) {
|
|
|
|
p_qh = (EHCI_QH *)USBH_OS_BusToVir((void *)(p_qh->QHHorLinkPtr & 0xFFFFFFE0));
|
|
CPU_DCACHE_RANGE_INV(p_qh, sizeof(EHCI_QH));
|
|
}
|
|
/* From this placeholder, get all active Intr qH. */
|
|
while (p_qh->FrameInterval == frame_interval) {
|
|
|
|
if (p_qh->QHCurQTDPtr != 0u) { /* Are there qTDs completed for this active qH?... */
|
|
EHCI_QHDone(p_hc_drv, p_qh); /* ...Yes, process compeled qTD(s). */
|
|
}
|
|
|
|
if ((p_qh->QHHorLinkPtr & 0x01u) != 0u) {
|
|
break;
|
|
} else { /* Get next active qH. */
|
|
p_qh = (EHCI_QH *)USBH_OS_BusToVir((void *)(p_qh->QHHorLinkPtr & 0xFFFFFFE0u));
|
|
CPU_DCACHE_RANGE_INV(p_qh, sizeof(EHCI_QH));
|
|
}
|
|
}
|
|
}
|
|
|
|
p_intr_info = p_intr_info->NxtIntrInfo; /* Go to next placeholder containing opened Intr ep(s). */
|
|
}
|
|
/* ------------ ISOCHRONOUS XFER COMPLETION ----------- */
|
|
p_ep_desc = p_ehci->HeadIsocEPDesc;
|
|
|
|
while (p_ep_desc != 0) { /* Browse the list of opened Isochronous EP */
|
|
p_ep = p_ep_desc->EPPtr;
|
|
p_urb = &p_ep->URB;
|
|
|
|
while (p_urb != 0) { /* Search for every Isoc transfer completed */
|
|
|
|
if ((p_ep_desc->TDTailPtr != 0 ) &&
|
|
(p_urb->State == USBH_URB_STATE_SCHEDULED) &&
|
|
(p_urb->ArgPtr != 0 )) {
|
|
/* (1) iTD processing */
|
|
if (p_ep->DevSpd == USBH_DEV_SPD_HIGH) {
|
|
/* Retrieve the last iTD associated with this URB */
|
|
p_urb_info = (EHCI_ISOC_EP_URB *)p_urb->ArgPtr;
|
|
p_itd = (EHCI_ITD *)p_urb_info->iTD_Addr;
|
|
CPU_DCACHE_RANGE_INV(p_itd, sizeof(EHCI_ITD));
|
|
|
|
for (index = 0u; index < 8u; index++){ /* Search the last transaction of the iTD */
|
|
|
|
/* Is Isoc transfer completed? */
|
|
if ((DEF_BIT_IS_SET(p_itd->ITDStsAndCntrl[index], DWORDx_ITD_IOC) == DEF_YES) &&
|
|
(DEF_BIT_IS_SET(p_itd->ITDStsAndCntrl[index], DWORDx_ITD_STATUS_ACTIVE) == DEF_NO )) {
|
|
|
|
/* Retrieve Device @ and EP @ */
|
|
dev_addr = (p_itd->ITDBufPagePtrList[0] & 0x0000007Fu);
|
|
ep_addr = (p_itd->ITDBufPagePtrList[0] & 0x00000F00u) >> 8u;
|
|
|
|
bytes_to_xfer = EHCI_ITDDone(p_hc_drv,
|
|
p_ep_desc,
|
|
dev_addr,
|
|
ep_addr,
|
|
p_urb);
|
|
p_urb->XferLen = bytes_to_xfer;
|
|
/* See Note #1. */
|
|
if (p_urb == &p_ep->URB) {
|
|
USBH_URB_Done(p_urb);
|
|
p_urb->ArgPtr = (void *)0;
|
|
} else if (p_urb == p_ep->URB.AsyncURB_NxtPtr) {
|
|
USBH_URB_Done(p_urb);
|
|
p_urb->ArgPtr = (void *)0;
|
|
p_urb->URB_DoneSignal = DEF_TRUE;
|
|
} else if(p_urb_previous->URB_DoneSignal == DEF_TRUE) {
|
|
USBH_URB_Done(p_urb);
|
|
p_urb->ArgPtr = (void *)0;
|
|
p_urb->URB_DoneSignal = DEF_TRUE;
|
|
p_urb_previous->URB_DoneSignal = DEF_FALSE;
|
|
} else {
|
|
/* Empty Else Statement */
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else { /* (2) siTD processing */
|
|
/* Retrieve the last siTD associated with this URB */
|
|
p_urb_info = (EHCI_ISOC_EP_URB *)p_urb->ArgPtr;
|
|
p_sitd = (EHCI_SITD *)p_urb_info->iTD_Addr;
|
|
CPU_DCACHE_RANGE_INV(p_sitd, sizeof(EHCI_SITD));
|
|
|
|
/* Is Isoc transfer completed? */
|
|
if (DEF_BIT_IS_SET(p_sitd->SITDStsCtrl, DWORD3_SITD_STATUS_ACTIVE) == DEF_NO) {
|
|
|
|
/* Retrieve Device @ and EP @ */
|
|
dev_addr = (p_sitd->SITDEpCapChar[0] & 0x0000007Fu);
|
|
ep_addr = (p_sitd->SITDEpCapChar[0] & 0x00000F00u) >> 8u;
|
|
|
|
bytes_to_xfer = EHCI_SITDDone(p_hc_drv,
|
|
p_ep_desc,
|
|
dev_addr,
|
|
ep_addr,
|
|
p_urb);
|
|
p_urb->XferLen = p_urb->DMA_BufLen - bytes_to_xfer;
|
|
USBH_URB_Done(p_urb); /* Notify about URB completion */
|
|
p_urb->ArgPtr = (void *)0;
|
|
}
|
|
}
|
|
}
|
|
p_urb_previous = p_urb; /* Keep ref of previous URB in progress. */
|
|
p_urb = p_urb->AsyncURB_NxtPtr; /* Go to the next URB in progress */
|
|
}
|
|
p_ep_desc = p_ep_desc->NxtEPDesc; /* Go to the next opened Isoc EP */
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if ((int_status & EHCI_USBSTS_RD_FLR) != 0u) { /* ----------- (4) FRAME LIST ROLLOVER INT ------------ */
|
|
p_ehci->FNOCnt++; /* Count frame number overrun */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_PeriodicOrderPrepare()
|
|
*
|
|
* Description : Initialize EHCI_BranchArray[] array following a scheduling pattern.
|
|
*
|
|
* Argument(s) : idx Starting array index
|
|
*
|
|
* power bits to shift(Power of two)
|
|
*
|
|
* list_size Size of Periodic Frame List
|
|
*
|
|
* Return(s) : None
|
|
*
|
|
* Note(s) : None
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static void EHCI_PeriodicOrderPrepare (CPU_INT32U idx,
|
|
CPU_INT32U power,
|
|
CPU_INT32U list_size)
|
|
{
|
|
EHCI_BranchArray [idx + (1u << power)] = EHCI_BranchArray[idx] + ((list_size / 2u) / (1u << power));
|
|
|
|
if (power == 0u) {
|
|
return;
|
|
}
|
|
|
|
power--;
|
|
|
|
EHCI_PeriodicOrderPrepare(idx,
|
|
power,
|
|
list_size);
|
|
|
|
EHCI_PeriodicOrderPrepare(idx + (1u << (power + 1u)),
|
|
power,
|
|
list_size);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_CapRegRead()
|
|
*
|
|
* Description : Read the EHCI capability registers.
|
|
*
|
|
* Argument(s) : p_ehci Pointer to EHCI_DEV structure.
|
|
*
|
|
* p_cap Pointer to EHCI capability structure.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void EHCI_CapRegRead (EHCI_DEV *p_ehci,
|
|
EHCI_CAP *p_cap)
|
|
{
|
|
CPU_INT08U i;
|
|
CPU_INT32U reg;
|
|
CPU_INT08U cap_len;
|
|
CPU_INT16U hci_ver;
|
|
|
|
|
|
reg = MEM_VAL_GET_INT32U_LITTLE(&(p_ehci->HcCapReg->CapLen_HCIVersion));
|
|
cap_len = MEM_VAL_GET_INT08U(®);
|
|
hci_ver = MEM_VAL_GET_INT16U((CPU_INT08U *)® + 2);
|
|
|
|
p_cap->CapLen = cap_len;
|
|
p_cap->HCIVersion = hci_ver;
|
|
p_cap->HCSParams = p_ehci->HcCapReg->HCSParams; /* Structural Parameters */
|
|
p_cap->HCCParams = p_ehci->HcCapReg->HCCParams; /* Capability Parameters */
|
|
|
|
if ((p_cap->HCSParams & EHCI_HCSPARAMS_RD_PRR) != 0u) {
|
|
for (i = 0u; i < 15u; i++) { /* Companion Port Route Description */
|
|
p_cap->HCSPPortRoute[i] = p_ehci->HcCapReg->HCSPPortRoute[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_DMA_Init()
|
|
*
|
|
* Description : Allocate all structures used by the EHCI driver.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* Return(s) : USBH_ERR_NONE, if successful.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Note(s) : (1) 8 represents eight frames. One siTD per frame is required. Multiplying per 8 frames
|
|
* is a safety margin which allows the applicatio to define an isochronous transfer
|
|
* which will span several frames, and so several siTD will be required.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static USBH_ERR EHCI_DMA_Init (USBH_HC_DRV *p_hc_drv)
|
|
{
|
|
LIB_ERR err_lib;
|
|
CPU_SIZE_T octets_reqd;
|
|
CPU_INT32U max_nbr_qh;
|
|
CPU_INT32U max_nbr_qh_alloc;
|
|
CPU_INT32U max_nbr_qtd;
|
|
CPU_INT32U max_nbr_itd = 0u;
|
|
CPU_INT32U max_data_buf;
|
|
CPU_INT32U total_mem_req;
|
|
CPU_INT08U *p_dedicated_mem;
|
|
EHCI_DEV *p_ehci;
|
|
USBH_HC_CFG *p_hc_cfg;
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
CPU_INT32U max_ep_desc;
|
|
#endif
|
|
|
|
|
|
p_ehci = p_hc_drv->DataPtr;
|
|
p_hc_cfg = p_hc_drv->HC_CfgPtr;
|
|
|
|
max_nbr_qh = (USBH_CFG_MAX_NBR_DEVS + /* Total ctrl, bulk, intr EPs + 1 dummy async Q head EP.*/
|
|
p_hc_cfg->MaxNbrEP_BulkOpen +
|
|
1u);
|
|
max_nbr_qh_alloc = max_nbr_qh;
|
|
|
|
max_data_buf = (USBH_CFG_MAX_NBR_DEVS +
|
|
p_hc_cfg->MaxNbrEP_BulkOpen);
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
max_nbr_qh_alloc += EHCI_INTR_QH_LIST_SIZE;
|
|
|
|
max_nbr_qh += p_hc_cfg->MaxNbrEP_IntrOpen;
|
|
max_nbr_qh_alloc+= p_hc_cfg->MaxNbrEP_IntrOpen;
|
|
|
|
max_data_buf += p_hc_cfg->MaxNbrEP_IntrOpen + p_hc_cfg->MaxNbrEP_IsocOpen;
|
|
|
|
max_ep_desc = p_hc_cfg->MaxNbrEP_IsocOpen; /* Total Isochronous endpoints */
|
|
|
|
max_nbr_itd = (max_ep_desc * EHCI_MAX_ITD) * ((p_hc_cfg->DataBufMaxLen / (8u * 3072u)) + 1u);
|
|
max_nbr_itd += 256u; /* For dummy SITDs in the periodic list */
|
|
max_nbr_itd += max_ep_desc * EHCI_MAX_SITD * 8u; /* See Note #1. */
|
|
#endif
|
|
|
|
/* 1 is added to take the ceiling value */
|
|
max_nbr_qtd = max_nbr_qh * ((p_hc_cfg->DataBufMaxLen / (20u * 1024u)) + 1u);
|
|
|
|
if (p_hc_cfg->DedicatedMemAddr != (CPU_ADDR)0) { /* --------------- DEDICATED MEMORY ------------------- */
|
|
|
|
if (p_hc_cfg->DataBufFromSysMemEn == DEF_DISABLED) { /* Data buffers allocated from dedicated memory */
|
|
total_mem_req = (max_nbr_qh_alloc * sizeof(EHCI_QH) ) +
|
|
((max_nbr_qtd + 1u) * sizeof(EHCI_QTD)) +
|
|
(max_nbr_itd * sizeof(EHCI_ITD)) +
|
|
(p_hc_cfg->DataBufMaxLen * max_data_buf);
|
|
} else { /* Data buffers allocated from main memory */
|
|
total_mem_req = (max_nbr_qh_alloc * sizeof(EHCI_QH) ) +
|
|
((max_nbr_qtd + 1u) * sizeof(EHCI_QTD)) +
|
|
(max_nbr_itd * sizeof(EHCI_ITD));
|
|
}
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
total_mem_req += (EHCI_MAX_PERIODIC_LIST_SIZE * sizeof(void *));
|
|
|
|
p_dedicated_mem = (CPU_INT08U *)USB_ALIGNED((CPU_INT08U *)p_hc_cfg->DedicatedMemAddr, 4096u);
|
|
#else
|
|
p_dedicated_mem = (CPU_INT08U *)USB_ALIGNED((CPU_INT08U *)p_hc_cfg->DedicatedMemAddr, 32u);
|
|
#endif
|
|
|
|
if (total_mem_req > ((p_hc_cfg->DedicatedMemAddr + p_hc_cfg->DedicatedMemSize) - (CPU_ADDR)p_dedicated_mem)) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
/* Align first byte of dedicated mem on 4096 for ... */
|
|
/* ... periodic list. */
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
p_ehci->PeriodicListBase = (CPU_INT32U *)p_dedicated_mem;
|
|
p_dedicated_mem += (EHCI_MAX_PERIODIC_LIST_SIZE * sizeof (void *));
|
|
#endif
|
|
|
|
p_ehci->DMA_EHCI.QHPtr = (EHCI_QH *)p_dedicated_mem;
|
|
p_dedicated_mem += (max_nbr_qh_alloc * sizeof(EHCI_QH));
|
|
|
|
p_ehci->DMA_EHCI.QTDPtr = (EHCI_QTD *)p_dedicated_mem;
|
|
p_dedicated_mem += ((max_nbr_qtd + 1u) * sizeof(EHCI_QTD)); /* 1 for dummy head QTD */
|
|
|
|
p_ehci->DMA_EHCI.ITDPtr = (EHCI_ITD *)p_dedicated_mem;
|
|
p_dedicated_mem += (max_nbr_itd * sizeof(EHCI_ITD));
|
|
|
|
p_ehci->DMA_EHCI.BufPtr = (void *)p_dedicated_mem;
|
|
|
|
Mem_PoolCreate( &p_ehci->HC_QHPool, /* Create DMA QH Pool */
|
|
(void *)p_ehci->DMA_EHCI.QHPtr,
|
|
max_nbr_qh_alloc * sizeof(EHCI_QH),
|
|
max_nbr_qh_alloc,
|
|
sizeof(EHCI_QH),
|
|
32u, /* qH must be aligned on 32-byte boundary */
|
|
&octets_reqd,
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
Mem_PoolCreate( &p_ehci->HC_QTDPool, /* Create DMA QTD Pool */
|
|
(void *)p_ehci->DMA_EHCI.QTDPtr,
|
|
((max_nbr_qtd + 1u) * sizeof(EHCI_QTD)),
|
|
max_nbr_qtd,
|
|
sizeof(EHCI_QTD),
|
|
32u, /* qTD must be aligned on 32-byte boundary */
|
|
&octets_reqd,
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
Mem_PoolCreate( &p_ehci->HC_ITDPool, /* Create DMA iTD/siTD Pool */
|
|
(void *)p_ehci->DMA_EHCI.ITDPtr,
|
|
(max_nbr_itd * sizeof(EHCI_ITD)),
|
|
max_nbr_itd,
|
|
sizeof(EHCI_ITD),
|
|
32u, /* iTD must be aligned on a 32-byte boundary. */
|
|
&octets_reqd,
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
#endif
|
|
} else { /* ---------------- SYSTEM MEMORY --------------------- */
|
|
Mem_PoolCreate( &p_ehci->HC_QHPool, /* Create DMA QH Pool */
|
|
(void *)0, /* Allocate from HEAP region */
|
|
max_nbr_qh_alloc * sizeof(EHCI_QH),
|
|
max_nbr_qh_alloc,
|
|
sizeof(EHCI_QH),
|
|
32u, /* qH must be aligned on 32-byte boundary */
|
|
&octets_reqd,
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
p_ehci->DMA_EHCI.QHPtr = (EHCI_QH *)p_ehci->HC_QHPool.PoolAddrStart;
|
|
|
|
Mem_PoolCreate ( &p_ehci->HC_QTDPool, /* Create DMA QTD Pool */
|
|
(void *)0, /* Allocate from HEAP region */
|
|
((max_nbr_qtd + 1u) * sizeof(EHCI_QTD)),
|
|
max_nbr_qtd + 1u,
|
|
sizeof(EHCI_QTD),
|
|
32u, /* qTD must be aligned on 32-byte boundary */
|
|
&octets_reqd,
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
p_ehci->DMA_EHCI.QTDPtr = (EHCI_QTD *)p_ehci->HC_QTDPool.PoolAddrStart;
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
/* Create DMA iTD/siTD Pool */
|
|
Mem_PoolCreate ( &p_ehci->HC_ITDPool,
|
|
(void *)0, /* Allocate from HEAP region */
|
|
(max_nbr_itd * sizeof(EHCI_ITD)),
|
|
max_nbr_itd,
|
|
sizeof(EHCI_ITD),
|
|
32u, /* iTD must be aligned on a 32-byte boundary. */
|
|
&octets_reqd,
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
p_ehci->DMA_EHCI.ITDPtr = (EHCI_ITD *)p_ehci->HC_ITDPool.PoolAddrStart;
|
|
/* Get a mem block for Periodic Frame List */
|
|
p_ehci->PeriodicListBase = (CPU_INT32U *)Mem_HeapAlloc(512u * sizeof(CPU_INT32U),
|
|
4096u,
|
|
&octets_reqd,
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
if (max_ep_desc > 0u) {
|
|
Mem_PoolCreate ( &p_ehci->HC_Isoc_EP_DescPool, /* Create Isochronous EP Pool */
|
|
(void *)0, /* Allocate from HEAP region */
|
|
(max_ep_desc * sizeof(EHCI_ISOC_EP_DESC)),
|
|
max_ep_desc,
|
|
sizeof(EHCI_ISOC_EP_DESC),
|
|
sizeof(CPU_ALIGN),
|
|
&octets_reqd,
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
Mem_PoolCreate ( &p_ehci->HC_Isoc_EP_URBPool, /* Create pool for storing URB info during ongoing xfer */
|
|
(void *)0, /* Allocate from HEAP region */
|
|
(max_ep_desc * 2u * sizeof(EHCI_ISOC_EP_URB)),
|
|
(max_ep_desc * 2u),
|
|
sizeof(EHCI_ISOC_EP_URB),
|
|
sizeof(CPU_ALIGN),
|
|
&octets_reqd,
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ((p_hc_cfg->DedicatedMemAddr != (CPU_ADDR)0) &&
|
|
(p_hc_cfg->DataBufFromSysMemEn == DEF_DISABLED)) { /* ----------- DATA BUF FROM DEDICATED MEM ------------ */
|
|
|
|
Mem_PoolCreate ( &p_ehci->BufPool, /* Create Data buffer Pool */
|
|
(void *)p_ehci->DMA_EHCI.BufPtr,
|
|
(p_hc_cfg->DataBufMaxLen * max_data_buf),
|
|
max_data_buf,
|
|
p_hc_cfg->DataBufMaxLen,
|
|
#if (CPU_CFG_CACHE_MGMT_EN == DEF_ENABLED)
|
|
CONFIG_ARCH_CACHE_LINE,
|
|
#else
|
|
sizeof(CPU_ALIGN),
|
|
#endif
|
|
&octets_reqd,
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
}
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
Mem_PoolCreate ( &p_ehci->IntrInfoPool, /* Create Intr info pool used for completed Intr xfer. */
|
|
(void *)0, /* Allocate from HEAP region */
|
|
(p_hc_cfg->MaxNbrEP_IntrOpen * sizeof(EHCI_INTR_INFO)),
|
|
(p_hc_cfg->MaxNbrEP_IntrOpen),
|
|
sizeof(EHCI_INTR_INFO),
|
|
sizeof(CPU_ALIGN),
|
|
&octets_reqd,
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
p_ehci->HeadIntrInfo = (EHCI_INTR_INFO *)0;
|
|
#endif
|
|
|
|
return (USBH_ERR_NONE);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_QH_Clr()
|
|
*
|
|
* Description : Clear the contents of a queue head structure.
|
|
*
|
|
* Argument(s) : p_qh Pointer to the queue head structure.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void EHCI_QH_Clr (EHCI_QH *p_qh)
|
|
{
|
|
p_qh->QHHorLinkPtr = (CPU_INT32U)0;
|
|
p_qh->QHEpCapChar[0] = (CPU_INT32U)0;
|
|
p_qh->QHEpCapChar[1] = (CPU_INT32U)0;
|
|
p_qh->QHCurQTDPtr = (CPU_INT32U)0;
|
|
p_qh->QHNxtQTDPtr = (CPU_INT32U)0;
|
|
p_qh->QHAltNxtQTDPtr = (CPU_INT32U)0;
|
|
p_qh->QHToken = (CPU_INT32U)0;
|
|
p_qh->QHBufPagePtrList[0] = (CPU_INT32U)0;
|
|
p_qh->QHBufPagePtrList[1] = (CPU_INT32U)0;
|
|
p_qh->QHBufPagePtrList[2] = (CPU_INT32U)0;
|
|
p_qh->QHBufPagePtrList[3] = (CPU_INT32U)0;
|
|
p_qh->QHBufPagePtrList[4] = (CPU_INT32U)0;
|
|
p_qh->QTDHead = (CPU_INT32U)0;
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_QTD_Clr()
|
|
*
|
|
* Description : Clear the contents of a queue element transfer descriptor structure.
|
|
*
|
|
* Argument(s) : p_qtd Pointer to the queue element transfer descriptor.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void EHCI_QTD_Clr (EHCI_QTD *p_qtd)
|
|
{
|
|
p_qtd->QTDNxtPtr = (CPU_INT32U)0;
|
|
p_qtd->QTDAltNxtPtr = (CPU_INT32U)0;
|
|
p_qtd->QTDToken = (CPU_INT32U)0;
|
|
p_qtd->QTDBufPagePtrList[0] = (CPU_INT32U)0;
|
|
p_qtd->QTDBufPagePtrList[1] = (CPU_INT32U)0;
|
|
p_qtd->QTDBufPagePtrList[2] = (CPU_INT32U)0;
|
|
p_qtd->QTDBufPagePtrList[3] = (CPU_INT32U)0;
|
|
p_qtd->QTDBufPagePtrList[4] = (CPU_INT32U)0;
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_SITD_Clr()
|
|
*
|
|
* Description : Clear the contents of a split transaction element transfer descriptor structure.
|
|
*
|
|
* Argument(s) : p_sitd Pointer to the split transaction element transfer descriptor.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static void EHCI_SITD_Clr (EHCI_SITD *p_sitd)
|
|
{
|
|
p_sitd->SITDNxtLinkPtr = (CPU_INT32U)0;
|
|
p_sitd->SITDEpCapChar[0] = (CPU_INT32U)0;
|
|
p_sitd->SITDEpCapChar[1] = (CPU_INT32U)0;
|
|
p_sitd->SITDStsCtrl = (CPU_INT32U)0;
|
|
p_sitd->SITDBufPagePtrList[0] = (CPU_INT32U)0;
|
|
p_sitd->SITDBufPagePtrList[1] = (CPU_INT32U)0;
|
|
p_sitd->SITDBackLinkPtr = (CPU_INT32U)0;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_ITD_Clr()
|
|
*
|
|
* Description : Clear the contents of a split transaction element transfer descriptor structure.
|
|
*
|
|
* Argument(s) : p_itd Pointer to the split transaction element transfer descriptor.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static void EHCI_ITD_Clr (EHCI_ITD *p_itd)
|
|
{
|
|
CPU_INT08U i;
|
|
|
|
|
|
p_itd->ITDNxtLinkPtr = (CPU_INT32U)0;
|
|
|
|
for (i = 0u; i < 8u; i++) {
|
|
p_itd->ITDStsAndCntrl[i] = (CPU_INT32U)0;
|
|
}
|
|
|
|
for (i = 0u; i < 7u; i++) {
|
|
p_itd->ITDBufPagePtrList[i] = (CPU_INT32U)0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_EP_DescClr()
|
|
*
|
|
* Description : Clear the contents of a isochronous endpoint descriptor structure.
|
|
*
|
|
* Argument(s) : p_ep_desc Pointer to isochronous endpoint descriptor structure
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static void EHCI_EP_DescClr (EHCI_ISOC_EP_DESC *p_ep_desc)
|
|
{
|
|
p_ep_desc->TDTailPtr = (void *)0;
|
|
p_ep_desc->EPPtr = (USBH_EP *)0;
|
|
p_ep_desc->SMask = (CPU_INT08U )0;
|
|
p_ep_desc->CMask = (CPU_INT08U )0;
|
|
p_ep_desc->AppStartFrame = (CPU_INT08U )0;
|
|
p_ep_desc->NbrFrame = (CPU_INT08U )0;
|
|
p_ep_desc->FrameInterval = (CPU_INT08U )0;
|
|
p_ep_desc->NxtEPDesc = (EHCI_ISOC_EP_DESC *)0;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_QTDRemove()
|
|
*
|
|
* Description : Free the memory of all QTDs in the QTD list and calculate the total bytes transfered by
|
|
* all QTDs.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_qh Pointer to EHCI_QH structure.
|
|
*
|
|
* Return(s) : Total number of bytes transferred.
|
|
*
|
|
* Note(s) : None
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_INT32U EHCI_QTDRemove (USBH_HC_DRV *p_hc_drv,
|
|
EHCI_QH *p_qh)
|
|
{
|
|
EHCI_QTD *p_qtd;
|
|
EHCI_QTD *p_qtd_next;
|
|
EHCI_DEV *p_ehci;
|
|
CPU_INT32U rem_len;
|
|
CPU_INT32U terminate;
|
|
LIB_ERR err_lib;
|
|
|
|
|
|
CPU_DCACHE_RANGE_INV(p_qh, sizeof(EHCI_QH));
|
|
p_qtd = (EHCI_QTD *)p_qh->QTDHead;
|
|
if (p_qtd == (CPU_INT32U)0) {
|
|
return (0u);
|
|
}
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
p_qh->QTDHead = 0u;
|
|
terminate = 0u;
|
|
rem_len = 0u;
|
|
|
|
CPU_DCACHE_RANGE_FLUSH(p_qh, sizeof(EHCI_QH));
|
|
CPU_DCACHE_RANGE_INV(p_qtd, sizeof(EHCI_QTD));
|
|
|
|
while (terminate != 1u) { /* Until QTD terminate bit set is found */
|
|
rem_len += ((p_qtd->QTDToken >> 16u) & 0x7FFFu); /* Bits 16-30 represent, bytes that are not transferred */
|
|
|
|
p_qtd_next = (EHCI_QTD *)USBH_OS_BusToVir((void *)(p_qtd->QTDNxtPtr & 0xFFFFFFE0));
|
|
terminate = (p_qtd->QTDNxtPtr & 1u);
|
|
/* Free the QTD */
|
|
Mem_PoolBlkFree(&p_ehci->HC_QTDPool, (void *)p_qtd, (LIB_ERR *)&err_lib);
|
|
|
|
if (terminate != 1u) {
|
|
p_qtd = p_qtd_next;
|
|
CPU_DCACHE_RANGE_INV(p_qtd, sizeof(EHCI_QTD));
|
|
}
|
|
}
|
|
|
|
return (rem_len);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_PeriodicListInit()
|
|
*
|
|
* Description : Initialize the periodic list. This will create a dummy queue head, which is the head of
|
|
* the all queue heads, writes the periodic list base address with appropriate value and
|
|
* enables the periodic list processing.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* Return(s) : USBH_ERR_NONE, if successful.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Note(s) : (1) Interrupt qH are are organized into a tree structure with Periodic Frame List (PFL)
|
|
* entries being the leaf nodes. The desired polling rate of an Interrupt endpoint is
|
|
* achieved by scheduling the qH at the appropriate depth in the tree. The higher the
|
|
* polling rate, the closer to the tree root the qH will be placed since multiple lists
|
|
* will converge on it. The figure below illustrates the Interrupt qH structure (PFL of
|
|
* 32 entries is shown for simplification):
|
|
*
|
|
* 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 <-- 32 ms polling interval
|
|
* |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_|
|
|
* 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 <-- 16 ms polling interval
|
|
* |___| |___| |___| |___| |___| |___| |___| |___|
|
|
* 0 0 0 0 0 0 0 0 <-- 8 ms polling interval
|
|
* |_______| |_______| |_______| |_______|
|
|
* 0 0 0 0 <-- 4 ms polling interval
|
|
* |_______________| |_______________|
|
|
* 0 0 <-- 2 ms polling interval
|
|
* |_______________________________|
|
|
* 0 <-- 1 ms polling interval
|
|
*
|
|
* Current EHCI driver has a PFL of 256 entries. The above tree structure can be easily
|
|
* extented to a PFL of 256 entries. Each depth level in the tree represents a polling
|
|
* interval. Hence for a PFL of 256 entries, 9 polling intervals are defined: 256, 128,
|
|
* 64, 32, 16, 8, 4, 2, 1 ms.
|
|
* The tree nodes are dummy disabled Interrupt qH acting as a placeholder where 0 or more
|
|
* active qH may be enqueued. The total number of dummy qH is:
|
|
*
|
|
* 256 + 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 511.
|
|
*
|
|
* Hence, the EHCI driver has 511 different scheduling lists into which active qH can be
|
|
* scheduled. While browsing the PFL, the host controller will visit 1 dummy qH every
|
|
* frame, 2 dummy qH once every 2 frames until 256 dummy qH once every 256 frames.
|
|
* The entire binary tree is stored into the "QHLists" array.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static USBH_ERR EHCI_PeriodicListInit (USBH_HC_DRV *p_hc_drv)
|
|
{
|
|
EHCI_SITD *p_new_sitd;
|
|
EHCI_QH *p_new_qh;
|
|
EHCI_DEV *p_ehci;
|
|
CPU_INT32U list_ix;
|
|
CPU_INT32U ix_prev;
|
|
LIB_ERR err_lib;
|
|
|
|
|
|
ix_prev = 0u;
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
/* Build Intr QH lists with disabled QH (see Note #1). */
|
|
for (list_ix = EHCI_QH_LIST_256MS; list_ix <= EHCI_QH_LIST_01MS; list_ix++) {
|
|
/* Get dummy qH used as placeholder for Intr xfer. */
|
|
p_new_qh = (EHCI_QH *)Mem_PoolBlkGet(&p_ehci->HC_QHPool,
|
|
sizeof(EHCI_QH),
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
EHCI_QH_Clr(p_new_qh);
|
|
p_ehci->QHLists[list_ix] = p_new_qh;
|
|
|
|
p_new_qh->QHEpCapChar[1] = QH_EPCAP_HBPM(DWORD3_QH_HBPM_1);
|
|
p_new_qh->QHNxtQTDPtr = (CPU_INT32U)0x00000001;
|
|
p_new_qh->QHAltNxtQTDPtr = (CPU_INT32U)0x00000001;
|
|
|
|
if (list_ix < EHCI_QH_LIST_128MS) {
|
|
p_new_qh->FrameInterval = 256u;
|
|
} else if ((list_ix >= EHCI_QH_LIST_128MS) && (list_ix < EHCI_QH_LIST_64MS)) {
|
|
ix_prev = EHCI_QH_LIST_256MS + ((list_ix - EHCI_QH_LIST_128MS) * 2u);
|
|
p_new_qh->FrameInterval = 128u;
|
|
} else if ((list_ix >= EHCI_QH_LIST_64MS) && (list_ix < EHCI_QH_LIST_32MS)) {
|
|
ix_prev = EHCI_QH_LIST_128MS + ((list_ix - EHCI_QH_LIST_64MS) * 2u);
|
|
p_new_qh->FrameInterval = 64u;
|
|
} else if ((list_ix >= EHCI_QH_LIST_32MS) && (list_ix < EHCI_QH_LIST_16MS)) {
|
|
ix_prev = EHCI_QH_LIST_64MS + ((list_ix - EHCI_QH_LIST_32MS) * 2u);
|
|
p_new_qh->FrameInterval = 32u;
|
|
} else if ((list_ix >= EHCI_QH_LIST_16MS) && (list_ix < EHCI_QH_LIST_08MS)) {
|
|
ix_prev = EHCI_QH_LIST_32MS + ((list_ix - EHCI_QH_LIST_16MS) * 2u);
|
|
p_new_qh->FrameInterval = 16u;
|
|
} else if ((list_ix >= EHCI_QH_LIST_08MS) && (list_ix < EHCI_QH_LIST_04MS)) {
|
|
ix_prev = EHCI_QH_LIST_16MS + ((list_ix - EHCI_QH_LIST_08MS) * 2u);
|
|
p_new_qh->FrameInterval = 8u;
|
|
} else if ((list_ix >= EHCI_QH_LIST_04MS) && (list_ix < EHCI_QH_LIST_02MS)) {
|
|
ix_prev = EHCI_QH_LIST_08MS + ((list_ix - EHCI_QH_LIST_04MS) * 2u);
|
|
p_new_qh->FrameInterval = 4u;
|
|
} else if ((list_ix >= EHCI_QH_LIST_02MS) && (list_ix < EHCI_QH_LIST_01MS)) {
|
|
ix_prev = EHCI_QH_LIST_04MS + ((list_ix - EHCI_QH_LIST_02MS) * 2u);
|
|
p_new_qh->FrameInterval = 2u;
|
|
} else if (list_ix == EHCI_QH_LIST_01MS) {
|
|
/* 2MS list points to 1MS lists. */
|
|
ix_prev = EHCI_QH_LIST_02MS + ((list_ix - EHCI_QH_LIST_01MS) * 2u);
|
|
p_new_qh->FrameInterval = 1u;
|
|
} else {
|
|
/* Empty Else Statement */
|
|
}
|
|
|
|
p_new_qh->QHHorLinkPtr = HOR_LNK_PTR_TYP(DWORD1_TYP_QH);
|
|
|
|
if (list_ix != EHCI_QH_LIST_01MS) {
|
|
p_new_qh->QHHorLinkPtr |= HOR_LNK_PTR_T(DWORD1_T_VALID);
|
|
} else {
|
|
p_new_qh->QHHorLinkPtr |= HOR_LNK_PTR_T(DWORD1_T_INVALID);
|
|
}
|
|
|
|
CPU_DCACHE_RANGE_FLUSH(p_new_qh, sizeof(EHCI_QH));
|
|
|
|
if ((list_ix >= EHCI_QH_LIST_128MS) && (list_ix < EHCI_QH_LIST_64MS)) {
|
|
|
|
p_ehci->QHLists[EHCI_BranchArray[ix_prev]]->QHHorLinkPtr |= (CPU_INT32U)USBH_OS_VirToBus((void *)p_new_qh);
|
|
p_ehci->QHLists[EHCI_BranchArray[ix_prev + 1u]]->QHHorLinkPtr |= (CPU_INT32U)USBH_OS_VirToBus((void *)p_new_qh);
|
|
|
|
CPU_DCACHE_RANGE_FLUSH(p_ehci->QHLists[EHCI_BranchArray[ix_prev]], sizeof(EHCI_QH));
|
|
CPU_DCACHE_RANGE_FLUSH(p_ehci->QHLists[EHCI_BranchArray[ix_prev + 1u]], sizeof(EHCI_QH));
|
|
|
|
} else if ((list_ix >= EHCI_QH_LIST_64MS) && (list_ix <= EHCI_QH_LIST_01MS)) {
|
|
|
|
p_ehci->QHLists[ix_prev]->QHHorLinkPtr |= (CPU_INT32U)USBH_OS_VirToBus((void *)p_new_qh);
|
|
p_ehci->QHLists[ix_prev + 1u]->QHHorLinkPtr |= (CPU_INT32U)USBH_OS_VirToBus((void *)p_new_qh);
|
|
|
|
CPU_DCACHE_RANGE_FLUSH(p_ehci->QHLists[ix_prev], sizeof(EHCI_QH));
|
|
CPU_DCACHE_RANGE_FLUSH(p_ehci->QHLists[ix_prev + 1u], sizeof(EHCI_QH));
|
|
} else {
|
|
/* Empty Else Statement */
|
|
}
|
|
}
|
|
|
|
for (list_ix = EHCI_QH_LIST_256MS; list_ix < EHCI_QH_LIST_128MS; list_ix++) {
|
|
|
|
p_new_sitd = (EHCI_SITD *)Mem_PoolBlkGet(&p_ehci->HC_ITDPool,
|
|
sizeof(EHCI_SITD),
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
EHCI_ITD_Clr((EHCI_ITD *)p_new_sitd); /* Clear SiTD struct (overlay with iTD struct). */
|
|
|
|
p_new_sitd->SITDNxtLinkPtr = (CPU_INT32U)USBH_OS_VirToBus(p_ehci->QHLists[list_ix]) |
|
|
HOR_LNK_PTR_TYP(DWORD1_TYP_QH) |
|
|
HOR_LNK_PTR_T(DWORD1_T_VALID);
|
|
CPU_DCACHE_RANGE_FLUSH(p_new_sitd, sizeof(EHCI_SITD));
|
|
|
|
/* Insert this SiTD into periodic frame list */
|
|
p_ehci->PeriodicListBase[list_ix] = (CPU_INT32U)USBH_OS_VirToBus((void *)p_new_sitd) |
|
|
HOR_LNK_PTR_TYP(DWORD1_TYP_SITD);
|
|
CPU_DCACHE_RANGE_FLUSH(&p_ehci->PeriodicListBase[list_ix], sizeof(CPU_INT32U));
|
|
}
|
|
/* Update the periodic list base address */
|
|
PERIODICLISTBASE = (CPU_INT32U)USBH_OS_VirToBus((void *)p_ehci->PeriodicListBase);
|
|
|
|
USBCMD |= EHCI_USBCMD_WR_PSE; /* Enable periodic list processing */
|
|
|
|
return (USBH_ERR_NONE);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_AsyncListInit()
|
|
*
|
|
* Description : Initialize the asynchronous list. This will create a dummy queue head, which is the head
|
|
* of the all queue heads, writes the asynchronous list base address with appropriate value
|
|
* and enables the asynchronous list processing.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* Return(s) : USBH_ERR_NONE, if successful.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Note(s) : (1) Mark a queue head as being the head of the reclamation list.
|
|
* See section "4.8.3 Empty Asynchronous Schedule Detection" of EHCI spec for more details
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static USBH_ERR EHCI_AsyncListInit (USBH_HC_DRV *p_hc_drv)
|
|
{
|
|
EHCI_QH *p_new_qh;
|
|
EHCI_DEV *p_ehci;
|
|
LIB_ERR err_lib;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
p_new_qh = (EHCI_QH *)Mem_PoolBlkGet(&p_ehci->HC_QHPool,
|
|
sizeof(EHCI_QH),
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
return (USBH_ERR_ALLOC);
|
|
}
|
|
|
|
EHCI_QH_Clr(p_new_qh);
|
|
/* Set Head of reclamation list flag */
|
|
p_new_qh->QHHorLinkPtr = (CPU_INT32U)(USBH_OS_VirToBus(p_new_qh)) |
|
|
HOR_LNK_PTR_TYP(DWORD1_TYP_QH);
|
|
p_new_qh->QHEpCapChar[0] = QH_EPCHAR_H(DWORD2_QH_R_H); /* See Note #1 */
|
|
/* One transaction per micro frame */
|
|
p_new_qh->QHEpCapChar[1] = QH_EPCAP_HBPM(DWORD3_QH_HBPM_1);
|
|
p_new_qh->QHCurQTDPtr = (CPU_INT32U)0;
|
|
p_new_qh->QHNxtQTDPtr = (CPU_INT32U)0x00000001;
|
|
p_new_qh->QHAltNxtQTDPtr = (CPU_INT32U)0x00000001;
|
|
p_new_qh->QHToken = (CPU_INT32U)0;
|
|
p_new_qh->QHBufPagePtrList[0] = (CPU_INT32U)0;
|
|
p_new_qh->QHBufPagePtrList[1] = (CPU_INT32U)0;
|
|
p_new_qh->QHBufPagePtrList[2] = (CPU_INT32U)0;
|
|
p_new_qh->QHBufPagePtrList[3] = (CPU_INT32U)0;
|
|
p_new_qh->QHBufPagePtrList[4] = (CPU_INT32U)0;
|
|
CPU_DCACHE_RANGE_FLUSH(p_new_qh, sizeof(EHCI_QH));
|
|
p_ehci->AsyncQHHead = p_new_qh;
|
|
/* Update the async list base address */
|
|
ASYNCLISTADDR = (CPU_INT32U)(USBH_OS_VirToBus(p_new_qh));
|
|
|
|
return (USBH_ERR_NONE);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_QHDone()
|
|
*
|
|
* Description : Process completed queue head.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_qh Pointer to EHCI_QH structure.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : None
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void EHCI_QHDone (USBH_HC_DRV *p_hc_drv,
|
|
EHCI_QH *p_qh)
|
|
{
|
|
CPU_INT32U err_sts;
|
|
CPU_INT32U bytes_to_xfer;
|
|
CPU_INT32U qtd_head_addr;
|
|
CPU_INT32U qtd_head_addr_tmp;
|
|
USBH_URB *p_urb;
|
|
USBH_EP *p_ep;
|
|
|
|
|
|
CPU_DCACHE_RANGE_INV(p_qh, sizeof(EHCI_QH));
|
|
p_ep = p_qh->EPPtr;
|
|
err_sts = (p_qh->QHToken) & 0x000000FFu;
|
|
/* Search the URB associated with this transfer */
|
|
qtd_head_addr = (CPU_INT32U)p_qh->QTDHead;
|
|
p_urb = &p_ep->URB;
|
|
|
|
if (p_urb->AsyncURB_NxtPtr != 0) { /* Extra URB has been allocated for this EP */
|
|
|
|
while (p_urb->AsyncURB_NxtPtr != 0) {
|
|
|
|
qtd_head_addr_tmp = (CPU_INT32U)p_urb->ArgPtr;
|
|
if(qtd_head_addr_tmp == qtd_head_addr) { /* URB associated with this transfer found */
|
|
break;
|
|
}
|
|
|
|
p_urb = p_urb->AsyncURB_NxtPtr; /* Get the next extra URB in the queue */
|
|
}
|
|
}
|
|
|
|
|
|
if ((err_sts & O_QH_STS_HALTED) != 0u) { /* If QTD status is halted, retrieve error. */
|
|
/* EP corresponding to this QH */
|
|
if ((err_sts & O_QH_STS_DBE) != 0u) { /* Data Buffer Error */
|
|
p_urb->Err = USBH_ERR_HC_IO;
|
|
} else if (((err_sts & O_QH_STS_BD) != 0u) ||
|
|
((err_sts & O_QH_STS_XACT_ERR) != 0u) ||
|
|
((err_sts & O_QH_STS_MMF) != 0u) ||
|
|
((err_sts & O_QH_STS_PE) != 0u)) { /* Babble Detected */
|
|
p_urb->Err = USBH_ERR_HC_IO;
|
|
} else { /* If not the above errors, then it is stall */
|
|
p_urb->Err = USBH_ERR_EP_STALL;
|
|
}
|
|
|
|
bytes_to_xfer = EHCI_QTDRemove(p_hc_drv, p_qh); /* Remove and free all QTDs from the QTD list */
|
|
p_qh->QHCurQTDPtr = 0u;
|
|
p_urb->XferLen = p_urb->DMA_BufLen - bytes_to_xfer;
|
|
USBH_URB_Done(p_urb);
|
|
|
|
} else if ((err_sts & O_QH_STS_ACTIVE) == 0u){ /* The transaction completed successfully */
|
|
/* EP corresponding to this QH */
|
|
p_urb->Err = USBH_ERR_NONE;
|
|
bytes_to_xfer = EHCI_QTDRemove(p_hc_drv, p_qh); /* Remove and free all QTDs from the QTD list */
|
|
p_qh->QHCurQTDPtr = 0u;
|
|
p_urb->XferLen = p_urb->DMA_BufLen - bytes_to_xfer;
|
|
USBH_URB_Done(p_urb);
|
|
} else {
|
|
/* Empty Else Statement */
|
|
}
|
|
|
|
CPU_DCACHE_RANGE_FLUSH(p_qh, sizeof(EHCI_QH));
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_SITDDone()
|
|
*
|
|
* Description : Process completed queue head.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_ep_desc Pointer to endpoint descriptor
|
|
*
|
|
* dev_addr Device address
|
|
*
|
|
* ep_addr Endpoint address
|
|
*
|
|
* p_urb Pointer to URB structure.
|
|
*
|
|
* Return(s) : Total number of bytes transfered
|
|
*
|
|
* Note(s) : (1) Table 3-11 EHCI spec. Total Bytes To Transfer field of siTD. For an OUT, the host
|
|
* controller decrements this value. The number of bytes transferred is not written back.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static CPU_INT32U EHCI_SITDDone (USBH_HC_DRV *p_hc_drv,
|
|
EHCI_ISOC_EP_DESC *p_ep_desc,
|
|
CPU_INT08U dev_addr,
|
|
CPU_INT08U ep_addr,
|
|
USBH_URB *p_urb)
|
|
{
|
|
USBH_EP *p_ep;
|
|
EHCI_SITD *p_sitd;
|
|
CPU_INT32U *p_hw_desc;
|
|
CPU_INT16U frame_nbr;
|
|
CPU_INT08U err_sts;
|
|
CPU_INT32U len_rem_per_frame;
|
|
CPU_INT32U total_len_rem;
|
|
CPU_INT32U i;
|
|
USBH_ERR err;
|
|
LIB_ERR err_lib;
|
|
EHCI_ISOC_EP_URB *p_urb_info;
|
|
CPU_INT08U ep_dir;
|
|
EHCI_DEV *p_ehci;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
p_ep = p_ep_desc->EPPtr;
|
|
ep_dir = USBH_EP_DirGet(p_ep);
|
|
total_len_rem = 0u;
|
|
p_urb_info = (EHCI_ISOC_EP_URB *)p_urb->ArgPtr;
|
|
frame_nbr = p_urb_info->AppStartFrame;
|
|
|
|
for (i = 0u; i < p_urb_info->NbrFrame; i++) {
|
|
|
|
frame_nbr %= 256; /* Keep the Periodic Frame List index between 0 and 255 */
|
|
p_hw_desc = (CPU_INT32U *)USBH_OS_BusToVir((void *)(p_ehci->PeriodicListBase[frame_nbr] & 0xFFFFFFE0));
|
|
CPU_DCACHE_RANGE_INV(p_hw_desc, sizeof(CPU_INT32U));
|
|
|
|
while ((*p_hw_desc & 0x01u) == 0u) {
|
|
|
|
if ((*p_hw_desc & 0x06u) == HOR_LNK_PTR_TYP(DWORD1_TYP_SITD)) {
|
|
p_sitd = (EHCI_SITD *)USBH_OS_BusToVir((void *)(*p_hw_desc & 0xFFFFFFE0));
|
|
CPU_DCACHE_RANGE_INV(p_sitd, sizeof(EHCI_SITD));
|
|
|
|
if (((p_sitd->SITDEpCapChar[0] & 0x0000007Fu) == dev_addr) &&
|
|
(((p_sitd->SITDEpCapChar[0] & 0x00000F00u) >> 8u) == ep_addr )) {
|
|
|
|
*p_hw_desc = p_sitd->SITDNxtLinkPtr;
|
|
CPU_DCACHE_RANGE_FLUSH(p_hw_desc, sizeof(CPU_INT32U));
|
|
|
|
err_sts = p_sitd->SITDStsCtrl & 0x000000F2u;
|
|
|
|
if (((err_sts & O_SITD_STS_DBE) != 0u) ||
|
|
((err_sts & O_SITD_STS_ERR) != 0u) ||
|
|
((err_sts & O_SITD_STS_BD) != 0u) ||
|
|
((err_sts & O_SITD_STS_XACT_ERR) != 0u) ||
|
|
((err_sts & O_SITD_STS_MMF) != 0u)) {
|
|
err = USBH_ERR_HC_IO;
|
|
} else {
|
|
err = USBH_ERR_NONE;
|
|
}
|
|
|
|
if (ep_dir == USBH_EP_DIR_IN) {
|
|
/* Compute nbr of bytes remaining. */
|
|
len_rem_per_frame = (p_sitd->SITDStsCtrl >> 16u) & 0x3FFu;
|
|
total_len_rem += len_rem_per_frame;
|
|
/* Nbr of received bytes per frame. */
|
|
p_urb->IsocDescPtr->FrmLen[i] -= len_rem_per_frame;
|
|
} else {
|
|
total_len_rem = 0u; /* Nbr of bytes sent. See Note #1 */
|
|
}
|
|
/* Free the siTD structure */
|
|
Mem_PoolBlkFree( &p_ehci->HC_ITDPool,
|
|
(void *)p_sitd,
|
|
&err_lib);
|
|
|
|
p_urb->IsocDescPtr->FrmErr[i] = err;
|
|
break;
|
|
}
|
|
}
|
|
|
|
p_hw_desc = (CPU_INT32U *)USBH_OS_BusToVir((void *)(*p_hw_desc & 0xFFFFFFE0u));
|
|
CPU_DCACHE_RANGE_INV(p_hw_desc, sizeof(CPU_INT32U));
|
|
}
|
|
|
|
frame_nbr += p_ep_desc->FrameInterval;
|
|
}
|
|
|
|
Mem_PoolBlkFree( &p_ehci->HC_Isoc_EP_URBPool,
|
|
(void *)p_urb_info,
|
|
&err_lib);
|
|
|
|
return (total_len_rem);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_ITDDone()
|
|
*
|
|
* Description : Process completed queue head.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_ep_desc Pointer to endpoint descriptor
|
|
*
|
|
* dev_addr Device address
|
|
*
|
|
* ep_addr Endpoint address
|
|
*
|
|
* p_urb Pointer to URB structure.
|
|
*
|
|
* Return(s) : Number of bytes received
|
|
*
|
|
* Note(s) : (1) Table 3-3 EHCI spec. Transaction X Length field of iTD structure: For an OUT, this
|
|
* field is the number of data bytes the host controller will send during the transaction.
|
|
* The HC is not required to update this field to reflect the actual number of bytes
|
|
* transferred during the transfer.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static CPU_INT32U EHCI_ITDDone (USBH_HC_DRV *p_hc_drv,
|
|
EHCI_ISOC_EP_DESC *p_ep_desc,
|
|
CPU_INT08U dev_addr,
|
|
CPU_INT08U ep_addr,
|
|
USBH_URB *p_urb)
|
|
{
|
|
USBH_EP *p_ep;
|
|
EHCI_ITD *p_itd;
|
|
CPU_INT32U *p_hw_desc;
|
|
CPU_INT16U frame_nbr;
|
|
CPU_INT08U micro_frame_nbr;
|
|
CPU_INT08U err_sts;
|
|
CPU_INT08U i;
|
|
CPU_INT32U len_rxd_per_uframe;
|
|
CPU_INT32U total_len_rxd;
|
|
CPU_INT08U index;
|
|
USBH_ERR err;
|
|
LIB_ERR err_lib;
|
|
EHCI_ISOC_EP_URB *p_urb_info;
|
|
CPU_INT08U ep_dir;
|
|
EHCI_DEV *p_ehci;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
p_ep = p_ep_desc->EPPtr;
|
|
ep_dir = USBH_EP_DirGet(p_ep);
|
|
total_len_rxd = 0u;
|
|
p_urb_info = (EHCI_ISOC_EP_URB *)p_urb->ArgPtr;
|
|
frame_nbr = p_urb_info->AppStartFrame;
|
|
/* Search for iTD(s) associated to the Isoc EP */
|
|
for (i = 0u; i < p_urb_info->NbrFrame; i++) {
|
|
|
|
frame_nbr %= 256; /* Keep the Periodic Frame List index between 0 and 255 */
|
|
/* Retrieve the 1st linked data structure at this frame */
|
|
p_hw_desc = (CPU_INT32U *)USBH_OS_BusToVir((void *)(p_ehci->PeriodicListBase[frame_nbr] & 0xFFFFFFE0));
|
|
CPU_DCACHE_RANGE_INV(p_hw_desc, sizeof(CPU_INT32U));
|
|
|
|
while ((*p_hw_desc & 0x01u) == 0) { /* While Link Pointer is valid, browse the linked list */
|
|
/* Is the data structure referenced by a iTD ? */
|
|
if (((*p_hw_desc & 0x06u) == HOR_LNK_PTR_TYP(DWORD1_TYP_ITD))) {
|
|
/* Retrieve the physical addr of the iTD */
|
|
p_itd = (EHCI_ITD *)USBH_OS_BusToVir((void *)(*p_hw_desc & 0xFFFFFFE0u));
|
|
CPU_DCACHE_RANGE_INV(p_itd, sizeof(EHCI_ITD));
|
|
|
|
if (((p_itd->ITDBufPagePtrList[0] & 0x0000007Fu) == dev_addr) ||
|
|
(((p_itd->ITDBufPagePtrList[0] & 0x00000F00u) >> 8u) == ep_addr)) {
|
|
|
|
*p_hw_desc = p_itd->ITDNxtLinkPtr; /* Remove the iTD from this Periodic Frame List location*/
|
|
CPU_DCACHE_RANGE_FLUSH(p_hw_desc, sizeof(CPU_INT32U));
|
|
|
|
/* Get the completion status for each microframe */
|
|
for (micro_frame_nbr = 0; micro_frame_nbr < 8u; micro_frame_nbr++) {
|
|
err_sts = (p_itd->ITDStsAndCntrl[micro_frame_nbr] & 0xF0000000u) >> 28u;
|
|
|
|
if (((err_sts & O_ITD_STS_DBE) != 0u) ||
|
|
((err_sts & O_ITD_STS_BD) != 0u) ||
|
|
((err_sts & O_ITD_STS_XACTERR) != 0u)) {
|
|
err = USBH_ERR_HC_IO;
|
|
} else {
|
|
err = USBH_ERR_NONE;
|
|
}
|
|
|
|
if (ep_dir == USBH_EP_DIR_IN) {
|
|
/* Compute nbr of byte received */
|
|
len_rxd_per_uframe = ((p_itd->ITDStsAndCntrl[micro_frame_nbr] >> 16u) & 0x0FFFu);
|
|
total_len_rxd += len_rxd_per_uframe;
|
|
/* Nbr of received bytes per microframe. */
|
|
index = ((i*8u) + micro_frame_nbr);
|
|
p_urb->IsocDescPtr->FrmLen[index] = len_rxd_per_uframe;
|
|
|
|
} else {
|
|
total_len_rxd = 0u; /* Nbr of bytes sent. See Note #1 */
|
|
}
|
|
}
|
|
|
|
/* Save the completion status of the xfer */
|
|
p_urb->IsocDescPtr->FrmErr[i] = err;
|
|
/* Free the iTD structure */
|
|
Mem_PoolBlkFree( &p_ehci->HC_ITDPool,
|
|
(void *)p_itd,
|
|
&err_lib);
|
|
break;
|
|
}
|
|
}
|
|
|
|
p_hw_desc = (CPU_INT32U *)USBH_OS_BusToVir((void *)(*p_hw_desc & 0xFFFFFFE0));
|
|
CPU_DCACHE_RANGE_INV(p_hw_desc, sizeof(CPU_INT32U));
|
|
}
|
|
|
|
frame_nbr += p_ep_desc->FrameInterval;
|
|
}
|
|
/* Free the HCD Isoc EP structure */
|
|
Mem_PoolBlkFree( &p_ehci->HC_Isoc_EP_URBPool,
|
|
(void *)p_urb_info,
|
|
&err_lib);
|
|
|
|
return (total_len_rxd);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_BW_Get()
|
|
*
|
|
* Description : Get bandwidth allocation
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_ep Pointer to endpoint structure
|
|
*
|
|
* p_data Pointer to EHCI_QH structure
|
|
*
|
|
* Return(s) : USBH_ERR_NONE If successful.
|
|
* Specific error code otherwise.
|
|
*
|
|
* Note(s) : None
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static USBH_ERR EHCI_BW_Get (USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
void *p_data)
|
|
{
|
|
CPU_INT32U min_avail; /* Minimum available BW in a branch */
|
|
CPU_INT32U max_of_min_avail; /* Maximum value of all minimum available BW */
|
|
CPU_INT08U mask_nbr;
|
|
CPU_INT16U branch_nbr;
|
|
CPU_INT16U frame_nbr;
|
|
CPU_INT08U micro_frame_nbr;
|
|
CPU_INT08U nbr_mask = 0u;
|
|
CPU_INT16U nbr_branch;
|
|
CPU_INT16U frames_per_branch;
|
|
CPU_INT08U s_mask = 0u;
|
|
CPU_INT08U c_mask = 0u;
|
|
CPU_INT32U interval;
|
|
CPU_INT32U frame_interval;
|
|
CPU_BOOLEAN enough_BW;
|
|
CPU_INT16U ep_max_pkt_size;
|
|
CPU_INT08U ep_type;
|
|
CPU_INT08U ep_dir;
|
|
EHCI_QH *p_qh;
|
|
EHCI_ISOC_EP_DESC *p_ep_desc;
|
|
EHCI_DEV *p_ehci;
|
|
CPU_INT16U i;
|
|
CPU_INT08U j;
|
|
|
|
|
|
ep_max_pkt_size = USBH_EP_MaxPktSizeGet(p_ep);
|
|
ep_type = USBH_EP_TypeGet(p_ep);
|
|
ep_dir = USBH_EP_DirGet(p_ep);
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
|
|
if ((ep_type == USBH_EP_TYPE_INTR ) &&
|
|
(p_ep->DevSpd != USBH_DEV_SPD_HIGH)) {
|
|
|
|
interval = p_ep->Desc.bInterval;
|
|
j = 0u;
|
|
for (i = 0u; i < 8u; i++) {
|
|
if (((0x01u << i) & interval) != 0u) {
|
|
j = i;
|
|
}
|
|
}
|
|
interval &= (0x01u << j);
|
|
} else {
|
|
interval = 1 << (p_ep->Desc.bInterval - 1);
|
|
}
|
|
|
|
if ((p_ep->DevSpd == USBH_DEV_SPD_HIGH) && /* For high speed devices, interval is in usec units */
|
|
(interval < 8u)) {
|
|
|
|
switch (interval) {
|
|
case 1u:
|
|
s_mask = (CPU_INT08U)S_MASK_1MICROFRM; /* S-Mask for One Micro frame polling rate */
|
|
nbr_mask = 1u;
|
|
break;
|
|
|
|
case 2u:
|
|
s_mask = (CPU_INT08U)S_MASK_2MICROFRM; /* S-Mask for Two Micro frame polling rate */
|
|
nbr_mask = 2u;
|
|
break;
|
|
|
|
case 4u:
|
|
s_mask = (CPU_INT08U)S_MASK_4MICROFRM; /* S-Mask for Four Micro frame polling rate */
|
|
nbr_mask = 4u;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
frame_interval = 1u; /* EP should be inserted in Perodic frame list at frame interval rate 1 Ms */
|
|
|
|
} else { /* If the EP is Non-HS or HS with interval >= 8 */
|
|
s_mask = (CPU_INT08U)S_MASK_8MICROFRM;
|
|
nbr_mask = 7u;
|
|
|
|
if (p_ep->DevSpd == USBH_DEV_SPD_HIGH) {
|
|
frame_interval = interval / 8u; /* Convert micro frame interval to frame interval */
|
|
} else {
|
|
frame_interval = interval;
|
|
}
|
|
|
|
if (frame_interval > 256u) {
|
|
frame_interval = 256u;
|
|
}
|
|
}
|
|
|
|
if (ep_type == USBH_EP_TYPE_INTR) {
|
|
p_qh = (EHCI_QH *)p_data; /* For intr EP p_data var points to EHCI_QH struct. */
|
|
p_qh->FrameInterval = frame_interval;
|
|
} else {
|
|
p_ep_desc = (EHCI_ISOC_EP_DESC *)p_data; /* For isoc EP p_data pts to EHCI_ISOC_EP_DESC struct. */
|
|
p_ep_desc->FrameInterval = frame_interval;
|
|
}
|
|
|
|
max_of_min_avail = 0u;
|
|
nbr_branch = frame_interval;
|
|
frames_per_branch = 256u / nbr_branch;
|
|
|
|
if (ep_type == USBH_EP_TYPE_INTR) {
|
|
/* For each possible S-Mask */
|
|
for (mask_nbr = 0u; mask_nbr < nbr_mask; mask_nbr++) {
|
|
/* Starting from a frame number */
|
|
for (branch_nbr = 0u; branch_nbr < nbr_branch; branch_nbr++) {
|
|
enough_BW = 1u;
|
|
min_avail = EHCI_MAX_BW_PER_MICRO_FRAME;
|
|
/* For each frame after the interval */
|
|
frame_nbr = branch_nbr;
|
|
for (i = 0u; i < frames_per_branch; i++) {
|
|
/* For each micro frame */
|
|
for (micro_frame_nbr = 0u; micro_frame_nbr < 8u; micro_frame_nbr++) {
|
|
|
|
if ((s_mask & (1u << micro_frame_nbr)) != 0u) { /* If corresponding bit is set in S-Mask */
|
|
/* Take BW in this frame number and micro frame number */
|
|
min_avail = DEF_MIN(min_avail, p_ehci->MaxPeriodicBWArr[frame_nbr][micro_frame_nbr]);
|
|
|
|
if (min_avail < ep_max_pkt_size) { /* If BW is not available */
|
|
enough_BW = 0u;
|
|
break;
|
|
} else {
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (enough_BW == 0) { /* If BW is not available, go to next starting frame nbr*/
|
|
break;
|
|
}
|
|
|
|
frame_nbr += frame_interval;
|
|
}
|
|
|
|
if ((min_avail > max_of_min_avail) && (enough_BW != 0)) {
|
|
max_of_min_avail = min_avail; /* Take maximum of all minimum available */
|
|
p_qh->BWStartFrame = branch_nbr; /* Update starting frame number */
|
|
p_qh->SMask = s_mask; /* Update S-Mask */
|
|
CPU_DCACHE_RANGE_FLUSH(p_qh, sizeof(EHCI_QH));
|
|
}
|
|
}
|
|
|
|
s_mask = s_mask << 1u;
|
|
}
|
|
|
|
if (max_of_min_avail < ep_max_pkt_size) {
|
|
return (USBH_ERR_BW_NOT_AVAIL);
|
|
}
|
|
|
|
} else if ((ep_type == USBH_EP_TYPE_ISOC ) &&
|
|
(p_ep->DevSpd == USBH_DEV_SPD_FULL)) {
|
|
|
|
p_ep_desc->TCnt = (ep_max_pkt_size / 188) + 1;
|
|
|
|
if (ep_dir == USBH_EP_DIR_IN) {
|
|
s_mask = S_MASK_SPLIT_0_MICROFRM;
|
|
|
|
} else if (ep_dir == USBH_EP_DIR_OUT) {
|
|
c_mask = 0u;
|
|
} else {
|
|
/* Empty Else Statement */
|
|
}
|
|
|
|
switch (p_ep_desc->TCnt) {
|
|
|
|
case 1:
|
|
if (ep_dir == USBH_EP_DIR_OUT) {
|
|
s_mask = S_MASK_SPLIT_0_MICROFRM;
|
|
} else if (ep_dir == USBH_EP_DIR_IN) {
|
|
c_mask = C_MASK_SPLIT_0_MICROFRM;
|
|
} else {
|
|
/* Empty Else Statement */
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
if (ep_dir == USBH_EP_DIR_OUT) {
|
|
s_mask = S_MASK_SPLIT_01_MICROFRM;
|
|
} else if (ep_dir == USBH_EP_DIR_IN) {
|
|
c_mask = C_MASK_SPLIT_01_MICROFRM;
|
|
} else {
|
|
/* Empty Else Statement */
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
if (ep_dir == USBH_EP_DIR_OUT) {
|
|
s_mask = S_MASK_SPLIT_012_MICROFRM;
|
|
} else if (ep_dir == USBH_EP_DIR_IN) {
|
|
c_mask = C_MASK_SPLIT_012_MICROFRM;
|
|
} else {
|
|
/* Empty Else Statement */
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
if (ep_dir == USBH_EP_DIR_OUT) {
|
|
s_mask = S_MASK_SPLIT_0123_MICROFRM;
|
|
} else if (ep_dir == USBH_EP_DIR_IN) {
|
|
c_mask = C_MASK_SPLIT_0123_MICROFRM;
|
|
} else {
|
|
/* Empty Else Statement */
|
|
}
|
|
break;
|
|
|
|
case 5:
|
|
if (ep_dir == USBH_EP_DIR_OUT) {
|
|
s_mask = S_MASK_SPLIT_01234_MICROFRM;
|
|
} else if (ep_dir == USBH_EP_DIR_IN) {
|
|
c_mask = C_MASK_SPLIT_01234_MICROFRM;
|
|
} else {
|
|
/* Empty Else Statement */
|
|
}
|
|
break;
|
|
|
|
case 6:
|
|
if (ep_dir == USBH_EP_DIR_OUT) {
|
|
s_mask = S_MASK_SPLIT_012345_MICROFRM;
|
|
} else if (ep_dir == USBH_EP_DIR_IN) {
|
|
c_mask = C_MASK_SPLIT_012345_MICROFRM;
|
|
} else {
|
|
/* Empty Else Statement */
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
nbr_mask = 7 - p_ep_desc->TCnt;
|
|
/* For each possible S-Mask */
|
|
for (mask_nbr = 0u; mask_nbr < nbr_mask; mask_nbr++) {
|
|
enough_BW = 1u;
|
|
min_avail = EHCI_MAX_BW_PER_MICRO_FRAME;
|
|
|
|
for (frame_nbr = 0u; frame_nbr < 256u; frame_nbr++) {
|
|
|
|
for (micro_frame_nbr = 0u; micro_frame_nbr < 8u; micro_frame_nbr++) {
|
|
|
|
if ((s_mask & (1 << micro_frame_nbr)) != 0u) { /* If corresponding bit is set in S-Mask */
|
|
min_avail = DEF_MIN(min_avail, p_ehci->MaxPeriodicBWArr[frame_nbr][micro_frame_nbr]);
|
|
|
|
if (min_avail < ep_max_pkt_size) { /* If BW is not available */
|
|
enough_BW = 0u;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((c_mask & (1u << micro_frame_nbr)) != 0u) { /* If corresponding bit is set in S-Mask */
|
|
min_avail = DEF_MIN(min_avail, p_ehci->MaxPeriodicBWArr[frame_nbr][micro_frame_nbr]);
|
|
|
|
if (min_avail < ep_max_pkt_size) { /* If BW is not available */
|
|
enough_BW = 0u;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (enough_BW == 0u) { /* If BW is not available, go to next starting frame nbr*/
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((min_avail > max_of_min_avail) &&
|
|
(enough_BW != 0u)) {
|
|
max_of_min_avail = min_avail; /* Take max of all min available */
|
|
p_ep_desc->SMask = s_mask; /* Update S-Mask */
|
|
p_ep_desc->CMask = c_mask;
|
|
}
|
|
|
|
s_mask = s_mask << 1u;
|
|
c_mask = c_mask << 1u;
|
|
}
|
|
|
|
if (max_of_min_avail < ep_max_pkt_size) {
|
|
return (USBH_ERR_BW_NOT_AVAIL);
|
|
}
|
|
}
|
|
|
|
return (USBH_ERR_NONE);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_BW_Update()
|
|
*
|
|
* Description : Update bandwidth allocation
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_ep Pointer to endpoint structure
|
|
*
|
|
* p_data Pointer to EHCI_QH structure or isochronous endpoint structure
|
|
*
|
|
* bw_use Determine if bandwidth needs to be incremented/decremented in the array
|
|
*
|
|
* Return(s) : None
|
|
*
|
|
* Note(s) : None
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static void EHCI_BW_Update(USBH_HC_DRV *p_hc_drv,
|
|
USBH_EP *p_ep,
|
|
void *p_data,
|
|
CPU_BOOLEAN bw_use)
|
|
{
|
|
EHCI_QH *p_qh;
|
|
EHCI_ISOC_EP_DESC *p_ep_desc;
|
|
EHCI_DEV *p_ehci;
|
|
CPU_INT16U frame_nbr;
|
|
CPU_INT08U micro_frame_nbr;
|
|
CPU_INT08U start_frame_nbr;
|
|
CPU_INT16U ep_max_pkt_size;
|
|
CPU_INT08U s_mask;
|
|
CPU_INT08U c_mask;
|
|
CPU_INT16U frame_interval;
|
|
CPU_INT16U frames_per_branch;
|
|
CPU_INT16U i;
|
|
CPU_INT08U ep_type;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
ep_max_pkt_size = USBH_EP_MaxPktSizeGet(p_ep);
|
|
ep_type = USBH_EP_TypeGet(p_ep);
|
|
|
|
if (ep_type == USBH_EP_TYPE_INTR) {
|
|
p_qh = (EHCI_QH *)p_data;
|
|
|
|
CPU_DCACHE_RANGE_INV(p_qh, sizeof(EHCI_QH));
|
|
s_mask = p_qh->SMask;
|
|
frame_interval = p_qh->FrameInterval;
|
|
start_frame_nbr = p_qh->BWStartFrame;
|
|
} else {
|
|
p_ep_desc = (EHCI_ISOC_EP_DESC *)p_data;
|
|
s_mask = p_ep_desc->SMask;
|
|
c_mask = p_ep_desc->CMask;
|
|
frame_interval = p_ep_desc->FrameInterval;
|
|
start_frame_nbr = 0u;
|
|
}
|
|
|
|
frames_per_branch = 256u / frame_interval;
|
|
|
|
frame_nbr = start_frame_nbr;
|
|
for (i = 0u; i < frames_per_branch; i++) {
|
|
/* For each micro frame */
|
|
for (micro_frame_nbr = 0u; micro_frame_nbr < 8u; micro_frame_nbr++) {
|
|
if ((s_mask & (1 << micro_frame_nbr)) != 0u) { /* If corresponding bit is set in S-Mask */
|
|
if (bw_use == DEF_TRUE) { /* If BW is used, decrement BW in periodic BW array */
|
|
p_ehci->MaxPeriodicBWArr[frame_nbr][micro_frame_nbr] -= ep_max_pkt_size;
|
|
} else { /* If BW is released, increment BW in periodic BW array */
|
|
p_ehci->MaxPeriodicBWArr[frame_nbr][micro_frame_nbr] += ep_max_pkt_size;
|
|
}
|
|
}
|
|
|
|
if ((ep_type == USBH_EP_TYPE_ISOC ) &&
|
|
(p_ep->DevSpd == USBH_DEV_SPD_FULL)) {
|
|
if ((c_mask & (1 << micro_frame_nbr)) != 0u) { /* If corresponding bit is set in S-Mask */
|
|
if (bw_use == DEF_TRUE) {
|
|
/* If BW is used, decrement the BW in periodicBW array */
|
|
p_ehci->MaxPeriodicBWArr[frame_nbr][micro_frame_nbr] -= ep_max_pkt_size;
|
|
} else {
|
|
/* If BW is released, increment the BW in periodic BW array*/
|
|
p_ehci->MaxPeriodicBWArr[frame_nbr][micro_frame_nbr] += ep_max_pkt_size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
frame_nbr += frame_interval;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_IntrEPInsert()
|
|
*
|
|
* Description : Insert an Interrupt QH in the software QH list
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_qh_to_insert Pointer to EHCI_QH structure.
|
|
*
|
|
* Return(s) : None
|
|
*
|
|
* Note(s) : None
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBH_EHCI_CFG_PERIODIC_EN == DEF_ENABLED)
|
|
static void EHCI_IntrEPInsert(USBH_HC_DRV *p_hc_drv,
|
|
EHCI_QH *p_qh_to_insert)
|
|
|
|
|
|
{
|
|
EHCI_QH *p_prev_qh;
|
|
EHCI_DEV *p_ehci;
|
|
CPU_INT16U frame_interval;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
|
|
CPU_DCACHE_RANGE_INV(p_qh_to_insert, sizeof(EHCI_QH));
|
|
frame_interval = p_qh_to_insert->FrameInterval; /* Get ep polling interval to which belongs qH. */
|
|
/* Get a qH placeholder. */
|
|
p_prev_qh = p_ehci->QHLists[p_qh_to_insert->BWStartFrame];
|
|
|
|
CPU_DCACHE_RANGE_INV(p_prev_qh, sizeof(EHCI_QH));
|
|
while (p_prev_qh->FrameInterval != frame_interval) { /* Search polling interval list matching to qH to insert*/
|
|
|
|
p_prev_qh = (EHCI_QH *)USBH_OS_BusToVir((void *)(p_prev_qh->QHHorLinkPtr & 0xFFFFFFE0));
|
|
CPU_DCACHE_RANGE_INV(p_prev_qh, sizeof(EHCI_QH));
|
|
}
|
|
|
|
/* Insert qH at the selected placeholder. */
|
|
p_qh_to_insert->QHHorLinkPtr = p_prev_qh->QHHorLinkPtr;
|
|
p_prev_qh->QHHorLinkPtr = HOR_LNK_PTR_T(DWORD1_T_INVALID); /* Invalidate QH Next Link Ptr so that HC ignores it. */
|
|
p_prev_qh->QHHorLinkPtr = (CPU_INT32U)USBH_OS_VirToBus((void *)p_qh_to_insert) |
|
|
HOR_LNK_PTR_TYP(DWORD1_TYP_QH);
|
|
p_prev_qh->QHHorLinkPtr |= HOR_LNK_PTR_T(DWORD1_T_VALID); /* Validate Next Link Ptr pointed to QH to insert. */
|
|
|
|
CPU_DCACHE_RANGE_FLUSH(p_qh_to_insert, sizeof(EHCI_QH));
|
|
CPU_DCACHE_RANGE_FLUSH(p_prev_qh, sizeof(EHCI_QH));
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
* ROOT HUB FUNCTIONS
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_PortStatusGet()
|
|
*
|
|
* Description : Get port status changes and port status.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* port_nbr Port number.
|
|
*
|
|
* p_port_status Pointer to the port status structure.
|
|
*
|
|
* Return(s) : DEF_OK, If successful.
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN EHCI_PortStatusGet (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr,
|
|
USBH_HUB_PORT_STATUS *p_port_status)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
CPU_INT32U portsc;
|
|
CPU_INT16U status;
|
|
CPU_INT16U chng;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
|
|
if ((port_nbr == 0 ) || /* Port number starts from 1 */
|
|
(port_nbr > p_ehci->NbrPorts)) {
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
portsc = PORTSC(port_nbr - 1);
|
|
status = ((portsc & EHCI_PORTSC_RD_CCS) | /* bit0 to bit15 indicate port status */
|
|
((portsc & EHCI_PORTSC_RD_PED) >> 1) | /* bit16 to bit31 indicate port status chng */
|
|
((portsc & EHCI_PORTSC_RD_PP) >> 4));
|
|
|
|
if (p_ehci->DrvType == EHCI_HCD_GENERIC) {
|
|
|
|
if ((portsc & EHCI_PORTSC_RD_LS) == 0x400u) { /* Line status K-state: Low-speed device. */
|
|
status |= USBH_HUB_STATUS_PORT_LOW_SPD;
|
|
} else if ((portsc & EHCI_PORTSC_RD_PED) != 0u) {
|
|
status |= USBH_HUB_STATUS_PORT_HIGH_SPD;
|
|
} else {
|
|
/* Empty Else Statement */
|
|
}
|
|
|
|
} else { /* Port speed detection (Synopsys USB 2.0 Host IP). */
|
|
switch (portsc & EHCI_SYNOPSYS_PORTSC_RD_PSPD_MASK) {
|
|
case EHCI_SYNOPSYS_PORTSC_RD_PSPD_LS:
|
|
status |= USBH_HUB_STATUS_PORT_LOW_SPD;
|
|
break;
|
|
|
|
|
|
case EHCI_SYNOPSYS_PORTSC_RD_PSPD_FS:
|
|
status |= USBH_HUB_STATUS_PORT_FULL_SPD;
|
|
break;
|
|
|
|
|
|
case EHCI_SYNOPSYS_PORTSC_RD_PSPD_HS:
|
|
default:
|
|
status |= USBH_HUB_STATUS_PORT_HIGH_SPD;
|
|
break;
|
|
}
|
|
}
|
|
|
|
chng = (((portsc & EHCI_PORTSC_RD_CSC ) >> 1) |
|
|
((portsc & EHCI_PORTSC_RD_PEDC) >> 2));
|
|
|
|
if((p_ehci->PortResetChng & (1u << (port_nbr - 1u))) != 0u){
|
|
chng |= USBH_HUB_STATUS_C_PORT_RESET;
|
|
}
|
|
|
|
p_port_status->wPortChange = MEM_VAL_GET_INT16U_LITTLE(&chng);
|
|
p_port_status->wPortStatus = MEM_VAL_GET_INT16U_LITTLE(&status);
|
|
|
|
return (DEF_OK);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_HubDescGet()
|
|
*
|
|
* Description : Return root hub descriptor.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* p_buf Pointer to buffer that will receive hub descriptor.
|
|
*
|
|
* buf_len Buffer length in octets.
|
|
*
|
|
* Return(s) : DEF_OK, If successful.
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN EHCI_HubDescGet (USBH_HC_DRV *p_hc_drv,
|
|
void *p_buf,
|
|
CPU_INT08U buf_len)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
CPU_INT32U hc_rh_desc_a;
|
|
CPU_BOOLEAN port_pwr_mode;
|
|
USBH_HUB_DESC hub_desc;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
|
|
hc_rh_desc_a = p_ehci->HcCap.HCSParams;
|
|
port_pwr_mode = EHCI_PortPwrModeGet(p_ehci);/* Get port power mode */
|
|
hub_desc.bDescLength = USBH_HUB_LEN_HUB_DESC;
|
|
hub_desc.bDescriptorType = USBH_HUB_DESC_TYPE_HUB;
|
|
hub_desc.bNbrPorts = hc_rh_desc_a & EHCI_HCSPARAMS_RD_NP;
|
|
hub_desc.wHubCharacteristics = port_pwr_mode;
|
|
hub_desc.bHubContrCurrent = 0u;
|
|
|
|
/* Write the structure in USB format */
|
|
USBH_HUB_FmtHubDesc(&hub_desc, (void *)p_ehci->EHCI_HubBuf);
|
|
|
|
if (buf_len > sizeof(USBH_HUB_DESC)) {
|
|
buf_len = sizeof(USBH_HUB_DESC);
|
|
}
|
|
|
|
Mem_Copy( p_buf,
|
|
(void *)p_ehci->EHCI_HubBuf,
|
|
buf_len);
|
|
|
|
return (DEF_OK);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_PortEnSet()
|
|
*
|
|
* Description : Enable given port.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* port_nbr Port Number.
|
|
*
|
|
* Return(s) : DEF_OK, If successful.
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN EHCI_PortEnSet (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr)
|
|
{
|
|
(void)p_hc_drv;
|
|
(void)port_nbr;
|
|
|
|
return (DEF_OK);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_PortEnClr()
|
|
*
|
|
* Description : Clear port enable status.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* port_nbr Port Number.
|
|
*
|
|
* Return(s) : DEF_OK, If successful.
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN EHCI_PortEnClr (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr)
|
|
{
|
|
(void)p_hc_drv;
|
|
(void)port_nbr;
|
|
|
|
return (DEF_OK);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_PortEnChngClr()
|
|
*
|
|
* Description : Clear port enable status change.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* port_nbr Port Number.
|
|
*
|
|
* Return(s) : DEF_OK, If successful.
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN EHCI_PortEnChngClr (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
|
|
if ((port_nbr == 0 ) || /* Port number starts from 1 */
|
|
(port_nbr > p_ehci->NbrPorts)) {
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
PORTSC(port_nbr - 1) |= EHCI_PORTSC_WR_PEDC; /* Clear Port enable/disable status chng */
|
|
|
|
return (DEF_OK);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_PortPwrSet()
|
|
*
|
|
* Description : Set port power based on port power mode.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* port_nbr Port Number.
|
|
*
|
|
* Return(s) : DEF_OK, If successful.
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN EHCI_PortPwrSet (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
CPU_INT08U pwr_mode;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
|
|
if ((port_nbr == 0) || /* Port number start from 1. */
|
|
(port_nbr > p_ehci->NbrPorts)) {
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
pwr_mode = EHCI_PortPwrModeGet(p_ehci); /* Determine port power mode. */
|
|
|
|
if (pwr_mode == EHCI_PORT_POWERED_INDIVIDUAL) {
|
|
PORTSC(port_nbr - 1) |= EHCI_PORTSC_WR_PP_ON; /* Set Port Power */
|
|
}
|
|
|
|
return (DEF_OK);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_PortPwrClr()
|
|
*
|
|
* Description : Clear port power.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* port_nbr Port Number.
|
|
*
|
|
* Return(s) : DEF_OK, If successful.
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN EHCI_PortPwrClr (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr)
|
|
{
|
|
(void)p_hc_drv;
|
|
(void)port_nbr;
|
|
|
|
return (DEF_OK);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_PortResetSet()
|
|
*
|
|
* Description : Reset given port.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* port_nbr Port Number.
|
|
*
|
|
* Return(s) : DEF_OK, If successful.
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN EHCI_PortResetSet (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
CPU_INT32U portsc;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
|
|
if ((port_nbr == 0) || /* Port number starts from 1. */
|
|
(port_nbr > p_ehci->NbrPorts)) {
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
if ((USBSTATUS & EHCI_USBSTS_RD_HC_HAL) != 0u) { /* HC is in Halted state */
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
if (p_ehci->DrvType == EHCI_HCD_GENERIC) {
|
|
/* Line status K-state: Low-speed device, */
|
|
/* ... release port ownership. */
|
|
if ((PORTSC(port_nbr - 1u) & EHCI_PORTSC_RD_LS) == 0x400u) {
|
|
|
|
PORTSC(port_nbr - 1) |= EHCI_PORTSC_WR_CSC |
|
|
EHCI_PORTSC_WR_PEDC |
|
|
EHCI_PORTSC_WR_OCC;
|
|
PORTSC(port_nbr - 1) |= EHCI_PORTSC_WR_PO;
|
|
return (DEF_FAIL);
|
|
}
|
|
}
|
|
|
|
portsc = PORTSC(port_nbr - 1); /* Clear port enable bit */
|
|
portsc &= ~EHCI_PORTSC_WR_PED;
|
|
portsc |= EHCI_PORTSC_WR_PR;
|
|
PORTSC(port_nbr - 1) = portsc;
|
|
p_ehci->PortResetChng |= (1 << (port_nbr - 1));
|
|
|
|
return (DEF_OK);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_PortResetChngClr()
|
|
*
|
|
* Description : Clear port reset status change.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* port_nbr Port Number.
|
|
*
|
|
* Return(s) : DEF_OK, If successful.
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN EHCI_PortResetChngClr (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
CPU_INT08U cnt;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
cnt = 0u;
|
|
|
|
if ((port_nbr == 0u) || /* Port number starts from 1 */
|
|
(port_nbr > p_ehci->NbrPorts)) {
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
PORTSC(port_nbr - 1) &= ~EHCI_PORTSC_WR_PR;
|
|
|
|
USBH_OS_DlyMS(100u); /* Wait until port reset is cleared */
|
|
|
|
while (((PORTSC(port_nbr - 1u) & EHCI_PORTSC_RD_PR) != 0u) &&
|
|
(cnt < 5u)) {
|
|
USBH_OS_DlyMS(2u);
|
|
cnt++;
|
|
}
|
|
|
|
if (cnt >= 5u){
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
p_ehci->PortResetChng &= (~(1 << (port_nbr - 1)));
|
|
|
|
if (p_ehci->DrvType == EHCI_HCD_GENERIC) {
|
|
/* If port is not enabled after port reset completion, */
|
|
if ((PORTSC(port_nbr - 1u) & EHCI_PORTSC_RD_PED) == 0u) {
|
|
PORTSC(port_nbr - 1u) |= EHCI_PORTSC_WR_CSC |
|
|
EHCI_PORTSC_WR_PEDC |
|
|
EHCI_PORTSC_WR_OCC;
|
|
PORTSC(port_nbr - 1u) |= EHCI_PORTSC_WR_PO; /* Release port ownership. */
|
|
|
|
return (DEF_FAIL); /* Not a high speed device. */
|
|
}
|
|
}
|
|
|
|
return (DEF_OK);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_PortSuspendClr()
|
|
*
|
|
* Description : Resume given port if port is suspended.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* port_nbr Port Number.
|
|
*
|
|
* Return(s) : DEF_OK, If successful.
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN EHCI_PortSuspendClr (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
|
|
if ((port_nbr == 0u) || /* Port number starts from 1 */
|
|
(port_nbr > p_ehci->NbrPorts)) {
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
if ((PORTSC(port_nbr - 1u) & EHCI_PORTSC_RD_SUSP) != 0u) {
|
|
USBH_OS_DlyMS(100u);
|
|
PORTSC(port_nbr - 1u) |= EHCI_PORTSC_WR_FPR;
|
|
USBH_OS_DlyMS(200u);
|
|
PORTSC(port_nbr - 1u) &= ~EHCI_PORTSC_WR_FPR;
|
|
}
|
|
|
|
return (DEF_OK);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_PortConnChngClr()
|
|
*
|
|
* Description : Clear port connect status change.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* port_nbr Port Number.
|
|
*
|
|
* Return(s) : DEF_OK, If successful.
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN EHCI_PortConnChngClr (USBH_HC_DRV *p_hc_drv,
|
|
CPU_INT08U port_nbr)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
|
|
if ((port_nbr == 0 ) || /* Port number starts from 1 */
|
|
(port_nbr > p_ehci->NbrPorts)) {
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
PORTSC(port_nbr - 1) |= EHCI_PORTSC_WR_CSC; /* Clear Port connection status chng */
|
|
|
|
return (DEF_OK);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_PCD_IntEn()
|
|
*
|
|
* Description : Enable root hub interrupt.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* Return(s) : DEF_OK, If successful.
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN EHCI_PCD_IntEn (USBH_HC_DRV *p_hc_drv)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
|
|
if (p_ehci->HC_Started == DEF_TRUE) {
|
|
USBINT |= EHCI_USBINTR_WR_PCIE;
|
|
}
|
|
|
|
return (DEF_OK);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_PCD_IntDis()
|
|
*
|
|
* Description : Disable root hub interrupt.
|
|
*
|
|
* Argument(s) : p_hc_drv Pointer to host controller driver structure.
|
|
*
|
|
* Return(s) : DEF_OK, If successful.
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN EHCI_PCD_IntDis (USBH_HC_DRV *p_hc_drv)
|
|
{
|
|
EHCI_DEV *p_ehci;
|
|
|
|
|
|
p_ehci = (EHCI_DEV *)p_hc_drv->DataPtr;
|
|
USBINT &= ~EHCI_USBINTR_WR_PCIE;
|
|
|
|
return (DEF_OK);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_PortPwrModeGet()
|
|
*
|
|
* Description : Retrieve whether the given port is individually powered, globally powered or always powered.
|
|
*
|
|
* Argument(s) : p_ehci Pointer to EHCI_DEV structure.
|
|
*
|
|
* Return(s) : EHCI_PORT_POWERED_ALWAYS, if port is always powered. No switching.
|
|
* EHCI_PORT_POWERED_INDIVIDUAL, if port is individually powered.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN EHCI_PortPwrModeGet (EHCI_DEV *p_ehci)
|
|
{
|
|
if ((p_ehci->HcCap.HCSParams & EHCI_HCSPARAMS_RD_PPC) != 0u) {
|
|
return ((CPU_BOOLEAN)EHCI_PORT_POWERED_INDIVIDUAL); /* Ports are individually powered */
|
|
}
|
|
|
|
return ((CPU_BOOLEAN)EHCI_PORT_POWERED_ALWAYS); /* Ports are always powered */
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* EHCI_PortSuspendSet()
|
|
*
|
|
* Description : Suspend the given port if the port is enabled
|
|
*
|
|
* Argument(s) : p_ehci Pointer to EHCI_DEV structure
|
|
*
|
|
* port_nbr Port number
|
|
*
|
|
* Return(s) : USBH_ERR_NONE.
|
|
*
|
|
* Note(s) : See EHCI specification for USB, section 4.3.1. The software must wait at least 10 msec
|
|
* after a port indicates that it is suspended before initiating port resume
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static USBH_ERR EHCI_PortSuspendSet (EHCI_DEV *p_ehci,
|
|
CPU_INT32U port_nbr)
|
|
{
|
|
if ((PORTSC(port_nbr - 1) & EHCI_PORTSC_RD_PED) != 0u) {
|
|
PORTSC(port_nbr - 1) |= EHCI_PORTSC_RD_SUSP;
|
|
}
|
|
|
|
return (USBH_ERR_NONE);
|
|
}
|