/*++ Copyright (c) 1999 Microsoft Corporation Module Name: iso.c Abstract: miniport transfer code for iso 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 Microsoft Corporation. All Rights Reserved. Revision History: 3-1-00 : created, jdunn --*/ #include "common.h" //implements the following miniport functions: USB_MINIPORT_STATUS OHCI_OpenIsoEndpoint( PDEVICE_DATA DeviceData, PENDPOINT_PARAMETERS EndpointParameters, PENDPOINT_DATA EndpointData ) /*++ Routine Description: Arguments: Return Value: --*/ { PUCHAR buffer; HW_32BIT_PHYSICAL_ADDRESS phys, edPhys; PHCD_ENDPOINT_DESCRIPTOR ed; ULONG i, available, tdCount; LOGENTRY(DeviceData, G, '_opS', 0, 0, 0); buffer = EndpointParameters->CommonBufferVa; phys = EndpointParameters->CommonBufferPhys; available = EndpointParameters->CommonBufferBytes; #if DBG { ULONG offset; offset = BYTE_OFFSET(buffer); // OHCI requires 16 byte alignemnt OHCI_ASSERT(DeviceData, (offset % 16) == 0); } #endif // use control list EndpointData->StaticEd = &DeviceData->StaticEDList[ED_ISOCHRONOUS]; // make the Ed ed = (PHCD_ENDPOINT_DESCRIPTOR) buffer; edPhys = phys; phys += sizeof(HCD_ENDPOINT_DESCRIPTOR); buffer += sizeof(HCD_ENDPOINT_DESCRIPTOR); available -= sizeof(HCD_ENDPOINT_DESCRIPTOR); EndpointData->TdList = (PHCD_TD_LIST) buffer; tdCount = available/sizeof(HCD_TRANSFER_DESCRIPTOR); LOGENTRY(DeviceData, G, '_tdC', tdCount, TDS_PER_ISO_ENDPOINT, 0); OHCI_ASSERT(DeviceData, tdCount >= TDS_PER_ISO_ENDPOINT); EndpointData->TdCount = tdCount; for (i=0; iTdList->Td[i], phys); phys += sizeof(HCD_TRANSFER_DESCRIPTOR); } EndpointData->HcdEd = OHCI_InitializeED(DeviceData, EndpointData, ed, &EndpointData->TdList->Td[0], edPhys); // iso endpoints do not halt ed->EdFlags = EDFLAG_NOHALT; OHCI_InsertEndpointInSchedule(DeviceData, EndpointData); return USBMP_STATUS_SUCCESS; } ULONG OHCI_IsoTransferLookAhead( PDEVICE_DATA DeviceData, PENDPOINT_DATA EndpointData, PTRANSFER_PARAMETERS TransferParameters, PTRANSFER_CONTEXT TransferContext, PMINIPORT_ISO_TRANSFER IsoTransfer ) /*++ Routine Description: Calculates how many TDs we will need for this transfer Arguments: Return Value: --*/ { PHCD_TRANSFER_DESCRIPTOR td, lastTd; ULONG currentPacket; PMINIPORT_ISO_TRANSFER tmpIsoTransfer; ULONG need = 1, n; LOGENTRY(DeviceData, G, '_lk1', EndpointData, TransferParameters, EndpointData->HcdEd); OHCI_ASSERT(DeviceData, IsoTransfer->PacketCount > 0); // sometimes you just need memory n = sizeof(MINIPORT_ISO_TRANSFER) + sizeof(MINIPORT_ISO_PACKET) * (IsoTransfer->PacketCount-1); tmpIsoTransfer = ExAllocatePool(NonPagedPool, n); if (tmpIsoTransfer == NULL) { // this will cause us to return busy return 99999; } RtlCopyMemory(tmpIsoTransfer, IsoTransfer, n); td = OHCI_ALLOC_TD(DeviceData, EndpointData); currentPacket = 0; // we need at least one TD to do the caculations if (td == USB_BAD_PTR) { // this will cause us to return busy return 99999; } do { INITIALIZE_TD_FOR_TRANSFER(td, TransferContext); //TransferContext->PendingTds++; //EndpointData->PendingTds++; currentPacket = OHCI_MapIsoTransferToTd(DeviceData, tmpIsoTransfer, currentPacket, td); // alloc another iso TD lastTd = td; // reuse the same td since this is not a real transfer //td = OHCI_ALLOC_TD(DeviceData, EndpointData); need++; SET_NEXT_TD(lastTd, td); } while (currentPacket < tmpIsoTransfer->PacketCount); // free the TD we borrowed OHCI_FREE_TD(DeviceData, EndpointData, td); ExFreePool(tmpIsoTransfer); return need; } USB_MINIPORT_STATUS OHCI_IsoTransfer( PDEVICE_DATA DeviceData, PENDPOINT_DATA EndpointData, PTRANSFER_PARAMETERS TransferParameters, PTRANSFER_CONTEXT TransferContext, PMINIPORT_ISO_TRANSFER IsoTransfer ) /*++ Routine Description: Arguments: Return Value: --*/ { PHCD_TRANSFER_DESCRIPTOR td, lastTd; ULONG currentPacket; ULONG tdsNeeded; EndpointData->PendingTransfers++; // we have enough tds, program the transfer LOGENTRY(DeviceData, G, '_nby', EndpointData, TransferParameters, EndpointData->HcdEd); TransferContext->IsoTransfer = IsoTransfer; // lookahead calculation // see if we can handle this transfer // tdsNeeded = OHCI_IsoTransferLookAhead(DeviceData, EndpointData, TransferParameters, TransferContext, IsoTransfer); if ((EndpointData->TdCount - EndpointData->PendingTds) < tdsNeeded) { return USBMP_STATUS_BUSY; } // // grab the dummy TD from the tail of the queue // td = EndpointData->HcdTailP; OHCI_ASSERT(DeviceData, td->Flags & TD_FLAG_BUSY); currentPacket = 0; LOGENTRY(DeviceData, G, '_iso', EndpointData, TransferContext, td); do { INITIALIZE_TD_FOR_TRANSFER(td, TransferContext); TransferContext->PendingTds++; EndpointData->PendingTds++; currentPacket = OHCI_MapIsoTransferToTd(DeviceData, IsoTransfer, currentPacket, td); // alloc another iso TD lastTd = td; td = OHCI_ALLOC_TD(DeviceData, EndpointData); OHCI_ASSERT(DeviceData, td != USB_BAD_PTR); SET_NEXT_TD(lastTd, td); } while (currentPacket < IsoTransfer->PacketCount); // td should be the new dummy, // now put a new dummy TD on the tail of the EP queue // SET_NEXT_TD_NULL(td); // // Set new TailP in ED // note: This is the last TD in the list and the place holder. // TransferContext->NextXferTd = EndpointData->HcdTailP = td; //if (TransferParameters->TransferBufferLength > 128) { // TEST_TRAP(); //} // put the request on the hardware queue LOGENTRY(DeviceData, G, '_Til', TransferContext->PendingTds , td->PhysicalAddress, EndpointData->HcdEd->HwED.HeadP); EndpointData->HcdEd->HwED.TailP = td->PhysicalAddress; LOGENTRY(DeviceData, G, '_igo', EndpointData->HcdHeadP, TransferContext->TcFlags, 0); //if (TransferParameters->TransferBufferLength > 128) { // TEST_TRAP(); //} return USBMP_STATUS_SUCCESS; } ULONG OHCI_MapIsoTransferToTd( PDEVICE_DATA DeviceData, PMINIPORT_ISO_TRANSFER IsoTransfer, ULONG CurrentPacket, PHCD_TRANSFER_DESCRIPTOR Td ) /*++ Routine Description: Arguments: Returns: LengthMapped --*/ { HW_32BIT_PHYSICAL_ADDRESS logicalStart, logicalEnd; HW_32BIT_PHYSICAL_ADDRESS startPage, endPage; PMINIPORT_ISO_PACKET iPacket; ULONG packetsThisTd; ULONG lengthThisTd, offset; USHORT startFrame; packetsThisTd = 0; lengthThisTd = 0; logicalStart = 0; LOGENTRY(DeviceData, G, '_mpI', CurrentPacket, IsoTransfer->PacketCount, 0); OHCI_ASSERT(DeviceData, CurrentPacket < IsoTransfer->PacketCount); Td->FrameIndex = CurrentPacket; while (CurrentPacket < IsoTransfer->PacketCount) { LOGENTRY(DeviceData, G, '_mpC', CurrentPacket, IsoTransfer->PacketCount, 0); iPacket = &IsoTransfer->Packets[CurrentPacket]; OHCI_ASSERT(DeviceData, iPacket->BufferPointerCount < 3); OHCI_ASSERT(DeviceData, iPacket->BufferPointerCount != 0); // cases are: // case 1 - packet has pagebreak // case 1a we have already filled in part of the current TD // // // case 1b we have not used the current TD yet // // // case 2 - packet has no pagebreak and will fit // case 2a current packet is on different page than previous // packet // // // case 2b current packet is on same page as previous packet // // // case 2c TD has not been used yet // // // case 3 - packet will not fit // // does the packet have a page break? if (iPacket->BufferPointerCount > 1) { // yes, // case 1 if (packetsThisTd != 0) { // case 1a // we have packets in this TD bail, // leave it for next time LOGENTRY(DeviceData, G, '_c1a', 0, 0, lengthThisTd); break; } // case 1b give the packet its own TD // convert to a 16-bit frame number startFrame = (USHORT) iPacket->FrameNumber; LOGENTRY(DeviceData, G, '_c1b', iPacket, CurrentPacket, startFrame); logicalStart = iPacket->BufferPointer0.Hw32 & ~OHCI_PAGE_SIZE_MASK; offset = iPacket->BufferPointer0.Hw32 & OHCI_PAGE_SIZE_MASK; logicalEnd = iPacket->BufferPointer1.Hw32 + iPacket->BufferPointer1Length; lengthThisTd = iPacket->Length; packetsThisTd++; CurrentPacket++; Td->HwTD.Packet[0].Offset = (USHORT) offset; Td->HwTD.Packet[0].Ones = 0xFFFF; break; } // will this packet fit in the current Td? if (packetsThisTd < 8 && (lengthThisTd+iPacket->Length < OHCI_PAGE_SIZE * 2)) { LOGENTRY(DeviceData, G, '_fit', iPacket, CurrentPacket, 0); OHCI_ASSERT(DeviceData, iPacket->BufferPointerCount == 1); OHCI_ASSERT(DeviceData, iPacket->Length == iPacket->BufferPointer0Length); // yes // case 2 if (logicalStart == 0) { // first packet, set logical start & end // case 2c and frame number LOGENTRY(DeviceData, G, '_c2c', iPacket, CurrentPacket, 0); startFrame = (USHORT) iPacket->FrameNumber; offset = iPacket->BufferPointer0.Hw32 & OHCI_PAGE_SIZE_MASK; logicalStart = iPacket->BufferPointer0.Hw32 & ~OHCI_PAGE_SIZE_MASK; logicalEnd = iPacket->BufferPointer0.Hw32 + iPacket->BufferPointer0Length; lengthThisTd += iPacket->Length; Td->HwTD.Packet[0].Offset = (USHORT) offset; Td->HwTD.Packet[0].Ones = 0xFFFF; packetsThisTd++; CurrentPacket++; } else { // not first packet LOGENTRY(DeviceData, G, '_adp', iPacket, CurrentPacket, packetsThisTd); logicalEnd = iPacket->BufferPointer0.Hw32 + iPacket->Length; OHCI_ASSERT(DeviceData, lengthThisTd < OHCI_PAGE_SIZE * 2); Td->HwTD.Packet[packetsThisTd].Offset = (USHORT) (lengthThisTd + offset); Td->HwTD.Packet[packetsThisTd].Ones = 0xFFFF; lengthThisTd += iPacket->Length; packetsThisTd++; startPage = logicalStart & ~OHCI_PAGE_SIZE_MASK; endPage = logicalEnd & ~OHCI_PAGE_SIZE_MASK; CurrentPacket++; // did we cross a page? if (startPage != endPage) { // yes, bail now LOGENTRY(DeviceData, G, '_c2a', Td, CurrentPacket, 0); break; } LOGENTRY(DeviceData, G, '_c2b', Td, CurrentPacket, 0); // no, keep going } } else { // won't fit // bail and leave it for next time LOGENTRY(DeviceData, G, '_ca3', Td, CurrentPacket, 0); break; } } Td->HwTD.CBP = logicalStart; Td->HwTD.BE = logicalEnd-1; Td->TransferCount = lengthThisTd; Td->HwTD.Iso.StartingFrame = startFrame; Td->HwTD.Iso.FrameCount = packetsThisTd-1; Td->HwTD.Iso.Isochronous = 1; Td->HwTD.Iso.IntDelay = HcTDIntDelay_0ms; LOGENTRY(DeviceData, G, '_iso', Td, 0, CurrentPacket); return CurrentPacket; } VOID OHCI_ProcessDoneIsoTd( PDEVICE_DATA DeviceData, PHCD_TRANSFER_DESCRIPTOR Td, BOOLEAN CompleteTransfer ) /*++ Routine Description: process a completed Iso TD Parameters --*/ { PTRANSFER_CONTEXT transferContext; PENDPOINT_DATA endpointData; USBD_STATUS usbdStatus; PMINIPORT_ISO_TRANSFER isoTransfer; ULONG frames, n, i; transferContext = TRANSFER_CONTEXT_PTR(Td->TransferContext); isoTransfer = transferContext->IsoTransfer; transferContext->PendingTds--; endpointData = transferContext->EndpointData; endpointData->PendingTds--; LOGENTRY(DeviceData, G, '_Did', transferContext, 0, Td); // walk the PSWs and fill in the IsoTransfer structure frames = Td->HwTD.Iso.FrameCount+1; n = Td->FrameIndex; for (i = 0; iPackets[n+i]; psw = &Td->HwTD.Packet[i]; mpPak->LengthTransferred = 0; if (IN_TRANSFER(transferContext->TransferParameters)) { // in transfer // if we got an error the length may still be // valid, so we return it if (psw->ConditionCode != HcCC_NotAccessed) { mpPak->LengthTransferred = psw->Size; } LOGENTRY(DeviceData, G, '_isI', i, mpPak->LengthTransferred, psw->ConditionCode); } else { // out transfer // assume all data was sent if no error is indicated if (psw->ConditionCode == HcCC_NoError) { mpPak->LengthTransferred = mpPak->Length; } LOGENTRY(DeviceData, G, '_isO', i, mpPak->LengthTransferred, psw->ConditionCode); } if (psw->ConditionCode == HcCC_NoError) { mpPak->UsbdStatus = USBD_STATUS_SUCCESS; } else { mpPak->UsbdStatus = psw->ConditionCode; mpPak->UsbdStatus |= 0xC0000000; } } // mark the TD free OHCI_FREE_TD(DeviceData, endpointData, Td); if (transferContext->PendingTds == 0 && CompleteTransfer) { // all TDs for this transfer are done // clear the HAVE_TRANSFER flag to indicate // we can take another endpointData->PendingTransfers--; transferContext->TransferParameters->FrameCompleted = OHCI_Get32BitFrameNumber(DeviceData); LOGENTRY(DeviceData, G, '_cpi', transferContext, 0, 0); USBPORT_COMPLETE_ISO_TRANSFER(DeviceData, endpointData, transferContext->TransferParameters, transferContext->IsoTransfer); } } VOID OHCI_PollIsoEndpoint( PDEVICE_DATA DeviceData, 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, currentTd; PHCD_ENDPOINT_DESCRIPTOR ed; ULONG i; PTRANSFER_CONTEXT transfer; HW_32BIT_PHYSICAL_ADDRESS headP; ed = EndpointData->HcdEd; LOGENTRY(DeviceData, G, '_pli', ed, 0, 0); // note it is important the the compiler generate a // dword move when reading the queuehead HeadP register // since this location is also accessed by the host // hardware headP = ed->HwED.HeadP; // get the 'currentTD' currentTd = (PHCD_TRANSFER_DESCRIPTOR) USBPORT_PHYSICAL_TO_VIRTUAL(headP & ~HcEDHeadP_FLAGS, DeviceData, EndpointData); LOGENTRY(DeviceData, G, '_cTD', currentTd, headP & ~HcEDHeadP_FLAGS, TRANSFER_CONTEXT_PTR(currentTd->TransferContext)); // iso endpoints shpuld not halt OHCI_ASSERT(DeviceData, (ed->HwED.HeadP & HcEDHeadP_HALT) == 0) // Walk the swHeadP to the current TD (hw headp) // mark all TDs we find as completed // // NOTE: this step may be skipped if the // done queue is reliable td = EndpointData->HcdHeadP; while (td != currentTd) { LOGENTRY(DeviceData, G, '_mDN', td, 0, 0); SET_FLAG(td->Flags, TD_FLAG_DONE); td = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD); } // set the sw headp to the new current head EndpointData->HcdHeadP = currentTd; // now flush all completed TDs for (i=0; iTdCount; i++) { td = &EndpointData->TdList->Td[i]; if ((td->Flags & (TD_FLAG_XFER | TD_FLAG_DONE)) == (TD_FLAG_XFER | TD_FLAG_DONE)) { OHCI_ProcessDoneIsoTd(DeviceData, td, TRUE); } } }