/*++ Copyright (c) 1999 Microsoft Corporation Module Name: dma.c Abstract: functions for processing ennpoints that use DMA to process transfers Environment: kernel mode only Notes: Revision History: 6-20-99 : created --*/ #include "common.h" #ifdef ALLOC_PRAGMA #endif // non paged functions // USBPORT_DmaEndpointWorker // USBPORT_DmaEndpointPaused // USBPORT_DmaEndpointActive MP_ENDPOINT_STATE USBPORT_DmaEndpointActive( PDEVICE_OBJECT FdoDeviceObject, PHCD_ENDPOINT Endpoint ) /*++ Routine Description: process the active state returns the next needed state if we discover the need for a transition Arguments: Return Value: None. --*/ { MP_ENDPOINT_STATE currentState; PLIST_ENTRY listEntry; MP_ENDPOINT_STATE nextState; PHCD_TRANSFER_CONTEXT transfer; PDEVICE_EXTENSION devExt; GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt); ASSERT_ENDPOINT(Endpoint); currentState = USBPORT_GetEndpointState(Endpoint); LOGENTRY(Endpoint, FdoDeviceObject, LOG_XFERS, 'dmaA', 0, Endpoint, currentState); USBPORT_ASSERT(currentState == ENDPOINT_ACTIVE); ASSERT_ENDPOINT_LOCKED(Endpoint); // BUGBUG //nextState = ENDPOINT_IDLE; nextState = ENDPOINT_ACTIVE; // now walk thru and process active requests GET_HEAD_LIST(Endpoint->ActiveList, listEntry); while (listEntry && listEntry != &Endpoint->ActiveList) { transfer = (PHCD_TRANSFER_CONTEXT) CONTAINING_RECORD( listEntry, struct _HCD_TRANSFER_CONTEXT, TransferLink); LOGENTRY(Endpoint, FdoDeviceObject, LOG_XFERS, 'pACT', transfer, 0, 0); ASSERT_TRANSFER(transfer); USBPORT_ASSERT(transfer->Tp.TransferBufferLength <= EP_MAX_TRANSFER(Endpoint)); // process the transfer if (TEST_FLAG(transfer->Flags, USBPORT_TXFLAG_KILL_SPLIT)) { USBPORT_QueueDoneTransfer(transfer, STATUS_SUCCESS); break; } else if (!TEST_FLAG(transfer->Flags,USBPORT_TXFLAG_IN_MINIPORT) && !TEST_FLAG(Endpoint->Flags, EPFLAG_NUKED)) { USB_MINIPORT_STATUS mpStatus; // transfer has not been called down yet // call it down now if (TEST_FLAG(transfer->Flags, USBPORT_TXFLAG_ISO)) { LOGENTRY(Endpoint, FdoDeviceObject, LOG_ISO, 'subI', mpStatus, Endpoint, transfer); MP_SubmitIsoTransfer(devExt, Endpoint, transfer, mpStatus); } else { MP_SubmitTransfer(devExt, Endpoint, transfer, mpStatus); } LOGENTRY(Endpoint, FdoDeviceObject, LOG_XFERS, 'subm', mpStatus, Endpoint, transfer); if (mpStatus == USBMP_STATUS_SUCCESS) { LARGE_INTEGER timeout; SET_FLAG(transfer->Flags, USBPORT_TXFLAG_IN_MINIPORT); // miniport took it -- set the timeout KeQuerySystemTime(&transfer->TimeoutTime); timeout.QuadPart = transfer->MillisecTimeout; // convert to 100ns units timeout.QuadPart = timeout.QuadPart * 10000; transfer->TimeoutTime.QuadPart += timeout.QuadPart; } else if (mpStatus == USBMP_STATUS_BUSY) { // miniport busy try later break; } else { // an error, we will need to complete // this transfer for the miniport LOGENTRY(Endpoint, FdoDeviceObject, LOG_XFERS, 'tERR', transfer, mpStatus, 0); if (TEST_FLAG(transfer->Flags, USBPORT_TXFLAG_ISO)) { LOGENTRY(Endpoint, FdoDeviceObject, LOG_ISO, 'iERR', transfer, mpStatus, 0); USBPORT_ErrorCompleteIsoTransfer(FdoDeviceObject, Endpoint, transfer); } else { TEST_TRAP(); } break; } // go active nextState = ENDPOINT_ACTIVE; } // if we find a canceled 'active' transfer we need to pause // the enpoint so we can flush it out if (TEST_FLAG(transfer->Flags, USBPORT_TXFLAG_CANCELED) || TEST_FLAG(transfer->Flags, USBPORT_TXFLAG_ABORTED) ) { // we need to pause the endpoint LOGENTRY(Endpoint, FdoDeviceObject, LOG_XFERS, 'inAC', transfer, Endpoint, transfer->Flags); nextState = ENDPOINT_PAUSE; break; } listEntry = transfer->TransferLink.Flink; } USBPORT_DmaEndpointActive_Done: return nextState; } MP_ENDPOINT_STATE USBPORT_DmaEndpointPaused( PDEVICE_OBJECT FdoDeviceObject, PHCD_ENDPOINT Endpoint ) /*++ Routine Description: process the paused state endpoint is paused, cancel any transfers that need canceling Arguments: Return Value: None. --*/ { MP_ENDPOINT_STATE currentState; MP_ENDPOINT_STATE nextState; PLIST_ENTRY listEntry; PHCD_TRANSFER_CONTEXT transfer; PDEVICE_EXTENSION devExt; GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt); ASSERT_ENDPOINT(Endpoint); currentState = USBPORT_GetEndpointState(Endpoint); LOGENTRY(Endpoint, FdoDeviceObject, LOG_XFERS, 'dmaP', 0, Endpoint, currentState); USBPORT_ASSERT(currentState == ENDPOINT_PAUSE); nextState = currentState; // now walk thru and process active requests GET_HEAD_LIST(Endpoint->ActiveList, listEntry); while (listEntry && listEntry != &Endpoint->ActiveList) { // extract the urb that is currently on the active // list, there should only be one transfer = (PHCD_TRANSFER_CONTEXT) CONTAINING_RECORD( listEntry, struct _HCD_TRANSFER_CONTEXT, TransferLink); LOGENTRY(Endpoint, FdoDeviceObject, LOG_XFERS, 'pPAU', transfer, Endpoint, 0); ASSERT_TRANSFER(transfer); if (TEST_FLAG(transfer->Flags, USBPORT_TXFLAG_CANCELED) || TEST_FLAG(transfer->Flags, USBPORT_TXFLAG_ABORTED)) { if (TEST_FLAG(transfer->Flags, USBPORT_TXFLAG_ISO) && TEST_FLAG(transfer->Flags, USBPORT_TXFLAG_IN_MINIPORT) && !TEST_FLAG(Endpoint->Flags, EPFLAG_NUKED)) { ULONG cf, lastFrame; PTRANSFER_URB urb; LOGENTRY(Endpoint, FdoDeviceObject, LOG_XFERS, 'drn+', transfer, 0, 0); urb = transfer->Urb; ASSERT_TRANSFER_URB(urb); // iso transfer in the miniport, we need to let the // iso TDs drain out, before doing an abort. lastFrame = urb->u.Isoch.StartFrame + urb->u.Isoch.NumberOfPackets; // get the current frame MP_Get32BitFrameNumber(devExt, cf); if (cf < lastFrame + 1) { LOGENTRY(Endpoint, FdoDeviceObject, LOG_XFERS, 'drne', transfer, 0, 0); goto stay_paused; } } if ( TEST_FLAG(transfer->Flags, USBPORT_TXFLAG_IN_MINIPORT) && !TEST_FLAG(Endpoint->Flags, EPFLAG_NUKED)) { ULONG bytesTransferred = 0; // abort the transfer LOGENTRY(Endpoint, FdoDeviceObject, LOG_XFERS, 'inMP', transfer, 0, 0); MP_AbortTransfer(devExt, Endpoint, transfer, bytesTransferred); // make sure we indicate any data that has been transferred // prior to abort if (TEST_FLAG(transfer->Flags, USBPORT_TXFLAG_ISO)) { USBPORT_FlushIsoTransfer(FdoDeviceObject, &transfer->Tp, transfer->IsoTransfer); } else { transfer->MiniportBytesTransferred = bytesTransferred; } LOGENTRY(Endpoint, FdoDeviceObject, LOG_XFERS, 'abrL', 0, transfer, bytesTransferred); // pick up the next ptr listEntry = transfer->TransferLink.Flink; // no more references, put this transfer on the // cancel list RemoveEntryList(&transfer->TransferLink); if (TEST_FLAG(transfer->Flags, USBPORT_TXFLAG_SPLIT_CHILD)) { USBPORT_CancelSplitTransfer(FdoDeviceObject, transfer); } else { InsertTailList(&Endpoint->CancelList, &transfer->TransferLink); } } else { // transfer not in miniport, put it on the // cancel list since it can not be completed // by the miniport. LOGENTRY(Endpoint, FdoDeviceObject, LOG_XFERS, 'niMP', transfer, 0, 0); // pick up the next ptr listEntry = transfer->TransferLink.Flink; RemoveEntryList(&transfer->TransferLink); if (TEST_FLAG(transfer->Flags, USBPORT_TXFLAG_SPLIT_CHILD)) { USBPORT_CancelSplitTransfer(FdoDeviceObject, transfer); } else { InsertTailList(&Endpoint->CancelList, &transfer->TransferLink); } } } else { listEntry = transfer->TransferLink.Flink; } } /* while */ // cancel routine will bump us back to // the active state nextState = ENDPOINT_ACTIVE; stay_paused: return nextState; } VOID USBPORT_DmaEndpointWorker( PHCD_ENDPOINT Endpoint ) /*++ Routine Description: endpoints that need transfers mapped come thru here Arguments: Return Value: None. --*/ { PDEVICE_OBJECT fdoDeviceObject; MP_ENDPOINT_STATE currentState; MP_ENDPOINT_STATE nextState; BOOLEAN invalidate = FALSE; ASSERT_ENDPOINT(Endpoint); fdoDeviceObject = Endpoint->FdoDeviceObject; LOGENTRY(Endpoint, fdoDeviceObject, LOG_XFERS, 'dmaW', 0, Endpoint, 0); ACQUIRE_ENDPOINT_LOCK(Endpoint, fdoDeviceObject, 'Le90'); // we should be in the last requested state currentState = USBPORT_GetEndpointState(Endpoint); switch(currentState) { case ENDPOINT_PAUSE: nextState = USBPORT_DmaEndpointPaused( fdoDeviceObject, Endpoint); break; case ENDPOINT_ACTIVE: nextState = USBPORT_DmaEndpointActive( fdoDeviceObject, Endpoint); break; default: // state not handled // this is a bug TEST_TRAP(); } // release the endpoint lists RELEASE_ENDPOINT_LOCK(Endpoint, fdoDeviceObject, 'Ue90'); // flush out canceled requests USBPORT_FlushCancelList(Endpoint); // endpoint has now been processed, if we were paused all canceled // transfers were removed. // We were either // 1. paused and need to stay paused (for iso drain) // 2. paused and need to go active // 3. active and need to pause // 4. active and need to stay active // ACQUIRE_ENDPOINT_LOCK(Endpoint, fdoDeviceObject, 'LeJ0'); // set to new endpoint state if necessary if (nextState != currentState) { // case 2, 3 USBPORT_SetEndpointState(Endpoint, nextState); } else if (nextState == currentState && nextState == ENDPOINT_PAUSE) { invalidate = TRUE; } RELEASE_ENDPOINT_LOCK(Endpoint, fdoDeviceObject, 'UeJ0'); if (invalidate) { // state change, defer this to the worker USBPORT_InvalidateEndpoint(fdoDeviceObject, Endpoint, IEP_SIGNAL_WORKER); } } typedef struct _USBPORT_DB_HANDLE { ULONG Sig; LIST_ENTRY DbLink; PVOID DbSystemAddress; ULONG DbLength; PUCHAR DbData; } USBPORT_DB_HANDLE, *PUSBPORT_DB_HANDLE; VOID USBPORTSVC_NotifyDoubleBuffer( PDEVICE_DATA DeviceData, PTRANSFER_PARAMETERS TransferParameters, PVOID DbSystemAddress, ULONG DbLength ) /*++ Routine Description: Notify the port driver that double buffering has occured, port driver will create a node for use during a subsequent adapter flush. Arguments: Return Value: none --*/ { PDEVICE_EXTENSION devExt; PHCD_TRANSFER_CONTEXT transfer; PDEVICE_OBJECT fdoDeviceObject; PUSBPORT_DB_HANDLE dbHandle; ULONG length; BOOLEAN write; DEVEXT_FROM_DEVDATA(devExt, DeviceData); ASSERT_FDOEXT(devExt); fdoDeviceObject = devExt->HcFdoDeviceObject; LOGENTRY(NULL, fdoDeviceObject, LOG_XFERS, 'NOdb', 0, 0, TransferParameters); TRANSFER_FROM_TPARAMETERS(transfer, TransferParameters); ASSERT_TRANSFER(transfer); write = transfer->Direction == WriteData ? TRUE : FALSE; // allocate a node and add it to the list, we don't care if it is a // write if (!write && transfer->MapRegisterBase != NULL) { PUCHAR pch; length = sizeof(USBPORT_DB_HANDLE) + DbLength; ALLOC_POOL_Z(pch, NonPagedPool, length); LOGENTRY(NULL, fdoDeviceObject, LOG_XFERS, 'db++', DbSystemAddress, DbLength, transfer); dbHandle = (PUSBPORT_DB_HANDLE) pch; pch += sizeof(USBPORT_DB_HANDLE); dbHandle->Sig = SIG_DB; dbHandle->DbSystemAddress = DbSystemAddress; dbHandle->DbLength = DbLength; dbHandle->DbData = pch; RtlCopyMemory(pch, DbSystemAddress, DbLength); if (TEST_FLAG(transfer->Flags, USBPORT_TXFLAG_SPLIT_CHILD)) { ASSERT_TRANSFER(transfer->Transfer); InsertTailList(&transfer->Transfer->DoubleBufferList, &dbHandle->DbLink); } else { InsertTailList(&transfer->DoubleBufferList, &dbHandle->DbLink); } } } VOID USBPORT_FlushAdapterDBs( PDEVICE_OBJECT FdoDeviceObject, PHCD_TRANSFER_CONTEXT Transfer ) /*++ Routine Description: Arguments: Return Value: none --*/ { PDEVICE_EXTENSION devExt; PLIST_ENTRY listEntry; PUSBPORT_DB_HANDLE dbHandle; GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt); ASSERT_TRANSFER(Transfer); LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'flDB', Transfer, 0, 0); // dump the 4 dwords of the transfer buffer //{ // PULONG p; // // p = (PULONG) Transfer->SgList.MdlVirtualAddress; // LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'dmp1', *(p), // *(p+1), *(p+2)); //} while (!IsListEmpty(&Transfer->DoubleBufferList)) { listEntry = RemoveHeadList(&Transfer->DoubleBufferList); LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'flle', Transfer, listEntry, 0); dbHandle = (PUSBPORT_DB_HANDLE) CONTAINING_RECORD( listEntry, struct _USBPORT_DB_HANDLE, DbLink); LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'DBHf', Transfer, dbHandle, 0); ASSERT_DB_HANDLE(dbHandle); // flush to the system address RtlCopyMemory(dbHandle->DbSystemAddress, dbHandle->DbData, dbHandle->DbLength); FREE_POOL(FdoDeviceObject, dbHandle); } }