Files
base/middleware/usb/uC-USBH/HCD/OHCI/usbh_hcd_ohci.c
2025-11-07 09:57:14 +08:00

3768 lines
164 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 OHCI DRIVER
*
* Filename : usbh_ohci.c
* Version : V3.42.01
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* INCLUDE FILES
*********************************************************************************************************
*/
#define USBH_OHCI_MODULE
#define MICRIUM_SOURCE
#include <cpu_cache.h>
#include "usbh_hcd_ohci.h"
#include "../../Source/usbh_hub.h"
/*
*********************************************************************************************************
* LOCAL DEFINES
*********************************************************************************************************
*/
/* ------------- OPERATIONAL REGISTER ACCESS ---------- */
#define HcRevision(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x00u))
#define HcCtrl(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x04u))
#define HcCmdStatus(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x08u))
#define HcIntStatus(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x0Cu))
#define HcIntEn(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x10u))
#define HcIntDis(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x14u))
#define HcHCCA(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x18u))
#define HcPeriodCurED(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x1Cu))
#define HcCtrlHeadED(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x20u))
#define HcCtrlCurED(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x24u))
#define HcBulkHeadED(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x28u))
#define HcBulkCurED(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x2Cu))
#define HcDoneHead(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x30u))
#define HcFrameInterval(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x34u))
#define HcFrameRem(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x38u))
#define HcFrameNbr(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x3Cu))
#define HcPeriodicStart(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x40u))
#define HcLSTh(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x44u))
#define HcRhDescA(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x48u))
#define HcRhDescB(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x4Cu))
#define HcRhStatus(ohcibase) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x50u))
#define HcRhPortStatus(ohcibase, i) ((CPU_INT32U *)((CPU_INT32U)(ohcibase) + 0x54u + (4 * (i))))
/* ----------- OHCI REGISTER OFFSET DEFINES ----------- */
#define OHCI_OR_HC_REVISION 0x00u /* HCI specification implemented by HC. */
#define OHCI_OR_HC_CTRL 0x04u /* Operating modes for the HC. */
#define OHCI_OR_HC_CMD_STATUS 0x08u /* Used by HC to rx cmds from HCD & reflect HC status. */
#define OHCI_OR_HC_INT_STATUS 0x0Cu /* Provide status on events that cause HW interrupts. */
#define OHCI_OR_HC_INT_EN 0x10u /* Used to enable HW interrupts. */
#define OHCI_OR_HC_INT_DIS 0x14u /* Used to disable HW interrupts. */
#define OHCI_OR_HC_HCCA 0x18u /* Physical addr of HCCA. */
#define OHCI_OR_HC_PERIOD_CUR_ED 0x1Cu /* Physical addr of current isoc or intr ED. */
#define OHCI_OR_HC_CTRL_HEAD_ED 0x20u /* Physical addr of first ED of ctrl list. */
#define OHCI_OR_HC_CTRL_CUR_ED 0x24u /* Physical addr of current ED of ctrl list. */
#define OHCI_OR_HC_BULK_HEAD_ED 0x28u /* Physical addr of first ED of bulk list. */
#define OHCI_OR_HC_BULK_CUR_ED 0x2Cu /* Physical addr of current ED of bulk list. */
#define OHCI_OR_HC_DONE_HEAD 0x30u /* Physical addr of last compeleted TD added to done q. */
#define OHCI_OR_HC_FRAME_INTERVAL 0x34u /* Bit time interval in frame & max pkt size w/o ovrun. */
#define OHCI_OR_HC_FRAME_REM 0x38u /* Bit time remaining in current frame. */
#define OHCI_OR_HC_FRAME_NBR 0x3Cu /* Frame number counter. */
#define OHCI_OR_HC_PERIODIC_START 0x40u /* Earliest time HC should start processing per list. */
#define OHCI_OR_HC_LS_TH 0x44u /* Used by HC to determine whether to tx LS pkt. */
#define OHCI_OR_HC_RH_DESC_A 0x48u /* Describes characteristics of the root hub. */
#define OHCI_OR_HC_RH_DESC_B 0x4Cu /* Describes characteristics of the root hub. */
#define OHCI_OR_HC_RH_STATUS 0x50u /* Hub status & status change. */
#define OHCI_OR_HC_RH_PORT_STATUS(n) (0x54u + (n) * 4)
/* ------------- OHCI REGISTER BIT DEFINES ------------ */
/* --------------- HcRevision REGISTER ---------------- */
#define OHCI_OR_REVISION_MASK 0x000000FFu
/* ---------------- HcControl REGISTER ---------------- */
#define OHCI_OR_CTRL_CBSR_MASK 0x00000003u /* Control Bulk Service Ratio. */
#define OHCI_OR_CTRL_CBSR_1_1 0x00000000u
#define OHCI_OR_CTRL_CBSR_2_1 0x00000001u
#define OHCI_OR_CTRL_CBSR_3_1 0x00000002u
#define OHCI_OR_CTRL_CBSR_4_1 0x00000003u
#define OHCI_OR_CTRL_PLE 0x00000004u /* Periodic List Enable. */
#define OHCI_OR_CTRL_IE 0x00000008u /* Isochronous Enable. */
#define OHCI_OR_CTRL_CLE 0x00000010u /* Control List Enable. */
#define OHCI_OR_CTRL_BLE 0x00000020u /* Bulk List Enable. */
#define OHCI_OR_CTRL_HCFS 0x000000C0u /* Host Controller Functional State. */
#define OHCI_OR_CTRL_HCFS_RESET 0x00000000u
#define OHCI_OR_CTRL_HCFS_RESUME 0x00000040u
#define OHCI_OR_CTRL_HCFS_OPER 0x00000080u
#define OHCI_OR_CTRL_HCFS_SUSPEND 0x000000C0u
#define OHCI_OR_CTRL_IR 0x00000100u /* Interrupt Routing. */
#define OHCI_OR_CTRL_RWC 0x00000200u /* Remote Wakeup Connected. */
#define OHCI_OR_CTRL_RWE 0x00000400u /* Remote Wakeup Enable. */
/* -------------- HcCommandStatus REGISTER ------------ */
#define OHCI_OR_CMD_STATUS_HCR 0x00000001u /* Host Controller Reset. */
#define OHCI_OR_CMD_STATUS_CLF 0x00000002u /* Control List Filled. */
#define OHCI_OR_CMD_STATUS_BLF 0x00000004u /* Bulk List Filled. */
#define OHCI_OR_CMD_STATUS_OCR 0x00000008u /* Ownership Change Request. */
#define OHCI_OR_CMD_STATUS_S0C 0x00030000u /* Scheduling Overrun Count. */
/* ------------ HcInterruptStatus REGISTER ------------ */
#define OHCI_OR_INT_STATUS_SO 0x00000001u /* Scheduling Overrun. */
#define OHCI_OR_INT_STATUS_WDH 0x00000002u /* Writeback Done Head. */
#define OHCI_OR_INT_STATUS_SF 0x00000004u /* Start of Frame. */
#define OHCI_OR_INT_STATUS_RD 0x00000008u /* Resume Detected. */
#define OHCI_OR_INT_STATUS_UE 0x00000010u /* Unrecoverable Error. */
#define OHCI_OR_INT_STATUS_FNO 0x00000020u /* Frame Number Overflow. */
#define OHCI_OR_INT_STATUS_RHSC 0x00000040u /* Root Hub Status Change. */
#define OHCI_OR_INT_STATUS_OC 0x40000000u /* Ownership Change. */
/* ------------ HcInterruptEnable REGISTER ------------ */
#define OHCI_OR_INT_EN_SO 0x00000001u /* Scheduling Overrun. */
#define OHCI_OR_INT_EN_WDH 0x00000002u /* Writeback Done Head. */
#define OHCI_OR_INT_EN_SF 0x00000004u /* Start of Frame. */
#define OHCI_OR_INT_EN_RD 0x00000008u /* Resume Detected. */
#define OHCI_OR_INT_EN_UE 0x00000010u /* Unrecoverable Error. */
#define OHCI_OR_INT_EN_FNO 0x00000020u /* Frame Number Overflow. */
#define OHCI_OR_INT_EN_RHSC 0x00000040u /* Root Hub Status Change. */
#define OHCI_OR_INT_EN_OC 0x40000000u /* Ownership Change. */
#define OHCI_OR_INT_EN_MIE 0x80000000u /* Master Interrupt Enable. */
/* ----------- HcInterruptDisable REGISTER ------------ */
#define OHCI_OR_INT_DISABLE_SO 0x00000001u /* Scheduling Overrun. */
#define OHCI_OR_INT_DISABLE_WDH 0x00000002u /* Writeback Done Head. */
#define OHCI_OR_INT_DISABLE_SF 0x00000004u /* Start of Frame. */
#define OHCI_OR_INT_DISABLE_RD 0x00000008u /* Resume Detected. */
#define OHCI_OR_INT_DISABLE_UE 0x00000010u /* Unrecoverable Error. */
#define OHCI_OR_INT_DISABLE_FNO 0x00000020u /* Frame Number Overflow. */
#define OHCI_OR_INT_DISABLE_RHSC 0x00000040u /* Root Hub Status Change. */
#define OHCI_OR_INT_DISABLE_OC 0x40000000u /* Ownership Change. */
#define OHCI_OR_INT_DISABLE_MIE 0x80000000u /* Master Interrupt Enable. */
/* ----------- HcPeriodCurrentED REGISTER ------------- */
#define OHCI_OR_PERIOD_CUR_ED_PCED 0xFFFFFFF0u /* Period Current ED. */
/* ------------- HcControlHeadED REGISTER ------------- */
#define OHCI_OR_CTRL_HEAD_ED_CHED 0xFFFFFFF0u /* Control Head ED. */
/* ------------ HcControlCurrentED REGISTER ----------- */
#define OHCI_OR_CTRL_CUR_ED_CCED 0xFFFFFFF0u /* Control Current ED. */
/* -------------- HcBulkHeadED REGISTER --------------- */
#define OHCI_OR_BULK_HEAD_ED_BHED 0xFFFFFFF0u /* Bulk Head ED. */
/* -------------- HcBulkCurrentED REGISTER ------------ */
#define OHCI_OR_BULK_CUR_ED_BCED 0xFFFFFFF0u /* Bulk Current ED. */
/* --------------- HcDoneHead REGISTER ---------------- */
#define OHCI_OR_DONEHEAD_DH 0xFFFFFFF0u /* Done Head. */
/* ---------------- HcFmInterval REGISTER ------------- */
#define OHCI_OR_FRAME_INTERVAL_FI 0x00003FFFu /* Frame Interval. */
#define OHCI_OR_FRAME_INTERVAL_FSMPS 0x7FFF0000u /* FS Largest Data Packet. */
#define OHCI_OR_FRAME_INTERVAL_FIT 0x80000000u /* Frame Interval Toggle. */
/* --------------- HcFmRemaining REGISTER ------------- */
#define OHCI_OR_FRAME_REM_FR 0x00003FFFu /* Frame Remaining. */
#define OHCI_OR_FRAME_REM_FRT 0x80000000u /* Frame Remaining Toggle. */
/* ----------------- HcFmNumber REGISTER -------------- */
#define OHCI_OR_FRAME_NBR_FN 0x0000FFFFu /* Frame Number. */
/* -------------- HcPeriodicStart REGISTER ------------ */
#define OHCI_OR_PERIODIC_START_PS 0x00003FFFu /* Periodic Start. */
/* -------------- HcLSThreshold REGISTER -------------- */
#define OHCI_OR_LS_TH_LST 0x000007FFu /* LS Threshold. */
/* ------------ HcRhDescriptorA REGISTER -------------- */
#define OHCI_OR_RH_DA_NDP 0x000000FFu /* Number Downstream Ports. */
#define OHCI_OR_RH_DA_PSM 0x00000100u /* Power Switching Mode. */
#define OHCI_OR_RH_DA_NPS 0x00000200u /* No Power Switching. */
#define OHCI_OR_RH_DA_DT 0x00000400u /* Device Type. */
#define OHCI_OR_RH_DA_OCPM 0x00000800u /* Over Current Protection Mode. */
#define OHCI_OR_RH_DA_NOCP 0x00001000u /* No Over Current Protection. */
#define OHCI_OR_RH_DA_POTPGT 0xFF000000u /* Power On To Power Good Time. */
/* ------------- HcRhDescriptorB REGISTER ------------- */
#define OHCI_OR_RH_DB_DR 0x0000FFFFu /* Device Removable. */
#define OHCI_OR_RH_DB_PPCM 0xFFFF0000u /* Port Power Control Mask. */
/* ---------------- HcRhStatus REGISTER --------------- */
#define OHCI_OR_RH_STATUS_LPS 0x00000001u /* Local Power Status. */
#define OHCI_OR_RH_STATUS_OCI 0x00000002u /* Over Current indicator. */
#define OHCI_OR_RH_STATUS_DRWE 0x00008000u /* Device Remote Wakeup Enable. */
#define OHCI_OR_RH_STATUS_LPSC 0x00010000u /* Local Power Status Change. */
#define OHCI_OR_RH_STATUS_OCIC 0x00020000u /* Over Current Indicator Change. */
#define OHCI_OR_RH_STATUS_CRWE 0x80000000u /* Clear Remote Wakeup Enable. */
/* ---------- HcRhPortStatus[1:NDP] REGISTER ---------- */
#define OHCI_OR_RH_PORT_CCS 0x00000001u /* Current Connect Status. */
#define OHCI_OR_RH_PORT_PES 0x00000002u /* Port Enable Status. */
#define OHCI_OR_RH_PORT_PSS 0x00000004u /* Port Suspend Status. */
#define OHCI_OR_RH_PORT_POCI 0x00000008u /* Port Over Current Indicator. */
#define OHCI_OR_RH_PORT_PRS 0x00000010u /* Port Reset Status. */
#define OHCI_OR_RH_PORT_PPS 0x00000100u /* Port Power Status. */
#define OHCI_OR_RH_PORT_LSDA 0x00000200u /* Low Speed Device Attached. */
#define OHCI_OR_RH_PORT_CSC 0x00010000u /* Connect Status Change. */
#define OHCI_OR_RH_PORT_PESC 0x00020000u /* Port Enable Status Change. */
#define OHCI_OR_RH_PORT_PSSC 0x00040000u /* Port Suspend Status Change. */
#define OHCI_OR_RH_PORT_OCIC 0x00080000u /* Over Current Indicator Change. */
#define OHCI_OR_RH_PORT_PRSC 0x00100000u /* Port Reset Status Change. */
/* --------- OHCI CONDITION CODE DEFINES -------------- */
/* Note(s) : (1) See 'OpenHCI - Open Host Controller */
/* Interface Specification for USB', */
/* Revision 1.0a, Section 4.3.3. */
/* (2) The 'CC' or 'Condition Code' field of */
/* a Transfer Descriptor contains one of */
/* these values. */
#define OHCI_CODE_NOERROR 0x00u /* TD processed without errors. */
#define OHCI_CODE_CRC 0x01u /* Last data pkt from EP contained a CRC error. */
#define OHCI_CODE_BITSTUFFING 0x02u /* Last data pkt from EP contained a bitstuffing viol. */
#define OHCI_CODE_DATATOGGLEMISMATCH 0x03u /* Last pkt from EP had unexpected data toggle PID. */
#define OHCI_CODE_STALL 0x04u /* TD was moved to Done Queue because EP returned stall.*/
#define OHCI_CODE_DEVICENOTRESPONDING 0x05u /* Dev didn't respond to token (IN) or provide hs (OUT).*/
#define OHCI_CODE_PIDCHECKFAILURE 0x06u /* Chk bits on PID from EP failed on data PID or hs. */
#define OHCI_CODE_UNEXPECTEDPID 0x07u /* Rx PID was not valid or PID value not defined. */
#define OHCI_CODE_DATAOVERRUN 0x08u /* EP rtn'd more than max pkt size or more than buf. */
#define OHCI_CODE_DATAUNDERRUN 0x09u /* EP rtn'd less than max pkt size & less than buf. */
#define OHCI_CODE_BUFFEROVERRUN 0x0Cu /* During IN, HC could not write data fast enough. */
#define OHCI_CODE_BUFFERUNDERRUN 0x0Du /* During OUT, HC could not retrieve data fast enough. */
#define OHCI_CODE_NOTACCESSED 0x0Fu /* Set by software before TD is placed on list. */
/* ------------ FRAME INTERVAL DEFINES ---------------- */
#define OHCI_FMIINTERVAL 0x2EDFu /* 12000 bits per frame. */
#define OHCI_FMINTERVAL_DFLT ((((6u * (OHCI_FMIINTERVAL - 210u)) / 7u) << 16u) | OHCI_FMIINTERVAL)
#define OHCI_FMINTERVAL_PERIODIC_START 0x2A2Fu
/* ------ OHCI ENDPOINT DESCRIPTOR FIELD DEFINES ------ */
/* Note(s) : (1) See 'OpenHCI - Open Host Controller */
/* Interface Specification for USB', */
/* Revision 1.0a, Section 4.2.2. */
/* ------------------- CONTROL WORD ------------------- */
#define OHCI_ED_FUNC_ADDR(x) ((x)) /* Usb Function Address. */
#define OHCI_ED_EP_NUM(x) ((x) << 7) /* EndPoint Number. */
#define OHCI_ED_DIR_TD 0x00000000u /* Get Direction from TD. */
#define OHCI_ED_DIR_OUT 0x00000800u /* Direction Out. */
#define OHCI_ED_DIR_IN 0x00001000u /* Direction In. */
#define OHCI_ED_SPD_FULL 0x00000000u /* Full Speed. */
#define OHCI_ED_SPD_LOW 0x00002000u /* Low Speed. */
#define OHCI_ED_SKIP 0x00004000u /* Skip. */
#define OHCI_ED_FMT_GEN_TD 0x00000000u /* General Format. */
#define OHCI_ED_FMT_ISO_TD 0x00008000u /* Isochronous Format. */
#define OHCI_ED_MAXPKTSIZE(x) (((x) << 16u)) /* Maximum Packet Size. */
/* ----------------- HEAD POINTER WORD ---------------- */
#define OHCI_ED_HALT 0x00000001u /* Halted. */
#define OHCI_ED_TOGGLECARRY 0x00000002u /* Toggle Carry. */
/* ------ OHCI TRANSMIT DESCRIPTOR FIELD DEFINES ------ */
/* Note(s) : (1) See 'OpenHCI - Open Host Controller */
/* Interface Specification for USB', */
/* Revision 1.0a, Sections 4.3.1.2. */
#define OHCI_TD_ROUNDING 0x00040000u /* Buffer Rounding. */
#define OHCI_TD_DIR_SETUP 0x00000000u /* Direction of Setup Packet. */
#define OHCI_TD_DIR_OUT 0x00080000u /* Direction Out. */
#define OHCI_TD_DIR_IN 0x00100000u /* Direction In. */
#define OHCI_TD_DELAY_INT(x) ((CPU_INT32U)(x) << 21u) /* Delay Interrupt. */
#define OHCI_TD_TOGGLE_0 0x02000000u /* Toggle 0. */
#define OHCI_TD_TOGGLE_1 0x03000000u /* Toggle 1. */
#define OHCI_TD_CC (((CPU_INT32U)0x0Fu << 28u)) /* Completion Code. */
#define OHCI_TDMASK_EC(x) ((((x) & 0x0C000000u) >> 26))
#define OHCI_TDMASK_CC(x) ((((x) & 0xF0000000u) >> 28))
/* - OHCI ISOCHRONOUS TRANSMIT DESCRIPTOR FIELD DEFINES */
/* Note(s) : (1) See 'OpenHCI - Open Host Controller */
/* Interface Specification for USB', */
/* Revision 1.0a, Sections 4.3.2.1. */
#define OHCI_ITD_OFFSET_4K_PAGE_BOUNDARY 0x0FFFu /* OffsetN field */
#define OHCI_ITD_OFFSET_BIT12_LOWER_WORD 0x1000u /* OffsetN field */
#define OHCI_ITD_OFFSET_BIT12_UPPER_WORD 0x10000000u /* OffsetN field */
#define OHCI_ITD_PSWN_CC_NOT_ACCESSED 0xE000u /* Packet Status Word (Condition Code) */
#define OHCI_TD_SF(x) ((x)) /* Start of Frame. */
#define OHCI_TD_FC(x) (((CPU_INT32U)x) << 24u) /* Frame Count. */
/* ------------- OHCI HCD_TD STATE DEFINES ------------ */
#define OHCI_HCD_TD_STATE_NONE 0u
#define OHCI_HCD_TD_STATE_COMPLETED 1u
#define OHCI_HCD_TD_STATE_CANCELLED 2u
/* ----------- OHCI POWER POWER METHOD DEFINES -------- */
#define OHCI_PORT_PWRD_ALWAYS 0u
#define OHCI_PORT_PWRD_GLOBAL 1u
#define OHCI_PORT_PWRD_INDIVIDUAL 2u
/* --------------- MEMORY MAPPING MACROS -------------- */
#define VIR2BUS(x) USBH_OS_VirToBus((x)) /* Conversion of Address from Virtual to Physical */
#define BUS2VIR(x) USBH_OS_BusToVir((x)) /* Conversion of Address from Physical to Virtual */
/* ---------- REGISTER ACCESS FUNCTION MACROS --------- */
#define Rd32(reg) (*((CPU_REG32 *)(reg)))
#define Wr32(reg, val) (*((CPU_REG32 *)(reg)) = val)
/* ----------------- 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
*********************************************************************************************************
*/
/* ------------ OHCI 32MS INTERVAL LIST ORDER --------- */
static const CPU_INT32U OHCI_BranchArray[] = {
0u, 16u, 8u, 24u,
4u, 20u, 12u, 28u,
2u, 18u, 10u, 26u,
6u, 22u, 14u, 30u,
1u, 17u, 9u, 25u,
5u, 21u, 13u, 29u,
3u, 19u, 11u, 27u,
7u, 23u, 15u, 31u
};
/*
*********************************************************************************************************
* LOCAL DATA TYPES
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL TABLES
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL GLOBAL VARIABLES
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL FUNCTION PROTOTYPES
*********************************************************************************************************
*/
/* --------------- DRIVER API FUNCTIONS --------------- */
static void OHCI_Init (USBH_HC_DRV *p_hc_drv,
USBH_ERR *p_err);
static void OHCI_Start (USBH_HC_DRV *p_hc_drv,
USBH_ERR *p_err);
static void OHCI_Stop (USBH_HC_DRV *p_hc_drv,
USBH_ERR *p_err);
static USBH_DEV_SPD OHCI_SpdGet (USBH_HC_DRV *p_hc_drv,
USBH_ERR *p_err);
static void OHCI_Suspend (USBH_HC_DRV *p_hc_drv,
USBH_ERR *p_err);
static void OHCI_Resume (USBH_HC_DRV *p_hc_drv,
USBH_ERR *p_err);
static CPU_INT32U OHCI_FrameNbrGet (USBH_HC_DRV *p_hc_drv,
USBH_ERR *p_err);
static void OHCI_EP_Open (USBH_HC_DRV *p_hc_drv,
USBH_EP *p_ep,
USBH_ERR *p_err);
static void OHCI_EP_Close (USBH_HC_DRV *p_hc_drv,
USBH_EP *p_ep,
USBH_ERR *p_err);
static void OHCI_EP_Abort (USBH_HC_DRV *p_hc_drv,
USBH_EP *p_ep,
USBH_ERR *p_err);
static CPU_BOOLEAN OHCI_IsHalt_EP (USBH_HC_DRV *p_hc_drv,
USBH_EP *p_ep,
USBH_ERR *p_err);
static void OHCI_URB_Submit (USBH_HC_DRV *p_hc_drv,
USBH_URB *p_urb,
USBH_ERR *p_err);
static void OHCI_URB_Complete (USBH_HC_DRV *p_hc_drv,
USBH_URB *p_urb,
USBH_ERR *p_err);
static void OHCI_URB_Abort (USBH_HC_DRV *p_hc_drv,
USBH_URB *p_urb,
USBH_ERR *p_err);
/* ---------------- INTERNAL FUNCTIONS ---------------- */
static USBH_ERR OHCI_DMA_Init (USBH_HC_DRV *p_hc_drv);
static void OHCI_HCD_ED_Clr (OHCI_HCD_ED *p_hcd_ed);
static void OHCI_HC_ED_Clr (OHCI_HC_ED *p_hc_ed);
static OHCI_HCD_ED *OHCI_HCD_ED_Create (USBH_HC_DRV *p_hc_drv);
static void OHCI_HCD_ED_Destroy (USBH_HC_DRV *p_hc_drv,
OHCI_HCD_ED *p_hcd_ed);
static void OHCI_HCD_TD_Clr (OHCI_HCD_TD *p_hcd_td);
static void OHCI_HC_TD_Clr (OHCI_HC_TD *p_hc_td);
static OHCI_HCD_TD *OHCI_HCD_TD_Create (USBH_HC_DRV *p_hc_drv);
static void OHCI_HCD_TD_Destroy (USBH_HC_DRV *p_hc_drv,
OHCI_HCD_TD *p_hcd_td);
static USBH_ERR OHCI_PeriodicListInit(USBH_HC_DRV *p_hc_drv);
static USBH_ERR OHCI_PeriodicEPOpen (USBH_HC_DRV *p_hc_drv,
OHCI_HCD_ED *p_ed,
CPU_INT32U interval);
static OHCI_HCD_TD *OHCI_HC_TD_Insert (USBH_HC_DRV *p_hc_drv,
USBH_EP *p_ep,
USBH_URB *p_urb,
CPU_INT32U td_ctrl,
CPU_INT32U buf_ptr,
CPU_INT32U buf_end,
CPU_INT32U *p_offsets);
static USBH_ERR OHCI_EPPause (USBH_HC_DRV *p_hc_drv,
USBH_EP *p_ep);
static void OHCI_EPResume (USBH_HC_DRV *p_hc_drv,
USBH_EP *p_ep);
static void OHCI_EPHalt (USBH_EP *p_ep);
static void OHCI_HaltEPClr (USBH_EP *p_ep);
static USBH_ERR OHCI_DoneHeadProcess (USBH_HC_DRV *p_hc_drv);
static void OHCI_ISR (void *p_arg);
/* -------------- ROOT HUB API FUNCTIONS -------------- */
static CPU_BOOLEAN OHCI_PortStatusGet (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr,
USBH_HUB_PORT_STATUS *p_port_status);
static CPU_BOOLEAN OHCI_HubDescGet (USBH_HC_DRV *p_hc_drv,
void *p_buf,
CPU_INT08U buf_len);
static CPU_BOOLEAN OHCI_PortEnSet (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr);
static CPU_BOOLEAN OHCI_PortEnClr (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr);
static CPU_BOOLEAN OHCI_PortEnChngClr (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr);
static CPU_BOOLEAN OHCI_PortPwrSet (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr);
static CPU_BOOLEAN OHCI_PortPwrClr (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr);
static CPU_BOOLEAN OHCI_PortResetSet (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr);
static CPU_BOOLEAN OHCI_PortResetChngClr(USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr);
static CPU_BOOLEAN OHCI_PortSuspendClr (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr);
static CPU_BOOLEAN OHCI_PortConnChngClr (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr);
static CPU_BOOLEAN OHCI_RHSC_IntEn (USBH_HC_DRV *p_hc_drv);
static CPU_BOOLEAN OHCI_RHSC_IntDis (USBH_HC_DRV *p_hc_drv);
static CPU_INT08S OHCI_PortPwrModeGet (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr);
static void OHCI_SetDataPtr (OHCI_DEV *p_ohci,
OHCI_HCD_TD *p_hcd_td,
OHCI_HC_TD *p_hc_td);
static OHCI_HCD_TD *OHCI_GetDataPtr (OHCI_DEV *p_ohci,
OHCI_HC_TD *p_hc_td);
/*
*********************************************************************************************************
* INITIALIZED GLOBAL VARIABLES
*********************************************************************************************************
*/
USBH_HC_DRV_API OHCI_DrvAPI = {
OHCI_Init,
OHCI_Start,
OHCI_Stop,
OHCI_SpdGet,
OHCI_Suspend,
OHCI_Resume,
OHCI_FrameNbrGet,
OHCI_EP_Open,
OHCI_EP_Close,
OHCI_EP_Abort,
OHCI_IsHalt_EP,
OHCI_URB_Submit,
OHCI_URB_Complete,
OHCI_URB_Abort,
};
USBH_HC_RH_API OHCI_RH_API = {
OHCI_PortStatusGet,
OHCI_HubDescGet,
OHCI_PortEnSet,
OHCI_PortEnClr,
OHCI_PortEnChngClr,
OHCI_PortPwrSet,
OHCI_PortPwrClr,
OHCI_PortResetSet,
OHCI_PortResetChngClr,
OHCI_PortSuspendClr,
OHCI_PortConnChngClr,
OHCI_RHSC_IntEn,
OHCI_RHSC_IntDis
};
/*
*********************************************************************************************************
* LOCAL CONFIGURATION ERRORS
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*********************************************************************************************************
* GLOBAL FUNCTIONS
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*********************************************************************************************************
* LOCAL FUNCTIONS
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* OHCI_Init()
*
* Description : Initialize OHCI host controller, issue hardware reset and software reset,
*
* initalize frame interval, and enable interrupts
*
* Argument(s) : hc_nbr Host controller number
*
* p_base_addr Base address of the host controller
*
* p_hc_dev Pointer to the OHCI device structure
*
* p_rh_dev Pointer to the root hub device structure
*
* Return(s) : USBH_ERR_NONE If initialization is success
* USBH_ERR_HC_INIT_FAILED If HC reset failed
* Specific error code Otherwise
*
* Note(s) : None
*********************************************************************************************************
*/
static void OHCI_Init (USBH_HC_DRV *p_hc_drv,
USBH_ERR *p_err)
{
OHCI_DEV *p_ohci;
USBH_HC_CFG *p_hc_cfg;
USBH_HC_BSP_API *p_bsp_api;
CPU_INT32U hc_ctrl;
CPU_SIZE_T octets_reqd;
CPU_INT32U cnt;
LIB_ERR err_lib;
CPU_BOOLEAN valid;
p_ohci = (OHCI_DEV *)Mem_HeapAlloc(sizeof(OHCI_DEV),
sizeof(CPU_ALIGN),
&octets_reqd,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBH_ERR_ALLOC;
return;
}
Mem_Clr(p_ohci, sizeof(OHCI_DEV));
p_hc_drv->DataPtr = (void *)p_ohci;
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;
}
}
*p_err = OHCI_DMA_Init(p_hc_drv); /* Initialize OHCI DMA structures */
if (*p_err != USBH_ERR_NONE) {
return;
}
p_hc_cfg = p_hc_drv->HC_CfgPtr;
/* Disable all interrupts */
Wr32(HcIntDis(p_hc_cfg->BaseAddr), OHCI_OR_INT_DISABLE_MIE |
OHCI_OR_INT_DISABLE_SO |
OHCI_OR_INT_DISABLE_WDH |
OHCI_OR_INT_DISABLE_SF |
OHCI_OR_INT_DISABLE_RD |
OHCI_OR_INT_DISABLE_UE |
OHCI_OR_INT_DISABLE_FNO |
OHCI_OR_INT_DISABLE_RHSC |
OHCI_OR_INT_DISABLE_OC);
valid = OHCI_RHSC_IntDis(p_hc_drv); /* Disable Root Hub interrupt */
if (valid == DEF_FAIL) {
*p_err = USBH_ERR_HC_INIT;
return;
}
hc_ctrl = Rd32( HcCtrl(p_hc_cfg->BaseAddr));
if ((hc_ctrl & OHCI_OR_CTRL_RWC) != 0u) {
hc_ctrl = OHCI_OR_CTRL_RWE; /* Enable remote wakeup */
p_ohci->CanWakeUp = 1u;
} else {
hc_ctrl = 0u;
}
/* ------------- RESET HOST CONTROLLER ---------------- */
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
USBH_PRINT_LOG("OHCI Applying Hardware Reset...\r\n");
#endif
USBH_OS_DlyMS(50u); /* Wait 50 ms before applying reset. */
hc_ctrl |= OHCI_OR_CTRL_HCFS_RESET; /* Apply hardware reset. */
Wr32(HcCtrl(p_hc_cfg->BaseAddr), hc_ctrl);
Wr32(HcCtrlHeadED(p_hc_cfg->BaseAddr), 0u); /* Initialize control list head to zero. */
Wr32(HcBulkHeadED(p_hc_cfg->BaseAddr), 0u); /* Initialize bulk list head to zero. */
Wr32(HcHCCA(p_hc_cfg->BaseAddr), 0u); /* Initialize HCCA interrupt table to zero. */
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
USBH_PRINT_LOG("OHCI Applying Software Reset...\r\n");
#endif
/* Apply software reset. */
Wr32(HcCmdStatus(p_hc_cfg->BaseAddr), OHCI_OR_CMD_STATUS_HCR);
cnt = 30u; /* HC software reset may take 10 us, wait 30 us. */
while ((Rd32(HcCmdStatus(p_hc_cfg->BaseAddr)) & OHCI_OR_CMD_STATUS_HCR) != 0u) {
if (cnt == 0u) {
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
USBH_PRINT_LOG("Host controller Software RESET failed.\r\n");
#endif
*p_err = USBH_ERR_HC_INIT;
return;
}
cnt--;
USBH_OS_DlyUS(1u);
}
/* HC goto OPERATIONAL state in 2 ms time limit */
/* Write frame interval & largest data pkt ctr. */
Wr32(HcFrameInterval(p_hc_cfg->BaseAddr), OHCI_FMINTERVAL_DFLT);
/* Write periodic start. */
Wr32(HcPeriodicStart(p_hc_cfg->BaseAddr), OHCI_FMINTERVAL_PERIODIC_START);
/* Root hub number of ports */
p_ohci->NbrPorts = Rd32(HcRhDescA(p_hc_cfg->BaseAddr)) & OHCI_OR_RH_DA_NDP;
*p_err = OHCI_PeriodicListInit(p_hc_drv); /* Initialize Periodic lists */
if (*p_err != USBH_ERR_NONE) {
return;
}
/* HCCA DMA */
CPU_DCACHE_RANGE_FLUSHINV(p_ohci->DMA_OHCI.HCCAPtr, sizeof(OHCI_HCCA));
Wr32(HcHCCA(p_hc_cfg->BaseAddr), (CPU_INT32U)VIR2BUS((void *)p_ohci->DMA_OHCI.HCCAPtr));
*p_err = USBH_ERR_NONE;
}
/*
*********************************************************************************************************
* OHCI_Start()
*
* Description : Start OHCI HC
*
* Argument(s) : p_ohci Pointer to OHCI device structure
*
* Return(s) : USBH_ERR_NONE
*
* Note(s) : None
*********************************************************************************************************
*/
static void OHCI_Start (USBH_HC_DRV *p_hc_drv,
USBH_ERR *p_err)
{
CPU_INT32U hc_ctrl;
USBH_HC_BSP_API *p_bsp_api;
USBH_HC_CFG *p_hc_cfg;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
p_bsp_api = p_hc_drv->BSP_API_Ptr;
hc_ctrl = Rd32(HcCtrl(p_hc_cfg->BaseAddr));
if ((hc_ctrl & OHCI_OR_CTRL_RWC) != 0u) {
hc_ctrl = OHCI_OR_CTRL_RWE; /* Enable remote wakeup */
}
hc_ctrl &= ~OHCI_OR_CTRL_HCFS;
hc_ctrl |= OHCI_OR_CTRL_HCFS_OPER; /* Put HC in operational state. */
Wr32(HcCtrl(p_hc_cfg->BaseAddr), hc_ctrl);
Wr32(HcIntDis(p_hc_cfg->BaseAddr), 0u);
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
USBH_PRINT_LOG("OHCI 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(OHCI_ISR, p_err);
if (*p_err != USBH_ERR_NONE) {
return;
}
}
Wr32(HcIntEn(p_hc_cfg->BaseAddr), OHCI_OR_INT_EN_MIE | /* Enable int. */
OHCI_OR_INT_EN_WDH |
OHCI_OR_INT_EN_FNO |
OHCI_OR_INT_EN_RD |
OHCI_OR_INT_EN_SO |
OHCI_OR_INT_EN_UE |
OHCI_OR_INT_EN_OC);
*p_err = USBH_ERR_NONE;
}
/*
*********************************************************************************************************
* OHCI_Stop()
*
* Description : Stop OHCI HC
*
* Argument(s) : p_ohci Pointer to OHCI device structure
*
* Return(s) : USBH_ERR_NONE
*
* Note(s) : None
*********************************************************************************************************
*/
static void OHCI_Stop (USBH_HC_DRV *p_hc_drv,
USBH_ERR *p_err)
{
USBH_HC_CFG *p_hc_cfg;
USBH_HC_BSP_API *p_bsp_api;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
p_bsp_api = p_hc_drv->BSP_API_Ptr;
Wr32(HcCtrl(p_hc_cfg->BaseAddr), OHCI_OR_CTRL_HCFS_RESET); /* Apply hw reset. */
if ((p_bsp_api != (USBH_HC_BSP_API *)0) &&
(p_bsp_api->ISR_Unreg != 0)) {
p_bsp_api->ISR_Unreg(p_err);
if (*p_err != USBH_ERR_NONE) {
return;
}
}
*p_err = USBH_ERR_NONE;
}
/*
*********************************************************************************************************
* OHCI_SpeedGet()
*
* Description : Returns Host Controller Speed.
*
* Argument(s) : p_hc_dev Pointer to OHCI device structure
*
* Return(s) : USBH_DEV_SPD_FULL
*
* Note(s) : None.
*
*********************************************************************************************************
*/
static USBH_DEV_SPD OHCI_SpdGet (USBH_HC_DRV *p_hc_drv,
USBH_ERR *p_err)
{
(void)p_hc_drv;
*p_err = USBH_ERR_NONE;
return (USBH_DEV_SPD_FULL); /*Since OHCI controller standard supports upto FULL speed */
}
/*
*********************************************************************************************************
* OHCI_Suspend()
*
* Description : Suspend HC
*
* Argument(s) : p_dev Pointer to root hub device structure
*
* Return(s) : USBH_ERR_NONE If the HC is suspended
* USBH_ERR_UNKNOWN If the HC state is not among the 4 states described in USB spec
*
* Note(s) : None
*********************************************************************************************************
*/
static void OHCI_Suspend (USBH_HC_DRV *p_hc_drv,
USBH_ERR *p_err)
{
CPU_INT32U hc_ctrl;
CPU_INT32U cnt;
CPU_INT32U cur_frame_nbr;
USBH_HC_CFG *p_hc_cfg;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
hc_ctrl = Rd32( HcCtrl(p_hc_cfg->BaseAddr)); /* Read the Host controller state */
switch ((hc_ctrl & OHCI_OR_CTRL_HCFS)) {
case OHCI_OR_CTRL_HCFS_RESET:
case OHCI_OR_CTRL_HCFS_SUSPEND:
*p_err = USBH_ERR_NONE; /* HC is already in suspend state */
return;
case OHCI_OR_CTRL_HCFS_RESUME: /* Current state is resume or operational,... */
case OHCI_OR_CTRL_HCFS_OPER: /* ...goto suspend state. */
break;
default:
*p_err = USBH_ERR_UNKNOWN;
return;
}
/* Stop list processing; take effect at the next frame */
hc_ctrl &= ~(OHCI_OR_CTRL_PLE |
OHCI_OR_CTRL_IE |
OHCI_OR_CTRL_CLE |
OHCI_OR_CTRL_BLE);
cnt = 0u;
cur_frame_nbr = Rd32(HcFrameNbr(p_hc_cfg->BaseAddr)); /* Get the current frame number */
/* Wait for the current frame to complete */
while (Rd32(HcFrameNbr(p_hc_cfg->BaseAddr)) == cur_frame_nbr) {
++cnt;
if (cnt > 3u) {
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
USBH_PRINT_LOG("OHCI_Suspend() FmNumber does not increment !!! ERROR\r\n");
#endif
*p_err = USBH_ERR_UNKNOWN;
return;
}
USBH_OS_DlyMS(1u);
}
hc_ctrl &= ~OHCI_OR_CTRL_HCFS;
hc_ctrl |= OHCI_OR_CTRL_HCFS_SUSPEND; /* Enable suspend */
Wr32( HcCtrl(p_hc_cfg->BaseAddr), hc_ctrl); /* Write the changes to control register */
*p_err = USBH_ERR_NONE;
}
/*
*********************************************************************************************************
* OHCI_Resume()
*
* Description : Resume HC
*
* Argument(s) : p_hc_dev Pointer to OHCI device structure
*
* Return(s) : USBH_ERR_NONE If the HC is resumed
* USBH_ERR_UNKNOWN If the HC state is not among the 4 states described in USB spec
*
* Note(s) : None
*********************************************************************************************************
*/
static void OHCI_Resume (USBH_HC_DRV *p_hc_drv,
USBH_ERR *p_err)
{
CPU_INT32U hc_ctrl;
USBH_HC_CFG *p_hc_cfg;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
hc_ctrl = Rd32( HcCtrl(p_hc_cfg->BaseAddr)); /* Read the Host controller state */
switch ((hc_ctrl & OHCI_OR_CTRL_HCFS)) {
case OHCI_OR_CTRL_HCFS_RESUME:
case OHCI_OR_CTRL_HCFS_OPER:
*p_err = USBH_ERR_NONE;
return;
case OHCI_OR_CTRL_HCFS_RESET:
case OHCI_OR_CTRL_HCFS_SUSPEND:
break;
default:
*p_err = USBH_ERR_UNKNOWN;
return;
}
hc_ctrl &= ~OHCI_OR_CTRL_HCFS; /* Enable Resume */
hc_ctrl |= OHCI_OR_CTRL_HCFS_RESUME;
Wr32( HcCtrl(p_hc_cfg->BaseAddr), hc_ctrl);
hc_ctrl &= ~OHCI_OR_CTRL_HCFS; /* Enable Operational */
hc_ctrl |= OHCI_OR_CTRL_HCFS_OPER;
Wr32( HcCtrl(p_hc_cfg->BaseAddr), hc_ctrl);
Wr32( HcIntEn(p_hc_cfg->BaseAddr), OHCI_OR_INT_EN_RHSC);
*p_err = USBH_ERR_NONE;
}
/*
*********************************************************************************************************
* OHCI_FrameNbrGet()
*
* Description : Retrieve frame number.
*
* Argument(s) : p_dev Pointer to device structure.
*
* Return(s) : Frame number.
*
* Note(s) : None.
*********************************************************************************************************
*/
static CPU_INT32U OHCI_FrameNbrGet (USBH_HC_DRV *p_hc_drv,
USBH_ERR *p_err)
{
USBH_HC_CFG *p_hc_cfg;
CPU_INT32U frm_nbr;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
frm_nbr = Rd32(HcFrameNbr(p_hc_cfg->BaseAddr)); /* Get the current frame number */
*p_err = USBH_ERR_NONE;
return (frm_nbr);
}
/*
*********************************************************************************************************
* OHCI_EP_Open()
*
* Description : Create OHCI endpoint descriptor structure for the given endpoint
*
* Argument(s) : p_dev Pointer to device structure
*
* p_ep Pointer to endpoint structure
*
* Return(s) : USBH_ERR_NONE If endpoint open is success
* USBH_ERR_INVALID_ARGS If p_dev or p_ep is 0
* USBH_ERR_HW_DESC_ALLOC If ED is not created due to insufficient memory
* Specific error code Otherwise
*
* Note(s) : None
*********************************************************************************************************
*/
static void OHCI_EP_Open (USBH_HC_DRV *p_hc_drv,
USBH_EP *p_ep,
USBH_ERR *p_err)
{
OHCI_HCD_ED *p_hcd_ed_list;
OHCI_HC_ED *p_new_hc_ed;
OHCI_HCD_ED *p_new_hcd_ed;
OHCI_DEV *p_ohci;
CPU_INT32U list_idx;
CPU_INT08U ep_nbr;
CPU_INT08U ep_type;
CPU_INT16U ep_max_pkt_size;
CPU_INT08U ep_dir;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
ep_nbr = USBH_EP_LogNbrGet(p_ep); /* Get endpoint attributes */
ep_dir = USBH_EP_DirGet(p_ep);
ep_type = USBH_EP_TypeGet(p_ep);
ep_max_pkt_size = USBH_EP_MaxPktSizeGet(p_ep);
p_new_hcd_ed = OHCI_HCD_ED_Create(p_hc_drv);
if (p_new_hcd_ed == (OHCI_HCD_ED *)0) {
*p_err = USBH_ERR_ALLOC;
return;
}
/* Initialize HC_ED structure */
p_new_hc_ed = p_new_hcd_ed->HC_EDPtr;
p_new_hc_ed->Ctrl = OHCI_ED_FUNC_ADDR(p_ep->DevAddr) |
OHCI_ED_EP_NUM(ep_nbr) |
((ep_dir == USBH_EP_DIR_IN) ? OHCI_ED_DIR_IN :
((ep_dir == USBH_EP_DIR_OUT) ? OHCI_ED_DIR_OUT : OHCI_ED_DIR_TD)) |
((p_ep->DevSpd == USBH_DEV_SPD_FULL) ? OHCI_ED_SPD_FULL : OHCI_ED_SPD_LOW) |
(((ep_type == USBH_EP_TYPE_CTRL) || (ep_type == USBH_EP_TYPE_BULK) || (ep_type == USBH_EP_TYPE_INTR)) ?
OHCI_ED_FMT_GEN_TD : OHCI_ED_FMT_ISO_TD) |
OHCI_ED_MAXPKTSIZE(ep_max_pkt_size);
p_new_hc_ed->Next = 0u;
p_new_hc_ed->TailTD = 0u;
p_new_hc_ed->HeadTD = 0u;
p_new_hcd_ed->Head_HCD_TDPtr = 0;
p_new_hcd_ed->Tail_HCD_TDPtr = 0;
p_new_hcd_ed->NextPtr = 0;
CPU_DCACHE_RANGE_FLUSHINV(p_new_hc_ed, 16u);
/* Ctrl or Bulk ep open */
if ((ep_type == USBH_EP_TYPE_CTRL) ||
(ep_type == USBH_EP_TYPE_BULK)) {
if (ep_type == USBH_EP_TYPE_CTRL) {
list_idx = OHCI_ED_LIST_CTRL;
} else {
list_idx = OHCI_ED_LIST_BULK;
}
p_hcd_ed_list = p_ohci->EDLists[list_idx];
if (p_hcd_ed_list == (OHCI_HCD_ED *)0) { /* Check for empty list */
p_hcd_ed_list = p_new_hcd_ed;
p_ohci->EDLists[list_idx] = p_new_hcd_ed;
} else {
while (p_hcd_ed_list->NextPtr) { /* Goto end of Ed List */
p_hcd_ed_list = p_hcd_ed_list->NextPtr;
}
p_hcd_ed_list->NextPtr = p_new_hcd_ed; /* Insert ed at the end */
p_hcd_ed_list->HC_EDPtr->Next = p_new_hcd_ed->DMA_HC_ED;
CPU_DCACHE_RANGE_FLUSHINV(p_hcd_ed_list->HC_EDPtr, 16u);
}
} else {
*p_err = OHCI_PeriodicEPOpen(p_hc_drv, /* Periodic ep open */
p_new_hcd_ed,
p_ep->Interval);
if (*p_err != USBH_ERR_NONE) {
OHCI_HCD_ED_Destroy(p_hc_drv, p_new_hcd_ed);
return;
}
}
p_ep->ArgPtr = (void *)p_new_hcd_ed;
OHCI_HC_TD_Insert(p_hc_drv, p_ep, 0, 0u, 0u, 0u, 0); /* Insert Dummy TD in the list */
*p_err = USBH_ERR_NONE;
}
/*
*********************************************************************************************************
* OHCI_EP_Close()
*
* Description : Close the endpoint by unlinking the OHCI endpoint descriptor
*
* Argument(s) : p_dev Pointer to device structure
*
* p_ep Pointer to endpoint structure
*
* Return(s) : USBH_ERR_NONE If endpoint closed successfully
* USBH_ERR_INVALID_ARGS If p_dev or p_ep is 0
* Specific error code Otherwise
*
* Note(s) : None
*********************************************************************************************************
*/
static void OHCI_EP_Close (USBH_HC_DRV *p_hc_drv,
USBH_EP *p_ep,
USBH_ERR *p_err)
{
OHCI_DEV *p_ohci;
USBH_HC_CFG *p_hc_cfg;
OHCI_HCD_ED *p_hcd_ed;
OHCI_HCD_ED *p_hcd_ed_list;
CPU_INT32U list_idx;
CPU_INT32U list_begin_idx;
CPU_INT32U list_end_idx;
CPU_INT32U mask;
CPU_INT32U hc_ctrl;
CPU_INT32U *p_hc_cur_ed;
CPU_INT08U ep_type;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
p_hcd_ed = (OHCI_HCD_ED *)p_ep->ArgPtr;
p_hcd_ed_list = (OHCI_HCD_ED *)0;
ep_type = USBH_EP_TypeGet(p_ep);
if (ep_type == USBH_EP_TYPE_CTRL) { /* Determine which list */
list_begin_idx = OHCI_ED_LIST_CTRL; /* There is only one Control list */
list_end_idx = OHCI_ED_LIST_CTRL; /* There is only one Control list */
mask = OHCI_OR_CTRL_CLE;
p_hc_cur_ed = HcCtrlCurED(p_hc_cfg->BaseAddr);
} else if (ep_type == USBH_EP_TYPE_BULK) {
list_begin_idx = OHCI_ED_LIST_BULK; /* There is only one Bulk list */
list_end_idx = OHCI_ED_LIST_BULK; /* There is only one Bulk list */
mask = OHCI_OR_CTRL_BLE;
p_hc_cur_ed = HcBulkCurED(p_hc_cfg->BaseAddr);
} else {
list_begin_idx = OHCI_ED_LIST_32MS; /* Update bandwidth usage */
list_end_idx = OHCI_ED_LIST_01MS; /* There are 32 periodic lists */
mask = OHCI_OR_CTRL_PLE;
p_hc_cur_ed = HcPeriodCurED(p_hc_cfg->BaseAddr);
}
/* Disable list processing by HC */
hc_ctrl = Rd32( HcCtrl(p_hc_cfg->BaseAddr));
hc_ctrl &= ~mask;
Wr32( HcCtrl(p_hc_cfg->BaseAddr), hc_ctrl);
*p_err = OHCI_EPPause(p_hc_drv, p_ep);
if (*p_err != USBH_ERR_NONE) {
return;
}
/* Go through each list */
for (list_idx = list_begin_idx; list_idx <= list_end_idx; list_idx++) {
p_hcd_ed_list = p_ohci->EDLists[list_idx]; /* Get the Head ed of the list */
if (p_hcd_ed_list == p_hcd_ed) { /* The current ed found at head */
break;
}
/* Locate the previous Ed to the current ed */
while ((p_hcd_ed_list != 0u ) &&
(p_hcd_ed_list->NextPtr != p_hcd_ed)) {
p_hcd_ed_list = p_hcd_ed_list->NextPtr;
}
/* The current ed found in the middle */
if ((p_hcd_ed_list != 0u ) &&
(p_hcd_ed_list->NextPtr == p_hcd_ed)) {
break;
}
}
if (p_hcd_ed_list == p_hcd_ed) { /* Unlink this ed from the list */
p_ohci->EDLists[list_idx] = p_hcd_ed_list->NextPtr; /* Current ed is head. Update head */
if (ep_type == USBH_EP_TYPE_CTRL) {
if (p_ohci->EDLists[list_idx]) {
Wr32(HcCtrlHeadED(p_hc_cfg->BaseAddr), p_ohci->EDLists[OHCI_ED_LIST_CTRL]->DMA_HC_ED);
} else {
Wr32(HcCtrlHeadED(p_hc_cfg->BaseAddr), 0u);
}
} else if (ep_type == USBH_EP_TYPE_BULK) {
if (p_ohci->EDLists[list_idx]) {
Wr32(HcBulkHeadED(p_hc_cfg->BaseAddr), p_ohci->EDLists[OHCI_ED_LIST_BULK]->DMA_HC_ED);
} else {
Wr32(HcBulkHeadED(p_hc_cfg->BaseAddr), 0u);
}
} else { /* ed found in Periodic head? */
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
USBH_PRINT_LOG(" #######ERROR!!! ED found in Periodic head ???######\r\n");
#endif
}
} else if (p_hcd_ed_list && (p_hcd_ed_list->NextPtr == p_hcd_ed)) {
p_hcd_ed_list->NextPtr = p_hcd_ed->NextPtr; /* Unlink ed. The Previous ed should point to next ... */
p_hcd_ed_list->HC_EDPtr->Next = p_hcd_ed->HC_EDPtr->Next; /* of current ed */
CPU_DCACHE_RANGE_FLUSHINV(p_hcd_ed_list->HC_EDPtr, 16u);
p_hcd_ed->HC_EDPtr->HeadTD = 0u; /* Empy TD List */
p_hcd_ed->HC_EDPtr->TailTD = 0u;
CPU_DCACHE_RANGE_FLUSHINV(p_hcd_ed->HC_EDPtr, 16u);
}
OHCI_HCD_TD_Destroy(p_hc_drv, p_hcd_ed->Head_HCD_TDPtr); /* Free dummy TD */
OHCI_HCD_ED_Destroy(p_hc_drv, p_hcd_ed);
p_ep->ArgPtr = (void *)0;
Wr32(p_hc_cur_ed, 0u); /* Current ed regiser should not point to this ed */
/* Enable list processing by HC */
hc_ctrl = Rd32( HcCtrl(p_hc_cfg->BaseAddr));
hc_ctrl |= mask;
Wr32( HcCtrl(p_hc_cfg->BaseAddr), hc_ctrl);
*p_err = USBH_ERR_NONE;
}
/*
*********************************************************************************************************
* OHCI_EP_Abort()
*
* Description : Abort all pending URBs in the queue head.
*
* Argument(s) : p_dev Pointer to device structure.
*
* p_ep Pointer to endpoint structure.
*
* Return(s) : USBH_ERR_NONE If success
* USBH_ERR_INVALID_ARGS If p_dev or p_ep is 0
*
* Note(s) : None.
*********************************************************************************************************
*/
static void OHCI_EP_Abort (USBH_HC_DRV *p_hc_drv,
USBH_EP *p_ep,
USBH_ERR *p_err)
{
OHCI_HCD_ED *p_hcd_ed;
USBH_URB *p_urb;
OHCI_HCD_TD *p_temp_td;
p_hcd_ed = (OHCI_HCD_ED *)p_ep->ArgPtr;
p_urb = (USBH_URB *)0;
p_temp_td = p_hcd_ed->Head_HCD_TDPtr; /* Flush all TDs and make TD list empty in this ED. */
while ((p_temp_td != (OHCI_HCD_TD *)0) &&
(p_temp_td->URBPtr != (USBH_URB *)0)) {
p_urb = p_temp_td->URBPtr;
OHCI_EPPause(p_hc_drv, p_ep); /* Pause EP, HC skips processing ED pointed to by EP. */
p_urb->Err = USBH_ERR_URB_ABORT;
USBH_URB_Complete(p_urb);
p_temp_td = p_hcd_ed->Head_HCD_TDPtr; /* Get the updated headTd */
}
p_hcd_ed->HC_EDPtr->HeadTD = p_hcd_ed->HC_EDPtr->TailTD; /* Empty TD List */
CPU_DCACHE_RANGE_FLUSHINV(p_hcd_ed->HC_EDPtr, 16u);
OHCI_EPResume(p_hc_drv, p_ep); /* Resume EP, HC can now process this ED */
*p_err = USBH_ERR_NONE;
}
/*
*********************************************************************************************************
* OHCI_IsHalt_EP()
*
* Description : Retrieve endpoint halt state.
*
* Argument(s) : p_dev Pointer to device structure.
*
* p_ep Pointer to endpoint structure.
*
* Return(s) : DEF_TRUE If halted.
*
* DEF_FALSE Otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
static CPU_BOOLEAN OHCI_IsHalt_EP (USBH_HC_DRV *p_hc_drv,
USBH_EP *p_ep,
USBH_ERR *p_err)
{
OHCI_HCD_ED *p_hcd_ed;
OHCI_HC_ED *p_hc_ed;
(void)p_hc_drv;
p_hcd_ed = (OHCI_HCD_ED *)p_ep->ArgPtr;
p_hc_ed = p_hcd_ed->HC_EDPtr;
p_hcd_ed->IsHalt = (((p_hc_ed->HeadTD & OHCI_ED_HALT) != 0u) ? DEF_TRUE : DEF_FALSE);
CPU_DCACHE_RANGE_INV(p_hc_ed, 16u);
*p_err = USBH_ERR_NONE;
return (p_hcd_ed->IsHalt);
}
/*
*********************************************************************************************************
* OHCI_URB_Submit()
*
* Description : Create the TD for the URB, insert the TDs into the appropriate endpoint descriptor,
* and enable the host controller to begin processing the lists
*
* Argument(s) : p_dev Pointer to device structure
*
* p_ep Pointer to endpoint structure
*
* p_urb Pointer to URB structure
*
* Return(s) : USBH_ERR_NONE If successful
* USBH_ERR_INVALID_ARGS If p_dev, p_ep, or p_urb is 0
* USBH_ERR_DMA_BUF_ALLOC If insufficient memory for DMA buffer
* USBH_ERR_HW_DESC_ALLOC If insufficient memory for hardware descriptor
*
* Note(s) : (1) Both the General TD and the Isochronous TD provide a means of specifying a buffer
* that is from 0 to 8,192 bytes long. Additionally, the data buffer described in a
* single TD can span up to two physically disjoint pages.
*********************************************************************************************************
*/
static void OHCI_URB_Submit (USBH_HC_DRV *p_hc_drv,
USBH_URB *p_urb,
USBH_ERR *p_err)
{
OHCI_DEV *p_ohci;
OHCI_HCD_TD *p_hcd_td;
CPU_INT32U buf;
CPU_INT32U buf_len;
CPU_INT32U max_td_len;
CPU_INT08U dly_int;
CPU_INT32U td_toggle;
CPU_INT32U frame;
CPU_INT32U temp;
CPU_INT32U token;
CPU_INT08U ep_type;
CPU_INT16U ep_max_pkt_size;
CPU_INT32U buf_start;
CPU_INT32U offsets[4];
CPU_INT32U td_ix;
CPU_INT32U frm_ix;
CPU_INT32U off_ix;
CPU_INT16U nbr_tds;
CPU_INT16U nbr_hcd_tds_avail;
CPU_INT16U nbr_hc_tds_avail;
CPU_INT32U nbr_bytes;
CPU_INT32U td_ctrl;
LIB_ERR err_lib;
USBH_HC_CFG *p_hc_cfg;
USBH_EP *p_ep;
CPU_SR_ALLOC();
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
p_ep = p_urb->EP_Ptr;
max_td_len = 0u;
dly_int = 0u;
td_toggle = 0u;
frame = 0u;
ep_type = USBH_EP_TypeGet(p_ep);
ep_max_pkt_size = USBH_EP_MaxPktSizeGet(p_ep);
if ((p_hc_cfg->DedicatedMemAddr != (CPU_ADDR)0) &&
(p_hc_cfg->DataBufFromSysMemEn == DEF_DISABLED)) {
if (p_urb->UserBufLen != 0u) {
p_urb->DMA_BufPtr = Mem_PoolBlkGet((MEM_POOL *)&p_ohci->BufPool,
(CPU_SIZE_T ) p_hc_cfg->DataBufMaxLen,
(LIB_ERR *)&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((void *)p_urb->DMA_BufPtr,
(void *)p_urb->UserBufPtr,
(CPU_SIZE_T )p_urb->DMA_BufLen);
}
}
} else {
p_urb->DMA_BufPtr = p_urb->UserBufPtr;
p_urb->DMA_BufLen = p_urb->UserBufLen;
}
buf_len = p_urb->DMA_BufLen;
buf = (p_urb->DMA_BufLen) ? (CPU_INT32U) VIR2BUS(p_urb->DMA_BufPtr) : 0u;
if (p_urb->DMA_BufLen) {
if ((p_urb->Token == USBH_TOKEN_OUT ) ||
(p_urb->Token == USBH_TOKEN_SETUP)) {
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);
}
}
nbr_tds = ((buf + buf_len) - (buf & 0xFFFFF000u)) / 0x2000u; /* Calculate number of TDs needed for this xfer */
if ((((buf + buf_len) - (buf & 0xFFFFF000u)) % 0x2000u) != 0u) {
nbr_tds++;
}
CPU_CRITICAL_ENTER();
if (ep_type == USBH_EP_TYPE_ISOC) {
if(p_urb->IsocDescPtr->StartFrm == 0u) {
frame = Rd32(HcFrameNbr(p_hc_cfg->BaseAddr)) + 1u; /* Start this xfer immediately after current frame nbr */
} else {
frame = p_urb->IsocDescPtr->StartFrm; /* Start this xfer at the caller specified frame number */
}
frm_ix = 0u;
/* Init each iTD describing the Isochronous xfer */
for (td_ix = 0u; (td_ix < nbr_tds) && (frm_ix < p_urb->IsocDescPtr->NbrFrm); td_ix++) {
buf_start = buf & 0xFFFFF000u; /* 4K aligned start address of the buffer */
/* Number of bytes in this TD */
nbr_bytes = DEF_MIN(((buf_start + 0x2000u) - buf), buf_len);
offsets[0] = 0u;
offsets[1] = 0u;
offsets[2] = 0u;
offsets[3] = 0u;
/* Init each Isochronous data packet */
for (off_ix = 0u; (off_ix < 8u) && (frm_ix < p_urb->IsocDescPtr->NbrFrm) && (nbr_bytes >= p_urb->IsocDescPtr->FrmLen[frm_ix]); off_ix++) {
if ((off_ix % 2u) == 0u) {
/* Even offset. Write to lower word */
offsets[off_ix/2u] |= (buf & OHCI_ITD_OFFSET_4K_PAGE_BOUNDARY) | OHCI_ITD_PSWN_CC_NOT_ACCESSED;
/* if buffer address crosses the 4K-page boundary... */
if ((buf - buf_start) > OHCI_ITD_OFFSET_4K_PAGE_BOUNDARY) {
/* .... Set bit 12 to use Upper 20 bits of Buffer End */
offsets[off_ix/2u] |= OHCI_ITD_OFFSET_BIT12_LOWER_WORD;
}
} else {
/* Odd offset Write to higher word */
offsets[off_ix/2u] |= ((buf & OHCI_ITD_OFFSET_4K_PAGE_BOUNDARY) | OHCI_ITD_PSWN_CC_NOT_ACCESSED) << 16;
/* if buffer address crosses the 4K-page boundary... */
if ((buf - buf_start) > OHCI_ITD_OFFSET_4K_PAGE_BOUNDARY) {
/* Set Upperword bit 12 to use Upper 20 bits of Buffer End */
offsets[off_ix/2u] |= OHCI_ITD_OFFSET_BIT12_UPPER_WORD;
}
}
buf += p_urb->IsocDescPtr->FrmLen[frm_ix];/* Increment the buffer to point to next frame */
nbr_bytes -= p_urb->IsocDescPtr->FrmLen[frm_ix];/* update remaining bytes */
buf_len -= p_urb->IsocDescPtr->FrmLen[frm_ix++];
}
if ((td_ix + 1u) == nbr_tds) {
dly_int = 0u; /* Enable interrupt on Last TD */
} else {
dly_int = 7u; /* No interrupt for TDs in the middle */
}
frame &= 0xFFFFu; /* Lower 16 bits of Frame number */
td_ctrl = 0u;
td_ctrl = OHCI_TD_CC;
td_ctrl |= OHCI_TD_FC(off_ix - 1u);
td_ctrl |= OHCI_TD_DELAY_INT(dly_int);
td_ctrl |= OHCI_TD_SF(frame);
p_hcd_td = OHCI_HC_TD_Insert(p_hc_drv,
p_ep,
p_urb,
td_ctrl,
buf_start,
buf - 1u,
&offsets[0]);
if (p_hcd_td == (OHCI_HCD_TD *)0) {
CPU_CRITICAL_EXIT();
*p_err = USBH_ERR_ALLOC;
return;
}
frame += off_ix;
}
} else {
/* TD for the Status Phase */
if ((ep_type == USBH_EP_TYPE_CTRL) &&
(buf_len == 0u)) {
/* If xfer_dir IN status TD is OUT */
if (p_urb->Token == USBH_TOKEN_OUT) {
token = OHCI_TD_DIR_OUT;
} else {
token = OHCI_TD_DIR_IN;
}
/* If xfer_dir OUT status TD is IN */
p_hcd_td = OHCI_HC_TD_Insert(p_hc_drv,
p_ep,
p_urb,
(token | OHCI_TD_CC | OHCI_TD_DELAY_INT(0) | OHCI_TD_TOGGLE_1),
0u,
0u,
0);
if (p_hcd_td == (OHCI_HCD_TD *)0) {
CPU_CRITICAL_EXIT();
*p_err = USBH_ERR_ALLOC;
return;
}
}
nbr_hcd_tds_avail = (CPU_INT16U)Mem_PoolBlkGetNbrAvail(&p_ohci->HCD_TDPool,
&err_lib);
nbr_hc_tds_avail = (CPU_INT16U)Mem_PoolBlkGetNbrAvail(&p_ohci->HC_TDPool,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
CPU_CRITICAL_EXIT();
*p_err = USBH_ERR_ALLOC;
return;
}
if ((nbr_tds > nbr_hcd_tds_avail) ||
(nbr_tds > nbr_hc_tds_avail)) { /* Make sure there are enough TDs avail. */
CPU_CRITICAL_EXIT();
*p_err = USBH_ERR_ALLOC;
return;
}
while (buf_len) {
/* Max TD Len is 8192 if the buffer is 4K aligned */
max_td_len = 0x2000u - (buf & 0x00000FFFu);
if (max_td_len < buf_len) {
max_td_len -= max_td_len % ep_max_pkt_size;
} else {
max_td_len = buf_len;
}
if (buf_len - max_td_len) {
dly_int = 7u; /* No interrupt for TDs in the middle */
} else {
dly_int = 0u; /* Interrupt on last TD. No delay */
}
if (ep_type == USBH_EP_TYPE_CTRL) {
if (p_urb->Token == USBH_TOKEN_SETUP) {
td_toggle = OHCI_TD_TOGGLE_0;
} else {
td_toggle = OHCI_TD_TOGGLE_1;
}
} else {
td_toggle = 0u;
}
if (p_urb->Token == USBH_TOKEN_SETUP) {
token = OHCI_TD_DIR_SETUP;
} else if (p_urb->Token == USBH_TOKEN_OUT) {
token = OHCI_TD_DIR_OUT;
} else {
token = OHCI_TD_DIR_IN;
}
p_hcd_td = OHCI_HC_TD_Insert(p_hc_drv, /* Insert TD into List */
p_ep,
p_urb,
(OHCI_TD_ROUNDING | token | OHCI_TD_CC | OHCI_TD_DELAY_INT(dly_int) | td_toggle),
buf,
buf + max_td_len - 1u,
0);
if (p_hcd_td == (OHCI_HCD_TD *)0) {
CPU_CRITICAL_EXIT();
*p_err = USBH_ERR_ALLOC;
return;
}
buf_len -= max_td_len; /* update current buffer */
buf += max_td_len;
}
}
CPU_CRITICAL_EXIT();
switch (ep_type) {
case USBH_EP_TYPE_CTRL:
Wr32(HcCtrlHeadED(p_hc_cfg->BaseAddr), /* Write control list addr to HcControlHeadEd */
p_ohci->EDLists[OHCI_ED_LIST_CTRL]->DMA_HC_ED);
Wr32(HcCmdStatus(p_hc_cfg->BaseAddr), /* Enable Control List Filled */
Rd32(HcCmdStatus(p_hc_cfg->BaseAddr)) | OHCI_OR_CMD_STATUS_CLF);
Wr32(HcCtrl(p_hc_cfg->BaseAddr), /* Enable Control List Processing. */
Rd32( HcCtrl(p_hc_cfg->BaseAddr))| OHCI_OR_CTRL_CLE);
break;
case USBH_EP_TYPE_BULK:
Wr32(HcBulkHeadED(p_hc_cfg->BaseAddr), /* Write control list addr to HcControlHeadEd */
p_ohci->EDLists[OHCI_ED_LIST_BULK]->DMA_HC_ED);
Wr32(HcCmdStatus(p_hc_cfg->BaseAddr), /* Enable Bulk List Filled */
Rd32(HcCmdStatus(p_hc_cfg->BaseAddr))| OHCI_OR_CMD_STATUS_BLF);
Wr32(HcCtrl(p_hc_cfg->BaseAddr), /* Enable Bulk List Processing */
Rd32( HcCtrl(p_hc_cfg->BaseAddr)) | OHCI_OR_CTRL_BLE);
break;
case USBH_EP_TYPE_INTR:
Wr32(HcCtrl(p_hc_cfg->BaseAddr), /* Enable INTERRUPT list processing */
Rd32( HcCtrl(p_hc_cfg->BaseAddr)) | OHCI_OR_CTRL_PLE);
break;
case USBH_EP_TYPE_ISOC: /* Enable ISOCH list processing */
temp = Rd32(HcCtrl(p_hc_cfg->BaseAddr));
if((temp & OHCI_OR_CTRL_PLE) != 0u) { /* If Periodic List Schedule already enabled */
/* Enable only processing of isochronous EDs */
Wr32(HcCtrl(p_hc_cfg->BaseAddr), temp | OHCI_OR_CTRL_IE);
} else {
/* Enable Periodic List Schedule AND processing of ... */
/* ...isochronous EDs */
while ((temp & (OHCI_OR_CTRL_IE | OHCI_OR_CTRL_PLE)) == 0u) {
Wr32(HcCtrl(p_hc_cfg->BaseAddr),
temp | OHCI_OR_CTRL_IE | OHCI_OR_CTRL_PLE);
temp = Rd32( HcCtrl(p_hc_cfg->BaseAddr));
}
}
break;
default:
break;
}
*p_err = USBH_ERR_NONE;
}
/*
*********************************************************************************************************
* OHCI_URB_Complete()
*
* Description : Determine the number bytes transfered in this URB, remove the TD containing this URB,
* set the transfer condition to successful or failed.
*
* Argument(s) : p_dev Pointer to device structure.
*
* p_ep Pointer to endpoint structure.
*
* p_urb Pointer to URB structure.
*
* Return(s) : USBH_ERR_NONE If successful
* USBH_ERR_INVALID_ARGS If p_dev, p_ep, or p_urb is 0
*
* Note(s) : None.
*********************************************************************************************************
*/
static void OHCI_URB_Complete (USBH_HC_DRV *p_hc_drv,
USBH_URB *p_urb,
USBH_ERR *p_err)
{
OHCI_HCD_TD *p_temp_td;
OHCI_HCD_TD *p_del_td;
OHCI_HC_TD *p_hc_td;
CPU_INT32U buf_len;
CPU_INT32U actual_len;
CPU_BOOLEAN update_ed_head;
CPU_INT32U head;
CPU_INT32S cond_code;
CPU_INT32S completion_code;
OHCI_DEV *p_ohci;
OHCI_HCD_ED *p_hcd_ed;
USBH_ISOC_DESC *p_isoc_desc;
CPU_BOOLEAN is_halt;
CPU_BOOLEAN clr_halt;
CPU_INT32U frm_ix;
CPU_INT08U off_ix;
CPU_INT32U buf_end;
CPU_INT08U ep_type;
CPU_INT08U ep_dir;
CPU_INT32U i;
LIB_ERR err_lib;
USBH_EP *p_ep;
USBH_HC_CFG *p_hc_cfg;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
p_ep = p_urb->EP_Ptr;
update_ed_head = DEF_FALSE;
completion_code = 0;
p_hcd_ed = (OHCI_HCD_ED *)p_ep->ArgPtr;
p_isoc_desc = p_urb->IsocDescPtr;
is_halt = OHCI_IsHalt_EP(p_hc_drv, p_ep, p_err);
clr_halt = DEF_FALSE;
ep_type = USBH_EP_TypeGet(p_ep);
ep_dir = USBH_EP_DirGet(p_ep);
frm_ix = 0u;
head = p_hcd_ed->HC_EDPtr->HeadTD & (~0xFu); /* Phy address of current ed Head */
if (p_hcd_ed->Head_HCD_TDPtr == p_hcd_ed->Tail_HCD_TDPtr) { /* TD list is Empty */
*p_err = USBH_ERR_NONE;
return;
}
p_temp_td = p_hcd_ed->Head_HCD_TDPtr; /* Current Head Td in TD list */
while ((p_temp_td != 0u) &&
(p_temp_td->URBPtr != p_urb)) { /* Goto the TD containing the URB to be cancelled */
if (p_temp_td->DMA_HC_TD == head) { /* URB contains TDs beyond current ed head. */
update_ed_head = DEF_TRUE; /* Update ed Head */
}
p_temp_td = p_temp_td->NextPtr;
}
buf_len = p_urb->DMA_BufLen;
actual_len = 0u;
completion_code = OHCI_CODE_NOTACCESSED;
while (p_temp_td->URBPtr == p_urb) {
if (p_temp_td->DMA_HC_TD == head) { /* URB contains TDs beyond current ed head. */
update_ed_head = DEF_TRUE; /* Update ed Head */
}
p_hc_td = p_temp_td->HC_TdPtr;
cond_code = OHCI_TDMASK_CC(p_hc_td->Ctrl); /* Condition code tells transfer success/fail */
/* Determine the bytes transfered in DATA stage */
if (ep_type == USBH_EP_TYPE_ISOC) {
for (off_ix = 0u; (off_ix < 8u) && (frm_ix < p_isoc_desc->NbrFrm); off_ix++) {
if (p_hc_td->Offsets[off_ix / 2u] == 0u) {
break;
}
if ((off_ix % 2) == 0) {
completion_code = (p_hc_td->Offsets[off_ix / 2] >> 12) & 0xFu;
p_isoc_desc->FrmLen[frm_ix++] = p_hc_td->Offsets[off_ix / 2] & 0xFFFu; /* Actual Length transfered */
} else {
completion_code = (p_hc_td->Offsets[off_ix / 2] >> 28) & 0xFu;
p_isoc_desc->FrmLen[frm_ix++] = (p_hc_td->Offsets[off_ix / 2] >> 16) & 0x0FFFu; /* Actual Length transfered */
}
switch (completion_code) {
case OHCI_CODE_NOERROR:
p_isoc_desc->FrmErr[frm_ix] = USBH_ERR_NONE;
break;
case OHCI_CODE_CRC:
case OHCI_CODE_BITSTUFFING:
case OHCI_CODE_BUFFEROVERRUN:
case OHCI_CODE_BUFFERUNDERRUN:
case OHCI_CODE_DATATOGGLEMISMATCH:
case OHCI_CODE_PIDCHECKFAILURE:
case OHCI_CODE_UNEXPECTEDPID:
case OHCI_CODE_DATAOVERRUN:
case OHCI_CODE_DATAUNDERRUN:
p_isoc_desc->FrmErr[frm_ix] = USBH_ERR_HC_IO;
break;
case OHCI_CODE_STALL:
p_isoc_desc->FrmErr[frm_ix] = USBH_ERR_EP_STALL;
break;
case OHCI_CODE_DEVICENOTRESPONDING:
p_isoc_desc->FrmErr[frm_ix] = USBH_ERR_DEV_NOT_RESPONDING;
break;
default:
p_isoc_desc->FrmErr[frm_ix] = USBH_ERR_UNKNOWN;
break;
}
}
actual_len = buf_len;
} else if ((p_hc_td->BufEnd != 0u ) &&
(cond_code != OHCI_CODE_NOTACCESSED)) {
/* currBufPtr != buf_end If last TD buffer is */
/* ... not transfered completely */
buf_end = p_hc_td->BufEnd;
if ((p_hc_td->CurBuf != 0u) &&
(p_hc_td->CurBuf != buf_end)) {
actual_len = buf_len - (1u + (CPU_INT32U)buf_end - (CPU_INT32U)p_hc_td->CurBuf);
} else {
actual_len = buf_len;
}
}
p_del_td = p_temp_td; /* Free memory used by completed TDs */
if (p_temp_td == p_hcd_ed->Head_HCD_TDPtr) { /* Head TD? update head to point to next */
p_hcd_ed->Head_HCD_TDPtr = p_hcd_ed->Head_HCD_TDPtr->NextPtr;
}
p_temp_td = p_temp_td->NextPtr; /* Move to next TD */
/* Free TD in the following cases : */
/* (1) TD is Completed, ISR called */
/* (2) (TD NOTACCESSED or DEVICENOTRESPONDING) */
/* ... and ed is in HALT state */
if ((p_del_td->State != OHCI_HCD_TD_STATE_COMPLETED) &&
(is_halt == DEF_FALSE)) {
OHCI_EPPause(p_hc_drv, p_ep);
is_halt = DEF_TRUE;
clr_halt = DEF_TRUE;
}
OHCI_HCD_TD_Destroy(p_hc_drv, p_del_td);
if (cond_code != OHCI_CODE_NOTACCESSED) {
completion_code = cond_code;
}
}
if (update_ed_head && p_temp_td) {
p_hcd_ed->HC_EDPtr->HeadTD = p_temp_td->DMA_HC_TD;
}
if ((ep_type == USBH_EP_TYPE_ISOC) &&
(ep_dir == USBH_EP_DIR_IN )) {
actual_len = 0u;
for (i = 0u; i < p_isoc_desc->NbrFrm; i++) {
actual_len += p_isoc_desc->FrmLen[i];
}
}
p_urb->XferLen = actual_len;
if (completion_code == OHCI_CODE_DATAUNDERRUN) { /* Short packet is OK */
completion_code = OHCI_CODE_NOERROR;
clr_halt = DEF_TRUE;
}
if (clr_halt == DEF_TRUE) {
OHCI_HaltEPClr(p_ep);
}
if (p_urb->Err == 0) {
switch (completion_code) {
case OHCI_CODE_NOERROR:
p_urb->Err = USBH_ERR_NONE;
break;
case OHCI_CODE_CRC:
case OHCI_CODE_BITSTUFFING:
case OHCI_CODE_BUFFEROVERRUN:
case OHCI_CODE_BUFFERUNDERRUN:
case OHCI_CODE_DATATOGGLEMISMATCH:
case OHCI_CODE_PIDCHECKFAILURE:
case OHCI_CODE_UNEXPECTEDPID:
case OHCI_CODE_DATAOVERRUN:
case OHCI_CODE_DATAUNDERRUN:
p_urb->Err = USBH_ERR_HC_IO;
break;
case OHCI_CODE_STALL:
p_urb->Err = USBH_ERR_EP_STALL;
break;
case OHCI_CODE_DEVICENOTRESPONDING:
p_urb->Err = USBH_ERR_DEV_NOT_RESPONDING;
break;
default:
p_urb->Err = USBH_ERR_UNKNOWN;
break;
}
}
if ((p_hc_cfg->DedicatedMemAddr != (CPU_ADDR)0) &&
(p_hc_cfg->DataBufFromSysMemEn == DEF_DISABLED)) {
if (p_urb->DMA_BufPtr != (void *)0) {
if ((p_urb->Token == USBH_TOKEN_IN) &&
(p_urb->XferLen != 0u)) {
Mem_Copy((void *)p_urb->UserBufPtr,
(void *)p_urb->DMA_BufPtr,
(CPU_SIZE_T )p_urb->XferLen);
}
Mem_PoolBlkFree((MEM_POOL *)&p_ohci->BufPool,
(void *) p_urb->DMA_BufPtr,
(LIB_ERR *)&err_lib);
}
}
*p_err = USBH_ERR_NONE;
}
/*
*********************************************************************************************************
* OHCI_URB_Abort()
*
* Description : Abort pending transfer.
*
* Argument(s) : p_dev Pointer to device structure.
*
* p_ep Pointer to endpoint structure.
*
* p_urb Pointer to URB structure.
*
* Return(s) : USBH_ERR_NONE If successful
* USBH_ERR_INVALID_ARGS If p_dev, p_ep, or p_urb is 0
*
* Note(s) : None.
*********************************************************************************************************
*/
static void OHCI_URB_Abort (USBH_HC_DRV *p_hc_drv,
USBH_URB *p_urb,
USBH_ERR *p_err)
{
USBH_EP *p_ep;
p_ep = p_urb->EP_Ptr;
if (p_ep->ArgPtr == DEF_NULL) { /* EP was closed. */
*p_err = USBH_ERR_NONE;
return;
}
OHCI_EPPause(p_hc_drv, p_ep); /* Pause ED, host cntrlr skips processing this ED. */
OHCI_URB_Complete(p_hc_drv, p_urb, p_err);
OHCI_EPResume(p_hc_drv, p_ep); /* Resume processing this ED. */
*p_err = USBH_ERR_NONE;
}
/*
*********************************************************************************************************
* OHCI_ISR()
*
* Description : OHCI interrupt service routine.
*
* Argument(s) : p_arg Pointer to OHCI_DEV structure.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void OHCI_ISR (void *p_arg)
{
OHCI_DEV *p_ohci;
USBH_HC_DRV *p_hc_drv;
USBH_HC_CFG *p_hc_cfg;
CPU_INT32U int_status;
CPU_INT32U int_en;
USBH_ERR err;
CPU_INT32U hcca_frame_nbr;
p_hc_drv = (USBH_HC_DRV *)p_arg;
p_ohci = p_hc_drv->DataPtr;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
int_status = Rd32(HcIntStatus(p_hc_cfg->BaseAddr)); /* Read Interrupt Status */
int_en = Rd32(HcIntEn(p_hc_cfg->BaseAddr)); /* Read Interrupt enable status */
int_status &= int_en;
if (int_status == 0u) {
return;
}
if ((int_status & OHCI_OR_INT_STATUS_SO) != 0u) {
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
USBH_PRINT_LOG("# Schedule OverRun #\r\n"); /* TODO - Schedule overrun. Adjust BW */
#endif
}
if ((int_status & OHCI_OR_INT_STATUS_UE) != 0u) {
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
USBH_PRINT_LOG("# Unrecoverable Error #\r\n");
#endif
}
if ((int_status & OHCI_OR_INT_STATUS_RHSC) != 0u) { /* Root hub status change interrupt */
USBH_HUB_RH_Event(p_hc_drv->RH_DevPtr);
}
if ((int_status & OHCI_OR_INT_STATUS_WDH) != 0u) { /* Handle Done TD */
err = OHCI_DoneHeadProcess(p_hc_drv);
if (err != USBH_ERR_NONE) {
Wr32( HcIntDis(p_hc_cfg->BaseAddr), OHCI_OR_INT_STATUS_WDH);
}
}
if ((int_status & OHCI_OR_INT_EN_FNO) != 0u) { /* Handle Frame Number Overflow */
CPU_DCACHE_RANGE_INV((void *)&p_ohci->DMA_OHCI.HCCAPtr->FrameNbr, 8u);
hcca_frame_nbr = p_ohci->DMA_OHCI.HCCAPtr->FrameNbr;
if(DEF_BIT_IS_CLR(hcca_frame_nbr, DEF_BIT_15) == DEF_YES) {
p_ohci->FrameNbr++;
}
}
/* TODO - Disable all lists. */
Wr32(HcIntStatus(p_hc_cfg->BaseAddr), int_status); /* Acknowledge interrupt */
}
/*
*********************************************************************************************************
* OHCI_HCD_ED_Clr()
*
* Description : Initialize OHCI Driver Endpoint Descriptor.
*
* Argument(s) : p_hcd_ed Pointer to host controller driver endpoint descriptor.
*
* Return(s) : None
*
* Note(s) : None.
*********************************************************************************************************
*/
static void OHCI_HCD_ED_Clr (OHCI_HCD_ED *p_hcd_ed)
{
p_hcd_ed->HC_EDPtr = (OHCI_HC_ED *)0;
p_hcd_ed->DMA_HC_ED = (CPU_INT32U )0;
p_hcd_ed->Head_HCD_TDPtr = (OHCI_HCD_TD *)0;
p_hcd_ed->Tail_HCD_TDPtr = (OHCI_HCD_TD *)0;
p_hcd_ed->NextPtr = (OHCI_HCD_ED *)0;
p_hcd_ed->ListInterval = (CPU_INT32U )0;
p_hcd_ed->BW = (CPU_INT32U )0;
p_hcd_ed->IsHalt = (CPU_BOOLEAN )DEF_FALSE;
}
/*
*********************************************************************************************************
* OHCI_HC_ED_Clr()
*
* Description : Initialize host controller endpoint descriptor.
*
* Argument(s) : p_hc_ed Pointer to host controller endpoint descriptor.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void OHCI_HC_ED_Clr (OHCI_HC_ED *p_hc_ed)
{
p_hc_ed->Ctrl = 0u;
p_hc_ed->TailTD = 0u;
p_hc_ed->HeadTD = 0u;
p_hc_ed->Next = 0u;
}
/*
*********************************************************************************************************
* OHCI_HCD_ED_Create()
*
* Description : Create OHCI Driver Endpoint Descriptor.
*
* Argument(s) : p_ohci OHCI Device structure pointer.
*
* Return(s) : Pointer to OHCI_HCD_ED structure If success
*
* 0 Otherwise
*
* Note(s) : None.
*********************************************************************************************************
*/
static OHCI_HCD_ED *OHCI_HCD_ED_Create (USBH_HC_DRV *p_hc_drv)
{
OHCI_HCD_ED *p_new_hcd_ed;
OHCI_HC_ED *p_new_hc_ed;
OHCI_DEV *p_ohci;
LIB_ERR err_lib;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
p_new_hcd_ed = (OHCI_HCD_ED *)Mem_PoolBlkGet((MEM_POOL *)&p_ohci->HCD_EDPool,
(CPU_SIZE_T ) sizeof(OHCI_HCD_ED),
(LIB_ERR *)&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
return ((OHCI_HCD_ED *)0);
}
OHCI_HCD_ED_Clr(p_new_hcd_ed);
p_new_hc_ed = (OHCI_HC_ED *)Mem_PoolBlkGet((MEM_POOL *)&p_ohci->HC_EDPool,
(CPU_SIZE_T ) sizeof(OHCI_HC_ED),
(LIB_ERR *)&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
Mem_PoolBlkFree((MEM_POOL *)&p_ohci->HCD_EDPool,
(void *) p_new_hcd_ed,
(LIB_ERR *)&err_lib);
return ((OHCI_HCD_ED *)0);
}
OHCI_HC_ED_Clr(p_new_hc_ed);
p_new_hcd_ed->NextPtr = 0;
p_new_hc_ed->Next = 0u;
p_new_hcd_ed->HC_EDPtr = p_new_hc_ed;
p_new_hcd_ed->DMA_HC_ED = (CPU_INT32U)VIR2BUS(p_new_hc_ed);
p_new_hc_ed->Ctrl = 0u;
return (p_new_hcd_ed);
}
/*
*********************************************************************************************************
* OHCI_HCD_ED_Destroy()
*
* Description : Destroy OHCI Driver Endpoint Descriptor.
*
* Argument(s) : p_ohci OHCI Device structure pointer
*
* : p_hcd_ed Pointer to OHCI_HCD_ED structure
*
* Return(s) : None
*
* Note(s) : None.
*********************************************************************************************************
*/
static void OHCI_HCD_ED_Destroy (USBH_HC_DRV *p_hc_drv,
OHCI_HCD_ED *p_hcd_ed)
{
OHCI_DEV *p_ohci;
LIB_ERR err_lib;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
Mem_PoolBlkFree((MEM_POOL *)&p_ohci->HC_EDPool,
(void *) p_hcd_ed->HC_EDPtr,
(LIB_ERR *)&err_lib);
Mem_PoolBlkFree((MEM_POOL *)&p_ohci->HCD_EDPool,
(void *) p_hcd_ed,
(LIB_ERR *)&err_lib);
}
/*
*********************************************************************************************************
* OHCI_HCD_TD_Clr()
*
* Description : Initialize OHCI driver transfer descriptor
*
* Argument(s) : p_hcd_td Pointer to OHCI_HCD_TD structure
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void OHCI_HCD_TD_Clr (OHCI_HCD_TD *p_hcd_td)
{
p_hcd_td->HC_TdPtr = (OHCI_HC_TD *)0;
p_hcd_td->DMA_HC_TD = (CPU_INT32U )0;
p_hcd_td->NextPtr = (OHCI_HCD_TD *)0;
p_hcd_td->URBPtr = (USBH_URB *)0;
}
/*
*********************************************************************************************************
* OHCI_HC_TD_Clr()
*
* Description : Initialize OHCI transfer descriptor.
*
* Argument(s) : p_hc_td Pointer to the OHCI_HC_TD structure
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void OHCI_HC_TD_Clr (OHCI_HC_TD *p_hc_td)
{
CPU_INT08U ix;
p_hc_td->Ctrl = 0u;
p_hc_td->CurBuf = 0u;
p_hc_td->Next = 0u;
p_hc_td->BufEnd = 0u;
for (ix = 0u; ix < 4u; ix++) {
p_hc_td->Offsets[ix] = 0u;
}
}
/*
*********************************************************************************************************
* OHCI_HCD_TD_Create()
*
* Description : Create Host controller driver Transfer Descriptor
*
* Argument(s) : p_ohci OHCI Device structure pointer
*
* Return(s) : Pointer to OHCI_HCD_TD structure If success
*
* 0 Otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
static OHCI_HCD_TD *OHCI_HCD_TD_Create (USBH_HC_DRV *p_hc_drv)
{
OHCI_HCD_TD *p_new_hcd_td;
OHCI_HC_TD *p_new_hc_td;
OHCI_DEV *p_ohci;
LIB_ERR err_lib;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
/* Allocate HC DRIVER TD */
p_new_hcd_td = (OHCI_HCD_TD *)Mem_PoolBlkGet((MEM_POOL *)&p_ohci->HCD_TDPool,
(CPU_SIZE_T ) sizeof(OHCI_HCD_TD),
(LIB_ERR *)&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
return ((OHCI_HCD_TD *)0);
}
OHCI_HCD_TD_Clr(p_new_hcd_td);
/* Allocate HC DMA TD */
p_new_hc_td = (OHCI_HC_TD *)Mem_PoolBlkGet((MEM_POOL *)&p_ohci->HC_TDPool,
(CPU_SIZE_T ) sizeof(OHCI_HC_TD),
(LIB_ERR *)&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
Mem_PoolBlkFree( &p_ohci->HCD_TDPool,
(void *)p_new_hcd_td,
&err_lib);
return ((OHCI_HCD_TD *)0);
}
OHCI_HC_TD_Clr(p_new_hc_td);
p_new_hcd_td->DMA_HC_TD = (CPU_INT32U)VIR2BUS(p_new_hc_td); /* HC TD phy address */
p_new_hc_td->Ctrl = 0u; /* Initialize new HC TD */
p_new_hc_td->CurBuf = 0u;
p_new_hc_td->BufEnd = 0u;
p_new_hc_td->Next = 0u;
p_new_hcd_td->HC_TdPtr = p_new_hc_td;
p_new_hcd_td->NextPtr = 0;
OHCI_SetDataPtr(p_ohci,
p_new_hcd_td,
p_new_hc_td);
return (p_new_hcd_td);
}
/*
*********************************************************************************************************
* OHCI_HCD_TD_Destroy()
*
* Description : Destroy a Host controller driver Transfer Descriptor.
*
* Argument(s) : p_ohci OHCI Device structure pointer.
*
* p_hcd_td OHCI Driver Transfer Descriptor.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void OHCI_HCD_TD_Destroy (USBH_HC_DRV *p_hc_drv,
OHCI_HCD_TD *p_hcd_td)
{
OHCI_HC_TD *p_hc_td;
LIB_ERR err_lib;
OHCI_DEV *p_ohci;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
p_hc_td = p_hcd_td->HC_TdPtr; /* First lookup general TD pool */
Mem_PoolBlkFree( &p_ohci->HC_TDPool,
(void *)p_hc_td,
&err_lib);
Mem_PoolBlkFree( &p_ohci->HCD_TDPool, /* Remove OHCI Driver TD */
(void *)p_hcd_td,
&err_lib);
}
/*
*********************************************************************************************************
* OHCI_PeriodicListInit()
*
* Description : Initialize the periodic list corresponding to 32ms, 16ms, 8ms, 4ms, 2ms and 1ms intervals.
*
* Argument(s) : p_ohci OHCI Device Structure Pointer.
*
* Return(s) : USBH_ERR_NONE If successful.
* USBH_ERR_HW_DESC_ALLOC If ED is not created due to insufficient memory
*
* Note(s) : None.
*********************************************************************************************************
*/
static USBH_ERR OHCI_PeriodicListInit (USBH_HC_DRV *p_hc_drv)
{
CPU_INT32U list_ix;
CPU_INT32U ix_prev;
OHCI_HCD_ED *p_ed;
OHCI_DEV *p_ohci;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
ix_prev = 0u;
for (list_ix = OHCI_ED_LIST_32MS; list_ix <= OHCI_ED_LIST_01MS; list_ix++) {
p_ed = OHCI_HCD_ED_Create(p_hc_drv); /* Allocate driver ED. */
if (p_ed == (OHCI_HCD_ED *)0) {
return (USBH_ERR_ALLOC);
}
p_ed->HC_EDPtr->Ctrl = OHCI_ED_SKIP;
p_ohci->EDLists[list_ix] = p_ed;
/* For each 16MS list. */
if ((list_ix >= OHCI_ED_LIST_16MS) && (list_ix < OHCI_ED_LIST_08MS)) {
ix_prev = OHCI_ED_LIST_32MS + ((list_ix - OHCI_ED_LIST_16MS) * 2u);
p_ohci->EDLists[list_ix]->ListInterval = 16u;
/* 32MS lists point to 16MS lists. */
p_ohci->EDLists[OHCI_BranchArray[ix_prev]]->NextPtr = p_ohci->EDLists[list_ix];
p_ohci->EDLists[OHCI_BranchArray[ix_prev]]->HC_EDPtr->Next = p_ohci->EDLists[list_ix]->DMA_HC_ED;
p_ohci->EDLists[OHCI_BranchArray[ix_prev + 1u]]->NextPtr = p_ohci->EDLists[list_ix];
p_ohci->EDLists[OHCI_BranchArray[ix_prev + 1u]]->HC_EDPtr->Next = p_ohci->EDLists[list_ix]->DMA_HC_ED;
} else if ((list_ix >= OHCI_ED_LIST_08MS) && (list_ix <= OHCI_ED_LIST_01MS)) {
if ((list_ix >= OHCI_ED_LIST_08MS) &&
(list_ix < (OHCI_ED_LIST_04MS))) {
/* 16MS list points to 8MS lists. */
ix_prev = OHCI_ED_LIST_16MS + ((list_ix - OHCI_ED_LIST_08MS) * 2u);
p_ohci->EDLists[list_ix]->ListInterval = 8u;
} else if ((list_ix >= OHCI_ED_LIST_04MS) &&
(list_ix < OHCI_ED_LIST_02MS)) {
/* 8MS list points to 4MS lists. */
ix_prev = OHCI_ED_LIST_08MS + ((list_ix - OHCI_ED_LIST_04MS) * 2u);
p_ohci->EDLists[list_ix]->ListInterval = 4u;
} else if ((list_ix >= OHCI_ED_LIST_02MS) &&
(list_ix < OHCI_ED_LIST_01MS)) {
/* 4MS list points to 2MS lists. */
ix_prev = OHCI_ED_LIST_04MS + ((list_ix - OHCI_ED_LIST_02MS) * 2u);
p_ohci->EDLists[list_ix]->ListInterval = 2u;
} else if (list_ix == OHCI_ED_LIST_01MS) {
/* 2MS list points to 1MS lists. */
ix_prev = OHCI_ED_LIST_02MS + ((list_ix - OHCI_ED_LIST_01MS) * 2u);
p_ohci->EDLists[list_ix]->ListInterval = 1u;
}
p_ohci->EDLists[ix_prev]->NextPtr = p_ohci->EDLists[list_ix];
p_ohci->EDLists[ix_prev]->HC_EDPtr->Next = p_ohci->EDLists[list_ix]->DMA_HC_ED;
p_ohci->EDLists[ix_prev+1u]->NextPtr = p_ohci->EDLists[list_ix];
p_ohci->EDLists[ix_prev+1u]->HC_EDPtr->Next = p_ohci->EDLists[list_ix]->DMA_HC_ED;
}
}
/* Initialize HCCA interrupt table. */
for (list_ix = OHCI_ED_LIST_32MS; list_ix < OHCI_ED_LIST_16MS; list_ix++) {
p_ohci->DMA_OHCI.HCCAPtr->IntTbl[list_ix] = p_ohci->EDLists[OHCI_BranchArray[list_ix]]->DMA_HC_ED;
p_ohci->EDLists[OHCI_BranchArray[list_ix]]->ListInterval = 32u;
}
return (USBH_ERR_NONE);
}
/*
*********************************************************************************************************
* OHCI_PeriodicEPOpen()
*
* Description : Insert the periodic endpoint descriptor into periodic list according to its interval
*
* Argument(s) : p_ohci OHCI Device structure pointer
*
* p_ed OHCI Endpoint Descriptor pointer
*
* interval Periodic interval
*
* Return(s) : USBH_ERR_NONE If successful
* USBH_ERR_NO_BW If failed to allocate bandwidth
*
* Note(s) : None
*********************************************************************************************************
*/
static USBH_ERR OHCI_PeriodicEPOpen (USBH_HC_DRV *p_hc_drv,
OHCI_HCD_ED *p_ed,
CPU_INT32U interval)
{
USBH_ERR err;
CPU_INT32U sel_bw;
CPU_INT32S sel_ix;
CPU_INT32S sel_start_ix;
CPU_INT32U branch_max_bw;
CPU_INT32U branch_min_bw;
CPU_INT32U branch_start_ix;
CPU_INT32U ix;
CPU_INT32S sel_branch_ix;
CPU_INT32U n_branches;
OHCI_HCD_ED *p_lprev;
OHCI_HCD_ED *p_lnext;
OHCI_DEV *p_ohci;
err = USBH_ERR_NONE;
sel_bw = OHCI_MAX_PERIODIC_BW;
sel_ix = -1;
sel_start_ix = -1;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
/* Interval must be <= 32 and power of 2 */
interval = (interval >= 32u) ? 32u :
(interval >= 16u) ? 16u :
(interval >= 8u) ? 8u :
(interval >= 4u) ? 4u :
(interval >= 2u) ? 2u : 1u;
p_ed->ListInterval = interval;
n_branches = 32u / interval;
/* Find the BW used by the Sub branches */
for (branch_start_ix = 0u; branch_start_ix < 32u; branch_start_ix += n_branches) {
branch_min_bw = p_ohci->Branch[branch_start_ix];
branch_max_bw = p_ohci->Branch[branch_start_ix];
sel_branch_ix = branch_start_ix;
for (ix = 0u; ix < n_branches; ix++) { /* Find the maximum BW used by this group */
if (branch_max_bw < p_ohci->Branch[branch_start_ix + ix]) {
branch_max_bw = p_ohci->Branch[branch_start_ix + ix];
}
/* Minimum BW in the group */
if (branch_min_bw > p_ohci->Branch[branch_start_ix + ix]) {
branch_min_bw = p_ohci->Branch[branch_start_ix + ix];
sel_branch_ix = branch_start_ix + ix;
}
}
if (sel_bw > branch_max_bw) { /* Max BW in use in this group < the previous groups */
sel_bw = branch_max_bw;
sel_ix = sel_branch_ix;
sel_start_ix = branch_start_ix;
}
}
/* Is selected branch has enough BW for the ed */
if ((sel_ix >= 0 ) &&
(p_ed->BW < OHCI_MAX_PERIODIC_BW - sel_bw)) {
p_lprev = p_lnext = p_ohci->EDLists[OHCI_ED_LIST_32MS + OHCI_BranchArray[sel_ix]];
/* Goto the last ed of this interval */
while (p_lnext && (p_lnext->ListInterval >= interval)) {
p_lprev = p_lnext;
p_lnext = p_lnext->NextPtr;
}
if (p_lnext) { /* Now insert ed next to the last ed in the interval */
p_ed->HC_EDPtr->Next = p_lnext->DMA_HC_ED;
p_ed->NextPtr = p_lnext;
} else {
p_ed->HC_EDPtr->Next = (CPU_INT32U )0;
p_ed->NextPtr = (OHCI_HCD_ED *)0;
}
CPU_DCACHE_RANGE_FLUSHINV(p_ed->HC_EDPtr, 16u);
p_lprev->HC_EDPtr->Next = p_ed->DMA_HC_ED; /* Make previous ed point to this ed */
p_lprev->NextPtr = p_ed;
CPU_DCACHE_RANGE_FLUSHINV(p_lprev->HC_EDPtr, 16u);
/* Update the BW of branches in this sub group */
for (ix = sel_start_ix; ix < sel_start_ix + n_branches; ix++) {
p_ohci->Branch[ix] += p_ed->BW;
}
err = USBH_ERR_NONE;
} else { /* BW is not available, return ERROR */
err = USBH_ERR_BW_NOT_AVAIL;
}
return (err);
}
/*
*********************************************************************************************************
* OHCI_HC_TD_Insert()
*
* Description : Insert a transfer descriptor into corresponding OHCI Endpoint descriptor
*
* Argument(s) : p_ohci Pointer to OHCI structure.
*
* p_ep Pointer to endpoint structure.
*
* p_urb Pointer to URB structure.
*
* td_ctrl Transfer descriptor's Control field.
*
* buf_ptr Current buffer pointer.
*
* buf_end End address of that buffer.
*
* isoch_psw0 Isoch psw0.
*
* Return(s) : Pointer to tailTD in the TD list If success
* 0 If H/W descriptor allocation failed
*
* Note(s) : (1) For further details, see section "5.2.8.1 The NULL or Empty Queue" of OHCI spec release 1.0a
*
* (2) For further details, see section "5.2.8.4 Cancel" of OHCI spec release 1.0a
*
* (3) For further details, see section "5.2.8.2 Adding to a Queue" of OHCI spec release 1.0a
*********************************************************************************************************
*/
static OHCI_HCD_TD *OHCI_HC_TD_Insert (USBH_HC_DRV *p_hc_drv,
USBH_EP *p_ep,
USBH_URB *p_urb,
CPU_INT32U td_ctrl,
CPU_INT32U buf_ptr,
CPU_INT32U buf_end,
CPU_INT32U *p_offsets)
{
OHCI_HCD_ED *p_hcd_ed;
volatile OHCI_HC_ED *p_hc_ed;
OHCI_HCD_TD *p_new_hcd_td;
volatile OHCI_HC_TD *p_new_hc_td;
OHCI_HCD_TD *p_tail_hcd_td;
CPU_INT08U ep_type;
CPU_BOOLEAN EP_paused;
CPU_BOOLEAN is_halt;
CPU_INT32U cur_periodic_ED = 0;
CPU_INT32U cur_async_ED = 0;
CPU_INT32U hc_ed_addr;
USBH_HC_CFG *p_hc_cfg;
USBH_ERR err;
EP_paused = DEF_FALSE;
ep_type = USBH_EP_TypeGet(p_ep);
p_hcd_ed = (OHCI_HCD_ED *)p_ep->ArgPtr;
p_hc_ed = p_hcd_ed->HC_EDPtr;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
if (p_hcd_ed == (OHCI_HCD_ED *)0) {
return ((OHCI_HCD_TD *)0);
}
p_new_hcd_td = OHCI_HCD_TD_Create(p_hc_drv); /* Create a TD struct for HCD */
if(p_new_hcd_td == (OHCI_HCD_TD *)0) {
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
USBH_PRINT_LOG("%s fails. OHCI_CreateHCTD return NULL\r\n",__FUNCTION__);
#endif
return ((OHCI_HCD_TD *)0);
}
p_new_hc_td = p_new_hcd_td->HC_TdPtr; /* Get the created TD struct for Host Controller */
/* (1)-------- NULL or Empty Queue. Note #1 ----------- */
if (p_hcd_ed->Head_HCD_TDPtr == (OHCI_HCD_TD *)0) { /* TD queue is empty, create the 1st place holder */
/* HCD Head and Tail ptrs point to same TD struct */
p_hcd_ed->Head_HCD_TDPtr = p_new_hcd_td;
p_hcd_ed->Tail_HCD_TDPtr = p_new_hcd_td;
p_hcd_ed->Head_HCD_TDPtr->NextPtr = p_new_hcd_td;
p_new_hc_td->Next = p_new_hcd_td->DMA_HC_TD;
CPU_DCACHE_RANGE_FLUSHINV((void *)p_new_hc_td, sizeof(*p_new_hc_td));
/* HC Head and Tail ptrs point to same TD struct */
p_hc_ed->HeadTD = p_new_hcd_td->DMA_HC_TD;
p_hc_ed->TailTD = p_new_hcd_td->DMA_HC_TD;
CPU_DCACHE_RANGE_FLUSHINV((void *)p_hc_ed, 16u);
return ((OHCI_HCD_TD *)0); /* The first TD is a dummy TD */
}
/* (2)----------- Cancel operation. Note #2 ----------- */
is_halt = OHCI_IsHalt_EP(p_hc_drv, p_ep, &err);
if ((ep_type == USBH_EP_TYPE_ISOC) ||
(ep_type == USBH_EP_TYPE_INTR)) {
cur_periodic_ED = Rd32(HcPeriodCurED(p_hc_cfg->BaseAddr));
cur_periodic_ED = (CPU_INT32U) BUS2VIR((void *)cur_periodic_ED);
} else if (ep_type == USBH_EP_TYPE_CTRL) {
cur_async_ED = Rd32(HcCtrlCurED(p_hc_cfg->BaseAddr));
cur_async_ED = (CPU_INT32U) BUS2VIR((void *)cur_async_ED);
} else { /* Then ep_type must be USBH_EP_TYPE_BULK. */
cur_async_ED = Rd32(HcBulkCurED(p_hc_cfg->BaseAddr));
cur_async_ED = (CPU_INT32U) BUS2VIR((void *)cur_async_ED);
}
hc_ed_addr = (CPU_INT32U) p_hc_ed;
hc_ed_addr &= OHCI_OR_BULK_CUR_ED_BCED;
/* Is Endpoint Halted due to an error in processing TD? */
if ((is_halt == DEF_FALSE ) &&
((cur_periodic_ED == hc_ed_addr) || (cur_async_ED == hc_ed_addr))) {
OHCI_EPPause(p_hc_drv, p_ep); /* NO. So pause the endpoint processing by the HC */
EP_paused = DEF_TRUE;
}
/* (3)----------- Adding operation. Note #3 ----------- */
/* Current tail pointer */
p_tail_hcd_td = p_hcd_ed->Tail_HCD_TDPtr;
p_hcd_ed->Tail_HCD_TDPtr->NextPtr = p_new_hcd_td; /* Insert new TD place holder at the tail */
p_hcd_ed->Tail_HCD_TDPtr = p_new_hcd_td;
p_tail_hcd_td->HC_TdPtr->Ctrl = td_ctrl; /* Prepare TD with the parameters passed in */
p_tail_hcd_td->HC_TdPtr->CurBuf = buf_ptr;
p_tail_hcd_td->HC_TdPtr->BufEnd = buf_end;
p_tail_hcd_td->URBPtr = p_urb; /* Store the URB associated with this TD */
if (ep_type == USBH_EP_TYPE_ISOC) {
p_tail_hcd_td->HC_TdPtr->Offsets[0] = p_offsets[0];
p_tail_hcd_td->HC_TdPtr->Offsets[1] = p_offsets[1];
p_tail_hcd_td->HC_TdPtr->Offsets[2] = p_offsets[2];
p_tail_hcd_td->HC_TdPtr->Offsets[3] = p_offsets[3];
}
/* Insert new HC TD at the tail */
CPU_DCACHE_RANGE_FLUSHINV((void *)p_new_hc_td, sizeof(*p_new_hc_td));
p_tail_hcd_td->HC_TdPtr->Next = p_new_hcd_td->DMA_HC_TD;
CPU_DCACHE_RANGE_FLUSHINV(p_tail_hcd_td->HC_TdPtr, sizeof(*p_tail_hcd_td->HC_TdPtr));
p_hc_ed->TailTD = p_new_hcd_td->DMA_HC_TD;
CPU_DCACHE_RANGE_FLUSHINV((void *)p_hc_ed, 16u);
/* (4)----------- Cancel operation. Note #2 ----------- */
if (EP_paused == DEF_TRUE) { /* If EP paused */
OHCI_EPResume(p_hc_drv, p_ep); /* Resume the endpoint processing by the HC */
EP_paused = DEF_FALSE;
}
return (p_tail_hcd_td);
}
/*
*********************************************************************************************************
* OHCI_EPPause()
*
* Description : Halt process of endpoint descriptors
*
* Argument(s) : p_ohci Pointer to OHCI Device structure
*
* p_ep Pointer to endpoint structure
*
* Return(s) : USBH_ERR_NONE If successful
* : USBH_ERR_UNKNOWN If Frame number doesn't increment
*
* Note(s) : None
*********************************************************************************************************
*/
static USBH_ERR OHCI_EPPause (USBH_HC_DRV *p_hc_drv,
USBH_EP *p_ep)
{
CPU_INT32U cur_frame_nbr;
CPU_INT32U cnt;
CPU_BOOLEAN b_ret;
USBH_ERR err;
USBH_HC_CFG *p_hc_cfg;
cnt = 0u;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
b_ret = OHCI_IsHalt_EP(p_hc_drv, p_ep, &err);
if (b_ret == DEF_TRUE) {
return (USBH_ERR_NONE);
}
OHCI_EPHalt(p_ep); /* HALT this ed. */
/* OHCI stops processing this ed from next frame */
cur_frame_nbr = Rd32(HcFrameNbr(p_hc_cfg->BaseAddr)); /* Get the current frame number */
/* Wait until current frame is completed */
while (Rd32(HcFrameNbr(p_hc_cfg->BaseAddr)) == cur_frame_nbr) {
++cnt;
if (cnt > 3u) {
break;
}
USBH_OS_DlyMS(1u);
}
if (cnt > 3u) { /* Frame number not advanced !!!. */
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
USBH_PRINT_LOG("Failed to abort ep.\r\n");
#endif
}
return (USBH_ERR_NONE);
}
/*
*********************************************************************************************************
* OHCI_EPResume()
*
* Description : Resume endpoint operation
*
* Argument(s) : p_ohci Pointer to OHCI Device structure
*
* p_ep Pointer to endpoint structure
*
* Return(s) : None
*
* Note(s) : None
*********************************************************************************************************
*/
static void OHCI_EPResume (USBH_HC_DRV *p_hc_drv,
USBH_EP *p_ep)
{
(void)p_hc_drv;
OHCI_HaltEPClr(p_ep); /* Clear Halt ep */
}
/*
*********************************************************************************************************
* OHCI_EPHalt()
*
* Description : Halt endpoint operation
*
* Argument(s) : phost Pointer to host structure
*
* p_ep Pointer to endpoint structure
*
* Return(s) : None
*
* Note(s) : None
*********************************************************************************************************
*/
static void OHCI_EPHalt (USBH_EP *p_ep)
{
OHCI_HCD_ED *p_hcd_ed;
OHCI_HC_ED *p_hc_ed;
p_hcd_ed = (OHCI_HCD_ED *)p_ep->ArgPtr;
p_hc_ed = p_hcd_ed->HC_EDPtr;
p_hc_ed->Ctrl |= OHCI_ED_SKIP; /* HC skips this EP & continues to the next ED */
p_hcd_ed->IsHalt = (((p_hc_ed->HeadTD & OHCI_ED_HALT) != 0u) ? DEF_TRUE : DEF_FALSE);
CPU_DCACHE_RANGE_FLUSHINV(p_hc_ed, 16u);
}
/*
*********************************************************************************************************
* OHCI_HaltEPClr()
*
* Description : Clear endpoint halt condition
*
* Argument(s) : p_host Pointer to host structure
*
* p_ep Pointer to endpoint structure
*
* Return(s) : None
*
* Note(s) : None
*
*********************************************************************************************************
*/
static void OHCI_HaltEPClr (USBH_EP *p_ep)
{
OHCI_HCD_ED *p_hcd_ed;
OHCI_HC_ED *p_hc_ed;
p_hcd_ed = (OHCI_HCD_ED *)p_ep->ArgPtr;
p_hc_ed = p_hcd_ed->HC_EDPtr;
p_hc_ed->Ctrl &= (~OHCI_ED_SKIP);
p_hc_ed->HeadTD &= (~OHCI_ED_HALT);
p_hcd_ed->IsHalt = (((p_hc_ed->HeadTD & OHCI_ED_HALT) != 0u) ? DEF_TRUE : DEF_FALSE);
CPU_DCACHE_RANGE_FLUSHINV(p_hc_ed, 16u);
}
/*
*********************************************************************************************************
* OHCI_DoneHeadProcess()
*
* Description : Read the done head and traverse the done td list
*
* Argument(s) : p_ohci Pointer to OHCI Device structure
*
* Return(s) : USBH_ERR_NONE
*
* Note(s) : None
*********************************************************************************************************
*/
static USBH_ERR OHCI_DoneHeadProcess (USBH_HC_DRV *p_hc_drv)
{
OHCI_HC_TD *p_head_td; /* Head of done TD list pointed by HCCA doneHead */
OHCI_HCD_TD *p_head_hcd_td; /* Head of done TD list pointed by HCCA doneHead */
CPU_INT32U dma_head; /* Head of done TD list pointed by HCCA doneHead */
CPU_INT32U dma_rev_head; /* Reverse Head of TD List */
CPU_INT32U dma_head_next; /* Reverse Head of TD List */
USBH_URB *p_urb;
USBH_URB *p_prev_urb;
CPU_INT32S cond_code;
OHCI_DEV *p_ohci;
p_urb = (USBH_URB *)0;
p_prev_urb = (USBH_URB *)0;
dma_rev_head = 0u;
dma_head_next = 0u;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
CPU_DCACHE_RANGE_INV((void *)&p_ohci->DMA_OHCI.HCCAPtr->FrameNbr, 8u);
/* Done Head. First 24 bits address */
dma_head = p_ohci->DMA_OHCI.HCCAPtr->DoneHead & (~0x0Fu);
/* Traverse done TD queue in reverse order */
while (dma_head) {
p_head_td = (OHCI_HC_TD *)BUS2VIR((void *)dma_head);
dma_head_next = p_head_td->Next;
p_head_td->Next = dma_rev_head;
dma_rev_head = dma_head;
dma_head = dma_head_next;
}
/* Traverse the list in correct order */
dma_head = dma_rev_head;
while (dma_head) {
p_head_td = (OHCI_HC_TD *)BUS2VIR((void *)dma_head);
dma_head_next = p_head_td->Next;
dma_head = dma_head_next;
/* Determine TD pool the head_td belongs to */
/* First lookup general TD pool */
p_head_hcd_td = (OHCI_HCD_TD *)OHCI_GetDataPtr(p_ohci, p_head_td);
if (p_head_hcd_td == (OHCI_HCD_TD *)0) { /* Lookup isoch TD pool */
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
USBH_PRINT_LOG("# OHCI_GetDataPtr return NULL #\r\n");
#endif
continue;
}
cond_code = OHCI_TDMASK_CC(p_head_td->Ctrl); /* Get Condition code */
/* Is TD cancelled ? */
if (p_head_hcd_td->State == OHCI_HCD_TD_STATE_CANCELLED) {
OHCI_HCD_TD_Destroy(p_hc_drv, p_head_hcd_td);
} else {
p_head_hcd_td->State = OHCI_HCD_TD_STATE_COMPLETED;
p_urb = p_head_hcd_td->URBPtr;
if (cond_code) {
OHCI_EPHalt(p_urb->EP_Ptr);
}
if (p_urb != p_prev_urb) { /* URB may be present in more than one TD */
p_prev_urb = p_urb;
USBH_URB_Done(p_urb); /* Notify I/O task to complete URB */
}
}
}
return (USBH_ERR_NONE);
}
/*
*********************************************************************************************************
* OHCI_DMA_Init()
*
* Description : Allocate all structures used by the OHCI driver
*
* Argument(s) : p_ohci Pointer to OHCI Device structure
*
* Return(s) : USBH_ERR_NONE If success
* USBH_ERR_ALLOC If fails due to insufficient memory
*
* Note(s) : (1) The Host Controller Communications Area (HCCA) is a 256-byte structure of system memory
* that is used by HCD to send and receive specific control and status information to and
* from the Host Controller.
* This structure MUST be located on a 256-byte boundary.
*********************************************************************************************************
*/
static USBH_ERR OHCI_DMA_Init (USBH_HC_DRV *p_hc_drv)
{
CPU_SIZE_T octets_reqd;
LIB_ERR err_lib;
CPU_INT32U max_nbr_ed;
CPU_INT32U nbr_td_per_xfer;
CPU_INT32U max_nbr_td;
CPU_INT32U max_data_buf;
CPU_INT08U *p_dedicated_mem;
CPU_INT32U total_mem_req;
OHCI_DEV *p_ohci;
USBH_HC_CFG *p_hc_cfg;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
nbr_td_per_xfer = (p_hc_cfg->DataBufMaxLen / (4u * 1024u));
if ((p_hc_cfg->DataBufMaxLen % (4u * 1024u)) != 0u) {
nbr_td_per_xfer++;
}
max_nbr_ed = OHCI_ED_LIST_SIZE + p_hc_cfg->MaxNbrEP_BulkOpen + p_hc_cfg->MaxNbrEP_IntrOpen + p_hc_cfg->MaxNbrEP_IsocOpen;
/* Each ED contains 2 TD (1 dummy + 1 functional). */
max_nbr_td = ((nbr_td_per_xfer + 1u) * (p_hc_cfg->MaxNbrEP_BulkOpen + p_hc_cfg->MaxNbrEP_IntrOpen + p_hc_cfg->MaxNbrEP_IsocOpen)) + 4u;
max_data_buf = p_hc_cfg->MaxNbrEP_BulkOpen + p_hc_cfg->MaxNbrEP_IntrOpen + p_hc_cfg->MaxNbrEP_IsocOpen + 2u;
/* ---------------- DEDICATED MEMORY ------------------ */
if (p_hc_cfg->DedicatedMemAddr != (CPU_ADDR)0) { /* Allocate HCCA, EDs and TDs from a dedicated memory */
if (p_hc_cfg->DataBufFromSysMemEn == DEF_DISABLED) { /* Data buffers allocated from dedicated memory */
total_mem_req = (sizeof(OHCI_HCCA) + 256u) +
(max_nbr_td * sizeof(OHCI_HC_TD)) +
(max_nbr_ed * sizeof(OHCI_HC_ED)) +
(p_hc_cfg->DataBufMaxLen * max_data_buf);
} else { /* Data buffers allocated from main memory */
total_mem_req = (sizeof(OHCI_HCCA) + 256u) +
(max_nbr_td * sizeof(OHCI_HC_TD)) +
(max_nbr_ed * sizeof(OHCI_HC_ED));
}
if (total_mem_req > p_hc_cfg->DedicatedMemSize) {
return (USBH_ERR_ALLOC);
}
p_dedicated_mem = (CPU_INT08U *)p_hc_cfg->DedicatedMemAddr;
/* See Note #1. */
p_ohci->DMA_OHCI.HCCAPtr = (OHCI_HCCA *)USB_ALIGNED(p_dedicated_mem, 256u);
p_dedicated_mem += sizeof(OHCI_HCCA) + 256u;
p_ohci->DMA_OHCI.TDPtr = (OHCI_HC_TD *)p_dedicated_mem;
p_dedicated_mem += (max_nbr_td + 1u) * sizeof(OHCI_HC_TD);
p_ohci->DMA_OHCI.EDPtr = (OHCI_HC_ED *)p_dedicated_mem;
p_dedicated_mem += (max_nbr_ed + 1u) * sizeof(OHCI_HC_ED);
p_ohci->DMA_OHCI.BufPtr = (CPU_INT08U *)p_dedicated_mem;
Mem_PoolCreate ((MEM_POOL *)&p_ohci->HC_EDPool, /* Pool of EDs ... */
(void *) p_ohci->DMA_OHCI.EDPtr, /*... allocated from DEDICATED memory */
(CPU_SIZE_T ) (max_nbr_ed + 1u) * sizeof(OHCI_HC_ED),
(CPU_SIZE_T ) max_nbr_ed,
(CPU_SIZE_T ) sizeof(OHCI_HC_ED),
(CPU_SIZE_T ) CONFIG_ARCH_CACHE_LINE,
(CPU_SIZE_T *)&octets_reqd,
(LIB_ERR *)&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
return (USBH_ERR_ALLOC);
}
Mem_PoolCreate ((MEM_POOL *)&p_ohci->HC_TDPool, /* Pool of TDs ... */
(void *) p_ohci->DMA_OHCI.TDPtr, /*... allocated from DEDICATED memory */
(CPU_SIZE_T ) (max_nbr_td + 1u) * 32u,
(CPU_SIZE_T ) max_nbr_td,
(CPU_SIZE_T ) sizeof(OHCI_HC_TD),
(CPU_SIZE_T ) 32u,
(CPU_SIZE_T *)&octets_reqd,
(LIB_ERR *)&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
return (USBH_ERR_ALLOC);
}
/* ---------------- SYSTEM MEMORY --------------------- */
} else { /* Allocate HCCA, EDs and TDs from the system memory */
/* Get a single block from the heap for HCCA */
p_dedicated_mem = (CPU_INT08U *)Mem_HeapAlloc((CPU_SIZE_T )sizeof(OHCI_HCCA),
(CPU_SIZE_T )sizeof(OHCI_HCCA),
(CPU_SIZE_T *)&octets_reqd,
(LIB_ERR *)&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
return (USBH_ERR_ALLOC);
}
/* See Note #1. */
p_ohci->DMA_OHCI.HCCAPtr = (OHCI_HCCA *)p_dedicated_mem;
Mem_PoolCreate ((MEM_POOL *)&p_ohci->HC_EDPool, /* Pool of EDs ... */
(void *) 0, /*... allocated from the HEAP (SYSTEM memory) */
(CPU_SIZE_T ) (max_nbr_ed + 1u) * sizeof(OHCI_HC_ED),
(CPU_SIZE_T ) max_nbr_ed,
(CPU_SIZE_T ) sizeof(OHCI_HC_ED),
(CPU_SIZE_T ) CONFIG_ARCH_CACHE_LINE,
(CPU_SIZE_T *)&octets_reqd,
(LIB_ERR *)&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
return (USBH_ERR_ALLOC);
}
p_ohci->DMA_OHCI.EDPtr = (OHCI_HC_ED *)p_ohci->HC_EDPool.PoolAddrStart;
Mem_PoolCreate ((MEM_POOL *)&p_ohci->HC_TDPool, /* Pool of TDs ... */
(void *) 0, /*... allocated from the HEAP (SYSTEM memory) */
(CPU_SIZE_T ) (max_nbr_td + 1u) * 32u,
(CPU_SIZE_T ) max_nbr_td,
(CPU_SIZE_T ) sizeof(OHCI_HC_TD),
(CPU_SIZE_T ) 32u,
(CPU_SIZE_T *)&octets_reqd,
(LIB_ERR *)&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
return (USBH_ERR_ALLOC);
}
p_ohci->DMA_OHCI.TDPtr = (OHCI_HC_TD *)p_ohci->HC_TDPool.PoolAddrStart;
}
p_ohci->OHCI_Lookup = (OHCI_HCD_TD **)Mem_HeapAlloc((CPU_SIZE_T )(sizeof(void *) * (max_nbr_td + 1u)),
(CPU_SIZE_T ) sizeof(CPU_ALIGN),
(CPU_SIZE_T *)&octets_reqd,
(LIB_ERR *)&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
return (USBH_ERR_ALLOC);
}
/* Initialize the HCCA structure */
Mem_Set((void *)p_ohci->DMA_OHCI.HCCAPtr,
(CPU_INT08U )0,
(CPU_SIZE_T )sizeof(OHCI_HCCA));
Mem_PoolCreate ((MEM_POOL *)&p_ohci->HCD_EDPool, /* POOL for managing HCDED structures ... */
(void *) 0, /*... allocated from the HEAP (SYSTEM memory) */
(CPU_SIZE_T ) (max_nbr_ed + 1u) * sizeof(OHCI_HCD_ED),
(CPU_SIZE_T ) max_nbr_ed,
(CPU_SIZE_T ) sizeof(OHCI_HCD_ED),
(CPU_SIZE_T ) sizeof(CPU_ALIGN),
(CPU_SIZE_T *)&octets_reqd,
(LIB_ERR *)&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
return (USBH_ERR_ALLOC);
}
Mem_PoolCreate ((MEM_POOL *)&p_ohci->HCD_TDPool, /* POOL for managing HCDTD structures ... */
(void *) 0, /*... allocated from the HEAP (SYSTEM memory) */
(CPU_SIZE_T ) (max_nbr_td + 1u) * sizeof(OHCI_HCD_TD),
(CPU_SIZE_T ) max_nbr_td,
(CPU_SIZE_T ) sizeof(OHCI_HCD_TD),
(CPU_SIZE_T ) sizeof(CPU_ALIGN),
(CPU_SIZE_T *)&octets_reqd,
(LIB_ERR *)&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
return (USBH_ERR_ALLOC);
}
if ((p_hc_cfg->DedicatedMemAddr != (CPU_ADDR)0) &&
(p_hc_cfg->DataBufFromSysMemEn == DEF_DISABLED)) {
Mem_PoolCreate ((MEM_POOL *)&p_ohci->BufPool, /* POOL for managing HCDTD structures */
(void *) p_ohci->DMA_OHCI.BufPtr, /*... allocated from DEDICATED memory */
(CPU_SIZE_T ) p_hc_cfg->DataBufMaxLen * max_data_buf,
(CPU_SIZE_T ) max_data_buf,
(CPU_SIZE_T ) p_hc_cfg->DataBufMaxLen,
(CPU_SIZE_T ) sizeof(CPU_ALIGN),
(CPU_SIZE_T *)&octets_reqd,
(LIB_ERR *)&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
return (USBH_ERR_ALLOC);
}
}
return (USBH_ERR_NONE);
}
/*
*********************************************************************************************************
*********************************************************************************************************
* ROOT HUB FUNCTIONS
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* OHCI_PortStatusGet()
*
* Description : Retrieve port status changes and port status.
*
* Argument(s) : p_ohci Pointer to OHCI Device structure
*
* port_nbr Port Number
*
* port_status Pointer to the port Status structure
*
* Return(s) : USBH_ERR_NONE If successful.
* USBH_ERR_INVALID_PORT_NBR If invalid port number.
*
* Note(s) : None.
*********************************************************************************************************
*/
static CPU_BOOLEAN OHCI_PortStatusGet (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr,
USBH_HUB_PORT_STATUS *p_port_status)
{
OHCI_DEV *p_ohci;
CPU_INT32U status;
USBH_HC_CFG *p_hc_cfg;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
if ((port_nbr == 0) || /* Port number starts from 1 */
(port_nbr > p_ohci->NbrPorts)) {
return (DEF_FAIL);
}
status = Rd32(HcRhPortStatus(p_hc_cfg->BaseAddr, port_nbr - 1));
/* bit16 to bit 31 indicate port status change */
p_port_status->wPortChange = (status & 0xFFFF0000u) >> 16u;
p_port_status->wPortChange = MEM_VAL_GET_INT16U_LITTLE(&p_port_status->wPortChange);
/* bit0 to bit15 indicate port status */
p_port_status->wPortStatus = (status & 0xFFFFu);
p_port_status->wPortStatus = MEM_VAL_GET_INT16U_LITTLE(&p_port_status->wPortStatus);
return (DEF_OK);
}
/*
*********************************************************************************************************
* OHCI_HubDescGet()
*
* Description : Return root hub descriptor.
*
* Argument(s) : p_ohci Pointer to OHCI Device structure.
*
* p_buf Pointer to buffer to read descriptor.
*
* buf_len Length of buffer in bytes.
*
* Return(s) : DEF_OK.
*
* Note(s) : None.
*********************************************************************************************************
*/
static CPU_BOOLEAN OHCI_HubDescGet (USBH_HC_DRV *p_hc_drv,
void *p_buf,
CPU_INT08U buf_len)
{
OHCI_DEV *p_ohci;
CPU_INT32U hc_rh_desc_a;
CPU_INT08U port_always_powered;
USBH_HUB_DESC hub_desc;
USBH_HC_CFG *p_hc_cfg;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
hc_rh_desc_a = Rd32(HcRhDescA(p_hc_cfg->BaseAddr)); /* Determine nbr of ports present in HC */
/* Port Power switching. */
if ((Rd32(HcRhDescA(p_hc_cfg->BaseAddr)) & OHCI_OR_RH_DA_NPS) != 0u) {
port_always_powered = 1u;
} else {
port_always_powered = 0u;
}
Mem_Clr(&hub_desc, sizeof(USBH_HUB_DESC));
hub_desc.bDescLength = USBH_HUB_LEN_HUB_DESC;
hub_desc.bDescriptorType = USBH_HUB_DESC_TYPE_HUB;
hub_desc.bNbrPorts = hc_rh_desc_a & OHCI_OR_RH_DA_NDP;
hub_desc.wHubCharacteristics = (port_always_powered & 0x3u);
hub_desc.wHubCharacteristics = MEM_VAL_GET_INT16U_LITTLE(&hub_desc.wHubCharacteristics);
/* Power on to power good time */
hub_desc.bPwrOn2PwrGood = (Rd32(HcRhDescA(p_hc_cfg->BaseAddr)) & OHCI_OR_RH_DA_POTPGT) >> 24;
hub_desc.bHubContrCurrent = 0u;
/* Write the structure in USB format */
USBH_HUB_FmtHubDesc(&hub_desc, (void *)p_ohci->OHCI_HubBuf);
if (buf_len > sizeof(USBH_HUB_DESC)) {
buf_len = sizeof(USBH_HUB_DESC);
}
Mem_Copy((void *)p_buf,
(void *)p_ohci->OHCI_HubBuf,
(CPU_SIZE_T )buf_len);
return (DEF_OK);
}
/*
*********************************************************************************************************
* OHCI_PortEnSet()
*
* Description : Enable the given port.
*
* Argument(s) : p_ohci Pointer to OHCI Device structure
*
* port_nbr Port Number
*
* Return(s) : DEF_OK, If successful.
* DEF_FAIL, If invalid port number.
*
* Note(s) : None.
*********************************************************************************************************
*/
static CPU_BOOLEAN OHCI_PortEnSet (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr)
{
OHCI_DEV *p_ohci;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
if ((port_nbr == 0 ) || /* Port number starts from 1 */
(port_nbr > p_ohci->NbrPorts)) {
return (DEF_FAIL);
}
return (DEF_OK);
}
/*
*********************************************************************************************************
* OHCI_PortEnClr()
*
* Description : Clear port enable status.
*
* Argument(s) : p_ohci Pointer to OHCI Device structure
*
* port_nbr Port Number
*
* Return(s) : DEF_OK, If successful.
* DEF_FAIL, If invalid port number.
*
* Note(s) : None.
*********************************************************************************************************
*/
static CPU_BOOLEAN OHCI_PortEnClr (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr)
{
OHCI_DEV *p_ohci;
USBH_HC_CFG *p_hc_cfg;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
if ((port_nbr == 0) || /* Port number starts from 1 */
(port_nbr > p_ohci->NbrPorts)) {
return (DEF_FAIL);
}
Wr32(HcRhPortStatus(p_hc_cfg->BaseAddr, port_nbr - 1), /* Clear Port enable */
OHCI_OR_RH_PORT_CCS);
return (DEF_OK);
}
/*
*********************************************************************************************************
* OHCI_PortEnChngClr()
*
* Description : Clear port enable status change.
*
* Argument(s) : p_ohci Pointer to OHCI Device structure
*
* port_nbr Port Number
*
* Return(s) : DEF_OK, If successful.
* DEF_FAIL, If invalid port number.
*
* Note(s) : None.
*********************************************************************************************************
*/
static CPU_BOOLEAN OHCI_PortEnChngClr (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr)
{
OHCI_DEV *p_ohci;
USBH_HC_CFG *p_hc_cfg;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
if ((port_nbr == 0) || /* Port number starts from 1 */
(port_nbr > p_ohci->NbrPorts)) {
return (DEF_FAIL);
}
Wr32(HcRhPortStatus(p_hc_cfg->BaseAddr, port_nbr - 1), /* Clear Port reset status change */
OHCI_OR_RH_PORT_PESC);
return (DEF_OK);
}
/*
*********************************************************************************************************
* OHCI_PortPwrSet()
*
* Description : Set port power based on port power mode.
*
* Argument(s) : p_ohci Pointer to OHCI Device structure
*
* port_nbr Port Number
*
* Return(s) : DEF_OK, If successful.
* DEF_FAIL, If invalid port number.
*
* Note(s) : None.
*********************************************************************************************************
*/
static CPU_BOOLEAN OHCI_PortPwrSet (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr)
{
OHCI_DEV *p_ohci;
CPU_INT08U pwr_mode;
USBH_HC_CFG *p_hc_cfg;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
if ((port_nbr == 0) || /* Port number starts from 1 */
(port_nbr > p_ohci->NbrPorts)) {
return (DEF_FAIL);
}
pwr_mode = OHCI_PortPwrModeGet(p_hc_drv, port_nbr); /* Determine port power mode. */
if (pwr_mode == OHCI_PORT_PWRD_GLOBAL) { /* Set global power. */
Wr32(HcRhStatus(p_hc_cfg->BaseAddr), OHCI_OR_RH_STATUS_LPSC);
} else if (pwr_mode == OHCI_PORT_PWRD_INDIVIDUAL) { /* Set port power. */
Wr32(HcRhPortStatus(p_hc_cfg->BaseAddr, port_nbr - 1),
OHCI_OR_RH_PORT_PPS);
}
return (DEF_OK);
}
/*
*********************************************************************************************************
* OHCI_PortPwrClr()
*
* Description : Clear port power.
*
* Argument(s) : p_ohci Pointer to OHCI Device structure
*
* port_nbr Port Number
*
* Return(s) : DEF_OK, If successful.
* DEF_FAIL, If invalid port number.
*
* Note(s) : None.
*********************************************************************************************************
*/
static CPU_BOOLEAN OHCI_PortPwrClr (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr)
{
OHCI_DEV *p_ohci;
CPU_INT08U pwr_mode;
USBH_HC_CFG *p_hc_cfg;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
if ((port_nbr == 0) || /* Port number starts from 1 */
(port_nbr > p_ohci->NbrPorts)) {
return (DEF_FAIL);
}
pwr_mode = OHCI_PortPwrModeGet(p_hc_drv, port_nbr); /* Determine port power mode. */
if (pwr_mode == OHCI_PORT_PWRD_GLOBAL) { /* Clear global power. */
Wr32(HcRhStatus(p_hc_cfg->BaseAddr),
OHCI_OR_RH_STATUS_LPS);
} else if (pwr_mode == OHCI_PORT_PWRD_INDIVIDUAL) { /* Clear port power. */
Wr32(HcRhPortStatus(p_hc_cfg->BaseAddr, port_nbr - 1),
OHCI_OR_RH_PORT_LSDA);
}
return (DEF_OK);
}
/*
*********************************************************************************************************
* OHCI_PortResetSet()
*
* Description : Reset the given port.
*
* Argument(s) : p_ohci Pointer to OHCI Device structure
*
* port_nbr Port Number
*
* Return(s) : DEF_OK, If successful.
* DEF_FAIL, If invalid port number.
*
* Note(s) : None.
*********************************************************************************************************
*/
static CPU_BOOLEAN OHCI_PortResetSet (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr)
{
OHCI_DEV *p_ohci;
USBH_HC_CFG *p_hc_cfg;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
if ((port_nbr == 0) || /* Port number starts from 1 */
(port_nbr > p_ohci->NbrPorts)) {
return (DEF_FAIL);
}
Wr32(HcRhPortStatus(p_hc_cfg->BaseAddr, port_nbr - 1), /* Start port reset operation. */
OHCI_OR_RH_PORT_PRS);
return (DEF_OK);
}
/*
*********************************************************************************************************
* OHCI_PortResetChngClr()
*
* Description : Clear port reset status change.
*
* Argument(s) : p_ohci Pointer to OHCI Device structure
*
* port_nbr Port Number
*
* Return(s) : DEF_OK, If successful.
* DEF_FAIL, If invalid port number.
*
* Note(s) : None.
*********************************************************************************************************
*/
static CPU_BOOLEAN OHCI_PortResetChngClr (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr)
{
OHCI_DEV *p_ohci;
USBH_HC_CFG *p_hc_cfg;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
if ((port_nbr == 0) || /* Port number starts from 1 */
(port_nbr > p_ohci->NbrPorts)) {
return (DEF_FAIL);
}
Wr32(HcRhPortStatus(p_hc_cfg->BaseAddr, port_nbr - 1), /* Clear Port reset status change */
OHCI_OR_RH_PORT_PRSC);
return (DEF_OK);
}
/*
*********************************************************************************************************
* OHCI_PortSuspendClr()
*
* Description : Resume the given port if the port is suspended.
*
* Argument(s) : p_ohci Pointer to OHCI Device structure
*
* port_nbr Port Number
*
* Return(s) : DEF_OK, If successful.
* DEF_FAIL, If invalid port number.
*
* Note(s) : None.
*********************************************************************************************************
*/
static CPU_BOOLEAN OHCI_PortSuspendClr (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr)
{
OHCI_DEV *p_ohci;
USBH_HC_CFG *p_hc_cfg;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
if ((port_nbr == 0) || /* Port number starts from 1 */
(port_nbr > p_ohci->NbrPorts)) {
return (DEF_FAIL);
}
Wr32(HcRhPortStatus(p_hc_cfg->BaseAddr, port_nbr - 1), /* Start port reset operation. */
OHCI_OR_RH_PORT_POCI);
return (DEF_OK);
}
/*
*********************************************************************************************************
* OHCI_PortConnChngClr()
*
* Description : Clear port connect status change.
*
* Argument(s) : p_ohci Pointer to OHCI Device structure
*
* port_nbr Port Number
*
* Return(s) : DEF_OK, If successful.
* DEF_FAIL, If invalid port number.
*
* Note(s) : None.
*********************************************************************************************************
*/
static CPU_BOOLEAN OHCI_PortConnChngClr (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr)
{
OHCI_DEV *p_ohci;
USBH_HC_CFG *p_hc_cfg;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
p_ohci = (OHCI_DEV *)p_hc_drv->DataPtr;
if ((port_nbr == 0) || /* Port number starts from 1 */
(port_nbr > p_ohci->NbrPorts)) {
return (DEF_FAIL);
}
Wr32(HcRhPortStatus(p_hc_cfg->BaseAddr, port_nbr - 1), /* Clear Port connection status change */
OHCI_OR_RH_PORT_CSC);
return (DEF_OK);
}
/*
*********************************************************************************************************
* OHCI_RHSC_IntEn()
*
* Description : Enable root hub interrupt for the given OHCI controller
*
* Argument(s) : p_hc_dev Pointer to OHCI device structure
*
* Return(s) : DEF_OK.
*
* Note(s) : None
*********************************************************************************************************
*/
static CPU_BOOLEAN OHCI_RHSC_IntEn (USBH_HC_DRV *p_hc_drv)
{
USBH_HC_CFG *p_hc_cfg;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
Wr32(HcIntEn(p_hc_cfg->BaseAddr), OHCI_OR_INT_EN_RHSC);
return (DEF_OK);
}
/*
*********************************************************************************************************
* OHCI_RHSC_IntDis()
*
* Description : Disable root hub interrupt for the given OHCI controller
*
* Argument(s) : p_hc_dev Pointer to OHCI device structure
*
* Return(s) : DEF_OK.
*
* Note(s) : None
*********************************************************************************************************
*/
static CPU_BOOLEAN OHCI_RHSC_IntDis (USBH_HC_DRV *p_hc_drv)
{
USBH_HC_CFG *p_hc_cfg;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
Wr32(HcIntDis(p_hc_cfg->BaseAddr), OHCI_OR_INT_DISABLE_RHSC);
return (DEF_OK);
}
/*
*********************************************************************************************************
* OHCI_PortPwrModeGet()
*
* Description : Retrieve whether the given port is individually powered, globally powered or always powered
*
* Argument(s) : p_ohci Pointer to OHCI Device structure
*
* port_nbr Port Number
*
* Return(s) : PORT_ALWAYS_POWERED Port is always powered. No switiching
* PORT_INDIVIDUAL_POWERED Port is individually powered
* PORT_GLOBAL_POWERED Port is global powered
*
* Note(s) : None
*********************************************************************************************************
*/
static CPU_INT08S OHCI_PortPwrModeGet (USBH_HC_DRV *p_hc_drv,
CPU_INT08U port_nbr)
{
CPU_INT16U ppc_mask;
USBH_HC_CFG *p_hc_cfg;
p_hc_cfg = p_hc_drv->HC_CfgPtr;
if ((Rd32(HcRhDescA(p_hc_cfg->BaseAddr)) & OHCI_OR_RH_DA_NPS) != 0u) {
return (OHCI_PORT_PWRD_ALWAYS); /* Ports are always powered. */
}
if ((Rd32(HcRhDescA(p_hc_cfg->BaseAddr)) & OHCI_OR_RH_DA_PSM) != 0u) {
/* Power switching. */
ppc_mask = (Rd32(HcRhDescB(p_hc_cfg->BaseAddr)) & OHCI_OR_RH_DB_PPCM) >> 16;
if ((ppc_mask & (1 << (port_nbr - 0))) != 0u) {
return (OHCI_PORT_PWRD_INDIVIDUAL); /* Port is controlled individualy. */
}
}
return (OHCI_PORT_PWRD_GLOBAL); /* Port is global powered. */
}
/*
*********************************************************************************************************
* OHCI_SetDataPtr()
*
* Description : !!!! description to add
*
* Argument(s) : p_ohci Pointer to OHCI Device structure.
*
* p_buf Pointer to buffer to read descriptor.
*
* buf_len Length of buffer in bytes.
*
* Return(s) : USBH_ERR_NONE.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void OHCI_SetDataPtr (OHCI_DEV *p_ohci,
OHCI_HCD_TD *p_hcd_td,
OHCI_HC_TD *p_hc_td)
{
CPU_INT32U ix;
ix = (p_hc_td - p_ohci->DMA_OHCI.TDPtr);
p_ohci->OHCI_Lookup[ix] = p_hcd_td;
return;
}
/*
*********************************************************************************************************
* OHCI_GetDataPtr()
*
* Description : !!!! description to add
*
* Argument(s) : p_ohci Pointer to OHCI Device structure.
*
* p_buf Pointer to buffer to read descriptor.
*
* buf_len Length of buffer in bytes.
*
* Return(s) : USBH_ERR_NONE.
*
* Note(s) : None.
*********************************************************************************************************
*/
static OHCI_HCD_TD *OHCI_GetDataPtr (OHCI_DEV *p_ohci,
OHCI_HC_TD *p_hc_td)
{
CPU_INT32U ix;
ix = (p_hc_td - p_ohci->DMA_OHCI.TDPtr);
return ((OHCI_HCD_TD *)p_ohci->OHCI_Lookup[ix]);
}