/*++ Copyright (c) 1995,1996 Microsoft Corporation :ts=4 Module Name: urb.c Abstract: The module manages transactions on the USB. Environment: kernel mode only Notes: Revision History: 11-01-95 : created 04-28-96 : linked urb support & cancel support --*/ #include "wdm.h" #include "stdarg.h" #include "stdio.h" #include "usbdi.h" #include "hcdi.h" #include "uhcd.h" #define COMPUTE_HCD_EXTENSION_SIZE(urb) (sizeof(HCD_EXTENSION) + \ (urb->HcdUrbCommonTransfer.TransferBufferLength / PAGE_SIZE + 1) \ * sizeof(UHCD_LOGICAL_ADDRESS)) #define UHCD_IS_TRANSFER(urb) (((urb)->UrbHeader.Function == URB_FUNCTION_CONTROL_TRANSFER || \ (urb)->UrbHeader.Function == URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER ||\ (urb)->UrbHeader.Function == URB_FUNCTION_ISOCH_TRANSFER) \ ? TRUE : FALSE) // calc the maxrequests based on endpoint type nad // flag options UCHAR MAX_REQUESTS( IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor, IN ULONG EpFlags ) { if (EpFlags & EPFLAG_DBL_BUFFER) { return 1; } switch (USB_ENDPOINT_TYPE_MASK & EndpointDescriptor->bmAttributes) { case USB_ENDPOINT_TYPE_BULK: return 2; //return 1; case USB_ENDPOINT_TYPE_ISOCHRONOUS: return 3; default: return 1; } } #if 0 ULONG UHCD_CountPageCrossings( IN ULONG MaxRequests, IN ULONG MaxTransferSize ) /*++ Routine Description: Completes an I/O Request Arguments: Return Value: maximum number of possible page crossings --*/ { ULONG pageCrossings; pageCrossings = (MaxTransferSize + PAGE_SIZE) / PAGE_SIZE; if (MaxRequests>1) { pageCrossings *= 2; } // now allocate space for packet buffers UHCD_KdPrint((2, "'UHCD_CountPageCrossings, max transfer size 0x%x -- page crossings = 0x%x\n", MaxTransferSize, pageCrossings)); UHCD_ASSERT(pageCrossings > 0); return pageCrossings; } #endif VOID UHCD_CompleteIrp( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN NTSTATUS ntStatus, IN ULONG Information, IN PHCD_URB Urb ) /*++ Routine Description: Completes an I/O Request Arguments: DeviceObject - pointer to a device object Irp - pointer to an I/O Request Packet to complete ntStatus - status code to set int the IRP when completed Information - Urb - root transfer urb if this Irp is associated with a transfer. Return Value: --*/ { KIRQL irql; UCHAR flags = 0; STARTPROC("cIrp"); UHCD_KdPrint((2, "'UHCD_CompleteIrp status = %x\n", ntStatus)); LOGENTRY(LOG_MISC, 'ICan', Urb, 0, Irp); if (Urb) { // // if we have any working space free it now // if (UHCD_IS_TRANSFER(Urb) && HCD_AREA(Urb).HcdExtension) { PHCD_EXTENSION urbWork; urbWork = HCD_AREA(Urb).HcdExtension; // remember the flags flags = urbWork->Flags; RETHEAP(HCD_AREA(Urb).HcdExtension); HCD_AREA(Urb).HcdExtension = NULL; } // // Decrement the urb counter that we keep in our stack location for // the irp, when it goes to zero -- complete it // // // one Urb completed // DECREMENT_PENDING_URB_COUNT(Irp); if (PENDING_URB_COUNT(Irp)) { // // stall completion until all Urbs are done. // TEST_TRAP(); return; } else { IoAcquireCancelSpinLock(&irql); if (Irp->Cancel) { LOGENTRY(LOG_MISC, 'irpX', flags, 0, Irp); // note that the cancel routine will only mess // with the urbs on the active list -- any active // urbs should have been removed from the active // list before calling this routine // // Irp associated with this transfer has // been canceled. // // The cancel routine will complete the Irp // unless there are active transfers. // if (flags & UHCD_TRANSFER_ACTIVE) { IoSetCancelRoutine(Irp, NULL); IoReleaseCancelSpinLock(irql); // // if the io has already started the we must // complete the Irp with STATUS_CANCELLED here. // ntStatus = STATUS_CANCELLED; Information = 0; goto UHCD_CompleteIrp_CompleteRequest; } IoReleaseCancelSpinLock(irql); return; } else { // // Irp is no longer cancelable // LOGENTRY(LOG_MISC, 'NCan', flags, 0, Irp); IoSetCancelRoutine(Irp, NULL); IoReleaseCancelSpinLock(irql); // // Pending bit should be cleared // UHCD_ASSERT(!USBD_PENDING(Urb->HcdUrbCommonTransfer.Status)); } } } UHCD_CompleteIrp_CompleteRequest: Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = Information; LOGENTRY(LOG_MISC, 'irpC', Irp, DeviceObject, Urb); USBD_CompleteRequest(Irp, IO_NO_INCREMENT); ENDPROC("cIrp"); } NTSTATUS UHCD_URB_Dispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Process URBs from the dispatch routine. Arguments: DeviceObject - pointer to a device object Irp - pointer to an I/O Request Packet Return Value: --*/ { PHCD_URB urb; NTSTATUS ntStatus = STATUS_SUCCESS; PDEVICE_EXTENSION deviceExtension; PUHCD_ENDPOINT endpoint = NULL; ULONG siz, cnt; ULONG numTDs; UHCD_KdPrint((2, "'enter UHCD_URB_Dispatch \n")); deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; urb = (PHCD_URB) URB_FROM_IRP(Irp); if (deviceExtension->CurrentDevicePowerState != PowerDeviceD0 || deviceExtension->HcFlags & HCFLAG_HCD_SHUTDOWN) { // // someone is submitting requests while the // HC is suspended or OFF, we will just fail them // UHCD_KdPrint ((0, "'Warning: UHCD got a request while not in D0 in shutdown\n")); ntStatus = STATUS_DEVICE_NOT_READY; URB_HEADER(urb).Status = USBD_STATUS_REQUEST_FAILED; UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL); return ntStatus; } switch(URB_HEADER(urb).Function) { // // These commands get queued to the startio routine // for processing. // case URB_FUNCTION_HCD_OPEN_ENDPOINT: #ifdef ROOT_HUB if (urb->HcdUrbOpenEndpoint.DeviceAddress == deviceExtension->RootHubDeviceAddress) { // This routine will either complete // the irp or mark it pending. ntStatus = UHCD_RootHub_OpenEndpoint(DeviceObject, Irp, urb); break; } #endif // // Grow the common buffer pool based on how much // memory we'll need for transfers // // Well need enough for one set of TDs plus // a common buffer for each possible page crossing // within a trasnsfer. // first allocate space for the TDs // for now use a global value // // allocate the endpoint structures here while we are // at passive level // endpoint = (PUHCD_ENDPOINT) GETHEAP(NonPagedPool, sizeof(UHCD_ENDPOINT)); if (endpoint) { // start endpoint initialization RtlZeroMemory(endpoint, sizeof(*endpoint)); endpoint->Sig = SIG_EP; // check for bulk and iso special options // we only support double buffering for bulk-IN // we only support fast iso for iso-OUT if (urb->HcdUrbOpenEndpoint.HcdEndpointFlags & USBD_EP_FLAG_DOUBLE_BUFFER) { if (USB_ENDPOINT_DIRECTION_IN( urb->HcdUrbOpenEndpoint.EndpointDescriptor->bEndpointAddress) && (USB_ENDPOINT_TYPE_MASK & urb->HcdUrbOpenEndpoint.EndpointDescriptor->bmAttributes) == USB_ENDPOINT_TYPE_BULK) { SET_EPFLAG(endpoint, EPFLAG_DBL_BUFFER); } else { // bugbug error here? UHCD_KdPrint((1, "'WARNING: Cannot double buffer this endpoint\n")); } } #ifdef FAST_ISO if (urb->HcdUrbOpenEndpoint.HcdEndpointFlags & USBD_EP_FLAG_FAST_ISO) { if (USB_ENDPOINT_DIRECTION_OUT( urb->HcdUrbOpenEndpoint.EndpointDescriptor->bEndpointAddress) && (USB_ENDPOINT_TYPE_MASK & urb->HcdUrbOpenEndpoint.EndpointDescriptor->bmAttributes) == USB_ENDPOINT_TYPE_ISOCHRONOUS) { SET_EPFLAG(endpoint, EPFLAG_FAST_ISO); } else { // bugbug error here? UHCD_KdPrint((1, "'WARNING: Cannot fast-iso this endpoint\n")); } } #endif urb->HcdUrbOpenEndpoint.HcdEndpoint = endpoint; urb->HcdUrbOpenEndpoint.ScheduleOffset = 0; numTDs = endpoint->TDCount = UHCD_GetNumTDsPerEndoint((UCHAR) (USB_ENDPOINT_TYPE_MASK & urb->HcdUrbOpenEndpoint.EndpointDescriptor->bmAttributes)); UHCD_ASSERT(TD_LIST_SIZE(numTDs) <= UHCD_LARGE_COMMON_BUFFER_SIZE); if (endpoint->EndpointFlags & EPFLAG_DBL_BUFFER) { // do the double buffer init ntStatus = UHCD_InitializeNoDMAEndpoint(DeviceObject, endpoint); } if (NT_SUCCESS(ntStatus)) { endpoint->MaxRequests = MAX_REQUESTS(urb->HcdUrbOpenEndpoint.EndpointDescriptor, endpoint->EndpointFlags); UHCD_KdPrint((2, "'MaxRequests = %d\n", endpoint->MaxRequests)); // init transfer sequence numbers endpoint->NextXferId = 0; endpoint->CurrentXferId = 0; if (endpoint->EndpointFlags & EPFLAG_FAST_ISO) { // do the double buffer init UHCD_KdPrint((1, "'Using Fast ISO for Endpoint\n")); ntStatus = UHCD_FinishInitializeEndpoint(DeviceObject, endpoint, urb->HcdUrbOpenEndpoint.EndpointDescriptor, urb); if (NT_SUCCESS(ntStatus)) { ntStatus = UHCD_InitializeFastIsoEndpoint(DeviceObject, endpoint); } } else { for (cnt=0; cnt< endpoint->MaxRequests; cnt++) { if ((UHCD_AllocateHardwareDescriptors(DeviceObject, &endpoint->HardwareDescriptorList[cnt], endpoint->TDCount))) { PUCHAR descriptors; ULONG i; descriptors = endpoint->HardwareDescriptorList[cnt]->MemoryDescriptor->VirtualAddress; // Initialize the queue head for this endpoint // use the TD on the first list if (cnt == 0) { endpoint->QueueHead = (PHW_QUEUE_HEAD) descriptors; // save our endpoint in the QueueHead endpoint->QueueHead->Endpoint = endpoint; } // // the TDs we'll need to service this endpoint // endpoint->SlotTDList[cnt] = (PUHCD_TD_LIST) (descriptors + sizeof(HW_QUEUE_HEAD)); // BUGBUG possibly move this to allocate TD code for (i=0; i<= endpoint->TDCount; i++) { // one time init stuff // for isoch TDs. endpoint->SlotTDList[cnt]->TDs[i].Frame = 0; endpoint->SlotTDList[cnt]->TDs[i].Urb = NULL; } } else { RETHEAP(endpoint); endpoint = NULL; ntStatus = STATUS_INSUFFICIENT_RESOURCES; break; } } // for // point to first list if (endpoint) { endpoint->TDList = endpoint->SlotTDList[0]; } } } } else { // failed to alloc endpoint structure UHCD_KdTrap(("UHCD failed to alloc endpoint structure\n")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } #if DBG UHCD_KdPrint((2, "'Open Endpoint, max transfer size 0x%x \n", urb->HcdUrbOpenEndpoint.MaxTransferSize)); // transfers >64k are valid // if (urb->HcdUrbOpenEndpoint.MaxTransferSize > // 1024*64) { // // bigger than 64k, probably a bug. // UHCD_KdPrint((2, "'Open Endpoint, max transfer size 0x%x \n", // urb->HcdUrbOpenEndpoint.MaxTransferSize)); // TEST_TRAP(); // } #endif if (NT_SUCCESS(ntStatus)) { // // take the opportunity to grow our pool // in necessary // // BUGBUG possibly use max_packet size as a hint UHCD_MoreCommonBuffers(DeviceObject); URB_HEADER(urb).Status = UHCD_STATUS_PENDING_STARTIO; ntStatus = STATUS_PENDING; UHCD_KdPrint((2, "'Queue Irp To StartIo\n")); Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0; IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, 0, UHCD_StartIoCancel); } else { URB_HEADER(urb).Status = USBD_STATUS_NO_MEMORY; UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL); } break; case URB_FUNCTION_CONTROL_TRANSFER: case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: // this will activate the rolover interrupts deviceExtension->XferIdleTime = 0; case URB_FUNCTION_HCD_CLOSE_ENDPOINT: if (URB_HEADER(urb).Function == URB_FUNCTION_HCD_CLOSE_ENDPOINT) { endpoint = urb->HcdUrbCloseEndpoint.HcdEndpoint; ASSERT_ENDPOINT(endpoint); } else { //#if DBG // UHCD_KdPrint((2, "'originalTransfer Buffer = 0x%x \n", // urb->HcdUrbCommonTransfer.TransferBuffer)); // // Trash the TransferBuffer field - we only use MDLs // urb->HcdUrbCommonTransfer.TransferBuffer = (PVOID)-1; //#endif // allocate some working space and attach it to // this urb endpoint = HCD_AREA(urb).HcdEndpoint; ASSERT_ENDPOINT(endpoint); if (urb->HcdUrbCommonTransfer.TransferBufferLength > endpoint->MaxTransferSize) { ntStatus = STATUS_INVALID_PARAMETER; URB_HEADER(urb).Status = USBD_STATUS_INVALID_PARAMETER; UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL); break; } siz = COMPUTE_HCD_EXTENSION_SIZE(urb); HCD_AREA(urb).HcdExtension = GETHEAP(NonPagedPool, siz); if (HCD_AREA(urb).HcdExtension == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; URB_HEADER(urb).Status = USBD_STATUS_NO_MEMORY; UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL); break; } RtlZeroMemory(HCD_AREA(urb).HcdExtension, siz); } #ifdef ROOT_HUB if (endpoint->EndpointFlags & EPFLAG_ROOT_HUB) { // These routines will either complete // the irp or mark it pending. switch (URB_HEADER(urb).Function) { case URB_FUNCTION_CONTROL_TRANSFER: ntStatus = UHCD_RootHub_ControlTransfer(DeviceObject, Irp, urb); // note: URB and IRP may be gone break; case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: ntStatus = UHCD_RootHub_InterruptTransfer(DeviceObject, Irp, urb); // note: URB and IRP may be gone break; case URB_FUNCTION_HCD_CLOSE_ENDPOINT: ntStatus = UHCD_RootHub_CloseEndpoint(DeviceObject, Irp, urb); break; default: //BUGBUG could just complete it with an error //here // this is probably a bug in the hub driver UHCD_KdTrap(("Bogus transfer request to root hub\n")); } break; } #endif URB_HEADER(urb).Status = UHCD_STATUS_PENDING_STARTIO; ntStatus = STATUS_PENDING; UHCD_KdPrint((2, "'Queue Irp To StartIo\n")); Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0; IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, 0, UHCD_StartIoCancel); break; case URB_FUNCTION_ISOCH_TRANSFER: // // validate max transfer size // this will activate the rollover interrupts UHCD_WakeIdle(DeviceObject); endpoint = HCD_AREA(urb).HcdEndpoint; ASSERT_ENDPOINT(endpoint); // don't ref TDList endpoint->TDList = (PVOID) -1; if (urb->HcdUrbCommonTransfer.TransferBufferLength > endpoint->MaxTransferSize) { ntStatus = STATUS_INVALID_PARAMETER; URB_HEADER(urb).Status = USBD_STATUS_INVALID_PARAMETER; UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL); break; } // allocate some working space and attach it to // this urb siz = COMPUTE_HCD_EXTENSION_SIZE(urb); HCD_AREA(urb).HcdExtension = GETHEAP(NonPagedPool, siz); if (HCD_AREA(urb).HcdExtension == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; URB_HEADER(urb).Status = USBD_STATUS_NO_MEMORY; UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL); break; } RtlZeroMemory(HCD_AREA(urb).HcdExtension, siz); if (HCD_AREA(urb).HcdExtension == NULL) { UHCD_KdBreak((2, "'Unable to allocate working space for isoch transfer\n")); URB_HEADER(urb).Status = USBD_STATUS_NO_MEMORY; UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL); break; } URB_HEADER(urb).Status = UHCD_STATUS_PENDING_STARTIO; ntStatus = STATUS_PENDING; UHCD_KdPrint((2, "'Queue Irp To StartIo\n")); Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0; IoMarkIrpPending(Irp); #if DBG { UHCD_KdPrint((2, "'cf = %x", UHCD_GetCurrentFrame(DeviceObject))); } #endif if (endpoint->EndpointFlags & EPFLAG_FAST_ISO) { KIRQL irql; KeRaiseIrql(DISPATCH_LEVEL, &irql); UHCD_Transfer_StartIo(DeviceObject, Irp); KeLowerIrql(irql); } else { IoStartPacket(DeviceObject, Irp, 0, UHCD_StartIoCancel); } break; case URB_FUNCTION_GET_CURRENT_FRAME_NUMBER: // this will activate the rollover interrupts UHCD_WakeIdle(DeviceObject); urb->UrbGetCurrentFrameNumber.FrameNumber = UHCD_GetCurrentFrame(DeviceObject); LOGENTRY(LOG_MISC, 'gcfR', Irp, urb, urb->UrbGetCurrentFrameNumber.FrameNumber); URB_HEADER(urb).Status = USBD_STATUS_SUCCESS; UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL); break; case URB_FUNCTION_HCD_GET_ENDPOINT_STATE: case URB_FUNCTION_HCD_SET_ENDPOINT_STATE: URB_HEADER(urb).Status = UHCD_STATUS_PENDING_STARTIO; ntStatus = STATUS_PENDING; UHCD_KdPrint((2, "'Queue Irp To StartIo\n")); Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0; IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, 0, UHCD_StartIoCancel); break; case URB_FUNCTION_HCD_ABORT_ENDPOINT: endpoint = urb->HcdUrbAbortEndpoint.HcdEndpoint; ASSERT_ENDPOINT(endpoint); // // Mark the endpoint so that we abort all the transfers the // next time we process it. // SET_EPFLAG(endpoint, EPFLAG_ABORT_PENDING_TRANSFERS | EPFLAG_ABORT_ACTIVE_TRANSFERS); URB_HEADER(urb).Status = UHCD_STATUS_PENDING_STARTIO; ntStatus = STATUS_PENDING; UHCD_KdPrint((2, "'Queue Irp To StartIo\n")); Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0; IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, 0, UHCD_StartIoCancel); break; case URB_FUNCTION_SET_FRAME_LENGTH: { CHAR sofModify; //get the current value sofModify = (CHAR) (READ_PORT_UCHAR(SOF_MODIFY_REG(deviceExtension))); sofModify += (CHAR) urb->UrbSetFrameLength.FrameLengthDelta; WRITE_PORT_UCHAR(SOF_MODIFY_REG(deviceExtension), (UCHAR) sofModify); URB_HEADER(urb).Status = USBD_STATUS_SUCCESS; UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL); UHCD_KdPrint((2, "'Irp Completed in dispatch\n")); } break; case URB_FUNCTION_GET_FRAME_LENGTH: { CHAR sofModify; //get the current value sofModify = (CHAR) (READ_PORT_UCHAR(SOF_MODIFY_REG(deviceExtension))); urb->UrbGetFrameLength.FrameNumber = UHCD_GetCurrentFrame(DeviceObject); urb->UrbGetFrameLength.FrameLength = UHCD_12MHZ_SOF + sofModify; URB_HEADER(urb).Status = USBD_STATUS_SUCCESS; UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL); UHCD_KdPrint((2, "'Irp Completed in dispatch\n")); } break; default: UHCD_KdPrint((2, "'UHCD_URB_Dispatch -- invalid URB function (%x)\n", URB_HEADER(urb).Function)); URB_HEADER(urb).Status = USBD_STATUS_INVALID_URB_FUNCTION; ntStatus = STATUS_INVALID_PARAMETER; UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL); } UHCD_KdPrint((2, "'exit UHCD_URB_Dispatch (%x)\n", ntStatus)); return ntStatus; } VOID UHCD_StartIo( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Process URBs from the startIo routine. Arguments: DeviceObject - pointer to a device object Irp - pointer to an I/O Request Packet Return Value: --*/ { PHCD_URB urb; KIRQL irql; UHCD_KdPrint((2, "'enter UHCD_StartIo\n")); // // see if the Irp was canceled // urb = URB_FROM_IRP(Irp); IoAcquireCancelSpinLock(&irql); IoSetCancelRoutine(Irp, NULL); if (Irp->Cancel) { TEST_TRAP(); Irp->IoStatus.Status = STATUS_CANCELLED; IoReleaseCancelSpinLock(irql); IoCompleteRequest(Irp, IO_NO_INCREMENT); IoStartNextPacket(DeviceObject, FALSE); } else { if (UHCD_IS_TRANSFER(urb)) { IoSetCancelRoutine(Irp, UHCD_TransferCancel); } else { IoSetCancelRoutine(Irp, NULL); } IoReleaseCancelSpinLock(irql); switch(URB_HEADER(urb).Function) { case URB_FUNCTION_HCD_OPEN_ENDPOINT: UHCD_OpenEndpoint_StartIo(DeviceObject, Irp); break; case URB_FUNCTION_HCD_CLOSE_ENDPOINT: LOGENTRY(LOG_MISC, 'CLEP', (PUHCD_ENDPOINT)urb->HcdUrbAbortEndpoint.HcdEndpoint, 0, 0); UHCD_CloseEndpoint_StartIo(DeviceObject, Irp); break; case URB_FUNCTION_ISOCH_TRANSFER: case URB_FUNCTION_CONTROL_TRANSFER: case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: UHCD_Transfer_StartIo(DeviceObject, Irp); break; case URB_FUNCTION_HCD_ABORT_ENDPOINT: // walk the pending transfer list, since the // enpoint is halted no new transfers will // be dequeued by EndpointWorker and the only // way to clear the HALT is thru // GetSetEndpointState_StartIo so it is safe to // mess with the list. { PIRP irp; PHCD_URB urbtmp; PUHCD_ENDPOINT endpoint; PLIST_ENTRY listEntry; KIRQL irql; endpoint = (PUHCD_ENDPOINT) urb->HcdUrbAbortEndpoint.HcdEndpoint; ASSERT_ENDPOINT(endpoint); UHCD_KdPrint((2, "'Aborting endpoint %x flags = %x\n", endpoint, endpoint->EndpointFlags)); LOGENTRY(LOG_MISC, 'ABRP', endpoint, 0, 0); LOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'lck4'); while (!IsListEmpty(&endpoint->PendingTransferList)) { listEntry = RemoveHeadList(&endpoint->PendingTransferList); urbtmp = (PHCD_URB) CONTAINING_RECORD(listEntry, struct _URB_HCD_COMMON_TRANSFER, hca.HcdListEntry); UNLOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'ulk4'); UHCD_KdPrint((2, "'Aborting urb = %x\n", urbtmp)); URB_HEADER(urbtmp).Status = USBD_STATUS_CANCELED; // // complete the request // irp = HCD_AREA(urbtmp).HcdIrp; LOGENTRY(LOG_MISC, 'ABRc', endpoint, irp, 0); UHCD_CompleteIrp(DeviceObject, irp, STATUS_CANCELLED, 0, urbtmp); LOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'lck5'); } UNLOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'ulk5'); // do cleanup for fast iso if (endpoint->EndpointFlags & EPFLAG_FAST_ISO) { ULONG f; for (f=0; f < FRAME_LIST_SIZE; f++) { UHCD_CleanupFastIsoTD(DeviceObject, endpoint, f, FALSE); } } } // // we have already set the abortPendingTransfers and // abortActiveTransfers flags in the dispatch // routine. Now we need to request an interrupt // so that any active transfers will be cleaned // up. // UHCD_RequestInterrupt(DeviceObject, -2); IoStartNextPacket(DeviceObject, FALSE); URB_HEADER(urb).Status = USBD_STATUS_SUCCESS; UHCD_CompleteIrp(DeviceObject, Irp, STATUS_SUCCESS, 0, NULL); UHCD_KdPrint((2, "'UHCD Abort Endpoint Request Complete\n")); break; case URB_FUNCTION_HCD_GET_ENDPOINT_STATE: case URB_FUNCTION_HCD_SET_ENDPOINT_STATE: UHCD_GetSetEndpointState_StartIo(DeviceObject, Irp); break; default: UHCD_KdPrint((2, "'UHCD_URB_StartIo -- invalid URB function (%x)\n", URB_HEADER(urb).Function)); URB_HEADER(urb).Status = USBD_STATUS_INVALID_URB_FUNCTION; UHCD_CompleteIrp(DeviceObject, Irp, STATUS_SUCCESS, 0, NULL); } } UHCD_KdPrint((2, "'exit UHCD_StartIo\n")); } VOID UHCD_OpenEndpoint_StartIo( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Create a UHCD endpoint, this function is called from UHCD_StartIo to create a new endpoint structure. Arguments: DeviceObject - pointer to a device object Irp - pointer to an I/O Request Packet Return Value: --*/ { PHCD_URB urb; PDEVICE_EXTENSION deviceExtension; PUHCD_ENDPOINT endpoint; PUSB_ENDPOINT_DESCRIPTOR endpointDescriptor; KIRQL irql; NTSTATUS ntStatus; BOOLEAN haveBW; ULONG offset; UHCD_KdPrint((2, "'enter UHCD_OpenEndpoint_StartIo\n")); deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; // // extract a pointer to the URB // urb = URB_FROM_IRP(Irp); // // make sure the length of the urb is what we expect // if (urb->HcdUrbOpenEndpoint.Length != sizeof(struct _URB_HCD_OPEN_ENDPOINT)) { URB_HEADER(urb).Status = USBD_STATUS_INVALID_PARAMETER; ntStatus = STATUS_INVALID_PARAMETER; goto UHCD_OpenEndpoint_StartIo_Done; } // // information about the endpoint comes from the USB endpoint // descriptor passed in the URB. // endpointDescriptor = urb->HcdUrbOpenEndpoint.EndpointDescriptor; URB_HEADER(urb).Status = USBD_STATUS_SUCCESS; // // Allocate resources for the endpoint, this includes an endpoint // handle that will be passed to us in subsequent transfer requests // ntStatus = STATUS_INSUFFICIENT_RESOURCES; endpoint = urb->HcdUrbOpenEndpoint.HcdEndpoint; UHCD_ASSERT(endpoint != NULL); ASSERT_ENDPOINT(endpoint); if (endpoint) { // // initialize endpoint structures, state variables, queue head, ... // // bugbug -- may not need to call this for fastIiso ntStatus = UHCD_FinishInitializeEndpoint(DeviceObject, endpoint, endpointDescriptor, urb); if (!NT_SUCCESS(ntStatus)) { RETHEAP(endpoint); goto UHCD_OpenEndpoint_StartIo_Done; } UHCD_KdPrint((2, "'Open Endpoint:\n")); UHCD_KdPrint((2, "'Type = (%d) \n", endpoint->Type)); #if DBG switch (endpoint->Type) { case USB_ENDPOINT_TYPE_CONTROL: UHCD_KdPrint((2, "'-control\n")); break; case USB_ENDPOINT_TYPE_ISOCHRONOUS: UHCD_KdPrint((2, "'-iso\n")); break; case USB_ENDPOINT_TYPE_BULK: UHCD_KdPrint((2, "'-bulk\n")); break; case USB_ENDPOINT_TYPE_INTERRUPT: UHCD_KdPrint((2, "'-interrupt\n")); } if (USB_ENDPOINT_DIRECTION_IN(endpointDescriptor->bEndpointAddress)) { UHCD_KdPrint((2, "'IN\n")); } else { UHCD_KdPrint((2, "'OUT\n")); } #endif UHCD_KdPrint((2, "'EP Address = %d\n", endpoint->EndpointAddress)); UHCD_KdPrint((2, "'DEV Address = %d\n", endpoint->DeviceAddress)); UHCD_KdPrint((2, "'MaxPacket = %d\n", endpoint->MaxPacketSize)); UHCD_KdPrint((2, "'Interval: Requested = %d, Selected = %d\n", endpointDescriptor->bInterval, endpoint->Interval)); // // Now attempt to allocate the bandwidth we'll need to // open this endpoint // if (endpoint->Type == USB_ENDPOINT_TYPE_INTERRUPT) { ULONG i; // check bw available in all locations and // pick the least loaded frame offset = 0; for (i=0; i< endpoint->Interval; i++) { if (deviceExtension->BwTable[i] > deviceExtension->BwTable[offset]) { offset = i; } } haveBW = UHCD_AllocateBandwidth(DeviceObject, endpoint, offset); if (!haveBW) { // // could not use the least loaded frame, just grab the // first frame we can fit in. // for (offset=0; offsetInterval; offset++) { haveBW = UHCD_AllocateBandwidth(DeviceObject, endpoint, offset); if (haveBW) { UHCD_KdPrint((2, "'using offset %d\n", offset)); break; } } } urb->HcdUrbOpenEndpoint.ScheduleOffset = offset; } else { offset = 0; haveBW = UHCD_AllocateBandwidth(DeviceObject, endpoint, offset); } if (!haveBW) { // no offset ULONG cnt; // // insufficient bandwidth to open this // endpoint. // URB_HEADER(urb).Status = USBD_STATUS_NO_BANDWIDTH; UHCD_KdPrint((0, "'warning: no bandwidth for endpoint\n")); for (cnt=0; cnt< endpoint->MaxRequests; cnt++) { UHCD_FreeHardwareDescriptors(DeviceObject, endpoint->HardwareDescriptorList[cnt]); } RETHEAP(endpoint); goto UHCD_OpenEndpoint_StartIo_Done; } if (endpoint->EndpointFlags & EPFLAG_FAST_ISO) { goto UHCD_OpenEndpoint_StartIo_Done; } // // put the endpoint in to our list of active endpoints // // BUGBUG may want to wait for the first transfer to // do this. // LOCK_ENDPOINT_LIST(deviceExtension, irql); if (deviceExtension->EndpointListBusy) { // if the endpoint list is busy we have to put the endpoint // on a lookaside list so that the ISRDPC can add it later InsertHeadList(&deviceExtension->EndpointLookAsideList, &endpoint->ListEntry); } else { InsertHeadList(&deviceExtension->EndpointList, &endpoint->ListEntry); } UNLOCK_ENDPOINT_LIST(deviceExtension, irql); // // Put this Queue Head in the Schedule. // // // This routine will insert the queue head in the proper place // in the schedule and add the endpoint to the endpoint list // so that completed transfers will be detected. // KeAcquireSpinLock(&deviceExtension->HcScheduleSpin, &irql); UHCD_InsertQueueHeadInSchedule(DeviceObject, endpoint, endpoint->QueueHead, offset); // no offset // clear the idle flag just in case it got set CLR_EPFLAG(endpoint, EPFLAG_IDLE); KeReleaseSpinLock(&deviceExtension->HcScheduleSpin, irql); // // return the endpoint handle // urb->HcdUrbOpenEndpoint.HcdEndpoint = endpoint; ntStatus = STATUS_SUCCESS; } /* if endpoint */ // // Complete the IRP, status is in the status field of the URB // UHCD_KdPrint((2, "'exit UHCD_OpenEndpoint_StartIo (URB STATUS = %x)\n", URB_HEADER(urb).Status )); UHCD_OpenEndpoint_StartIo_Done: #if DBG // // sanity check our buffer pools // // UHCD_BufferPoolCheck(DeviceObject); #endif IoStartNextPacket(DeviceObject, FALSE); UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL); return; } VOID UHCD_CloseEndpoint_StartIo( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Free a UHCD endpoint, if there are any pending transfers for this endpoint this routine should fail. Arguments: DeviceObject - pointer to a device object Irp - pointer to an I/O Request Packet Return Value: --*/ { PHCD_URB urb; PUHCD_ENDPOINT endpoint; BOOLEAN outstandingTransfers = FALSE; PDEVICE_EXTENSION deviceExtension; ULONG i; KIRQL irql; BOOLEAN removed; UHCD_KdPrint((2, "'enter UHCD_CloseEndpoint_StartIo\n")); deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; urb = URB_FROM_IRP(Irp); endpoint = urb->HcdUrbCloseEndpoint.HcdEndpoint; ASSERT_ENDPOINT(endpoint); if (endpoint->EndpointFlags & EPFLAG_FAST_ISO) { UHCD_UnInitializeFastIsoEndpoint(DeviceObject, endpoint); UHCD_FreeBandwidth(DeviceObject, endpoint, endpoint->Offset); } // // if there are any pending transfers fail this request // LOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'lck4'); // // Do we have any transfers pending? // outstandingTransfers = !IsListEmpty(&endpoint->PendingTransferList); for (i=0; !outstandingTransfers && i < endpoint->MaxRequests; i++) { // // no outstanding transfers in the queue, check the active list // -- if some transfers get retired while we walk the list that // is OK. // if (endpoint->ActiveTransfers[i] != NULL) { outstandingTransfers = TRUE; } } UNLOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'ulk4'); if (outstandingTransfers) { // // If we have outstanding transfers then we fail the // request // URB_HEADER(urb).Status = USBD_STATUS_ERROR_BUSY; } else { // // Remove the endpoint Queue from the schedule // KeAcquireSpinLock(&deviceExtension->HcScheduleSpin, &irql); removed = UHCD_RemoveQueueHeadFromSchedule(DeviceObject, endpoint, endpoint->QueueHead, TRUE); KeReleaseSpinLock(&deviceExtension->HcScheduleSpin, irql); // stop 'NoDMA' transfers if (endpoint->EndpointFlags & EPFLAG_DBL_BUFFER) { UHCD_StopNoDMATransfer(DeviceObject, endpoint); } // // Put the endpoint on a queue to be freed at after // the next frame has executed // // At this point the hardware links have been updated to remove this // endpoint. // // we note the frame when it is safe to retire the endpoint so that // the queue head may be freed safely by the ISR DPC routine next time // we take an interrupt. endpoint->FrameToClose = UHCD_GetCurrentFrame(DeviceObject)+2; // BUGBUG this needs to be protected from the ISR DPC // routine where these things are actually freed. // queue it to be released if (removed) { InsertTailList(&deviceExtension->ClosedEndpointList, &endpoint->ListEntry); } URB_HEADER(urb).Status = USBD_STATUS_SUCCESS; } IoStartNextPacket(DeviceObject, FALSE); UHCD_CompleteIrp(DeviceObject, Irp, STATUS_SUCCESS, 0, NULL); UHCD_KdPrint((2, "'exit UHCD_CloseEndpoint_StartIo\n")); } VOID UHCD_InsertQueueHeadInSchedule( IN PDEVICE_OBJECT DeviceObject, IN PUHCD_ENDPOINT Endpoint, IN PHW_QUEUE_HEAD QueueHead, IN ULONG Offset ) /*++ Routine Description: Inserts an initialized queue head into the schedule Queue head schedule looks like this: PQH = persistant queue head CQH = control Queue Head IQH = Interrupt Queue Head BQH = Bulk Queue Head The persistant queue head software links look like this: |>---->---->-| | <- <- | prev BQH PQH CQH | -> -> | next |<----<----<-| IQH->IQH->PQH Hardware links look like this: PQH->CQH->BQH->| (reclimaton on) |-<-| or PQH->CQH->BQH->| (reclimation off) |<-T-| (T BIT SET) Iso/Interrupt hardware links: ISO->IQH->PQH Arguments: DeviceObject - device object for this controller. Endpoint - endpoint this Queue Head belongs to. QueueHead - queue head to insert in schedule. Return Value: --*/ { PDEVICE_EXTENSION deviceExtension; PHW_QUEUE_HEAD persistantQueueHead, queueHeadForFrame, nextQueueHead, prevQueueHead; #ifdef RECLAIM_BW PHW_QUEUE_HEAD firstBulkQueueHead; #endif ULONG i, interval; BOOLEAN fixFrameList = FALSE; UHCD_ASSERT(!(Endpoint->EndpointFlags & EPFLAG_ED_IN_SCHEDULE)); UHCD_KdPrint((2, "'enter InsertQueueHeadInSchedule\n")); ASSERT_ENDPOINT(Endpoint); ASSERT_QUEUE_HEAD(QueueHead); deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; persistantQueueHead = deviceExtension->PersistantQueueHead; switch(Endpoint->Type) { case USB_ENDPOINT_TYPE_CONTROL: LOGENTRY(LOG_MISC, 'Cep+', Endpoint, 0, DeviceObject); // // before // // a b // -PQH<->CQH- // |---<->---| // // after // // a b // -PQH<->CQH(new)<->CQH- // |-------<->----------| // // insert in list QueueHead->Next = persistantQueueHead->Next; //point to b QueueHead->Prev = persistantQueueHead; //point to a QueueHead->Next->Prev = QueueHead; // item b points back to new // PQH points to the new element QueueHead->Prev->Next = QueueHead; // item a points to new // Fix up hardware links QueueHead->HW_HLink = persistantQueueHead->HW_HLink; // we are in the chain just after the Persistant Queue Head // this line activates the QH persistantQueueHead->HW_HLink = QueueHead->PhysicalAddress; break; case USB_ENDPOINT_TYPE_INTERRUPT: LOGENTRY(LOG_MISC, 'Iep+', Endpoint, QueueHead, DeviceObject); QueueHead->Next = 0; QueueHead->Prev = 0; interval = Endpoint->Interval; UHCD_ASSERT(Offset < interval); UHCD_ASSERT(interval != 0); LOGENTRY(LOG_MISC, 'Iep+', Endpoint, interval, &deviceExtension->InterruptSchedule[0]); // MAX_INTERVAL is the maximum polling interval we support in UHCD (power of 2). // the requested polling intervals is always rounded to the next lowest power of 2. // We keep an array (InterruptSchedule) of MAX_INTERVAL with the virtual addresses // of the queue heads in the schedule. The size of InterruptSchedule is always a // multiple of MAX_FRAME. i.e. InterruptSchedule contains the virtual addresses of // queue heads for the first MAX_INTERVAL frames in the schedule. UHCD_ASSERT(interval<=MAX_INTERVAL); UHCD_ASSERT(OffsetInterruptSchedule[i]; LOGENTRY(LOG_MISC, 'Iqhf', 0, //queueHeadForFrame->Endpoint->Interval, queueHeadForFrame, i); // find the appropriate place to add ourselves if (queueHeadForFrame == persistantQueueHead || interval >= queueHeadForFrame->Endpoint->Interval) { // if the first entry is the persistant queue head or our polling // interval is greater or equal than this queue head then we just // add in front of this one, we become the root node for this // frame. LOGENTRY(LOG_MISC, 'I>SC', 0, QueueHead, i); deviceExtension->InterruptSchedule[i] = QueueHead; fixFrameList = TRUE; if (QueueHead->Next == 0) { QueueHead->Next = queueHeadForFrame; // update hardware link here QueueHead->HW_HLink = queueHeadForFrame->PhysicalAddress; } else { // ounce the next pointer is updated // it should never change UHCD_ASSERT(QueueHead->Next == queueHeadForFrame); } } else { // if our polling interval is less than the current queue // head we need to insert in the proper position LOGENTRY(LOG_MISC, 'INext; } while (nextQueueHead != persistantQueueHead && nextQueueHead->Endpoint->Interval > interval); LOGENTRY(LOG_MISC, 'INext == 0) { QueueHead->Next = nextQueueHead; // update hardware link here QueueHead->HW_HLink = nextQueueHead->PhysicalAddress; } else { UHCD_ASSERT(QueueHead->Next == nextQueueHead || nextQueueHead == QueueHead); } prevQueueHead->Next = QueueHead; // update hardware link here prevQueueHead->HW_HLink = QueueHead->PhysicalAddress; } // // repeat the process until we // have visited all possible nodes for this queue head // } // now update the physical frame list based on the virtual list // if we have modified it. if (fixFrameList) { UHCD_CopyInterruptScheduleToFrameList(DeviceObject); } break; case USB_ENDPOINT_TYPE_BULK: // // before // // b a // -BQH<->PQH- // |---<->---| // // after // // b a // -BQH<->BQH(new)<->PQH- // |-------<->----------| // // We need to add this endpoint to the tail of the // persistant queue // LOGENTRY(LOG_MISC, 'Bep+', Endpoint, 0, DeviceObject); // set up queue head fields UHCD_KdPrint((2, "'bulk QH = %x, %x\n", QueueHead, Endpoint)); // insert in list QueueHead->Next = persistantQueueHead; // point to a QueueHead->Prev = persistantQueueHead->Prev; // point to b // first item points back to us QueueHead->Prev->Next = QueueHead; // item b points to new // head points to the element // select the old tail element prevQueueHead = persistantQueueHead->Prev; // remember b persistantQueueHead->Prev = QueueHead; // item a points to new #ifdef RECLAIM_BW // // Fix up hardware links // // BUGBUG // NOTE: we are only reclaiming bandwidth for bulk right // now. // if (deviceExtension->SteppingVersion == UHCD_B0_STEP) { //TEST_TRAP(); // // BW reclimation, point back to the first bulk queue head // with no T bit set // // walk the list and find the first bulk // queue head firstBulkQueueHead = persistantQueueHead; do { PUHCD_ENDPOINT endpoint; endpoint = firstBulkQueueHead->Endpoint; if (endpoint && endpoint->Type == USB_ENDPOINT_TYPE_BULK) { break; } firstBulkQueueHead = firstBulkQueueHead->Next; } while (firstBulkQueueHead != persistantQueueHead); UHCD_ASSERT(firstBulkQueueHead != persistantQueueHead); QueueHead->HW_HLink = firstBulkQueueHead->PhysicalAddress; deviceExtension->HcFlags |= HCFLAG_BWRECLIMATION_ENABLED; } else { // Fix up hardware links // points to the control queue head with T bit set QueueHead->HW_HLink = prevQueueHead->HW_HLink; UHCD_ASSERT(QueueHead->HW_HLink & UHCD_CF_TERMINATE); } #else // Fix up hardware links // points to the control queue head with T bit set QueueHead->HW_HLink = prevQueueHead->HW_HLink; UHCD_ASSERT(QueueHead->HW_HLink & UHCD_CF_TERMINATE); #endif // we are in the chain just before the PersistantQueueHead // this line activates the QH prevQueueHead->HW_HLink = QueueHead->PhysicalAddress; break; case USB_ENDPOINT_TYPE_ISOCHRONOUS: LOGENTRY(LOG_MISC, 'Sep+', Endpoint, 0, DeviceObject); break; default: // invalid endpoint type, probably a bug UHCD_KdTrap( ("UHCD_InsertQueueHeadInSchedule inavlid endpoint type\n")); } SET_EPFLAG(Endpoint, EPFLAG_ED_IN_SCHEDULE); UHCD_KdPrint((2, "'exit InsertQueueHeadInSchedule\n")); return; } BOOLEAN UHCD_RemoveQueueHeadFromSchedule( IN PDEVICE_OBJECT DeviceObject, IN PUHCD_ENDPOINT Endpoint, IN PHW_QUEUE_HEAD QueueHead, IN BOOLEAN RemoveFromEPList ) /*++ Routine Description: Removes a queue head from the schedule Arguments: DeviceObject - device object for this controller. Endpoint - endpoint this Queue Head belongs to. QueueHead - queue head to remove. Return Value: returns TRUE if EP was removed from ep list --*/ { PDEVICE_EXTENSION deviceExtension; PHW_QUEUE_HEAD persistantQueueHead, queueHeadForFrame, nextQueueHead, prevQueueHead; BOOLEAN fixFrameList = FALSE; ULONG i; HW_DESCRIPTOR_PHYSICAL_ADDRESS physicalAddress; KIRQL irql; BOOLEAN removed = FALSE; UHCD_KdPrint((2, "'enter RemoveQueueHeadFromSchedule\n")); ASSERT_ENDPOINT(Endpoint); if (Endpoint->EndpointFlags & EPFLAG_FAST_ISO) { // nothing to remove return FALSE; } ASSERT_QUEUE_HEAD(QueueHead); deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; persistantQueueHead = deviceExtension->PersistantQueueHead; // // Remove the Queue Head from the endpoint list // if (RemoveFromEPList) { LOCK_ENDPOINT_LIST(deviceExtension, irql); if (deviceExtension->EndpointListBusy) { // mark the entry so we can remove it later SET_EPFLAG(Endpoint, EPFLAG_EP_CLOSED); } else { RemoveEntryList(&Endpoint->ListEntry); removed = TRUE; } UNLOCK_ENDPOINT_LIST(deviceExtension, irql); } if (!(Endpoint->EndpointFlags & EPFLAG_ED_IN_SCHEDULE)) { LOGENTRY(LOG_MISC, 'NISc', Endpoint, 0, DeviceObject); return removed; } switch(Endpoint->Type) { case USB_ENDPOINT_TYPE_CONTROL: case USB_ENDPOINT_TYPE_BULK: // set up queue head fields prevQueueHead = QueueHead->Prev; nextQueueHead = QueueHead->Next; // unlink software links prevQueueHead->Next = QueueHead->Next; nextQueueHead->Prev = QueueHead->Prev; if ((QueueHead->HW_HLink & UHCD_DESCRIPTOR_PTR_MASK) == (QueueHead->PhysicalAddress & UHCD_DESCRIPTOR_PTR_MASK)) { // // Queue head points to itself, this means it is // the only bulk queue in the list. // // This will only happen if we have BW reclaimation // is enabled. // physicalAddress = deviceExtension->PersistantQueueHead->PhysicalAddress; SET_T_BIT(physicalAddress); UHCD_ASSERT(physicalAddress & UHCD_CF_QUEUE); prevQueueHead->HW_HLink = physicalAddress; } else { // Fix up hardware link prevQueueHead->HW_HLink = QueueHead->HW_HLink; } break; case USB_ENDPOINT_TYPE_INTERRUPT: // // Brute force method: // Walk every frame in the InterruptSchedule and update // any node that references this queue head. // for (i=0; iInterruptSchedule[i]; if (queueHeadForFrame == QueueHead) { // Queue Head was root node for this frame deviceExtension->InterruptSchedule[i] = QueueHead->Next; fixFrameList = TRUE; } else { while (queueHeadForFrame != persistantQueueHead && queueHeadForFrame->Endpoint->Interval >= QueueHead->Endpoint->Interval) { if (queueHeadForFrame->Next == QueueHead) { // found a link to our queue head, // remove it queueHeadForFrame->Next = QueueHead->Next; // unlink from Hardware Queue queueHeadForFrame->HW_HLink = QueueHead->HW_HLink; } queueHeadForFrame = queueHeadForFrame->Next; } } if (fixFrameList) { UHCD_CopyInterruptScheduleToFrameList(DeviceObject); } } break; case USB_ENDPOINT_TYPE_ISOCHRONOUS: LOGENTRY(LOG_MISC, 'Sep-', Endpoint, 0, DeviceObject); break; default: // Invalid endpoint type, probably a bug UHCD_KdTrap( ("UHCD_RemoveQueueHeadFromSchedule inavlid endpoint type\n")); } CLR_EPFLAG(Endpoint, EPFLAG_ED_IN_SCHEDULE); UHCD_KdPrint((2, "'exit RemoveQueueHeadFromSchedule\n")); return removed; } VOID UHCD_CopyInterruptScheduleToFrameList( IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: Transfers the virtual interrupt schedule to the frame list Arguments: DeviceObject - device object for this controller. Return Value: None. --*/ { PDEVICE_EXTENSION deviceExtension; ULONG i; deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; for (i=0; i < FRAME_LIST_SIZE; i++) { if (i == 0 || i == FRAME_LIST_SIZE-1) deviceExtension->TriggerTDList->TDs[i == 0 ? 0 : 1].HW_Link = deviceExtension->InterruptSchedule[i % MAX_INTERVAL]->PhysicalAddress; else { ULONG currentTdCopy, currentTd; PUHCD_ENDPOINT endpoint; currentTd = *( ((PULONG) (deviceExtension->FrameListVirtualAddress)+i)); currentTdCopy = *( ((PULONG) (deviceExtension->FrameListCopyVirtualAddress)+i)); endpoint = UHCD_GetLastFastIsoEndpoint(DeviceObject); // have fast iso? if (endpoint) { PFAST_ISO_DATA fastIsoData; PHW_TRANSFER_DESCRIPTOR transferDescriptor; // fast iso TDs are present, we will need to insert // the interrupt schedule after these TDs. fastIsoData = &endpoint->FastIsoData; transferDescriptor = (PHW_TRANSFER_DESCRIPTOR) (fastIsoData->IsoTDListVa + (i*32)); transferDescriptor->HW_Link = deviceExtension->InterruptSchedule[i % MAX_INTERVAL]->PhysicalAddress; } else { // no fast iso -- just update the schedule *( ((PULONG) (deviceExtension->FrameListCopyVirtualAddress)+i) ) = deviceExtension->InterruptSchedule[i % MAX_INTERVAL]->PhysicalAddress; // if the currentTd == the copy then we don't have any iso // tds in the schedule so it is safe to update the schedule directly if (currentTd == currentTdCopy) { *( ((PULONG) (deviceExtension->FrameListVirtualAddress)+i) ) = deviceExtension->InterruptSchedule[i % MAX_INTERVAL]->PhysicalAddress; } } } } } VOID UHCD_StartIoCancel( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Arguments: Return Value: --*/ { PHCD_URB urb; // // Irp has not been processed by StartIo yet // LOGENTRY(LOG_MISC, 'sioC', Irp, 0, DeviceObject); if (DeviceObject->CurrentIrp == Irp) { LOGENTRY(LOG_MISC, 'curI', Irp, 0, DeviceObject); IoReleaseCancelSpinLock(Irp->CancelIrql); } else { LOGENTRY(LOG_MISC, 'Ncur', Irp, 0, DeviceObject); if (KeRemoveEntryDeviceQueue(&DeviceObject->DeviceQueue, &Irp->Tail.Overlay.DeviceQueueEntry)) { LOGENTRY(LOG_MISC, 'YDVQ', Irp, 0, DeviceObject); TEST_TRAP(); urb = (PHCD_URB) URB_FROM_IRP(Irp); IoReleaseCancelSpinLock(Irp->CancelIrql); while (urb) { URB_HEADER(urb).Status = USBD_STATUS_CANCELED; if (UHCD_IS_TRANSFER(urb)) { urb = urb->HcdUrbCommonTransfer.UrbLink; } else { break; } } Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; USBD_CompleteRequest(Irp, IO_NO_INCREMENT); } else { LOGENTRY(LOG_MISC, 'NDVQ', Irp, 0, DeviceObject); TEST_TRAP(); IoReleaseCancelSpinLock(Irp->CancelIrql); } } } VOID UHCD_GetSetEndpointState_StartIo( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Change or report state of an endpoint Arguments: DeviceObject - pointer to a device object Irp - pointer to an I/O Request Packet Return Value: --*/ { PUHCD_ENDPOINT endpoint; PHCD_URB urb; BOOLEAN outstandingTransfers; ULONG i; KIRQL irql; urb = (PHCD_URB) URB_FROM_IRP(Irp); endpoint = (PUHCD_ENDPOINT) urb->HcdUrbEndpointState.HcdEndpoint; ASSERT_ENDPOINT(endpoint); // // Do we have any transfers pending? // irql = KeGetCurrentIrql(); LOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'lck5'); outstandingTransfers = !IsListEmpty(&endpoint->PendingTransferList); #if DBG if (outstandingTransfers) { UHCD_KdPrint((2, "'GET_ENDPOINT_STATE ep has pending transfers\n")); } #endif for (i=0; !outstandingTransfers && i < endpoint->MaxRequests; i++) { // // no outstanding transfers in the queue, check the active list // -- if some transfers get retired while we walk the list that // is OK. // if (endpoint->ActiveTransfers[i] != NULL) { UHCD_KdPrint((2, "'GETSET_ENDPOINT_STATE ep has active transfers\n")); outstandingTransfers = TRUE; } } UNLOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'ulk5'); switch (urb->HcdUrbEndpointState.Function) { case URB_FUNCTION_HCD_GET_ENDPOINT_STATE: urb->HcdUrbEndpointState.HcdEndpointState = 0; if (endpoint->EndpointFlags & EPFLAG_HOST_HALTED) { UHCD_KdPrint((2, "'GET_ENDPOINT_STATE host halted\n")); urb->HcdUrbEndpointState.HcdEndpointState |= HCD_ENDPOINT_HALTED; } if (outstandingTransfers) { urb->HcdUrbEndpointState.HcdEndpointState |= HCD_ENDPOINT_TRANSFERS_QUEUED; } URB_HEADER(urb).Status = USBD_STATUS_SUCCESS; UHCD_KdPrint((2, "'GET_ENDPOINT_STATE state = %x\n", urb->HcdUrbEndpointState.HcdEndpointState)); break; case URB_FUNCTION_HCD_SET_ENDPOINT_STATE: if (!outstandingTransfers) { LOGENTRY(LOG_MISC, 'cla2', endpoint, 0, 0); CLR_EPFLAG(endpoint, EPFLAG_ABORT_PENDING_TRANSFERS | EPFLAG_ABORT_ACTIVE_TRANSFERS); } UHCD_KdPrint((2, "'Set Enpoint State flags = %x\n", endpoint->EndpointFlags)); if (endpoint->EndpointFlags & (EPFLAG_ABORT_ACTIVE_TRANSFERS | EPFLAG_ABORT_PENDING_TRANSFERS)) { //fail the request IoStartNextPacket(DeviceObject, FALSE); // let USBD map the error for us URB_HEADER(urb).Status = USBD_STATUS_ERROR_BUSY; UHCD_CompleteIrp(DeviceObject, Irp, STATUS_SUCCESS, 0, NULL); return; } if (endpoint->EndpointFlags & EPFLAG_DBL_BUFFER) { // stop streaming data UHCD_StopNoDMATransfer(DeviceObject, endpoint); } // // restore virgin status to the pipe // SET_EPFLAG(endpoint, EPFLAG_VIRGIN); // // set the data toggle back to 0 // if (urb->HcdUrbEndpointState.HcdEndpointState & HCD_ENDPOINT_RESET_DATA_TOGGLE) { endpoint->DataToggle = 0; } if (!(urb->HcdUrbEndpointState.HcdEndpointState & HCD_ENDPOINT_HALTED)) { // // halt bit cleared, reset the endpoint. // LOGENTRY(LOG_MISC, 'cla3', endpoint, 0, 0); CLR_EPFLAG(endpoint, EPFLAG_ABORT_PENDING_TRANSFERS | EPFLAG_ABORT_ACTIVE_TRANSFERS | EPFLAG_HOST_HALTED); // // Start any transfers in the pending queue // if (endpoint->EndpointFlags & EPFLAG_DBL_BUFFER) { // transfers will re-start on the next interrupt UHCD_RequestInterrupt(DeviceObject, -2); } else { UHCD_EndpointWorker(DeviceObject, endpoint); } } URB_HEADER(urb).Status = USBD_STATUS_SUCCESS; break; default: // unknown command, probably a bug UHCD_KdTrap(("Bogus Endpoint state command\n")); } IoStartNextPacket(DeviceObject, FALSE); UHCD_CompleteIrp(DeviceObject, Irp, STATUS_SUCCESS, 0, NULL); } NTSTATUS UHCD_FinishInitializeEndpoint( IN PDEVICE_OBJECT DeviceObject, IN PUHCD_ENDPOINT Endpoint, IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor, IN PHCD_URB Urb ) /*++ Routine Description: Change or report state of an endpoint Arguments: Endpoint - endpoint structure to initilaize EndpointDescriptor - pointer to the USB endpoint descriptor for this endpoint. Urb - urb associated with the open request Return Value: --*/ { UCHAR tmp; ULONG i; NTSTATUS ntStatus = STATUS_SUCCESS; // note that some fields are already initilaized ASSERT_ENDPOINT(Endpoint); Endpoint->Type = USB_ENDPOINT_TYPE_MASK & EndpointDescriptor->bmAttributes; Endpoint->EndpointAddress = EndpointDescriptor->bEndpointAddress; Endpoint->MaxPacketSize = EndpointDescriptor->wMaxPacketSize; Endpoint->DeviceAddress = (UCHAR) Urb->HcdUrbOpenEndpoint.DeviceAddress; Endpoint->LastPacketDataToggle = Endpoint->DataToggle = 0; Endpoint->Interval = 0; Endpoint->IdleTime = 0; SET_EPFLAG(Endpoint, EPFLAG_VIRGIN); SET_EPFLAG(Endpoint, EPFLAG_INIT); Endpoint->MaxTransferSize = Urb->HcdUrbOpenEndpoint.MaxTransferSize; // No DMA endpoint ? #if DBG if (Endpoint->EndpointFlags & EPFLAG_DBL_BUFFER) { // client idicates that transfers will be mostly short // in this case we will turn off short packet detect // and double buffer all transfers to reduce the overhead of // having to program each transfer to the hardware separately UHCD_KdPrint((1, "'Client requesting double buffering EP = %x\n", Endpoint)); } #endif #if DBG if (!(Endpoint->EndpointFlags & EPFLAG_ROOT_HUB)) { UHCD_ASSERT(Endpoint->TDCount == UHCD_GetNumTDsPerEndoint(Endpoint->Type)); } #endif if (Urb->HcdUrbOpenEndpoint.HcdEndpointFlags & USBD_EP_FLAG_LOWSPEED) { SET_EPFLAG(Endpoint, EPFLAG_LOWSPEED); } if (Urb->HcdUrbOpenEndpoint.HcdEndpointFlags & USBD_EP_FLAG_NEVERHALT) { SET_EPFLAG(Endpoint, EPFLAG_NO_HALT); } KeInitializeSpinLock(&Endpoint->ActiveListSpin); KeInitializeSpinLock(&Endpoint->PendingListSpin); #if DBG Endpoint->AccessPendingList = 0; Endpoint->AccessActiveList = 0; #endif UHCD_KdPrint((2, "'MaxRequests = %d\n", Endpoint->MaxRequests)); UHCD_ASSERT(Endpoint->MaxRequests == MAX_REQUESTS(EndpointDescriptor, Endpoint->EndpointFlags)); // Select the highest interval we support <= the requested interval. // note: an interval of zero gets a oeriod of MAX_INTERVAL tmp = EndpointDescriptor->bInterval; Endpoint->Interval = MAX_INTERVAL; if (EndpointDescriptor->bInterval > MAX_INTERVAL || EndpointDescriptor->bInterval == 0) { tmp |= MAX_INTERVAL; } while ((Endpoint->Interval & tmp) == 0) { Endpoint->Interval >>= 1; } // // make sure that iso endpoints have an // interval of 1 // if (Endpoint->Type == USB_ENDPOINT_TYPE_ISOCHRONOUS) { Endpoint->Interval = 1; //iso endpoints have a period of 1 } InitializeListHead(&Endpoint->PendingTransferList); for (i=0; iActiveTransfers[i] = NULL; } return ntStatus; } USHORT UHCD_GetNumTDsPerEndoint( IN UCHAR EndpointType ) /*++ Routine Description: Return the number of TDs to use for this endpoint based on type Arguments: Return Value: --*/ { // use max TDs always for bulk to get max thru-put regardless // of packet size. // Historical Note: // this is a change from Win98gold and Win98se, // originally we only enabled this if MAX_PACKET was 64 to save // memory for slower devices -- but vendors bitched about it so // now we enable it regardless of packet size. if (EndpointType == USB_ENDPOINT_TYPE_ISOCHRONOUS || EndpointType == USB_ENDPOINT_TYPE_BULK) { return MAX_TDS_PER_ENDPOINT; } else { return MIN_TDS_PER_ENDPOINT; } } VOID UHCD_BW_Reclimation( IN PDEVICE_OBJECT DeviceObject, IN BOOLEAN Enable ) /*++ Routine Description: Turn on/off BW reclimation for the Bulk Queues Arguments: Return Value: None. --*/ { PDEVICE_EXTENSION deviceExtension; PHW_QUEUE_HEAD persistantQueueHead, firstBulkQueueHead, lastBulkQueueHead; deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; persistantQueueHead = deviceExtension->PersistantQueueHead; if ((deviceExtension->HcFlags & HCFLAG_BWRECLIMATION_ENABLED) == Enable) { // no state change just return; return; } LOGENTRY(LOG_MISC, 'BRCL', deviceExtension, Enable, DeviceObject); // // BW reclimation, point back to the first bulk queue head // with no T bit set // // walk the list and find the first bulk // queue head firstBulkQueueHead = persistantQueueHead; do { PUHCD_ENDPOINT endpoint; endpoint = firstBulkQueueHead->Endpoint; if (endpoint && endpoint->Type == USB_ENDPOINT_TYPE_BULK) { break; } firstBulkQueueHead = firstBulkQueueHead->Next; } while (firstBulkQueueHead != persistantQueueHead); if (firstBulkQueueHead != persistantQueueHead) { // no bulk endpoints PHW_QUEUE_HEAD next; PUHCD_ENDPOINT endpoint; lastBulkQueueHead = firstBulkQueueHead; do { next = lastBulkQueueHead->Next; endpoint = next->Endpoint; if (endpoint == NULL || endpoint->Type != USB_ENDPOINT_TYPE_BULK) { break; } lastBulkQueueHead = next; } while (1); if (Enable) { //clear the T-bit to enable reclimation LOGENTRY(LOG_MISC, 'BRC+', lastBulkQueueHead, 0, DeviceObject); CLEAR_T_BIT(lastBulkQueueHead->HW_HLink); deviceExtension->HcFlags |= HCFLAG_BWRECLIMATION_ENABLED; } else { //set the T-bit to disable reclimation LOGENTRY(LOG_MISC, 'BRC-', lastBulkQueueHead, 0, DeviceObject); SET_T_BIT(lastBulkQueueHead->HW_HLink); deviceExtension->HcFlags &= ~HCFLAG_BWRECLIMATION_ENABLED; } } }