From 913cb9dc645d6db47d8c2a0be0369083b8bed25d Mon Sep 17 00:00:00 2001 From: vanjeff Date: Wed, 11 Jul 2007 06:46:38 +0000 Subject: Import EhciDxe and UhciDxe into MdeModulePkg. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3191 6f19259b-4bc3-4df7-8a09-765794883524 --- MdeModulePkg/Bus/Pci/EhciDxe/EhciUrb.c | 669 +++++++++++++++++++++++++++++++++ 1 file changed, 669 insertions(+) create mode 100644 MdeModulePkg/Bus/Pci/EhciDxe/EhciUrb.c (limited to 'MdeModulePkg/Bus/Pci/EhciDxe/EhciUrb.c') diff --git a/MdeModulePkg/Bus/Pci/EhciDxe/EhciUrb.c b/MdeModulePkg/Bus/Pci/EhciDxe/EhciUrb.c new file mode 100644 index 0000000000..7b8b7b58f2 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/EhciDxe/EhciUrb.c @@ -0,0 +1,669 @@ +/** @file + +Copyright (c) 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + EhciUrb.c + +Abstract: + + This file contains URB request, each request is warpped in a + URB (Usb Request Block) + +Revision History + +**/ + +#include "Ehci.h" + + +/** + Create a single QTD to hold the data + + @param Ehc The EHCI device + @param Data Current data not associated with a QTD + @param DataLen The length of the data + @param PktId Packet ID to use in the QTD + @param Toggle Data toggle to use in the QTD + @param MaxPacket Maximu packet length of the endpoint + + @return Created QTD or NULL if failed to create one + +**/ +EHC_QTD * +EhcCreateQtd ( + IN USB2_HC_DEV *Ehc, + IN UINT8 *Data, + IN UINTN DataLen, + IN UINT8 PktId, + IN UINT8 Toggle, + IN UINTN MaxPacket + ) +{ + EHC_QTD *Qtd; + QTD_HW *QtdHw; + UINTN Index; + UINTN Len; + UINTN ThisBufLen; + + ASSERT (Ehc != NULL); + + Qtd = UsbHcAllocateMem (Ehc->MemPool, sizeof (EHC_QTD)); + + if (Qtd == NULL) { + return NULL; + } + + Qtd->Signature = EHC_QTD_SIG; + Qtd->Data = Data; + Qtd->DataLen = 0; + + InitializeListHead (&Qtd->QtdList); + + QtdHw = &Qtd->QtdHw; + QtdHw->NextQtd = QTD_LINK (NULL, TRUE); + QtdHw->AltNext = QTD_LINK (NULL, TRUE); + QtdHw->Status = QTD_STAT_ACTIVE; + QtdHw->Pid = PktId; + QtdHw->ErrCnt = QTD_MAX_ERR; + QtdHw->IOC = 0; + QtdHw->TotalBytes = 0; + QtdHw->DataToggle = Toggle; + + // + // Fill in the buffer points + // + if (Data != NULL) { + Len = 0; + + for (Index = 0; Index <= QTD_MAX_BUFFER; Index++) { + // + // Set the buffer point (Check page 41 EHCI Spec 1.0). No need to + // compute the offset and clear Reserved fields. This is already + // done in the data point. + // + QtdHw->Page[Index] = EHC_LOW_32BIT (Data); + QtdHw->PageHigh[Index] = EHC_HIGH_32BIT (Data); + + ThisBufLen = QTD_BUF_LEN - (EHC_LOW_32BIT (Data) & QTD_BUF_MASK); + + if (Len + ThisBufLen >= DataLen) { + Len = DataLen; + break; + } + + Len += ThisBufLen; + Data += ThisBufLen; + } + + // + // Need to fix the last pointer if the Qtd can't hold all the + // user's data to make sure that the length is in the unit of + // max packets. If it can hold all the data, there is no such + // need. + // + if (Len < DataLen) { + Len = Len - Len % MaxPacket; + } + + QtdHw->TotalBytes = (UINT32) Len; + Qtd->DataLen = Len; + } + + return Qtd; +} + + + +/** + Initialize the queue head for interrupt transfer, + that is, initialize the following three fields: + 1. SplitXState in the Status field + 2. Microframe S-mask + 3. Microframe C-mask + + @param Ep The queue head's related endpoint + @param QhHw The queue head to initialize + + @return None + +**/ +STATIC +VOID +EhcInitIntQh ( + IN USB_ENDPOINT *Ep, + IN QH_HW *QhHw + ) +{ + // + // Because UEFI interface can't utilitize an endpoint with + // poll rate faster than 1ms, only need to set one bit in + // the queue head. simple. But it may be changed later. If + // sub-1ms interrupt is supported, need to update the S-Mask + // here + // + if (Ep->DevSpeed == EFI_USB_SPEED_HIGH) { + QhHw->SMask = QH_MICROFRAME_0; + return ; + } + + // + // For low/full speed device, the transfer must go through + // the split transaction. Need to update three fields + // 1. SplitXState in the status + // 2. Microframe S-Mask + // 3. Microframe C-Mask + // UEFI USB doesn't exercise admission control. It simplely + // schedule the high speed transactions in microframe 0, and + // full/low speed transactions at microframe 1. This also + // avoid the use of FSTN. + // + QhHw->SMask = QH_MICROFRAME_1; + QhHw->CMask = QH_MICROFRAME_3 | QH_MICROFRAME_4 | QH_MICROFRAME_5; +} + + + +/** + Allocate and initialize a EHCI queue head + + @param Ehci The EHCI device + @param Ep The endpoint to create queue head for + + @return Created queue head or NULL if failed to create one + +**/ +EHC_QH * +EhcCreateQh ( + IN USB2_HC_DEV *Ehci, + IN USB_ENDPOINT *Ep + ) +{ + EHC_QH *Qh; + QH_HW *QhHw; + + Qh = UsbHcAllocateMem (Ehci->MemPool, sizeof (EHC_QH)); + + if (Qh == NULL) { + return NULL; + } + + Qh->Signature = EHC_QH_SIG; + Qh->NextQh = NULL; + Qh->Interval = Ep->PollRate; + + InitializeListHead (&Qh->Qtds); + + QhHw = &Qh->QhHw; + QhHw->HorizonLink = QH_LINK (NULL, 0, TRUE); + QhHw->DeviceAddr = Ep->DevAddr; + QhHw->Inactive = 0; + QhHw->EpNum = Ep->EpAddr; + QhHw->EpSpeed = Ep->DevSpeed; + QhHw->DtCtrl = 0; + QhHw->ReclaimHead = 0; + QhHw->MaxPacketLen = (UINT32) Ep->MaxPacket; + QhHw->CtrlEp = 0; + QhHw->NakReload = QH_NAK_RELOAD; + QhHw->HubAddr = Ep->HubAddr; + QhHw->PortNum = Ep->HubPort; + QhHw->Multiplier = 1; + QhHw->DataToggle = Ep->Toggle; + + if (Ep->DevSpeed != EFI_USB_SPEED_HIGH) { + QhHw->Status |= QTD_STAT_DO_SS; + } + + switch (Ep->Type) { + case EHC_CTRL_TRANSFER: + // + // Special initialization for the control transfer: + // 1. Control transfer initialize data toggle from each QTD + // 2. Set the Control Endpoint Flag (C) for low/full speed endpoint. + // + QhHw->DtCtrl = 1; + + if (Ep->DevSpeed != EFI_USB_SPEED_HIGH) { + QhHw->CtrlEp = 1; + } + break; + + case EHC_INT_TRANSFER_ASYNC: + case EHC_INT_TRANSFER_SYNC: + // + // Special initialization for the interrupt transfer + // to set the S-Mask and C-Mask + // + QhHw->NakReload = 0; + EhcInitIntQh (Ep, QhHw); + break; + + case EHC_BULK_TRANSFER: + if ((Ep->DevSpeed == EFI_USB_SPEED_HIGH) && (Ep->Direction == EfiUsbDataOut)) { + QhHw->Status |= QTD_STAT_DO_PING; + } + + break; + } + + return Qh; +} + + + +/** + Convert the poll interval from application to that + be used by EHCI interface data structure. Only need + to get the max 2^n that is less than interval. UEFI + can't support high speed endpoint with a interval less + than 8 microframe because interval is specified in + the unit of ms (millisecond) + + @param Interval The interval to convert + + @return The converted interval + +**/ +STATIC +UINTN +EhcConvertPollRate ( + IN UINTN Interval + ) +{ + UINTN BitCount; + + if (Interval == 0) { + return 1; + } + + // + // Find the index (1 based) of the highest non-zero bit + // + BitCount = 0; + + while (Interval != 0) { + Interval >>= 1; + BitCount++; + } + + return 1 << (BitCount - 1); +} + + + +/** + Free a list of QTDs + + @param Ehc The EHCI device + @param Qtds The list head of the QTD + + @return None + +**/ +STATIC +VOID +EhcFreeQtds ( + IN USB2_HC_DEV *Ehc, + IN LIST_ENTRY *Qtds + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + EHC_QTD *Qtd; + + EFI_LIST_FOR_EACH_SAFE (Entry, Next, Qtds) { + Qtd = EFI_LIST_CONTAINER (Entry, EHC_QTD, QtdList); + + RemoveEntryList (&Qtd->QtdList); + UsbHcFreeMem (Ehc->MemPool, Qtd, sizeof (EHC_QTD)); + } +} + + +/** + Free an allocated URB. It is possible for it to be partially inited. + + @param Ehc The EHCI device + @param Urb The URB to free + + @return None + +**/ +VOID +EhcFreeUrb ( + IN USB2_HC_DEV *Ehc, + IN URB *Urb + ) +{ + EFI_PCI_IO_PROTOCOL *PciIo; + + PciIo = Ehc->PciIo; + + if (Urb->RequestPhy != NULL) { + PciIo->Unmap (PciIo, Urb->RequestMap); + } + + if (Urb->DataMap != NULL) { + PciIo->Unmap (PciIo, Urb->DataMap); + } + + if (Urb->Qh != NULL) { + // + // Ensure that this queue head has been unlinked from the + // schedule data structures. Free all the associated QTDs + // + EhcFreeQtds (Ehc, &Urb->Qh->Qtds); + UsbHcFreeMem (Ehc->MemPool, Urb->Qh, sizeof (EHC_QH)); + } + + gBS->FreePool (Urb); +} + + + +/** + Create a list of QTDs for the URB + + @param Ehc The EHCI device + @param Urb The URB to create QTDs for + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for QTD + @retval EFI_SUCCESS The QTDs are allocated for the URB + +**/ +STATIC +EFI_STATUS +EhcCreateQtds ( + IN USB2_HC_DEV *Ehc, + IN URB *Urb + ) +{ + USB_ENDPOINT *Ep; + EHC_QH *Qh; + EHC_QTD *Qtd; + EHC_QTD *StatusQtd; + EHC_QTD *NextQtd; + LIST_ENTRY *Entry; + UINT32 AlterNext; + UINT8 Toggle; + UINTN Len; + UINT8 Pid; + + ASSERT ((Urb != NULL) && (Urb->Qh != NULL)); + + // + // EHCI follows the alternet next QTD pointer if it meets + // a short read and the AlterNext pointer is valid. UEFI + // EHCI driver should terminate the transfer except the + // control transfer. + // + Toggle = 0; + Qh = Urb->Qh; + Ep = &Urb->Ep; + StatusQtd = NULL; + AlterNext = QTD_LINK (NULL, TRUE); + + if (Ep->Direction == EfiUsbDataIn) { + AlterNext = QTD_LINK (Ehc->ShortReadStop, FALSE); + } + + // + // Build the Setup and status packets for control transfer + // + if (Urb->Ep.Type == EHC_CTRL_TRANSFER) { + Len = sizeof (EFI_USB_DEVICE_REQUEST); + Qtd = EhcCreateQtd (Ehc, Urb->RequestPhy, Len, QTD_PID_SETUP, 0, Ep->MaxPacket); + + if (Qtd == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InsertTailList (&Qh->Qtds, &Qtd->QtdList); + + // + // Create the status packet now. Set the AlterNext to it. So, when + // EHCI meets a short control read, it can resume at the status stage. + // Use the opposite direction of the data stage, or IN if there is + // no data stage. + // + if (Ep->Direction == EfiUsbDataIn) { + Pid = QTD_PID_OUTPUT; + } else { + Pid = QTD_PID_INPUT; + } + + StatusQtd = EhcCreateQtd (Ehc, NULL, 0, Pid, 1, Ep->MaxPacket); + + if (StatusQtd == NULL) { + goto ON_ERROR; + } + + if (Ep->Direction == EfiUsbDataIn) { + AlterNext = QTD_LINK (StatusQtd, FALSE); + } + + Toggle = 1; + } + + // + // Build the data packets for all the transfers + // + if (Ep->Direction == EfiUsbDataIn) { + Pid = QTD_PID_INPUT; + } else { + Pid = QTD_PID_OUTPUT; + } + + Qtd = NULL; + Len = 0; + + while (Len < Urb->DataLen) { + Qtd = EhcCreateQtd ( + Ehc, + (UINT8 *) Urb->DataPhy + Len, + Urb->DataLen - Len, + Pid, + Toggle, + Ep->MaxPacket + ); + + if (Qtd == NULL) { + goto ON_ERROR; + } + + Qtd->QtdHw.AltNext = AlterNext; + InsertTailList (&Qh->Qtds, &Qtd->QtdList); + + // + // Switch the Toggle bit if odd number of packets are included in the QTD. + // + if (((Qtd->DataLen + Ep->MaxPacket - 1) / Ep->MaxPacket) % 2) { + Toggle = 1 - Toggle; + } + + Len += Qtd->DataLen; + } + + // + // Insert the status packet for control transfer + // + if (Ep->Type == EHC_CTRL_TRANSFER) { + InsertTailList (&Qh->Qtds, &StatusQtd->QtdList); + } + + // + // OK, all the QTDs needed are created. Now, fix the NextQtd point + // + EFI_LIST_FOR_EACH (Entry, &Qh->Qtds) { + Qtd = EFI_LIST_CONTAINER (Entry, EHC_QTD, QtdList); + + // + // break if it is the last entry on the list + // + if (Entry->ForwardLink == &Qh->Qtds) { + break; + } + + NextQtd = EFI_LIST_CONTAINER (Entry->ForwardLink, EHC_QTD, QtdList); + Qtd->QtdHw.NextQtd = QTD_LINK (NextQtd, FALSE); + } + + // + // Link the QTDs to the queue head + // + NextQtd = EFI_LIST_CONTAINER (Qh->Qtds.ForwardLink, EHC_QTD, QtdList); + Qh->QhHw.NextQtd = QTD_LINK (NextQtd, FALSE); + return EFI_SUCCESS; + +ON_ERROR: + EhcFreeQtds (Ehc, &Qh->Qtds); + return EFI_OUT_OF_RESOURCES; +} + + +/** + Create a new URB and its associated QTD + + @param Ehc The EHCI device + @param DevAddr The device address + @param EpAddr Endpoint addrress & its direction + @param DevSpeed The device speed + @param Toggle Initial data toggle to use + @param MaxPacket The max packet length of the endpoint + @param Hub The transaction translator to use + @param Type The transaction type + @param Request The standard USB request for control transfer + @param Data The user data to transfer + @param DataLen The length of data buffer + @param Callback The function to call when data is transferred + @param Context The context to the callback + @param Interval The interval for interrupt transfer + + @return Created URB or NULL + +**/ +URB * +EhcCreateUrb ( + IN USB2_HC_DEV *Ehc, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINT8 Toggle, + IN UINTN MaxPacket, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Hub, + IN UINTN Type, + IN EFI_USB_DEVICE_REQUEST *Request, + IN VOID *Data, + IN UINTN DataLen, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback, + IN VOID *Context, + IN UINTN Interval + ) +{ + USB_ENDPOINT *Ep; + EFI_PHYSICAL_ADDRESS PhyAddr; + EFI_PCI_IO_PROTOCOL_OPERATION MapOp; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_STATUS Status; + UINTN Len; + URB *Urb; + VOID *Map; + + Urb = AllocateZeroPool (sizeof (URB)); + + if (Urb == NULL) { + return NULL; + } + + Urb->Signature = EHC_URB_SIG; + InitializeListHead (&Urb->UrbList); + + Ep = &Urb->Ep; + Ep->DevAddr = DevAddr; + Ep->EpAddr = EpAddr & 0x0F; + Ep->Direction = ((EpAddr & 0x80) ? EfiUsbDataIn : EfiUsbDataOut); + Ep->DevSpeed = DevSpeed; + Ep->MaxPacket = MaxPacket; + + Ep->HubAddr = 0; + Ep->HubPort = 0; + + if (DevSpeed != EFI_USB_SPEED_HIGH) { + ASSERT (Hub != NULL); + + Ep->HubAddr = Hub->TranslatorHubAddress; + Ep->HubPort = Hub->TranslatorPortNumber; + } + + Ep->Toggle = Toggle; + Ep->Type = Type; + Ep->PollRate = EhcConvertPollRate (Interval); + + Urb->Request = Request; + Urb->Data = Data; + Urb->DataLen = DataLen; + Urb->Callback = Callback; + Urb->Context = Context; + + PciIo = Ehc->PciIo; + Urb->Qh = EhcCreateQh (Ehc, &Urb->Ep); + + if (Urb->Qh == NULL) { + goto ON_ERROR; + } + + // + // Map the request and user data + // + if (Request != NULL) { + Len = sizeof (EFI_USB_DEVICE_REQUEST); + MapOp = EfiPciIoOperationBusMasterRead; + Status = PciIo->Map (PciIo, MapOp, Request, &Len, &PhyAddr, &Map); + + if (EFI_ERROR (Status) || (Len != sizeof (EFI_USB_DEVICE_REQUEST))) { + goto ON_ERROR; + } + + Urb->RequestPhy = (VOID *) ((UINTN) PhyAddr); + Urb->RequestMap = Map; + } + + if (Data != NULL) { + Len = DataLen; + + if (Ep->Direction == EfiUsbDataIn) { + MapOp = EfiPciIoOperationBusMasterWrite; + } else { + MapOp = EfiPciIoOperationBusMasterRead; + } + + Status = PciIo->Map (PciIo, MapOp, Data, &Len, &PhyAddr, &Map); + + if (EFI_ERROR (Status) || (Len != DataLen)) { + goto ON_ERROR; + } + + Urb->DataPhy = (VOID *) ((UINTN) PhyAddr); + Urb->DataMap = Map; + } + + Status = EhcCreateQtds (Ehc, Urb); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return Urb; + +ON_ERROR: + EhcFreeUrb (Ehc, Urb); + return NULL; +} -- cgit v1.2.3