/*++ 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; iIrps[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; iIrps[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; iIrps[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; iIrps[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; }; }