git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10877 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			708 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			708 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| 
 | |
|   The UHCI register operation routines.
 | |
| 
 | |
| 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 "Uhci.h"
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Map address of request structure buffer.
 | |
| 
 | |
|   @param  Uhc                The UHCI device.
 | |
|   @param  Request            The user request buffer.
 | |
|   @param  MappedAddr         Mapped address of request.
 | |
|   @param  Map                Identificaion of this mapping to return.
 | |
| 
 | |
|   @return EFI_SUCCESS        Success.
 | |
|   @return EFI_DEVICE_ERROR   Fail to map the user request.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UhciMapUserRequest (
 | |
|   IN  USB_HC_DEV          *Uhc,
 | |
|   IN  OUT VOID            *Request,
 | |
|   OUT UINT8               **MappedAddr,
 | |
|   OUT VOID                **Map
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS            Status;
 | |
|   UINTN                 Len;
 | |
|   EFI_PHYSICAL_ADDRESS  PhyAddr;
 | |
| 
 | |
|   Len    = sizeof (EFI_USB_DEVICE_REQUEST);
 | |
|   Status = Uhc->PciIo->Map (
 | |
|                          Uhc->PciIo,
 | |
|                          EfiPciIoOperationBusMasterRead,
 | |
|                          Request,
 | |
|                          &Len,
 | |
|                          &PhyAddr,
 | |
|                          Map
 | |
|                          );
 | |
| 
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     *MappedAddr = (UINT8 *) (UINTN) PhyAddr;
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Map address of user data buffer.
 | |
| 
 | |
|   @param  Uhc                The UHCI device.
 | |
|   @param  Direction          Direction of the data transfer.
 | |
|   @param  Data               The user data buffer.
 | |
|   @param  Len                Length of the user data.
 | |
|   @param  PktId              Packet identificaion.
 | |
|   @param  MappedAddr         Mapped address to return.
 | |
|   @param  Map                Identificaion of this mapping to return.
 | |
| 
 | |
|   @return EFI_SUCCESS        Success.
 | |
|   @return EFI_DEVICE_ERROR   Fail to map the user data.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UhciMapUserData (
 | |
|   IN  USB_HC_DEV              *Uhc,
 | |
|   IN  EFI_USB_DATA_DIRECTION  Direction,
 | |
|   IN  VOID                    *Data,
 | |
|   IN  OUT UINTN               *Len,
 | |
|   OUT UINT8                   *PktId,
 | |
|   OUT UINT8                   **MappedAddr,
 | |
|   OUT VOID                    **Map
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS            Status;
 | |
|   EFI_PHYSICAL_ADDRESS  PhyAddr;
 | |
| 
 | |
|   Status = EFI_SUCCESS;
 | |
| 
 | |
|   switch (Direction) {
 | |
|   case EfiUsbDataIn:
 | |
|     //
 | |
|     // BusMasterWrite means cpu read
 | |
|     //
 | |
|     *PktId = INPUT_PACKET_ID;
 | |
|     Status = Uhc->PciIo->Map (
 | |
|                            Uhc->PciIo,
 | |
|                            EfiPciIoOperationBusMasterWrite,
 | |
|                            Data,
 | |
|                            Len,
 | |
|                            &PhyAddr,
 | |
|                            Map
 | |
|                            );
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto EXIT;
 | |
|     }
 | |
| 
 | |
|     *MappedAddr = (UINT8 *) (UINTN) PhyAddr;
 | |
|     break;
 | |
| 
 | |
|   case EfiUsbDataOut:
 | |
|     *PktId = OUTPUT_PACKET_ID;
 | |
|     Status = Uhc->PciIo->Map (
 | |
|                            Uhc->PciIo,
 | |
|                            EfiPciIoOperationBusMasterRead,
 | |
|                            Data,
 | |
|                            Len,
 | |
|                            &PhyAddr,
 | |
|                            Map
 | |
|                            );
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto EXIT;
 | |
|     }
 | |
| 
 | |
|     *MappedAddr = (UINT8 *) (UINTN) PhyAddr;
 | |
|     break;
 | |
| 
 | |
|   case EfiUsbNoData:
 | |
|     if ((Len != NULL) && (*Len != 0)) {
 | |
|       Status    = EFI_INVALID_PARAMETER;
 | |
|       goto EXIT;
 | |
|     }
 | |
| 
 | |
|     *PktId      = OUTPUT_PACKET_ID;
 | |
|     *MappedAddr = NULL;
 | |
|     *Map        = NULL;
 | |
|     break;
 | |
| 
 | |
|   default:
 | |
|     Status      = EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
| EXIT:
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Link the TD To QH.
 | |
| 
 | |
|   @param  Uhc         The UHCI device.
 | |
|   @param  Qh          The queue head for the TD to link to.
 | |
|   @param  Td          The TD to link.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| UhciLinkTdToQh (
 | |
|   IN USB_HC_DEV           *Uhc,
 | |
|   IN UHCI_QH_SW           *Qh,
 | |
|   IN UHCI_TD_SW           *Td
 | |
|   )
 | |
| {
 | |
|   EFI_PHYSICAL_ADDRESS  PhyAddr;
 | |
| 
 | |
|   PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Td, sizeof (UHCI_TD_HW));
 | |
| 
 | |
|   ASSERT ((Qh != NULL) && (Td != NULL));
 | |
| 
 | |
|   Qh->QhHw.VerticalLink = QH_VLINK (PhyAddr, FALSE);
 | |
|   Qh->TDs               = (VOID *) Td;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Unlink TD from the QH.
 | |
| 
 | |
|   @param  Qh          The queue head to unlink from.
 | |
|   @param  Td          The TD to unlink.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| UhciUnlinkTdFromQh (
 | |
|   IN UHCI_QH_SW           *Qh,
 | |
|   IN UHCI_TD_SW           *Td
 | |
|   )
 | |
| {
 | |
|   ASSERT ((Qh != NULL) && (Td != NULL));
 | |
| 
 | |
|   Qh->QhHw.VerticalLink = QH_VLINK (NULL, TRUE);
 | |
|   Qh->TDs               = NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Append a new TD To the previous TD.
 | |
| 
 | |
|   @param  Uhc         The UHCI device.
 | |
|   @param  PrevTd      Previous UHCI_TD_SW to be linked to.
 | |
|   @param  ThisTd      TD to link.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| UhciAppendTd (
 | |
|   IN USB_HC_DEV     *Uhc,
 | |
|   IN UHCI_TD_SW     *PrevTd,
 | |
|   IN UHCI_TD_SW     *ThisTd
 | |
|   )
 | |
| {
 | |
|   EFI_PHYSICAL_ADDRESS  PhyAddr;
 | |
| 
 | |
|   PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, ThisTd, sizeof (UHCI_TD_HW));
 | |
| 
 | |
|   ASSERT ((PrevTd != NULL) && (ThisTd != NULL));
 | |
| 
 | |
|   PrevTd->TdHw.NextLink = TD_LINK (PhyAddr, TRUE, FALSE);
 | |
|   PrevTd->NextTd        = (VOID *) ThisTd;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Delete a list of TDs.
 | |
| 
 | |
|   @param  Uhc         The UHCI device.
 | |
|   @param  FirstTd     TD link list head.
 | |
| 
 | |
|   @return None.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| UhciDestoryTds (
 | |
|   IN USB_HC_DEV           *Uhc,
 | |
|   IN UHCI_TD_SW           *FirstTd
 | |
|   )
 | |
| {
 | |
|   UHCI_TD_SW            *NextTd;
 | |
|   UHCI_TD_SW            *ThisTd;
 | |
| 
 | |
|   NextTd = FirstTd;
 | |
| 
 | |
|   while (NextTd != NULL) {
 | |
|     ThisTd  = NextTd;
 | |
|     NextTd  = ThisTd->NextTd;
 | |
|     UsbHcFreeMem (Uhc->MemPool, ThisTd, sizeof (UHCI_TD_SW));
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Create an initialize a new queue head.
 | |
| 
 | |
|   @param  Uhc         The UHCI device.
 | |
|   @param  Interval    The polling interval for the queue.
 | |
| 
 | |
|   @return The newly created queue header.
 | |
| 
 | |
| **/
 | |
| UHCI_QH_SW *
 | |
| UhciCreateQh (
 | |
|   IN  USB_HC_DEV        *Uhc,
 | |
|   IN  UINTN             Interval
 | |
|   )
 | |
| {
 | |
|   UHCI_QH_SW            *Qh;
 | |
| 
 | |
|   Qh = UsbHcAllocateMem (Uhc->MemPool, sizeof (UHCI_QH_SW));
 | |
| 
 | |
|   if (Qh == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   Qh->QhHw.HorizonLink  = QH_HLINK (NULL, TRUE);
 | |
|   Qh->QhHw.VerticalLink = QH_VLINK (NULL, TRUE);
 | |
|   Qh->Interval          = UhciConvertPollRate(Interval);
 | |
|   Qh->TDs               = NULL;
 | |
|   Qh->NextQh            = NULL;
 | |
| 
 | |
|   return Qh;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Create and intialize a TD.
 | |
| 
 | |
|   @param  Uhc         The UHCI device.
 | |
| 
 | |
|   @return The newly allocated and initialized TD.
 | |
| 
 | |
| **/
 | |
| UHCI_TD_SW *
 | |
| UhciCreateTd (
 | |
|   IN  USB_HC_DEV          *Uhc
 | |
|   )
 | |
| {
 | |
|   UHCI_TD_SW              *Td;
 | |
| 
 | |
|   Td     = UsbHcAllocateMem (Uhc->MemPool, sizeof (UHCI_TD_SW));
 | |
|   if (Td == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   Td->TdHw.NextLink = TD_LINK (NULL, FALSE, TRUE);
 | |
|   Td->NextTd        = NULL;
 | |
|   Td->Data          = NULL;
 | |
|   Td->DataLen       = 0;
 | |
| 
 | |
|   return Td;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Create and initialize a TD for Setup Stage of a control transfer.
 | |
| 
 | |
|   @param  Uhc         The UHCI device.
 | |
|   @param  DevAddr     Device address.
 | |
|   @param  Request     A pointer to cpu memory address of Device request.
 | |
|   @param  RequestPhy  A pointer to pci memory address of Device request.
 | |
|   @param  IsLow       Full speed or low speed.
 | |
| 
 | |
|   @return The created setup Td Pointer.
 | |
| 
 | |
| **/
 | |
| UHCI_TD_SW *
 | |
| UhciCreateSetupTd (
 | |
|   IN  USB_HC_DEV          *Uhc,
 | |
|   IN  UINT8               DevAddr,
 | |
|   IN  UINT8               *Request,
 | |
|   IN  UINT8               *RequestPhy,
 | |
|   IN  BOOLEAN             IsLow
 | |
|   )
 | |
| {
 | |
|   UHCI_TD_SW              *Td;
 | |
| 
 | |
|   Td = UhciCreateTd (Uhc);
 | |
| 
 | |
|   if (Td == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   Td->TdHw.NextLink     = TD_LINK (NULL, TRUE, TRUE);
 | |
|   Td->TdHw.ShortPacket  = FALSE;
 | |
|   Td->TdHw.IsIsoch      = FALSE;
 | |
|   Td->TdHw.IntOnCpl     = FALSE;
 | |
|   Td->TdHw.ErrorCount   = 0x03;
 | |
|   Td->TdHw.Status      |= USBTD_ACTIVE;
 | |
|   Td->TdHw.DataToggle   = 0;
 | |
|   Td->TdHw.EndPoint     = 0;
 | |
|   Td->TdHw.LowSpeed     = IsLow ? 1 : 0;
 | |
|   Td->TdHw.DeviceAddr   = DevAddr & 0x7F;
 | |
|   Td->TdHw.MaxPacketLen = (UINT32) (sizeof (EFI_USB_DEVICE_REQUEST) - 1);
 | |
|   Td->TdHw.PidCode      = SETUP_PACKET_ID;
 | |
|   Td->TdHw.DataBuffer   = (UINT32) (UINTN) RequestPhy;
 | |
| 
 | |
|   Td->Data              = Request;
 | |
|   Td->DataLen           = (UINT16) sizeof (EFI_USB_DEVICE_REQUEST);
 | |
| 
 | |
|   return Td;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Create a TD for data.
 | |
| 
 | |
|   @param  Uhc         The UHCI device.
 | |
|   @param  DevAddr     Device address.
 | |
|   @param  Endpoint    Endpoint number.
 | |
|   @param  DataPtr     A pointer to cpu memory address of Data buffer.
 | |
|   @param  DataPhyPtr  A pointer to pci memory address of Data buffer.
 | |
|   @param  Len         Data length.
 | |
|   @param  PktId       Packet ID.
 | |
|   @param  Toggle      Data toggle value.
 | |
|   @param  IsLow       Full speed or low speed.
 | |
| 
 | |
|   @return Data Td pointer if success, otherwise NULL.
 | |
| 
 | |
| **/
 | |
| UHCI_TD_SW *
 | |
| UhciCreateDataTd (
 | |
|   IN  USB_HC_DEV          *Uhc,
 | |
|   IN  UINT8               DevAddr,
 | |
|   IN  UINT8               Endpoint,
 | |
|   IN  UINT8               *DataPtr,
 | |
|   IN  UINT8               *DataPhyPtr,
 | |
|   IN  UINTN               Len,
 | |
|   IN  UINT8               PktId,
 | |
|   IN  UINT8               Toggle,
 | |
|   IN  BOOLEAN             IsLow
 | |
|   )
 | |
| {
 | |
|   UHCI_TD_SW  *Td;
 | |
| 
 | |
|   //
 | |
|   // Code as length - 1, and the max valid length is 0x500
 | |
|   //
 | |
|   ASSERT (Len <= 0x500);
 | |
| 
 | |
|   Td  = UhciCreateTd (Uhc);
 | |
| 
 | |
|   if (Td == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   Td->TdHw.NextLink     = TD_LINK (NULL, TRUE, TRUE);
 | |
|   Td->TdHw.ShortPacket  = FALSE;
 | |
|   Td->TdHw.IsIsoch      = FALSE;
 | |
|   Td->TdHw.IntOnCpl     = FALSE;
 | |
|   Td->TdHw.ErrorCount   = 0x03;
 | |
|   Td->TdHw.Status       = USBTD_ACTIVE;
 | |
|   Td->TdHw.LowSpeed     = IsLow ? 1 : 0;
 | |
|   Td->TdHw.DataToggle   = Toggle & 0x01;
 | |
|   Td->TdHw.EndPoint     = Endpoint & 0x0F;
 | |
|   Td->TdHw.DeviceAddr   = DevAddr & 0x7F;
 | |
|   Td->TdHw.MaxPacketLen = (UINT32) (Len - 1);
 | |
|   Td->TdHw.PidCode      = (UINT8) PktId;
 | |
|   Td->TdHw.DataBuffer   = (UINT32) (UINTN) DataPhyPtr;
 | |
| 
 | |
|   Td->Data              = DataPtr;
 | |
|   Td->DataLen           = (UINT16) Len;
 | |
| 
 | |
|   return Td;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Create TD for the Status Stage of control transfer.
 | |
| 
 | |
|   @param  Uhc         The UHCI device.
 | |
|   @param  DevAddr     Device address.
 | |
|   @param  PktId       Packet ID.
 | |
|   @param  IsLow       Full speed or low speed.
 | |
| 
 | |
|   @return Status Td Pointer.
 | |
| 
 | |
| **/
 | |
| UHCI_TD_SW *
 | |
| UhciCreateStatusTd (
 | |
|   IN  USB_HC_DEV          *Uhc,
 | |
|   IN  UINT8               DevAddr,
 | |
|   IN  UINT8               PktId,
 | |
|   IN  BOOLEAN             IsLow
 | |
|   )
 | |
| {
 | |
|   UHCI_TD_SW              *Td;
 | |
| 
 | |
|   Td = UhciCreateTd (Uhc);
 | |
| 
 | |
|   if (Td == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   Td->TdHw.NextLink     = TD_LINK (NULL, TRUE, TRUE);
 | |
|   Td->TdHw.ShortPacket  = FALSE;
 | |
|   Td->TdHw.IsIsoch      = FALSE;
 | |
|   Td->TdHw.IntOnCpl     = FALSE;
 | |
|   Td->TdHw.ErrorCount   = 0x03;
 | |
|   Td->TdHw.Status      |= USBTD_ACTIVE;
 | |
|   Td->TdHw.MaxPacketLen = 0x7FF;      //0x7FF: there is no data (refer to UHCI spec)
 | |
|   Td->TdHw.DataToggle   = 1;
 | |
|   Td->TdHw.EndPoint     = 0;
 | |
|   Td->TdHw.LowSpeed     = IsLow ? 1 : 0;
 | |
|   Td->TdHw.DeviceAddr   = DevAddr & 0x7F;
 | |
|   Td->TdHw.PidCode      = (UINT8) PktId;
 | |
|   Td->TdHw.DataBuffer   = (UINT32) (UINTN) NULL;
 | |
| 
 | |
|   Td->Data              = NULL;
 | |
|   Td->DataLen           = 0;
 | |
| 
 | |
|   return Td;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Create Tds list for Control Transfer.
 | |
| 
 | |
|   @param  Uhc         The UHCI device.
 | |
|   @param  DeviceAddr  The device address.
 | |
|   @param  DataPktId   Packet Identification of Data Tds.
 | |
|   @param  Request     A pointer to cpu memory address of request structure buffer to transfer.
 | |
|   @param  RequestPhy  A pointer to pci memory address of request structure buffer to transfer.
 | |
|   @param  Data        A pointer to cpu memory address of user data buffer to transfer.
 | |
|   @param  DataPhy     A pointer to pci memory address of user data buffer to transfer.
 | |
|   @param  DataLen     Length of user data to transfer.
 | |
|   @param  MaxPacket   Maximum packet size for control transfer.
 | |
|   @param  IsLow       Full speed or low speed.
 | |
| 
 | |
|   @return The Td list head for the control transfer.
 | |
| 
 | |
| **/
 | |
| UHCI_TD_SW *
 | |
| UhciCreateCtrlTds (
 | |
|   IN USB_HC_DEV           *Uhc,
 | |
|   IN UINT8                DeviceAddr,
 | |
|   IN UINT8                DataPktId,
 | |
|   IN UINT8                *Request,
 | |
|   IN UINT8                *RequestPhy,
 | |
|   IN UINT8                *Data,
 | |
|   IN UINT8                *DataPhy,
 | |
|   IN UINTN                DataLen,
 | |
|   IN UINT8                MaxPacket,
 | |
|   IN BOOLEAN              IsLow
 | |
|   )
 | |
| {
 | |
|   UHCI_TD_SW                *SetupTd;
 | |
|   UHCI_TD_SW                *FirstDataTd;
 | |
|   UHCI_TD_SW                *DataTd;
 | |
|   UHCI_TD_SW                *PrevDataTd;
 | |
|   UHCI_TD_SW                *StatusTd;
 | |
|   UINT8                     DataToggle;
 | |
|   UINT8                     StatusPktId;
 | |
|   UINTN                     ThisTdLen;
 | |
| 
 | |
| 
 | |
|   DataTd      = NULL;
 | |
|   SetupTd     = NULL;
 | |
|   FirstDataTd = NULL;
 | |
|   PrevDataTd  = NULL;
 | |
|   StatusTd    = NULL;
 | |
| 
 | |
|   //
 | |
|   // Create setup packets for the transfer
 | |
|   //
 | |
|   SetupTd = UhciCreateSetupTd (Uhc, DeviceAddr, Request, RequestPhy, IsLow);
 | |
| 
 | |
|   if (SetupTd == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Create data packets for the transfer
 | |
|   //
 | |
|   DataToggle = 1;
 | |
| 
 | |
|   while (DataLen > 0) {
 | |
|     //
 | |
|     // PktSize is the data load size in each Td.
 | |
|     //
 | |
|     ThisTdLen = (DataLen > MaxPacket ? MaxPacket : DataLen);
 | |
| 
 | |
|     DataTd = UhciCreateDataTd (
 | |
|                Uhc,
 | |
|                DeviceAddr,
 | |
|                0,
 | |
|                Data,  //cpu memory address
 | |
|                DataPhy, //Pci memory address
 | |
|                ThisTdLen,
 | |
|                DataPktId,
 | |
|                DataToggle,
 | |
|                IsLow
 | |
|                );
 | |
| 
 | |
|     if (DataTd == NULL) {
 | |
|       goto FREE_TD;
 | |
|     }
 | |
| 
 | |
|     if (FirstDataTd == NULL) {
 | |
|       FirstDataTd         = DataTd;
 | |
|       FirstDataTd->NextTd = NULL;
 | |
|     } else {
 | |
|       UhciAppendTd (Uhc, PrevDataTd, DataTd);
 | |
|     }
 | |
| 
 | |
|     DataToggle ^= 1;
 | |
|     PrevDataTd = DataTd;
 | |
|     Data += ThisTdLen;
 | |
|     DataPhy += ThisTdLen;
 | |
|     DataLen -= ThisTdLen;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Status packet is on the opposite direction to data packets
 | |
|   //
 | |
|   if (OUTPUT_PACKET_ID == DataPktId) {
 | |
|     StatusPktId = INPUT_PACKET_ID;
 | |
|   } else {
 | |
|     StatusPktId = OUTPUT_PACKET_ID;
 | |
|   }
 | |
| 
 | |
|   StatusTd = UhciCreateStatusTd (Uhc, DeviceAddr, StatusPktId, IsLow);
 | |
| 
 | |
|   if (StatusTd == NULL) {
 | |
|     goto FREE_TD;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Link setup Td -> data Tds -> status Td together
 | |
|   //
 | |
|   if (FirstDataTd != NULL) {
 | |
|     UhciAppendTd (Uhc, SetupTd, FirstDataTd);
 | |
|     UhciAppendTd (Uhc, PrevDataTd, StatusTd);
 | |
|   } else {
 | |
|     UhciAppendTd (Uhc, SetupTd, StatusTd);
 | |
|   }
 | |
| 
 | |
|   return SetupTd;
 | |
| 
 | |
| FREE_TD:
 | |
|   if (SetupTd != NULL) {
 | |
|     UhciDestoryTds (Uhc, SetupTd);
 | |
|   }
 | |
| 
 | |
|   if (FirstDataTd != NULL) {
 | |
|     UhciDestoryTds (Uhc, FirstDataTd);
 | |
|   }
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Create Tds list for Bulk/Interrupt Transfer.
 | |
| 
 | |
|   @param  Uhc         USB_HC_DEV.
 | |
|   @param  DevAddr     Address of Device.
 | |
|   @param  EndPoint    Endpoint Number.
 | |
|   @param  PktId       Packet Identification of Data Tds.
 | |
|   @param  Data        A pointer to cpu memory address of user data buffer to transfer.
 | |
|   @param  DataPhy     A pointer to pci memory address of user data buffer to transfer.
 | |
|   @param  DataLen     Length of user data to transfer.
 | |
|   @param  DataToggle  Data Toggle Pointer.
 | |
|   @param  MaxPacket   Maximum packet size for Bulk/Interrupt transfer.
 | |
|   @param  IsLow       Is Low Speed Device.
 | |
| 
 | |
|   @return The Tds list head for the bulk transfer.
 | |
| 
 | |
| **/
 | |
| UHCI_TD_SW *
 | |
| UhciCreateBulkOrIntTds (
 | |
|   IN USB_HC_DEV           *Uhc,
 | |
|   IN UINT8                DevAddr,
 | |
|   IN UINT8                EndPoint,
 | |
|   IN UINT8                PktId,
 | |
|   IN UINT8                *Data,
 | |
|   IN UINT8                *DataPhy,
 | |
|   IN UINTN                DataLen,
 | |
|   IN OUT UINT8            *DataToggle,
 | |
|   IN UINT8                MaxPacket,
 | |
|   IN BOOLEAN              IsLow
 | |
|   )
 | |
| {
 | |
|   UHCI_TD_SW              *DataTd;
 | |
|   UHCI_TD_SW              *FirstDataTd;
 | |
|   UHCI_TD_SW              *PrevDataTd;
 | |
|   UINTN                   ThisTdLen;
 | |
| 
 | |
|   DataTd      = NULL;
 | |
|   FirstDataTd = NULL;
 | |
|   PrevDataTd  = NULL;
 | |
| 
 | |
|   //
 | |
|   // Create data packets for the transfer
 | |
|   //
 | |
|   while (DataLen > 0) {
 | |
|     //
 | |
|     // PktSize is the data load size that each Td.
 | |
|     //
 | |
|     ThisTdLen = DataLen;
 | |
| 
 | |
|     if (DataLen > MaxPacket) {
 | |
|       ThisTdLen = MaxPacket;
 | |
|     }
 | |
| 
 | |
|     DataTd = UhciCreateDataTd (
 | |
|                Uhc,
 | |
|                DevAddr,
 | |
|                EndPoint,
 | |
|                Data,
 | |
|                DataPhy,
 | |
|                ThisTdLen,
 | |
|                PktId,
 | |
|                *DataToggle,
 | |
|                IsLow
 | |
|                );
 | |
| 
 | |
|     if (DataTd == NULL) {
 | |
|       goto FREE_TD;
 | |
|     }
 | |
| 
 | |
|     if (PktId == INPUT_PACKET_ID) {
 | |
|       DataTd->TdHw.ShortPacket = TRUE;
 | |
|     }
 | |
| 
 | |
|     if (FirstDataTd == NULL) {
 | |
|       FirstDataTd         = DataTd;
 | |
|       FirstDataTd->NextTd = NULL;
 | |
|     } else {
 | |
|       UhciAppendTd (Uhc, PrevDataTd, DataTd);
 | |
|     }
 | |
| 
 | |
|     *DataToggle ^= 1;
 | |
|     PrevDataTd   = DataTd;
 | |
|     Data        += ThisTdLen;
 | |
|     DataPhy     += ThisTdLen;
 | |
|     DataLen     -= ThisTdLen;
 | |
|   }
 | |
| 
 | |
|   return FirstDataTd;
 | |
| 
 | |
| FREE_TD:
 | |
|   if (FirstDataTd != NULL) {
 | |
|     UhciDestoryTds (Uhc, FirstDataTd);
 | |
|   }
 | |
| 
 | |
|   return NULL;
 | |
| }
 |