3768 lines
164 KiB
C
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]);
|
|
}
|