1277 lines
39 KiB
C++
1277 lines
39 KiB
C++
/*++
|
|
|
|
Copyright (c) 1996-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
mrb.cpp
|
|
|
|
Abstract:
|
|
|
|
This source file contains the state machine for
|
|
MU requests blocks to the device. The bulk-only protocol
|
|
specifices three stages for every transfer.
|
|
|
|
The only entry point to the MRB state machine is
|
|
|
|
MU_fStartMrb
|
|
|
|
Environment:
|
|
|
|
kernel mode
|
|
|
|
Revision History:
|
|
|
|
06-12-2000 : started rewrite : georgioc
|
|
10-20-2000 : major cleanup (separated out the MRB state machine,
|
|
from the partial write state machine, which is now
|
|
in disk.cpp : mitchd
|
|
03-07-2001 : generalized the way write commands are handled to aid
|
|
the partial write state machine (in disk.cpp) support
|
|
more flexible block sizes : mitchd
|
|
|
|
--*/
|
|
|
|
|
|
/*++
|
|
Description of MRB state machine changes started on 03/07/01.
|
|
|
|
Even after the cleanup on 10-20-2000, there was still a special case in
|
|
the state machine to transfer the data stage of an MRB from two buffers:
|
|
the user's buffer, and the MU WriteBuffer used to maintain media block
|
|
integrity. This code peered into disk.cpp's flags to figure out when this
|
|
was necessary, somewhat of a hack. The problem with this code is that it
|
|
only supports a media block size that is double the emulated sector size.
|
|
We now need to lift this restriction in order to support larger media.
|
|
|
|
So this change formalizes the hack by adding supporting a new MRB flag,
|
|
MRB_FLAGS_SPLIT_WRITE, and to new fields filled out by disk.cpp: UserStartOffset,
|
|
and UserEndOffset. When MRB_FLAGS_SPLIT_WRITE the transfer length, must be
|
|
exactly one media block. The write is performed as follows:
|
|
|
|
1) Write UserStartOffset bytes from the beginning of the WriteBuffer.
|
|
2) Write (UserEndOffset-UserStartOffset) bytes from the beginning of the
|
|
user buffer.
|
|
3) Write (MediaBlockSize-UserEndOffset) bytes from the WriteBuffer begining
|
|
UserStartOffset bytes into the WriteBuffer.
|
|
|
|
This process allows a write to be submitted from the WriteBuffer inserting an
|
|
arbitrary number of bytes from the user buffer at any point. In the process
|
|
the MRB state machine becomes more independent of the disk.cpp layer. The
|
|
new code should be ever scalable to larger and larger media block sizes.
|
|
|
|
--*/
|
|
|
|
//*****************************************************************************
|
|
// I N C L U D E S
|
|
//*****************************************************************************
|
|
#include "mu.h"
|
|
|
|
//*****************************************************************************
|
|
// Local Functions
|
|
//*****************************************************************************
|
|
DEFINE_USB_DEBUG_FUNCTIONS("MU");
|
|
VOID
|
|
FASTCALL
|
|
MU_fCbwTransfer(
|
|
IN PMU_DEVICE_EXTENSION DeviceExtension
|
|
);
|
|
|
|
VOID
|
|
MU_CbwCompletion (
|
|
IN PURB Urb,
|
|
IN PVOID Context
|
|
);
|
|
|
|
VOID
|
|
FASTCALL
|
|
MU_fDataTransfer (
|
|
PURB Urb,
|
|
PMU_DEVICE_EXTENSION DeviceExtension
|
|
);
|
|
|
|
VOID
|
|
MU_DataCompletion (
|
|
IN PURB Urb,
|
|
IN PVOID Context
|
|
);
|
|
|
|
VOID
|
|
FASTCALL
|
|
MU_fCswTransfer (
|
|
IN PMU_DEVICE_EXTENSION DeviceExtension
|
|
);
|
|
|
|
VOID
|
|
MU_CswCompletion (
|
|
IN PURB Urb,
|
|
IN PVOID Context
|
|
);
|
|
|
|
//
|
|
// MU_MrbTimeout is local, but declared also in mu.h, because
|
|
// mu.cpp sets up the DPC at init time.
|
|
//
|
|
|
|
VOID
|
|
MU_MrbTimeout (
|
|
IN PKDPC Dpc,
|
|
IN PVOID Context,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
);
|
|
|
|
VOID
|
|
FASTCALL
|
|
MU_fMrbErrorHandler(
|
|
IN PMU_DEVICE_EXTENSION DeviceExtension,
|
|
IN NTSTATUS Status
|
|
);
|
|
|
|
|
|
VOID
|
|
MU_ResetDeviceCompletionRoutine (
|
|
IN PURB Urb,
|
|
IN PVOID Context
|
|
);
|
|
|
|
#if DBG
|
|
VOID
|
|
FASTCALL
|
|
MU_fValidateMrb(
|
|
IN PMU_DEVICE_EXTENSION DeviceExtension
|
|
);
|
|
#define MU_DBG_VALIDATE_MRB(DeviceExtension) MU_fValidateMrb(DeviceExtension)
|
|
#else
|
|
#define MU_DBG_VALIDATE_MRB(DeviceExtension)
|
|
#endif
|
|
|
|
//*****************************************************************************
|
|
// Implementation
|
|
//*****************************************************************************
|
|
|
|
VOID
|
|
FASTCALL
|
|
MU_fStartMrb(
|
|
IN PMU_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Entry point for the MRB state machine.
|
|
|
|
Does some sanity checks in debug, makes
|
|
sure the device is still connected,
|
|
and calls MU_fCbwTransfer - the first
|
|
phase of any request.
|
|
|
|
--*/
|
|
{
|
|
KIRQL oldIrql;
|
|
|
|
USB_DBG_ENTRY_PRINT(("MU_fStartMrb(0x%0.8x)", DeviceExtension));
|
|
|
|
//
|
|
// In Debug perform some simple tests
|
|
// to make sure that the MRB is well formed.
|
|
//
|
|
|
|
MU_DBG_VALIDATE_MRB(DeviceExtension);
|
|
|
|
|
|
|
|
//
|
|
// Raise Irql to synchronize with removal
|
|
//
|
|
|
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
|
|
|
//
|
|
// Make sure that the device is still there.
|
|
//
|
|
|
|
if(TEST_FLAG(DeviceExtension->DeviceFlags, DF_PENDING_REMOVE|DF_REMOVED))
|
|
{
|
|
//
|
|
// Call the callback with a failure.
|
|
//
|
|
USB_DBG_TRACE_PRINT(("Mrb.CompletionRoutine(0x%0.8x, STATUS_DEVICE_NOT_CONNECTED)", DeviceExtension));
|
|
DeviceExtension->Mrb.CompletionRoutine(DeviceExtension, STATUS_DEVICE_NOT_CONNECTED);
|
|
} else
|
|
{
|
|
//
|
|
// Send the CBW
|
|
//
|
|
|
|
ASSERT(!TEST_FLAG(DeviceExtension->DeviceFlags, DF_ANY_URB_PENDING|DF_RESET_STEPS));
|
|
MU_fCbwTransfer(DeviceExtension);
|
|
}
|
|
KeLowerIrql(oldIrql);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
MU_fCbwTransfer(
|
|
IN PMU_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Builds an URB for the CBW phase and send it.
|
|
--*/
|
|
{
|
|
|
|
PMU_REQUEST_BLOCK mrb;
|
|
PCBW cbw;
|
|
LARGE_INTEGER deltaTime;
|
|
|
|
USB_DBG_ENTRY_PRINT(("MU_CbwTransfer(DeviceExtension=0x%0.8x)", DeviceExtension));
|
|
|
|
//
|
|
// Get the mrb
|
|
//
|
|
|
|
mrb = &DeviceExtension->Mrb;
|
|
|
|
//
|
|
// Initialize the Command Block Wrapper
|
|
//
|
|
|
|
cbw = &DeviceExtension->Mrb.Cbw;
|
|
cbw->dCBWSignature = CBW_SIGNATURE;
|
|
cbw->dCBWTag = PtrToUlong(DeviceExtension->PendingIrp);
|
|
cbw->dCBWDataTransferLength = mrb->TransferLength;
|
|
cbw->bCBWFlags = (UCHAR)((mrb->Flags & MRB_FLAGS_DATA_IN) ? CBW_FLAGS_DATA_IN : CBW_FLAGS_DATA_OUT);
|
|
cbw->bCBWLUN = 0; // Xbox supports only signle LUN
|
|
cbw->bCDBLength = 10; //Xbox supports on 10 byte cdb's
|
|
|
|
//
|
|
// The caller filled out the CDB in the CBW
|
|
//
|
|
|
|
//
|
|
// start the request timer
|
|
//
|
|
SET_FLAG(DeviceExtension->DeviceFlags, DF_MRB_TIMER_RUNNING);
|
|
deltaTime.QuadPart = MRB_STANDARD_TIMEOUT * MRB_TIMEOUT_UNIT;
|
|
KeSetTimer(&DeviceExtension->Mrb.Timer, deltaTime, &DeviceExtension->Mrb.TimeoutDpcObject);
|
|
|
|
|
|
//
|
|
// Build and submit an URB for the CBW
|
|
//
|
|
SET_FLAG(DeviceExtension->DeviceFlags, DF_PRIMARY_URB_PENDING);
|
|
USB_BUILD_BULK_OR_INTERRUPT_TRANSFER(
|
|
&DeviceExtension->Urb.BulkOrInterruptTransfer,
|
|
DeviceExtension->BulkOutEndpointHandle,
|
|
cbw,
|
|
sizeof(CBW),
|
|
USB_TRANSFER_DIRECTION_OUT,
|
|
MU_CbwCompletion,
|
|
DeviceExtension,
|
|
FALSE //short transfer is an error
|
|
);
|
|
|
|
//
|
|
// Don't worry about errors the completion
|
|
// routine will handle them.
|
|
//
|
|
ASSERT(DeviceExtension->MuInstance);
|
|
DeviceExtension->MuInstance->Device->SubmitRequest(&DeviceExtension->Urb);
|
|
|
|
USB_DBG_EXIT_PRINT(("MU_CbwTransfer returning"));
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
MU_CbwCompletion (
|
|
IN PURB Urb,
|
|
IN PVOID Context
|
|
)
|
|
/*
|
|
Routine Description:
|
|
Completion routine for MU_fStartMrb.
|
|
|
|
If any type of error occured (including a timeout) call MU_HandleBulkError()
|
|
to handle it, and exit.
|
|
|
|
On success, if a data phase is required start it, otherwise call MU_fCswTransfer.
|
|
*/
|
|
{
|
|
PMU_DEVICE_EXTENSION deviceExtension;
|
|
PMU_REQUEST_BLOCK mrb;
|
|
|
|
USB_DBG_ENTRY_PRINT(("MU_CbwCompletion(Urb=0x%0.8x,Context=0x%0.8x)", Urb, Context));
|
|
|
|
deviceExtension = (PMU_DEVICE_EXTENSION) Context;
|
|
ASSERT(Urb == &deviceExtension->Urb);
|
|
|
|
//
|
|
// If the URB failed (which includes being cancelled by the timer DPC)
|
|
// call the error handling code.
|
|
//
|
|
|
|
if(USBD_ERROR(Urb->Header.Status) ||
|
|
TEST_FLAG(deviceExtension->DeviceFlags, DF_REMOVED|DF_PENDING_REMOVE)
|
|
)
|
|
{
|
|
USB_DBG_WARN_PRINT(("CBW transfer failed, usbdStatus=0x%0.8x", Urb->Header.Status));
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags, DF_PRIMARY_URB_PENDING);
|
|
MU_fMrbErrorHandler(deviceExtension, IUsbDevice::NtStatusFromUsbdStatus(Urb->Header.Status));
|
|
return;
|
|
}
|
|
|
|
mrb = &deviceExtension->Mrb;
|
|
|
|
// 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(mrb->DataBuffer)
|
|
{
|
|
|
|
ASSERT(mrb->TransferLength != 0);
|
|
mrb->BytesSubmitted = 0;
|
|
|
|
//
|
|
// Start the first URB
|
|
//
|
|
|
|
ASSERT(TEST_FLAG(deviceExtension->DeviceFlags, DF_PRIMARY_URB_PENDING));
|
|
MU_fDataTransfer(&deviceExtension->Urb, deviceExtension);
|
|
|
|
//
|
|
// If there is more data, start up with the second URB
|
|
//
|
|
|
|
if(mrb->BytesSubmitted < mrb->TransferLength)
|
|
{
|
|
SET_FLAG(deviceExtension->DeviceFlags, DF_SECONDARY_URB_PENDING);
|
|
MU_fDataTransfer((PURB)&deviceExtension->BulkUrbSecondary, deviceExtension);
|
|
}
|
|
|
|
} else
|
|
{
|
|
ASSERT(0 == mrb->TransferLength);
|
|
MU_fCswTransfer(deviceExtension);
|
|
}
|
|
|
|
USB_DBG_EXIT_PRINT(("MU_CbwCompletion returning"));
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
MU_fDataTransfer (
|
|
PURB Urb,
|
|
PMU_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*
|
|
Routine Description:
|
|
Submit URB for the data stage of the MRB
|
|
stage machine. It uses the URB passed to it.
|
|
|
|
Since USB guarantees that multiple requests
|
|
on the same endpoint will complete in sequence
|
|
we can just keep alternating them until the
|
|
whole transfer buffer is sent.
|
|
*/
|
|
{
|
|
PMU_REQUEST_BLOCK mrb;
|
|
PVOID endpointHandle;
|
|
PVOID transferBuffer;
|
|
ULONG transferLength;
|
|
UCHAR direction;
|
|
|
|
USB_DBG_ENTRY_PRINT(("MU_DataTransfer(Urb=0x%0.8x,DeviceExtension=0x%0.8x)", Urb, DeviceExtension));
|
|
|
|
|
|
mrb = &DeviceExtension->Mrb;
|
|
|
|
//
|
|
// Bulk IN or Bulk OUT?
|
|
//
|
|
|
|
if ((mrb->Flags & MRB_FLAGS_UNSPECIFIED_DIRECTION) == MRB_FLAGS_DATA_IN)
|
|
{
|
|
endpointHandle = DeviceExtension->BulkInEndpointHandle;
|
|
direction = USB_TRANSFER_DIRECTION_IN;
|
|
} else {
|
|
ASSERT((mrb->Flags & MRB_FLAGS_UNSPECIFIED_DIRECTION) == MRB_FLAGS_DATA_OUT);
|
|
endpointHandle = DeviceExtension->BulkOutEndpointHandle;
|
|
direction = USB_TRANSFER_DIRECTION_OUT;
|
|
}
|
|
|
|
//**
|
|
//** Calculate the transfer buffer for this URB
|
|
//**
|
|
if(TEST_FLAG(mrb->Flags,MRB_FLAGS_SPLIT_WRITE))
|
|
//
|
|
// MRB_FLAGS_SPLIT_WRITE has three stages, handle
|
|
// the correct one.
|
|
{
|
|
ASSERT(USB_TRANSFER_DIRECTION_OUT == direction);
|
|
if (mrb->BytesSubmitted < mrb->UserStartOffset)
|
|
//
|
|
// First stage - from beginning of WriteBuffer
|
|
//
|
|
{
|
|
transferBuffer = MU_DriverExtension.WriteBuffer + mrb->BytesSubmitted;
|
|
|
|
} else if(mrb->BytesSubmitted < mrb->UserEndOffset)
|
|
//
|
|
// Second stage - from User's buffer
|
|
//
|
|
{
|
|
transferBuffer = mrb->DataBuffer + (mrb->BytesSubmitted - mrb->UserStartOffset);
|
|
|
|
} else
|
|
//
|
|
// Third stage - from end of WriteBuffer
|
|
//
|
|
{
|
|
transferBuffer = MU_DriverExtension.WriteBuffer + mrb->UserStartOffset + (mrb->BytesSubmitted - mrb->UserEndOffset);
|
|
}
|
|
} else
|
|
//
|
|
// The MRB_FLAGS_SPLIT_WRITE is NOT set, so just do the simple thing
|
|
// from the user buffer.
|
|
{
|
|
transferBuffer = mrb->DataBuffer + mrb->BytesSubmitted;
|
|
}
|
|
|
|
//
|
|
// Calculate the transfer length
|
|
//
|
|
|
|
transferLength = mrb->TransferLength - mrb->BytesSubmitted;
|
|
if(transferLength > MRB_TRANSFER_SIZE)
|
|
{
|
|
transferLength = MRB_TRANSFER_SIZE;
|
|
}
|
|
mrb->BytesSubmitted += transferLength;
|
|
|
|
//
|
|
// Build and submit an URB for the Transfer
|
|
//
|
|
|
|
USB_BUILD_BULK_OR_INTERRUPT_TRANSFER(
|
|
&Urb->BulkOrInterruptTransfer,
|
|
endpointHandle,
|
|
transferBuffer,
|
|
transferLength,
|
|
direction,
|
|
MU_DataCompletion,
|
|
DeviceExtension,
|
|
FALSE //short transfer is an error
|
|
);
|
|
|
|
//
|
|
// Extend the timeout.
|
|
//
|
|
LARGE_INTEGER deltaTime;
|
|
deltaTime.QuadPart = MRB_DATA_TIMEOUT * MRB_TIMEOUT_UNIT;
|
|
KeSetTimer(&DeviceExtension->Mrb.Timer, deltaTime, &DeviceExtension->Mrb.TimeoutDpcObject);
|
|
|
|
//
|
|
// Don't worry about errors the completion
|
|
// routine will handle them.
|
|
//
|
|
ASSERT(DeviceExtension->MuInstance);
|
|
DeviceExtension->MuInstance->Device->SubmitRequest(Urb);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
MU_DataCompletion (
|
|
IN PURB Urb,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This is the completion routine for MU_fDataTransfer.
|
|
|
|
On an error (from USB), it calls MU_HandleBulkErrors.
|
|
Then it returns.
|
|
|
|
If there is more data to submit it loops back to
|
|
MU_fDataTransfer to keep the data moving.
|
|
|
|
If there is no more data to submit, it checks to
|
|
see if there is an other URB outstanding. If there
|
|
are no more URBs outstanding, then it moves on to the
|
|
next stage by calling MU_fTransferCsw.
|
|
--*/
|
|
{
|
|
PMU_DEVICE_EXTENSION deviceExtension = (PMU_DEVICE_EXTENSION) Context;
|
|
PMU_REQUEST_BLOCK mrb;
|
|
|
|
mrb = &deviceExtension->Mrb;
|
|
|
|
//
|
|
// Check to see if there is an error pending (from the other URB)
|
|
//
|
|
|
|
if(TEST_FLAG(deviceExtension->DeviceFlags,DF_ERROR_PENDING))
|
|
{
|
|
USBD_STATUS usbdStatus;
|
|
|
|
|
|
//
|
|
// DF_ERROR_PENDING was set only so that we get here,
|
|
// clear it now.
|
|
//
|
|
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags,DF_ERROR_PENDING);
|
|
|
|
// Grab the status from the OTHER Urb
|
|
if(Urb == &deviceExtension->Urb)
|
|
{
|
|
usbdStatus = deviceExtension->BulkUrbSecondary.Hdr.Status;
|
|
} else
|
|
{
|
|
usbdStatus = deviceExtension->Urb.Header.Status;
|
|
}
|
|
//
|
|
// There is definately no URB pending now.
|
|
// (it is more efficient to clear both flags, than to
|
|
// figure out which one we need to clear).
|
|
//
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags, DF_PRIMARY_URB_PENDING|DF_SECONDARY_URB_PENDING);
|
|
MU_fMrbErrorHandler(deviceExtension, IUsbDevice::NtStatusFromUsbdStatus(usbdStatus));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If this URB failed, cancel the other URB, if it is outstanding,
|
|
// otherwise, start handling the error here.
|
|
//
|
|
|
|
if(USBD_ERROR(Urb->Header.Status) ||
|
|
TEST_FLAG(deviceExtension->DeviceFlags, DF_REMOVED|DF_PENDING_REMOVE)
|
|
)
|
|
{
|
|
USB_DBG_WARN_PRINT(("Data transfer failed (USB status)0x%0.8x", Urb->Header.Status));
|
|
|
|
//
|
|
// On error, we figure out which URB we are processing and
|
|
// clear its pending flag. If the URB is pending we need
|
|
// to cancel it, and return.
|
|
//
|
|
if(Urb == &deviceExtension->Urb)
|
|
{
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags,DF_PRIMARY_URB_PENDING);
|
|
if(TEST_FLAG(deviceExtension->DeviceFlags,DF_SECONDARY_URB_PENDING))
|
|
{
|
|
ASSERT(deviceExtension->MuInstance);
|
|
SET_FLAG(deviceExtension->DeviceFlags,DF_ERROR_PENDING);
|
|
deviceExtension->MuInstance->Device->CancelRequest((PURB)&deviceExtension->BulkUrbSecondary);
|
|
return;
|
|
}
|
|
} else
|
|
{
|
|
ASSERT(Urb == (PURB)&deviceExtension->BulkUrbSecondary);
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags,DF_SECONDARY_URB_PENDING);
|
|
if(TEST_FLAG(deviceExtension->DeviceFlags,DF_PRIMARY_URB_PENDING))
|
|
{
|
|
ASSERT(deviceExtension->MuInstance);
|
|
SET_FLAG(deviceExtension->DeviceFlags,DF_ERROR_PENDING);
|
|
deviceExtension->MuInstance->Device->CancelRequest(&deviceExtension->Urb);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we are here there are no outstanding URBs for this device, so
|
|
// we can start the error handling.
|
|
//
|
|
|
|
MU_fMrbErrorHandler(deviceExtension, IUsbDevice::NtStatusFromUsbdStatus(Urb->Header.Status));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Ugh, that was a lot of error handling code,
|
|
// but we want to be robust!
|
|
//
|
|
|
|
//
|
|
// If there is more data to submit
|
|
// then call MU_fDataTransfer,
|
|
// and return.
|
|
//
|
|
if(mrb->BytesSubmitted < mrb->TransferLength)
|
|
{
|
|
MU_fDataTransfer(Urb, deviceExtension);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We could just move on to the CSW stage now,
|
|
// even if the other URB is still pending for
|
|
// a read or write. This is acceptable, because
|
|
// of 3.3 of the Bulk-Only specification. However,
|
|
// we do not lose measurable efficiency by only
|
|
// continuing if there are no more data URB
|
|
// outstanding. This method reduces the number
|
|
// of possible error cases we need to check,
|
|
// and there are already too many!
|
|
//
|
|
|
|
if(Urb == &deviceExtension->Urb)
|
|
{
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags,DF_PRIMARY_URB_PENDING);
|
|
if(TEST_FLAG(deviceExtension->DeviceFlags,DF_SECONDARY_URB_PENDING))
|
|
{
|
|
// Nothing to do, because our twin URB is still pending
|
|
return;
|
|
}
|
|
} else
|
|
{
|
|
ASSERT(Urb == (PURB)&deviceExtension->BulkUrbSecondary);
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags,DF_SECONDARY_URB_PENDING);
|
|
if(TEST_FLAG(deviceExtension->DeviceFlags,DF_PRIMARY_URB_PENDING))
|
|
{
|
|
// Nothing to do, because our twin URB is still pending
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reset the primary URB flag as MU_fCswTransfer will use it.
|
|
//
|
|
|
|
SET_FLAG(deviceExtension->DeviceFlags,DF_PRIMARY_URB_PENDING);
|
|
|
|
//
|
|
// The data transfer is over, go on to CSW stage
|
|
//
|
|
MU_fCswTransfer(deviceExtension);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
MU_fCswTransfer (
|
|
IN PMU_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Builds and submits an URB for the CswTransfer. It is broken
|
|
out as a separate routineas it is called from two places
|
|
(MU_DataCompletion and MU_CbwCompletion).
|
|
--*/
|
|
{
|
|
USB_DBG_ENTRY_PRINT(("MU_DataTransfer(DeviceExtension=0x%0.8x)",DeviceExtension));
|
|
|
|
USB_BUILD_BULK_OR_INTERRUPT_TRANSFER(
|
|
&DeviceExtension->Urb.BulkOrInterruptTransfer,
|
|
DeviceExtension->BulkInEndpointHandle,
|
|
&DeviceExtension->Mrb.Csw,
|
|
sizeof(CSW),
|
|
USB_TRANSFER_DIRECTION_IN,
|
|
MU_CswCompletion,
|
|
DeviceExtension,
|
|
FALSE //short transfer is an error
|
|
);
|
|
|
|
//
|
|
// Extend the timeout for the CSW.
|
|
//
|
|
LARGE_INTEGER deltaTime;
|
|
deltaTime.QuadPart = ((LONGLONG)DeviceExtension->Mrb.TimeOutValue) * MRB_TIMEOUT_UNIT;
|
|
KeSetTimer(&DeviceExtension->Mrb.Timer, deltaTime, &DeviceExtension->Mrb.TimeoutDpcObject);
|
|
|
|
//
|
|
// Submit the URB, the completion routine will worry about errors.
|
|
//
|
|
ASSERT(DeviceExtension->MuInstance);
|
|
DeviceExtension->MuInstance->Device->SubmitRequest(&DeviceExtension->Urb);
|
|
|
|
USB_DBG_EXIT_PRINT(("MU_CswTransfer returning"));
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
MU_CswCompletion (
|
|
IN PURB Urb,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Completion routine for the CSW transfer.
|
|
|
|
Checks for a number of possible error conditions.
|
|
If any are found it calls MU_HandleBulkError.
|
|
|
|
Otherwise calls the MRBs completion routine
|
|
with STATUS_SUCCESS.
|
|
--*/
|
|
{
|
|
PMU_DEVICE_EXTENSION deviceExtension = (PMU_DEVICE_EXTENSION)Context;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
USB_DBG_ENTRY_PRINT(("MU_CswCompletion(Urb=0x%0.8x,Context=0x%0.8x)", Urb, Context));
|
|
|
|
//
|
|
// Cancel the timer
|
|
//
|
|
|
|
if(TEST_FLAG(deviceExtension->DeviceFlags, DF_MRB_TIMER_RUNNING))
|
|
{
|
|
KeCancelTimer(&deviceExtension->Mrb.Timer);
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags, DF_MRB_TIMER_RUNNING);
|
|
}
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags,DF_PRIMARY_URB_PENDING);
|
|
|
|
if(USBD_ERROR(Urb->Header.Status))
|
|
{
|
|
// The Data Bulk Transfer was not successful. Look at how the
|
|
// the transfer failed to figure out how to recover.
|
|
//
|
|
|
|
USB_DBG_WARN_PRINT(("CSW transfer failed, usbdStatus=0x%0.8x", Urb->Header.Status));
|
|
status = IUsbDevice::NtStatusFromUsbdStatus(Urb->Header.Status);
|
|
}
|
|
|
|
//
|
|
// Check Signature.
|
|
//
|
|
|
|
if(deviceExtension->Mrb.Csw.dCSWSignature != CSW_SIGNATURE)
|
|
{
|
|
USB_DBG_WARN_PRINT(("CSW signature incorrect."));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Check Tag
|
|
//
|
|
|
|
if(deviceExtension->Mrb.Csw.dCSWTag != deviceExtension->Mrb.Cbw.dCBWTag)
|
|
{
|
|
USB_DBG_WARN_PRINT(("CSW tag incorrect, expecting 0x%0.8x, was 0x%0.8x.",
|
|
deviceExtension->Mrb.Cbw.dCBWTag,
|
|
deviceExtension->Mrb.Csw.dCSWTag
|
|
));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Check For Phase Error (means missed handshake in the protocol)
|
|
//
|
|
|
|
if(deviceExtension->Mrb.Csw.bCSWStatus == CSW_STATUS_PHASE_ERROR)
|
|
{
|
|
USB_DBG_WARN_PRINT(("CSW returned CSW_STATUS_PHASE_ERROR\n"));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Check for failed CSW. Basically the device had trouble
|
|
// completing the command. It could be that we sent an unsupported
|
|
// command or that we tried to write past the end of the media
|
|
// or some other error on our part. OR, THE MEDIA IS BAD.
|
|
// On a read or write, we ASSUME that this driver did not screw up
|
|
// and infact a media block has failed!!! It sucks that we don't
|
|
// have more specific errors, but we didn't invent the protocol,
|
|
// we just borrowed it from the USB mass storage committee.
|
|
//
|
|
|
|
if(deviceExtension->Mrb.Csw.bCSWStatus == CSW_STATUS_FAILED)
|
|
{
|
|
USB_DBG_WARN_PRINT(("CSW returned CSW_STATUS_FAILED\n"));
|
|
status = STATUS_DATA_ERROR;
|
|
}
|
|
|
|
ASSERT(!TEST_FLAG(deviceExtension->DeviceFlags, DF_ANY_URB_PENDING));
|
|
|
|
//
|
|
// If any error occurred, call the handle bulk error routine.
|
|
//
|
|
|
|
if(NT_ERROR(status))
|
|
{
|
|
MU_fMrbErrorHandler(deviceExtension, status);
|
|
return;
|
|
}
|
|
|
|
ASSERT(deviceExtension->Mrb.Csw.bCSWStatus == CSW_STATUS_GOOD);
|
|
|
|
//
|
|
// Call the MRB completion routine with a success code
|
|
//
|
|
USB_DBG_TRACE_PRINT(("Mrb.CompletionRoutine(0x%0.8x, STATUS_SUCCESS)", deviceExtension));
|
|
deviceExtension->Mrb.CompletionRoutine(deviceExtension, STATUS_SUCCESS);
|
|
|
|
USB_DBG_EXIT_PRINT(("MU_CswCompletion returning"));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
MU_MrbTimeout (
|
|
IN PKDPC Dpc,
|
|
IN PVOID Context,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This is the DPC routine for the MRB timeout timer. The timer is set
|
|
before the Cbw is transmitted, and canceled when the bulk-only request
|
|
completes by receiving a Csw, or when error occurs aborting the request.
|
|
|
|
The timer is intended to protect against devices that NAK forever.
|
|
Device that are just extremely slow will hit this as well. This code
|
|
does not protect against software bugs in this driver or lower in the
|
|
USB stack.
|
|
|
|
All this routine does is cancel any URBs that are outstanding. This will break
|
|
the deadlock, and the normal error handling in the state machine will kick in.
|
|
|
|
Timeout Timing:
|
|
|
|
The timer is set at CBW, and extended at each data stage and at CSW.
|
|
The length of the timeout for the data stages and CSW is fixed. The caller
|
|
in the disk layer can set the timeout for the CBW. This is to accomodate commands
|
|
like VERIFY that may require very long timeouts on the CBW and they don't have
|
|
a data stage.
|
|
|
|
--*/
|
|
{
|
|
PMU_DEVICE_EXTENSION deviceExtension = (PMU_DEVICE_EXTENSION) Context;
|
|
|
|
USB_DBG_WARN_PRINT(("MRB state machine timed out."));
|
|
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags, DF_MRB_TIMER_RUNNING);
|
|
|
|
if(TEST_FLAG(deviceExtension->DeviceFlags,DF_PRIMARY_URB_PENDING))
|
|
{
|
|
ASSERT(deviceExtension->MuInstance);
|
|
deviceExtension->MuInstance->Device->CancelRequest(&deviceExtension->Urb);
|
|
}
|
|
|
|
if(TEST_FLAG(deviceExtension->DeviceFlags, DF_SECONDARY_URB_PENDING))
|
|
{
|
|
ASSERT(deviceExtension->MuInstance);
|
|
deviceExtension->MuInstance->Device->CancelRequest((PURB)&deviceExtension->BulkUrbSecondary);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
MU_fMrbErrorHandler(
|
|
IN PMU_DEVICE_EXTENSION DeviceExtension,
|
|
IN NTSTATUS Status
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine is called whenever a stage of the Mrb state machine detects
|
|
an error (USB failure, device removed, CSW problem, or timeout). There
|
|
should be no outstanding Urbs when this routine is called.
|
|
|
|
If the MRB timer is running, cancel it.
|
|
|
|
If the device has been removed, this routine cleans up the Mrb state machine
|
|
and calls the Mrb competion routine with STATUS_DEVICE_NOT_CONNECTED.
|
|
|
|
If the device is still present, it kicks of the USB bulk-only reset
|
|
sequence. This a fairly gentle way to get the bulk-only transport
|
|
protocol back in-sync between the device and this driver.
|
|
|
|
See the MU_ResetDeviceCompletionRoutine description for details on
|
|
the reset sequence. This routine performs the first two steps,
|
|
then second of which is asynchronous and completes in
|
|
MU_ResetDeviceCompletionRoutine.
|
|
--*/
|
|
{
|
|
PMU_INSTANCE muInstance = DeviceExtension->MuInstance;
|
|
|
|
//
|
|
// Stop the Mrb Timer, if it is running
|
|
//
|
|
|
|
if(TEST_FLAG(DeviceExtension->DeviceFlags, DF_MRB_TIMER_RUNNING))
|
|
{
|
|
KeCancelTimer(&DeviceExtension->Mrb.Timer);
|
|
CLEAR_FLAG(DeviceExtension->DeviceFlags, DF_MRB_TIMER_RUNNING);
|
|
}
|
|
|
|
//
|
|
// There should be no URB's pending when this routine is hit.
|
|
//
|
|
|
|
ASSERT(!TEST_FLAG(DeviceExtension->DeviceFlags, DF_ANY_URB_PENDING));
|
|
|
|
//
|
|
// Check if the device has been removed
|
|
//
|
|
|
|
if(TEST_FLAG(DeviceExtension->DeviceFlags, DF_REMOVED|DF_PENDING_REMOVE))
|
|
{
|
|
USB_DBG_TRACE_PRINT(("Mrb.CompletionRoutine(0x%0.8x, STATUS_DEVICE_NOT_CONNECTED)", DeviceExtension));
|
|
DeviceExtension->Mrb.CompletionRoutine(DeviceExtension, STATUS_DEVICE_NOT_CONNECTED);
|
|
return;
|
|
}
|
|
|
|
ASSERT(muInstance);
|
|
|
|
//**
|
|
//** Start the reset sequence,
|
|
//**
|
|
|
|
USB_DBG_TRACE_PRINT(("Initiating Bulk-Only reset sequence."));
|
|
|
|
SET_FLAG(DeviceExtension->DeviceFlags, DF_RESET_STEP1);
|
|
|
|
//
|
|
// stored the failed status, so that we still have it
|
|
// after a reset. Note this field is a union with
|
|
// BytesSubmitted so it is not valid if we resubmit the MRB.
|
|
//
|
|
DeviceExtension->Mrb.FailedStatus = Status;
|
|
|
|
//
|
|
// Clear the data toggle and software halt flag. This is a
|
|
// synchronous USB request and should never fail.
|
|
//
|
|
|
|
USB_BUILD_SET_ENDPOINT_STATE(
|
|
&DeviceExtension->Urb.GetSetEndpointState,
|
|
DeviceExtension->BulkInEndpointHandle,
|
|
USB_ENDPOINT_STATE_DATA_TOGGLE_RESET | USB_ENDPOINT_STATE_CLEAR_HALT
|
|
);
|
|
if(USBD_ERROR(muInstance->Device->SubmitRequest(&DeviceExtension->Urb)))
|
|
{
|
|
USB_DBG_WARN_PRINT(("MU_ResetDeviceDpc: Bulk-IN SetEndpointStatus failed!\n"));
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the MRB timer to time the reset.
|
|
//
|
|
LARGE_INTEGER deltaTime;
|
|
deltaTime.QuadPart = MRB_RESET_TIMEOUT*MRB_TIMEOUT_UNIT;
|
|
SET_FLAG(DeviceExtension->DeviceFlags, DF_MRB_TIMER_RUNNING);
|
|
SET_FLAG(DeviceExtension->DeviceFlags,DF_PRIMARY_URB_PENDING);
|
|
KeSetTimer(&DeviceExtension->Mrb.Timer, deltaTime, &DeviceExtension->Mrb.TimeoutDpcObject);
|
|
|
|
//
|
|
// now send CLEAR_FEATURE(ENDPOINT_STALL) for the IN endpoint
|
|
//
|
|
|
|
USB_BUILD_CLEAR_FEATURE(
|
|
&DeviceExtension->Urb.ControlTransfer,
|
|
USB_COMMAND_TO_ENDPOINT,
|
|
USB_FEATURE_ENDPOINT_STALL,
|
|
muInstance->BulkInEndpointAddress,
|
|
(PURB_COMPLETE_PROC)MU_ResetDeviceCompletionRoutine,
|
|
DeviceExtension
|
|
);
|
|
|
|
//
|
|
// The completion routine deal with errors.
|
|
//
|
|
muInstance->Device->SubmitRequest(&DeviceExtension->Urb);
|
|
}
|
|
|
|
VOID
|
|
MU_ResetDeviceCompletionRoutine (
|
|
IN PURB Urb,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This routine is the heart of the USB Bulk-Only Mass Storage Reset Sequence.
|
|
The reset sequence consists of:
|
|
1) Clear the STALL on the Bulk-IN endpoint.
|
|
2) Clear the STALL on the Bulk-OUT endpoint.
|
|
3) Send the the BULK_ONLY_MASS_STORAGE_RESET command on the control pipe.
|
|
|
|
Clearing a STALL is a two step process on Xbox, first you send a
|
|
SET_ENDPOINT_STATE Urb to clear the endpoint HALT bit, and to reset the
|
|
data toggle. Then you send a CLEAR_FEATURE(ENDPOINT_STALL) over the
|
|
control endpoint to the device, so it clears its STALL bit.
|
|
|
|
Step 1) was initiated by MU_fMrbErrorHandler. So this routine verifies
|
|
that that succeeded.
|
|
|
|
Steps 2) and 3) are both asynchronous. So this routine is a multistage
|
|
state machine. DeviceFlags are used to keep track of the stage.
|
|
|
|
Any failure during the reset sequence, results in reporting the device as
|
|
not responding to the core USB stack, if MU_Remove has not been called in
|
|
the interm. The core USB stack will drop the device (notifying everyone
|
|
it is gone), and then reenumerate. This procedure is not gentle!
|
|
|
|
At the end of the reset sequence the retry count of the MRB is decremented
|
|
and checked, if there are still retries remaining, then MU_fCbwTransfer is
|
|
called to restart the Mrb.
|
|
|
|
Otherwise, the MRB completion routine is called with STATUS_UNSUCCESSFUL.
|
|
Unfortunately, the original error (timeout, or transmission failure)
|
|
has not been propogated.
|
|
--*/
|
|
{
|
|
PMU_DEVICE_EXTENSION deviceExtension = (PMU_DEVICE_EXTENSION) Context;
|
|
ULONG resetStage = deviceExtension->DeviceFlags & DF_RESET_STEPS;
|
|
PMU_INSTANCE muInstance = deviceExtension->MuInstance;
|
|
|
|
//
|
|
// Mark that the primary URB is not outstanding
|
|
//
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags,DF_PRIMARY_URB_PENDING);
|
|
|
|
//
|
|
// Stop the MRB timer if it is running
|
|
//
|
|
if(TEST_FLAG(deviceExtension->DeviceFlags, DF_MRB_TIMER_RUNNING))
|
|
{
|
|
KeCancelTimer(&deviceExtension->Mrb.Timer);
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags, DF_MRB_TIMER_RUNNING);
|
|
}
|
|
|
|
//
|
|
// Check for device not connected.
|
|
//
|
|
if(TEST_FLAG(deviceExtension->DeviceFlags, DF_REMOVED|DF_PENDING_REMOVE))
|
|
{
|
|
USB_DBG_TRACE_PRINT(("Mrb.CompletionRoutine(0x%0.8x, STATUS_DEVICE_NOT_CONNECTED)", deviceExtension));
|
|
deviceExtension->Mrb.CompletionRoutine(deviceExtension, STATUS_DEVICE_NOT_CONNECTED);
|
|
return;
|
|
}
|
|
|
|
ASSERT(muInstance);
|
|
|
|
//
|
|
// Check for USB errors
|
|
//
|
|
|
|
if(USBD_ERROR(Urb->Header.Status))
|
|
{
|
|
|
|
USB_DBG_WARN_PRINT(("Reset Sequence Failed, reporting device as not responding."));
|
|
|
|
//
|
|
// Clear all the reset flags, reset is over, wherever it was
|
|
//
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags, DF_RESET_STEPS);
|
|
|
|
//
|
|
// Report the device as not responding
|
|
//
|
|
muInstance->Device->DeviceNotResponding();
|
|
|
|
//
|
|
// Fail the MRB as STATUS_DEVICE_NOT_CONNECTED
|
|
//
|
|
USB_DBG_TRACE_PRINT(("Mrb.CompletionRoutine(0x%0.8x, STATUS_DEVICE_NOT_CONNECTED)", deviceExtension));
|
|
deviceExtension->Mrb.CompletionRoutine(deviceExtension, STATUS_DEVICE_NOT_CONNECTED);
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Move on to the next stage of the Reset sequence.
|
|
// (note that we are switching on the resetStage
|
|
// of the just completed stage.)
|
|
//
|
|
|
|
switch(resetStage)
|
|
{
|
|
case DF_RESET_STEP1:
|
|
|
|
//
|
|
// Switch the flag to indicate step2
|
|
//
|
|
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags, DF_RESET_STEP1);
|
|
SET_FLAG(deviceExtension->DeviceFlags, DF_RESET_STEP2);
|
|
|
|
//
|
|
// Clear the data toggle and software halt flag for Bulk-OUT
|
|
// This is a synchronous USB request and should never fail.
|
|
//
|
|
|
|
USB_BUILD_SET_ENDPOINT_STATE(
|
|
&deviceExtension->Urb.GetSetEndpointState,
|
|
deviceExtension->BulkOutEndpointHandle,
|
|
USB_ENDPOINT_STATE_DATA_TOGGLE_RESET | USB_ENDPOINT_STATE_CLEAR_HALT
|
|
);
|
|
|
|
if(USBD_ERROR(muInstance->Device->SubmitRequest(&deviceExtension->Urb)))
|
|
{
|
|
USB_DBG_WARN_PRINT(("MU_ResetDeviceDpc: Bulk-OUT SetEndpointStatus failed!"));
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
//
|
|
// Build CLEAR_FEATURE(ENDPOINT_STALL) for the OUT endpoint
|
|
// (it will get sent at the end of this routine.)
|
|
|
|
USB_BUILD_CLEAR_FEATURE(
|
|
&deviceExtension->Urb.ControlTransfer,
|
|
USB_COMMAND_TO_ENDPOINT,
|
|
USB_FEATURE_ENDPOINT_STALL,
|
|
muInstance->BulkOutEndpointAddress,
|
|
(PURB_COMPLETE_PROC)MU_ResetDeviceCompletionRoutine,
|
|
deviceExtension
|
|
);
|
|
|
|
break;
|
|
case DF_RESET_STEP2:
|
|
|
|
//
|
|
// Switch the flag to indicate step2
|
|
//
|
|
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags, DF_RESET_STEP2);
|
|
SET_FLAG(deviceExtension->DeviceFlags, DF_RESET_STEP3);
|
|
|
|
//
|
|
// Build the BULK_ONLY_MASS_STORAGE_RESET command
|
|
// (it will get sent at the end of this routine.)
|
|
|
|
USB_BUILD_CONTROL_TRANSFER(
|
|
&deviceExtension->Urb.ControlTransfer,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
USB_TRANSFER_DIRECTION_OUT,
|
|
MU_ResetDeviceCompletionRoutine,
|
|
deviceExtension,
|
|
FALSE,
|
|
(USB_HOST_TO_DEVICE | USB_CLASS_COMMAND | USB_COMMAND_TO_INTERFACE),
|
|
BULK_ONLY_MASS_STORAGE_RESET,
|
|
0,
|
|
muInstance->InterfaceNumber,
|
|
0);
|
|
|
|
break;
|
|
case DF_RESET_STEP3:
|
|
|
|
//
|
|
// Clear DF_RESET_STEP3, the reset sequence is over.
|
|
//
|
|
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags, DF_RESET_STEP3);
|
|
|
|
//
|
|
// Check for retries.
|
|
//
|
|
// retries = (total tries) - 1, so post decrement
|
|
// when checking.
|
|
//
|
|
|
|
if(deviceExtension->Mrb.Retries--)
|
|
{
|
|
//
|
|
// If we resubmit the Mrb we need to pet the
|
|
// watchdog, it wasn't intended to wait through
|
|
// lots of retries.
|
|
//
|
|
MU_DEBUG_PET_WATCHDOG(deviceExtension);
|
|
//
|
|
// Resubmit the Mrb
|
|
//
|
|
MU_fCbwTransfer(deviceExtension);
|
|
} else
|
|
{
|
|
//
|
|
// Fail with status unsuccessful
|
|
//
|
|
USB_DBG_TRACE_PRINT(("Mrb.CompletionRoutine(0x%0.8x, STATUS_UNSUCCESSFUL)", deviceExtension));
|
|
deviceExtension->Mrb.CompletionRoutine(deviceExtension, deviceExtension->Mrb.FailedStatus);
|
|
}
|
|
|
|
return;
|
|
|
|
default:
|
|
USB_DBG_ERROR_PRINT(("Reaching here indicates an MU driver bug!"));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Set the MRB timer to time the reset.
|
|
//
|
|
LARGE_INTEGER deltaTime;
|
|
deltaTime.QuadPart = MRB_RESET_TIMEOUT*MRB_TIMEOUT_UNIT;
|
|
SET_FLAG(deviceExtension->DeviceFlags,DF_PRIMARY_URB_PENDING);
|
|
SET_FLAG(deviceExtension->DeviceFlags, DF_MRB_TIMER_RUNNING);
|
|
KeSetTimer(&deviceExtension->Mrb.Timer, deltaTime, &deviceExtension->Mrb.TimeoutDpcObject);
|
|
|
|
//
|
|
// Submit the URB built in either case DF_RESET_STEP1,
|
|
// or case DF_RESET_STEP2.
|
|
//
|
|
SET_FLAG(deviceExtension->DeviceFlags,DF_PRIMARY_URB_PENDING);
|
|
muInstance->Device->SubmitRequest(&deviceExtension->Urb);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
#if DBG
|
|
VOID
|
|
FASTCALL
|
|
MU_fValidateMrb(
|
|
IN PMU_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Debug Only routine that validates the current mrb. All this
|
|
routine does is spew errors or possible errors, otherwise
|
|
it does not effect the operation of the driver.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// 1) If no direction flag is set, then the DataBuffer
|
|
// and TransferLength are NULL, and 0.
|
|
//
|
|
if(!TEST_FLAG(DeviceExtension->Mrb.Flags, MRB_FLAGS_UNSPECIFIED_DIRECTION))
|
|
{
|
|
if(DeviceExtension->Mrb.DataBuffer || DeviceExtension->Mrb.TransferLength)
|
|
{
|
|
USB_DBG_ERROR_PRINT(("No direction, but non-NULL DataBuffer or non-zero length"));
|
|
}
|
|
}
|
|
|
|
//
|
|
// 2) Only one direction flag is set.
|
|
//
|
|
if(TEST_ALL_FLAGS(DeviceExtension->Mrb.Flags, MRB_FLAGS_UNSPECIFIED_DIRECTION))
|
|
{
|
|
USB_DBG_ERROR_PRINT(("More than one direction flag is set in Mrb!"));
|
|
}
|
|
//
|
|
// 3) If a direction flag is that the DataBuffer is non-NULL
|
|
// and that the size is finite.
|
|
//
|
|
if(TEST_FLAG(DeviceExtension->Mrb.Flags, MRB_FLAGS_UNSPECIFIED_DIRECTION))
|
|
{
|
|
if(NULL == DeviceExtension->Mrb.DataBuffer)
|
|
{
|
|
USB_DBG_ERROR_PRINT(("Mrb with transfer to\from NULL DataBuffer!"));
|
|
}
|
|
if(0 == DeviceExtension->Mrb.TransferLength)
|
|
{
|
|
USB_DBG_ERROR_PRINT(("Mrb with transfer of 0 length!"));
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#endif //DBG
|
|
|