|
|
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
iso.c
Abstract:
Constructs and handle iso transfers.
Environment:
kernel mode only
Notes:
Revision History:
1-1-00 : created
--*/
#include "common.h"
#ifdef ALLOC_PRAGMA
#endif
// non paged functions
MP_HW_PHYSICAL_ADDRESS USBPORT_GetPhysicalAddressFromOffset( PTRANSFER_SG_LIST SgList, ULONG Offset, PULONG Idx ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { PTRANSFER_SG_ENTRY32 sg; ULONG i; MP_HW_PHYSICAL_ADDRESS p; ULONG c = SgList->SgCount-1;
for(i=0; i < SgList->SgCount; i++) {
if (Offset >= SgList->SgEntry[i].StartOffset && Offset < SgList->SgEntry[i].StartOffset + SgList->SgEntry[i].Length) { break; } } // i = idx of sg entry that this packet falls in
sg = &SgList->SgEntry[i];
// the 'offset' of the packet minus the start offset of the
// sg entry is the offset into this sg entry that the packet
// starts
// {.sgN...}{.sgN+1.}{.sgN+2.}{.sgN+3.} sg entries
// b--------------------------->e client buffer
// <p0><p1><p2><p3><p4><p5><p6> urb 'packets'
// x--------x--------x--------x--------x physical pages
// <m0><m1><m2><m3><m4><m5><m6>
*Idx = i;
USBPORT_ASSERT(Offset >= sg->StartOffset);
p = sg->LogicalAddress; p.Hw32 += (Offset - sg->StartOffset);
return p; }
VOID USBPORT_InitializeIsoTransfer( PDEVICE_OBJECT FdoDeviceObject, PTRANSFER_URB Urb, PHCD_TRANSFER_CONTEXT Transfer ) /*++
Routine Description:
Initialize the iso transfer structure from the orginal client URB and SG list
{.sgN...}{.sgN...}{..sgN..} sg entries b--------------------------->e client buffer <p0><p1><p2><p3><p4><p5><p6> urb 'packets' x--------x--------x--------x--------x physical pages <m0><m1><m2><m3><m4><m5><m6>
The sg entries are not that useful to the USB controller HW since the HW deals in usb packets so we create a structure that describes the physical addresses assocaited with each packet.
Arguments:
Return Value:
--*/ { PDEVICE_EXTENSION devExt; PMINIPORT_ISO_TRANSFER isoTransfer; PUSBD_ISO_PACKET_DESCRIPTOR usbdPak; PMINIPORT_ISO_PACKET mpPak; PTRANSFER_SG_LIST sgList; ULONG p, i, cf, j; ULONG sgIdx_Start, sgIdx_End, offset; PUCHAR va; MP_HW_PHYSICAL_ADDRESS b0, b1; ULONG b1Idx, b0Idx; BOOLEAN highSpeed; ASSERT_TRANSFER(Transfer); highSpeed = TEST_FLAG(Transfer->Flags, USBPORT_TXFLAG_HIGHSPEED);
GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt); isoTransfer = Transfer->IsoTransfer; sgList = &Transfer->SgList;
LOGENTRY(Transfer->Endpoint, FdoDeviceObject, LOG_ISO, 'iISO', Urb, Transfer, isoTransfer);
isoTransfer->Sig = SIG_ISOCH; isoTransfer->PacketCount = Urb->u.Isoch.NumberOfPackets; isoTransfer->SystemAddress = sgList->MdlSystemAddress;
// note: proper start frame was computed when the transfer
// was queued.
// check the current frame if it is too late to transmit any
// packets set the appropriate errors in the URB
MP_Get32BitFrameNumber(devExt, cf);
LOGENTRY(Transfer->Endpoint, FdoDeviceObject, LOG_ISO, 'isCf', cf, Urb->u.Isoch.StartFrame, isoTransfer);
if (highSpeed) { // for high speed we are dealing with microframes
// (8 packest per frame)
// BUGBUG this needs to be failed
USBPORT_ASSERT((isoTransfer->PacketCount % 8) == 0); for (i = Urb->u.Isoch.StartFrame; i < Urb->u.Isoch.StartFrame + Urb->u.Isoch.NumberOfPackets/8; i++) {
if (i <= cf) { p = (i - Urb->u.Isoch.StartFrame)*8; for (j=0; j<8; j++) { usbdPak = &Urb->u.Isoch.IsoPacket[p+j];
if (usbdPak->Status == USBD_STATUS_NOT_SET) { usbdPak->Status = USBD_STATUS_ISO_NA_LATE_USBPORT;
LOGENTRY(Transfer->Endpoint, FdoDeviceObject, LOG_ISO, 'late', cf, i, Transfer); } } } } } else { for (i = Urb->u.Isoch.StartFrame; i < Urb->u.Isoch.StartFrame + Urb->u.Isoch.NumberOfPackets; i++) {
if (i <= cf) { p = i - Urb->u.Isoch.StartFrame; usbdPak = &Urb->u.Isoch.IsoPacket[p];
if (usbdPak->Status == USBD_STATUS_NOT_SET) { usbdPak->Status = USBD_STATUS_ISO_NA_LATE_USBPORT;
LOGENTRY(Transfer->Endpoint, FdoDeviceObject, LOG_ISO, 'late', cf, i, Transfer); } } } } // initialize the packets
for (p=0; p< isoTransfer->PacketCount; p++) { ULONG n; usbdPak = &Urb->u.Isoch.IsoPacket[p]; mpPak = &isoTransfer->Packets[p];
// first Zero the mp packet
RtlZeroMemory(mpPak, sizeof(*mpPak));
// each packet has an 'offset' from the start
// of the client buffer we need to find the sg
// entry associated with this packet based on
// this 'offset' and get the physical address
// for the satrt of the packet
b0 = USBPORT_GetPhysicalAddressFromOffset(sgList, usbdPak->Offset, &b0Idx); LOGENTRY(NULL, FdoDeviceObject, LOG_ISO, 'ib0=', usbdPak->Offset, b0Idx, p);
// length is implied by the offset specified in the
// usbd packet, the length is the difference between the
// current packet start offset and the next packet start
// offset.
if (p == isoTransfer->PacketCount - 1) { n = Transfer->Tp.TransferBufferLength; } else { n = Urb->u.Isoch.IsoPacket[p+1].Offset; } mpPak->Length = n - usbdPak->Offset; if (highSpeed) { mpPak->FrameNumber = Urb->u.Isoch.StartFrame+p/8; mpPak->MicroFrameNumber = p%8; } else { mpPak->FrameNumber = Urb->u.Isoch.StartFrame+p; }
// get the sg entry associated with the last byte of the packet
b1 = USBPORT_GetPhysicalAddressFromOffset(sgList, usbdPak->Offset + mpPak->Length -1, &b1Idx); LOGENTRY(NULL, FdoDeviceObject, LOG_ISO, 'ib1=', usbdPak->Offset, b1Idx, usbdPak->Offset + mpPak->Length);
USBPORT_ASSERT(b1Idx - b0Idx < 2);
if (b0Idx == b1Idx) { // this packet is contained by a single sg entry
mpPak->BufferPointer0 = b0; mpPak->BufferPointer0Length = mpPak->Length; mpPak->BufferPointerCount = 1; } else { PTRANSFER_SG_ENTRY32 sg; // this packet crosses an sg entry...
mpPak->BufferPointer0 = b0; // since this packet bumps in to the end
// of a page the length is page_size minus
// phys offset
mpPak->BufferPointer0Length = 0x1000; mpPak->BufferPointer0Length -= (b0.Hw32 & 0xFFF);
// since we crossed an sg entry on this packet
// the start address will be the phys address
// of the sg entry
sg = &sgList->SgEntry[b1Idx]; mpPak->BufferPointer1 = sg->LogicalAddress; mpPak->BufferPointer1Length = mpPak->Length - mpPak->BufferPointer0Length; mpPak->BufferPointerCount = 2; } USBPORT_ASSERT(mpPak->BufferPointerCount != 0);
} }
VOID USBPORT_ErrorCompleteIsoTransfer( PDEVICE_OBJECT FdoDeviceObject, PHCD_ENDPOINT Endpoint, PHCD_TRANSFER_CONTEXT Transfer ) /*++
Routine Description:
Arguments:
Return Value:
None.
--*/ { PDEVICE_EXTENSION devExt; PTRANSFER_URB urb; USBD_STATUS usbdStatus; PMINIPORT_ISO_TRANSFER isoTransfer; ULONG bytesTransferred, p; ASSERT_TRANSFER(Transfer); isoTransfer = Transfer->IsoTransfer;
GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt);
ASSERT_ENDPOINT(Endpoint); usbdStatus = USBD_STATUS_ISOCH_REQUEST_FAILED; urb = Transfer->Urb; LOGENTRY(Endpoint, FdoDeviceObject, LOG_ISO, 'cpLi', 0, Transfer, urb); ASSERT_TRANSFER_URB(urb);
USBPORT_KdPrint((1, " ISO (USBD_STATUS_ISOCH_REQUEST_FAILED) - packets %d\n", isoTransfer->PacketCount)); // do some conversion on the isoTransfer data
bytesTransferred = 0; urb->u.Isoch.ErrorCount = isoTransfer->PacketCount; for (p=0; p<isoTransfer->PacketCount; p++) { urb->u.Isoch.IsoPacket[p].Status = isoTransfer->Packets[p].UsbdStatus; }
urb->TransferBufferLength = bytesTransferred; // insert the transfer on to our
// 'done list', this riutine initaites
// a DPC to complete the transfers
#ifdef USBPERF
USBPORT_QueueDoneTransfer(Transfer, usbdStatus); #else
USBPORT_QueueDoneTransfer(Transfer, usbdStatus);
USBPORT_SignalWorker(FdoDeviceObject); #endif
}
USBD_STATUS USBPORT_FlushIsoTransfer( PDEVICE_OBJECT FdoDeviceObject, PTRANSFER_PARAMETERS TransferParameters, PMINIPORT_ISO_TRANSFER IsoTransfer ) /*++
Routine Description:
called to complete a transfer.
** Must be called in the context of PollEndpoint
Arguments:
Return Value:
None.
--*/ { PDEVICE_EXTENSION devExt; PHCD_TRANSFER_CONTEXT transfer; PTRANSFER_URB urb; USBD_STATUS usbdStatus = USBD_STATUS_SUCCESS; ULONG bytesTransferred, p;
GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt);
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'cpTi', 0, TransferParameters->FrameCompleted, TransferParameters);
TRANSFER_FROM_TPARAMETERS(transfer, TransferParameters); ASSERT_TRANSFER(transfer);
urb = transfer->Urb; LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'cpUi', 0, transfer, urb); ASSERT_TRANSFER_URB(urb);
transfer->MiniportFrameCompleted = TransferParameters->FrameCompleted;
// do some conversion on the isoTransfer data
bytesTransferred = 0;
urb->u.Isoch.ErrorCount = 0; for (p=0; p<IsoTransfer->PacketCount; p++) { bytesTransferred += IsoTransfer->Packets[p].LengthTransferred;
urb->u.Isoch.IsoPacket[p].Status = IsoTransfer->Packets[p].UsbdStatus;
// note:
// in an effort to create some consistency we handle the buffer
// underrun case here.
// That is:
// if the SHORT_XFER_OK flag is set AND
// the error is USBD_STATUS_DATA_UNDERRUN
// then
// ignore the error
// NOTE: The OHCI controllers report USBD_STATUS_DATA_UNDERRUN
// for short iso packets
if (/*urb->TransferFlags & USBD_SHORT_TRANSFER_OK && */ urb->u.Isoch.IsoPacket[p].Status == USBD_STATUS_DATA_UNDERRUN) { urb->u.Isoch.IsoPacket[p].Status = USBD_STATUS_SUCCESS; LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'igER', urb->u.Isoch.IsoPacket[p].Status, transfer, urb); } if (urb->u.Isoch.IsoPacket[p].Status != USBD_STATUS_SUCCESS) { urb->u.Isoch.ErrorCount++; }
if (transfer->Direction == ReadData) { urb->u.Isoch.IsoPacket[p].Length = IsoTransfer->Packets[p].LengthTransferred; }
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'isoP', urb->u.Isoch.IsoPacket[p].Length, urb->u.Isoch.IsoPacket[p].Status, 0);
}
if (urb->u.Isoch.ErrorCount == IsoTransfer->PacketCount) { // all errors set global status in urb
usbdStatus = USBD_STATUS_ISOCH_REQUEST_FAILED; }
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'isoD', 0, bytesTransferred, urb->u.Isoch.ErrorCount);
transfer->MiniportBytesTransferred = bytesTransferred;
return usbdStatus; }
VOID USBPORTSVC_CompleteIsoTransfer( PDEVICE_DATA DeviceData, PENDPOINT_DATA EndpointData, PTRANSFER_PARAMETERS TransferParameters, PMINIPORT_ISO_TRANSFER IsoTransfer ) /*++
Routine Description:
called to complete a transfer.
** Must be called in the context of PollEndpoint
Arguments:
Return Value:
None.
--*/ { PDEVICE_EXTENSION devExt; PHCD_TRANSFER_CONTEXT transfer; PDEVICE_OBJECT fdoDeviceObject; USBD_STATUS usbdStatus; ULONG bytesTransferred, p;
DEVEXT_FROM_DEVDATA(devExt, DeviceData); ASSERT_FDOEXT(devExt);
fdoDeviceObject = devExt->HcFdoDeviceObject;
TRANSFER_FROM_TPARAMETERS(transfer, TransferParameters); ASSERT_TRANSFER(transfer);
SET_FLAG(transfer->Flags, USBPORT_TXFLAG_MPCOMPLETED); usbdStatus = USBPORT_FlushIsoTransfer(fdoDeviceObject, TransferParameters, IsoTransfer);
// insert the transfer on to our
// 'done list' and signal the worker
// thread
#ifdef USBPERF
USBPORT_QueueDoneTransfer(transfer, usbdStatus); #else
USBPORT_QueueDoneTransfer(transfer, usbdStatus); USBPORT_SignalWorker(devExt->HcFdoDeviceObject); #endif
}
|