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.
579 lines
17 KiB
579 lines
17 KiB
/*++
|
|
|
|
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);
|
|
|
|
}
|
|
}
|