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.
610 lines
14 KiB
610 lines
14 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
core.c
|
|
|
|
Abstract:
|
|
|
|
We maintain two lists for Transfer Irps
|
|
|
|
(1)
|
|
PendingTransferIrps - transfers on the endpoint pending List
|
|
protected by PendingIrpLock
|
|
|
|
(2)
|
|
ActiveTransferIrps - transfers on the enpoint ACTIVE, CANCEL list
|
|
or on the MapTransfer List
|
|
protected by ActiveIrpLock
|
|
|
|
each list has its own cancel and completion routine
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
6-20-99 : created
|
|
|
|
--*/
|
|
|
|
#include "common.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#endif
|
|
|
|
// non paged functions
|
|
// USBPORT_QueuePendingTransferIrp
|
|
// USBPORT_CancelPendingTransferIrp
|
|
// USBPORT_InsertIrpInTable
|
|
// USBPORT_RemoveIrpFromTable
|
|
// USBPORT_FindIrpInTable
|
|
|
|
VOID
|
|
USBPORT_InsertIrpInTable(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PUSBPORT_IRP_TABLE IrpTable,
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
PUSBPORT_IRP_TABLE t = IrpTable;
|
|
|
|
USBPORT_ASSERT(IrpTable != NULL);
|
|
|
|
t = (PUSBPORT_IRP_TABLE) &IrpTable;
|
|
|
|
do {
|
|
t = t->NextTable;
|
|
for (i = 0; i<IRP_TABLE_LENGTH; i++) {
|
|
if (t->Irps[i] == NULL) {
|
|
t->Irps[i] = Irp;
|
|
return;
|
|
}
|
|
}
|
|
} while (t->NextTable);
|
|
|
|
// no room, grow the table and recurse
|
|
|
|
ALLOC_POOL_Z(t->NextTable, NonPagedPool,
|
|
sizeof(USBPORT_IRP_TABLE));
|
|
|
|
if (t->NextTable != NULL) {
|
|
USBPORT_InsertIrpInTable(FdoDeviceObject, t->NextTable, Irp);
|
|
} else {
|
|
// we should handle this more gracefully
|
|
// you can hit this in a low resource scenario
|
|
BUGCHECK(USBBUGCODE_INTERNAL_ERROR, 0, 0, 0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
PIRP
|
|
USBPORT_RemoveIrpFromTable(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PUSBPORT_IRP_TABLE IrpTable,
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
PUSBPORT_IRP_TABLE t = IrpTable;
|
|
|
|
USBPORT_ASSERT(IrpTable != NULL);
|
|
|
|
t = (PUSBPORT_IRP_TABLE) &IrpTable;
|
|
|
|
do {
|
|
t = t->NextTable;
|
|
for (i = 0; i<IRP_TABLE_LENGTH; i++) {
|
|
if (t->Irps[i] == Irp) {
|
|
t->Irps[i] = NULL;
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'rmIP', i, Irp, IrpTable);
|
|
return Irp;
|
|
}
|
|
}
|
|
} while (t->NextTable);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PIRP
|
|
USBPORT_FindUrbInIrpTable(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PUSBPORT_IRP_TABLE IrpTable,
|
|
PTRANSFER_URB Urb,
|
|
PIRP InputIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given and table urb we scan for it in the the irp table
|
|
if we find it it means the client has submitted the same
|
|
urb twice.
|
|
|
|
This function is used to validate client drivers, there is
|
|
a small perf hit taken here but probably worth it.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
PUSBPORT_IRP_TABLE t = IrpTable;
|
|
PIRP tIrp = NULL;
|
|
PTRANSFER_URB urb;
|
|
PIO_STACK_LOCATION irpStack;
|
|
|
|
USBPORT_ASSERT(IrpTable != NULL);
|
|
|
|
t = (PUSBPORT_IRP_TABLE) &IrpTable;
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'fndU', t, Urb, 0);
|
|
|
|
do {
|
|
t = t->NextTable;
|
|
for (i=0; i<IRP_TABLE_LENGTH; i++) {
|
|
tIrp = t->Irps[i];
|
|
if (tIrp != NULL) {
|
|
irpStack = IoGetCurrentIrpStackLocation(tIrp);
|
|
urb = irpStack->Parameters.Others.Argument1;
|
|
if (urb == Urb) {
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'fkkX', tIrp, urb, InputIrp);
|
|
if (tIrp == InputIrp) {
|
|
// this is a double submit by the client driver, that
|
|
// is the irp is still pending
|
|
BUGCHECK(USBBUGCODE_DOUBLE_SUBMIT, (ULONG_PTR) tIrp,
|
|
(ULONG_PTR) urb, 0);
|
|
} else {
|
|
// this is the case where the URB is attached to
|
|
// another irp
|
|
BUGCHECK(USBBUGCODE_BAD_URB, (ULONG_PTR) tIrp, (ULONG_PTR) InputIrp,
|
|
(ULONG_PTR) urb);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
} while (t->NextTable);
|
|
|
|
return tIrp;
|
|
}
|
|
|
|
|
|
PIRP
|
|
USBPORT_FindIrpInTable(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PUSBPORT_IRP_TABLE IrpTable,
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
PUSBPORT_IRP_TABLE t = IrpTable;
|
|
|
|
USBPORT_ASSERT(IrpTable != NULL);
|
|
|
|
t = (PUSBPORT_IRP_TABLE) &IrpTable;
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'fIPT', t, Irp, 0);
|
|
|
|
do {
|
|
t = t->NextTable;
|
|
for (i = 0; i<IRP_TABLE_LENGTH; i++) {
|
|
if (t->Irps[i] == Irp) {
|
|
return Irp;
|
|
}
|
|
}
|
|
} while (t->NextTable);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
USBPORT_QueuePendingTransferIrp(
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PHCD_TRANSFER_CONTEXT transfer;
|
|
PHCD_ENDPOINT endpoint;
|
|
KIRQL cancelIrql, irql;
|
|
PDEVICE_OBJECT fdoDeviceObject;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDEVICE_EXTENSION devExt;
|
|
PTRANSFER_URB urb;
|
|
|
|
// on entry the urb is not cancelable ie
|
|
// no cancel routine
|
|
|
|
// extract the urb;
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
urb = irpStack->Parameters.Others.Argument1;
|
|
|
|
ASSERT_TRANSFER_URB(urb);
|
|
transfer = urb->pd.HcdTransferContext;
|
|
endpoint = transfer->Endpoint;
|
|
|
|
fdoDeviceObject = endpoint->FdoDeviceObject;
|
|
GET_DEVICE_EXT(devExt, fdoDeviceObject);
|
|
ASSERT_FDOEXT(devExt);
|
|
|
|
LOGENTRY(NULL, fdoDeviceObject, LOG_XFERS, 'tIRP', transfer, endpoint, 0);
|
|
|
|
USBPORT_ASSERT(Irp == transfer->Irp);
|
|
USBPORT_ASSERT(Irp != NULL);
|
|
|
|
Irp->IoStatus.Status = STATUS_PENDING;
|
|
IoMarkIrpPending(Irp);
|
|
|
|
ACQUIRE_PENDING_IRP_LOCK(devExt, irql);
|
|
|
|
IoSetCancelRoutine(Irp, USBPORT_CancelPendingTransferIrp);
|
|
|
|
if (Irp->Cancel &&
|
|
IoSetCancelRoutine(Irp, NULL)) {
|
|
|
|
// irp was canceled and our cancel routine
|
|
// did not run
|
|
RELEASE_PENDING_IRP_LOCK(devExt, irql);
|
|
|
|
USBPORT_CompleteTransfer(urb,
|
|
USBD_STATUS_CANCELED);
|
|
} else {
|
|
|
|
// cancel routine is set
|
|
USBPORT_InsertPendingTransferIrp(fdoDeviceObject, Irp);
|
|
|
|
USBPORT_QueuePendingUrbToEndpoint(endpoint, urb);
|
|
|
|
RELEASE_PENDING_IRP_LOCK(devExt, irql);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
USBPORT_CancelPendingTransferIrp(
|
|
PDEVICE_OBJECT PdoDeviceObject,
|
|
PIRP CancelIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PIRP irp;
|
|
PDEVICE_EXTENSION devExt, rhDevExt;
|
|
PHCD_TRANSFER_CONTEXT transfer;
|
|
PHCD_ENDPOINT endpoint;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDEVICE_OBJECT fdoDeviceObject;
|
|
KIRQL irql;
|
|
|
|
// release cancel spinlock
|
|
IoReleaseCancelSpinLock(CancelIrp->CancelIrql);
|
|
|
|
GET_DEVICE_EXT(rhDevExt, PdoDeviceObject);
|
|
ASSERT_PDOEXT(rhDevExt);
|
|
fdoDeviceObject = rhDevExt->HcFdoDeviceObject;
|
|
|
|
GET_DEVICE_EXT(devExt, fdoDeviceObject);
|
|
ASSERT_FDOEXT(devExt);
|
|
|
|
LOGENTRY(NULL, fdoDeviceObject, LOG_XFERS, 'canP', fdoDeviceObject, CancelIrp, 0);
|
|
|
|
ACQUIRE_PENDING_IRP_LOCK(devExt, irql);
|
|
|
|
irp = USBPORT_RemovePendingTransferIrp(fdoDeviceObject, CancelIrp);
|
|
|
|
if (irp) {
|
|
PTRANSFER_URB urb;
|
|
|
|
// found it
|
|
irpStack = IoGetCurrentIrpStackLocation(CancelIrp);
|
|
urb = irpStack->Parameters.Others.Argument1;
|
|
|
|
ASSERT_TRANSFER_URB(urb);
|
|
transfer = urb->pd.HcdTransferContext;
|
|
endpoint = transfer->Endpoint;
|
|
|
|
USBPORT_ASSERT(fdoDeviceObject == endpoint->FdoDeviceObject);
|
|
|
|
ACQUIRE_ENDPOINT_LOCK(endpoint, fdoDeviceObject, 'Le10');
|
|
|
|
// remove request from the endpoint,
|
|
// it will be on the pending list
|
|
#if DBG
|
|
USBPORT_ASSERT(
|
|
USBPORT_FindUrbInList(urb, &endpoint->PendingList));
|
|
#endif
|
|
RemoveEntryList(&transfer->TransferLink);
|
|
transfer->TransferLink.Flink = NULL;
|
|
transfer->TransferLink.Blink = NULL;
|
|
|
|
RELEASE_ENDPOINT_LOCK(endpoint, fdoDeviceObject, 'Ue10');
|
|
}
|
|
|
|
|
|
RELEASE_PENDING_IRP_LOCK(devExt, irql);
|
|
|
|
// noone nows about this irp anymore
|
|
// complete it with status canceled
|
|
if (irp) {
|
|
USBPORT_CompleteTransfer(transfer->Urb,
|
|
USBD_STATUS_CANCELED);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
USBPORT_CancelActiveTransferIrp(
|
|
PDEVICE_OBJECT PdoDeviceObject,
|
|
PIRP CancelIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cancels come in on the root hub Pdo
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PIRP irp;
|
|
PDEVICE_EXTENSION devExt, rhDevExt;
|
|
PHCD_TRANSFER_CONTEXT transfer;
|
|
PHCD_ENDPOINT endpoint;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDEVICE_OBJECT fdoDeviceObject;
|
|
KIRQL irql;
|
|
|
|
GET_DEVICE_EXT(rhDevExt, PdoDeviceObject);
|
|
ASSERT_PDOEXT(rhDevExt);
|
|
fdoDeviceObject = rhDevExt->HcFdoDeviceObject;
|
|
|
|
GET_DEVICE_EXT(devExt, fdoDeviceObject);
|
|
ASSERT_FDOEXT(devExt);
|
|
|
|
LOGENTRY(NULL, fdoDeviceObject, LOG_XFERS, 'canA', fdoDeviceObject, CancelIrp, 0);
|
|
|
|
// when we have the fdo we can release the global cancel lock
|
|
IoReleaseCancelSpinLock(CancelIrp->CancelIrql);
|
|
|
|
ACQUIRE_ACTIVE_IRP_LOCK(fdoDeviceObject, devExt, irql);
|
|
|
|
irp = USBPORT_FindActiveTransferIrp(fdoDeviceObject, CancelIrp);
|
|
|
|
// if irp is not on our list then we have already completed it.
|
|
if (irp) {
|
|
|
|
PTRANSFER_URB urb;
|
|
|
|
USBPORT_ASSERT(irp == CancelIrp);
|
|
LOGENTRY(NULL, fdoDeviceObject, LOG_XFERS, 'CANA', fdoDeviceObject, irp, 0);
|
|
// found it
|
|
// mark the transfer so it will be canceled the next
|
|
// time we process the endpoint.
|
|
irpStack = IoGetCurrentIrpStackLocation(irp);
|
|
urb = irpStack->Parameters.Others.Argument1;
|
|
|
|
ASSERT_TRANSFER_URB(urb);
|
|
transfer = urb->pd.HcdTransferContext;
|
|
endpoint = transfer->Endpoint;
|
|
|
|
USBPORT_ASSERT(fdoDeviceObject == endpoint->FdoDeviceObject);
|
|
|
|
ACQUIRE_ENDPOINT_LOCK(endpoint, fdoDeviceObject, 'LeI0');
|
|
|
|
if (TEST_FLAG(transfer->Flags, USBPORT_TXFLAG_SPLIT)) {
|
|
KIRQL tIrql;
|
|
PLIST_ENTRY listEntry;
|
|
PHCD_TRANSFER_CONTEXT childTransfer;
|
|
|
|
SET_FLAG(transfer->Flags, USBPORT_TXFLAG_CANCELED);
|
|
|
|
ACQUIRE_TRANSFER_LOCK(fdoDeviceObject, transfer, tIrql);
|
|
|
|
// mark all children as cancelled
|
|
GET_HEAD_LIST(transfer->SplitTransferList, listEntry);
|
|
|
|
while (listEntry &&
|
|
listEntry != &transfer->SplitTransferList) {
|
|
|
|
childTransfer = (PHCD_TRANSFER_CONTEXT) CONTAINING_RECORD(
|
|
listEntry,
|
|
struct _HCD_TRANSFER_CONTEXT,
|
|
SplitLink);
|
|
|
|
ASSERT_TRANSFER(childTransfer);
|
|
|
|
SET_FLAG(childTransfer->Flags, USBPORT_TXFLAG_CANCELED);
|
|
|
|
listEntry = childTransfer->SplitLink.Flink;
|
|
}
|
|
|
|
RELEASE_TRANSFER_LOCK(fdoDeviceObject, transfer, tIrql);
|
|
|
|
} else {
|
|
SET_FLAG(transfer->Flags, USBPORT_TXFLAG_CANCELED);
|
|
}
|
|
RELEASE_ENDPOINT_LOCK(endpoint, fdoDeviceObject, 'UeI0');
|
|
|
|
RELEASE_ACTIVE_IRP_LOCK(fdoDeviceObject, devExt, irql);
|
|
// if we canceled a transfer then
|
|
// this endpoint needs attention
|
|
USBPORT_InvalidateEndpoint(fdoDeviceObject,
|
|
endpoint,
|
|
IEP_SIGNAL_WORKER);
|
|
} else {
|
|
|
|
RELEASE_ACTIVE_IRP_LOCK(fdoDeviceObject, devExt, irql);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
PIRP
|
|
USBPORT_FindActiveTransferIrp(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION devExt;
|
|
|
|
GET_DEVICE_EXT(devExt, FdoDeviceObject);
|
|
ASSERT_FDOEXT(devExt);
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'fnAC', 0, Irp, 0);
|
|
|
|
return USBPORT_FindIrpInTable(FdoDeviceObject,
|
|
devExt->ActiveTransferIrpTable,
|
|
Irp);
|
|
}
|
|
|
|
|
|
PIRP
|
|
USBPORT_RemoveActiveTransferIrp(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION devExt;
|
|
|
|
GET_DEVICE_EXT(devExt, FdoDeviceObject);
|
|
ASSERT_FDOEXT(devExt);
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'rmAC', 0, Irp, 0);
|
|
|
|
return USBPORT_RemoveIrpFromTable(FdoDeviceObject,
|
|
devExt->ActiveTransferIrpTable,
|
|
Irp);
|
|
|
|
}
|
|
|
|
|
|
PIRP
|
|
USBPORT_RemovePendingTransferIrp(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION devExt;
|
|
|
|
GET_DEVICE_EXT(devExt, FdoDeviceObject);
|
|
ASSERT_FDOEXT(devExt);
|
|
|
|
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'rmPN', 0, Irp, 0);
|
|
|
|
return USBPORT_RemoveIrpFromTable(FdoDeviceObject,
|
|
devExt->PendingTransferIrpTable,
|
|
Irp);
|
|
}
|
|
|
|
|
|
VOID
|
|
USBPORT_FreeIrpTable(
|
|
PDEVICE_OBJECT FdoDeviceObject,
|
|
PUSBPORT_IRP_TABLE BaseIrpTable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PUSBPORT_IRP_TABLE tmp;
|
|
|
|
while (BaseIrpTable != NULL) {
|
|
tmp = BaseIrpTable->NextTable;
|
|
FREE_POOL(FdoDeviceObject, BaseIrpTable);
|
|
BaseIrpTable = tmp;
|
|
};
|
|
}
|