Import EhciDxe and UhciDxe into MdeModulePkg.
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3191 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
669
MdeModulePkg/Bus/Pci/EhciDxe/EhciUrb.c
Normal file
669
MdeModulePkg/Bus/Pci/EhciDxe/EhciUrb.c
Normal file
@@ -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;
|
||||
}
|
Reference in New Issue
Block a user