805 lines
26 KiB
C++
805 lines
26 KiB
C++
/*++
|
|
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
XDCS2.CPP
|
|
|
|
Abstract:
|
|
|
|
CHANGED THE API COMPLETELY FROM THE ORIGINAL XDCS
|
|
|
|
This source file implements the major entry points to the Xbox DVD Code Server
|
|
driver (XDCS). XDCS is a USB interface based device that contains the DVD playback
|
|
code library. The code resides in ROM on the device.
|
|
|
|
|
|
The entire implementation is in this file.
|
|
|
|
Client API:
|
|
|
|
XDCSGetInformation - Gets information about the code on the device
|
|
XDCSDownloadCode - Downloads the code synchronously
|
|
XDCSDownloadCodeAsync - Downloads the code asynchronously
|
|
|
|
Entries required by the USB core stack:
|
|
|
|
XDCS_Init
|
|
XDCS_AddDevice
|
|
XDCS_RemoveDevice
|
|
|
|
Environment:
|
|
|
|
Xbox USB class driver.
|
|
|
|
Revision History:
|
|
|
|
03-30-01 : Mitchell Dernis(mitchd) - created
|
|
|
|
Implementation and Use Overview:
|
|
|
|
Totally different API from the original XDCS
|
|
|
|
--*/
|
|
|
|
//*****************************************************************************
|
|
// Headers
|
|
//*****************************************************************************
|
|
|
|
// Xbox Kernel Service Headers
|
|
#define _XAPI_
|
|
extern "C" {
|
|
#include <ntos.h>
|
|
}
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
|
|
// Xbox System Software Headers
|
|
#include <xtl.h>
|
|
#include <xboxp.h>
|
|
|
|
// Useful Debug definitions
|
|
#define MODULE_POOL_TAG 'SCDX'
|
|
#include <debug.h>
|
|
DEFINE_USB_DEBUG_FUNCTIONS("XDCS");
|
|
|
|
// Xbox USB Class driver header
|
|
#include <usb.h>
|
|
|
|
// XDCS specific header
|
|
|
|
|
|
|
|
//*****************************************************************************
|
|
// XDCS constants and structures from the XDCS specification
|
|
//*****************************************************************************
|
|
#define XDCS_REQUEST_GET_ROM_FILE_INFO 1
|
|
#define XDCS_REQUEST_GET_ROM_FILE_BLOCK 2
|
|
#define XDCS_BLOCK_LENGTH 1024 //1k blocks
|
|
#define XDCS_BLOCK_SHIFT 10 //bit shift to divide by 1k
|
|
#define XDCS_CONTROL_TD_QUOTA (2*(3+1024/64)) //2 1k transfers with 64 byte fifo
|
|
#define XDCS_MAX_PORTS 4 //The maximum number of ports supported
|
|
#define XDCS_REQUEST_TIMEOUT (-10000*200) //200 ms timeout
|
|
#define XDCS_MAX_TRIES 4
|
|
|
|
//*****************************************************************************
|
|
// Declare XDCS types and class.
|
|
//*****************************************************************************
|
|
DECLARE_XPP_TYPE(XDEVICE_TYPE_DVD_CODE_SERVER)
|
|
USB_DEVICE_TYPE_TABLE_BEGIN(XDCS_)
|
|
USB_DEVICE_TYPE_TABLE_ENTRY(XDEVICE_TYPE_DVD_CODE_SERVER)
|
|
USB_DEVICE_TYPE_TABLE_END()
|
|
USB_CLASS_DRIVER_DECLARATION(XDCS_, XBOX_DEVICE_CLASS_XDCS, 0, 0)
|
|
#pragma data_seg(".XPP$ClassXdcs")
|
|
USB_CLASS_DECLARATION_POINTER(XDCS_)
|
|
#pragma data_seg(".XPP$Data")
|
|
|
|
//*****************************************************************************
|
|
// Declare Local Structures
|
|
//*****************************************************************************
|
|
|
|
typedef struct _XDCS_DEVICE
|
|
{
|
|
UCHAR bFlags;
|
|
UCHAR bInterfaceNumber;
|
|
USHORT usInstance; //Update whenever a device is removed
|
|
IUsbDevice *Device;
|
|
XDCS_DVD_CODE_INFORMATION CodeInformation;
|
|
} XDCS_DEVICE, *PXDCS_DEVICE;
|
|
|
|
#define XDCS_DEVICE_FLAG_CONNECTED 0x01
|
|
#define XDCS_DEVICE_FLAG_INUSE 0x02
|
|
#define XDCS_DEVICE_FLAG_ENUMERATING 0x04
|
|
|
|
#define MAKE_DWORD_INSTANCE(usInstance, dwPort) (((usInstance) << 16)|(dwPort))
|
|
#define PORT_FROM_DWORD_INSTANCE(dwInstance) (0x0000FFFF&(dwInstance))
|
|
#define USHORT_INSTANCE_FROM_DWORD_INSTANCE(dwInstance) ((USHORT)(((dwInstance)&0xFFFF0000)>>16))
|
|
|
|
typedef struct _XDCS_GLOBALS
|
|
{
|
|
// Single set of resources for data transfer
|
|
ULONG ulGlobalFlags;
|
|
ULONG Urb1LastLength; //*** DO NOT REORDER
|
|
URB Urb1; //*** SEE
|
|
ULONG Urb2LastLength; //*** #define XDCS_URB_LAST_LEN
|
|
URB Urb2; //*** BELOW
|
|
URB UrbEnum;
|
|
ULONG BytesSubmitted;
|
|
ULONG RetryCount;
|
|
USBD_STATUS UsbdStatus;
|
|
KTIMER WatchDogTimer;
|
|
KDPC WatchDogTimerDpc;
|
|
PXDCS_ASYNC_DOWNLOAD_REQUEST CurrentDownloadRequest;
|
|
// Track attached devices
|
|
XDCS_DEVICE Devices[XDCS_MAX_PORTS];
|
|
} XDCS_GLOBALS, *PXDCS_GLOBALS;
|
|
|
|
#define XDCS_GLOBAL_FLAG_TRANSFER_IN_PROGRESS 0x00000001
|
|
#define XDCS_GLOBAL_FLAG_URB1_OUTSTANDING 0x00000002
|
|
#define XDCS_GLOBAL_FLAG_URB2_OUTSTANDING 0x00000004
|
|
#define XDCS_GLOBAL_FLAG_ENUM_URB_OUTSTANDING 0x00000008
|
|
#define XDCS_GLOBAL_FLAG_DOWNLOAD_URBS_OUTSTANDING (XDCS_GLOBAL_FLAG_URB1_OUTSTANDING|\
|
|
XDCS_GLOBAL_FLAG_URB2_OUTSTANDING)
|
|
#define XDCS_GLOBAL_FLAG_URBS_OUTSTANDING (XDCS_GLOBAL_FLAG_URB1_OUTSTANDING|\
|
|
XDCS_GLOBAL_FLAG_ENUM_URB_OUTSTANDING|\
|
|
XDCS_GLOBAL_FLAG_URB2_OUTSTANDING)
|
|
#define XDCS_GLOBAL_FLAG_WATCHDOG_RUNNING 0x00000010
|
|
|
|
//
|
|
// Takes advantage that Urb1LastLength is stored
|
|
// right before Urb1 and Urb2LastLength is stored
|
|
// right before Urb2.
|
|
//
|
|
#define XDCS_URB_LAST_LEN(Urb) (*((PULONG)(Urb)-1))
|
|
|
|
//****************************************
|
|
// Internally useful macros
|
|
//****************************************
|
|
#define SET_FLAG(variable, flag) ((variable) |= (flag))
|
|
#define CLEAR_FLAG(variable, flag) ((variable) &= ~(flag))
|
|
#define TEST_FLAG(variable, flag) ((variable)&(flag))
|
|
#define TEST_ANY_FLAG(variable, flag) ((variable)&(flag))
|
|
#define TEST_ALL_FLAGS(variable, flag) ((flag)==((variable)&(flag)))
|
|
|
|
//*****************************************************************************
|
|
// Really cool inline template function for walking buffers.
|
|
//*****************************************************************************
|
|
template <class T>
|
|
inline T *AdvancePtr(T *pT, ULONG cb)
|
|
{
|
|
return (T *)(((ULONG)pT) + cb);
|
|
}
|
|
|
|
|
|
//*****************************************************************************
|
|
// Declare Globals
|
|
//*****************************************************************************
|
|
USB_RESOURCE_REQUIREMENTS XDCS_gResourceRequirements =
|
|
{USB_CONNECTOR_TYPE_DIRECT, 1, 2, 1, 0, 0, XDCS_CONTROL_TD_QUOTA, 0, 0, 0};
|
|
XDCS_GLOBALS XDCS_Globals = {0};
|
|
|
|
//*****************************************************************************
|
|
// Forward Declaration of Local Functions
|
|
//*****************************************************************************
|
|
#if DBG
|
|
#define LOCALFUNC
|
|
#else
|
|
//Compiler may be able to optimize static functions better.
|
|
#define LOCALFUNC static
|
|
#endif
|
|
LOCALFUNC VOID XDCS_AddComplete(PURB Urb, PXDCS_DEVICE XdcsDevice);
|
|
LOCALFUNC VOID XDCS_DownloadStateMachine(PURB Urb, PXDCS_DEVICE XdcsDevice);
|
|
LOCALFUNC BOOL XDCS_BuildAndSubmitDownloadRequest(PURB Urb,PXDCS_DEVICE XdcsDevice);
|
|
LOCALFUNC VOID XDCS_CompleteDownload(PURB Urb, PXDCS_DEVICE XdcsDevice);
|
|
LOCALFUNC VOID XDCS_WatchDogTimeout (PKDPC Dpc,PVOID,PVOID,PVOID);
|
|
LOCALFUNC VOID XDCS_StartExtendWatchdog();
|
|
LOCALFUNC VOID XDCS_StopWatchdog();
|
|
|
|
//*****************************************************************************
|
|
// Implementation of USB Required Entry Points:
|
|
// XDCS_Init,
|
|
// XDCS_AddDevice,
|
|
// XDCS_RemoveDevice
|
|
//*****************************************************************************
|
|
#pragma code_seg(".XPPCINIT")
|
|
|
|
EXTERNUSB VOID XDCS_Init(IUsbInit *pUsbInit)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Called by the USB core stack when XDCS is linked in to the application.
|
|
Initialize our globals, register our driver resource requirements.
|
|
--*/
|
|
{
|
|
USB_DBG_ENTRY_PRINT(("Entering XDCS_Init"));
|
|
|
|
//Init the timer and timer dpc
|
|
KeInitializeDpc(&XDCS_Globals.WatchDogTimerDpc, XDCS_WatchDogTimeout, NULL);
|
|
KeInitializeTimer(&XDCS_Globals.WatchDogTimer);
|
|
|
|
// register resources
|
|
pUsbInit->RegisterResources(&XDCS_gResourceRequirements);
|
|
|
|
USB_DBG_EXIT_PRINT(("Exiting XDCS_Init"));
|
|
return;
|
|
}
|
|
#pragma code_seg(".XPPCODE")
|
|
|
|
EXTERNUSB VOID XDCS_AddDevice(IN IUsbDevice *Device)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
1) Records that the device is present and relevant info that
|
|
it may need later.
|
|
2) Gets the ROM info
|
|
|
|
Arguments:
|
|
Device - Interface to USB core stack for device.
|
|
|
|
Notes:
|
|
AddDevice routines are always called at DPC level.
|
|
--*/
|
|
{
|
|
PXDCS_DEVICE xdcsDevice;
|
|
ULONG ulPort;
|
|
|
|
USB_DBG_ENTRY_PRINT(("XDCS_AddDevice(Device=0x%0.8x)", Device));
|
|
|
|
//Get and check the port. (It should only work direct connect).
|
|
ulPort = Device->GetPort();
|
|
if(ulPort >= XDCS_MAX_PORTS)
|
|
{
|
|
Device->AddComplete(USBD_STATUS_UNSUPPORTED_DEVICE);
|
|
return;
|
|
}
|
|
|
|
xdcsDevice = &XDCS_Globals.Devices[ulPort];
|
|
|
|
//This device should not already be marked as connected
|
|
ASSERT(!TEST_FLAG(xdcsDevice->bFlags,XDCS_DEVICE_FLAG_CONNECTED));
|
|
xdcsDevice->bInterfaceNumber = Device->GetInterfaceNumber();
|
|
Device->SetExtension((PVOID)xdcsDevice);
|
|
xdcsDevice->Device = Device;
|
|
SET_FLAG(xdcsDevice->bFlags, XDCS_DEVICE_FLAG_ENUMERATING);
|
|
|
|
// Build and submit the XDCS_REQUEST_GET_ROM_FILE_INFO request
|
|
USB_BUILD_CONTROL_TRANSFER(
|
|
&XDCS_Globals.UrbEnum.ControlTransfer,
|
|
NULL,
|
|
(PVOID)&xdcsDevice->CodeInformation, //right into the user's buffer
|
|
sizeof(XDCS_DVD_CODE_INFORMATION),
|
|
USB_TRANSFER_DIRECTION_IN,
|
|
(PURB_COMPLETE_PROC)XDCS_AddComplete,
|
|
(PVOID)xdcsDevice,
|
|
FALSE,
|
|
USB_DEVICE_TO_HOST|USB_VENDOR_COMMAND|USB_COMMAND_TO_INTERFACE,
|
|
XDCS_REQUEST_GET_ROM_FILE_INFO,
|
|
0,
|
|
xdcsDevice->bInterfaceNumber,
|
|
sizeof(XDCS_DVD_CODE_INFORMATION)
|
|
);
|
|
|
|
SET_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_ENUM_URB_OUTSTANDING);
|
|
XDCS_StartExtendWatchdog();
|
|
|
|
//Don't worry about errors the completion routine will handle.
|
|
xdcsDevice->Device->SubmitRequest(&XDCS_Globals.UrbEnum);
|
|
|
|
USB_DBG_EXIT_PRINT(("ExitingXDCS_AddDevice"));
|
|
return;
|
|
}
|
|
|
|
LOCALFUNC VOID XDCS_AddComplete(PURB Urb, PXDCS_DEVICE XdcsDevice)
|
|
{
|
|
|
|
CLEAR_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_ENUM_URB_OUTSTANDING);
|
|
CLEAR_FLAG(XdcsDevice->bFlags, XDCS_DEVICE_FLAG_ENUMERATING);
|
|
XDCS_StopWatchdog();
|
|
|
|
if(USBD_SUCCESS(Urb->Header.Status))
|
|
{
|
|
//Mark the device as connected
|
|
SET_FLAG(XdcsDevice->bFlags,XDCS_DEVICE_FLAG_CONNECTED);
|
|
//Complete the Add.
|
|
XdcsDevice->Device->SetClassSpecificType(0);
|
|
XdcsDevice->Device->AddComplete(USBD_STATUS_SUCCESS);
|
|
} else
|
|
{
|
|
XdcsDevice->Device->AddComplete(Urb->Header.Status);
|
|
}
|
|
}
|
|
|
|
|
|
EXTERNUSB VOID
|
|
XDCS_RemoveDevice (
|
|
IN IUsbDevice *Device
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
1) Records the removal.
|
|
2) If the device is not in use, it and completes the remove.
|
|
|
|
Arguments:
|
|
Device - Interface to USB core stack for device that is to be
|
|
removed.
|
|
Notes:
|
|
RemoveDevice routines are always called at DPC level.
|
|
--*/
|
|
{
|
|
USB_DBG_ENTRY_PRINT(("XDCS_RemoveDevice(Device=0x%0.8x)", Device));
|
|
PXDCS_DEVICE xdcsDevice = (PXDCS_DEVICE) Device->GetExtension();
|
|
|
|
//Mark the device as not connected.
|
|
CLEAR_FLAG(xdcsDevice->bFlags,XDCS_DEVICE_FLAG_CONNECTED);
|
|
|
|
//Increment the instance counter - so when it is
|
|
//plugged back in it is different.
|
|
xdcsDevice->usInstance++;
|
|
|
|
//If the device is not in use, complete the removal.
|
|
//Otherwise, who ever is using it will complete the removal
|
|
//after they clean up.
|
|
if(!TEST_FLAG(xdcsDevice->bFlags,XDCS_DEVICE_FLAG_INUSE))
|
|
{
|
|
xdcsDevice->Device = NULL;
|
|
Device->SetExtension(NULL);
|
|
Device->RemoveComplete();
|
|
}
|
|
|
|
USB_DBG_EXIT_PRINT(("exiting XDCS_RemoveDevice"));
|
|
return;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
XDCSGetInformation(
|
|
IN DWORD dwPort,
|
|
OUT PDWORD pdwDeviceInstance,
|
|
OUT PXDCS_DVD_CODE_INFORMATION pDvdCodeInformation
|
|
)
|
|
{
|
|
|
|
PXDCS_DEVICE xdcsDevice;
|
|
KIRQL oldIrql;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
// Validate parameters
|
|
RIP_ON_NOT_TRUE("XDCSGetInformation", (dwPort < XDCS_MAX_PORTS));
|
|
RIP_ON_NOT_TRUE("XDCSGetInformation", (NULL != pdwDeviceInstance));
|
|
RIP_ON_NOT_TRUE("XDCSGetInformation", (NULL != pDvdCodeInformation));
|
|
|
|
//Get the requested device.
|
|
xdcsDevice = &XDCS_Globals.Devices[dwPort];
|
|
|
|
//Synchronize access
|
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
|
|
|
// Make sure the device is connected
|
|
if(!TEST_FLAG(xdcsDevice->bFlags, XDCS_DEVICE_FLAG_CONNECTED))
|
|
{
|
|
dwError = ERROR_DEVICE_NOT_CONNECTED;
|
|
}
|
|
else
|
|
{
|
|
RtlCopyMemory(pDvdCodeInformation, &xdcsDevice->CodeInformation, sizeof(XDCS_DVD_CODE_INFORMATION));
|
|
*pdwDeviceInstance = MAKE_DWORD_INSTANCE(xdcsDevice->usInstance, dwPort);
|
|
}
|
|
|
|
//Done synchronizing
|
|
KeLowerIrql(oldIrql);
|
|
|
|
return dwError;
|
|
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
XDCSDownloadCode(
|
|
DWORD dwDeviceInstance,
|
|
PVOID pvBuffer,
|
|
ULONG ulOffset,
|
|
ULONG ulLength,
|
|
PULONG pulBytesRead
|
|
)
|
|
{
|
|
XDCS_ASYNC_DOWNLOAD_REQUEST asyncRequest;
|
|
|
|
//
|
|
// Need an event to signal when done
|
|
//
|
|
asyncRequest.hCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if(NULL == asyncRequest.hCompleteEvent)
|
|
{
|
|
return GetLastError();
|
|
}
|
|
|
|
//
|
|
// Fill out rest of async request
|
|
//
|
|
asyncRequest.dwDeviceInstance = dwDeviceInstance;
|
|
asyncRequest.pvBuffer = pvBuffer;
|
|
asyncRequest.ulOffset = ulOffset;
|
|
asyncRequest.ulLength = ulLength;
|
|
|
|
//
|
|
// Submit async request
|
|
//
|
|
XDCSDownloadCodeAsync(&asyncRequest);
|
|
|
|
//
|
|
// Wait for the request to complete
|
|
//
|
|
WaitForSingleObject(asyncRequest.hCompleteEvent, INFINITE);
|
|
|
|
//
|
|
// Copy byte read
|
|
//
|
|
*pulBytesRead = asyncRequest.ulBytesRead;
|
|
|
|
//
|
|
// Return the status
|
|
//
|
|
return asyncRequest.ulStatus;
|
|
}
|
|
|
|
__inline BOOL SleepIfBusy(DWORD dwError)
|
|
{
|
|
if(ERROR_BUSY == dwError)
|
|
{
|
|
Sleep(10);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
XDCSDownloadCodeAsync(
|
|
IN OUT PXDCS_ASYNC_DOWNLOAD_REQUEST pXDCSDownloadRequest
|
|
)
|
|
{
|
|
DWORD dwPort = PORT_FROM_DWORD_INSTANCE(pXDCSDownloadRequest->dwDeviceInstance);
|
|
PXDCS_DEVICE xdcsDevice = &XDCS_Globals.Devices[dwPort];
|
|
DWORD dwError;
|
|
KIRQL oldIrql;
|
|
|
|
|
|
//
|
|
// Loop until we managed to get capture the
|
|
// transfer resources, poor man's mutex, but
|
|
// there should not be any contention for this.
|
|
do
|
|
{
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
//Synchronize access
|
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
|
|
|
//
|
|
// Check instance to make sure that the caller is getting
|
|
// what it thinks it is getting. As long as the client
|
|
// got the instance from us, this is sufficient to check that
|
|
// the device is actually connected, unless the device has been
|
|
// added and removed 65536 times since they called us last.
|
|
//
|
|
if( xdcsDevice->usInstance != USHORT_INSTANCE_FROM_DWORD_INSTANCE(pXDCSDownloadRequest->dwDeviceInstance))
|
|
{
|
|
dwError = ERROR_DEVICE_NOT_CONNECTED;
|
|
} else if(TEST_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_TRANSFER_IN_PROGRESS))
|
|
{
|
|
dwError = ERROR_BUSY;
|
|
#if DBG
|
|
} else if(pXDCSDownloadRequest->ulOffset%XDCS_BLOCK_LENGTH)
|
|
{
|
|
KeLowerIrql(oldIrql);
|
|
RIP("XDCSDownloadCode\\XDCSDownloadCodeAsync, Offset must be 1 kbyte aligned.");
|
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
|
} else if(
|
|
(pXDCSDownloadRequest->ulOffset+pXDCSDownloadRequest->ulLength) >
|
|
xdcsDevice->CodeInformation.dwCodeLength
|
|
)
|
|
{
|
|
KeLowerIrql(oldIrql);
|
|
RIP("XDCSDownloadCode\\XDCSDownloadCodeAsync, Transfer request is beyond data length");
|
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
|
#endif
|
|
} else
|
|
{
|
|
//
|
|
// Claim the transfer resources
|
|
//
|
|
SET_FLAG(xdcsDevice->bFlags, XDCS_DEVICE_FLAG_INUSE);
|
|
SET_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_TRANSFER_IN_PROGRESS);
|
|
XDCS_Globals.CurrentDownloadRequest = pXDCSDownloadRequest;
|
|
|
|
//
|
|
// Open the default endpoint
|
|
//
|
|
USB_BUILD_OPEN_DEFAULT_ENDPOINT(&XDCS_Globals.Urb1.OpenEndpoint);
|
|
xdcsDevice->Device->SubmitRequest(&XDCS_Globals.Urb1);
|
|
|
|
//
|
|
// Start Download
|
|
//
|
|
XDCS_DownloadStateMachine(NULL, xdcsDevice);
|
|
dwError = ERROR_IO_PENDING;
|
|
}
|
|
KeLowerIrql(oldIrql);
|
|
|
|
}while (SleepIfBusy(dwError));
|
|
|
|
return dwError;
|
|
}
|
|
|
|
LOCALFUNC
|
|
VOID
|
|
XDCS_DownloadStateMachine(
|
|
PURB Urb,
|
|
PXDCS_DEVICE XdcsDevice
|
|
)
|
|
{
|
|
//
|
|
// Check if this is the first call.
|
|
//
|
|
if(NULL==Urb)
|
|
{
|
|
XDCS_Globals.UsbdStatus = USBD_STATUS_SUCCESS;
|
|
XDCS_Globals.BytesSubmitted = 0;
|
|
XDCS_Globals.CurrentDownloadRequest->ulStatus = ERROR_IO_PENDING;
|
|
XDCS_Globals.CurrentDownloadRequest->ulBytesRead = 0;
|
|
XDCS_Globals.RetryCount = 0;
|
|
SET_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_DOWNLOAD_URBS_OUTSTANDING);
|
|
XDCS_BuildAndSubmitDownloadRequest(&XDCS_Globals.Urb1, XdcsDevice);
|
|
XDCS_BuildAndSubmitDownloadRequest(&XDCS_Globals.Urb2, XdcsDevice);
|
|
} else
|
|
//
|
|
// This is an URB completing
|
|
//
|
|
{
|
|
BOOL fDone = FALSE;
|
|
if(USBD_SUCCESS(Urb->Header.Status))
|
|
{
|
|
XDCS_Globals.RetryCount = 0;
|
|
XDCS_Globals.CurrentDownloadRequest->ulBytesRead += Urb->ControlTransfer.TransferBufferLength;
|
|
//
|
|
// Resubmit the URB if there is more to transfer
|
|
//
|
|
fDone = XDCS_BuildAndSubmitDownloadRequest(Urb, XdcsDevice);
|
|
}else if( (XDCS_Globals.RetryCount++ < XDCS_MAX_TRIES) && TEST_FLAG(XdcsDevice->bFlags, XDCS_DEVICE_FLAG_CONNECTED) )
|
|
//
|
|
// Check the retry count and make sure the device is still connected.
|
|
// If we have retries left, just resubmit, but remember to reset
|
|
// the length, this is an IN\OUT parameter.
|
|
//
|
|
{
|
|
XDCS_StartExtendWatchdog(); //Pet watchdog
|
|
Urb->CommonTransfer.TransferBufferLength = XDCS_URB_LAST_LEN(Urb);
|
|
XdcsDevice->Device->SubmitRequest(Urb);
|
|
} else
|
|
//
|
|
// Failed and the device is not connected or the retry count is exceeded.
|
|
//
|
|
{
|
|
//
|
|
// Record the error if it is the first
|
|
//
|
|
if(USBD_SUCCESS(XDCS_Globals.UsbdStatus))
|
|
{
|
|
XDCS_Globals.UsbdStatus = Urb->Header.Status;
|
|
}
|
|
//
|
|
// If the twin URB is outstanding, cancel it. If it is not oustanding
|
|
// mark that we are done.
|
|
//
|
|
if( &XDCS_Globals.Urb1 == Urb)
|
|
{
|
|
CLEAR_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_URB1_OUTSTANDING);
|
|
if(TEST_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_URB2_OUTSTANDING))
|
|
{
|
|
XdcsDevice->Device->CancelRequest(&XDCS_Globals.Urb2);
|
|
} else
|
|
{
|
|
fDone = TRUE;
|
|
}
|
|
} else
|
|
{
|
|
CLEAR_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_URB2_OUTSTANDING);
|
|
if(TEST_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_URB1_OUTSTANDING))
|
|
{
|
|
XdcsDevice->Device->CancelRequest(&XDCS_Globals.Urb1);
|
|
} else
|
|
{
|
|
fDone = TRUE;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// If the transfer is done, close down the endpoint.
|
|
//
|
|
if(fDone)
|
|
{
|
|
XDCS_StopWatchdog();
|
|
USB_BUILD_CLOSE_DEFAULT_ENDPOINT(
|
|
&XDCS_Globals.Urb1.CloseEndpoint,
|
|
(PURB_COMPLETE_PROC)XDCS_CompleteDownload,
|
|
(PVOID)XdcsDevice
|
|
);
|
|
XdcsDevice->Device->SubmitRequest(&XDCS_Globals.Urb1);
|
|
}
|
|
}
|
|
}
|
|
|
|
LOCALFUNC
|
|
BOOL
|
|
XDCS_BuildAndSubmitDownloadRequest(
|
|
PURB Urb,
|
|
PXDCS_DEVICE XdcsDevice
|
|
)
|
|
{
|
|
//
|
|
// If there are more byte to submit, do it.
|
|
//
|
|
if(XDCS_Globals.BytesSubmitted < XDCS_Globals.CurrentDownloadRequest->ulLength)
|
|
{
|
|
//Calculate the transfer length for this command
|
|
ULONG ulTransferSize = XDCS_Globals.CurrentDownloadRequest->ulLength - XDCS_Globals.BytesSubmitted;
|
|
if(ulTransferSize > XDCS_BLOCK_LENGTH) ulTransferSize = XDCS_BLOCK_LENGTH;
|
|
|
|
//
|
|
// Store the length in case we need to retry
|
|
//
|
|
XDCS_URB_LAST_LEN(Urb) = ulTransferSize;
|
|
|
|
USBD_STATUS usbdStatus;
|
|
USB_BUILD_CONTROL_TRANSFER(
|
|
&Urb->ControlTransfer,
|
|
NULL,
|
|
AdvancePtr(XDCS_Globals.CurrentDownloadRequest->pvBuffer, XDCS_Globals.BytesSubmitted),
|
|
ulTransferSize,
|
|
USB_TRANSFER_DIRECTION_IN,
|
|
(PURB_COMPLETE_PROC)XDCS_DownloadStateMachine,
|
|
(PVOID)XdcsDevice,
|
|
FALSE,
|
|
USB_DEVICE_TO_HOST|USB_VENDOR_COMMAND|USB_COMMAND_TO_INTERFACE,
|
|
XDCS_REQUEST_GET_ROM_FILE_BLOCK,
|
|
(WORD)((XDCS_Globals.CurrentDownloadRequest->ulOffset + XDCS_Globals.BytesSubmitted)>>XDCS_BLOCK_SHIFT),
|
|
(WORD)XdcsDevice->bInterfaceNumber,
|
|
(WORD)ulTransferSize
|
|
);
|
|
XDCS_Globals.BytesSubmitted += ulTransferSize;
|
|
XDCS_StartExtendWatchdog();
|
|
usbdStatus = XdcsDevice->Device->SubmitRequest(Urb);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If there are no more requests to submit then clear the appropriate
|
|
// XDCS_GLOBAL_FLAG_URB?_OUTSTANDING flag.
|
|
//
|
|
if( &XDCS_Globals.Urb1 == Urb)
|
|
{
|
|
CLEAR_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_URB1_OUTSTANDING);
|
|
} else
|
|
{
|
|
CLEAR_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_URB2_OUTSTANDING);
|
|
}
|
|
|
|
return
|
|
TEST_ANY_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_DOWNLOAD_URBS_OUTSTANDING) ?
|
|
FALSE : TRUE;
|
|
|
|
}
|
|
|
|
LOCALFUNC
|
|
VOID
|
|
XDCS_CompleteDownload(
|
|
PURB Urb,
|
|
PXDCS_DEVICE XdcsDevice
|
|
)
|
|
{
|
|
//
|
|
// Set the status and signal the hEvent.
|
|
//
|
|
XDCS_Globals.CurrentDownloadRequest->ulStatus = IUsbDevice::Win32FromUsbdStatus(XDCS_Globals.UsbdStatus);
|
|
if(XDCS_Globals.CurrentDownloadRequest->hCompleteEvent)
|
|
SetEvent(XDCS_Globals.CurrentDownloadRequest->hCompleteEvent);
|
|
|
|
//
|
|
// Release the shared resources
|
|
//
|
|
XDCS_Globals.CurrentDownloadRequest = NULL;
|
|
CLEAR_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_TRANSFER_IN_PROGRESS);
|
|
|
|
//
|
|
// Clear the in use flag
|
|
//
|
|
CLEAR_FLAG(XdcsDevice->bFlags, XDCS_DEVICE_FLAG_INUSE);
|
|
//
|
|
// Check if the device was removed while we were using it. If it was
|
|
// need to complete the remove.
|
|
//
|
|
if(!TEST_FLAG(XdcsDevice->bFlags, XDCS_DEVICE_FLAG_CONNECTED))
|
|
{
|
|
XdcsDevice->Device->SetExtension(NULL);
|
|
XdcsDevice->Device->RemoveComplete();
|
|
XdcsDevice->Device = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
LOCALFUNC
|
|
VOID
|
|
XDCS_WatchDogTimeout (
|
|
IN PKDPC Dpc,
|
|
IN PVOID Context,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This is the DPC routine for the XDCS watchdog timer. It protects
|
|
against hung requests, NAK forever.
|
|
|
|
Whenever it fires it cancels all outstanding URBs.
|
|
|
|
--*/
|
|
{
|
|
|
|
USB_DBG_WARN_PRINT(("XDCS timed out."));
|
|
|
|
CLEAR_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_WATCHDOG_RUNNING);
|
|
|
|
PXDCS_DEVICE xdcsDevice = NULL;
|
|
if(XDCS_Globals.CurrentDownloadRequest)
|
|
{
|
|
xdcsDevice = &XDCS_Globals.Devices[
|
|
PORT_FROM_DWORD_INSTANCE(XDCS_Globals.CurrentDownloadRequest->dwDeviceInstance)
|
|
];
|
|
|
|
if(TEST_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_URB1_OUTSTANDING))
|
|
{
|
|
xdcsDevice->Device->CancelRequest(&XDCS_Globals.Urb1);
|
|
}
|
|
if(TEST_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_URB2_OUTSTANDING))
|
|
{
|
|
xdcsDevice->Device->CancelRequest(&XDCS_Globals.Urb2);
|
|
}
|
|
}
|
|
|
|
if(TEST_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_ENUM_URB_OUTSTANDING))
|
|
{
|
|
for(DWORD dwPort=0; dwPort<XDCS_MAX_PORTS; dwPort++)
|
|
{
|
|
if( TEST_FLAG(XDCS_Globals.Devices[dwPort].bFlags, XDCS_DEVICE_FLAG_ENUMERATING) )
|
|
{
|
|
XDCS_Globals.Devices[dwPort].Device->CancelRequest(&XDCS_Globals.UrbEnum);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
LOCALFUNC
|
|
VOID
|
|
XDCS_StartExtendWatchdog()
|
|
{
|
|
LARGE_INTEGER timeout;
|
|
timeout.QuadPart = XDCS_REQUEST_TIMEOUT;
|
|
SET_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_WATCHDOG_RUNNING);
|
|
KeSetTimer(&XDCS_Globals.WatchDogTimer, timeout, &XDCS_Globals.WatchDogTimerDpc);
|
|
}
|
|
|
|
LOCALFUNC VOID XDCS_StopWatchdog()
|
|
{
|
|
if(!TEST_ANY_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_URBS_OUTSTANDING))
|
|
{
|
|
KeCancelTimer(&XDCS_Globals.WatchDogTimer);
|
|
CLEAR_FLAG(XDCS_Globals.ulGlobalFlags, XDCS_GLOBAL_FLAG_WATCHDOG_RUNNING);
|
|
}
|
|
}
|