945 lines
29 KiB
C++
945 lines
29 KiB
C++
/*++
|
|
|
|
Copyright (c) 1996-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
MU.CPP
|
|
|
|
Abstract:
|
|
|
|
This source file implements the major entry points to the MU driver.
|
|
|
|
Entries required by the USB core stack:
|
|
MU_Init
|
|
MU_AddDevice
|
|
MU_RemoveDevice
|
|
Entries used by XAPI for making MU's accessible.
|
|
|
|
|
|
|
|
Environment:
|
|
|
|
Xbox USB class driver.
|
|
|
|
Revision History:
|
|
|
|
George Chrysanthakopoulos (georgioc) wrote the original several months ago, loosely based
|
|
on the existing Win2k USB mass storage driver.
|
|
|
|
10-18-00 : Mitchell Dernis(mitchd) - systematic code and comment cleanup, now that all
|
|
the major architectural issues are resolved.
|
|
11-08-00 : Mitchell Dernis(mitchd) - change when DeviceObject are allocated
|
|
|
|
--*/
|
|
|
|
//*****************************************************************************
|
|
// I N C L U D E S
|
|
//*****************************************************************************
|
|
#include "mu.h"
|
|
#include <stdio.h>
|
|
|
|
//*****************************************************************************
|
|
// Global Declarations
|
|
//*****************************************************************************
|
|
MU_DRIVER_EXTENSION MU_DriverExtension;
|
|
|
|
DRIVER_OBJECT MU_DriverObject = {
|
|
MU_StartIo, // DriverStartIo
|
|
NULL, // DriverDeleteDevice
|
|
NULL, // DriverDismountVolume
|
|
{
|
|
IoInvalidDeviceRequest, // IRP_MJ_CREATE
|
|
IoInvalidDeviceRequest, // IRP_MJ_CLOSE
|
|
MU_InternalIo, // IRP_MJ_READ
|
|
MU_InternalIo, // IRP_MJ_WRITE
|
|
IoInvalidDeviceRequest, // IRP_MJ_QUERY_INFORMATION
|
|
IoInvalidDeviceRequest, // IRP_MJ_SET_INFORMATION
|
|
IoInvalidDeviceRequest, // IRP_MJ_FLUSH_BUFFERS
|
|
IoInvalidDeviceRequest, // IRP_MJ_QUERY_VOLUME_INFORMATION
|
|
IoInvalidDeviceRequest, // IRP_MJ_DIRECTORY_CONTROL
|
|
IoInvalidDeviceRequest, // IRP_MJ_FILE_SYSTEM_CONTROL
|
|
MU_InternalIo, // IRP_MJ_DEVICE_CONTROL
|
|
MU_InternalIo, // IRP_MJ_INTERNAL_DEVICE_CONTROL
|
|
IoInvalidDeviceRequest, // IRP_MJ_SHUTDOWN
|
|
IoInvalidDeviceRequest, // IRP_MJ_CLEANUP
|
|
}
|
|
};
|
|
|
|
//This is exposed for XAPI so that XMountMU can check if the user tries to exceed it.
|
|
#if DBG
|
|
extern "C" ULONG MU_MaxUserDevices = MU_DEFAULT_MAX_MOUNTED;
|
|
#endif
|
|
|
|
//*****************************************************************************
|
|
// Local Function Declarations
|
|
//*****************************************************************************
|
|
DEFINE_USB_DEBUG_FUNCTIONS("MU");
|
|
|
|
VOID
|
|
FASTCALL
|
|
MU_fCloseEndpoints (
|
|
IN PMU_DEVICE_EXTENSION DeviceExtension
|
|
);
|
|
|
|
VOID
|
|
MU_CloseEndpointCompletion (
|
|
IN PURB Urb,
|
|
IN PVOID Context
|
|
);
|
|
|
|
|
|
//*****************************************************************************
|
|
// Declare XID types and class.
|
|
//*****************************************************************************
|
|
DECLARE_XPP_TYPE(XDEVICE_TYPE_MEMORY_UNIT)
|
|
USB_DEVICE_TYPE_TABLE_BEGIN(MU_)
|
|
USB_DEVICE_TYPE_TABLE_ENTRY(XDEVICE_TYPE_MEMORY_UNIT)
|
|
USB_DEVICE_TYPE_TABLE_END()
|
|
USB_CLASS_DRIVER_DECLARATION(MU_, USB_DEVICE_CLASS_STORAGE, 0xFF, 0xFF)
|
|
#pragma data_seg(".XPP$ClassMU")
|
|
USB_CLASS_DECLARATION_POINTER(MU_)
|
|
#pragma data_seg(".XPP$Data")
|
|
|
|
USB_RESOURCE_REQUIREMENTS MU_gResourceRequirements =
|
|
{USB_CONNECTOR_TYPE_LOW_POWER, 0, 0, 1, 2, 0, 0, MU_BULK_MIN_TD_QUOTA, 0, 0};
|
|
|
|
//*****************************************************************************
|
|
// Implementation of USB Required Entry Points:
|
|
// MU_Init,
|
|
// MU_AddDevice,
|
|
// MU_RemoveDevice
|
|
//*****************************************************************************
|
|
#pragma code_seg(".XPPCINIT")
|
|
EXTERNUSB VOID MU_Init (IUsbInit *pUsbInit)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Called by the USB core stack if the MU driver's description table is
|
|
linked in to the application.
|
|
--*/
|
|
{
|
|
ULONG portCount;
|
|
ULONG maxDeviceCount;
|
|
ULONG maxOpenCount;
|
|
ULONG allocSize;
|
|
ULONG index;
|
|
OBJECT_STRING deviceObjectName;
|
|
PMU_DEVICE_EXTENSION deviceExtension;
|
|
PDEVICE_OBJECT deviceObject;
|
|
|
|
USB_DBG_ENTRY_PRINT(("entering MU_Init\n"));
|
|
|
|
//
|
|
// The "MU_DriverExtension" is just a global. Initialize it.
|
|
//
|
|
|
|
RtlZeroMemory(&MU_DriverExtension,sizeof(MU_DRIVER_EXTENSION));
|
|
InitializeListHead(&MU_DriverExtension.PartialRequestQueue);
|
|
|
|
//
|
|
// Calculate maxDeviceCount and allocate the array of devices.
|
|
//
|
|
|
|
portCount = XGetPortCount();
|
|
maxDeviceCount = portCount * MU_CONTROLLERS_PER_PORT;
|
|
|
|
//
|
|
// Allocate an array of pointers to hold the nodes (MU_INSTANCES)
|
|
//
|
|
|
|
allocSize = sizeof(MU_INSTANCE)*maxDeviceCount;
|
|
MU_DriverExtension.Nodes = (PMU_INSTANCE) RTL_ALLOCATE_HEAP(allocSize);
|
|
ASSERT(MU_DriverExtension.Nodes); //This allocation happens at boot, it better not fail.
|
|
RtlZeroMemory(MU_DriverExtension.Nodes, allocSize);
|
|
|
|
//
|
|
// Register the user selected devices
|
|
//
|
|
if(pUsbInit->UseDefaultCount())
|
|
{
|
|
maxOpenCount = MU_DEFAULT_MAX_MOUNTED;
|
|
} else
|
|
{
|
|
maxOpenCount = pUsbInit->GetMaxDeviceTypeCount(XDEVICE_TYPE_MEMORY_UNIT);
|
|
RIP_ON_NOT_TRUE_WITH_MESSAGE(maxOpenCount <= maxDeviceCount, "XInitDevices: requested more XDEVICE_TYPE_MEMORY_UNIT than available ports.");
|
|
|
|
// Save the number of devices that the user requested so we can RIP in debug versions of XMountMU if the user tries to exceed this.
|
|
#if DBG
|
|
MU_MaxUserDevices = maxOpenCount;
|
|
#endif
|
|
|
|
// Bump up the maximum opened count. This is because API's such as XGetMUNameFromPortAndSlot,
|
|
// and XReadMUMetaData may need to mount an MU behind the game's back, the game has not already mounted it.
|
|
// These API's are smart enough never to mount more than one at a time beyond what the game has mounted.
|
|
maxOpenCount++;
|
|
// The maximum count never needs to exceed the maximum physical devices.
|
|
if(maxOpenCount > maxDeviceCount) maxOpenCount = maxDeviceCount;
|
|
}
|
|
|
|
//
|
|
// Allocate all the device objects and place them on a free list
|
|
//
|
|
RtlInitObjectString(&deviceObjectName, OTEXT("\\Device\\MU_0"));
|
|
for(index = 0; index < maxOpenCount; index++)
|
|
{
|
|
NTSTATUS status;
|
|
deviceObjectName.Buffer[sizeof("\\Device\\MU")] = (index > 9) ? (index + OTEXT('A')) : (index + OTEXT('0'));
|
|
status = IoCreateDevice(
|
|
&MU_DriverObject,
|
|
sizeof(MU_DEVICE_EXTENSION),
|
|
&deviceObjectName,
|
|
FILE_DEVICE_MEMORY_UNIT,
|
|
FALSE,
|
|
&deviceObject
|
|
);
|
|
if(NT_SUCCESS(status))
|
|
{
|
|
|
|
deviceExtension = (PMU_DEVICE_EXTENSION)deviceObject->DeviceExtension;
|
|
RtlZeroMemory(deviceExtension, sizeof(MU_DEVICE_EXTENSION));
|
|
deviceExtension->DeviceObject = deviceObject;
|
|
deviceExtension->DeviceObjectNumber = index;
|
|
deviceExtension->DeviceFlags = DF_REMOVED;
|
|
deviceObject->StackSize = 1;
|
|
deviceObject->Flags |= DO_DIRECT_IO;
|
|
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
MU_ReleaseDeviceObject(deviceExtension);
|
|
} else break;
|
|
}
|
|
|
|
#if DBG
|
|
if(index < maxOpenCount)
|
|
{
|
|
RIP("XInitDevices: Memory Unit failed to allocate memory all requested instances.");
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Register the resources for the support device count
|
|
//
|
|
MU_gResourceRequirements.MaxDevices = maxOpenCount;
|
|
if(maxOpenCount > 1) MU_gResourceRequirements.MaxBulkTDperTransfer *= 2;
|
|
pUsbInit->RegisterResources(&MU_gResourceRequirements);
|
|
|
|
//
|
|
// Xbox has no concept of drivers failing load
|
|
// so there is no point in returning an error code
|
|
// other than STATUS_SUCCESS
|
|
//
|
|
|
|
USB_DBG_EXIT_PRINT(("exiting MU_Init\n"));
|
|
|
|
return;
|
|
}
|
|
#pragma code_seg(".XPPCODE")
|
|
|
|
EXTERNUSB VOID MU_AddDevice(IN IUsbDevice *Device)
|
|
/*++
|
|
Routine Description:
|
|
Standard USB class driver AddDevice routine.
|
|
|
|
Verifies that MU is supported.
|
|
|
|
Claims a preallocated MU_INSTANCE, and caches
|
|
enumtime information needed later.
|
|
|
|
Connects the IUsbDevice to the MU_INSTANCE.
|
|
|
|
Arguments:
|
|
Device - Interface to USB core stack for device.
|
|
|
|
Notes:
|
|
AddDevice routines are always called at DPC level.
|
|
--*/
|
|
{
|
|
PMU_INSTANCE muInstance;
|
|
ULONG port;
|
|
ULONG slot;
|
|
const USB_ENDPOINT_DESCRIPTOR *endpointDescriptor;
|
|
|
|
USB_DBG_ENTRY_PRINT(("MU_AddDevice(Device=0x%0.8x)", Device));
|
|
|
|
//
|
|
// Get the device port and slot
|
|
//
|
|
|
|
port = Device->GetPort();
|
|
if(port >= 16)
|
|
{
|
|
port -= 16;
|
|
slot = 1;
|
|
} else
|
|
{
|
|
slot = 0;
|
|
}
|
|
USB_DBG_TRACE_PRINT(("Memory Unit Added: Port = %d, Slot = %d", port, slot));
|
|
|
|
//
|
|
// Choose the correct MU instance object
|
|
//
|
|
|
|
muInstance = &MU_DriverExtension.Nodes[NODE_INDEX_FROM_PORT_AND_SLOT(port,slot)];
|
|
ASSERT(1==++muInstance->AddRemoveCount);
|
|
//
|
|
// get the endpoint descriptor for bulk-IN
|
|
//
|
|
|
|
endpointDescriptor = Device->GetEndpointDescriptor(USB_ENDPOINT_TYPE_BULK, TRUE, 0);
|
|
if(endpointDescriptor)
|
|
{
|
|
|
|
muInstance->BulkInEndpointAddress = endpointDescriptor->bEndpointAddress;
|
|
|
|
//
|
|
// Verify wMaxPacketSize, for Xbox performance reasons, we require
|
|
// MU_MAX_BULK_PACKET_SIZE (64 bytes). Furthermore, assumptions are
|
|
// made regarding max packet size based on 64 bytes. So this really
|
|
// is a requirement.
|
|
//
|
|
|
|
if (endpointDescriptor->wMaxPacketSize != MU_MAX_BULK_PACKET_SIZE)
|
|
{
|
|
USB_DBG_WARN_PRINT(("unsupported device: wMaxPacketSize is incorrect for the bulk-IN endpoint."));
|
|
Device->AddComplete(USBD_STATUS_UNSUPPORTED_DEVICE);
|
|
return;
|
|
}
|
|
|
|
} else
|
|
{
|
|
USB_DBG_WARN_PRINT(("unsupported device: endpoint descriptor not found for bulk-IN."));
|
|
Device->AddComplete(USBD_STATUS_UNSUPPORTED_DEVICE);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// get the endpoint descriptor for bulk-OUT
|
|
//
|
|
|
|
endpointDescriptor = Device->GetEndpointDescriptor(USB_ENDPOINT_TYPE_BULK, FALSE, 0);
|
|
if (endpointDescriptor)
|
|
{
|
|
muInstance->BulkOutEndpointAddress = endpointDescriptor->bEndpointAddress;
|
|
|
|
//
|
|
// Verify wMaxPacketSize, see comments a few lines above regarding wMaxPacketSize
|
|
// for the bulk-IN endpoint.
|
|
//
|
|
|
|
if (endpointDescriptor->wMaxPacketSize != MU_MAX_BULK_PACKET_SIZE)
|
|
{
|
|
USB_DBG_WARN_PRINT(("unsupported device: wMaxPacketSize is incorrect for the bulk-OUT endpoint."));
|
|
Device->AddComplete(USBD_STATUS_UNSUPPORTED_DEVICE);
|
|
return;
|
|
}
|
|
|
|
} else {
|
|
USB_DBG_WARN_PRINT(("unsupported device: endpoint descriptor not found for bulk-OUT."));
|
|
Device->AddComplete(USBD_STATUS_UNSUPPORTED_DEVICE);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Set our muInstance in our IUsbDevice
|
|
//
|
|
|
|
Device->SetExtension(muInstance);
|
|
|
|
//
|
|
// save our IUsbDevice pointer
|
|
//
|
|
|
|
muInstance->Device = Device;
|
|
muInstance->InterfaceNumber = Device->GetInterfaceNumber();
|
|
|
|
//
|
|
// set the class specific type so that it gets enumerated to applications.
|
|
//
|
|
|
|
Device->SetClassSpecificType(0);
|
|
Device->AddComplete(USBD_STATUS_SUCCESS);
|
|
}
|
|
|
|
EXTERNUSB VOID
|
|
MU_RemoveDevice (
|
|
IN IUsbDevice *Device
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Standard USB class driver RemoveDevice routine.
|
|
|
|
There are two possibilities: the device is in use (has a
|
|
DeviceObject attached and the endpoints are opened), or
|
|
the device is not in use. In the former case, we mark
|
|
the device as DF_PENDING_REMOVE, and kick off the state
|
|
machine to close the endpoints. The last step of that state
|
|
machine will see DF_PENDING_REMOVE and call Device->RemoveComplete()
|
|
for us. In the latter case, we can just call Device->RemoveComplete().
|
|
|
|
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(("MU_RemoveDevice(Device=0x%0.8x)", Device));
|
|
|
|
PMU_INSTANCE muInstance = (PMU_INSTANCE) Device->GetExtension();
|
|
PMU_DEVICE_EXTENSION deviceExtension = muInstance->DeviceExtension;
|
|
ASSERT(0==--muInstance->AddRemoveCount);
|
|
|
|
//
|
|
// If the MU_INSTANCE is currently attached to a device object
|
|
// for the current muInstance, then the endpoints are open and need to
|
|
// be closed before we can complete the remove. However, it is possible
|
|
// that the deviceExtension is for a previous incarnation of this
|
|
// muInstance (a.k.a. the MU was removed, added, and now is being removed
|
|
// again, without the device object ever having been closed). In this
|
|
// bizarre circumstance, we can just complete the remove.
|
|
//
|
|
if(deviceExtension && !TEST_FLAG(deviceExtension->DeviceFlags, DF_REMOVED))
|
|
{
|
|
//
|
|
// Mark the device remove pending and let the close
|
|
// endpoint state machine take care of the rest.
|
|
//
|
|
SET_FLAG(deviceExtension->DeviceFlags, DF_PENDING_REMOVE);
|
|
MU_fCloseEndpoints(deviceExtension);
|
|
} else
|
|
//
|
|
// Otherwise, the device is not in use, we can just complete the
|
|
// remove right here.
|
|
//
|
|
{
|
|
muInstance->Device->RemoveComplete();
|
|
muInstance->Device = NULL;
|
|
}
|
|
|
|
USB_DBG_EXIT_PRINT(("exiting MU_RemoveDevice"));
|
|
|
|
}
|
|
|
|
//*****************************************************************************
|
|
// Implementation of XAPI Entry Points:
|
|
// MU_CreateDeviceObject,
|
|
// MU_CloseDeviceObject
|
|
//*****************************************************************************
|
|
|
|
NTSTATUS
|
|
MU_CreateDeviceObject(
|
|
IN ULONG Port,
|
|
IN ULONG Slot,
|
|
IN POBJECT_STRING DeviceName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This routine is called to expose an MU as a device object. It is called by XMountMU,
|
|
and it also must be called to open a device with the RAW filesystem, which is used for
|
|
formatting and some low-level tests.
|
|
|
|
All endpoint handles are opened at them same time.
|
|
|
|
Arguments:
|
|
|
|
Port - port of MU to open
|
|
Slot - slot of MU to open
|
|
DeviceName - name to give device object
|
|
|
|
Notes:
|
|
|
|
MU_CloseDeviceObject reverses MU_CreateDeviceObject
|
|
freeing all memory.
|
|
|
|
MU_CreateDeviceObject should only be called once on a particular port and slot.
|
|
--*/
|
|
{
|
|
PMU_INSTANCE muInstance;
|
|
PMU_DEVICE_EXTENSION deviceExtension;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
USBD_STATUS usbdStatus;
|
|
KIRQL oldIrql;
|
|
|
|
USB_DBG_ENTRY_PRINT(("MU_CreateDeviceObject(Port=0x%0.8x, Slot=0x%0.8x)", Port, Slot));
|
|
|
|
//
|
|
// ASSERT arguments.
|
|
//
|
|
|
|
ASSERT(Port < XGetPortCount());
|
|
ASSERT((Slot == XDEVICE_TOP_SLOT) || (Slot == XDEVICE_BOTTOM_SLOT));
|
|
|
|
//
|
|
// Choose the correct MU instance object
|
|
//
|
|
|
|
muInstance = &MU_DriverExtension.Nodes[NODE_INDEX_FROM_PORT_AND_SLOT(Port,Slot)];
|
|
|
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
|
|
|
deviceExtension = MU_AcquireDeviceObject();
|
|
|
|
if(NULL == deviceExtension)
|
|
{
|
|
KeLowerIrql(oldIrql);
|
|
RIP("XMountMu() - exceeded number of devices requested with XInitDevices");
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Verify that the device is connected.
|
|
//
|
|
if(!muInstance->Device)
|
|
{
|
|
MU_ReleaseDeviceObject(deviceExtension);
|
|
KeLowerIrql(oldIrql);
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
//
|
|
// Point the MU_INSTANCE to the device extension
|
|
// and vice versa. Now the failure processing
|
|
// can survive the normal close processing, even
|
|
// if the reset of open fails.
|
|
//
|
|
ASSERT(NULL == muInstance->DeviceExtension);
|
|
muInstance->DeviceExtension = deviceExtension;
|
|
deviceExtension->MuInstance = muInstance;
|
|
|
|
//
|
|
// Initialize the DPC object and mrb timeouts
|
|
//
|
|
|
|
KeInitializeDpc(&deviceExtension->Mrb.TimeoutDpcObject, MU_MrbTimeout, deviceExtension);
|
|
KeInitializeTimer(&deviceExtension->Mrb.Timer);
|
|
|
|
//
|
|
// Initialize the debug watchdog parameters
|
|
//
|
|
MU_DEBUG_INIT_WATCHDOG_PARAMETERS(deviceExtension);
|
|
|
|
//
|
|
// Initialize the close event.
|
|
//
|
|
|
|
KeInitializeEvent(&deviceExtension->CloseEvent, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// Open the default endpoint
|
|
//
|
|
|
|
USB_BUILD_OPEN_DEFAULT_ENDPOINT(&deviceExtension->Urb.OpenEndpoint);
|
|
usbdStatus = muInstance->Device->SubmitRequest(&deviceExtension->Urb);
|
|
if(USBD_SUCCESS(usbdStatus))
|
|
{
|
|
SET_FLAG(deviceExtension->DeviceFlags,DF_DEFAULT_ENDPOINT_OPEN);
|
|
} else
|
|
{
|
|
status = IUsbDevice::NtStatusFromUsbdStatus(usbdStatus);
|
|
USB_DBG_WARN_PRINT(("Failed to open the default endpoint for an MU: usbdStatus = 0x%0.8x", usbdStatus));
|
|
goto SkipRemainingEndpointOpens;
|
|
}
|
|
|
|
//
|
|
// Open the Bulk-IN endpoint
|
|
//
|
|
USB_BUILD_OPEN_ENDPOINT(
|
|
&deviceExtension->Urb.OpenEndpoint,
|
|
muInstance->BulkInEndpointAddress,
|
|
USB_ENDPOINT_TYPE_BULK,
|
|
MU_MAX_BULK_PACKET_SIZE,
|
|
0 //interval unimportant for bulk endpoint
|
|
);
|
|
usbdStatus = muInstance->Device->SubmitRequest(&deviceExtension->Urb);
|
|
if(USBD_SUCCESS(usbdStatus))
|
|
{
|
|
SET_FLAG(deviceExtension->DeviceFlags,DF_BULK_IN_ENDPOINT_OPEN);
|
|
deviceExtension->BulkInEndpointHandle = deviceExtension->Urb.OpenEndpoint.EndpointHandle;
|
|
} else
|
|
{
|
|
status = IUsbDevice::NtStatusFromUsbdStatus(usbdStatus);
|
|
USB_DBG_WARN_PRINT(("Failed to open the bulk-IN endpoint for an MU: usbdStatus = 0x%0.8x", usbdStatus));
|
|
goto SkipRemainingEndpointOpens;
|
|
}
|
|
|
|
//
|
|
// Open the Bulk-OUT endpoint (if needed)
|
|
//
|
|
|
|
USB_BUILD_OPEN_ENDPOINT(
|
|
&deviceExtension->Urb.OpenEndpoint,
|
|
muInstance->BulkOutEndpointAddress,
|
|
USB_ENDPOINT_TYPE_BULK,
|
|
MU_MAX_BULK_PACKET_SIZE,
|
|
0
|
|
);
|
|
usbdStatus = muInstance->Device->SubmitRequest(&deviceExtension->Urb);
|
|
if(USBD_SUCCESS(usbdStatus))
|
|
{
|
|
SET_FLAG(deviceExtension->DeviceFlags,DF_BULK_OUT_ENDPOINT_OPEN);
|
|
deviceExtension->BulkOutEndpointHandle = deviceExtension->Urb.OpenEndpoint.EndpointHandle;
|
|
} else
|
|
{
|
|
status = IUsbDevice::NtStatusFromUsbdStatus(usbdStatus);
|
|
USB_DBG_WARN_PRINT(("Failed to open the bulk-OUT endpoint for an MU: usbdStatus = 0x%0.8x", usbdStatus));
|
|
}
|
|
|
|
SkipRemainingEndpointOpens:
|
|
|
|
KeLowerIrql(oldIrql);
|
|
|
|
//
|
|
// If an error occured, the device may be "half" opened.
|
|
// MU_CloseDeviceObject is smart enough to close a partially
|
|
// opened device, so we will just call it.
|
|
//
|
|
if(NT_ERROR(status))
|
|
{
|
|
MU_CloseDeviceObject(Port, Slot);
|
|
} else
|
|
{
|
|
//generate the device object name in the caller's buffer
|
|
DeviceName->Length = (sizeof(OTEXT("\\Device\\MU_0"))/sizeof(OCHAR))-1;
|
|
ASSERT(DeviceName->MaximumLength-1 >= DeviceName->Length);
|
|
ASSERT(deviceExtension->DeviceObjectNumber < 16);
|
|
soprintf(DeviceName->Buffer, OTEXT("\\Device\\MU_%x"), deviceExtension->DeviceObjectNumber);
|
|
}
|
|
|
|
USB_DBG_EXIT_PRINT(("MU_CreateDeviceObject returning 0x%0.8x" status));
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
MU_CloseDeviceObject(
|
|
IN ULONG Port,
|
|
IN ULONG Slot
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Reverse MU_CreateDeviceObject.
|
|
|
|
Argument:
|
|
DeviceObject - MU DeviceObject to close.
|
|
--*/
|
|
{
|
|
PMU_DEVICE_EXTENSION deviceExtension;
|
|
PMU_INSTANCE muInstance;
|
|
KIRQL oldIrql;
|
|
|
|
USB_DBG_ENTRY_PRINT(("MU_CloseDeviceObject(Port=%d,Slot=%d)", Port, Slot));
|
|
|
|
ASSERT_LESS_THAN_DISPATCH_LEVEL();
|
|
|
|
//
|
|
// ASSERT arguments.
|
|
//
|
|
|
|
ASSERT(Port < XGetPortCount());
|
|
ASSERT((Slot == XDEVICE_TOP_SLOT) || (Slot == XDEVICE_BOTTOM_SLOT));
|
|
|
|
//
|
|
// Choose the correct MU instance object
|
|
//
|
|
|
|
muInstance = &MU_DriverExtension.Nodes[NODE_INDEX_FROM_PORT_AND_SLOT(Port,Slot)];
|
|
|
|
//
|
|
// Raise Irql to synchronize
|
|
//
|
|
|
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
|
|
|
//
|
|
// Get the deviceExtension from the muInstance.
|
|
//
|
|
|
|
deviceExtension = muInstance->DeviceExtension;
|
|
|
|
//
|
|
// Hitting assert probably indicates that MU_CloseDeviceObject was called
|
|
// for an MU that was already closed. Since this is not a
|
|
// public API, we just ASSERT. Fix the calling code!
|
|
//
|
|
|
|
ASSERT(deviceExtension);
|
|
|
|
//
|
|
// If the device extension points back to an active MU_INSTANCE behind this device object,
|
|
// close the endpoints on it first.
|
|
//
|
|
|
|
if(deviceExtension->MuInstance)
|
|
{
|
|
//
|
|
// Mark the device as pending close
|
|
//
|
|
SET_FLAG(deviceExtension->DeviceFlags, DF_PENDING_CLOSE);
|
|
|
|
//
|
|
// Let the close endpoint state machine finish up the work of closing.
|
|
//
|
|
MU_fCloseEndpoints(deviceExtension);
|
|
|
|
//
|
|
// Lower irql for synchronization purposes.
|
|
//
|
|
KeLowerIrql(oldIrql);
|
|
|
|
//
|
|
// Wait for the close endpoint state machine to finish
|
|
//
|
|
KeWaitForSingleObject(&deviceExtension->CloseEvent, Executive, KernelMode, FALSE, NULL);
|
|
|
|
} else
|
|
{
|
|
muInstance->DeviceExtension = NULL;
|
|
KeLowerIrql(oldIrql);
|
|
}
|
|
//
|
|
// The MU instance can now forget about the device object.
|
|
//
|
|
MU_ReleaseDeviceObject(deviceExtension);
|
|
|
|
USB_DBG_EXIT_PRINT(("MU_CloseDeviceObject returning."));
|
|
return;
|
|
};
|
|
|
|
PDEVICE_OBJECT
|
|
MU_GetExistingDeviceObject(
|
|
IN ULONG Port,
|
|
IN ULONG Slot
|
|
)
|
|
{
|
|
PMU_INSTANCE muInstance;
|
|
|
|
USB_DBG_ENTRY_PRINT(("MU_GetExistingDeviceObject(Port=%d,Slot=%d)", Port, Slot));
|
|
|
|
//
|
|
// ASSERT arguments.
|
|
//
|
|
|
|
ASSERT(Port < XGetPortCount());
|
|
ASSERT((Slot == XDEVICE_TOP_SLOT) || (Slot == XDEVICE_BOTTOM_SLOT));
|
|
|
|
//
|
|
// Choose the correct MU instance object
|
|
//
|
|
|
|
muInstance = &MU_DriverExtension.Nodes[NODE_INDEX_FROM_PORT_AND_SLOT(Port,Slot)];
|
|
|
|
//
|
|
// This routine should only be invoked for a MU object that has been
|
|
// created. The caller is responsible for synchronizing calls to create
|
|
// and close MUs with this routine.
|
|
//
|
|
|
|
ASSERT(muInstance->DeviceExtension);
|
|
|
|
return muInstance->DeviceExtension->DeviceObject;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
// Implementation of local functions:
|
|
// MU_fCloseEndpoints
|
|
// MU_CloseEndpointCompletion
|
|
//*****************************************************************************
|
|
VOID
|
|
FASTCALL
|
|
MU_fCloseEndpoints (
|
|
IN PMU_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Enters the close endpoint state machine.
|
|
|
|
It is protected against reentrancy, by checking
|
|
a flag.
|
|
|
|
If an MU_fOpenEndpoints is in progress, it signals
|
|
that MU_fOpenEndpoints should call it when it
|
|
syncs up its new status.
|
|
--*/
|
|
{
|
|
ASSERT_DISPATCH_LEVEL();
|
|
|
|
//
|
|
// If a close is not already in progress, start one
|
|
//
|
|
|
|
if(!TEST_FLAG(DeviceExtension->DeviceFlags, DF_CLOSING_ENDPOINTS))
|
|
{
|
|
SET_FLAG(DeviceExtension->DeviceFlags,DF_CLOSING_ENDPOINTS);
|
|
MU_CloseEndpointCompletion(NULL, DeviceExtension);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
MU_CloseEndpointCompletion (
|
|
PURB Urb,
|
|
PVOID Context
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This is the heart of the close endpoint state machine.
|
|
Each time it is called, it looks for an endpoint that
|
|
needs to be closed, and builds an URB to close it and
|
|
return back to this routine when it is closed. At the
|
|
end of the routine, whichever URB was built is submitted.
|
|
|
|
When all the endpoints are closed, it will get to the
|
|
last else clause. It will complete a remove if one
|
|
was pending, and exit the close endpoint state machine,
|
|
before the URB is submitted (since it build one).
|
|
|
|
--*/
|
|
{
|
|
PMU_DEVICE_EXTENSION deviceExtension = (PMU_DEVICE_EXTENSION) Context;
|
|
PMU_INSTANCE muInstance = deviceExtension->MuInstance;
|
|
|
|
ASSERT_DISPATCH_LEVEL();
|
|
|
|
if(TEST_FLAG(deviceExtension->DeviceFlags, DF_BULK_OUT_ENDPOINT_OPEN))
|
|
//
|
|
// The Bulk-OUT endpoint needs to be closed.
|
|
//
|
|
{
|
|
//
|
|
// Build the URB.
|
|
//
|
|
|
|
USB_BUILD_CLOSE_ENDPOINT(
|
|
&deviceExtension->CloseEndpointUrb,
|
|
deviceExtension->BulkOutEndpointHandle,
|
|
MU_CloseEndpointCompletion,
|
|
deviceExtension
|
|
);
|
|
|
|
//
|
|
// Mark the endpoint closed
|
|
//
|
|
|
|
deviceExtension->BulkOutEndpointHandle = 0;
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags,DF_BULK_OUT_ENDPOINT_OPEN);
|
|
|
|
} else if(TEST_FLAG(deviceExtension->DeviceFlags, DF_BULK_IN_ENDPOINT_OPEN))
|
|
//
|
|
// The Bulk-IN endpoint needs to be closed.
|
|
//
|
|
{
|
|
//
|
|
// Build the URB.
|
|
//
|
|
|
|
USB_BUILD_CLOSE_ENDPOINT(
|
|
&deviceExtension->CloseEndpointUrb,
|
|
deviceExtension->BulkInEndpointHandle,
|
|
MU_CloseEndpointCompletion,
|
|
deviceExtension
|
|
);
|
|
|
|
//
|
|
// Mark the endpoint closed
|
|
//
|
|
|
|
deviceExtension->BulkInEndpointHandle = 0;
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags,DF_BULK_IN_ENDPOINT_OPEN);
|
|
|
|
} else if(TEST_FLAG(deviceExtension->DeviceFlags, DF_DEFAULT_ENDPOINT_OPEN))
|
|
//
|
|
// The default endpoint needs to be closed.
|
|
//
|
|
{
|
|
//
|
|
// Build the URB.
|
|
//
|
|
|
|
USB_BUILD_CLOSE_DEFAULT_ENDPOINT(
|
|
&deviceExtension->CloseEndpointUrb,
|
|
MU_CloseEndpointCompletion,
|
|
deviceExtension
|
|
);
|
|
|
|
//
|
|
// Mark the endpoint closed
|
|
//
|
|
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags, DF_DEFAULT_ENDPOINT_OPEN);
|
|
|
|
} else
|
|
//
|
|
// Our state machine is done closing endpoints
|
|
//
|
|
{
|
|
|
|
//
|
|
// We are no longer closing endpoints.
|
|
//
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags, DF_CLOSING_ENDPOINTS);
|
|
|
|
//
|
|
// This state machine has been invoked because
|
|
// a remove and\or close is pending. In either
|
|
// case, the device object needs to forget about
|
|
// the MU_INSTANCE, but NOT vice versa. The MuInstance
|
|
// only forgets about the device object on close.
|
|
//
|
|
deviceExtension->MuInstance = NULL;
|
|
|
|
//
|
|
// If a close is pending, signal the close event
|
|
// so the close caller that started the state machine
|
|
// can finish up its processing.
|
|
//
|
|
if(TEST_FLAG(deviceExtension->DeviceFlags, DF_PENDING_CLOSE))
|
|
{
|
|
//
|
|
// We can forget about the device object now
|
|
// as it will be deleted by MU_CloseDeviceObject
|
|
// when we signal the close event.
|
|
//
|
|
muInstance->DeviceExtension = NULL;
|
|
KeSetEvent(&deviceExtension->CloseEvent, IO_NO_INCREMENT, FALSE);
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags, DF_PENDING_CLOSE);
|
|
}
|
|
|
|
//
|
|
// If a remove is pending, then process it.
|
|
//
|
|
|
|
if(TEST_FLAG(deviceExtension->DeviceFlags, DF_PENDING_REMOVE))
|
|
{
|
|
//
|
|
// if remove was pending, call remove complete
|
|
//
|
|
|
|
muInstance->Device->RemoveComplete();
|
|
muInstance->Device = NULL;
|
|
|
|
//
|
|
// it is now removed, not remove pending.
|
|
//
|
|
|
|
CLEAR_FLAG(deviceExtension->DeviceFlags, DF_PENDING_REMOVE);
|
|
SET_FLAG(deviceExtension->DeviceFlags, DF_REMOVED);
|
|
}
|
|
|
|
//
|
|
// We are done, so return before submitting the URB
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If we are here, we have built an URB, submit it.
|
|
//
|
|
|
|
muInstance->Device->SubmitRequest((PURB)&deviceExtension->CloseEndpointUrb);
|
|
|
|
return;
|
|
}
|