Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

689 lines
20 KiB

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
iso.c
Abstract:
miniport transfer code for iso
Environment:
kernel mode only
Notes:
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
PURPOSE.
Copyright (c) 1999 Microsoft Corporation. All Rights Reserved.
Revision History:
3-1-00 : created, jdunn
--*/
#include "common.h"
//implements the following miniport functions:
USB_MINIPORT_STATUS
OHCI_OpenIsoEndpoint(
PDEVICE_DATA DeviceData,
PENDPOINT_PARAMETERS EndpointParameters,
PENDPOINT_DATA EndpointData
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PUCHAR buffer;
HW_32BIT_PHYSICAL_ADDRESS phys, edPhys;
PHCD_ENDPOINT_DESCRIPTOR ed;
ULONG i, available, tdCount;
LOGENTRY(DeviceData, G, '_opS', 0, 0, 0);
buffer = EndpointParameters->CommonBufferVa;
phys = EndpointParameters->CommonBufferPhys;
available = EndpointParameters->CommonBufferBytes;
#if DBG
{
ULONG offset;
offset = BYTE_OFFSET(buffer);
// OHCI requires 16 byte alignemnt
OHCI_ASSERT(DeviceData, (offset % 16) == 0);
}
#endif
// use control list
EndpointData->StaticEd =
&DeviceData->StaticEDList[ED_ISOCHRONOUS];
// make the Ed
ed = (PHCD_ENDPOINT_DESCRIPTOR) buffer;
edPhys = phys;
phys += sizeof(HCD_ENDPOINT_DESCRIPTOR);
buffer += sizeof(HCD_ENDPOINT_DESCRIPTOR);
available -= sizeof(HCD_ENDPOINT_DESCRIPTOR);
EndpointData->TdList = (PHCD_TD_LIST) buffer;
tdCount = available/sizeof(HCD_TRANSFER_DESCRIPTOR);
LOGENTRY(DeviceData, G, '_tdC', tdCount, TDS_PER_ISO_ENDPOINT, 0);
OHCI_ASSERT(DeviceData, tdCount >= TDS_PER_ISO_ENDPOINT);
EndpointData->TdCount = tdCount;
for (i=0; i<tdCount; i++) {
OHCI_InitializeTD(DeviceData,
EndpointData,
&EndpointData->TdList->Td[i],
phys);
phys += sizeof(HCD_TRANSFER_DESCRIPTOR);
}
EndpointData->HcdEd =
OHCI_InitializeED(DeviceData,
EndpointData,
ed,
&EndpointData->TdList->Td[0],
edPhys);
// iso endpoints do not halt
ed->EdFlags = EDFLAG_NOHALT;
OHCI_InsertEndpointInSchedule(DeviceData,
EndpointData);
return USBMP_STATUS_SUCCESS;
}
ULONG
OHCI_IsoTransferLookAhead(
PDEVICE_DATA DeviceData,
PENDPOINT_DATA EndpointData,
PTRANSFER_PARAMETERS TransferParameters,
PTRANSFER_CONTEXT TransferContext,
PMINIPORT_ISO_TRANSFER IsoTransfer
)
/*++
Routine Description:
Calculates how many TDs we will need for this transfer
Arguments:
Return Value:
--*/
{
PHCD_TRANSFER_DESCRIPTOR td, lastTd;
ULONG currentPacket;
PMINIPORT_ISO_TRANSFER tmpIsoTransfer;
ULONG need = 1, n;
LOGENTRY(DeviceData, G, '_lk1', EndpointData, TransferParameters,
EndpointData->HcdEd);
OHCI_ASSERT(DeviceData, IsoTransfer->PacketCount > 0);
// sometimes you just need memory
n = sizeof(MINIPORT_ISO_TRANSFER) + sizeof(MINIPORT_ISO_PACKET) *
(IsoTransfer->PacketCount-1);
tmpIsoTransfer = ExAllocatePool(NonPagedPool, n);
if (tmpIsoTransfer == NULL) {
// this will cause us to return busy
return 99999;
}
RtlCopyMemory(tmpIsoTransfer, IsoTransfer, n);
td = OHCI_ALLOC_TD(DeviceData, EndpointData);
currentPacket = 0;
// we need at least one TD to do the caculations
if (td == USB_BAD_PTR) {
// this will cause us to return busy
return 99999;
}
do {
INITIALIZE_TD_FOR_TRANSFER(td, TransferContext);
//TransferContext->PendingTds++;
//EndpointData->PendingTds++;
currentPacket =
OHCI_MapIsoTransferToTd(DeviceData,
tmpIsoTransfer,
currentPacket,
td);
// alloc another iso TD
lastTd = td;
// reuse the same td since this is not a real transfer
//td = OHCI_ALLOC_TD(DeviceData, EndpointData);
need++;
SET_NEXT_TD(lastTd, td);
} while (currentPacket < tmpIsoTransfer->PacketCount);
// free the TD we borrowed
OHCI_FREE_TD(DeviceData, EndpointData, td);
ExFreePool(tmpIsoTransfer);
return need;
}
USB_MINIPORT_STATUS
OHCI_IsoTransfer(
PDEVICE_DATA DeviceData,
PENDPOINT_DATA EndpointData,
PTRANSFER_PARAMETERS TransferParameters,
PTRANSFER_CONTEXT TransferContext,
PMINIPORT_ISO_TRANSFER IsoTransfer
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PHCD_TRANSFER_DESCRIPTOR td, lastTd;
ULONG currentPacket;
ULONG tdsNeeded;
EndpointData->PendingTransfers++;
// we have enough tds, program the transfer
LOGENTRY(DeviceData, G, '_nby', EndpointData, TransferParameters,
EndpointData->HcdEd);
TransferContext->IsoTransfer = IsoTransfer;
// lookahead calculation
// see if we can handle this transfer
//
tdsNeeded = OHCI_IsoTransferLookAhead(DeviceData,
EndpointData,
TransferParameters,
TransferContext,
IsoTransfer);
if ((EndpointData->TdCount - EndpointData->PendingTds) <
tdsNeeded) {
return USBMP_STATUS_BUSY;
}
//
// grab the dummy TD from the tail of the queue
//
td = EndpointData->HcdTailP;
OHCI_ASSERT(DeviceData, td->Flags & TD_FLAG_BUSY);
currentPacket = 0;
LOGENTRY(DeviceData, G, '_iso', EndpointData, TransferContext,
td);
do {
INITIALIZE_TD_FOR_TRANSFER(td, TransferContext);
TransferContext->PendingTds++;
EndpointData->PendingTds++;
currentPacket =
OHCI_MapIsoTransferToTd(DeviceData,
IsoTransfer,
currentPacket,
td);
// alloc another iso TD
lastTd = td;
td = OHCI_ALLOC_TD(DeviceData, EndpointData);
OHCI_ASSERT(DeviceData, td != USB_BAD_PTR);
SET_NEXT_TD(lastTd, td);
} while (currentPacket < IsoTransfer->PacketCount);
// td should be the new dummy,
// now put a new dummy TD on the tail of the EP queue
//
SET_NEXT_TD_NULL(td);
//
// Set new TailP in ED
// note: This is the last TD in the list and the place holder.
//
TransferContext->NextXferTd =
EndpointData->HcdTailP = td;
//if (TransferParameters->TransferBufferLength > 128) {
// TEST_TRAP();
//}
// put the request on the hardware queue
LOGENTRY(DeviceData, G,
'_Til', TransferContext->PendingTds ,
td->PhysicalAddress, EndpointData->HcdEd->HwED.HeadP);
EndpointData->HcdEd->HwED.TailP = td->PhysicalAddress;
LOGENTRY(DeviceData, G, '_igo', EndpointData->HcdHeadP,
TransferContext->TcFlags, 0);
//if (TransferParameters->TransferBufferLength > 128) {
// TEST_TRAP();
//}
return USBMP_STATUS_SUCCESS;
}
ULONG
OHCI_MapIsoTransferToTd(
PDEVICE_DATA DeviceData,
PMINIPORT_ISO_TRANSFER IsoTransfer,
ULONG CurrentPacket,
PHCD_TRANSFER_DESCRIPTOR Td
)
/*++
Routine Description:
Arguments:
Returns:
LengthMapped
--*/
{
HW_32BIT_PHYSICAL_ADDRESS logicalStart, logicalEnd;
HW_32BIT_PHYSICAL_ADDRESS startPage, endPage;
PMINIPORT_ISO_PACKET iPacket;
ULONG packetsThisTd;
ULONG lengthThisTd, offset;
USHORT startFrame;
packetsThisTd = 0;
lengthThisTd = 0;
logicalStart = 0;
LOGENTRY(DeviceData, G, '_mpI', CurrentPacket,
IsoTransfer->PacketCount, 0);
OHCI_ASSERT(DeviceData, CurrentPacket < IsoTransfer->PacketCount);
Td->FrameIndex = CurrentPacket;
while (CurrentPacket < IsoTransfer->PacketCount) {
LOGENTRY(DeviceData, G, '_mpC', CurrentPacket,
IsoTransfer->PacketCount, 0);
iPacket = &IsoTransfer->Packets[CurrentPacket];
OHCI_ASSERT(DeviceData, iPacket->BufferPointerCount < 3);
OHCI_ASSERT(DeviceData, iPacket->BufferPointerCount != 0);
// cases are:
// case 1 - packet has pagebreak
// case 1a we have already filled in part of the current TD
// <bail, next pass will be 1b>
//
// case 1b we have not used the current TD yet
// <add packet to TD and bail>
//
// case 2 - packet has no pagebreak and will fit
// case 2a current packet is on different page than previous
// packet
// <add packet and bail>
//
// case 2b current packet is on same page as previous packet
// <add packet and try to add another>
//
// case 2c TD has not been used yet
// <add packet and try to add another>
//
// case 3 - packet will not fit
// <bail>
// does the packet have a page break?
if (iPacket->BufferPointerCount > 1) {
// yes,
// case 1
if (packetsThisTd != 0) {
// case 1a
// we have packets in this TD bail,
// leave it for next time
LOGENTRY(DeviceData, G, '_c1a', 0, 0, lengthThisTd);
break;
}
// case 1b give the packet its own TD
// convert to a 16-bit frame number
startFrame = (USHORT) iPacket->FrameNumber;
LOGENTRY(DeviceData, G, '_c1b', iPacket, CurrentPacket, startFrame);
logicalStart = iPacket->BufferPointer0.Hw32 & ~OHCI_PAGE_SIZE_MASK;
offset = iPacket->BufferPointer0.Hw32 & OHCI_PAGE_SIZE_MASK;
logicalEnd = iPacket->BufferPointer1.Hw32 +
iPacket->BufferPointer1Length;
lengthThisTd = iPacket->Length;
packetsThisTd++;
CurrentPacket++;
Td->HwTD.Packet[0].Offset = (USHORT) offset;
Td->HwTD.Packet[0].Ones = 0xFFFF;
break;
}
// will this packet fit in the current Td?
if (packetsThisTd < 8 &&
(lengthThisTd+iPacket->Length < OHCI_PAGE_SIZE * 2)) {
LOGENTRY(DeviceData, G, '_fit', iPacket, CurrentPacket, 0);
OHCI_ASSERT(DeviceData, iPacket->BufferPointerCount == 1);
OHCI_ASSERT(DeviceData, iPacket->Length ==
iPacket->BufferPointer0Length);
// yes
// case 2
if (logicalStart == 0) {
// first packet, set logical start & end
// case 2c and frame number
LOGENTRY(DeviceData, G, '_c2c', iPacket, CurrentPacket, 0);
startFrame = (USHORT) iPacket->FrameNumber;
offset = iPacket->BufferPointer0.Hw32 & OHCI_PAGE_SIZE_MASK;
logicalStart = iPacket->BufferPointer0.Hw32 & ~OHCI_PAGE_SIZE_MASK;
logicalEnd = iPacket->BufferPointer0.Hw32 +
iPacket->BufferPointer0Length;
lengthThisTd += iPacket->Length;
Td->HwTD.Packet[0].Offset = (USHORT) offset;
Td->HwTD.Packet[0].Ones = 0xFFFF;
packetsThisTd++;
CurrentPacket++;
} else {
// not first packet
LOGENTRY(DeviceData, G, '_adp', iPacket, CurrentPacket,
packetsThisTd);
logicalEnd = iPacket->BufferPointer0.Hw32 +
iPacket->Length;
OHCI_ASSERT(DeviceData, lengthThisTd < OHCI_PAGE_SIZE * 2);
Td->HwTD.Packet[packetsThisTd].Offset
= (USHORT) (lengthThisTd + offset);
Td->HwTD.Packet[packetsThisTd].Ones = 0xFFFF;
lengthThisTd += iPacket->Length;
packetsThisTd++;
startPage = logicalStart & ~OHCI_PAGE_SIZE_MASK;
endPage = logicalEnd & ~OHCI_PAGE_SIZE_MASK;
CurrentPacket++;
// did we cross a page?
if (startPage != endPage) {
// yes, bail now
LOGENTRY(DeviceData, G, '_c2a', Td, CurrentPacket, 0);
break;
}
LOGENTRY(DeviceData, G, '_c2b', Td, CurrentPacket, 0);
// no, keep going
}
} else {
// won't fit
// bail and leave it for next time
LOGENTRY(DeviceData, G, '_ca3', Td, CurrentPacket, 0);
break;
}
}
Td->HwTD.CBP = logicalStart;
Td->HwTD.BE = logicalEnd-1;
Td->TransferCount = lengthThisTd;
Td->HwTD.Iso.StartingFrame = startFrame;
Td->HwTD.Iso.FrameCount = packetsThisTd-1;
Td->HwTD.Iso.Isochronous = 1;
Td->HwTD.Iso.IntDelay = HcTDIntDelay_0ms;
LOGENTRY(DeviceData, G, '_iso', Td, 0, CurrentPacket);
return CurrentPacket;
}
VOID
OHCI_ProcessDoneIsoTd(
PDEVICE_DATA DeviceData,
PHCD_TRANSFER_DESCRIPTOR Td,
BOOLEAN CompleteTransfer
)
/*++
Routine Description:
process a completed Iso TD
Parameters
--*/
{
PTRANSFER_CONTEXT transferContext;
PENDPOINT_DATA endpointData;
USBD_STATUS usbdStatus;
PMINIPORT_ISO_TRANSFER isoTransfer;
ULONG frames, n, i;
transferContext = TRANSFER_CONTEXT_PTR(Td->TransferContext);
isoTransfer = transferContext->IsoTransfer;
transferContext->PendingTds--;
endpointData = transferContext->EndpointData;
endpointData->PendingTds--;
LOGENTRY(DeviceData, G, '_Did', transferContext,
0,
Td);
// walk the PSWs and fill in the IsoTransfer structure
frames = Td->HwTD.Iso.FrameCount+1;
n = Td->FrameIndex;
for (i = 0; i<frames; i++) {
PMINIPORT_ISO_PACKET mpPak;
PHC_OFFSET_PSW psw;
mpPak = &isoTransfer->Packets[n+i];
psw = &Td->HwTD.Packet[i];
mpPak->LengthTransferred = 0;
if (IN_TRANSFER(transferContext->TransferParameters)) {
// in transfer
// if we got an error the length may still be
// valid, so we return it
if (psw->ConditionCode != HcCC_NotAccessed) {
mpPak->LengthTransferred = psw->Size;
}
LOGENTRY(DeviceData, G, '_isI',
i,
mpPak->LengthTransferred,
psw->ConditionCode);
} else {
// out transfer
// assume all data was sent if no error is indicated
if (psw->ConditionCode == HcCC_NoError) {
mpPak->LengthTransferred = mpPak->Length;
}
LOGENTRY(DeviceData, G, '_isO',
i,
mpPak->LengthTransferred,
psw->ConditionCode);
}
if (psw->ConditionCode == HcCC_NoError) {
mpPak->UsbdStatus = USBD_STATUS_SUCCESS;
} else {
mpPak->UsbdStatus = psw->ConditionCode;
mpPak->UsbdStatus |= 0xC0000000;
}
}
// mark the TD free
OHCI_FREE_TD(DeviceData, endpointData, Td);
if (transferContext->PendingTds == 0 && CompleteTransfer) {
// all TDs for this transfer are done
// clear the HAVE_TRANSFER flag to indicate
// we can take another
endpointData->PendingTransfers--;
transferContext->TransferParameters->FrameCompleted =
OHCI_Get32BitFrameNumber(DeviceData);
LOGENTRY(DeviceData, G, '_cpi',
transferContext,
0,
0);
USBPORT_COMPLETE_ISO_TRANSFER(DeviceData,
endpointData,
transferContext->TransferParameters,
transferContext->IsoTransfer);
}
}
VOID
OHCI_PollIsoEndpoint(
PDEVICE_DATA DeviceData,
PENDPOINT_DATA EndpointData
)
/*++
Routine Description:
Called when the endpoint 'needs attention'
The goal here is to determine which TDs, if any,
have completed and complete any associated transfers
Arguments:
Return Value:
--*/
{
PHCD_TRANSFER_DESCRIPTOR td, currentTd;
PHCD_ENDPOINT_DESCRIPTOR ed;
ULONG i;
PTRANSFER_CONTEXT transfer;
HW_32BIT_PHYSICAL_ADDRESS headP;
ed = EndpointData->HcdEd;
LOGENTRY(DeviceData, G, '_pli', ed, 0, 0);
// note it is important the the compiler generate a
// dword move when reading the queuehead HeadP register
// since this location is also accessed by the host
// hardware
headP = ed->HwED.HeadP;
// get the 'currentTD'
currentTd = (PHCD_TRANSFER_DESCRIPTOR)
USBPORT_PHYSICAL_TO_VIRTUAL(headP & ~HcEDHeadP_FLAGS,
DeviceData,
EndpointData);
LOGENTRY(DeviceData, G, '_cTD', currentTd,
headP & ~HcEDHeadP_FLAGS,
TRANSFER_CONTEXT_PTR(currentTd->TransferContext));
// iso endpoints shpuld not halt
OHCI_ASSERT(DeviceData, (ed->HwED.HeadP & HcEDHeadP_HALT) == 0)
// Walk the swHeadP to the current TD (hw headp)
// mark all TDs we find as completed
//
// NOTE: this step may be skipped if the
// done queue is reliable
td = EndpointData->HcdHeadP;
while (td != currentTd) {
LOGENTRY(DeviceData, G, '_mDN', td, 0, 0);
SET_FLAG(td->Flags, TD_FLAG_DONE);
td = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
}
// set the sw headp to the new current head
EndpointData->HcdHeadP = currentTd;
// now flush all completed TDs
for (i=0; i<EndpointData->TdCount; i++) {
td = &EndpointData->TdList->Td[i];
if ((td->Flags & (TD_FLAG_XFER | TD_FLAG_DONE)) ==
(TD_FLAG_XFER | TD_FLAG_DONE)) {
OHCI_ProcessDoneIsoTd(DeviceData,
td,
TRUE);
}
}
}