mirror of https://github.com/tongzx/nt5src
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.
5869 lines
176 KiB
5869 lines
176 KiB
/*++
|
|
|
|
Copyright (c) 1996-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
SCSI.C
|
|
|
|
Abstract:
|
|
|
|
This source file contains the dispatch routines which handle:
|
|
|
|
IRP_MJ_DEVICE_CONTROL
|
|
IRP_MJ_SCSI
|
|
|
|
Environment:
|
|
|
|
kernel mode
|
|
|
|
Revision History:
|
|
|
|
06-01-98 : started rewrite
|
|
|
|
--*/
|
|
|
|
//*****************************************************************************
|
|
// I N C L U D E S
|
|
//*****************************************************************************
|
|
|
|
#include <ntddk.h>
|
|
#include <usbdi.h>
|
|
#include <usbdlib.h>
|
|
#include <ntddscsi.h>
|
|
#include <ntddstor.h>
|
|
|
|
#include "usbmass.h"
|
|
|
|
//*****************************************************************************
|
|
// L O C A L F U N C T I O N P R O T O T Y P E S
|
|
//*****************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_QueryProperty (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_BuildDeviceDescriptor (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor,
|
|
IN OUT PULONG DescriptorLength
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_BuildAdapterDescriptor (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor,
|
|
IN OUT PULONG DescriptorLength
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_SendPassThrough (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP RequestIrp
|
|
);
|
|
|
|
VOID
|
|
USBSTOR_CancelIo (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
//
|
|
// CBI (Control/Bulk/Interrupt) Routines
|
|
//
|
|
|
|
NTSTATUS
|
|
USBSTOR_IssueClientCdb (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_ClientCdbCompletion (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID NotUsed
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_IssueClientBulkRequest (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_ClientBulkCompletionRoutine (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID NotUsed
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_IssueInterruptRequest (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_InterruptDataCompletionRoutine (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID NotUsed
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_IssueRequestSenseCdb (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG_PTR AutoFlag
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_RequestSenseCdbCompletion (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID AutoFlag
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_IssueRequestSenseBulkRequest (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG_PTR AutoFlag
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_SenseDataCompletionRoutine (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID AutoFlag
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_IssueRequestSenseInterruptRequest (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG_PTR AutoFlag
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_RequestSenseInterruptCompletionRoutine (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID AutoFlag
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_ProcessRequestSenseCompletion (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG_PTR AutoFlag
|
|
);
|
|
|
|
VOID
|
|
USBSTOR_QueueResetPipe (
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
);
|
|
|
|
VOID
|
|
USBSTOR_ResetPipeWorkItem (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context
|
|
);
|
|
|
|
//
|
|
// Bulk-Only Routines
|
|
//
|
|
|
|
NTSTATUS
|
|
USBSTOR_CbwTransfer (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_CbwCompletion (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID NotUsed
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_DataTransfer (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_DataCompletion (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID NotUsed
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_CswTransfer (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_CswCompletion (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID NotUsed
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_IssueRequestSense (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
USBSTOR_BulkQueueResetPipe (
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
);
|
|
|
|
VOID
|
|
USBSTOR_BulkResetPipeWorkItem (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context
|
|
);
|
|
|
|
//
|
|
// CBI / Bulk-Only Common Routines
|
|
//
|
|
|
|
VOID
|
|
USBSTOR_QueueResetDevice (
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
);
|
|
|
|
VOID
|
|
USBSTOR_ResetDeviceWorkItem (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_IsDeviceConnected (
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_ResetDevice (
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
);
|
|
|
|
NTSTATUS
|
|
USBSTOR_IssueInternalCdb (
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PVOID DataBuffer,
|
|
PULONG DataTransferLength,
|
|
PCDB Cdb,
|
|
UCHAR CdbLength,
|
|
ULONG TimeOutValue
|
|
);
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, USBSTOR_DeviceControl)
|
|
#pragma alloc_text(PAGE, USBSTOR_QueryProperty)
|
|
#pragma alloc_text(PAGE, USBSTOR_BuildDeviceDescriptor)
|
|
#pragma alloc_text(PAGE, USBSTOR_BuildAdapterDescriptor)
|
|
#pragma alloc_text(PAGE, USBSTOR_SendPassThrough)
|
|
#pragma alloc_text(PAGE, USBSTOR_IssueInternalCdb)
|
|
#pragma alloc_text(PAGE, USBSTOR_GetInquiryData)
|
|
#pragma alloc_text(PAGE, USBSTOR_IsFloppyDevice)
|
|
#endif
|
|
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_DeviceControl()
|
|
//
|
|
// Dispatch routine which handles IRP_MJ_DEVICE_CONTROL
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_DeviceControl (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
ULONG ioControlCode;
|
|
NTSTATUS ntStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
DBGPRINT(2, ("enter: USBSTOR_DeviceControl\n"));
|
|
|
|
//LOGENTRY('IOCT', DeviceObject, Irp, 0);
|
|
|
|
DBGFBRK(DBGF_BRK_IOCTL);
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
// Only the PDO should handle these ioctls
|
|
//
|
|
if (deviceExtension->Type == USBSTOR_DO_TYPE_PDO)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
|
|
fdoDeviceExtension = ((PPDO_DEVICE_EXTENSION)deviceExtension)->ParentFDO->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
|
|
|
|
switch (ioControlCode)
|
|
{
|
|
case IOCTL_STORAGE_QUERY_PROPERTY:
|
|
ntStatus = USBSTOR_QueryProperty(DeviceObject, Irp);
|
|
break;
|
|
|
|
case IOCTL_SCSI_PASS_THROUGH:
|
|
case IOCTL_SCSI_PASS_THROUGH_DIRECT:
|
|
|
|
ntStatus = USBSTOR_SendPassThrough(DeviceObject, Irp);
|
|
|
|
Irp->IoStatus.Status = ntStatus;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
break;
|
|
|
|
|
|
case IOCTL_SCSI_GET_ADDRESS: // XXXXX
|
|
DBGPRINT(2, ("IOCTL_SCSI_GET_ADDRESS\n"));
|
|
goto IoctlNotSupported;
|
|
|
|
|
|
case IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER:
|
|
//
|
|
// Pass the Irp down the stack
|
|
//
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
|
|
ntStatus = IoCallDriver(fdoDeviceExtension->StackDeviceObject,
|
|
Irp);
|
|
break;
|
|
|
|
|
|
default:
|
|
IoctlNotSupported:
|
|
// Maybe we can just ignore these. Print debug info
|
|
// for now so we know what IOCTLs that we've seen so
|
|
// far that we fail.
|
|
//
|
|
DBGPRINT(2, ("ioControlCode not supported 0x%08X\n",
|
|
ioControlCode));
|
|
|
|
DBGFBRK(DBGF_BRK_IOCTL);
|
|
|
|
ntStatus = STATUS_NOT_SUPPORTED;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(deviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
DBGPRINT(2, ("ioctl not supported for FDO\n"));
|
|
|
|
ntStatus = STATUS_NOT_SUPPORTED;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
DBGPRINT(2, ("exit: USBSTOR_DeviceControl %08X\n", ntStatus));
|
|
|
|
//LOGENTRY('ioct', ntStatus, 0, 0);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_QueryProperty()
|
|
//
|
|
// Dispatch routine which handles IRP_MJ_DEVICE_CONTROL,
|
|
// IOCTL_STORAGE_QUERY_PROPERTY for the PDO
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_QueryProperty (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSTORAGE_PROPERTY_QUERY query;
|
|
ULONG inputLength;
|
|
ULONG outputLength;
|
|
NTSTATUS ntStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
DBGPRINT(2, ("enter: USBSTOR_QueryProperty\n"));
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
query = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
inputLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
|
|
|
|
outputLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
if (inputLength < sizeof(STORAGE_PROPERTY_QUERY))
|
|
{
|
|
ntStatus = STATUS_INVALID_PARAMETER; // Bad InputBufferLength
|
|
outputLength = 0;
|
|
goto USBSTOR_QueryPropertyDone;
|
|
}
|
|
|
|
switch (query->PropertyId)
|
|
{
|
|
case StorageDeviceProperty:
|
|
|
|
switch (query->QueryType)
|
|
{
|
|
case PropertyExistsQuery:
|
|
ntStatus = STATUS_SUCCESS;
|
|
outputLength = 0;
|
|
break;
|
|
|
|
case PropertyStandardQuery:
|
|
ntStatus = USBSTOR_BuildDeviceDescriptor(
|
|
DeviceObject,
|
|
Irp->AssociatedIrp.SystemBuffer,
|
|
&outputLength);
|
|
break;
|
|
|
|
default:
|
|
ntStatus = STATUS_INVALID_PARAMETER_2; // Bad QueryType
|
|
outputLength = 0;
|
|
break;
|
|
|
|
}
|
|
break;
|
|
|
|
case StorageAdapterProperty:
|
|
|
|
switch (query->QueryType)
|
|
{
|
|
case PropertyExistsQuery:
|
|
ntStatus = STATUS_SUCCESS;
|
|
outputLength = 0;
|
|
break;
|
|
|
|
case PropertyStandardQuery:
|
|
ntStatus = USBSTOR_BuildAdapterDescriptor(
|
|
DeviceObject,
|
|
Irp->AssociatedIrp.SystemBuffer,
|
|
&outputLength);
|
|
break;
|
|
|
|
default:
|
|
ntStatus = STATUS_INVALID_PARAMETER_2; // Bad QueryType
|
|
outputLength = 0;
|
|
break;
|
|
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
ntStatus = STATUS_INVALID_PARAMETER_1; // Bad PropertyId
|
|
outputLength = 0;
|
|
break;
|
|
}
|
|
|
|
USBSTOR_QueryPropertyDone:
|
|
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = outputLength;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
DBGPRINT(2, ("exit: USBSTOR_QueryProperty %08X\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_BuildDeviceDescriptor()
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_BuildDeviceDescriptor (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor,
|
|
IN OUT PULONG DescriptorLength
|
|
)
|
|
{
|
|
PPDO_DEVICE_EXTENSION pdoDeviceExtension;
|
|
PINQUIRYDATA inquiryData;
|
|
LONG inquiryLength;
|
|
STORAGE_DEVICE_DESCRIPTOR localDescriptor;
|
|
PUCHAR currentOffset;
|
|
LONG bytesRemaining;
|
|
|
|
PAGED_CODE();
|
|
|
|
DBGPRINT(2, ("enter: USBSTOR_BuildDeviceDescriptor\n"));
|
|
|
|
// Get a pointer to our Inquiry data
|
|
//
|
|
pdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(pdoDeviceExtension->Type == USBSTOR_DO_TYPE_PDO);
|
|
|
|
inquiryData = (PINQUIRYDATA)pdoDeviceExtension->InquiryDataBuffer;
|
|
|
|
// inquiryLength = 5 + inquiryData->AdditionalLength;
|
|
//
|
|
// if (inquiryLength > INQUIRYDATABUFFERSIZE)
|
|
// {
|
|
// inquiryLength = INQUIRYDATABUFFERSIZE;
|
|
// }
|
|
//
|
|
// Just return whatever we got from the device and leave it up to
|
|
// whoever looks at this information to decide how much is valid.
|
|
//
|
|
inquiryLength = sizeof(pdoDeviceExtension->InquiryDataBuffer);
|
|
|
|
// Zero initialize the output buffer
|
|
//
|
|
RtlZeroMemory(Descriptor, *DescriptorLength);
|
|
|
|
|
|
// Build the temp local descriptor
|
|
//
|
|
RtlZeroMemory(&localDescriptor, sizeof(localDescriptor));
|
|
|
|
localDescriptor.Version = sizeof(localDescriptor);
|
|
|
|
localDescriptor.Size = FIELD_OFFSET(STORAGE_DEVICE_DESCRIPTOR,
|
|
RawDeviceProperties) +
|
|
inquiryLength +
|
|
sizeof(inquiryData->VendorId) + 1 +
|
|
sizeof(inquiryData->ProductId) + 1 +
|
|
sizeof(inquiryData->ProductRevisionLevel) + 1;
|
|
|
|
localDescriptor.DeviceType = inquiryData->DeviceType;
|
|
localDescriptor.DeviceTypeModifier = inquiryData->DeviceTypeModifier;
|
|
localDescriptor.RemovableMedia = inquiryData->RemovableMedia;
|
|
|
|
localDescriptor.BusType = BusTypeUsb;
|
|
|
|
|
|
// Start copying as much data as will fit in the output buffer
|
|
//
|
|
currentOffset = (PUCHAR)Descriptor;
|
|
bytesRemaining = *DescriptorLength;
|
|
|
|
|
|
// First copy the temp local descriptor
|
|
//
|
|
RtlCopyMemory(currentOffset,
|
|
&localDescriptor,
|
|
min(bytesRemaining,
|
|
FIELD_OFFSET(STORAGE_DEVICE_DESCRIPTOR,
|
|
RawDeviceProperties)));
|
|
|
|
bytesRemaining -= FIELD_OFFSET(STORAGE_DEVICE_DESCRIPTOR,
|
|
RawDeviceProperties);
|
|
|
|
if (bytesRemaining <= 0)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// This should advance us to RawDeviceProperties[0]
|
|
//
|
|
currentOffset += FIELD_OFFSET(STORAGE_DEVICE_DESCRIPTOR,
|
|
RawDeviceProperties);
|
|
|
|
// Next copy the Inquiry data
|
|
//
|
|
Descriptor->RawPropertiesLength = min(bytesRemaining, inquiryLength);
|
|
|
|
RtlCopyMemory(currentOffset,
|
|
inquiryData,
|
|
Descriptor->RawPropertiesLength);
|
|
|
|
bytesRemaining -= inquiryLength;
|
|
|
|
if (bytesRemaining <= 0)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
currentOffset += inquiryLength;
|
|
|
|
|
|
// Now copy the Vendor Id
|
|
//
|
|
RtlCopyMemory(currentOffset,
|
|
inquiryData->VendorId,
|
|
min(bytesRemaining, sizeof(inquiryData->VendorId)));
|
|
|
|
bytesRemaining -= sizeof(inquiryData->VendorId) + 1; // include null
|
|
|
|
if (bytesRemaining >= 0)
|
|
{
|
|
Descriptor->VendorIdOffset = (ULONG)((ULONG_PTR) currentOffset -
|
|
(ULONG_PTR) Descriptor);
|
|
}
|
|
|
|
if (bytesRemaining <= 0)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
currentOffset += sizeof(inquiryData->VendorId) + 1;
|
|
|
|
|
|
// Now copy the Product Id
|
|
//
|
|
RtlCopyMemory(currentOffset,
|
|
inquiryData->ProductId,
|
|
min(bytesRemaining, sizeof(inquiryData->ProductId)));
|
|
|
|
bytesRemaining -= sizeof(inquiryData->ProductId) + 1; // include null
|
|
|
|
if (bytesRemaining >= 0)
|
|
{
|
|
Descriptor->ProductIdOffset = (ULONG)((ULONG_PTR) currentOffset -
|
|
(ULONG_PTR) Descriptor);
|
|
}
|
|
|
|
if (bytesRemaining <= 0)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
currentOffset += sizeof(inquiryData->ProductId) + 1;
|
|
|
|
|
|
// And finally copy the Product Revision Level
|
|
//
|
|
RtlCopyMemory(currentOffset,
|
|
inquiryData->ProductRevisionLevel,
|
|
min(bytesRemaining, sizeof(inquiryData->ProductRevisionLevel)));
|
|
|
|
bytesRemaining -= sizeof(inquiryData->ProductRevisionLevel) + 1; // include null
|
|
|
|
if (bytesRemaining >= 0)
|
|
{
|
|
Descriptor->ProductRevisionOffset = (ULONG)((ULONG_PTR) currentOffset -
|
|
(ULONG_PTR) Descriptor);
|
|
}
|
|
|
|
if (bytesRemaining <= 0)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
*DescriptorLength -= bytesRemaining;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_BuildAdapterDescriptor()
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_BuildAdapterDescriptor (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor,
|
|
IN OUT PULONG DescriptorLength
|
|
)
|
|
{
|
|
PPDO_DEVICE_EXTENSION pdoDeviceExtension;
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
STORAGE_ADAPTER_DESCRIPTOR localDescriptor;
|
|
NTSTATUS ntStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
DBGPRINT(2, ("enter: USBSTOR_BuildAdapterDescriptor\n"));
|
|
|
|
pdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(pdoDeviceExtension->Type == USBSTOR_DO_TYPE_PDO);
|
|
|
|
fdoDeviceExtension = pdoDeviceExtension->ParentFDO->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
localDescriptor.Version = sizeof(localDescriptor);
|
|
localDescriptor.Size = sizeof(localDescriptor);
|
|
|
|
localDescriptor.MaximumTransferLength = USBSTOR_MAX_TRANSFER_SIZE;
|
|
localDescriptor.MaximumPhysicalPages = USBSTOR_MAX_TRANSFER_PAGES;
|
|
localDescriptor.AlignmentMask = 0;
|
|
localDescriptor.AdapterUsesPio = FALSE;
|
|
localDescriptor.AdapterScansDown = FALSE;
|
|
localDescriptor.CommandQueueing = FALSE;
|
|
localDescriptor.AcceleratedTransfer = FALSE;
|
|
|
|
localDescriptor.BusType = BusTypeUsb;
|
|
|
|
localDescriptor.BusMajorVersion = fdoDeviceExtension->DeviceIsHighSpeed ?
|
|
2 : 1;
|
|
|
|
localDescriptor.BusMinorVersion = 0;
|
|
|
|
if (*DescriptorLength > localDescriptor.Size)
|
|
{
|
|
*DescriptorLength = localDescriptor.Size;
|
|
}
|
|
|
|
RtlCopyMemory(Descriptor,
|
|
&localDescriptor,
|
|
*DescriptorLength);
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
DBGPRINT(2, ("exit: USBSTOR_BuildAdapterDescriptor %08X\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_SendPassThrough()
|
|
//
|
|
// This routine handles IOCTL_SCSI_PASS_THROUGH requests.
|
|
// It creates an Irp/Srb which is processed normally by the port driver.
|
|
// This call is synchornous.
|
|
//
|
|
// (This routine borrowed from ATAPI.SYS)
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_SendPassThrough (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP RequestIrp
|
|
)
|
|
{
|
|
PPDO_DEVICE_EXTENSION pdoDeviceExtension;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_PASS_THROUGH srbControl;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
KEVENT event;
|
|
LARGE_INTEGER startingOffset;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
ULONG outputLength;
|
|
ULONG length;
|
|
ULONG bufferOffset;
|
|
PVOID buffer;
|
|
PVOID endByte;
|
|
PVOID senseBuffer;
|
|
UCHAR majorCode;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
DBGPRINT(2, ("enter: USBSTOR_SendPassThrough\n"));
|
|
|
|
pdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(pdoDeviceExtension->Type == USBSTOR_DO_TYPE_PDO);
|
|
|
|
startingOffset.QuadPart = (LONGLONG)1;
|
|
|
|
// Get a pointer to the control block.
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(RequestIrp);
|
|
srbControl = RequestIrp->AssociatedIrp.SystemBuffer;
|
|
|
|
// Validiate the user buffer.
|
|
//
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(SCSI_PASS_THROUGH))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (srbControl->Length != sizeof(SCSI_PASS_THROUGH) &&
|
|
srbControl->Length != sizeof(SCSI_PASS_THROUGH_DIRECT))
|
|
{
|
|
return STATUS_REVISION_MISMATCH;
|
|
}
|
|
|
|
outputLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
// Validate the rest of the buffer parameters.
|
|
//
|
|
if (srbControl->CdbLength > 16)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (srbControl->SenseInfoLength != 0 &&
|
|
(srbControl->Length > srbControl->SenseInfoOffset ||
|
|
(srbControl->SenseInfoOffset + srbControl->SenseInfoLength >
|
|
srbControl->DataBufferOffset && srbControl->DataTransferLength != 0)))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
majorCode = !srbControl->DataIn ? IRP_MJ_WRITE : IRP_MJ_READ;
|
|
|
|
if (srbControl->DataTransferLength == 0)
|
|
{
|
|
length = 0;
|
|
buffer = NULL;
|
|
bufferOffset = 0;
|
|
majorCode = IRP_MJ_FLUSH_BUFFERS;
|
|
|
|
}
|
|
else if ((srbControl->DataBufferOffset > outputLength) &&
|
|
(srbControl->DataBufferOffset >
|
|
irpStack->Parameters.DeviceIoControl.InputBufferLength))
|
|
{
|
|
// The data buffer offset is greater than system buffer. Assume this
|
|
// is a user mode address.
|
|
//
|
|
if ((srbControl->SenseInfoOffset + srbControl->SenseInfoLength >
|
|
outputLength) &&
|
|
srbControl->SenseInfoLength)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
length = srbControl->DataTransferLength;
|
|
buffer = (PCHAR) srbControl->DataBufferOffset;
|
|
bufferOffset = 0;
|
|
|
|
// make sure the user buffer is valid
|
|
//
|
|
if (RequestIrp->RequestorMode != KernelMode)
|
|
{
|
|
if (length)
|
|
{
|
|
endByte = (PVOID)((PCHAR)buffer + length - 1);
|
|
|
|
if (buffer >= endByte)
|
|
{
|
|
return STATUS_INVALID_USER_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (srbControl->DataIn != SCSI_IOCTL_DATA_IN)
|
|
{
|
|
if (((srbControl->SenseInfoOffset + srbControl->SenseInfoLength >
|
|
outputLength) &&
|
|
srbControl->SenseInfoLength != 0) ||
|
|
(srbControl->DataBufferOffset + srbControl->DataTransferLength >
|
|
irpStack->Parameters.DeviceIoControl.InputBufferLength) ||
|
|
(srbControl->Length > srbControl->DataBufferOffset))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
if (srbControl->DataIn)
|
|
{
|
|
if ((srbControl->DataBufferOffset + srbControl->DataTransferLength >
|
|
outputLength) ||
|
|
(srbControl->Length > srbControl->DataBufferOffset))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
length = (ULONG)srbControl->DataBufferOffset +
|
|
srbControl->DataTransferLength;
|
|
|
|
buffer = (PUCHAR) srbControl;
|
|
bufferOffset = (ULONG)srbControl->DataBufferOffset;
|
|
}
|
|
|
|
// Validate that the request isn't too large for the miniport.
|
|
//
|
|
if (srbControl->DataTransferLength &&
|
|
((ADDRESS_AND_SIZE_TO_SPAN_PAGES(
|
|
(PUCHAR)buffer+bufferOffset,
|
|
srbControl->DataTransferLength
|
|
) > USBSTOR_MAX_TRANSFER_PAGES) ||
|
|
(USBSTOR_MAX_TRANSFER_SIZE < srbControl->DataTransferLength)))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (srbControl->TimeOutValue == 0 ||
|
|
srbControl->TimeOutValue > 30 * 60 * 60)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Check for illegal command codes.
|
|
//
|
|
|
|
if (srbControl->Cdb[0] == SCSIOP_COPY ||
|
|
srbControl->Cdb[0] == SCSIOP_COMPARE ||
|
|
srbControl->Cdb[0] == SCSIOP_COPY_COMPARE)
|
|
{
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
// If this request came through a normal device control rather than from
|
|
// class driver then the device must exist and be unclaimed. Class drivers
|
|
// will set the minor function code for the device control. It is always
|
|
// zero for a user request.
|
|
//
|
|
if (irpStack->MinorFunction == 0 &&
|
|
pdoDeviceExtension->Claimed)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Allocate an aligned request sense buffer.
|
|
//
|
|
if (srbControl->SenseInfoLength != 0)
|
|
{
|
|
senseBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
|
srbControl->SenseInfoLength,
|
|
POOL_TAG);
|
|
if (senseBuffer == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
senseBuffer = NULL;
|
|
}
|
|
|
|
//
|
|
// Initialize the notification event.
|
|
//
|
|
|
|
KeInitializeEvent(&event,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
// Build IRP for this request.
|
|
// Note we do this synchronously for two reasons. If it was done
|
|
// asynchonously then the completion code would have to make a special
|
|
// check to deallocate the buffer. Second if a completion routine were
|
|
// used then an addation stack locate would be needed.
|
|
//
|
|
|
|
try
|
|
{
|
|
irp = IoBuildSynchronousFsdRequest(
|
|
majorCode,
|
|
DeviceObject,
|
|
buffer,
|
|
length,
|
|
&startingOffset,
|
|
&event,
|
|
&ioStatusBlock);
|
|
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
// An exception was incurred while attempting to probe the
|
|
// caller's parameters. Dereference the file object and return
|
|
// an appropriate error status code.
|
|
//
|
|
if (senseBuffer != NULL)
|
|
{
|
|
ExFreePool(senseBuffer);
|
|
}
|
|
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
if (irp == NULL)
|
|
{
|
|
if (senseBuffer != NULL)
|
|
{
|
|
ExFreePool(senseBuffer);
|
|
}
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
irpStack = IoGetNextIrpStackLocation(irp);
|
|
|
|
// Set major code.
|
|
//
|
|
irpStack->MajorFunction = IRP_MJ_SCSI;
|
|
irpStack->MinorFunction = 1;
|
|
|
|
// Fill in SRB fields.
|
|
//
|
|
irpStack->Parameters.Others.Argument1 = &srb;
|
|
|
|
// Zero out the srb.
|
|
//
|
|
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
// Fill in the srb.
|
|
//
|
|
srb.Length = SCSI_REQUEST_BLOCK_SIZE;
|
|
srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
srb.SrbStatus = SRB_STATUS_PENDING;
|
|
srb.CdbLength = srbControl->CdbLength;
|
|
srb.SenseInfoBufferLength = srbControl->SenseInfoLength;
|
|
|
|
switch (srbControl->DataIn)
|
|
{
|
|
case SCSI_IOCTL_DATA_OUT:
|
|
if (srbControl->DataTransferLength)
|
|
{
|
|
srb.SrbFlags = SRB_FLAGS_DATA_OUT;
|
|
}
|
|
break;
|
|
|
|
case SCSI_IOCTL_DATA_IN:
|
|
if (srbControl->DataTransferLength)
|
|
{
|
|
srb.SrbFlags = SRB_FLAGS_DATA_IN;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
srb.SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DATA_OUT;
|
|
break;
|
|
}
|
|
|
|
if (srbControl->DataTransferLength == 0)
|
|
{
|
|
srb.SrbFlags = 0;
|
|
}
|
|
else
|
|
{
|
|
// Flush the data buffer for output. This will insure that the data is
|
|
// written back to memory.
|
|
//
|
|
KeFlushIoBuffers(irp->MdlAddress, FALSE, TRUE);
|
|
}
|
|
|
|
srb.DataTransferLength = srbControl->DataTransferLength;
|
|
srb.TimeOutValue = srbControl->TimeOutValue;
|
|
srb.DataBuffer = (PCHAR) buffer + bufferOffset;
|
|
srb.SenseInfoBuffer = senseBuffer;
|
|
srb.OriginalRequest = irp;
|
|
RtlCopyMemory(srb.Cdb, srbControl->Cdb, srbControl->CdbLength);
|
|
|
|
// Call port driver to handle this request.
|
|
//
|
|
status = IoCallDriver(DeviceObject, irp);
|
|
|
|
// Wait for request to complete.
|
|
//
|
|
if (status == STATUS_PENDING)
|
|
{
|
|
KeWaitForSingleObject(&event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
ioStatusBlock.Status = status;
|
|
}
|
|
|
|
// Copy the returned values from the srb to the control structure.
|
|
//
|
|
srbControl->ScsiStatus = srb.ScsiStatus;
|
|
|
|
if (srb.SrbStatus & SRB_STATUS_AUTOSENSE_VALID)
|
|
{
|
|
// Set the status to success so that the data is returned.
|
|
//
|
|
ioStatusBlock.Status = STATUS_SUCCESS;
|
|
srbControl->SenseInfoLength = srb.SenseInfoBufferLength;
|
|
|
|
// Copy the sense data to the system buffer.
|
|
//
|
|
RtlCopyMemory((PUCHAR) srbControl + srbControl->SenseInfoOffset,
|
|
senseBuffer,
|
|
srb.SenseInfoBufferLength);
|
|
}
|
|
else
|
|
{
|
|
srbControl->SenseInfoLength = 0;
|
|
}
|
|
|
|
|
|
// Free the sense buffer.
|
|
//
|
|
if (senseBuffer != NULL)
|
|
{
|
|
ExFreePool(senseBuffer);
|
|
}
|
|
|
|
// If the srb status is buffer underrun then set the status to success.
|
|
// This insures that the data will be returned to the caller.
|
|
//
|
|
if (SRB_STATUS(srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN)
|
|
{
|
|
ioStatusBlock.Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
srbControl->DataTransferLength = srb.DataTransferLength;
|
|
|
|
// Set the information length
|
|
//
|
|
if (!srbControl->DataIn || bufferOffset == 0)
|
|
{
|
|
|
|
RequestIrp->IoStatus.Information = srbControl->SenseInfoOffset +
|
|
srbControl->SenseInfoLength;
|
|
}
|
|
else
|
|
{
|
|
RequestIrp->IoStatus.Information = srbControl->DataBufferOffset +
|
|
srbControl->DataTransferLength;
|
|
}
|
|
|
|
RequestIrp->IoStatus.Status = ioStatusBlock.Status;
|
|
|
|
DBGPRINT(2, ("exit: USBSTOR_SendPassThrough %08X\n",
|
|
ioStatusBlock.Status));
|
|
|
|
return ioStatusBlock.Status;
|
|
}
|
|
|
|
|
|
//******************************************************************************
|
|
//
|
|
// IsRequestValid()
|
|
//
|
|
// Validates IRP_MJ_SCSI SRB_FUNCTION_EXECUTE_SCSI requests against
|
|
// assumptions made later when processing the Srb.
|
|
//
|
|
//******************************************************************************
|
|
|
|
BOOLEAN
|
|
IsRequestValid (
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
BOOLEAN result;
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
// Default return value unless a problem is found.
|
|
//
|
|
result = TRUE;
|
|
|
|
// Note: SRB_FLAGS_UNSPECIFIED_DIRECTION is defined as
|
|
// (SRB_FLAGS_DATA_IN | SRB_FLAGS_DATA_OUT)
|
|
|
|
if ((srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) == 0) {
|
|
|
|
// Neither SRB_FLAGS_DATA_IN nor SRB_FLAGS_DATA_IN is set.
|
|
// A transfer buffer should not be specified.
|
|
|
|
if (srb->DataTransferLength ||
|
|
srb->DataBuffer ||
|
|
Irp->MdlAddress) {
|
|
|
|
result = FALSE;
|
|
}
|
|
|
|
} else if ((srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) ==
|
|
SRB_FLAGS_UNSPECIFIED_DIRECTION) {
|
|
|
|
// Both SRB_FLAGS_DATA_IN and SRB_FLAGS_DATA_IN are set.
|
|
// We don't currently have a way to resolve this.
|
|
|
|
result = FALSE;
|
|
|
|
} else {
|
|
|
|
// Either SRB_FLAGS_DATA_IN or SRB_FLAGS_DATA_IN is set.
|
|
// A transfer buffer should be specified.
|
|
|
|
if (!srb->DataTransferLength ||
|
|
srb->DataTransferLength > USBSTOR_MAX_TRANSFER_SIZE ||
|
|
//!srb->DataBuffer ||
|
|
!Irp->MdlAddress) {
|
|
|
|
result = FALSE;
|
|
}
|
|
}
|
|
|
|
if (!result) {
|
|
|
|
DBGPRINT(1, ("SrbFlags %08X, DataTransferLength %08X, "
|
|
"DataBuffer %08X, MdlAddress %08X\n",
|
|
srb->SrbFlags,
|
|
srb->DataTransferLength,
|
|
srb->DataBuffer,
|
|
Irp->MdlAddress));
|
|
|
|
DBGPRINT(1, ("Irp %08X, Srb %08X\n",
|
|
Irp, srb));
|
|
|
|
DBGFBRK(DBGF_BRK_INVALID_REQ);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_Scsi()
|
|
//
|
|
// Dispatch routine which handles IRP_MJ_SCSI
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_Scsi (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PPDO_DEVICE_EXTENSION pdoDeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
KIRQL irql;
|
|
NTSTATUS ntStatus;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_Scsi\n"));
|
|
|
|
DBGFBRK(DBGF_BRK_SCSI);
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
// Only the PDO should handle IRP_MJ_SCSI
|
|
//
|
|
if (deviceExtension->Type == USBSTOR_DO_TYPE_PDO)
|
|
{
|
|
pdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
LOGENTRY('SCSI', DeviceObject, Irp, srb->Function);
|
|
|
|
switch (srb->Function)
|
|
{
|
|
case SRB_FUNCTION_EXECUTE_SCSI:
|
|
|
|
DBGPRINT(3, ("SRB_FUNCTION_EXECUTE_SCSI\n"));
|
|
|
|
// XXXXX check STOP / REMOVE flags
|
|
|
|
// XXXXX check SRB_FLAGS_BYPASS_LOCKED_QUEUE flag
|
|
|
|
if (IsRequestValid(Irp))
|
|
{
|
|
srb->SrbStatus = SRB_STATUS_PENDING;
|
|
|
|
IoMarkIrpPending(Irp);
|
|
|
|
IoStartPacket(pdoDeviceExtension->ParentFDO,
|
|
Irp,
|
|
&srb->QueueSortKey,
|
|
USBSTOR_CancelIo);
|
|
|
|
ntStatus = STATUS_PENDING;
|
|
}
|
|
else
|
|
{
|
|
ntStatus = STATUS_INVALID_PARAMETER;
|
|
}
|
|
break;
|
|
|
|
|
|
case SRB_FUNCTION_FLUSH:
|
|
|
|
DBGPRINT(2, ("SRB_FUNCTION_FLUSH\n"));
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
break;
|
|
|
|
case SRB_FUNCTION_CLAIM_DEVICE:
|
|
|
|
DBGPRINT(2, ("SRB_FUNCTION_CLAIM_DEVICE\n"));
|
|
|
|
//KeAcquireSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock,
|
|
// &irql);
|
|
{
|
|
if (pdoDeviceExtension->Claimed)
|
|
{
|
|
ntStatus = STATUS_DEVICE_BUSY;
|
|
srb->SrbStatus = SRB_STATUS_BUSY;
|
|
}
|
|
else
|
|
{
|
|
pdoDeviceExtension->Claimed = TRUE;
|
|
srb->DataBuffer = DeviceObject;
|
|
ntStatus = STATUS_SUCCESS;
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
//KeReleaseSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock,
|
|
// irql);
|
|
break;
|
|
|
|
case SRB_FUNCTION_RELEASE_DEVICE:
|
|
|
|
DBGPRINT(2, ("SRB_FUNCTION_RELEASE_DEVICE\n"));
|
|
|
|
//KeAcquireSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock,
|
|
// &irql);
|
|
{
|
|
pdoDeviceExtension->Claimed = FALSE;
|
|
}
|
|
//KeReleaseSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock,
|
|
// irql);
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
|
|
DBGPRINT(2, ("Unhandled SRB function %d\n", srb->Function));
|
|
|
|
ntStatus = STATUS_NOT_SUPPORTED;
|
|
srb->SrbStatus = SRB_STATUS_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(deviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
DBGPRINT(2, ("IRP_MJ_SCSI not supported for FDO\n"));
|
|
|
|
ntStatus = STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (ntStatus != STATUS_PENDING)
|
|
{
|
|
Irp->IoStatus.Status = ntStatus;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_Scsi %08X\n", ntStatus));
|
|
|
|
LOGENTRY('scsi', ntStatus, 0, 0);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_TranslateCDBSubmit()
|
|
//
|
|
// Called by USBSTOR_StartIo() before a request is started.
|
|
//
|
|
//******************************************************************************
|
|
|
|
VOID
|
|
USBSTOR_TranslateCDBSubmit (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PSCSI_REQUEST_BLOCK Srb
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PCDB cdb;
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
if (fdoDeviceExtension->InterfaceDescriptor->bInterfaceSubClass ==
|
|
USBSTOR_SUBCLASS_SCSI_PASSTHROUGH)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Save the original CDB
|
|
//
|
|
cdb = (PCDB)Srb->Cdb;
|
|
|
|
RtlCopyMemory(fdoDeviceExtension->OriginalCDB, cdb, 16);
|
|
|
|
// Make sure the CDB is padded with zero bytes.
|
|
//
|
|
if (Srb->CdbLength < 16)
|
|
{
|
|
RtlZeroMemory(&Srb->Cdb[Srb->CdbLength],
|
|
16 - Srb->CdbLength);
|
|
|
|
}
|
|
Srb->CdbLength = 12;
|
|
|
|
switch (Srb->Cdb[0])
|
|
{
|
|
// Send a SCSIOP_START_STOP_UNIT request instead of a
|
|
// SCSIOP_TEST_UNIT_READY request for selected buggy
|
|
// devices which don't otherwise update their internal
|
|
// geometry information when the media changes.
|
|
//
|
|
case SCSIOP_TEST_UNIT_READY:
|
|
|
|
if (TEST_FLAG(fdoDeviceExtension->DeviceHackFlags,
|
|
DHF_TUR_START_UNIT))
|
|
{
|
|
// Zero the new CDB
|
|
//
|
|
RtlZeroMemory(cdb, 16);
|
|
|
|
cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
|
|
cdb->START_STOP.Start = 1;
|
|
}
|
|
break;
|
|
|
|
// Convert 6-byte Mode Sense to 10-byte Mode Sense
|
|
//
|
|
case SCSIOP_MODE_SENSE:
|
|
{
|
|
UCHAR PageCode;
|
|
UCHAR Length;
|
|
|
|
// Extract the relevant params from original CDB
|
|
//
|
|
PageCode = cdb->MODE_SENSE.PageCode;
|
|
Length = cdb->MODE_SENSE.AllocationLength;
|
|
|
|
// Zero the new CDB
|
|
//
|
|
RtlZeroMemory(cdb, 16);
|
|
|
|
// Insert the relevant params into the translated CDB
|
|
//
|
|
cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
|
|
cdb->MODE_SENSE10.PageCode = PageCode;
|
|
cdb->MODE_SENSE10.AllocationLength[1] = Length;
|
|
}
|
|
break;
|
|
|
|
// Convert 6-byte Mode Select to 10-byte Mode Select
|
|
//
|
|
case SCSIOP_MODE_SELECT:
|
|
{
|
|
UCHAR SPBit;
|
|
UCHAR Length;
|
|
|
|
// Extract the relevant params from original CDB
|
|
//
|
|
SPBit = cdb->MODE_SELECT.SPBit;
|
|
Length = cdb->MODE_SELECT.ParameterListLength;
|
|
|
|
// Zero the new CDB
|
|
//
|
|
RtlZeroMemory(cdb, 16);
|
|
|
|
// Insert the relevant params into the translated CDB
|
|
//
|
|
cdb->MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10;
|
|
cdb->MODE_SELECT10.SPBit = SPBit;
|
|
cdb->MODE_SELECT10.PFBit = 1;
|
|
cdb->MODE_SELECT10.ParameterListLength[1] = Length;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_TranslateSrbStatus()
|
|
//
|
|
// This routine translates an srb status into an ntstatus.
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_TranslateSrbStatus(
|
|
IN PSCSI_REQUEST_BLOCK Srb
|
|
)
|
|
{
|
|
switch (SRB_STATUS(Srb->SrbStatus)) {
|
|
case SRB_STATUS_INVALID_LUN:
|
|
case SRB_STATUS_INVALID_TARGET_ID:
|
|
case SRB_STATUS_NO_DEVICE:
|
|
case SRB_STATUS_NO_HBA:
|
|
return(STATUS_DEVICE_DOES_NOT_EXIST);
|
|
case SRB_STATUS_COMMAND_TIMEOUT:
|
|
case SRB_STATUS_BUS_RESET:
|
|
case SRB_STATUS_TIMEOUT:
|
|
return(STATUS_IO_TIMEOUT);
|
|
case SRB_STATUS_SELECTION_TIMEOUT:
|
|
return(STATUS_DEVICE_NOT_CONNECTED);
|
|
case SRB_STATUS_BAD_FUNCTION:
|
|
case SRB_STATUS_BAD_SRB_BLOCK_LENGTH:
|
|
return(STATUS_INVALID_DEVICE_REQUEST);
|
|
case SRB_STATUS_DATA_OVERRUN:
|
|
return(STATUS_BUFFER_OVERFLOW);
|
|
default:
|
|
return(STATUS_IO_DEVICE_ERROR);
|
|
}
|
|
|
|
return(STATUS_IO_DEVICE_ERROR);
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_TranslateCDBComplete()
|
|
//
|
|
// Called everywhere a request is completed.
|
|
//
|
|
//******************************************************************************
|
|
|
|
VOID
|
|
USBSTOR_TranslateCDBComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PSCSI_REQUEST_BLOCK Srb
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PCDB cdb;
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
if (fdoDeviceExtension->InterfaceDescriptor->bInterfaceSubClass ==
|
|
USBSTOR_SUBCLASS_SCSI_PASSTHROUGH)
|
|
{
|
|
#if DBG
|
|
if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
|
|
(Srb->SenseInfoBufferLength >= 14))
|
|
{
|
|
PSENSE_DATA senseData;
|
|
|
|
senseData = (PSENSE_DATA)Srb->SenseInfoBuffer;
|
|
|
|
DBGPRINT(1, ("OP: %02X SenseKey %02X ASC %02X ASCQ %02X\n",
|
|
Srb->Cdb[0],
|
|
senseData->SenseKey,
|
|
senseData->AdditionalSenseCode,
|
|
senseData->AdditionalSenseCodeQualifier));
|
|
}
|
|
#endif
|
|
if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS)
|
|
{
|
|
Irp->IoStatus.Status = USBSTOR_TranslateSrbStatus(Srb);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (Srb->Cdb[0] != fdoDeviceExtension->OriginalCDB[0])
|
|
{
|
|
cdb = (PCDB)Srb->Cdb;
|
|
|
|
switch (Srb->Cdb[0])
|
|
{
|
|
// Convert 10-byte Mode Sense back to 6-byte Mode Sense
|
|
//
|
|
case SCSIOP_MODE_SENSE10:
|
|
{
|
|
if ((SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS ||
|
|
SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) &&
|
|
Srb->DataTransferLength >= sizeof(MODE_PARAMETER_HEADER10))
|
|
{
|
|
PMODE_PARAMETER_HEADER hdr6;
|
|
PMODE_PARAMETER_HEADER10 hdr10;
|
|
|
|
hdr6 = (PMODE_PARAMETER_HEADER) Srb->DataBuffer;
|
|
hdr10 = (PMODE_PARAMETER_HEADER10)Srb->DataBuffer;
|
|
|
|
// Convert the 10-byte header to a 6-byte header
|
|
//
|
|
hdr6->ModeDataLength = hdr10->ModeDataLength[1];
|
|
|
|
hdr6->MediumType = hdr10->MediumType;
|
|
|
|
hdr6->DeviceSpecificParameter =
|
|
hdr10->DeviceSpecificParameter;
|
|
|
|
hdr6->BlockDescriptorLength =
|
|
hdr10->BlockDescriptorLength[1];
|
|
|
|
// Advance past headers
|
|
//
|
|
hdr6++;
|
|
hdr10++;
|
|
|
|
// Copy everything past the 10-byte header
|
|
//
|
|
RtlMoveMemory(hdr6,
|
|
hdr10,
|
|
(Srb->DataTransferLength -
|
|
sizeof(MODE_PARAMETER_HEADER10)));
|
|
|
|
// Adjust the return size to account for the smaller header
|
|
//
|
|
Srb->DataTransferLength -= (sizeof(MODE_PARAMETER_HEADER10) -
|
|
sizeof(MODE_PARAMETER_HEADER));
|
|
|
|
// Since we just shrunk Srb->DataTransferLength, don't
|
|
// we have SRB_STATUS_DATA_OVERRUN by definition???
|
|
//
|
|
if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)
|
|
{
|
|
Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN |
|
|
SRB_STATUS_AUTOSENSE_VALID;
|
|
}
|
|
else
|
|
{
|
|
Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Restore the original CDB
|
|
//
|
|
RtlCopyMemory(cdb, fdoDeviceExtension->OriginalCDB, 16);
|
|
}
|
|
|
|
#if DBG
|
|
if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
|
|
(Srb->SenseInfoBufferLength >= 14))
|
|
{
|
|
PSENSE_DATA senseData;
|
|
|
|
senseData = (PSENSE_DATA)Srb->SenseInfoBuffer;
|
|
|
|
DBGPRINT(1, ("OP: %02X SenseKey %02X ASC %02X ASCQ %02X\n",
|
|
Srb->Cdb[0],
|
|
senseData->SenseKey,
|
|
senseData->AdditionalSenseCode,
|
|
senseData->AdditionalSenseCodeQualifier));
|
|
}
|
|
#endif
|
|
|
|
if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS)
|
|
{
|
|
Irp->IoStatus.Status = USBSTOR_TranslateSrbStatus(Srb);
|
|
}
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_CancelIo()
|
|
//
|
|
// This routine runs at DPC level (until the cancel spinlock is released).
|
|
//
|
|
//******************************************************************************
|
|
|
|
VOID
|
|
USBSTOR_CancelIo (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
if (DeviceObject->CurrentIrp == Irp)
|
|
{
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
LOGENTRY('CAN1', DeviceObject, Irp, 0);
|
|
|
|
DBGPRINT(1, ("USBSTOR_CancelIo cancelling CurrentIrp\n"));
|
|
}
|
|
else if (KeRemoveEntryDeviceQueue(&DeviceObject->DeviceQueue,
|
|
&Irp->Tail.Overlay.DeviceQueueEntry))
|
|
{
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
LOGENTRY('CAN2', DeviceObject, Irp, 0);
|
|
|
|
DBGPRINT(1, ("USBSTOR_CancelIo cancelling queued Irp\n"));
|
|
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
else
|
|
{
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
}
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_StartIo()
|
|
//
|
|
// This routine handles IRP_MJ_SCSI, SRB_FUNCTION_EXECUTE_SCSI requests from
|
|
// the device the queue.
|
|
//
|
|
// This routine runs at DPC level.
|
|
//
|
|
//******************************************************************************
|
|
|
|
VOID
|
|
USBSTOR_StartIo (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
BOOLEAN startNext;
|
|
BOOLEAN deviceDisconnected;
|
|
BOOLEAN persistentError;
|
|
KIRQL irql;
|
|
NTSTATUS ntStatus;
|
|
|
|
LOGENTRY('STIO', DeviceObject, Irp, 0);
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_StartIo %08X %08X\n",
|
|
DeviceObject, Irp));
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get our Irp parameters
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
// Check to see if this is a power down Irp.
|
|
//
|
|
if (irpStack->MajorFunction == IRP_MJ_POWER)
|
|
{
|
|
// This is a power down Irp. Now that we know that no transfer
|
|
// requests are in progress, pass down the power Irp.
|
|
|
|
ASSERT(irpStack->MinorFunction == IRP_MN_SET_POWER);
|
|
ASSERT(irpStack->Parameters.Power.Type == DevicePowerState);
|
|
ASSERT(irpStack->Parameters.Power.State.DeviceState !=
|
|
PowerDeviceD0);
|
|
|
|
DBGPRINT(2, ("FDO Power Down Passing Down %08X %08X\n",
|
|
DeviceObject, Irp));
|
|
|
|
LOGENTRY('FPDC', DeviceObject, Irp, 0);
|
|
|
|
//
|
|
// Signal that it is time to pass the request down to the next
|
|
// lower driver
|
|
//
|
|
KeSetEvent(&fdoDeviceExtension->PowerDownEvent,
|
|
IO_NO_INCREMENT,
|
|
0);
|
|
|
|
// Leave the device queue blocked now by simply not calling
|
|
// IoStartNextPacket(). When we want to start the device queue
|
|
// again, simply call IoStartNextPacket().
|
|
|
|
return;
|
|
}
|
|
|
|
// If the Irp is not IRP_MJ_POWER it better be IRP_MJ_SCSI
|
|
//
|
|
ASSERT(irpStack->MajorFunction == IRP_MJ_SCSI);
|
|
|
|
// Check to see if the current Irp was cancelled.
|
|
//
|
|
IoAcquireCancelSpinLock(&irql);
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
|
|
if (Irp->Cancel)
|
|
{
|
|
// The current Irp was cancelled. Complete the request now, and start
|
|
// the next request, unless a reset is still in progress in which case
|
|
// the next request will be started when the reset completes.
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel(&fdoDeviceExtension->ExtensionDataSpinLock);
|
|
{
|
|
startNext = !TEST_FLAG(fdoDeviceExtension->DeviceFlags,
|
|
DF_RESET_IN_PROGRESS);
|
|
}
|
|
KeReleaseSpinLockFromDpcLevel(&fdoDeviceExtension->ExtensionDataSpinLock);
|
|
|
|
IoReleaseCancelSpinLock(irql);
|
|
|
|
LOGENTRY('CAN3', DeviceObject, Irp, 0);
|
|
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
if (startNext)
|
|
{
|
|
IoStartNextPacket(DeviceObject, TRUE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// The current Irp was not cancelled. It is no longer cancelable.
|
|
//
|
|
IoReleaseCancelSpinLock(irql);
|
|
|
|
// Get our Irp parameters
|
|
//
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
fdoDeviceExtension->OriginalSrb = srb;
|
|
|
|
deviceDisconnected = FALSE;
|
|
persistentError = FALSE;
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&fdoDeviceExtension->ExtensionDataSpinLock);
|
|
{
|
|
if (TEST_FLAG(fdoDeviceExtension->DeviceFlags, DF_DEVICE_DISCONNECTED))
|
|
{
|
|
deviceDisconnected = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fdoDeviceExtension->SrbTimeout = srb->TimeOutValue;
|
|
|
|
if (TEST_FLAG(fdoDeviceExtension->DeviceFlags, DF_PERSISTENT_ERROR))
|
|
{
|
|
persistentError = TRUE;
|
|
|
|
CLEAR_FLAG(fdoDeviceExtension->DeviceFlags, DF_PERSISTENT_ERROR);
|
|
}
|
|
}
|
|
}
|
|
KeReleaseSpinLockFromDpcLevel(&fdoDeviceExtension->ExtensionDataSpinLock);
|
|
|
|
|
|
if (deviceDisconnected)
|
|
{
|
|
LOGENTRY('siod', DeviceObject, Irp, 0);
|
|
|
|
// The device is disconnected, fail this request immediately and start
|
|
// the next request.
|
|
//
|
|
srb->SrbStatus = SRB_STATUS_NO_DEVICE;
|
|
srb->DataTransferLength = 0;
|
|
|
|
ntStatus = STATUS_DEVICE_DOES_NOT_EXIST;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
IoStartNextPacket(DeviceObject, TRUE);
|
|
}
|
|
else
|
|
{
|
|
// Translate the CDB if necessary
|
|
//
|
|
USBSTOR_TranslateCDBSubmit(DeviceObject, Irp, srb);
|
|
|
|
DBGPRINT(3, ("CDB OP 0x%02X, Length %d\n", srb->Cdb[0], srb->CdbLength));
|
|
|
|
if (fdoDeviceExtension->DriverFlags == DeviceProtocolBulkOnly)
|
|
{
|
|
ntStatus = USBSTOR_CbwTransfer(DeviceObject,
|
|
Irp);
|
|
}
|
|
else
|
|
{
|
|
if (persistentError && (srb->Cdb[0] != SCSIOP_REQUEST_SENSE))
|
|
{
|
|
// There was a persistent error during the last request which
|
|
// was not cleared with an AutoSense, and this request is not
|
|
// a Request Sense, so first clear the persistent error with a
|
|
// Request Sense before issuing this request.
|
|
//
|
|
ntStatus = USBSTOR_IssueRequestSenseCdb(DeviceObject,
|
|
Irp,
|
|
NON_AUTO_SENSE);
|
|
}
|
|
else
|
|
{
|
|
// Normal case, just issue the real request.
|
|
//
|
|
ntStatus = USBSTOR_IssueClientCdb(DeviceObject,
|
|
Irp);
|
|
}
|
|
}
|
|
}
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_StartIo %08X\n", ntStatus));
|
|
|
|
return;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_CheckRequestTimeOut()
|
|
//
|
|
// Returns TRUE if the request timed out and the request should be completed.
|
|
//
|
|
//******************************************************************************
|
|
|
|
BOOLEAN
|
|
USBSTOR_CheckRequestTimeOut (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
OUT PNTSTATUS NtStatus
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
BOOLEAN resetStarted;
|
|
KIRQL irql;
|
|
PIO_STACK_LOCATION irpStack;
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Check to see if a reset was started while this request was in progress.
|
|
//
|
|
resetStarted = FALSE;
|
|
|
|
KeAcquireSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock, &irql);
|
|
{
|
|
CLEAR_FLAG(fdoDeviceExtension->DeviceFlags, DF_SRB_IN_PROGRESS);
|
|
|
|
if (TEST_FLAG(fdoDeviceExtension->DeviceFlags, DF_RESET_IN_PROGRESS))
|
|
{
|
|
LOGENTRY('CRT1', DeviceObject, Irp, Srb);
|
|
|
|
resetStarted = TRUE;
|
|
}
|
|
}
|
|
KeReleaseSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock, irql);
|
|
|
|
// If a timeout reset has been started, then complete this request with
|
|
// a timeout error. Well, don't actually complete the request just yet.
|
|
// Signal the cancel completion event and let USBSTOR_ResetDeviceWorkItem()
|
|
// complete the request. This allows USBSTOR_ResetDeviceWorkItem() to
|
|
// cancel the request without worrying about the request completing and
|
|
// disappearing out from underneath it.
|
|
//
|
|
if (resetStarted)
|
|
{
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
Srb = fdoDeviceExtension->OriginalSrb;
|
|
irpStack->Parameters.Scsi.Srb = Srb;
|
|
|
|
Irp->IoStatus.Status = STATUS_IO_TIMEOUT;
|
|
Irp->IoStatus.Information = 0;
|
|
Srb->SrbStatus = SRB_STATUS_TIMEOUT;
|
|
|
|
USBSTOR_TranslateCDBComplete(DeviceObject, Irp, Srb);
|
|
|
|
*NtStatus = STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
KeSetEvent(&fdoDeviceExtension->CancelEvent,
|
|
IO_NO_INCREMENT,
|
|
FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
fdoDeviceExtension->PendingIrp = NULL;
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_IssueControlRequest()
|
|
//
|
|
// This routine is called by USBSTOR_IssueClientCdb() and
|
|
// USBSTOR_IssueRequestSenseCdb()
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// Intializes the Control transfer Urb and sends it down the stack:
|
|
//
|
|
// bmRequestType = 0x21, Class specific, host to device transfer, to
|
|
// recipient interface
|
|
// bRequest = 0x00, Accept Device Specific Command
|
|
// wValue = 0x00, Not Used
|
|
// wIndex = bInterfaceNumber
|
|
// wLength = length of device specific command block
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_IssueControlRequest (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG TransferBufferLength,
|
|
IN PVOID TransferBuffer,
|
|
IN PIO_COMPLETION_ROUTINE CompletionRoutine,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIO_STACK_LOCATION nextStack;
|
|
KIRQL irql;
|
|
NTSTATUS ntStatus;
|
|
|
|
struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST *controlUrb;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_IssueControlRequest\n"));
|
|
|
|
LOGENTRY('ICTR', DeviceObject, Irp, 0);
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get a pointer to the Control/Bulk/Interrupt Transfer URB in our
|
|
// Device Extension
|
|
//
|
|
controlUrb = &fdoDeviceExtension->Urb.ControlUrb;
|
|
|
|
// Initialize the Control Transfer URB, all fields default to zero
|
|
//
|
|
RtlZeroMemory(controlUrb, sizeof(*controlUrb));
|
|
|
|
controlUrb->Hdr.Length = sizeof(*controlUrb);
|
|
|
|
controlUrb->Hdr.Function = URB_FUNCTION_CLASS_INTERFACE;
|
|
|
|
// controlUrb->TransferFlags is already zero
|
|
|
|
controlUrb->TransferBufferLength = TransferBufferLength;
|
|
|
|
controlUrb->TransferBuffer = TransferBuffer;
|
|
|
|
// controlUrb->TransferBufferMDL is already zero
|
|
|
|
// controlUrb->RequestTypeReservedBits is already zero
|
|
|
|
// controlUrb->Request is already zero
|
|
|
|
// controlUrb->Value is already zero
|
|
|
|
// Target the request at the proper interface on the device
|
|
//
|
|
controlUrb->Index = fdoDeviceExtension->InterfaceInfo->InterfaceNumber;
|
|
|
|
// Set the Irp parameters for the lower driver
|
|
//
|
|
nextStack = IoGetNextIrpStackLocation(Irp);
|
|
|
|
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
|
|
nextStack->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_INTERNAL_USB_SUBMIT_URB;
|
|
|
|
nextStack->Parameters.Others.Argument1 = controlUrb;
|
|
|
|
// Set the completion routine, which will handle the next phase of the Srb
|
|
//
|
|
IoSetCompletionRoutine(Irp,
|
|
CompletionRoutine,
|
|
Context,
|
|
TRUE, // InvokeOnSuccess
|
|
TRUE, // InvokeOnError
|
|
TRUE); // InvokeOnCancel
|
|
|
|
|
|
KeAcquireSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock, &irql);
|
|
{
|
|
fdoDeviceExtension->PendingIrp = Irp;
|
|
|
|
SET_FLAG(fdoDeviceExtension->DeviceFlags, DF_SRB_IN_PROGRESS);
|
|
}
|
|
KeReleaseSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock, irql);
|
|
|
|
|
|
// Pass the Irp & Urb down the stack
|
|
//
|
|
ntStatus = IoCallDriver(fdoDeviceExtension->StackDeviceObject,
|
|
Irp);
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_IssueControlRequest %08X\n", ntStatus));
|
|
|
|
LOGENTRY('ictr', DeviceObject, Irp, ntStatus);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_IssueBulkOrInterruptRequest()
|
|
//
|
|
// This routine is called by USBSTOR_IssueClientBulkRequest(),
|
|
// USBSTOR_IssueInterruptRequest() and USBSTOR_IssueRequestSenseBulkRequest().
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// Initializes the Bulk or Interrupt transfer Urb and sends it down the stack
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_IssueBulkOrInterruptRequest (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN USBD_PIPE_HANDLE PipeHandle,
|
|
IN ULONG TransferFlags,
|
|
IN ULONG TransferBufferLength,
|
|
IN PVOID TransferBuffer,
|
|
IN PMDL TransferBufferMDL,
|
|
IN PIO_COMPLETION_ROUTINE CompletionRoutine,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIO_STACK_LOCATION nextStack;
|
|
KIRQL irql;
|
|
NTSTATUS ntStatus;
|
|
|
|
struct _URB_BULK_OR_INTERRUPT_TRANSFER *bulkIntrUrb;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_IssueBulkOrInterruptRequest\n"));
|
|
|
|
LOGENTRY('IBIR', DeviceObject, Irp, 0);
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get a pointer to the Bulk/Interrupt Transfer URB in our Device Extension
|
|
//
|
|
bulkIntrUrb = &fdoDeviceExtension->Urb.BulkIntrUrb;
|
|
|
|
// Initialize the Bulk/Interrupt Transfer URB, all fields default to zero
|
|
//
|
|
RtlZeroMemory(bulkIntrUrb, sizeof(*bulkIntrUrb));
|
|
|
|
bulkIntrUrb->Hdr.Length = sizeof(*bulkIntrUrb);
|
|
|
|
bulkIntrUrb->Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
|
|
|
|
bulkIntrUrb->PipeHandle = PipeHandle;
|
|
|
|
bulkIntrUrb->TransferFlags = TransferFlags;
|
|
|
|
bulkIntrUrb->TransferBufferLength = TransferBufferLength;
|
|
|
|
bulkIntrUrb->TransferBuffer = TransferBuffer;
|
|
|
|
bulkIntrUrb->TransferBufferMDL = TransferBufferMDL;
|
|
|
|
// Set the Irp parameters for the lower driver
|
|
//
|
|
nextStack = IoGetNextIrpStackLocation(Irp);
|
|
|
|
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
|
|
nextStack->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_INTERNAL_USB_SUBMIT_URB;
|
|
|
|
nextStack->Parameters.Others.Argument1 = bulkIntrUrb;
|
|
|
|
// Set the completion routine, which will handle the next phase of the Srb
|
|
//
|
|
IoSetCompletionRoutine(Irp,
|
|
CompletionRoutine,
|
|
Context,
|
|
TRUE, // InvokeOnSuccess
|
|
TRUE, // InvokeOnError
|
|
TRUE); // InvokeOnCancel
|
|
|
|
|
|
KeAcquireSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock, &irql);
|
|
{
|
|
fdoDeviceExtension->PendingIrp = Irp;
|
|
|
|
SET_FLAG(fdoDeviceExtension->DeviceFlags, DF_SRB_IN_PROGRESS);
|
|
}
|
|
KeReleaseSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock, irql);
|
|
|
|
// Pass the Irp & Urb down the stack
|
|
//
|
|
ntStatus = IoCallDriver(fdoDeviceExtension->StackDeviceObject,
|
|
Irp);
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_IssueBulkOrInterruptRequest %08X\n", ntStatus));
|
|
|
|
LOGENTRY('ibir', DeviceObject, Irp, ntStatus);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// CBI (Control/Bulk/Interrupt) Routines
|
|
//
|
|
|
|
//
|
|
// Phase 1, CDB Control transfer
|
|
//
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_IssueClientCdb()
|
|
//
|
|
// This routine is called by USBSTOR_StartIo().
|
|
//
|
|
// It runs at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// Starts a USB transfer to write the Srb->Cdb out the control endpoint.
|
|
//
|
|
// Sets USBSTOR_ClientCdbCompletion() as the completion routine.
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_IssueClientCdb (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
NTSTATUS ntStatus;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_IssueClientCdb\n"));
|
|
|
|
LOGENTRY('ICDB', DeviceObject, Irp, 0);
|
|
|
|
// Get the client Srb
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
ntStatus = USBSTOR_IssueControlRequest(
|
|
DeviceObject,
|
|
Irp,
|
|
srb->CdbLength, // TransferBufferLength
|
|
srb->Cdb, // TransferBuffer
|
|
USBSTOR_ClientCdbCompletion, // CompletionRoutine
|
|
NULL); // Context
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_IssueClientCdb %08X\n", ntStatus));
|
|
|
|
LOGENTRY('icdb', DeviceObject, Irp, ntStatus);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_ClientCdbCompletion()
|
|
//
|
|
// Completion routine used by USBSTOR_IssueClientCdb()
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// If a timeout reset occured, complete the request.
|
|
//
|
|
// Else if the CDB USB transfer failed due to a STALL and AutoSense is not
|
|
// disabled, do not complete the request yet and start a Request Sense by
|
|
// calling USBSTOR_IssueRequestSenseCdb(AUTO_SENSE).
|
|
//
|
|
// Else if the CDB USB transfer failed due to a STALL and AutoSense is
|
|
// disabled, mark a persistant error and complete the request.
|
|
//
|
|
// Else if the CDB USB transfer failed due to some other reason, complete the
|
|
// request and start a reset by queuing USBSTOR_ResetDeviceWorkItem().
|
|
//
|
|
// Else if the CDB USB transfer succeeded and the Srb has a transfer buffer,
|
|
// do not complete the request yet and start the bulk data transfer by calling
|
|
// USBSTOR_IssueClientBulkRequest().
|
|
//
|
|
// Else if the CDB USB transfer succeeded and the Srb has no transfer buffer,
|
|
// do not complete the request yet and start the command completion interrupt
|
|
// data transfer by calling USBSTOR_IssueInterruptRequest().
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_ClientCdbCompletion (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID NotUsed
|
|
)
|
|
{
|
|
PPDO_DEVICE_EXTENSION pdoDeviceExtension;
|
|
PDEVICE_OBJECT fdoDeviceObject;
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
KIRQL irql;
|
|
NTSTATUS ntStatus;
|
|
|
|
struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST *controlUrb;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_ClientCdbCompletion\n"));
|
|
|
|
LOGENTRY('CDBC', DeviceObject, Irp, Irp->IoStatus.Status);
|
|
|
|
pdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(pdoDeviceExtension->Type == USBSTOR_DO_TYPE_PDO);
|
|
|
|
fdoDeviceObject = pdoDeviceExtension->ParentFDO;
|
|
fdoDeviceExtension = fdoDeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get a pointer to the Control Transfer URB in our Device Extension
|
|
//
|
|
controlUrb = &fdoDeviceExtension->Urb.ControlUrb;
|
|
|
|
// Get our Irp parameters
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
// If a timeout reset occured, complete the request.
|
|
//
|
|
if (USBSTOR_CheckRequestTimeOut(fdoDeviceObject,
|
|
Irp,
|
|
srb,
|
|
&ntStatus))
|
|
{
|
|
LOGENTRY('cdb1', fdoDeviceObject, Irp, srb);
|
|
DBGPRINT(1, ("USBSTOR_ClientCdbCompletion: timeout completion\n"));
|
|
return ntStatus;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status))
|
|
{
|
|
// The CDB Control Transfer was not successful. Look at how the
|
|
// the transfer failed to figure out how to recover.
|
|
//
|
|
|
|
LOGENTRY('cdb2', Irp->IoStatus.Status, controlUrb->Hdr.Status, 0);
|
|
|
|
DBGPRINT(1, ("CDB transfer failed %08X %08X\n",
|
|
Irp->IoStatus.Status, controlUrb->Hdr.Status));
|
|
|
|
if (USBD_STATUS(controlUrb->Hdr.Status) ==
|
|
USBD_STATUS(USBD_STATUS_STALL_PID))
|
|
{
|
|
// The device STALLed the Control Transfer
|
|
|
|
srb->SrbStatus = SRB_STATUS_ERROR;
|
|
srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
|
|
srb->DataTransferLength = 0;
|
|
|
|
if (!(srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) &&
|
|
(srb->SenseInfoBufferLength != 0) &&
|
|
(srb->SenseInfoBuffer != NULL))
|
|
{
|
|
LOGENTRY('cdb3', fdoDeviceObject, Irp, srb);
|
|
|
|
// AutoSense is not disabled so do not complete the request yet
|
|
// and issue a Request Sense. This request will be completed
|
|
// and the next request started when the AutoSense Request
|
|
// Sense completes later.
|
|
//
|
|
ntStatus = USBSTOR_IssueRequestSenseCdb(fdoDeviceObject,
|
|
Irp,
|
|
AUTO_SENSE);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
else
|
|
{
|
|
LOGENTRY('cdb4', fdoDeviceObject, Irp, srb);
|
|
|
|
// AutoSense is disabled so mark a persistent error and complete
|
|
// this request now. Also start the next request now.
|
|
//
|
|
ntStatus = STATUS_IO_DEVICE_ERROR;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
KeAcquireSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock,
|
|
&irql);
|
|
{
|
|
SET_FLAG(fdoDeviceExtension->DeviceFlags,
|
|
DF_PERSISTENT_ERROR);
|
|
}
|
|
KeReleaseSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock,
|
|
irql);
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, &irql);
|
|
{
|
|
IoStartNextPacket(fdoDeviceObject, TRUE);
|
|
}
|
|
KeLowerIrql(irql);
|
|
|
|
return ntStatus;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOGENTRY('cdb5', fdoDeviceObject, Irp, srb);
|
|
|
|
// Else some other strange error has occured. Maybe the device is
|
|
// unplugged, or maybe the device port was disabled, or maybe the
|
|
// request was cancelled.
|
|
//
|
|
// Complete this request now and then reset the device. The next
|
|
// request will be started when the reset completes.
|
|
//
|
|
ntStatus = STATUS_IO_DEVICE_ERROR;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
srb->SrbStatus = SRB_STATUS_BUS_RESET;
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
USBSTOR_QueueResetDevice(fdoDeviceObject);
|
|
|
|
DBGPRINT(1, ("USBSTOR_ClientCdbCompletion: xfer error completion\n"));
|
|
|
|
return ntStatus;
|
|
}
|
|
}
|
|
|
|
// The CDB Control Transfer was successful. Start the next phase, either
|
|
// the Data Bulk Transfer or Command Completion Interrupt Transfer, and do
|
|
// not complete the request yet (unless there is no Bulk Transfer and the
|
|
// Interrupt Transfer is not supported).
|
|
//
|
|
if (Irp->MdlAddress != NULL)
|
|
{
|
|
LOGENTRY('cdb6', fdoDeviceObject, Irp, srb);
|
|
|
|
ASSERT(srb->DataTransferLength != 0);
|
|
|
|
// The Srb has a transfer buffer, start the Data Bulk Transfer.
|
|
//
|
|
ntStatus = USBSTOR_IssueClientBulkRequest(fdoDeviceObject,
|
|
Irp);
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
ntStatus = STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
else
|
|
{
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
srb->SrbStatus = SRB_STATUS_ERROR;
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
USBSTOR_QueueResetDevice(fdoDeviceObject);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(srb->DataTransferLength == 0);
|
|
|
|
// The Srb has no transfer buffer. If the Command Completion
|
|
// Interrupt Transfer is supported, start the Command Completion
|
|
// Interrupt Transfer, else just complete the request now and
|
|
// start the next request.
|
|
//
|
|
if (fdoDeviceExtension->InterruptInPipe)
|
|
{
|
|
LOGENTRY('cdb7', fdoDeviceObject, Irp, srb);
|
|
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
|
|
ntStatus = USBSTOR_IssueInterruptRequest(fdoDeviceObject,
|
|
Irp);
|
|
|
|
ntStatus = STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
else
|
|
{
|
|
LOGENTRY('cdb8', fdoDeviceObject, Irp, srb);
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, &irql);
|
|
{
|
|
IoStartNextPacket(fdoDeviceObject, TRUE);
|
|
}
|
|
KeLowerIrql(irql);
|
|
}
|
|
}
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_ClientCdbCompletion %08X\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// Phase 2, Data Bulk transfer
|
|
//
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_IssueClientBulkRequest()
|
|
//
|
|
// This routine is called by USBSTOR_ClientCdbCompletion().
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// Starts a USB transfer to read or write the Srb->DataBuffer data In or Out
|
|
// the Bulk endpoint.
|
|
//
|
|
// Sets USBSTOR_ClientBulkCompletionRoutine() as the completion routine.
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_IssueClientBulkRequest (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PMDL mdl;
|
|
PVOID mdlVa;
|
|
USBD_PIPE_HANDLE pipeHandle;
|
|
ULONG transferFlags;
|
|
NTSTATUS ntStatus;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_IssueClientBulkRequest\n"));
|
|
|
|
LOGENTRY('ICBK', DeviceObject, Irp, 0);
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get our Irp parameters
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
// Bulk IN or Bulk OUT?
|
|
//
|
|
if ((srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) == SRB_FLAGS_DATA_IN)
|
|
{
|
|
pipeHandle = fdoDeviceExtension->BulkInPipe->PipeHandle;
|
|
transferFlags = USBD_SHORT_TRANSFER_OK;
|
|
}
|
|
else if ((srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) == SRB_FLAGS_DATA_OUT)
|
|
{
|
|
pipeHandle = fdoDeviceExtension->BulkOutPipe->PipeHandle;
|
|
transferFlags = 0;
|
|
}
|
|
else
|
|
{
|
|
// Something is wrong if we end up here.
|
|
//
|
|
ASSERT((srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) &&
|
|
((srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) !=
|
|
SRB_FLAGS_UNSPECIFIED_DIRECTION));
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Check to see if this request is part of a split request.
|
|
//
|
|
mdlVa = MmGetMdlVirtualAddress(Irp->MdlAddress);
|
|
|
|
if (mdlVa == (PVOID)srb->DataBuffer)
|
|
{
|
|
// Not part of a split request, use original MDL
|
|
//
|
|
mdl = Irp->MdlAddress;
|
|
}
|
|
else
|
|
{
|
|
// Part of a split request, allocate new partial MDL
|
|
//
|
|
mdl = IoAllocateMdl(srb->DataBuffer,
|
|
srb->DataTransferLength,
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
if (mdl == NULL)
|
|
{
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
else
|
|
{
|
|
IoBuildPartialMdl(Irp->MdlAddress,
|
|
mdl,
|
|
srb->DataBuffer,
|
|
srb->DataTransferLength);
|
|
}
|
|
}
|
|
|
|
if (mdl != NULL)
|
|
{
|
|
ntStatus = USBSTOR_IssueBulkOrInterruptRequest(
|
|
DeviceObject,
|
|
Irp,
|
|
pipeHandle, // PipeHandle
|
|
transferFlags, // TransferFlags
|
|
srb->DataTransferLength, // TransferBufferLen
|
|
NULL, // TransferBuffer
|
|
mdl, // TransferBufferMDL
|
|
USBSTOR_ClientBulkCompletionRoutine, // CompletionRoutine
|
|
NULL); // Context
|
|
|
|
// Just return STATUS_SUCCESS at this point. If there is an error,
|
|
// USBSTOR_ClientBulkCompletionRoutine() will handle it, not the caller
|
|
// of USBSTOR_IssueClientBulkRequest().
|
|
//
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_IssueClientBulkRequest %08X\n", ntStatus));
|
|
|
|
LOGENTRY('icbk', DeviceObject, Irp, ntStatus);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_ClientBulkCompletionRoutine()
|
|
//
|
|
// Completion routine used by USBSTOR_IssueClientBulkRequest
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// If a timeout reset occured, complete the request.
|
|
//
|
|
// Else if the Bulk USB transfer failed due to a STALL and AutoSense is not
|
|
// disabled, do not complete the request yet and start a pipe reset by calling
|
|
// USBSTOR_QueueResetPipe().
|
|
//
|
|
// Else if the Bulk USB transfer failed due to a STALL and AutoSense is
|
|
// disabled, mark a persistant error and complete the request.
|
|
//
|
|
// Else if the Bulk USB transfer failed due to some other reason, complete the
|
|
// request and start a reset by queuing USBSTOR_ResetDeviceWorkItem().
|
|
//
|
|
// Else if the Bulk USB transfer succeeded, start the command completion
|
|
// interrupt data transfer by calling USBSTOR_IssueInterruptRequest().
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_ClientBulkCompletionRoutine (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID NotUsed
|
|
)
|
|
{
|
|
PPDO_DEVICE_EXTENSION pdoDeviceExtension;
|
|
PDEVICE_OBJECT fdoDeviceObject;
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
KIRQL irql;
|
|
NTSTATUS ntStatus;
|
|
|
|
struct _URB_BULK_OR_INTERRUPT_TRANSFER *bulkUrb;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_ClientBulkCompletionRoutine\n"));
|
|
|
|
LOGENTRY('CBKC', DeviceObject, Irp, Irp->IoStatus.Status);
|
|
|
|
pdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(pdoDeviceExtension->Type == USBSTOR_DO_TYPE_PDO);
|
|
|
|
fdoDeviceObject = pdoDeviceExtension->ParentFDO;
|
|
fdoDeviceExtension = fdoDeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get a pointer to the Bulk Transfer URB in our Device Extension
|
|
//
|
|
bulkUrb = &fdoDeviceExtension->Urb.BulkIntrUrb;
|
|
|
|
// Get our Irp parameters
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
if (bulkUrb->TransferBufferMDL != Irp->MdlAddress)
|
|
{
|
|
IoFreeMdl(bulkUrb->TransferBufferMDL);
|
|
}
|
|
|
|
// If a timeout reset occured, complete the request.
|
|
//
|
|
if (USBSTOR_CheckRequestTimeOut(fdoDeviceObject,
|
|
Irp,
|
|
srb,
|
|
&ntStatus))
|
|
{
|
|
LOGENTRY('cbk1', fdoDeviceObject, Irp, srb);
|
|
DBGPRINT(1, ("USBSTOR_ClientBulkCompletionRoutine: timeout completion\n"));
|
|
return ntStatus;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status))
|
|
{
|
|
// The Data Bulk Transfer was not successful. Look at how the
|
|
// the transfer failed to figure out how to recover.
|
|
//
|
|
|
|
LOGENTRY('cbk2', Irp->IoStatus.Status, bulkUrb->Hdr.Status, 0);
|
|
|
|
DBGPRINT(1, ("Client Bulk transfer failed %08X %08X\n",
|
|
Irp->IoStatus.Status, bulkUrb->Hdr.Status));
|
|
|
|
if (USBD_STATUS(bulkUrb->Hdr.Status) ==
|
|
USBD_STATUS(USBD_STATUS_STALL_PID))
|
|
{
|
|
// The device STALLed the Bulk Transfer
|
|
|
|
srb->SrbStatus = SRB_STATUS_ERROR;
|
|
srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
|
|
srb->DataTransferLength = bulkUrb->TransferBufferLength;
|
|
|
|
if (!(srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) &&
|
|
(srb->SenseInfoBufferLength != 0) &&
|
|
(srb->SenseInfoBuffer != NULL))
|
|
{
|
|
LOGENTRY('cbk3', fdoDeviceObject, Irp, srb);
|
|
|
|
// AutoSense is not disabled so do not complete the request
|
|
// yet. Queue a bulk pipe reset. After the bulk pipe reset
|
|
// completes, a Request Sense will be issued. This request
|
|
// will be completed and the next request started when the
|
|
// AutoSense Request Sense completes later.
|
|
//
|
|
USBSTOR_QueueResetPipe(fdoDeviceObject);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
else
|
|
{
|
|
LOGENTRY('cbk4', fdoDeviceObject, Irp, srb);
|
|
|
|
// AutoSense is disabled so mark a persistent error and
|
|
// complete the request, but also queue a bulk pipe reset.
|
|
//
|
|
// The next request will be started when the bulk pipe
|
|
// reset completes.
|
|
//
|
|
ntStatus = STATUS_IO_DEVICE_ERROR;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
KeAcquireSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock,
|
|
&irql);
|
|
{
|
|
SET_FLAG(fdoDeviceExtension->DeviceFlags,
|
|
DF_PERSISTENT_ERROR);
|
|
}
|
|
KeReleaseSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock,
|
|
irql);
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
USBSTOR_QueueResetPipe(fdoDeviceObject);
|
|
|
|
return ntStatus;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOGENTRY('cbk5', fdoDeviceObject, Irp, srb);
|
|
|
|
// Else some other strange error has occured. Maybe the device is
|
|
// unplugged, or maybe the device port was disabled, or maybe the
|
|
// request was cancelled.
|
|
//
|
|
// Complete this request now and then reset the device. The next
|
|
// request will be started when the reset completes.
|
|
//
|
|
ntStatus = STATUS_IO_DEVICE_ERROR;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
srb->SrbStatus = SRB_STATUS_BUS_RESET;
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
USBSTOR_QueueResetDevice(fdoDeviceObject);
|
|
|
|
DBGPRINT(1, ("USBSTOR_ClientBulkCompletionRoutine: xfer error completion\n"));
|
|
|
|
return ntStatus;
|
|
}
|
|
}
|
|
|
|
// Check for overrun
|
|
//
|
|
if (bulkUrb->TransferBufferLength < srb->DataTransferLength)
|
|
{
|
|
srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
|
|
}
|
|
else
|
|
{
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
}
|
|
|
|
// Update the the Srb data transfer length based on the actual bulk
|
|
// transfer length.
|
|
//
|
|
srb->DataTransferLength = bulkUrb->TransferBufferLength;
|
|
|
|
// Client data Bulk Transfer successful completion. If the Command
|
|
// Completion Interrupt Transfer is supported, start the Command Completion
|
|
// Interrupt Transfer, else just complete the request now and start the
|
|
// next request.
|
|
//
|
|
if (fdoDeviceExtension->InterruptInPipe)
|
|
{
|
|
LOGENTRY('cbk6', fdoDeviceObject, Irp, bulkUrb->TransferBufferLength);
|
|
|
|
ntStatus = USBSTOR_IssueInterruptRequest(fdoDeviceObject,
|
|
Irp);
|
|
|
|
ntStatus = STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
else
|
|
{
|
|
LOGENTRY('cbk7', fdoDeviceObject, Irp, bulkUrb->TransferBufferLength);
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
Irp->IoStatus.Information = srb->DataTransferLength;
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, &irql);
|
|
{
|
|
IoStartNextPacket(fdoDeviceObject, TRUE);
|
|
}
|
|
KeLowerIrql(irql);
|
|
}
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_ClientBulkCompletionRoutine %08X\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// Phase 3, Command completion Interrupt transfer
|
|
//
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_IssueInterruptRequest()
|
|
//
|
|
// This routine is called by USBSTOR_ClientCdbCompletion() and
|
|
// USBSTOR_ClientBulkCompletionRoutine()
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// Starts a USB transfer to read the command completion interrupt data In
|
|
// the Interrupt endpoint.
|
|
//
|
|
// Sets USBSTOR_InterruptDataCompletionRoutine() as the completion routine.
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_IssueInterruptRequest (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
USBD_PIPE_HANDLE pipeHandle;
|
|
ULONG transferBufferLength;
|
|
PVOID transferBuffer;
|
|
NTSTATUS ntStatus;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_IssueInterruptRequest\n"));
|
|
|
|
LOGENTRY('IINT', DeviceObject, Irp, 0);
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
pipeHandle = fdoDeviceExtension->InterruptInPipe->PipeHandle;
|
|
|
|
transferBufferLength = sizeof(fdoDeviceExtension->Cbi.InterruptData);
|
|
|
|
transferBuffer = &fdoDeviceExtension->Cbi.InterruptData;
|
|
|
|
ntStatus = USBSTOR_IssueBulkOrInterruptRequest(
|
|
DeviceObject,
|
|
Irp,
|
|
pipeHandle, // PipeHandle
|
|
0, // TransferFlags
|
|
transferBufferLength, // TransferBufferLength
|
|
transferBuffer, // TransferBuffer
|
|
NULL, // TransferBufferMDL
|
|
USBSTOR_InterruptDataCompletionRoutine, // CompletionRoutine
|
|
NULL); // Context
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_IssueInterruptRequest %08X\n", ntStatus));
|
|
|
|
LOGENTRY('iint', DeviceObject, Irp, ntStatus);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_InterruptDataCompletionRoutine()
|
|
//
|
|
// Completion routine used by USBSTOR_IssueInterruptRequest()
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// If a timeout reset occured, complete the request.
|
|
//
|
|
// Else if the Interrupt USB transfer failed due to any reason, complete the
|
|
// request and start a reset by queuing USBSTOR_ResetDeviceWorkItem().
|
|
//
|
|
// Else if the Interrupt USB transfer succeeded but the completion data is
|
|
// non-zero and AutoSense is not disabled, do not complete the request yet and
|
|
// start a Request Sense by calling USBSTOR_IssueRequestSenseCdb(AUTO).
|
|
//
|
|
// Else if the Interrupt USB transfer succeeded but the completion data is
|
|
// non-zero and AutoSense is disabled, mark a persistant error and complete
|
|
// the request.
|
|
//
|
|
// Else if the Interrupt USB transfer succeeded and the completion data is
|
|
// zero, complete the request.
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_InterruptDataCompletionRoutine (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID NotUsed
|
|
)
|
|
{
|
|
PPDO_DEVICE_EXTENSION pdoDeviceExtension;
|
|
PDEVICE_OBJECT fdoDeviceObject;
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
KIRQL irql;
|
|
NTSTATUS ntStatus;
|
|
|
|
struct _URB_BULK_OR_INTERRUPT_TRANSFER *intrUrb;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_InterruptDataCompletionRoutine\n"));
|
|
|
|
LOGENTRY('IDCR', DeviceObject, Irp, Irp->IoStatus.Status);
|
|
|
|
pdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(pdoDeviceExtension->Type == USBSTOR_DO_TYPE_PDO);
|
|
|
|
fdoDeviceObject = pdoDeviceExtension->ParentFDO;
|
|
fdoDeviceExtension = fdoDeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get a pointer to the Interrupt Transfer URB in our Device Extension
|
|
//
|
|
intrUrb = &fdoDeviceExtension->Urb.BulkIntrUrb;
|
|
|
|
// Get our Irp parameters
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
// If a timeout reset occured, complete the request.
|
|
//
|
|
if (USBSTOR_CheckRequestTimeOut(fdoDeviceObject,
|
|
Irp,
|
|
srb,
|
|
&ntStatus))
|
|
{
|
|
LOGENTRY('idc1', fdoDeviceObject, Irp, srb);
|
|
DBGPRINT(1, ("USBSTOR_InterruptDataCompletionRoutine: timeout completion\n"));
|
|
return ntStatus;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status))
|
|
{
|
|
// The Interrupt CDB USB transfer failed. Complete this request
|
|
// now and then reset the device. The next request will be started
|
|
// when the reset completes.
|
|
//
|
|
LOGENTRY('idc2', Irp->IoStatus.Status, intrUrb->Hdr.Status, 0);
|
|
|
|
DBGPRINT(1, ("Interrupt transfer failed %08X %08X\n",
|
|
Irp->IoStatus.Status, intrUrb->Hdr.Status));
|
|
|
|
ntStatus = STATUS_IO_DEVICE_ERROR;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
srb->SrbStatus = SRB_STATUS_BUS_RESET;
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
USBSTOR_QueueResetDevice(fdoDeviceObject);
|
|
|
|
DBGPRINT(1, ("USBSTOR_InterruptDataCompletionRoutine: xfer error completion\n"));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
if ((fdoDeviceExtension->Cbi.InterruptData != 0) &&
|
|
(srb->Cdb[0] != SCSIOP_INQUIRY) &&
|
|
(srb->Cdb[0] != SCSIOP_REQUEST_SENSE))
|
|
{
|
|
// Command completion interrupt data indicates an error. Either don't
|
|
// complete the request yet and start an AutoSense, or complete the
|
|
// request now and indicate a persistent error.
|
|
//
|
|
srb->SrbStatus = SRB_STATUS_ERROR;
|
|
srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
|
|
srb->DataTransferLength = 0; // XXXXX Leave as set by bulk completion???
|
|
|
|
if (!(srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) &&
|
|
(srb->SenseInfoBufferLength != 0) &&
|
|
(srb->SenseInfoBuffer != NULL))
|
|
{
|
|
LOGENTRY('idc3', fdoDeviceObject, Irp, srb);
|
|
|
|
// AutoSense is not disabled so do not complete the request
|
|
// yet. Queue a bulk pipe reset. After the bulk pipe reset
|
|
// completes, a Request Sense will be issued. This request
|
|
// will be completed and the next request started when the
|
|
// AutoSense Request Sense completes later.
|
|
//
|
|
USBSTOR_QueueResetPipe(fdoDeviceObject);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
else
|
|
{
|
|
LOGENTRY('idc4', fdoDeviceObject, Irp, srb);
|
|
|
|
// AutoSense is disabled so mark a persistent error and
|
|
// complete the request, but also queue a bulk pipe reset.
|
|
//
|
|
// The next request will be started when the bulk pipe
|
|
// reset completes.
|
|
//
|
|
ntStatus = STATUS_IO_DEVICE_ERROR;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
KeAcquireSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock, &irql);
|
|
{
|
|
SET_FLAG(fdoDeviceExtension->DeviceFlags, DF_PERSISTENT_ERROR);
|
|
}
|
|
KeReleaseSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock, irql);
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
USBSTOR_QueueResetPipe(fdoDeviceObject);
|
|
|
|
return ntStatus;
|
|
}
|
|
}
|
|
|
|
// Hack for Y-E Data USB Floppy. Occasionally it will return interrupt
|
|
// data with the wrong data toggle. The interrupt data with the wrong
|
|
// toggle is silently ignored, which results in a request timeout.
|
|
// Forcing a Request Sense command between the completion of one command
|
|
// and the start of the next appears to be one way to work around this.
|
|
//
|
|
if (TEST_FLAG(fdoDeviceExtension->DeviceHackFlags, DHF_FORCE_REQUEST_SENSE))
|
|
{
|
|
KeAcquireSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock, &irql);
|
|
{
|
|
SET_FLAG(fdoDeviceExtension->DeviceFlags, DF_PERSISTENT_ERROR);
|
|
}
|
|
KeReleaseSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock, irql);
|
|
}
|
|
|
|
// The Interrupt USB transfer succeeded and the completion data is zero,
|
|
// complete this request now. Also start the next request now.
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
|
|
ASSERT(srb->SrbStatus != SRB_STATUS_PENDING);
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
Irp->IoStatus.Information = srb->DataTransferLength;
|
|
|
|
LOGENTRY('idc5', fdoDeviceObject, Irp, srb);
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, &irql);
|
|
{
|
|
IoStartNextPacket(fdoDeviceObject, TRUE);
|
|
}
|
|
KeLowerIrql(irql);
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_InterruptDataCompletionRoutine %08X\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// Phase 4, Request Sense CDB Control transfer
|
|
//
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_IssueRequestSenseCdb()
|
|
//
|
|
// This routine can be called by USBSTOR_StartIo, USBSTOR_ClientCdbCompletion(),
|
|
// USBSTOR_InterruptDataCompletionRoutine(), and by USBSTOR_ResetPipeWorkItem().
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// Starts a USB transfer to write a Request Sense CDB out the control endpoint.
|
|
//
|
|
// Sets USBSTOR_RequestSenseCdbCompletion(AutoFlag) as the completion routine.
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_IssueRequestSenseCdb (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG_PTR AutoFlag
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
ULONG transferBufferLength;
|
|
PVOID transferBuffer;
|
|
NTSTATUS ntStatus;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_IssueRequestSenseCdb\n"));
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get the client Srb
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
// The Control Transfer data buffer is our own Request Sense Cdb
|
|
//
|
|
RtlZeroMemory(fdoDeviceExtension->Cbi.RequestSenseCDB,
|
|
sizeof(fdoDeviceExtension->Cbi.RequestSenseCDB));
|
|
|
|
fdoDeviceExtension->Cbi.RequestSenseCDB[0] = SCSIOP_REQUEST_SENSE;
|
|
|
|
transferBufferLength = sizeof(fdoDeviceExtension->Cbi.RequestSenseCDB);
|
|
|
|
transferBuffer = fdoDeviceExtension->Cbi.RequestSenseCDB;
|
|
|
|
// If this is an AutoSense, we'll use the client Srb Sense Info Buffer,
|
|
// else we are doing this Request Sense to clear a persistent error and
|
|
// we'll use our own sense info buffer.
|
|
//
|
|
if (AutoFlag == AUTO_SENSE)
|
|
{
|
|
fdoDeviceExtension->Cbi.RequestSenseCDB[4] =
|
|
srb->SenseInfoBufferLength;
|
|
}
|
|
else
|
|
{
|
|
fdoDeviceExtension->Cbi.RequestSenseCDB[4] =
|
|
sizeof(fdoDeviceExtension->Cbi.SenseData);
|
|
}
|
|
|
|
ntStatus = USBSTOR_IssueControlRequest(
|
|
DeviceObject,
|
|
Irp,
|
|
transferBufferLength, // TransferBufferLength
|
|
transferBuffer, // TransferBuffer
|
|
USBSTOR_RequestSenseCdbCompletion, // CompletionRoutine
|
|
(PVOID)AutoFlag); // Context
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_IssueRequestSenseCdb %08X\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_RequestSenseCdbCompletion()
|
|
//
|
|
// Completion routine used by USBSTOR_IssueRequestSenseCdb
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// If a timeout reset occured, complete the request.
|
|
//
|
|
// Else if the Request Sense CDB USB transfer failed, complete the request and
|
|
// start a reset by queuing USBSTOR_ResetDeviceWorkItem().
|
|
//
|
|
// Else if the Request Sense CDB USB transfer succeeded, do not complete the
|
|
// request yet and start the Request Sense Bulk USB data transfer by calling
|
|
// USBSTOR_IssueRequestSenseBulkRequest(AutoFlag)
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_RequestSenseCdbCompletion (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID AutoFlag
|
|
)
|
|
{
|
|
PPDO_DEVICE_EXTENSION pdoDeviceExtension;
|
|
PDEVICE_OBJECT fdoDeviceObject;
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
NTSTATUS ntStatus;
|
|
|
|
struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST *controlUrb;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_RequestSenseCdbCompletion\n"));
|
|
|
|
LOGENTRY('RSCC', DeviceObject, Irp, Irp->IoStatus.Status);
|
|
|
|
pdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(pdoDeviceExtension->Type == USBSTOR_DO_TYPE_PDO);
|
|
|
|
fdoDeviceObject = pdoDeviceExtension->ParentFDO;
|
|
fdoDeviceExtension = fdoDeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get a pointer to the Control/Bulk/Interrupt Transfer URB in our Device
|
|
// Extension
|
|
//
|
|
controlUrb = &fdoDeviceExtension->Urb.ControlUrb;
|
|
|
|
// Get our Irp parameters
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
// If a timeout reset occured, complete the request.
|
|
//
|
|
if (USBSTOR_CheckRequestTimeOut(fdoDeviceObject,
|
|
Irp,
|
|
srb,
|
|
&ntStatus))
|
|
{
|
|
LOGENTRY('rsc1', fdoDeviceObject, Irp, srb);
|
|
DBGPRINT(1, ("USBSTOR_RequestSenseCdbCompletion: timeout completion\n"));
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status))
|
|
{
|
|
LOGENTRY('rsc2', Irp->IoStatus.Status, controlUrb->Hdr.Status, 0);
|
|
|
|
DBGPRINT(1, ("Request Sense CDB transfer failed %08X %08X\n",
|
|
Irp->IoStatus.Status, controlUrb->Hdr.Status));
|
|
|
|
// The Request Sense CDB USB transfer failed. Complete this request
|
|
// now and then reset the device. The next request will be started
|
|
// when the reset completes.
|
|
//
|
|
ntStatus = STATUS_IO_DEVICE_ERROR;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
srb->SrbStatus = SRB_STATUS_BUS_RESET;
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
USBSTOR_QueueResetDevice(fdoDeviceObject);
|
|
|
|
DBGPRINT(1, ("USBSTOR_RequestSenseCdbCompletion: xfer error completion\n"));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
LOGENTRY('rsc3', Irp->IoStatus.Status, controlUrb->Hdr.Status, 0);
|
|
|
|
// The Request Sense CDB USB transfer succeeded, do not complete the request
|
|
// yet and start the Request Sense Bulk USB data transfer.
|
|
//
|
|
ntStatus = USBSTOR_IssueRequestSenseBulkRequest(fdoDeviceObject,
|
|
Irp,
|
|
(ULONG_PTR)AutoFlag);
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_RequestSenseCdbCompletion %08X\n", ntStatus));
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
//
|
|
// Phase 5, Request Sense Bulk transfer
|
|
//
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_IssueRequestSenseBulkRequest()
|
|
//
|
|
// This routine is called by USBSTOR_RequestSenseCdbCompletion().
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// Starts a USB transfer to read the Requese Sense data in the bulk endpoint.
|
|
//
|
|
// If AutoFlag==AUTO, transfer buffer = Srb->SenseInfoBuffer.
|
|
//
|
|
// Else if AutoFlag==NON_AUTO, transfer buffer = bit bucket
|
|
//
|
|
// Sets USBSTOR_SenseDataCompletionRoutine(AutoFlag) as the completion routine.
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_IssueRequestSenseBulkRequest (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG_PTR AutoFlag
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
USBD_PIPE_HANDLE pipeHandle;
|
|
ULONG transferBufferLength;
|
|
PVOID transferBuffer;
|
|
NTSTATUS ntStatus;
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get our Irp parameters
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
pipeHandle = fdoDeviceExtension->BulkInPipe->PipeHandle;
|
|
|
|
// If this is an AutoSense, we'll use the client Srb Sense Info Buffer,
|
|
// else we are doing this Request Sense to clear a persistent error and
|
|
// we'll use our own sense info buffer.
|
|
//
|
|
if (AutoFlag == AUTO_SENSE)
|
|
{
|
|
transferBufferLength = srb->SenseInfoBufferLength;
|
|
transferBuffer = srb->SenseInfoBuffer;
|
|
}
|
|
else
|
|
{
|
|
transferBufferLength = sizeof(fdoDeviceExtension->Cbi.SenseData);
|
|
transferBuffer = &fdoDeviceExtension->Cbi.SenseData;
|
|
}
|
|
|
|
RtlZeroMemory(&fdoDeviceExtension->Cbi.SenseData,
|
|
sizeof(fdoDeviceExtension->Cbi.SenseData));
|
|
|
|
ntStatus = USBSTOR_IssueBulkOrInterruptRequest(
|
|
DeviceObject,
|
|
Irp,
|
|
pipeHandle, // PipeHandle
|
|
USBD_SHORT_TRANSFER_OK, // TransferFlags
|
|
transferBufferLength, // TransferBufferLength
|
|
transferBuffer, // TransferBuffer
|
|
NULL, // TransferBufferMDL
|
|
USBSTOR_SenseDataCompletionRoutine, // CompletionRoutine
|
|
(PVOID)AutoFlag); // Context
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_SenseDataCompletionRoutine()
|
|
//
|
|
// Completion routine used by USBSTOR_IssueRequestSenseBulkRequest()
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// If a timeout reset occured, complete the request.
|
|
//
|
|
// Else if the Request Sense Bulk USB transfer failed due to any reason,
|
|
// complete the request and start a reset by queuing a call to
|
|
// USBSTOR_ResetDeviceWorkItem().
|
|
//
|
|
// Else if the Request Sense Bulk USB transfer succeeded and the device
|
|
// does support the command completion interrupt, start the command completion
|
|
// interrupt transfer by calling USBSTOR_IssueRequestSenseInterruptRequest().
|
|
//
|
|
// Else if the Request Sense Bulk USB transfer succeeded and the device
|
|
// does not support the command completion interrupt, complete the request
|
|
// by calling USBSTOR_ProcessRequestSenseCompletion().
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_SenseDataCompletionRoutine (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID AutoFlag
|
|
)
|
|
{
|
|
PPDO_DEVICE_EXTENSION pdoDeviceExtension;
|
|
PDEVICE_OBJECT fdoDeviceObject;
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
NTSTATUS ntStatus;
|
|
|
|
struct _URB_BULK_OR_INTERRUPT_TRANSFER *bulkUrb;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_SenseDataCompletionRoutine\n"));
|
|
|
|
LOGENTRY('SDCR', DeviceObject, Irp, Irp->IoStatus.Status);
|
|
|
|
pdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(pdoDeviceExtension->Type == USBSTOR_DO_TYPE_PDO);
|
|
|
|
fdoDeviceObject = pdoDeviceExtension->ParentFDO;
|
|
fdoDeviceExtension = fdoDeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get a pointer to the Bulk Transfer URB in our Device Extension
|
|
//
|
|
bulkUrb = &fdoDeviceExtension->Urb.BulkIntrUrb;
|
|
|
|
// Get our Irp parameters
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
// If a timeout reset occured, complete the request.
|
|
//
|
|
if (USBSTOR_CheckRequestTimeOut(fdoDeviceObject,
|
|
Irp,
|
|
srb,
|
|
&ntStatus))
|
|
{
|
|
LOGENTRY('sdc1', fdoDeviceObject, Irp, srb);
|
|
DBGPRINT(1, ("USBSTOR_SenseDataCompletionRoutine: timeout completion\n"));
|
|
return ntStatus;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status))
|
|
{
|
|
LOGENTRY('sdc2', Irp->IoStatus.Status, bulkUrb->Hdr.Status, 0);
|
|
|
|
DBGPRINT(1, ("BULK sense data transfer failed %08X %08X\n",
|
|
Irp->IoStatus.Status, bulkUrb->Hdr.Status));
|
|
|
|
// The Request Sense Bulk USB transfer failed. Complete this request
|
|
// now and then reset the device. The next request will be started
|
|
// when the reset completes.
|
|
//
|
|
ntStatus = STATUS_IO_DEVICE_ERROR;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
srb->SrbStatus = SRB_STATUS_BUS_RESET;
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
USBSTOR_QueueResetDevice(fdoDeviceObject);
|
|
|
|
DBGPRINT(1, ("USBSTOR_SenseDataCompletionRoutine: xfer error completion\n"));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
// The Request Sense Bulk transfer completed successfully.
|
|
|
|
LOGENTRY('sdc3', Irp->IoStatus.Status, bulkUrb->Hdr.Status,
|
|
bulkUrb->TransferBufferLength);
|
|
|
|
// Save the sense data so we can look at it after the command
|
|
// completion interrupt transfer completes.
|
|
//
|
|
if ((ULONG_PTR)AutoFlag == AUTO_SENSE)
|
|
{
|
|
RtlCopyMemory(&fdoDeviceExtension->Cbi.SenseData,
|
|
bulkUrb->TransferBuffer,
|
|
min(bulkUrb->TransferBufferLength,
|
|
sizeof(fdoDeviceExtension->Cbi.SenseData)));
|
|
|
|
// Update the SRB with the length of the sense data that was
|
|
// actually returned.
|
|
//
|
|
srb->SenseInfoBufferLength = (UCHAR)bulkUrb->TransferBufferLength;
|
|
}
|
|
|
|
DBGPRINT(2, ("Sense Data: 0x%02X 0x%02X 0x%02X\n",
|
|
fdoDeviceExtension->Cbi.SenseData.SenseKey,
|
|
fdoDeviceExtension->Cbi.SenseData.AdditionalSenseCode,
|
|
fdoDeviceExtension->Cbi.SenseData.AdditionalSenseCodeQualifier));
|
|
|
|
if (fdoDeviceExtension->InterruptInPipe)
|
|
{
|
|
// Command completion interrupt supported. Do not complete the
|
|
// request yet. Start the Request Sense command completion interrupt
|
|
// transfer.
|
|
//
|
|
ntStatus = USBSTOR_IssueRequestSenseInterruptRequest(
|
|
fdoDeviceObject,
|
|
Irp,
|
|
(ULONG_PTR)AutoFlag);
|
|
|
|
ntStatus = STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
else
|
|
{
|
|
// Command completion interrupt not supported. Complete the request
|
|
// now.
|
|
//
|
|
ntStatus = USBSTOR_ProcessRequestSenseCompletion(
|
|
fdoDeviceObject,
|
|
Irp,
|
|
(ULONG_PTR)AutoFlag);
|
|
}
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_SenseDataCompletionRoutine %08X\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// Phase 6, Request Sense Command completion Interrupt transfer
|
|
//
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_IssueRequestSenseInterruptRequest()
|
|
//
|
|
// This routine is called USBSTOR_SenseDataCompletionRoutine()
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// Starts a USB transfer to read the command completion interrupt data In
|
|
// the Interrupt endpoint.
|
|
//
|
|
// Sets USBSTOR_RequestSenseInterruptCompletionRoutine() as the completion
|
|
// routine.
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_IssueRequestSenseInterruptRequest (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG_PTR AutoFlag
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
USBD_PIPE_HANDLE pipeHandle;
|
|
ULONG transferBufferLength;
|
|
PVOID transferBuffer;
|
|
NTSTATUS ntStatus;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_IssueRequestSenseInterruptRequest\n"));
|
|
|
|
LOGENTRY('IRSI', DeviceObject, Irp, 0);
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
pipeHandle = fdoDeviceExtension->InterruptInPipe->PipeHandle;
|
|
|
|
transferBufferLength = sizeof(fdoDeviceExtension->Cbi.InterruptData);
|
|
|
|
transferBuffer = &fdoDeviceExtension->Cbi.InterruptData;
|
|
|
|
ntStatus = USBSTOR_IssueBulkOrInterruptRequest(
|
|
DeviceObject,
|
|
Irp,
|
|
pipeHandle, // PipeHandle
|
|
0, // TransferFlags
|
|
transferBufferLength, // TransferBufferLength
|
|
transferBuffer, // TransferBuffer
|
|
NULL, // TransferBufferMDL
|
|
USBSTOR_RequestSenseInterruptCompletionRoutine, // CompletionRoutine
|
|
(PVOID)AutoFlag); // Context
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_IssueRequestSenseInterruptRequest %08X\n",
|
|
ntStatus));
|
|
|
|
LOGENTRY('irsi', DeviceObject, Irp, ntStatus);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_RequestSenseInterruptCompletionRoutine()
|
|
//
|
|
// Completion routine used by USBSTOR_IssueRequestSenseInterruptRequest()
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// If a timeout reset occured, complete the request.
|
|
//
|
|
// Else if the Interrupt USB transfer failed due to any reason, complete the
|
|
// request and start a reset by queuing USBSTOR_ResetDeviceWorkItem().
|
|
//
|
|
// Else if the Interrupt USB transfer succeeded but the completion data is
|
|
// non-zero and AutoSense is not disabled, do not complete the request yet and
|
|
// start a Request Sense by calling USBSTOR_IssueRequestSenseCdb(AUTO).
|
|
//
|
|
// Else if the Interrupt USB transfer succeeded, ignore the interrupt data
|
|
// and complete the request by calling USBSTOR_ProcessRequestSenseCompletion().
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_RequestSenseInterruptCompletionRoutine (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID AutoFlag
|
|
)
|
|
{
|
|
PPDO_DEVICE_EXTENSION pdoDeviceExtension;
|
|
PDEVICE_OBJECT fdoDeviceObject;
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
NTSTATUS ntStatus;
|
|
|
|
struct _URB_BULK_OR_INTERRUPT_TRANSFER *intrUrb;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_RequestSenseInterruptCompletionRoutine\n"));
|
|
|
|
LOGENTRY('RSIC', DeviceObject, Irp, Irp->IoStatus.Status);
|
|
|
|
pdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(pdoDeviceExtension->Type == USBSTOR_DO_TYPE_PDO);
|
|
|
|
fdoDeviceObject = pdoDeviceExtension->ParentFDO;
|
|
fdoDeviceExtension = fdoDeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get a pointer to the Interrupt Transfer URB in our Device Extension
|
|
//
|
|
intrUrb = &fdoDeviceExtension->Urb.BulkIntrUrb;
|
|
|
|
// Get our Irp parameters
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
// If a timeout reset occured, complete the request.
|
|
//
|
|
if (USBSTOR_CheckRequestTimeOut(fdoDeviceObject,
|
|
Irp,
|
|
srb,
|
|
&ntStatus))
|
|
{
|
|
LOGENTRY('rsi1', fdoDeviceObject, Irp, srb);
|
|
DBGPRINT(1, ("USBSTOR_RequestSenseInterruptCompletionRoutine: timeout completion\n"));
|
|
return ntStatus;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status))
|
|
{
|
|
// The command completion Interrupt USB transfer failed. Complete
|
|
// this request now and then reset the device. The next request will
|
|
// be started when the reset completes.
|
|
//
|
|
LOGENTRY('rsi2', Irp->IoStatus.Status, intrUrb->Hdr.Status, 0);
|
|
|
|
DBGPRINT(1, ("Interrupt transfer failed %08X %08X\n",
|
|
Irp->IoStatus.Status, intrUrb->Hdr.Status));
|
|
|
|
ntStatus = STATUS_IO_DEVICE_ERROR;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
srb->SrbStatus = SRB_STATUS_BUS_RESET;
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
USBSTOR_QueueResetDevice(fdoDeviceObject);
|
|
|
|
DBGPRINT(1, ("USBSTOR_RequestSenseInterruptCompletionRoutine: xfer error completion\n"));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
// Request Sense Command Completion Interrupt tranfer completed successfully.
|
|
|
|
LOGENTRY('rsi3', Irp->IoStatus.Status, intrUrb->Hdr.Status,
|
|
intrUrb->TransferBufferLength);
|
|
|
|
ntStatus = USBSTOR_ProcessRequestSenseCompletion(
|
|
fdoDeviceObject,
|
|
Irp,
|
|
(ULONG_PTR)AutoFlag);
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_RequestSenseInterruptCompletionRoutine %08X\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_ProcessRequestSenseCompletion()
|
|
//
|
|
// This routine handles completion for USBSTOR_SenseDataCompletionRoutine()
|
|
// and USBSTOR_RequestSenseInterruptCompletionRoutine(). It basically just
|
|
// handles a couple of special cases.
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_ProcessRequestSenseCompletion (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG_PTR AutoFlag
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
KIRQL irql;
|
|
NTSTATUS ntStatus;
|
|
|
|
LOGENTRY('PRSC', DeviceObject, Irp, 0);
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get our Irp parameters
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
if (AutoFlag == NON_AUTO_SENSE)
|
|
{
|
|
LOGENTRY('prs1', DeviceObject, Irp, srb);
|
|
|
|
if ((fdoDeviceExtension->Cbi.SenseData.SenseKey ==
|
|
SCSI_SENSE_UNIT_ATTENTION)
|
|
&&
|
|
(fdoDeviceExtension->Cbi.SenseData.AdditionalSenseCode ==
|
|
SCSI_ADSENSE_BUS_RESET))
|
|
{
|
|
fdoDeviceExtension->LastSenseWasReset = TRUE;
|
|
}
|
|
|
|
// Just cleared the persistent error from the previous request,
|
|
// now issue the real request.
|
|
//
|
|
ntStatus = USBSTOR_IssueClientCdb(DeviceObject,
|
|
Irp);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
// SrbStatus and DataTransferLength were already set in
|
|
// USBSTOR_ClientCdbCompletion(), USBSTOR_ClientBulkCompletionRoutine(), or in
|
|
// or USBSTOR_InterruptDataCompletionRoutine() before we got here.
|
|
//
|
|
srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
|
|
|
|
USBSTOR_TranslateCDBComplete(DeviceObject, Irp, srb);
|
|
|
|
Irp->IoStatus.Information = srb->DataTransferLength;
|
|
|
|
ntStatus = Irp->IoStatus.Status;
|
|
|
|
// Disgusting hack for Y-E Data USB Floppy. On Medium Changed it doesn't
|
|
// automatically update the Write Protect status that you get back in
|
|
// the Mode Parameter Header on a Mode Sense. Supposedly a Start Unit
|
|
// request after a Medium Changed should cause it to update the Write
|
|
// Protect status, but that does not seem to be the case. A good old
|
|
// bus reset gets its attention though and updates the Write Protect
|
|
// status. Don't do this if the last status was a Bus Reset or that
|
|
// will cause a loop.
|
|
//
|
|
if ((fdoDeviceExtension->Cbi.SenseData.SenseKey ==
|
|
SCSI_SENSE_UNIT_ATTENTION)
|
|
&&
|
|
(fdoDeviceExtension->Cbi.SenseData.AdditionalSenseCode ==
|
|
SCSI_ADSENSE_MEDIUM_CHANGED)
|
|
&&
|
|
!fdoDeviceExtension->LastSenseWasReset
|
|
&&
|
|
TEST_FLAG(fdoDeviceExtension->DeviceHackFlags, DHF_MEDIUM_CHANGE_RESET))
|
|
{
|
|
LOGENTRY('prs2', DeviceObject, Irp, srb);
|
|
|
|
USBSTOR_QueueResetDevice(DeviceObject);
|
|
}
|
|
else
|
|
{
|
|
if ((fdoDeviceExtension->Cbi.SenseData.SenseKey ==
|
|
SCSI_SENSE_UNIT_ATTENTION)
|
|
&&
|
|
(fdoDeviceExtension->Cbi.SenseData.AdditionalSenseCode ==
|
|
SCSI_ADSENSE_BUS_RESET))
|
|
{
|
|
LOGENTRY('prs3', DeviceObject, Irp, srb);
|
|
|
|
fdoDeviceExtension->LastSenseWasReset = TRUE;
|
|
}
|
|
else
|
|
{
|
|
LOGENTRY('prs4', DeviceObject, Irp, srb);
|
|
|
|
fdoDeviceExtension->LastSenseWasReset = FALSE;
|
|
}
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, &irql);
|
|
{
|
|
IoStartNextPacket(DeviceObject, TRUE);
|
|
}
|
|
KeLowerIrql(irql);
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_QueueResetPipe()
|
|
//
|
|
// Called by USBSTOR_BulkCompletionRoutine() to clear the STALL on the bulk
|
|
// endpoints.
|
|
//
|
|
//******************************************************************************
|
|
|
|
VOID
|
|
USBSTOR_QueueResetPipe (
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
|
|
LOGENTRY('QRSP', DeviceObject, 0, 0);
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
INCREMENT_PENDING_IO_COUNT(fdoDeviceExtension);
|
|
|
|
IoQueueWorkItem(fdoDeviceExtension->IoWorkItem,
|
|
USBSTOR_ResetPipeWorkItem,
|
|
CriticalWorkQueue,
|
|
NULL);
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_ResetPipeWorkItem()
|
|
//
|
|
// WorkItem routine used by USBSTOR_ResetPipe()
|
|
//
|
|
// This routine runs at PASSIVE level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// Issue a Reset Pipe request to clear the Bulk endpoint STALL and reset
|
|
// the data toggle to Data0.
|
|
//
|
|
// If AutoSense is not disabled, do not complete the request yet and start
|
|
// a Request Sense by calling USBSTOR_IssueRequestSenseCdb(AUTO).
|
|
//
|
|
// Else if AutoSense is disabled, complete the request.
|
|
//
|
|
//******************************************************************************
|
|
|
|
VOID
|
|
USBSTOR_ResetPipeWorkItem (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
BOOLEAN persistentError;
|
|
KIRQL irql;
|
|
NTSTATUS ntStatus;
|
|
|
|
LOGENTRY('RSPW', DeviceObject, 0, 0);
|
|
|
|
DBGPRINT(2, ("enter: USBSTOR_ResetPipeWorkItem\n"));
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Reset the Bulk Endpoint. This clears the endpoint halt on the
|
|
// host side, resets the host side data toggle to Data0, and issues
|
|
// the Clear_Feature Endpoint_Stall request to the device.
|
|
//
|
|
ntStatus = USBSTOR_ResetPipe((PDEVICE_OBJECT)DeviceObject,
|
|
fdoDeviceExtension->BulkInPipe->PipeHandle);
|
|
|
|
ntStatus = USBSTOR_ResetPipe((PDEVICE_OBJECT)DeviceObject,
|
|
fdoDeviceExtension->BulkOutPipe->PipeHandle);
|
|
|
|
persistentError = FALSE;
|
|
|
|
KeAcquireSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock, &irql);
|
|
{
|
|
if (TEST_FLAG(fdoDeviceExtension->DeviceFlags, DF_PERSISTENT_ERROR))
|
|
{
|
|
persistentError = TRUE;
|
|
}
|
|
}
|
|
KeReleaseSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock, irql);
|
|
|
|
if (persistentError)
|
|
{
|
|
// We are not doing an AutoSense, start the next packet.
|
|
//
|
|
KeRaiseIrql(DISPATCH_LEVEL, &irql);
|
|
{
|
|
IoStartNextPacket(DeviceObject, TRUE);
|
|
}
|
|
KeLowerIrql(irql);
|
|
}
|
|
else
|
|
{
|
|
// We are doing an AutoSense, send the REQUEST_SENSE Cdb to the device.
|
|
//
|
|
ntStatus = USBSTOR_IssueRequestSenseCdb(
|
|
(PDEVICE_OBJECT)DeviceObject,
|
|
((PDEVICE_OBJECT)DeviceObject)->CurrentIrp,
|
|
AUTO_SENSE);
|
|
}
|
|
|
|
DBGPRINT(2, ("exit: USBSTOR_ResetPipeWorkItem\n"));
|
|
|
|
DECREMENT_PENDING_IO_COUNT(fdoDeviceExtension);
|
|
}
|
|
|
|
//
|
|
// Bulk-Only Routines
|
|
//
|
|
|
|
//
|
|
// Phase 1, CBW Transfer
|
|
//
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_CbwTransfer()
|
|
//
|
|
// This routine is called by USBSTOR_StartIo().
|
|
//
|
|
// It runs at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// Starts a USB transfer to write the Srb->Cdb wrapped inside a CBW out
|
|
// the Bulk OUT endpoint.
|
|
//
|
|
// Sets USBSTOR_CbwCompletion() as the completion routine.
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_CbwTransfer (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PPDO_DEVICE_EXTENSION pdoDeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PCBW cbw;
|
|
USBD_PIPE_HANDLE pipeHandle;
|
|
NTSTATUS ntStatus;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_CbwTransfer\n"));
|
|
|
|
LOGENTRY('ICBW', DeviceObject, Irp, 0);
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
fdoDeviceExtension->BulkOnly.StallCount = 0;
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
// Get the PDO extension from the PDO which was saved in the current
|
|
// stack location when the Irp was originally sent to the PDO.
|
|
//
|
|
pdoDeviceExtension = irpStack->DeviceObject->DeviceExtension;
|
|
ASSERT(pdoDeviceExtension->Type == USBSTOR_DO_TYPE_PDO);
|
|
|
|
LOGENTRY('icbl', DeviceObject, irpStack->DeviceObject,
|
|
pdoDeviceExtension->LUN);
|
|
|
|
// Get the client Srb
|
|
//
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
// Initialize the Command Block Wrapper
|
|
//
|
|
cbw = &fdoDeviceExtension->BulkOnly.CbwCsw.Cbw;
|
|
|
|
cbw->dCBWSignature = CBW_SIGNATURE;
|
|
|
|
cbw->dCBWTag = PtrToUlong(Irp);
|
|
|
|
cbw->dCBWDataTransferLength = srb->DataTransferLength;
|
|
|
|
cbw->bCBWFlags = (srb->SrbFlags & SRB_FLAGS_DATA_IN) ?
|
|
CBW_FLAGS_DATA_IN : CBW_FLAGS_DATA_OUT;
|
|
|
|
cbw->bCBWLUN = pdoDeviceExtension->LUN;
|
|
|
|
cbw->bCDBLength = srb->CdbLength;
|
|
|
|
RtlCopyMemory(cbw->CBWCDB, srb->Cdb, 16);
|
|
|
|
pipeHandle = fdoDeviceExtension->BulkOutPipe->PipeHandle;
|
|
|
|
ntStatus = USBSTOR_IssueBulkOrInterruptRequest(
|
|
DeviceObject,
|
|
Irp,
|
|
pipeHandle, // PipeHandle
|
|
0, // TransferFlags
|
|
sizeof(CBW), // TransferBufferLength
|
|
cbw, // TransferBuffer
|
|
NULL, // TransferBufferMDL
|
|
USBSTOR_CbwCompletion, // CompletionRoutine
|
|
NULL); // Context
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_CbwTransfer %08X\n", ntStatus));
|
|
|
|
LOGENTRY('icbw', DeviceObject, Irp, ntStatus);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_CbwCompletion()
|
|
//
|
|
// Completion routine used by USBSTOR_CbwTransfer()
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// If a timeout reset occured, complete the request.
|
|
//
|
|
// Else if the CBW USB transfer failed due to any reason, complete the
|
|
// request and start a reset by queuing USBSTOR_ResetDeviceWorkItem().
|
|
//
|
|
// Else if the CBW USB transfer succeeded and the Srb has a transfer buffer,
|
|
// do not complete the request yet and start the bulk data transfer by calling
|
|
// USBSTOR_DataTransfer().
|
|
//
|
|
// Else if the CBW USB transfer succeeded and the Srb has no transfer buffer,
|
|
// do not complete the request yet and start the CSW bulk transfer by calling
|
|
// USBSTOR_CswTransfer().
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_CbwCompletion (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID NotUsed
|
|
)
|
|
{
|
|
PPDO_DEVICE_EXTENSION pdoDeviceExtension;
|
|
PDEVICE_OBJECT fdoDeviceObject;
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
NTSTATUS ntStatus;
|
|
|
|
struct _URB_BULK_OR_INTERRUPT_TRANSFER *bulkUrb;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_CbwCompletion\n"));
|
|
|
|
LOGENTRY('CBWC', DeviceObject, Irp, Irp->IoStatus.Status);
|
|
|
|
pdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(pdoDeviceExtension->Type == USBSTOR_DO_TYPE_PDO);
|
|
|
|
fdoDeviceObject = pdoDeviceExtension->ParentFDO;
|
|
fdoDeviceExtension = fdoDeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get a pointer to the Bulk Transfer URB in our Device Extension
|
|
//
|
|
bulkUrb = &fdoDeviceExtension->Urb.BulkIntrUrb;
|
|
|
|
// Get our Irp parameters
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
// If a timeout reset occured, complete the request.
|
|
//
|
|
if (USBSTOR_CheckRequestTimeOut(fdoDeviceObject,
|
|
Irp,
|
|
srb,
|
|
&ntStatus))
|
|
{
|
|
LOGENTRY('cbw1', fdoDeviceObject, Irp, srb);
|
|
DBGPRINT(1, ("USBSTOR_CbwCompletion: timeout completion\n"));
|
|
return ntStatus;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status))
|
|
{
|
|
// The CBW Bulk Transfer was not successful.
|
|
//
|
|
LOGENTRY('cbw2', Irp->IoStatus.Status, bulkUrb->Hdr.Status, 0);
|
|
|
|
DBGPRINT(1, ("CBW transfer failed %08X %08X\n",
|
|
Irp->IoStatus.Status, bulkUrb->Hdr.Status));
|
|
|
|
srb = fdoDeviceExtension->OriginalSrb;
|
|
irpStack->Parameters.Scsi.Srb = srb;
|
|
|
|
// Complete this request now and then reset the device. The next
|
|
// request will be started when the reset completes.
|
|
//
|
|
ntStatus = STATUS_IO_DEVICE_ERROR;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
srb->SrbStatus = SRB_STATUS_BUS_RESET;
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
USBSTOR_QueueResetDevice(fdoDeviceObject);
|
|
|
|
DBGPRINT(1, ("USBSTOR_CbwCompletion: xfer error completion\n"));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
// The CBW Bulk Transfer was successful. Start the next phase, either
|
|
// the Data Bulk Transfer or CSW Bulk Transfer, and do not complete the
|
|
// request yet.
|
|
//
|
|
if (Irp->MdlAddress != NULL ||
|
|
srb != fdoDeviceExtension->OriginalSrb)
|
|
{
|
|
// The Srb has a transfer buffer, start the Data Bulk Transfer.
|
|
//
|
|
LOGENTRY('cbw3', fdoDeviceObject, Irp, srb);
|
|
|
|
ASSERT(srb->DataTransferLength != 0);
|
|
|
|
ntStatus = USBSTOR_DataTransfer(fdoDeviceObject,
|
|
Irp);
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
ntStatus = STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
else
|
|
{
|
|
srb = fdoDeviceExtension->OriginalSrb;
|
|
irpStack->Parameters.Scsi.Srb = srb;
|
|
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
srb->SrbStatus = SRB_STATUS_ERROR;
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
USBSTOR_QueueResetDevice(fdoDeviceObject);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The Srb has no transfer buffer. Start the CSW Bulk Transfer.
|
|
//
|
|
LOGENTRY('cbw4', fdoDeviceObject, Irp, srb);
|
|
|
|
ASSERT(srb->DataTransferLength == 0);
|
|
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
|
|
ntStatus = USBSTOR_CswTransfer(fdoDeviceObject,
|
|
Irp);
|
|
|
|
ntStatus = STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_CbwCompletion %08X\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// Phase 2, Data Transfer
|
|
//
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_DataTransfer()
|
|
//
|
|
// This routine is called by USBSTOR_ClientCdbCompletion().
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// Starts a USB transfer to read or write the Srb->DataBuffer data In or Out
|
|
// the Bulk endpoint.
|
|
//
|
|
// Sets USBSTOR_DataCompletion() as the completion routine.
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_DataTransfer (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PMDL mdl;
|
|
PVOID mdlVa;
|
|
PVOID transferBuffer;
|
|
USBD_PIPE_HANDLE pipeHandle;
|
|
ULONG transferFlags;
|
|
NTSTATUS ntStatus;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_DataTransfer\n"));
|
|
|
|
LOGENTRY('IBKD', DeviceObject, Irp, 0);
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get our Irp parameters
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
// Bulk IN or Bulk OUT?
|
|
//
|
|
if ((srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) == SRB_FLAGS_DATA_IN)
|
|
{
|
|
pipeHandle = fdoDeviceExtension->BulkInPipe->PipeHandle;
|
|
transferFlags = USBD_SHORT_TRANSFER_OK;
|
|
}
|
|
else if ((srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) == SRB_FLAGS_DATA_OUT)
|
|
{
|
|
pipeHandle = fdoDeviceExtension->BulkOutPipe->PipeHandle;
|
|
transferFlags = 0;
|
|
}
|
|
else
|
|
{
|
|
// Something is wrong if we end up here.
|
|
//
|
|
ASSERT((srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) &&
|
|
((srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) !=
|
|
SRB_FLAGS_UNSPECIFIED_DIRECTION));
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
mdl = NULL;
|
|
transferBuffer = NULL;
|
|
|
|
if (srb == fdoDeviceExtension->OriginalSrb)
|
|
{
|
|
// Check to see if this request is part of a split request.
|
|
//
|
|
mdlVa = MmGetMdlVirtualAddress(Irp->MdlAddress);
|
|
|
|
if (mdlVa == (PVOID)srb->DataBuffer)
|
|
{
|
|
// Not part of a split request, use original MDL
|
|
//
|
|
mdl = Irp->MdlAddress;
|
|
}
|
|
else
|
|
{
|
|
// Part of a split request, allocate new partial MDL
|
|
//
|
|
mdl = IoAllocateMdl(srb->DataBuffer,
|
|
srb->DataTransferLength,
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
if (mdl == NULL)
|
|
{
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
else
|
|
{
|
|
IoBuildPartialMdl(Irp->MdlAddress,
|
|
mdl,
|
|
srb->DataBuffer,
|
|
srb->DataTransferLength);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
transferBuffer = srb->DataBuffer;
|
|
|
|
// If (srb != fdoDeviceExtension->OriginalSrb) then
|
|
// srb->DataBuffer should equal OriginalSrb->SenseInfoBuffer,
|
|
// which should not be NULL if we end up here.
|
|
//
|
|
ASSERT(transferBuffer);
|
|
|
|
if (!transferBuffer) {
|
|
// just in case
|
|
ntStatus = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
if (mdl != NULL || transferBuffer != NULL)
|
|
{
|
|
ntStatus = USBSTOR_IssueBulkOrInterruptRequest(
|
|
DeviceObject,
|
|
Irp,
|
|
pipeHandle, // PipeHandle
|
|
transferFlags, // TransferFlags
|
|
srb->DataTransferLength, // TransferBufferLength
|
|
transferBuffer, // TransferBuffer
|
|
mdl, // TransferBufferMDL
|
|
USBSTOR_DataCompletion, // CompletionRoutine
|
|
NULL); // Context
|
|
|
|
// Just return STATUS_SUCCESS at this point. If there is an error,
|
|
// USBSTOR_DataCompletion() will handle it, not the caller of
|
|
// USBSTOR_DataTransfer().
|
|
//
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_DataTransfer %08X\n", ntStatus));
|
|
|
|
LOGENTRY('ibkd', DeviceObject, Irp, ntStatus);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_DataCompletion()
|
|
//
|
|
// Completion routine used by USBSTOR_DataTransfer
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// If a timeout reset occured, complete the request.
|
|
//
|
|
// Else if the Bulk USB transfer failed due to a STALL do not complete the
|
|
// request yet and start a pipe reset by calling USBSTOR_BulkQueueResetPipe().
|
|
//
|
|
// Else if the Bulk USB transfer failed due to some other reason, complete the
|
|
// request and start a reset by queuing USBSTOR_ResetDeviceWorkItem().
|
|
//
|
|
// Else if the Bulk USB transfer succeeded, start CSW transfer by calling
|
|
// USBSTOR_CswTransfer().
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_DataCompletion (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID NotUsed
|
|
)
|
|
{
|
|
PPDO_DEVICE_EXTENSION pdoDeviceExtension;
|
|
PDEVICE_OBJECT fdoDeviceObject;
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
NTSTATUS ntStatus;
|
|
|
|
struct _URB_BULK_OR_INTERRUPT_TRANSFER *bulkUrb;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_DataCompletion\n"));
|
|
|
|
LOGENTRY('BKDC', DeviceObject, Irp, Irp->IoStatus.Status);
|
|
|
|
pdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(pdoDeviceExtension->Type == USBSTOR_DO_TYPE_PDO);
|
|
|
|
fdoDeviceObject = pdoDeviceExtension->ParentFDO;
|
|
fdoDeviceExtension = fdoDeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get a pointer to the Bulk Transfer URB in our Device Extension
|
|
//
|
|
bulkUrb = &fdoDeviceExtension->Urb.BulkIntrUrb;
|
|
|
|
// Get our Irp parameters
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
if (srb == fdoDeviceExtension->OriginalSrb &&
|
|
bulkUrb->TransferBufferMDL != Irp->MdlAddress)
|
|
{
|
|
IoFreeMdl(bulkUrb->TransferBufferMDL);
|
|
}
|
|
|
|
// If a timeout reset occured, complete the request.
|
|
//
|
|
if (USBSTOR_CheckRequestTimeOut(fdoDeviceObject,
|
|
Irp,
|
|
srb,
|
|
&ntStatus))
|
|
{
|
|
LOGENTRY('bkd1', fdoDeviceObject, Irp, srb);
|
|
DBGPRINT(1, ("USBSTOR_DataCompletion: timeout completion\n"));
|
|
return ntStatus;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status))
|
|
{
|
|
// The Data Bulk Transfer was not successful. Look at how the
|
|
// the transfer failed to figure out how to recover.
|
|
//
|
|
|
|
LOGENTRY('bkd2', Irp->IoStatus.Status, bulkUrb->Hdr.Status, 0);
|
|
|
|
DBGPRINT(1, ("Data transfer failed %08X %08X\n",
|
|
Irp->IoStatus.Status, bulkUrb->Hdr.Status));
|
|
|
|
if (USBD_STATUS(bulkUrb->Hdr.Status) ==
|
|
USBD_STATUS(USBD_STATUS_STALL_PID))
|
|
{
|
|
// The device STALLed the Data Bulk Transfer
|
|
//
|
|
fdoDeviceExtension->BulkOnly.StallCount++;
|
|
|
|
// A STALL during the Data Bulk Transfer does not necessarily
|
|
// indicate an error. Accept the data that was actually
|
|
// transferred. If a STALL was seen it must have been seen
|
|
// before the requested amount of data was transferred.
|
|
//
|
|
ASSERT(bulkUrb->TransferBufferLength < srb->DataTransferLength);
|
|
srb->DataTransferLength = bulkUrb->TransferBufferLength;
|
|
srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
|
|
|
|
LOGENTRY('bkd3', fdoDeviceObject, Irp, srb);
|
|
|
|
// Queue a bulk pipe reset. After the bulk pipe reset
|
|
// completes, a CSW transfer will be started.
|
|
//
|
|
USBSTOR_BulkQueueResetPipe(fdoDeviceObject);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
else
|
|
{
|
|
LOGENTRY('bkd4', fdoDeviceObject, Irp, srb);
|
|
|
|
// Else some other strange error has occured. Maybe the device is
|
|
// unplugged, or maybe the device port was disabled, or maybe the
|
|
// request was cancelled.
|
|
//
|
|
// Complete this request now and then reset the device. The next
|
|
// request will be started when the reset completes.
|
|
//
|
|
srb = fdoDeviceExtension->OriginalSrb;
|
|
irpStack->Parameters.Scsi.Srb = srb;
|
|
|
|
ntStatus = STATUS_IO_DEVICE_ERROR;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
srb->SrbStatus = SRB_STATUS_BUS_RESET;
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
USBSTOR_QueueResetDevice(fdoDeviceObject);
|
|
|
|
DBGPRINT(1, ("USBSTOR_DataCompletion: xfer error completion\n"));
|
|
|
|
return ntStatus;
|
|
}
|
|
}
|
|
|
|
// Check for overrun
|
|
//
|
|
if (bulkUrb->TransferBufferLength < srb->DataTransferLength)
|
|
{
|
|
srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
|
|
}
|
|
else
|
|
{
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
}
|
|
|
|
// Update the the Srb data transfer length based on the actual bulk
|
|
// transfer length.
|
|
//
|
|
srb->DataTransferLength = bulkUrb->TransferBufferLength;
|
|
|
|
// Client data Bulk Transfer successful completion. Start the CSW transfer.
|
|
//
|
|
LOGENTRY('bkd5', fdoDeviceObject, Irp, bulkUrb->TransferBufferLength);
|
|
|
|
ntStatus = USBSTOR_CswTransfer(fdoDeviceObject,
|
|
Irp);
|
|
|
|
ntStatus = STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_DataCompletion %08X\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_CswTransfer()
|
|
//
|
|
// This routine is called by USBSTOR_CbwCompletion() and
|
|
// USBSTOR_DataCompletion()
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// Starts a USB transfer to read the CSW in the Bulk IN endpoint.
|
|
//
|
|
// Sets USBSTOR_CswCompletion() as the completion routine.
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_CswTransfer (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PCSW csw;
|
|
USBD_PIPE_HANDLE pipeHandle;
|
|
ULONG transferFlags;
|
|
ULONG transferBufferLength;
|
|
NTSTATUS ntStatus;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_CswTransfer\n"));
|
|
|
|
LOGENTRY('ICSW', DeviceObject, Irp, 0);
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
csw = &fdoDeviceExtension->BulkOnly.CbwCsw.Csw;
|
|
|
|
pipeHandle = fdoDeviceExtension->BulkInPipe->PipeHandle;
|
|
|
|
// Workaround for USB 2.0 controller Data Toggle / Babble bug
|
|
//
|
|
if (fdoDeviceExtension->BulkInPipe->MaximumPacketSize ==
|
|
sizeof(fdoDeviceExtension->BulkOnly.CbwCsw.MaxPacketSize))
|
|
|
|
{
|
|
transferFlags = USBD_SHORT_TRANSFER_OK;
|
|
|
|
transferBufferLength =
|
|
sizeof(fdoDeviceExtension->BulkOnly.CbwCsw.MaxPacketSize);
|
|
}
|
|
else
|
|
{
|
|
transferFlags = 0;
|
|
|
|
transferBufferLength = sizeof(CSW);
|
|
}
|
|
|
|
ntStatus = USBSTOR_IssueBulkOrInterruptRequest(
|
|
DeviceObject,
|
|
Irp,
|
|
pipeHandle, // PipeHandle
|
|
transferFlags, // TransferFlags
|
|
transferBufferLength, // TransferBufferLength
|
|
csw, // TransferBuffer
|
|
NULL, // TransferBufferMDL
|
|
USBSTOR_CswCompletion, // CompletionRoutine
|
|
NULL); // Context
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_CswTransfer %08X\n", ntStatus));
|
|
|
|
LOGENTRY('icsw', DeviceObject, Irp, ntStatus);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_CswCompletion()
|
|
//
|
|
// Completion routine used by USBSTOR_CswTransfer()
|
|
//
|
|
// This routine may run at DPC level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// If a timeout reset occured, complete the request.
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_CswCompletion (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID NotUsed
|
|
)
|
|
{
|
|
PPDO_DEVICE_EXTENSION pdoDeviceExtension;
|
|
PDEVICE_OBJECT fdoDeviceObject;
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PCSW csw;
|
|
KIRQL irql;
|
|
NTSTATUS ntStatus;
|
|
|
|
struct _URB_BULK_OR_INTERRUPT_TRANSFER *bulkUrb;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_CswCompletion\n"));
|
|
|
|
LOGENTRY('CSWC', DeviceObject, Irp, Irp->IoStatus.Status);
|
|
|
|
pdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(pdoDeviceExtension->Type == USBSTOR_DO_TYPE_PDO);
|
|
|
|
fdoDeviceObject = pdoDeviceExtension->ParentFDO;
|
|
fdoDeviceExtension = fdoDeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get a pointer to the Bulk Transfer URB in our Device Extension
|
|
//
|
|
bulkUrb = &fdoDeviceExtension->Urb.BulkIntrUrb;
|
|
|
|
// Get our Irp parameters
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
csw = &fdoDeviceExtension->BulkOnly.CbwCsw.Csw;
|
|
|
|
// If a timeout reset occured, complete the request.
|
|
//
|
|
if (USBSTOR_CheckRequestTimeOut(fdoDeviceObject,
|
|
Irp,
|
|
srb,
|
|
&ntStatus))
|
|
{
|
|
LOGENTRY('csw1', fdoDeviceObject, Irp, srb);
|
|
DBGPRINT(1, ("USBSTOR_CswCompletion: timeout completion\n"));
|
|
return ntStatus;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status))
|
|
{
|
|
// The Data Bulk Transfer was not successful. Look at how the
|
|
// the transfer failed to figure out how to recover.
|
|
//
|
|
|
|
LOGENTRY('csw2', Irp->IoStatus.Status, bulkUrb->Hdr.Status, 0);
|
|
|
|
DBGPRINT(1, ("CSW transfer failed %08X %08X\n",
|
|
Irp->IoStatus.Status, bulkUrb->Hdr.Status));
|
|
|
|
if (USBD_STATUS(bulkUrb->Hdr.Status) ==
|
|
USBD_STATUS(USBD_STATUS_STALL_PID) &&
|
|
fdoDeviceExtension->BulkOnly.StallCount < 2)
|
|
{
|
|
// The device STALLed the CSW Bulk Transfer
|
|
//
|
|
fdoDeviceExtension->BulkOnly.StallCount++;
|
|
|
|
LOGENTRY('csw3', fdoDeviceObject, Irp, srb);
|
|
|
|
// Queue a bulk pipe reset. After the bulk pipe reset
|
|
// completes, a CSW transfer will be started.
|
|
//
|
|
USBSTOR_BulkQueueResetPipe(fdoDeviceObject);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
else
|
|
{
|
|
LOGENTRY('csw4', fdoDeviceObject, Irp, srb);
|
|
|
|
// Else some other strange error has occured. Maybe the device is
|
|
// unplugged, or maybe the device port was disabled, or maybe the
|
|
// request was cancelled.
|
|
//
|
|
// Complete this request now and then reset the device. The next
|
|
// request will be started when the reset completes.
|
|
//
|
|
srb = fdoDeviceExtension->OriginalSrb;
|
|
irpStack->Parameters.Scsi.Srb = srb;
|
|
|
|
ntStatus = STATUS_IO_DEVICE_ERROR;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
srb->SrbStatus = SRB_STATUS_BUS_RESET;
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
USBSTOR_QueueResetDevice(fdoDeviceObject);
|
|
|
|
DBGPRINT(1, ("USBSTOR_DataCompletion: xfer error completion\n"));
|
|
|
|
return ntStatus;
|
|
}
|
|
}
|
|
|
|
if (csw->bCSWStatus == CSW_STATUS_GOOD)
|
|
{
|
|
// Complete this request now. Also start the next request now.
|
|
//
|
|
|
|
// SrbStatus should have been set in USBSTOR_DataCompletion()
|
|
//
|
|
ASSERT(srb->SrbStatus != SRB_STATUS_PENDING);
|
|
|
|
if (srb != fdoDeviceExtension->OriginalSrb)
|
|
{
|
|
// Update the original SRB with the length of the sense data that
|
|
// was actually returned.
|
|
//
|
|
fdoDeviceExtension->OriginalSrb->SenseInfoBufferLength =
|
|
(UCHAR)srb->DataTransferLength;
|
|
|
|
srb = fdoDeviceExtension->OriginalSrb;
|
|
irpStack->Parameters.Scsi.Srb = srb;
|
|
|
|
srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
|
|
}
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
Irp->IoStatus.Information = srb->DataTransferLength;
|
|
|
|
LOGENTRY('csw5', fdoDeviceObject, Irp, srb);
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, &irql);
|
|
{
|
|
IoStartNextPacket(fdoDeviceObject, TRUE);
|
|
}
|
|
KeLowerIrql(irql);
|
|
}
|
|
else if (csw->bCSWStatus == CSW_STATUS_FAILED &&
|
|
srb == fdoDeviceExtension->OriginalSrb)
|
|
{
|
|
LOGENTRY('csw6', fdoDeviceObject, Irp, srb);
|
|
|
|
srb->SrbStatus = SRB_STATUS_ERROR;
|
|
srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
|
|
srb->DataTransferLength = 0; // XXXXX Leave as set by bulk completion???
|
|
|
|
if (!(srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) &&
|
|
(srb->SenseInfoBufferLength != 0) &&
|
|
(srb->SenseInfoBuffer != NULL))
|
|
{
|
|
// Start the Request Sense thing
|
|
//
|
|
ntStatus = USBSTOR_IssueRequestSense(fdoDeviceObject,
|
|
Irp);
|
|
|
|
ntStatus = STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
}
|
|
else
|
|
{
|
|
ntStatus = STATUS_IO_DEVICE_ERROR; // XXXXX
|
|
Irp->IoStatus.Status = ntStatus; // XXXXX
|
|
Irp->IoStatus.Information = 0; // XXXXX
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, &irql);
|
|
{
|
|
IoStartNextPacket(fdoDeviceObject, TRUE);
|
|
}
|
|
KeLowerIrql(irql);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOGENTRY('csw7', fdoDeviceObject, Irp, srb);
|
|
|
|
// PHASE ERROR or Unknown Status
|
|
//
|
|
// Complete this request now and then reset the device. The next
|
|
// request will be started when the reset completes.
|
|
//
|
|
srb = fdoDeviceExtension->OriginalSrb;
|
|
irpStack->Parameters.Scsi.Srb = srb;
|
|
|
|
ntStatus = STATUS_IO_DEVICE_ERROR;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
srb->SrbStatus = SRB_STATUS_BUS_RESET;
|
|
|
|
USBSTOR_TranslateCDBComplete(fdoDeviceObject, Irp, srb);
|
|
|
|
USBSTOR_QueueResetDevice(fdoDeviceObject);
|
|
}
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_CswCompletion %08X\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_IssueRequestSense()
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_IssueRequestSense (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
NTSTATUS ntStatus;
|
|
|
|
DBGPRINT(3, ("enter: USBSTOR_IssueRequestSense\n"));
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get the current Srb
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
// Get a pointer to the internal Srb.
|
|
//
|
|
srb = &fdoDeviceExtension->BulkOnly.InternalSrb;
|
|
|
|
irpStack->Parameters.Scsi.Srb = srb;
|
|
|
|
|
|
// Initialize SRB & CDB to all ZERO
|
|
//
|
|
RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
// Initialize SRB
|
|
//
|
|
srb->Length = sizeof(SCSI_REQUEST_BLOCK);
|
|
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
srb->CdbLength = 12;
|
|
srb->SrbFlags = SRB_FLAGS_DATA_IN |
|
|
SRB_FLAGS_DISABLE_AUTOSENSE;
|
|
|
|
srb->DataTransferLength = fdoDeviceExtension->OriginalSrb->SenseInfoBufferLength;
|
|
srb->DataBuffer = fdoDeviceExtension->OriginalSrb->SenseInfoBuffer;
|
|
|
|
// Initialize CDB
|
|
//
|
|
srb->Cdb[0] = SCSIOP_REQUEST_SENSE;
|
|
srb->Cdb[4] = fdoDeviceExtension->OriginalSrb->SenseInfoBufferLength;
|
|
|
|
ntStatus = USBSTOR_CbwTransfer(DeviceObject,
|
|
Irp);
|
|
|
|
return ntStatus;
|
|
|
|
DBGPRINT(3, ("exit: USBSTOR_IssueRequestSense %08X\n", ntStatus));
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_BulkQueueResetPipe()
|
|
//
|
|
// Called by USBSTOR_DataCompletion() and USBSTOR_CswCompletion() to clear the
|
|
// STALL on the bulk endpoints.
|
|
//
|
|
//******************************************************************************
|
|
|
|
VOID
|
|
USBSTOR_BulkQueueResetPipe (
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
|
|
LOGENTRY('QRSP', DeviceObject, 0, 0);
|
|
|
|
DBGFBRK(DBGF_BRK_RESETPIPE);
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
INCREMENT_PENDING_IO_COUNT(fdoDeviceExtension);
|
|
|
|
IoQueueWorkItem(fdoDeviceExtension->IoWorkItem,
|
|
USBSTOR_BulkResetPipeWorkItem,
|
|
CriticalWorkQueue,
|
|
NULL);
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_BulkResetPipeWorkItem()
|
|
//
|
|
// WorkItem routine used by USBSTOR_BulkQueueResetPipe()
|
|
//
|
|
// This routine runs at PASSIVE level.
|
|
//
|
|
// Basic idea:
|
|
//
|
|
// Issue a Reset Pipe request to clear the Bulk endpoint STALL and reset
|
|
// the data toggle to Data0.
|
|
//
|
|
// Then start the CSW transfer.
|
|
//
|
|
//******************************************************************************
|
|
|
|
VOID
|
|
USBSTOR_BulkResetPipeWorkItem (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
NTSTATUS ntStatus;
|
|
|
|
struct _URB_BULK_OR_INTERRUPT_TRANSFER *bulkUrb;
|
|
|
|
LOGENTRY('RSPW', DeviceObject, 0, 0);
|
|
|
|
DBGPRINT(2, ("enter: USBSTOR_BulkResetPipeWorkItem\n"));
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Get a pointer to the Bulk Transfer URB in our Device Extension.
|
|
// We'll pull the appropriate Bulk Endpoint pipe handle out of the URB.
|
|
//
|
|
// NOTE: This assumes that the URB in our Device Extension has
|
|
// not been touched since USBSTOR_DataCompletion() or
|
|
// USBSTOR_CswCompletion() called USBSTOR_BulkQueueResetPipe().
|
|
//
|
|
bulkUrb = &fdoDeviceExtension->Urb.BulkIntrUrb;
|
|
|
|
// Reset the Bulk Endpoint. This clears the endpoint halt on the
|
|
// host side, resets the host side data toggle to Data0, and issues
|
|
// the Clear_Feature Endpoint_Stall request to the device.
|
|
//
|
|
ntStatus = USBSTOR_ResetPipe((PDEVICE_OBJECT)DeviceObject,
|
|
bulkUrb->PipeHandle);
|
|
|
|
ntStatus = USBSTOR_CswTransfer(
|
|
(PDEVICE_OBJECT)DeviceObject,
|
|
((PDEVICE_OBJECT)DeviceObject)->CurrentIrp);
|
|
|
|
|
|
DBGPRINT(2, ("exit: USBSTOR_BulkResetPipeWorkItem\n"));
|
|
|
|
DECREMENT_PENDING_IO_COUNT(fdoDeviceExtension);
|
|
}
|
|
|
|
//
|
|
// CBI / Bulk-Only Common Routines
|
|
//
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_TimerTick()
|
|
//
|
|
// Called once per second at DISPATCH_LEVEL after the device has been started.
|
|
// Checks to see if there is an active Srb which has timed out, and if so,
|
|
// kicks off the reset recovery process.
|
|
//
|
|
//******************************************************************************
|
|
|
|
VOID
|
|
USBSTOR_TimerTick (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID NotUsed
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
BOOLEAN reset;
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
reset = FALSE;
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&fdoDeviceExtension->ExtensionDataSpinLock);
|
|
{
|
|
if (!TEST_FLAG(fdoDeviceExtension->DeviceFlags, DF_RESET_IN_PROGRESS) &&
|
|
TEST_FLAG(fdoDeviceExtension->DeviceFlags, DF_SRB_IN_PROGRESS))
|
|
{
|
|
// There is no reset in progress and there is an Srb in progress.
|
|
// Decrement the timeout of the Srb. If it reaches zero, then we
|
|
// will reset the device.
|
|
//
|
|
if (--fdoDeviceExtension->SrbTimeout == 0)
|
|
{
|
|
SET_FLAG(fdoDeviceExtension->DeviceFlags, DF_RESET_IN_PROGRESS);
|
|
|
|
reset = TRUE;
|
|
}
|
|
}
|
|
}
|
|
KeReleaseSpinLockFromDpcLevel(&fdoDeviceExtension->ExtensionDataSpinLock);
|
|
|
|
if (reset)
|
|
{
|
|
LOGENTRY('TIMR', DeviceObject, 0, 0);
|
|
|
|
DBGPRINT(2, ("queuing USBSTOR_ResetDeviceWorkItem\n"));
|
|
|
|
// Queue WorkItem to reset the device
|
|
//
|
|
INCREMENT_PENDING_IO_COUNT(fdoDeviceExtension);
|
|
|
|
IoQueueWorkItem(fdoDeviceExtension->IoWorkItem,
|
|
USBSTOR_ResetDeviceWorkItem,
|
|
CriticalWorkQueue,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_QueueResetDevice()
|
|
//
|
|
//******************************************************************************
|
|
|
|
VOID
|
|
USBSTOR_QueueResetDevice (
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
KIRQL irql;
|
|
|
|
LOGENTRY('QRSD', DeviceObject, 0, 0);
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
KeAcquireSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock, &irql);
|
|
{
|
|
SET_FLAG(fdoDeviceExtension->DeviceFlags, DF_RESET_IN_PROGRESS);
|
|
}
|
|
KeReleaseSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock, irql);
|
|
|
|
// Queue WorkItem to reset the device
|
|
//
|
|
INCREMENT_PENDING_IO_COUNT(fdoDeviceExtension);
|
|
|
|
IoQueueWorkItem(fdoDeviceExtension->IoWorkItem,
|
|
USBSTOR_ResetDeviceWorkItem,
|
|
CriticalWorkQueue,
|
|
NULL);
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_ResetDeviceWorkItem()
|
|
//
|
|
// Work item which runs at PASSIVE_LEVEL in the context of a system thread.
|
|
// This routine first checks to see if the device is still attached, and if
|
|
// it is, the device is reset.
|
|
//
|
|
//******************************************************************************
|
|
|
|
VOID
|
|
USBSTOR_ResetDeviceWorkItem (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
KIRQL irql;
|
|
ULONG retryCount;
|
|
NTSTATUS ntStatus;
|
|
|
|
LOGENTRY('RSDW', DeviceObject, 0, 0);
|
|
|
|
DBGFBRK(DBGF_BRK_RESET);
|
|
|
|
DBGPRINT(2, ("enter: USBSTOR_ResetDeviceWorkItem\n"));
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// If the we timed out a request and it is still pending, cancel
|
|
// it and then wait for the cancel to finish, and then complete
|
|
// the request.
|
|
//
|
|
if (fdoDeviceExtension->PendingIrp)
|
|
{
|
|
LOGENTRY('rsd1', DeviceObject, fdoDeviceExtension->PendingIrp, 0);
|
|
|
|
IoCancelIrp(fdoDeviceExtension->PendingIrp);
|
|
|
|
LOGENTRY('rsd2', DeviceObject, fdoDeviceExtension->PendingIrp, 0);
|
|
|
|
KeWaitForSingleObject(&fdoDeviceExtension->CancelEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
LOGENTRY('rsd3', DeviceObject, fdoDeviceExtension->PendingIrp, 0);
|
|
|
|
// Some storage drivers (e.g. CDROM.SYS) assume that requests complete
|
|
// at DISPATCH_LEVEL.
|
|
//
|
|
KeRaiseIrql(DISPATCH_LEVEL, &irql);
|
|
{
|
|
IoCompleteRequest(fdoDeviceExtension->PendingIrp, IO_NO_INCREMENT);
|
|
}
|
|
KeLowerIrql(irql);
|
|
|
|
fdoDeviceExtension->PendingIrp = NULL;
|
|
}
|
|
|
|
// Try the reset up to 3 times
|
|
//
|
|
for (retryCount = 0; retryCount < 3; retryCount++)
|
|
{
|
|
//
|
|
// First figure out if the device is still connected.
|
|
//
|
|
ntStatus = USBSTOR_IsDeviceConnected(DeviceObject);
|
|
|
|
if (!NT_SUCCESS(ntStatus))
|
|
{
|
|
// Give up if the device is no longer connected.
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The device is still connected, now reset the device.
|
|
//
|
|
DBGPRINT(1, ("Reseting Device %d\n", retryCount));
|
|
|
|
ntStatus = USBSTOR_ResetDevice(DeviceObject);
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
// Reset was successful!
|
|
break;
|
|
}
|
|
}
|
|
|
|
KeAcquireSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock, &irql);
|
|
{
|
|
CLEAR_FLAG(fdoDeviceExtension->DeviceFlags, DF_RESET_IN_PROGRESS);
|
|
|
|
// If the reset failed, then abandon all hope and mark the device as
|
|
// disconnected.
|
|
//
|
|
if (!NT_SUCCESS(ntStatus))
|
|
{
|
|
SET_FLAG(fdoDeviceExtension->DeviceFlags, DF_DEVICE_DISCONNECTED);
|
|
}
|
|
}
|
|
KeReleaseSpinLock(&fdoDeviceExtension->ExtensionDataSpinLock, irql);
|
|
|
|
// A request has failed in a bad way or timed out if we are reseting the
|
|
// device. If the protocol was not specified then the default protocol
|
|
// was DeviceProtocolCB. Let's try DeviceProtocolBulkOnly now and see if
|
|
// we have any better luck. (Note that if a DeviceProtocolCB device fails
|
|
// the first request in a bad way then will we retry the first request as
|
|
// a DeviceProtocolBulkOnly device, which will then also fail and we will
|
|
// not recover from that situation).
|
|
//
|
|
if (fdoDeviceExtension->DriverFlags == DeviceProtocolUnspecified)
|
|
{
|
|
DBGPRINT(1, ("Setting Unspecified device to BulkOnly\n"));
|
|
|
|
fdoDeviceExtension->DriverFlags = DeviceProtocolBulkOnly;
|
|
}
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, &irql);
|
|
{
|
|
IoStartNextPacket(DeviceObject, TRUE);
|
|
}
|
|
KeLowerIrql(irql);
|
|
|
|
DECREMENT_PENDING_IO_COUNT(fdoDeviceExtension);
|
|
|
|
DBGPRINT(2, ("exit: USBSTOR_ResetDeviceWorkItem %08X\n", ntStatus));
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_IsDeviceConnected()
|
|
//
|
|
// This routine checks to see if the device is still attached.
|
|
//
|
|
// This routine runs at PASSIVE level.
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_IsDeviceConnected (
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIRP irp;
|
|
KEVENT localevent;
|
|
PIO_STACK_LOCATION nextStack;
|
|
ULONG portStatus;
|
|
NTSTATUS ntStatus;
|
|
|
|
DBGPRINT(1, ("enter: USBSTOR_IsDeviceConnected\n"));
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Allocate the Irp
|
|
//
|
|
irp = IoAllocateIrp((CCHAR)(fdoDeviceExtension->StackDeviceObject->StackSize),
|
|
FALSE);
|
|
|
|
if (irp == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// Initialize the event we'll wait on.
|
|
//
|
|
KeInitializeEvent(&localevent,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
// Set the Irp parameters
|
|
//
|
|
nextStack = IoGetNextIrpStackLocation(irp);
|
|
|
|
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
|
|
nextStack->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_INTERNAL_USB_GET_PORT_STATUS;
|
|
|
|
nextStack->Parameters.Others.Argument1 = &portStatus;
|
|
|
|
// Set the completion routine, which will signal the event
|
|
//
|
|
IoSetCompletionRoutineEx(DeviceObject,
|
|
irp,
|
|
USBSTOR_SyncCompletionRoutine,
|
|
&localevent,
|
|
TRUE, // InvokeOnSuccess
|
|
TRUE, // InvokeOnError
|
|
TRUE); // InvokeOnCancel
|
|
|
|
// Pass the Irp down the stack
|
|
//
|
|
ntStatus = IoCallDriver(fdoDeviceExtension->StackDeviceObject,
|
|
irp);
|
|
|
|
// If the request is pending, block until it completes
|
|
//
|
|
if (ntStatus == STATUS_PENDING)
|
|
{
|
|
KeWaitForSingleObject(&localevent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
ntStatus = irp->IoStatus.Status;
|
|
}
|
|
|
|
IoFreeIrp(irp);
|
|
|
|
if (NT_SUCCESS(ntStatus) && !(portStatus & USBD_PORT_CONNECTED))
|
|
{
|
|
ntStatus = STATUS_DEVICE_DOES_NOT_EXIST;
|
|
}
|
|
|
|
DBGPRINT(1, ("exit: USBSTOR_IsDeviceConnected %08X\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_ResetDevice()
|
|
//
|
|
// This routine resets the device (actually it resets the port to which the
|
|
// device is attached).
|
|
//
|
|
// This routine runs at PASSIVE level.
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_ResetDevice (
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PIRP irp;
|
|
KEVENT localevent;
|
|
PIO_STACK_LOCATION nextStack;
|
|
ULONG portStatus;
|
|
NTSTATUS ntStatus;
|
|
|
|
DBGPRINT(1, ("enter: USBSTOR_ResetDevice\n"));
|
|
|
|
fdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
// Allocate the Irp
|
|
//
|
|
irp = IoAllocateIrp((CCHAR)(fdoDeviceExtension->StackDeviceObject->StackSize),
|
|
FALSE);
|
|
|
|
if (irp == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// Initialize the event we'll wait on.
|
|
//
|
|
KeInitializeEvent(&localevent,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
// Set the Irp parameters
|
|
//
|
|
nextStack = IoGetNextIrpStackLocation(irp);
|
|
|
|
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
|
|
nextStack->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_INTERNAL_USB_RESET_PORT;
|
|
|
|
// Set the completion routine, which will signal the event
|
|
//
|
|
IoSetCompletionRoutineEx(DeviceObject,
|
|
irp,
|
|
USBSTOR_SyncCompletionRoutine,
|
|
&localevent,
|
|
TRUE, // InvokeOnSuccess
|
|
TRUE, // InvokeOnError
|
|
TRUE); // InvokeOnCancel
|
|
|
|
// Pass the Irp & Urb down the stack
|
|
//
|
|
ntStatus = IoCallDriver(fdoDeviceExtension->StackDeviceObject,
|
|
irp);
|
|
|
|
// If the request is pending, block until it completes
|
|
//
|
|
if (ntStatus == STATUS_PENDING)
|
|
{
|
|
KeWaitForSingleObject(&localevent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
ntStatus = irp->IoStatus.Status;
|
|
}
|
|
|
|
IoFreeIrp(irp);
|
|
|
|
DBGPRINT(1, ("exit: USBSTOR_ResetDevice %08X\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_IssueInternalCdb()
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_IssueInternalCdb (
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PVOID DataBuffer,
|
|
PULONG DataTransferLength,
|
|
PCDB Cdb,
|
|
UCHAR CdbLength,
|
|
ULONG TimeOutValue
|
|
)
|
|
{
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION nextStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PSENSE_DATA senseInfoBuffer;
|
|
PMDL mdl;
|
|
ULONG retryCount;
|
|
KEVENT localevent;
|
|
NTSTATUS ntStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
DBGPRINT(2, ("enter: USBSTOR_IssueInternalCdb\n"));
|
|
|
|
// Initialize these so we can bail early if an allocation fails
|
|
//
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
irp = NULL;
|
|
srb = NULL;
|
|
senseInfoBuffer = NULL;
|
|
mdl = NULL;
|
|
|
|
// Allocate the Srb
|
|
//
|
|
srb = ExAllocatePoolWithTag(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK),
|
|
POOL_TAG);
|
|
|
|
if (srb == NULL)
|
|
{
|
|
goto USBSTOR_GetInquiryData_Exit;
|
|
}
|
|
|
|
// Allocate the sense buffer
|
|
//
|
|
senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPool, SENSE_BUFFER_SIZE,
|
|
POOL_TAG);
|
|
|
|
if (senseInfoBuffer == NULL)
|
|
{
|
|
goto USBSTOR_GetInquiryData_Exit;
|
|
}
|
|
|
|
|
|
// Try the request up to 3 times
|
|
//
|
|
for (retryCount = 0; retryCount < 3; retryCount++)
|
|
{
|
|
// Allocate an Irp including a stack location for a completion routine
|
|
//
|
|
irp = IoAllocateIrp((CCHAR)(DeviceObject->StackSize), FALSE);
|
|
|
|
if (irp == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
nextStack = IoGetNextIrpStackLocation(irp);
|
|
nextStack->MajorFunction = IRP_MJ_SCSI;
|
|
nextStack->Parameters.Scsi.Srb = srb;
|
|
|
|
// (Re)Initialize the Srb
|
|
//
|
|
RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); // SRB & CDB all ZERO
|
|
|
|
srb->Length = sizeof(SCSI_REQUEST_BLOCK);
|
|
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
srb->CdbLength = CdbLength;
|
|
srb->SrbFlags = SRB_FLAGS_DATA_IN;
|
|
|
|
srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
|
|
srb->SenseInfoBuffer = senseInfoBuffer;
|
|
|
|
srb->DataTransferLength = *DataTransferLength;
|
|
srb->DataBuffer = DataBuffer;
|
|
|
|
srb->TimeOutValue = TimeOutValue;
|
|
|
|
// (Re)Initialize the Cdb
|
|
//
|
|
RtlCopyMemory(srb->Cdb, Cdb, CdbLength);
|
|
|
|
// Initialize the MDL (first time only)
|
|
//
|
|
if (retryCount == 0)
|
|
{
|
|
mdl = IoAllocateMdl(DataBuffer,
|
|
*DataTransferLength,
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if (!mdl)
|
|
{
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto USBSTOR_GetInquiryData_Exit;
|
|
}
|
|
|
|
MmBuildMdlForNonPagedPool(mdl);
|
|
}
|
|
|
|
irp->MdlAddress = mdl;
|
|
|
|
|
|
// Initialize the event we'll wait on
|
|
//
|
|
KeInitializeEvent(&localevent,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
// Set the completion routine, which will signal the event
|
|
//
|
|
IoSetCompletionRoutine(irp,
|
|
USBSTOR_SyncCompletionRoutine,
|
|
&localevent,
|
|
TRUE, // InvokeOnSuccess
|
|
TRUE, // InvokeOnError
|
|
TRUE); // InvokeOnCancel
|
|
|
|
// Pass the Irp & Srb down the stack
|
|
//
|
|
ntStatus = IoCallDriver(DeviceObject, irp);
|
|
|
|
// If the request is pending, block until it completes
|
|
//
|
|
if (ntStatus == STATUS_PENDING)
|
|
{
|
|
KeWaitForSingleObject(&localevent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
// Get final completion status
|
|
//
|
|
ntStatus = irp->IoStatus.Status;
|
|
}
|
|
|
|
DBGPRINT(2, ("USBSTOR_IssueInternalCdb %d %08X %08X\n",
|
|
retryCount, ntStatus, srb->SrbStatus));
|
|
|
|
if ((SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS) ||
|
|
(SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN))
|
|
{
|
|
ntStatus = STATUS_SUCCESS;
|
|
*DataTransferLength = srb->DataTransferLength;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
// Free the Irp. A new one will be allocated the next time around.
|
|
//
|
|
IoFreeIrp(irp);
|
|
irp = NULL;
|
|
}
|
|
|
|
USBSTOR_GetInquiryData_Exit:
|
|
|
|
if (mdl != NULL)
|
|
{
|
|
IoFreeMdl(mdl);
|
|
}
|
|
|
|
if (senseInfoBuffer != NULL)
|
|
{
|
|
ExFreePool(senseInfoBuffer);
|
|
}
|
|
|
|
if (srb != NULL)
|
|
{
|
|
ExFreePool(srb);
|
|
}
|
|
|
|
if (irp != NULL)
|
|
{
|
|
IoFreeIrp(irp);
|
|
}
|
|
|
|
DBGPRINT(2, ("exit: USBSTOR_IssueInternalCdb %08X\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_GetInquiryData()
|
|
//
|
|
//******************************************************************************
|
|
|
|
NTSTATUS
|
|
USBSTOR_GetInquiryData (
|
|
PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
PPDO_DEVICE_EXTENSION pdoDeviceExtension;
|
|
PDEVICE_OBJECT fdoDeviceObject;
|
|
PFDO_DEVICE_EXTENSION fdoDeviceExtension;
|
|
PVOID dataBuffer;
|
|
ULONG dataTransferLength;
|
|
CDB cdb;
|
|
NTSTATUS ntStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
DBGPRINT(2, ("enter: USBSTOR_GetInquiryData\n"));
|
|
|
|
pdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(pdoDeviceExtension->Type == USBSTOR_DO_TYPE_PDO);
|
|
|
|
fdoDeviceObject = pdoDeviceExtension->ParentFDO;
|
|
fdoDeviceExtension = fdoDeviceObject->DeviceExtension;
|
|
ASSERT(fdoDeviceExtension->Type == USBSTOR_DO_TYPE_FDO);
|
|
|
|
dataBuffer = pdoDeviceExtension->InquiryDataBuffer;
|
|
dataTransferLength = sizeof(pdoDeviceExtension->InquiryDataBuffer);
|
|
|
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|
|
|
cdb.CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY;
|
|
cdb.CDB6INQUIRY.AllocationLength = (UCHAR)dataTransferLength;
|
|
|
|
ntStatus = USBSTOR_IssueInternalCdb(DeviceObject,
|
|
dataBuffer,
|
|
&dataTransferLength,
|
|
&cdb,
|
|
sizeof(cdb.CDB6INQUIRY),
|
|
20);
|
|
|
|
if (NT_SUCCESS(ntStatus) &&
|
|
fdoDeviceExtension->DriverFlags == DeviceProtocolUnspecified)
|
|
{
|
|
// The Inquiry request is the first request we send to the device. If
|
|
// the first request was successful and the protocol was not specified,
|
|
// set it to the default protocol, which is DeviceProtocolCB.
|
|
//
|
|
DBGPRINT(1, ("Setting Unspecified device to CB\n"));
|
|
|
|
fdoDeviceExtension->DriverFlags = DeviceProtocolCB;
|
|
}
|
|
|
|
DBGPRINT(2, ("exit: USBSTOR_GetInquiryData %08X\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// USBSTOR_IsFloppyDevice()
|
|
//
|
|
// This routine issues a SCSIOP_READ_FORMATTED_CAPACITY request and looks
|
|
// at the returned Format Capacity Descriptor list to see if the device
|
|
// supports any of the known floppy capacities. If the device does support
|
|
// a known floppy capacity, it is assumed that the device is a floppy.
|
|
//
|
|
//******************************************************************************
|
|
|
|
typedef struct _FORMATTED_CAPACITY
|
|
{
|
|
ULONG NumberOfBlocks;
|
|
ULONG BlockLength;
|
|
} FORMATTED_CAPACITY, *PFORMATTED_CAPACITY;
|
|
|
|
FORMATTED_CAPACITY FloppyCapacities[] =
|
|
{
|
|
// Blocks BlockLen H T B/S S/T
|
|
{0x00000500, 0x000200}, // 2 80 512 8 640 KB F5_640_512
|
|
{0x000005A0, 0x000200}, // 2 80 512 9 720 KB F3_720_512
|
|
{0x00000960, 0x000200}, // 2 80 512 15 1.20 MB F3_1Pt2_512 (Toshiba)
|
|
{0x000004D0, 0x000400}, // 2 77 1024 8 1.23 MB F3_1Pt23_1024 (NEC)
|
|
{0x00000B40, 0x000200}, // 2 80 512 18 1.44 MB F3_1Pt44_512
|
|
{0x0003C300, 0x000200}, // 8 963 512 32 120 MB F3_120M_512
|
|
{0x000600A4, 0x000200} //13 890 512 34 200 MB HiFD
|
|
};
|
|
|
|
#define FLOPPY_CAPACITIES (sizeof(FloppyCapacities)/sizeof(FloppyCapacities[0]))
|
|
|
|
BOOLEAN
|
|
USBSTOR_IsFloppyDevice (
|
|
PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
PPDO_DEVICE_EXTENSION pdoDeviceExtension;
|
|
BOOLEAN isFloppy;
|
|
struct _READ_FORMATTED_CAPACITIES cdb;
|
|
PFORMATTED_CAPACITY_LIST capList;
|
|
PVOID dataBuffer;
|
|
ULONG dataTransferLength;
|
|
NTSTATUS ntStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
DBGPRINT(1, ("enter: USBSTOR_IsFloppyDevice\n"));
|
|
|
|
pdoDeviceExtension = DeviceObject->DeviceExtension;
|
|
ASSERT(pdoDeviceExtension->Type == USBSTOR_DO_TYPE_PDO);
|
|
|
|
isFloppy = FALSE;
|
|
|
|
// Allocate a transfer buffer for the SCSIOP_READ_FORMATTED_CAPACITY request
|
|
// The length of the returned descriptor array is limited to a byte field
|
|
// in the capacity list header.
|
|
//
|
|
dataTransferLength = sizeof(FORMATTED_CAPACITY_LIST) +
|
|
31 * sizeof(FORMATTED_CAPACITY_DESCRIPTOR);
|
|
|
|
ASSERT(dataTransferLength < 0x100);
|
|
|
|
dataBuffer = ExAllocatePoolWithTag(NonPagedPool,
|
|
dataTransferLength,
|
|
POOL_TAG);
|
|
|
|
if (dataBuffer)
|
|
{
|
|
RtlZeroMemory(dataBuffer, dataTransferLength);
|
|
|
|
RtlZeroMemory(&cdb, sizeof(cdb));
|
|
|
|
cdb.OperationCode = SCSIOP_READ_FORMATTED_CAPACITY;
|
|
cdb.AllocationLength[1] = (UCHAR)dataTransferLength;
|
|
|
|
capList = (PFORMATTED_CAPACITY_LIST)dataBuffer;
|
|
|
|
ntStatus = USBSTOR_IssueInternalCdb(DeviceObject,
|
|
dataBuffer,
|
|
&dataTransferLength,
|
|
(PCDB)&cdb,
|
|
sizeof(cdb),
|
|
20);
|
|
|
|
DBGPRINT(1, ("%08X %08X %02X\n",
|
|
ntStatus, dataTransferLength, capList->CapacityListLength));
|
|
|
|
if (NT_SUCCESS(ntStatus) &&
|
|
dataTransferLength >= sizeof(FORMATTED_CAPACITY_LIST) &&
|
|
capList->CapacityListLength &&
|
|
capList->CapacityListLength % sizeof(FORMATTED_CAPACITY_DESCRIPTOR) == 0)
|
|
{
|
|
ULONG NumberOfBlocks;
|
|
ULONG BlockLength;
|
|
ULONG i, j, count;
|
|
|
|
// Subtract the size of the Capacity List Header to get
|
|
// just the size of the Capacity List Descriptor array.
|
|
//
|
|
dataTransferLength -= sizeof(FORMATTED_CAPACITY_LIST);
|
|
|
|
// Only look at the Capacity List Descriptors that were
|
|
// actually returned.
|
|
//
|
|
if (dataTransferLength < capList->CapacityListLength)
|
|
{
|
|
count = dataTransferLength /
|
|
sizeof(FORMATTED_CAPACITY_DESCRIPTOR);
|
|
}
|
|
else
|
|
{
|
|
count = capList->CapacityListLength /
|
|
sizeof(FORMATTED_CAPACITY_DESCRIPTOR);
|
|
}
|
|
|
|
for (i=0; i<count; i++)
|
|
{
|
|
NumberOfBlocks = (capList->Descriptors[i].NumberOfBlocks[0] << 24) +
|
|
(capList->Descriptors[i].NumberOfBlocks[1] << 16) +
|
|
(capList->Descriptors[i].NumberOfBlocks[2] << 8) +
|
|
capList->Descriptors[i].NumberOfBlocks[3];
|
|
|
|
BlockLength = (capList->Descriptors[i].BlockLength[0] << 16) +
|
|
(capList->Descriptors[i].BlockLength[1] << 8) +
|
|
capList->Descriptors[i].BlockLength[2];
|
|
|
|
DBGPRINT(1, ("Capacity[%d] %08X %06X %d%d\n",
|
|
i,
|
|
NumberOfBlocks,
|
|
BlockLength,
|
|
capList->Descriptors[i].Valid,
|
|
capList->Descriptors[i].Maximum));
|
|
|
|
for (j=0; j<FLOPPY_CAPACITIES; j++)
|
|
{
|
|
if (NumberOfBlocks == FloppyCapacities[j].NumberOfBlocks &&
|
|
BlockLength == FloppyCapacities[j].BlockLength)
|
|
{
|
|
isFloppy = TRUE;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
ExFreePool(dataBuffer);
|
|
}
|
|
|
|
DBGPRINT(1, ("exit: USBSTOR_IsFloppyDevice %d\n", isFloppy));
|
|
|
|
return isFloppy;
|
|
}
|