git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10420 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			658 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			658 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| 
 | |
|     This file contains URB request, each request is warpped in a
 | |
|     URB (Usb Request Block).
 | |
| 
 | |
| Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>
 | |
| 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.
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "Ehci.h"
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Create a single QTD to hold the data.
 | |
| 
 | |
|   @param  Ehc                   The EHCI device.
 | |
|   @param  Data                  The cpu memory address of current data not associated with a QTD.
 | |
|   @param  DataPhy               The pci bus address of 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 UINT8                *DataPhy,
 | |
|   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 (DataPhy);
 | |
|       QtdHw->PageHigh[Index]  = EHC_HIGH_32BIT (DataPhy);
 | |
| 
 | |
|       ThisBufLen              = QTD_BUF_LEN - (EHC_LOW_32BIT (DataPhy) & QTD_BUF_MASK);
 | |
| 
 | |
|       if (Len + ThisBufLen >= DataLen) {
 | |
|         Len = DataLen;
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       Len += ThisBufLen;
 | |
|       Data += ThisBufLen;
 | |
|       DataPhy += 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.
 | |
| 
 | |
| **/
 | |
| 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.
 | |
| 
 | |
| **/
 | |
| 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 (UINTN)1 << (BitCount - 1);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Free a list of QTDs.
 | |
| 
 | |
|   @param  Ehc                   The EHCI device.
 | |
|   @param  Qtds                  The list head of the QTD.
 | |
| 
 | |
| **/
 | |
| 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.
 | |
| 
 | |
| **/
 | |
| 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.
 | |
| 
 | |
| **/
 | |
| 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;
 | |
|   EFI_PHYSICAL_ADDRESS    PhyAddr;
 | |
| 
 | |
|   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);
 | |
| 
 | |
|   PhyAddr   = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (EHC_QTD));
 | |
|   if (Ep->Direction == EfiUsbDataIn) {
 | |
|     AlterNext = QTD_LINK (PhyAddr, 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, (UINT8 *)Urb->Request, (UINT8 *)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, NULL, 0, Pid, 1, Ep->MaxPacket);
 | |
| 
 | |
|     if (StatusQtd == NULL) {
 | |
|       goto ON_ERROR;
 | |
|     }
 | |
| 
 | |
|     if (Ep->Direction == EfiUsbDataIn) {
 | |
|       PhyAddr   = UsbHcGetPciAddressForHostMem (Ehc->MemPool, StatusQtd, sizeof (EHC_QTD));
 | |
|       AlterNext = QTD_LINK (PhyAddr, 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->Data + Len,
 | |
|             (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 = (UINT8) (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);
 | |
|     PhyAddr             = UsbHcGetPciAddressForHostMem (Ehc->MemPool, NextQtd, sizeof (EHC_QTD));
 | |
|     Qtd->QtdHw.NextQtd  = QTD_LINK (PhyAddr, FALSE);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Link the QTDs to the queue head
 | |
|   //
 | |
|   NextQtd           = EFI_LIST_CONTAINER (Qh->Qtds.ForwardLink, EHC_QTD, QtdList);
 | |
|   PhyAddr           = UsbHcGetPciAddressForHostMem (Ehc->MemPool, NextQtd, sizeof (EHC_QTD));
 | |
|   Qh->QhHw.NextQtd  = QTD_LINK (PhyAddr, 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      = (UINT8) (EpAddr & 0x0F);
 | |
|   Ep->Direction   = (((EpAddr & 0x80) != 0) ? 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;
 | |
| }
 |