reactos/drivers/usb/usbstor/queue.c
2021-06-11 15:33:08 +03:00

362 lines
10 KiB
C

/*
* PROJECT: ReactOS Universal Serial Bus Bulk Storage Driver
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: USB block storage device driver.
* COPYRIGHT: 2005-2006 James Tabor
* 2011-2012 Michael Martin (michael.martin@reactos.org)
* 2011-2013 Johannes Anderwald (johannes.anderwald@reactos.org)
*/
#include "usbstor.h"
#define NDEBUG
#include <debug.h>
VOID
USBSTOR_QueueInitialize(
PFDO_DEVICE_EXTENSION FDODeviceExtension)
{
ASSERT(FDODeviceExtension->Common.IsFDO);
KeInitializeSpinLock(&FDODeviceExtension->IrpListLock);
InitializeListHead(&FDODeviceExtension->IrpListHead);
KeInitializeEvent(&FDODeviceExtension->NoPendingRequests, NotificationEvent, TRUE);
}
VOID
NTAPI
USBSTOR_CancelIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PFDO_DEVICE_EXTENSION FDODeviceExtension;
FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
ASSERT_IRQL_EQUAL(DISPATCH_LEVEL);
ASSERT(FDODeviceExtension->Common.IsFDO);
// this IRP isn't in our list here
// now release the cancel lock
IoReleaseCancelSpinLock(Irp->CancelIrql);
Irp->IoStatus.Status = STATUS_CANCELLED;
USBSTOR_QueueTerminateRequest(DeviceObject, Irp);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
USBSTOR_QueueNextRequest(DeviceObject);
}
VOID
NTAPI
USBSTOR_Cancel(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PFDO_DEVICE_EXTENSION FDODeviceExtension;
FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
ASSERT_IRQL_EQUAL(DISPATCH_LEVEL);
ASSERT(FDODeviceExtension->Common.IsFDO);
KeAcquireSpinLockAtDpcLevel(&FDODeviceExtension->IrpListLock);
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
KeReleaseSpinLockFromDpcLevel(&FDODeviceExtension->IrpListLock);
IoReleaseCancelSpinLock(Irp->CancelIrql);
Irp->IoStatus.Status = STATUS_CANCELLED;
USBSTOR_QueueTerminateRequest(DeviceObject, Irp);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
USBSTOR_QueueNextRequest(DeviceObject);
}
BOOLEAN
USBSTOR_QueueAddIrp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PDRIVER_CANCEL OldDriverCancel;
KIRQL OldLevel;
PFDO_DEVICE_EXTENSION FDODeviceExtension;
BOOLEAN IrpListFreeze;
BOOLEAN SrbProcessing;
PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp);
PSCSI_REQUEST_BLOCK Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1;
FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
ASSERT(FDODeviceExtension->Common.IsFDO);
IoMarkIrpPending(Irp);
KeAcquireSpinLock(&FDODeviceExtension->IrpListLock, &OldLevel);
SrbProcessing = FDODeviceExtension->IrpPendingCount != 0;
if (SrbProcessing)
{
// add irp to queue
InsertTailList(&FDODeviceExtension->IrpListHead, &Irp->Tail.Overlay.ListEntry);
}
FDODeviceExtension->IrpPendingCount++;
KeClearEvent(&FDODeviceExtension->NoPendingRequests);
// check if queue is freezed
IrpListFreeze = BooleanFlagOn(FDODeviceExtension->Flags, USBSTOR_FDO_FLAGS_IRP_LIST_FREEZE);
KeReleaseSpinLock(&FDODeviceExtension->IrpListLock, OldLevel);
// synchronize with cancellations by holding the cancel lock
IoAcquireCancelSpinLock(&Irp->CancelIrql);
if (SrbProcessing)
{
ASSERT(FDODeviceExtension->ActiveSrb != NULL);
OldDriverCancel = IoSetCancelRoutine(Irp, USBSTOR_Cancel);
}
else
{
ASSERT(FDODeviceExtension->ActiveSrb == NULL);
FDODeviceExtension->ActiveSrb = Request;
OldDriverCancel = IoSetCancelRoutine(Irp, USBSTOR_CancelIo);
}
// check if the irp has already been cancelled
if (Irp->Cancel && OldDriverCancel == NULL)
{
// cancel irp
Irp->CancelRoutine(DeviceObject, Irp);
return FALSE;
}
IoReleaseCancelSpinLock(Irp->CancelIrql);
// if list is freezed, dont start this packet
DPRINT("IrpListFreeze: %lu IrpPendingCount %lu\n", IrpListFreeze, FDODeviceExtension->IrpPendingCount);
return (IrpListFreeze || SrbProcessing);
}
PIRP
USBSTOR_RemoveIrp(
IN PDEVICE_OBJECT DeviceObject)
{
KIRQL OldLevel;
PFDO_DEVICE_EXTENSION FDODeviceExtension;
PLIST_ENTRY Entry;
PIRP Irp = NULL;
FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
ASSERT(FDODeviceExtension->Common.IsFDO);
KeAcquireSpinLock(&FDODeviceExtension->IrpListLock, &OldLevel);
if (!IsListEmpty(&FDODeviceExtension->IrpListHead))
{
Entry = RemoveHeadList(&FDODeviceExtension->IrpListHead);
// get offset to start of irp
Irp = (PIRP)CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
}
KeReleaseSpinLock(&FDODeviceExtension->IrpListLock, OldLevel);
return Irp;
}
VOID
USBSTOR_QueueWaitForPendingRequests(
IN PDEVICE_OBJECT DeviceObject)
{
PFDO_DEVICE_EXTENSION FDODeviceExtension;
FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
KeWaitForSingleObject(&FDODeviceExtension->NoPendingRequests,
Executive,
KernelMode,
FALSE,
NULL);
}
VOID
USBSTOR_QueueTerminateRequest(
IN PDEVICE_OBJECT FDODeviceObject,
IN PIRP Irp)
{
KIRQL OldLevel;
PFDO_DEVICE_EXTENSION FDODeviceExtension;
PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp);
PSCSI_REQUEST_BLOCK Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1;
FDODeviceExtension = (PFDO_DEVICE_EXTENSION)FDODeviceObject->DeviceExtension;
ASSERT(FDODeviceExtension->Common.IsFDO);
KeAcquireSpinLock(&FDODeviceExtension->IrpListLock, &OldLevel);
FDODeviceExtension->IrpPendingCount--;
// check if this was our current active SRB
if (FDODeviceExtension->ActiveSrb == Request)
{
// indicate processing is completed
FDODeviceExtension->ActiveSrb = NULL;
}
// Set the event if nothing else is pending
if (FDODeviceExtension->IrpPendingCount == 0 &&
FDODeviceExtension->ActiveSrb == NULL)
{
KeSetEvent(&FDODeviceExtension->NoPendingRequests, IO_NO_INCREMENT, FALSE);
}
KeReleaseSpinLock(&FDODeviceExtension->IrpListLock, OldLevel);
}
VOID
USBSTOR_QueueNextRequest(
IN PDEVICE_OBJECT DeviceObject)
{
PFDO_DEVICE_EXTENSION FDODeviceExtension;
PIRP Irp;
PIO_STACK_LOCATION IoStack;
PSCSI_REQUEST_BLOCK Request;
FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
ASSERT(FDODeviceExtension->Common.IsFDO);
// check first if there's already a request pending or the queue is frozen
if (FDODeviceExtension->ActiveSrb != NULL ||
BooleanFlagOn(FDODeviceExtension->Flags, USBSTOR_FDO_FLAGS_IRP_LIST_FREEZE))
{
// no work to do yet
return;
}
// remove first irp from list
Irp = USBSTOR_RemoveIrp(DeviceObject);
// is there an irp pending
if (!Irp)
{
// no work to do
IoStartNextPacket(DeviceObject, TRUE);
return;
}
IoStack = IoGetCurrentIrpStackLocation(Irp);
Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1;
ASSERT(Request);
FDODeviceExtension->ActiveSrb = Request;
// start next packet
IoStartPacket(DeviceObject, Irp, &Request->QueueSortKey, USBSTOR_CancelIo);
IoStartNextPacket(DeviceObject, TRUE);
}
VOID
USBSTOR_QueueRelease(
IN PDEVICE_OBJECT DeviceObject)
{
PFDO_DEVICE_EXTENSION FDODeviceExtension;
PIRP Irp;
KIRQL OldLevel;
PIO_STACK_LOCATION IoStack;
PSCSI_REQUEST_BLOCK Request;
FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
ASSERT(FDODeviceExtension->Common.IsFDO);
KeAcquireSpinLock(&FDODeviceExtension->IrpListLock, &OldLevel);
// clear freezed status
FDODeviceExtension->Flags &= ~USBSTOR_FDO_FLAGS_IRP_LIST_FREEZE;
KeReleaseSpinLock(&FDODeviceExtension->IrpListLock, OldLevel);
// grab newest irp
Irp = USBSTOR_RemoveIrp(DeviceObject);
if (!Irp)
{
return;
}
IoStack = IoGetCurrentIrpStackLocation(Irp);
Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1;
IoStartPacket(DeviceObject,
Irp,
&Request->QueueSortKey,
USBSTOR_CancelIo);
}
VOID
NTAPI
USBSTOR_StartIo(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
PSCSI_REQUEST_BLOCK Request;
PFDO_DEVICE_EXTENSION FDODeviceExtension;
PPDO_DEVICE_EXTENSION PDODeviceExtension;
KIRQL OldLevel;
BOOLEAN ResetInProgress;
DPRINT("USBSTOR_StartIo\n");
FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
ASSERT(FDODeviceExtension->Common.IsFDO);
IoAcquireCancelSpinLock(&OldLevel);
IoSetCancelRoutine(Irp, NULL);
// check if the irp has been cancelled
if (Irp->Cancel)
{
IoReleaseCancelSpinLock(OldLevel);
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
USBSTOR_QueueTerminateRequest(DeviceObject, Irp);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
USBSTOR_QueueNextRequest(DeviceObject);
return;
}
IoReleaseCancelSpinLock(OldLevel);
KeAcquireSpinLock(&FDODeviceExtension->CommonLock, &OldLevel);
ResetInProgress = BooleanFlagOn(FDODeviceExtension->Flags, USBSTOR_FDO_FLAGS_DEVICE_RESETTING);
KeReleaseSpinLock(&FDODeviceExtension->CommonLock, OldLevel);
IoStack = IoGetCurrentIrpStackLocation(Irp);
PDODeviceExtension = (PPDO_DEVICE_EXTENSION)IoStack->DeviceObject->DeviceExtension;
Request = IoStack->Parameters.Scsi.Srb;
ASSERT(PDODeviceExtension->Common.IsFDO == FALSE);
if (ResetInProgress)
{
// hard reset is in progress
Request->SrbStatus = SRB_STATUS_NO_DEVICE;
Request->DataTransferLength = 0;
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
USBSTOR_QueueTerminateRequest(DeviceObject, Irp);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
USBSTOR_QueueNextRequest(DeviceObject);
return;
}
USBSTOR_HandleExecuteSCSI(IoStack->DeviceObject, Irp);
// FIXME: handle error
}