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.
527 lines
15 KiB
527 lines
15 KiB
/*++
|
|
|
|
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
|
|
|
|
}
|