728 lines
25 KiB
C
728 lines
25 KiB
C
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
|
|
Module Name:
|
|
|
|
isoch.c
|
|
|
|
Abstract:
|
|
|
|
Functions used to support isochronous transfers.
|
|
|
|
Environment:
|
|
|
|
Designed for XBOX.
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
06-29-00 created by Mitchell Dernis (mitchd)
|
|
|
|
--*/
|
|
#ifdef OHCD_ISOCHRONOUS_SUPPORTED
|
|
//
|
|
// Pull in OS headers
|
|
//
|
|
#include <ntos.h>
|
|
|
|
//
|
|
// Setup the debug information for this file (see ..\inc\debug.h)
|
|
//
|
|
#define MODULE_POOL_TAG 'DCHO'
|
|
#include <debug.h>
|
|
DEFINE_USB_DEBUG_FUNCTIONS("OHCD");
|
|
|
|
|
|
//
|
|
// Pull in usb headers
|
|
//
|
|
#include <hcdi.h> //Interface between USBD and HCD
|
|
#include "ohcd.h" //Private OHCD stuff
|
|
|
|
USBD_STATUS
|
|
FASTCALL
|
|
OHCD_fIsochOpenEndpoint(
|
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
|
IN PURB Urb
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Called to open an isochronous endpoint.
|
|
--*/
|
|
{
|
|
POHCD_ISOCH_ENDPOINT endpoint = NULL;
|
|
USBD_STATUS status = USBD_STATUS_SUCCESS;
|
|
ULONG allocSize;
|
|
ULONG maxBuffers;
|
|
ULONG_PTR endpointMemory;
|
|
UCHAR index;
|
|
ULONG_PTR nextTdPhysical;
|
|
ULONG_PTR tdPhysical;
|
|
KIRQL oldIrql;
|
|
|
|
|
|
USB_DBG_ENTRY_PRINT(("Entering OHCD_OpenEndpoint"));
|
|
|
|
maxBuffers = OHCD_IsochPoolGetMaxBuffers();
|
|
allocSize = maxBuffers*sizeof(OHCD_ISOCH_TRANSFER_DESCRIPTOR)+sizeof(OHCD_ISOCH_ENDPOINT);
|
|
|
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
|
|
|
//
|
|
// Allocate the isoch endpoint (with prepended TD's) form pool
|
|
//
|
|
endpointMemory = OHCD_IsochPoolAllocateEndpoint();
|
|
|
|
//
|
|
// If there was no memory than fail the open.
|
|
//
|
|
if(!endpointMemory)
|
|
{
|
|
status = USBD_STATUS_NO_MEMORY;
|
|
goto ExitOpenIsochEndpoint;
|
|
}
|
|
|
|
//
|
|
// Zero the memory
|
|
//
|
|
RtlZeroMemory((PVOID)endpointMemory, allocSize);
|
|
|
|
//
|
|
// Break the memory up into the endpoint and the transfer descriptor memory.
|
|
// (The endpoint starts after the transfer descriptor memory).
|
|
//
|
|
endpoint = (POHCD_ISOCH_ENDPOINT)(
|
|
endpointMemory +
|
|
maxBuffers*sizeof(OHCD_ISOCH_TRANSFER_DESCRIPTOR)
|
|
);
|
|
endpoint->TransferDescriptors = (POHCD_ISOCH_TRANSFER_DESCRIPTOR)(endpointMemory);
|
|
endpoint->PhysicalAddress = OHCD_PoolGetPhysicalAddress(endpoint);
|
|
endpoint->EndpointType = USB_ENDPOINT_TYPE_ISOCHRONOUS;
|
|
endpoint->PollingInterval = 1;
|
|
endpoint->Bandwidth = USBD_CalculateUsbBandwidth(
|
|
Urb->IsochOpenEndpoint.MaxPacketSize,
|
|
endpoint->EndpointType,
|
|
FALSE
|
|
);
|
|
endpoint->MaxAttachedBuffers = (UCHAR)maxBuffers;
|
|
endpoint->Flags = (Urb->IsochOpenEndpoint.Flags&URB_FLAG_ISOCH_CIRCULAR_DMA) ? OHCD_ISOCH_EDFL_CIRCULAR_DMA : 0;
|
|
|
|
//
|
|
// Fill out everything in the control structure.
|
|
// Note the Direction here is set correctly for non-control endpoints.
|
|
// in the next step we have an if USB_ENDPOINT_TYPE_CONTROL and we fix it
|
|
// for the control endpoints.
|
|
//
|
|
endpoint->HcEndpointDescriptor.Control.FunctionAddress = Urb->IsochOpenEndpoint.FunctionAddress;
|
|
endpoint->HcEndpointDescriptor.Control.EndpointAddress = Urb->IsochOpenEndpoint.EndpointAddress;
|
|
endpoint->HcEndpointDescriptor.Control.Direction = (USB_ENDPOINT_DIRECTION_MASK & Urb->OpenEndpoint.EndpointAddress) ? 2 : 1;
|
|
endpoint->HcEndpointDescriptor.Control.Speed = 0;
|
|
endpoint->HcEndpointDescriptor.Control.Skip = 1;
|
|
endpoint->HcEndpointDescriptor.Control.Format = 1;
|
|
endpoint->HcEndpointDescriptor.Control.MaximumPacketSize = Urb->IsochOpenEndpoint.MaxPacketSize;
|
|
|
|
//
|
|
// Set the head and tail pointer to point to the first transfer descriptor
|
|
//
|
|
tdPhysical = OHCD_PoolGetPhysicalAddress(endpoint->TransferDescriptors);
|
|
WRITE_HEADP(&endpoint->HcEndpointDescriptor, (ULONG)tdPhysical);
|
|
endpoint->HcEndpointDescriptor.TailP = (ULONG)tdPhysical;
|
|
//
|
|
// Link up all transfer descriptors in a big circle.
|
|
// The hardware won't see them because HeadP and TailP point
|
|
// to the same one.
|
|
//
|
|
for(index = 0; index < maxBuffers; index++)
|
|
{
|
|
nextTdPhysical = OHCD_PoolGetPhysicalAddress(endpoint->TransferDescriptors+index+1);
|
|
endpoint->TransferDescriptors[index].TdIndexPrev = index-1;
|
|
endpoint->TransferDescriptors[index].TdIndex = index;
|
|
endpoint->TransferDescriptors[index].Endpoint = endpoint;
|
|
endpoint->TransferDescriptors[index].Context = NULL;
|
|
endpoint->TransferDescriptors[index].TransferComplete = NULL;
|
|
endpoint->TransferDescriptors[index].IsochTransferDescriptor.Format=1;
|
|
endpoint->TransferDescriptors[index].IsochTransferDescriptor.NextTD = nextTdPhysical;
|
|
tdPhysical = nextTdPhysical;
|
|
}
|
|
endpoint->TransferDescriptors[index-1].IsochTransferDescriptor.NextTD = 0;
|
|
endpoint->TransferDescriptors[0].TdIndexPrev = index - 1;
|
|
|
|
//
|
|
// Insert the endpoint into the schedule (it will then be visible to the hardware).
|
|
// (The cast is safe because, the beginning of the endpoint structure is the same
|
|
// as the beginning of the isochronous endpoint structure.)
|
|
//
|
|
status = OHCD_ScheduleAddEndpointPeriodic(DeviceExtension, (POHCD_ENDPOINT)endpoint);
|
|
//
|
|
// Udpate URB handle
|
|
//
|
|
if(USBD_SUCCESS(status))
|
|
{
|
|
Urb->IsochOpenEndpoint.EndpointHandle = endpoint;
|
|
}
|
|
else
|
|
{
|
|
Urb->IsochOpenEndpoint.EndpointHandle = NULL;
|
|
//
|
|
// Free the endpoint memory
|
|
//
|
|
OHCD_IsochPoolFreeEndpoint(endpointMemory);
|
|
}
|
|
|
|
ExitOpenIsochEndpoint:
|
|
Urb->Header.Status = status;
|
|
KeLowerIrql(oldIrql);
|
|
USB_DBG_EXIT_PRINT(("Exiting OHCD_OpenIsochEndpoint: usbdStatus = 0x%0.8x", status));
|
|
return status;
|
|
}
|
|
|
|
USBD_STATUS
|
|
FASTCALL
|
|
OHCD_fIsochCloseEndpoint(
|
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
|
IN PURB Urb
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine is the handler for URB_ISOCH_CLOSE_ENDPOINT
|
|
|
|
By the time this routine returns, all of the attached buffers can be freed.
|
|
|
|
Parameters:
|
|
|
|
DeviceExtension - host controller context
|
|
Urb - urb to handle
|
|
|
|
Return Value:
|
|
|
|
USBD_STATUS_SUCCESS success
|
|
--*/
|
|
{
|
|
POHCD_ISOCH_ENDPOINT endpoint = (POHCD_ISOCH_ENDPOINT)Urb->IsochCloseEndpoint.EndpointHandle;
|
|
KIRQL oldIrql;
|
|
//
|
|
// Use high Irql to synchronize.
|
|
//
|
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
|
|
|
//
|
|
// Remove the endpoint from the schedule.
|
|
//
|
|
OHCD_ScheduleRemoveEndpointPeriodic(DeviceExtension, (POHCD_ENDPOINT)endpoint);
|
|
|
|
//
|
|
// Pause the endpoint. The cast is OK, because the relevant fields are the same.
|
|
//
|
|
OHCD_fPauseEndpoint(DeviceExtension, (POHCD_ENDPOINT)endpoint);
|
|
|
|
//
|
|
// Link the URB into the list of pending closes.
|
|
//
|
|
ASSERT(DeviceExtension->PendingCloses != Urb);
|
|
Urb->CloseEndpoint.HcdNextClose = DeviceExtension->PendingCloses;
|
|
DeviceExtension->PendingCloses = Urb;
|
|
//
|
|
// At this point, the only software that knows about the endpoint is us, so high
|
|
// Irql is no longer needed.
|
|
//
|
|
KeLowerIrql(oldIrql);
|
|
|
|
//
|
|
// Complete this as pending.
|
|
//
|
|
return USBD_STATUS_PENDING;
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
OHCD_fIsochCompleteCloseEndpoint(
|
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
|
IN PURB_ISOCH_CLOSE_ENDPOINT Urb
|
|
)
|
|
{
|
|
|
|
POHCD_ISOCH_ENDPOINT endpoint = (POHCD_ISOCH_ENDPOINT)Urb->EndpointHandle;
|
|
ULONG_PTR endpointMemory;
|
|
POHCD_ISOCH_TRANSFER_DESCRIPTOR transferDescriptor;
|
|
ULONG descriptorIndex = endpoint->NextFreeTD + (endpoint->MaxAttachedBuffers - endpoint->AttachedBuffers);
|
|
|
|
//
|
|
// Unlock pages of attached buffers
|
|
//
|
|
|
|
while(endpoint->AttachedBuffers--)
|
|
{
|
|
descriptorIndex = descriptorIndex%endpoint->MaxAttachedBuffers;
|
|
transferDescriptor = endpoint->TransferDescriptors + (descriptorIndex++);
|
|
//
|
|
// Unlock the page(s)
|
|
//
|
|
//
|
|
MmLockUnlockPhysicalPage(transferDescriptor->IsochTransferDescriptor.BufferPage0, TRUE);
|
|
if((transferDescriptor->IsochTransferDescriptor.BufferPage0 ^ transferDescriptor->IsochTransferDescriptor.BufferEnd) & ~(PAGE_SIZE-1))
|
|
{
|
|
MmLockUnlockPhysicalPage(transferDescriptor->IsochTransferDescriptor.BufferEnd, TRUE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Find the original endpoint pointer allocated
|
|
//
|
|
|
|
endpointMemory = ((ULONG_PTR)(endpoint)) - (endpoint->MaxAttachedBuffers*sizeof(OHCD_ISOCH_TRANSFER_DESCRIPTOR));
|
|
OHCD_IsochPoolFreeEndpoint(endpointMemory);
|
|
|
|
//
|
|
// Complete the close
|
|
//
|
|
Urb->Hdr.Status = USBD_STATUS_SUCCESS;
|
|
USBD_CompleteRequest((PURB)Urb);
|
|
}
|
|
|
|
|
|
USBD_STATUS
|
|
FASTCALL
|
|
OHCD_fIsochAttachBuffer(
|
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
|
IN PURB Urb
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine is the handler for URB_ISOCH_ATTACH_BUFFER
|
|
1) check parameters
|
|
2) figure out which TD to use, and fill out basic stuff.
|
|
3) calculate the offset information
|
|
4) Move the tail pointer
|
|
5) Deal with starting frame if transfers are started.
|
|
|
|
Parameters:
|
|
DeviceExtension - host controller context
|
|
Urb - urb to handle
|
|
Return Value:
|
|
USBD_STATUS_SUCCESS success
|
|
USBD_STATUS_ISOCH_ALREADY_STARTED the transfers have already been started.
|
|
USBD_STATUS_ISOCH_ATTACH_MORE_BUFFERS circular DMA and fewer the MaxAttached Buffers are attached.
|
|
USBD_STATUS_BAD_START_FRAME Start frame is in the past or too far in the future.
|
|
--*/
|
|
|
|
{
|
|
POHCD_ISOCH_ENDPOINT endpoint = (POHCD_ISOCH_ENDPOINT)Urb->IsochAttachBuffer.EndpointHandle;
|
|
USBD_STATUS status = USBD_STATUS_SUCCESS;
|
|
POHCD_ISOCH_TRANSFER_DESCRIPTOR transferDescriptor;
|
|
PUSBD_ISOCH_BUFFER_DESCRIPTOR bufferDescriptor;
|
|
ULONG_PTR physicalAddress;
|
|
ULONG initialByteOffset, byteOffset;
|
|
ULONG frameIndex;
|
|
ULONG tdIndex;
|
|
ULONG bufferSize;
|
|
KIRQL oldIrql;
|
|
|
|
ASSERT(endpoint);
|
|
|
|
//
|
|
// Raise IRQL for synchronization purposes. Also it would
|
|
// could cause some frame problems if we were interrupted for
|
|
// an extended period of time.
|
|
//
|
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
|
|
|
//
|
|
// make sure we have transfer descriptors for the buffers.
|
|
//
|
|
if(endpoint->MaxAttachedBuffers == endpoint->AttachedBuffers)
|
|
{
|
|
status = USBD_STATUS_ISOCH_TOO_MANY_BUFFERS;
|
|
goto ExitIsochAttachBuffers;
|
|
}
|
|
//
|
|
// Get a pointer to the buffer descriptor and transfer descriptor
|
|
//
|
|
bufferDescriptor = Urb->IsochAttachBuffer.BufferDescriptor;
|
|
tdIndex = endpoint->NextFreeTD;
|
|
transferDescriptor = endpoint->TransferDescriptors + tdIndex;
|
|
endpoint->NextFreeTD = (endpoint->NextFreeTD+1)%endpoint->MaxAttachedBuffers;
|
|
|
|
//
|
|
// Fill out the software section of the TD.
|
|
//
|
|
transferDescriptor->TransferComplete = bufferDescriptor->TransferComplete;
|
|
transferDescriptor->Context = bufferDescriptor->Context;
|
|
transferDescriptor->Endpoint = endpoint;
|
|
//
|
|
// Fill copy the Delay interrupt information.
|
|
//
|
|
transferDescriptor->IsochTransferDescriptor.DelayInterrupt = Urb->IsochAttachBuffer.InterruptDelay;
|
|
|
|
//
|
|
// Fill out the offset from the pattern.
|
|
//
|
|
transferDescriptor->IsochTransferDescriptor.FrameCount = bufferDescriptor->FrameCount-1;
|
|
initialByteOffset = byteOffset = BYTE_OFFSET(bufferDescriptor->TransferBuffer);
|
|
for(frameIndex = 0; frameIndex < bufferDescriptor->FrameCount; frameIndex++)
|
|
{
|
|
transferDescriptor->IsochTransferDescriptor.Offset_PSW[frameIndex] = (USHORT)(byteOffset|(OHCI_CC_NOT_ACCESSED << 12));
|
|
byteOffset += bufferDescriptor->Pattern[frameIndex];
|
|
}
|
|
bufferSize = (byteOffset-initialByteOffset);
|
|
|
|
//
|
|
// Fillout the start address (and lock it)
|
|
//
|
|
MmLockUnlockBufferPages(bufferDescriptor->TransferBuffer, bufferSize, FALSE);
|
|
physicalAddress = MmGetPhysicalAddress(bufferDescriptor->TransferBuffer);
|
|
transferDescriptor->IsochTransferDescriptor.BufferPage0 = physicalAddress;
|
|
|
|
//
|
|
// Fill out the end address (and lock it, if it is a different page).
|
|
//
|
|
physicalAddress = ((ULONG_PTR)(bufferDescriptor->TransferBuffer)) + bufferSize - 1;
|
|
physicalAddress = MmGetPhysicalAddress((PVOID)physicalAddress);
|
|
transferDescriptor->IsochTransferDescriptor.BufferEnd = physicalAddress;
|
|
|
|
//
|
|
// If we are in circular DMA mode, cache the offset information.
|
|
//
|
|
if(endpoint->Flags&OHCD_ISOCH_EDFL_CIRCULAR_DMA)
|
|
{
|
|
RtlCopyMemory((PVOID)(&transferDescriptor->Offsets), (PVOID)transferDescriptor->IsochTransferDescriptor.Offset_PSW, sizeof(USHORT)*8);
|
|
}
|
|
//
|
|
// If the endpoint is running than we need to deal with the starting frame stuff.
|
|
//
|
|
if(endpoint->Flags&OHCD_ISOCH_EDFL_TRANSFERING)
|
|
{
|
|
//
|
|
// If the next unused frame is not in the past, use it.
|
|
// Otherwise, use the current frame (plus one for safety).
|
|
//
|
|
ULONG frame = OHCD_Get32BitFrameNumber(DeviceExtension)+1;
|
|
if( 0 < (((LONG)endpoint->NextUnusedFrame) - ((LONG)frame)) )
|
|
{
|
|
frame = endpoint->NextUnusedFrame;
|
|
}
|
|
transferDescriptor->IsochTransferDescriptor.StartingFrame = frame;
|
|
endpoint->NextUnusedFrame = frame + bufferDescriptor->FrameCount;
|
|
}
|
|
//
|
|
// Mark that we used a TD
|
|
//
|
|
endpoint->AttachedBuffers++;
|
|
//
|
|
// Move the tail pointer, if we can, if we cannot, the completion routine
|
|
// will get it.
|
|
if(endpoint->AttachedBuffers == endpoint->MaxAttachedBuffers)
|
|
{
|
|
ASSERT(0 == transferDescriptor->IsochTransferDescriptor.NextTD);
|
|
} else
|
|
{
|
|
ASSERT(transferDescriptor->IsochTransferDescriptor.NextTD);
|
|
endpoint->HcEndpointDescriptor.TailP = transferDescriptor->IsochTransferDescriptor.NextTD;
|
|
}
|
|
|
|
|
|
ExitIsochAttachBuffers:
|
|
//
|
|
// Done, we can lower the IRQL again.
|
|
//
|
|
KeLowerIrql(oldIrql);
|
|
Urb->Header.Status = status;
|
|
USBD_CompleteRequest(Urb);
|
|
return status;
|
|
}
|
|
|
|
USBD_STATUS
|
|
FASTCALL
|
|
OHCD_fIsochStartTransfer(
|
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
|
IN PURB Urb
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine is the handler for URB_ISOCH_START_TRANSFER
|
|
1) check parameters
|
|
2) Make sure transfer's are stopped.
|
|
3) Fix up starting frame information.
|
|
4) Clear the skip bit.
|
|
5) Set the transfering flag.
|
|
Parameters:
|
|
DeviceExtension - host controller context
|
|
Urb - urb to handle
|
|
Return Value:
|
|
USBD_STATUS_SUCCESS success
|
|
USBD_STATUS_ISOCH_ALREADY_STARTED the transfers have already been started.
|
|
USBD_STATUS_ISOCH_ATTACH_MORE_BUFFERS circular DMA and fewer the MaxAttached Buffers are attached.
|
|
USBD_STATUS_BAD_START_FRAME Start frame is in the past or too far in the future.
|
|
--*/
|
|
{
|
|
ULONG startingFrame, currentFrame;
|
|
UCHAR startingTD, tdIndex;
|
|
USBD_STATUS status = STATUS_SUCCESS;
|
|
KIRQL oldIrql;
|
|
POHCD_ISOCH_ENDPOINT endpoint = (POHCD_ISOCH_ENDPOINT)Urb->IsochStartTransfer.EndpointHandle;
|
|
|
|
ASSERT(USB_ENDPOINT_TYPE_ISOCHRONOUS == endpoint->EndpointType);
|
|
|
|
//
|
|
// Raise IRQL for synchronization purposes. Also it would
|
|
// could cause some frame problems if we were interrupted for
|
|
// an extended period of time.
|
|
//
|
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
|
//
|
|
// Make sure that the endpoint is not running already.
|
|
//
|
|
if(endpoint->Flags&OHCD_ISOCH_EDFL_TRANSFERING)
|
|
{
|
|
status = USBD_STATUS_ISOCH_ALREADY_STARTED;
|
|
goto ExitIsochStartTransfer;
|
|
}
|
|
//
|
|
// If the endpoint has been stopped, make sure it is late enough to restart.
|
|
//
|
|
currentFrame = OHCD_Get32BitFrameNumber(DeviceExtension); //We need the current frame now.
|
|
if(endpoint->Flags & OHCD_ISOCH_EDFL_STOPPING)
|
|
{
|
|
endpoint->Flags &= ~OHCD_ISOCH_EDFL_STOPPING;
|
|
//
|
|
// If the pause frame is the current frame, than wait a millsecond to
|
|
// make sure that the stop completed.
|
|
//
|
|
if(endpoint->PauseFrame == currentFrame)
|
|
{
|
|
LARGE_INTEGER Wait;
|
|
KeLowerIrql(oldIrql); //Can't wait at high Irql
|
|
Wait.QuadPart = -10000;
|
|
KeDelayExecutionThread(KernelMode, FALSE, &Wait);
|
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
|
}
|
|
//The frame has changed by now
|
|
currentFrame = OHCD_Get32BitFrameNumber(DeviceExtension);
|
|
}
|
|
|
|
//
|
|
// If circular DMA, make sure that all of the buffers are attached.
|
|
//
|
|
if(
|
|
(endpoint->Flags&OHCD_ISOCH_EDFL_CIRCULAR_DMA) &&
|
|
(endpoint->MaxAttachedBuffers != endpoint->AttachedBuffers)
|
|
)
|
|
{
|
|
status = USBD_STATUS_ISOCH_ATTACH_MORE_BUFFERS;
|
|
goto ExitIsochStartTransfer;
|
|
}
|
|
//
|
|
// Check that the starting frame is valid.
|
|
//
|
|
startingFrame = currentFrame+1;
|
|
if(!(Urb->IsochStartTransfer.Flags&URB_FLAG_ISOCH_START_ASAP))
|
|
{
|
|
//
|
|
// Signed subtraction account for wrap around
|
|
//
|
|
LONG FrameDiff = (LONG)Urb->IsochStartTransfer.FrameNumber - (LONG)startingFrame;
|
|
if( FrameDiff < 0 || FrameDiff > USBD_ISOCH_START_FRAME_RANGE)
|
|
{
|
|
status = USBD_STATUS_BAD_START_FRAME;
|
|
goto ExitIsochStartTransfer;
|
|
} else
|
|
{
|
|
startingFrame = Urb->IsochStartTransfer.FrameNumber;
|
|
}
|
|
}
|
|
//
|
|
// Propogate the frame information into all the TDs
|
|
//
|
|
//
|
|
startingTD = (endpoint->MaxAttachedBuffers - endpoint->AttachedBuffers) + endpoint->NextFreeTD;
|
|
startingTD = startingTD%endpoint->MaxAttachedBuffers;
|
|
tdIndex = startingTD;
|
|
do
|
|
{
|
|
endpoint->TransferDescriptors[tdIndex].IsochTransferDescriptor.StartingFrame = startingFrame;
|
|
startingFrame += endpoint->TransferDescriptors[tdIndex].IsochTransferDescriptor.FrameCount+1;
|
|
tdIndex = (tdIndex+1)%endpoint->MaxAttachedBuffers;
|
|
}while(tdIndex != endpoint->NextFreeTD);
|
|
|
|
endpoint->NextUnusedFrame = startingFrame;
|
|
|
|
//
|
|
// Clear the skip bit.
|
|
//
|
|
endpoint->HcEndpointDescriptor.Control.Skip = 0;
|
|
|
|
//
|
|
// Set the OHCD_ISOCH_EDFL_TRANSFERING flag
|
|
//
|
|
endpoint->Flags |= OHCD_ISOCH_EDFL_TRANSFERING;
|
|
|
|
ExitIsochStartTransfer:
|
|
KeLowerIrql(oldIrql);
|
|
Urb->Header.Status = status;
|
|
USBD_CompleteRequest((PURB)Urb);
|
|
return status;
|
|
|
|
}
|
|
|
|
USBD_STATUS
|
|
FASTCALL
|
|
OHCD_fIsochStopTransfer(
|
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
|
IN PURB Urb
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine is the handler for URB_ISOCH_STOP_TRANSFER
|
|
1) Set the Skip Bit.
|
|
2) Record the current frame number.
|
|
Parameters:
|
|
DeviceExtension - host controller context
|
|
Urb - urb to handle
|
|
Return Value:
|
|
USBD_STATUS_SUCCESS or
|
|
USBD_STATUS_ISOCH_NOT_STARTED if the device has not been started.
|
|
|
|
--*/
|
|
{
|
|
KIRQL oldIrql;
|
|
POHCD_ISOCH_ENDPOINT endpoint = (POHCD_ISOCH_ENDPOINT)Urb->IsochStartTransfer.EndpointHandle;
|
|
USBD_STATUS status = USBD_STATUS_SUCCESS;
|
|
|
|
ASSERT(USB_ENDPOINT_TYPE_ISOCHRONOUS == endpoint->EndpointType);
|
|
|
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
|
//
|
|
// If the transfer is started, stop it.
|
|
//
|
|
if(endpoint->Flags&OHCD_ISOCH_EDFL_TRANSFERING)
|
|
{
|
|
//
|
|
// Set the skip bit.
|
|
//
|
|
endpoint->HcEndpointDescriptor.Control.Skip = TRUE;
|
|
//
|
|
// Set the frame that we need to wait for.
|
|
//
|
|
endpoint->PauseFrame = OHCD_Get32BitFrameNumber(DeviceExtension)+2;
|
|
//
|
|
// Change the flags.
|
|
//
|
|
endpoint->Flags &= ~OHCD_ISOCH_EDFL_TRANSFERING;
|
|
endpoint->Flags |= OHCD_ISOCH_EDFL_STOPPING;
|
|
} else
|
|
{
|
|
status = USBD_STATUS_ISOCH_NOT_STARTED;
|
|
}
|
|
KeLowerIrql(oldIrql);
|
|
Urb->Header.Status = status;
|
|
USBD_CompleteRequest((PURB)Urb);
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
OHCD_fIsochProcessTD(
|
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
|
IN POHCD_TRANSFER_DESCRIPTOR TransferDescriptor
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine is called every time a transfer descriptor is retired.
|
|
1) copy the status and notify the class driver.
|
|
2) Relink the TD on to the end of the queue.
|
|
3) if circular mode, recopy the offsets and set the starting frame
|
|
4) if circular or needs dummy, move the tail pointer.
|
|
5) if not circular mode, "free" the attached buffer (decrease attached buffer count).
|
|
Parameters:
|
|
DeviceExtension - host controller context
|
|
TransferDescriptor - needs to be cast to an isoch transfer descriptor.
|
|
--*/
|
|
{
|
|
POHCD_ISOCH_TRANSFER_DESCRIPTOR transferDescriptor = (POHCD_ISOCH_TRANSFER_DESCRIPTOR)TransferDescriptor;
|
|
ULONG tdPhysical;
|
|
POHCD_ISOCH_ENDPOINT endpoint = transferDescriptor->Endpoint;
|
|
UCHAR tdIndexPrev = transferDescriptor->TdIndexPrev;
|
|
PFNUSBD_ISOCH_TRANSFER_COMPLETE transferComplete;
|
|
PVOID callbackContext;
|
|
USBD_ISOCH_TRANSFER_STATUS transferStatus;
|
|
BOOLEAN moveTail;
|
|
|
|
//
|
|
// Copy the info we need for the callback, including the status, the callback itself, and the context
|
|
//
|
|
transferStatus.Status = (USBD_STATUS) transferDescriptor->IsochTransferDescriptor.ConditionCode;
|
|
transferStatus.FrameCount = transferDescriptor->IsochTransferDescriptor.FrameCount + 1;
|
|
RtlCopyMemory((PVOID)transferStatus.PacketStatus, (PVOID)transferDescriptor->IsochTransferDescriptor.Offset_PSW, sizeof(USHORT)*8);
|
|
callbackContext = transferDescriptor->Context;
|
|
transferComplete = transferDescriptor->TransferComplete;
|
|
//
|
|
// Relink the transfer descriptor at the end of the endpoints queue.
|
|
//
|
|
transferDescriptor->IsochTransferDescriptor.NextTD = 0;
|
|
tdPhysical = OHCD_PoolGetPhysicalAddress((PVOID)transferDescriptor);
|
|
endpoint->TransferDescriptors[tdIndexPrev].IsochTransferDescriptor.NextTD = tdPhysical;
|
|
|
|
//
|
|
// If we are in circular DMA mode, update the starting frame
|
|
// and recopy the offset information, and move nextFree
|
|
//
|
|
if(endpoint->Flags&OHCD_ISOCH_EDFL_CIRCULAR_DMA)
|
|
{
|
|
ULONG startingFrame = OHCD_Get32BitFrameNumber(DeviceExtension)+1;
|
|
LONG FrameDiff = (LONG)endpoint->NextUnusedFrame - (LONG)startingFrame;
|
|
if(FrameDiff >= 0)
|
|
{
|
|
startingFrame = endpoint->NextUnusedFrame;
|
|
}
|
|
transferDescriptor->IsochTransferDescriptor.StartingFrame = startingFrame;
|
|
endpoint->NextUnusedFrame = startingFrame + transferDescriptor->IsochTransferDescriptor.FrameCount+1;
|
|
RtlCopyMemory((PVOID)transferDescriptor->IsochTransferDescriptor.Offset_PSW, (PVOID)&transferDescriptor->Offsets, sizeof(USHORT)*8);
|
|
//
|
|
// NextFreeTD is moved so that if we get a stop request it can restart on the
|
|
// next TD. This may or may not be desirable.
|
|
//
|
|
endpoint->NextFreeTD = (endpoint->NextFreeTD+1)%endpoint->MaxAttachedBuffers;
|
|
//
|
|
// Always move the tail pointer for circular mode.
|
|
//
|
|
moveTail = TRUE;
|
|
} else
|
|
{
|
|
//
|
|
// Unlock the page(s)
|
|
//
|
|
//
|
|
MmLockUnlockPhysicalPage(transferDescriptor->IsochTransferDescriptor.BufferPage0, TRUE);
|
|
if((transferDescriptor->IsochTransferDescriptor.BufferPage0 ^ transferDescriptor->IsochTransferDescriptor.BufferEnd) & ~(PAGE_SIZE-1))
|
|
{
|
|
MmLockUnlockPhysicalPage(transferDescriptor->IsochTransferDescriptor.BufferEnd, TRUE);
|
|
}
|
|
//
|
|
// The number of attached buffers (prior to this one completing) is at the
|
|
// maximum, then the attach routine would not have been able to move the
|
|
// tail pointer to point to a dummy, so set moveTail.
|
|
//
|
|
moveTail = (endpoint->AttachedBuffers == endpoint->MaxAttachedBuffers) ? TRUE : FALSE;
|
|
|
|
//
|
|
// Mark that the buffer is now unattached. (i.e. the TD can be reused.
|
|
//
|
|
endpoint->AttachedBuffers--;
|
|
}
|
|
//
|
|
// If moveTail is TRUE, either because we are in circular, or because we need to
|
|
// add a dummy TD, then do it now.
|
|
if(moveTail)
|
|
{
|
|
endpoint->HcEndpointDescriptor.TailP = tdPhysical;
|
|
}
|
|
//
|
|
// Call the class driver's callback
|
|
//
|
|
transferComplete(&transferStatus, callbackContext);
|
|
}
|
|
|
|
#endif //OHCD_ISOCHRONOUS_SUPPORTED
|