/*++ 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 #include #include #include #include #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; iDescriptors[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