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.
524 lines
15 KiB
524 lines
15 KiB
/*++
|
|
|
|
Copyright (c) 1999, 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
isoch.c
|
|
|
|
Abstract:
|
|
|
|
miniport transfer code for Isochronous
|
|
|
|
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, 2000 Microsoft Corporation. All Rights Reserved.
|
|
|
|
|
|
Revision History:
|
|
|
|
8-1-00 : created, jsenior
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
|
|
//implements the following miniport functions:
|
|
|
|
//non paged
|
|
//UhciIsochTransfer
|
|
//UhciProcessDoneIsochTd
|
|
//UhciPollIsochEndpoint
|
|
//UhciAbortIsochTransfer
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
UhciIsochTransfer(
|
|
IN PDEVICE_DATA DeviceData,
|
|
IN PENDPOINT_DATA EndpointData,
|
|
IN PTRANSFER_PARAMETERS TransferParameters,
|
|
IN PTRANSFER_CONTEXT TransferContext,
|
|
IN PMINIPORT_ISO_TRANSFER IsoTransfer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize all the TDs for an isochronous Transfer.
|
|
Queue up whatever TDs we can in the current schedule.
|
|
Whatever's left may get queued in the poll routine.
|
|
|
|
Arguments:
|
|
|
|
|
|
--*/
|
|
{
|
|
// indices and offsets
|
|
ULONG i, dbCount;
|
|
// lengths
|
|
ULONG lengthThisTd, lengthMapped = 0;
|
|
USHORT maxPacketSize = EndpointData->Parameters.MaxPacketSize;
|
|
// structure pointers
|
|
PTRANSFER_PARAMETERS tp;
|
|
PISOCH_TRANSFER_BUFFER buffer = NULL;
|
|
PHCD_TRANSFER_DESCRIPTOR firstTd, td; //, lastTd = NULL;
|
|
HW_32BIT_PHYSICAL_ADDRESS address;
|
|
PMINIPORT_ISO_PACKET packet;
|
|
BOOLEAN pageCrossing = FALSE;
|
|
USBD_STATUS insertResult;
|
|
USB_MINIPORT_STATUS mpStatus;
|
|
// Isoch pipes are uni-directional. Get the
|
|
// direction from the endpoint address.
|
|
UCHAR pid = GetPID(EndpointData->Parameters.EndpointAddress);
|
|
|
|
//
|
|
// Do we have enough free resources?
|
|
//
|
|
if (EndpointData->TdCount - EndpointData->TdsUsed <
|
|
IsoTransfer->PacketCount) {
|
|
// Not enough TDs to do this transfer yet.
|
|
// Tell the port driver to wait.
|
|
return USBMP_STATUS_BUSY;
|
|
}
|
|
// We may need DBs. Do we have enough?
|
|
for (i = 0, dbCount = 0; i < IsoTransfer->PacketCount; i++) {
|
|
if (IsoTransfer->Packets[i].BufferPointerCount == 2) {
|
|
dbCount++;
|
|
}
|
|
}
|
|
if (EndpointData->DbCount - EndpointData->DbsUsed <
|
|
dbCount) {
|
|
// Not enough DBs to do this transfer yet.
|
|
// Tell the port driver to wait.
|
|
return USBMP_STATUS_BUSY;
|
|
}
|
|
|
|
UhciCleanOutIsoch(DeviceData, FALSE);
|
|
|
|
#if DBG
|
|
{
|
|
ULONG cf;
|
|
cf = UhciGet32BitFrameNumber(DeviceData);
|
|
LOGENTRY(DeviceData, G, '_iso', IsoTransfer->PacketCount, cf,
|
|
IsoTransfer->Packets[0].FrameNumber);
|
|
|
|
}
|
|
#endif
|
|
// UhciKdPrint((DeviceData, 2, "'First packet frame number = %x\n", IsoTransfer->Packets[0].FrameNumber));
|
|
IncPendingTransfers(DeviceData, EndpointData);
|
|
|
|
// init the context
|
|
RtlZeroMemory(TransferContext, sizeof(*TransferContext));
|
|
TransferContext->Sig = SIG_UHCI_TRANSFER;
|
|
TransferContext->UsbdStatus = USBD_STATUS_SUCCESS;
|
|
TransferContext->EndpointData = EndpointData;
|
|
TransferContext->TransferParameters = tp = TransferParameters;
|
|
TransferContext->IsoTransfer = IsoTransfer;
|
|
|
|
UHCI_ASSERT(DeviceData,
|
|
EndpointData->Parameters.TransferType == Isochronous);
|
|
|
|
LOGENTRY(DeviceData, G, '_isT', EndpointData, TransferParameters, IsoTransfer->Packets[0].FrameNumber);
|
|
|
|
//
|
|
// One TD per transfer.
|
|
//
|
|
for (i = 0; i < IsoTransfer->PacketCount; i++) {
|
|
packet = &IsoTransfer->Packets[i];
|
|
address = packet->BufferPointer0.Hw32;
|
|
UHCI_ASSERT(DeviceData, address);
|
|
UHCI_ASSERT(DeviceData, packet->BufferPointerCount == 1 ||
|
|
packet->BufferPointerCount == 2);
|
|
|
|
//
|
|
// Is this packet ok to transfer?
|
|
//
|
|
UhciCheckIsochTransferInsertion(DeviceData,
|
|
insertResult,
|
|
packet->FrameNumber);
|
|
if (USBD_ERROR(insertResult)) {
|
|
// Not ok to transfer. Try the next one.
|
|
packet->UsbdStatus = insertResult;
|
|
|
|
lengthMapped +=
|
|
packet->BufferPointer0Length + packet->BufferPointer1Length;
|
|
LOGENTRY(DeviceData, G, '_BSF', UhciGet32BitFrameNumber(DeviceData), IsoTransfer->Packets[i].FrameNumber, i);
|
|
continue;
|
|
}
|
|
|
|
if (packet->BufferPointerCount == 1) {
|
|
//
|
|
// Normal, non-buffered case.
|
|
//
|
|
pageCrossing = FALSE;
|
|
lengthThisTd = packet->BufferPointer0Length;
|
|
} else {
|
|
//
|
|
// Page crossing. Must double buffer this transfer.
|
|
//
|
|
lengthThisTd = packet->BufferPointer0Length + packet->BufferPointer1Length;
|
|
|
|
buffer = (PISOCH_TRANSFER_BUFFER)
|
|
UHCI_ALLOC_DB(DeviceData, EndpointData, TRUE);
|
|
UHCI_ASSERT(DeviceData, buffer);
|
|
UHCI_ASSERT(DeviceData, buffer->Sig == SIG_HCD_IDB);
|
|
UHCI_ASSERT(DeviceData, buffer->PhysicalAddress);
|
|
buffer->SystemAddress = IsoTransfer->SystemAddress + lengthMapped;
|
|
buffer->Size = lengthThisTd;
|
|
UHCI_ASSERT(DeviceData, lengthThisTd <= MAX_ISOCH_PACKET_SIZE);
|
|
if (OutPID == pid) {
|
|
RtlCopyMemory(&buffer->Buffer[0],
|
|
buffer->SystemAddress,
|
|
lengthThisTd);
|
|
}
|
|
// Change the address for the TD
|
|
pageCrossing = TRUE;
|
|
address = buffer->PhysicalAddress;
|
|
}
|
|
|
|
TransferContext->PendingTds++;
|
|
|
|
td = UHCI_ALLOC_TD(DeviceData, EndpointData);
|
|
INITIALIZE_TD_FOR_TRANSFER(td, TransferContext);
|
|
|
|
//
|
|
// Initialize the TD fields
|
|
//
|
|
td->HwTD.Token.Pid = pid;
|
|
td->HwTD.Token.MaximumLength = MAXIMUM_LENGTH(lengthThisTd);
|
|
td->HwTD.Token.DataToggle = DataToggle0;
|
|
td->HwTD.Control.IsochronousSelect = 1;
|
|
td->HwTD.Control.ShortPacketDetect = 0; // Don't care about short packets
|
|
td->HwTD.Control.ActualLength = MAXIMUM_LENGTH(0);
|
|
td->HwTD.Control.ErrorCount = 0;
|
|
td->HwTD.Buffer = address;
|
|
td->IsoPacket = packet;
|
|
if (pageCrossing) {
|
|
SET_FLAG(td->Flags, TD_FLAG_DOUBLE_BUFFERED);
|
|
td->DoubleBuffer = (PTRANSFER_BUFFER) buffer;
|
|
}
|
|
// countIOC = countIOC + 1 == 10 ? 0 : countIOC+1;
|
|
//
|
|
// Request some interrupts near the end of the
|
|
// transfer
|
|
td->HwTD.Control.InterruptOnComplete =
|
|
(i+1 >= IsoTransfer->PacketCount) ? 1 : 0; //!countIOC;
|
|
|
|
address += lengthThisTd;
|
|
lengthMapped += lengthThisTd;
|
|
|
|
if (USBD_STATUS_SUCCESS == insertResult) {
|
|
//
|
|
// Put the TD in the schedule
|
|
//
|
|
LOGENTRY(DeviceData, G, '_qi1', td, 0, packet->FrameNumber);
|
|
INSERT_ISOCH_TD(DeviceData, td, packet->FrameNumber);
|
|
}
|
|
}
|
|
|
|
if (!TransferContext->PendingTds) {
|
|
// Nothing got queued. Complete the transfer.
|
|
DecPendingTransfers(DeviceData, EndpointData);
|
|
|
|
LOGENTRY(DeviceData, G, '_cpt',
|
|
packet->UsbdStatus,
|
|
TransferContext,
|
|
TransferContext->BytesTransferred);
|
|
|
|
USBPORT_INVALIDATE_ENDPOINT(DeviceData, EndpointData);
|
|
|
|
UhciKdPrint((DeviceData, 2, "'No tds queued for isoch tx.\n", EndpointData));
|
|
// return error and port will complete the transfer
|
|
mpStatus = USBMP_STATUS_FAILURE;
|
|
} else {
|
|
mpStatus = USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
UHCI_ASSERT(DeviceData, TransferContext->TransferParameters->TransferBufferLength == lengthMapped);
|
|
|
|
return mpStatus;
|
|
}
|
|
|
|
|
|
VOID
|
|
UhciProcessDoneIsochTd(
|
|
PDEVICE_DATA DeviceData,
|
|
PHCD_TRANSFER_DESCRIPTOR Td
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
process a completed isoch TD
|
|
|
|
Parameters
|
|
|
|
--*/
|
|
{
|
|
PTRANSFER_CONTEXT transferContext;
|
|
PENDPOINT_DATA endpointData;
|
|
ULONG byteCount;
|
|
PMINIPORT_ISO_PACKET packet;
|
|
|
|
transferContext = Td->TransferContext;
|
|
ASSERT_TRANSFER(DeviceData, transferContext);
|
|
|
|
transferContext->PendingTds--;
|
|
endpointData = transferContext->EndpointData;
|
|
packet = Td->IsoPacket;
|
|
UHCI_ASSERT(DeviceData, packet);
|
|
|
|
if (!TEST_FLAG(Td->Flags, TD_FLAG_ISO_QUEUED)) {
|
|
packet->UsbdStatus = USBD_STATUS_BAD_START_FRAME;
|
|
} else if (Td->HwTD.Control.Active) {
|
|
packet->UsbdStatus = USBD_STATUS_NOT_ACCESSED;
|
|
} else {
|
|
// completion status for this TD/packet?
|
|
packet->UsbdStatus = UhciGetErrorFromTD(DeviceData, Td);
|
|
}
|
|
|
|
LOGENTRY(DeviceData, G, '_Dit', transferContext,
|
|
packet->UsbdStatus,
|
|
Td);
|
|
|
|
byteCount = ACTUAL_LENGTH(Td->HwTD.Control.ActualLength);
|
|
|
|
transferContext->BytesTransferred += byteCount;
|
|
packet->LengthTransferred = byteCount;
|
|
|
|
//
|
|
// For double buffered transfers, we now have to copy back
|
|
// if this was an IN transfer.
|
|
//
|
|
if (Td->HwTD.Token.Pid == InPID &&
|
|
TEST_FLAG(Td->Flags, TD_FLAG_DOUBLE_BUFFERED)) {
|
|
PISOCH_TRANSFER_BUFFER buffer = (PISOCH_TRANSFER_BUFFER)Td->DoubleBuffer;
|
|
UHCI_ASSERT(DeviceData, TEST_FLAG(buffer->Flags, DB_FLAG_BUSY));
|
|
RtlCopyMemory(buffer->SystemAddress,
|
|
&buffer->Buffer[0],
|
|
buffer->Size);
|
|
}
|
|
|
|
// mark the TD free
|
|
// This also frees any double buffers.
|
|
UHCI_FREE_TD(DeviceData, endpointData, Td);
|
|
|
|
if (transferContext->PendingTds == 0) {
|
|
// all TDs for this transfer are done
|
|
// clear the HAVE_TRANSFER flag to indicate
|
|
// we can take another
|
|
DecPendingTransfers(DeviceData, endpointData);
|
|
|
|
LOGENTRY(DeviceData, G, '_cit',
|
|
packet->UsbdStatus,
|
|
transferContext,
|
|
transferContext->BytesTransferred);
|
|
|
|
transferContext->TransferParameters->FrameCompleted =
|
|
UhciGet32BitFrameNumber(DeviceData);
|
|
|
|
USBPORT_COMPLETE_ISOCH_TRANSFER(
|
|
DeviceData,
|
|
endpointData,
|
|
transferContext->TransferParameters,
|
|
transferContext->IsoTransfer);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
UhciPollIsochEndpoint(
|
|
IN PDEVICE_DATA DeviceData,
|
|
IN 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;
|
|
ULONG i;
|
|
PMINIPORT_ISO_PACKET packet;
|
|
USBD_STATUS insertResult;
|
|
|
|
LOGENTRY(DeviceData, G, '_PiE', EndpointData, 0, 0);
|
|
|
|
//
|
|
// Cleanup the isoch transfers that haven't completed yet.
|
|
//
|
|
UhciCleanOutIsoch(DeviceData, FALSE);
|
|
|
|
//
|
|
// Flush all completed TDs and
|
|
// queue up TDs that were pended.
|
|
//
|
|
// Don't care about errors.
|
|
// Just get 'em out of here.
|
|
//
|
|
for (i = 0; i<EndpointData->TdCount; i++) {
|
|
td = &EndpointData->TdList->Td[i];
|
|
|
|
if (TEST_FLAG(td->Flags, TD_FLAG_XFER)) {
|
|
if (td->IsoPacket->FrameNumber < DeviceData->LastFrameProcessed ||
|
|
td->IsoPacket->FrameNumber - DeviceData->LastFrameProcessed > UHCI_MAX_FRAME) {
|
|
//
|
|
// Done, whether we like it or not.
|
|
//
|
|
td->Flags |= TD_FLAG_DONE;
|
|
} else if (!TEST_FLAG(td->Flags, TD_FLAG_ISO_QUEUED)) {
|
|
packet = td->IsoPacket;
|
|
UhciKdPrint((DeviceData, 0, "'Late TD\n"));
|
|
UhciCheckIsochTransferInsertion(DeviceData,
|
|
insertResult,
|
|
packet->FrameNumber);
|
|
if (USBD_STATUS_SUCCESS == insertResult) {
|
|
//
|
|
// Put the TD in the schedule
|
|
//
|
|
LOGENTRY(DeviceData, G, '_qi2', td, 0, packet->FrameNumber);
|
|
INSERT_ISOCH_TD(DeviceData, td, packet->FrameNumber);
|
|
}
|
|
}
|
|
|
|
if (TEST_FLAG(td->Flags, TD_FLAG_DONE)) {
|
|
UhciProcessDoneIsochTd(DeviceData, td);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
UhciCleanOutIsoch(
|
|
IN PDEVICE_DATA DeviceData,
|
|
IN BOOLEAN ForceClean
|
|
)
|
|
{
|
|
ULONG i, currentFrame;
|
|
|
|
if (1 != InterlockedIncrement(&DeviceData->SynchronizeIsoCleanup)) {
|
|
InterlockedDecrement(&DeviceData->SynchronizeIsoCleanup);
|
|
return;
|
|
}
|
|
//
|
|
// Clean out the schedule, by pointing the frames
|
|
// back to the interrupt QHs.
|
|
//
|
|
currentFrame = UhciGet32BitFrameNumber(DeviceData);
|
|
|
|
if (currentFrame - DeviceData->LastFrameProcessed >= UHCI_MAX_FRAME ||
|
|
ForceClean) {
|
|
//
|
|
// Schedule overrun.
|
|
// Clean out all the frames.
|
|
//
|
|
UhciKdPrint((DeviceData, 2, "'Overrun L %x C %x\n", DeviceData->LastFrameProcessed, currentFrame));
|
|
for (i = 0;
|
|
i < UHCI_MAX_FRAME;
|
|
i++) {
|
|
UhciCleanFrameOfIsochTds (DeviceData, i);
|
|
}
|
|
} else {
|
|
ULONG frameIndex;
|
|
// normal cleanup of frames up to the current frame.
|
|
frameIndex = ACTUAL_FRAME(currentFrame);
|
|
UHCI_ASSERT(DeviceData, frameIndex < UHCI_MAX_FRAME);
|
|
|
|
for (i = ACTUAL_FRAME(DeviceData->LastFrameProcessed);
|
|
i != frameIndex;
|
|
i = ACTUAL_FRAME(i+1)) {
|
|
UhciCleanFrameOfIsochTds (DeviceData, i);
|
|
}
|
|
}
|
|
DeviceData->LastFrameProcessed = currentFrame;
|
|
|
|
InterlockedDecrement(&DeviceData->SynchronizeIsoCleanup);
|
|
}
|
|
|
|
VOID
|
|
UhciAbortIsochTransfer(
|
|
IN PDEVICE_DATA DeviceData,
|
|
IN PENDPOINT_DATA EndpointData,
|
|
IN PTRANSFER_CONTEXT TransferContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Aborts the specified Isoch transfer by freeing all
|
|
the TDs associated with said transfer. The dequeuing
|
|
of these transfers should have been done in the ISR
|
|
where we clean out the schedule.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PHCD_TRANSFER_DESCRIPTOR td;
|
|
ULONG i;
|
|
|
|
//
|
|
// The endpoint should not be in the schedule
|
|
//
|
|
LOGENTRY(DeviceData, G, '_Ait', EndpointData, TransferContext, 0);
|
|
|
|
UhciKdPrint((DeviceData, 2, "'Aborting isoch transfer %x\n", TransferContext));
|
|
|
|
//
|
|
// Cleanup the isoch transfers that haven't completed yet.
|
|
//
|
|
UhciCleanOutIsoch(DeviceData, FALSE);
|
|
|
|
//
|
|
// Free up all the tds in this transfer.
|
|
//
|
|
for (i=0; i<EndpointData->TdCount; i++) {
|
|
td = &EndpointData->TdList->Td[i];
|
|
if (td->TransferContext == TransferContext) {
|
|
UHCI_FREE_TD(DeviceData, EndpointData, td);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
UhciSetIsochEndpointState(
|
|
IN PDEVICE_DATA DeviceData,
|
|
IN PENDPOINT_DATA EndpointData,
|
|
IN MP_ENDPOINT_STATE State
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
LOGENTRY(DeviceData, G, '_Sis', EndpointData, State, 0);
|
|
}
|
|
|
|
|