/*++ 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 // urb 'packets' // x--------x--------x--------x--------x physical pages // *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 urb 'packets' x--------x--------x--------x--------x physical pages 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; pPacketCount; 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; pPacketCount; 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 }