1571 lines
48 KiB
C
1571 lines
48 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
ohcd.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Basic entry point implementation of the OpenHCI
|
||
|
driver.
|
||
|
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
Designed for XBOX.
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
01-17-00 created by Mitchell Dernis (mitchd)
|
||
|
|
||
|
--*/
|
||
|
|
||
|
|
||
|
//
|
||
|
// Pull in OS headers
|
||
|
//
|
||
|
#include <ntos.h>
|
||
|
#include <pci.h>
|
||
|
|
||
|
|
||
|
//
|
||
|
// Setup the debug information for this file (see ..\inc\debug.h)
|
||
|
//
|
||
|
#define MODULE_POOL_TAG 'DCHO'
|
||
|
#include <debug.h>
|
||
|
DEFINE_USB_DEBUG_FUNCTIONS("OHCD");
|
||
|
|
||
|
|
||
|
//
|
||
|
// Pull in usb headers
|
||
|
//
|
||
|
#include <hcdi.h> //Interface between USBD and HCD
|
||
|
#include "ohcd.h" //Private OHCD stuff
|
||
|
#include "devsys.h" //checks for development systems
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Spew the build settings during compile
|
||
|
//----------------------------------------------------------------------------
|
||
|
#ifdef OHCD_XBOX_HARDWARE_ONLY
|
||
|
#pragma message("OHCD: Xbox Hardware Only Build")
|
||
|
#endif
|
||
|
#if(USB_HOST_CONTROLLER_CONFIGURATION==USB_SINGLE_HOST_CONTROLLER)
|
||
|
#pragma message("OHCD: Single Host Controller Support")
|
||
|
#endif
|
||
|
#if(USB_HOST_CONTROLLER_CONFIGURATION==USB_DUAL_HOST_CONTROLLER_XDK)
|
||
|
#pragma message("OHCD: Dual Host Controller for Silver XDK boxes")
|
||
|
#endif
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Interrupt objects for each HC - this cannot come from the pool,
|
||
|
// because it has code in it, and the pool is not executable.
|
||
|
//----------------------------------------------------------------------------
|
||
|
KINTERRUPT OHCD_InterruptObject[HCD_MAX_HOST_CONTROLLERS];
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Forward declaration of functions defined and used only this in this module
|
||
|
//----------------------------------------------------------------------------
|
||
|
USBD_STATUS
|
||
|
FASTCALL
|
||
|
OHCD_fOpenEndpoint(
|
||
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
IN OUT PURB Urb
|
||
|
);
|
||
|
|
||
|
USBD_STATUS
|
||
|
FASTCALL
|
||
|
OHCD_fCloseEndpoint(
|
||
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
IN OUT PURB Urb
|
||
|
);
|
||
|
USBD_STATUS
|
||
|
FASTCALL
|
||
|
OHCD_fGetEndpointState(
|
||
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
IN OUT PURB Urb
|
||
|
);
|
||
|
|
||
|
USBD_STATUS
|
||
|
FASTCALL
|
||
|
OHCD_fSetEndpointState(
|
||
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
IN OUT PURB Urb
|
||
|
);
|
||
|
USBD_STATUS
|
||
|
FASTCALL
|
||
|
OHCD_fAbortEndpoint(
|
||
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
IN OUT PURB Urb
|
||
|
);
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
OHCD_fSetResources(
|
||
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
PPCI_DEVICE_DESCRIPTOR PciDevice
|
||
|
);
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
OHCD_fDequeueControlTransfer(
|
||
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
IN PURB Urb
|
||
|
);
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
OHCD_fDequeueBulkTransfer(
|
||
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
IN PURB Urb
|
||
|
);
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
OHCD_fDequeueInterruptTransfer(
|
||
|
IN POHCD_ENDPOINT Endpoint,
|
||
|
IN PURB Urb
|
||
|
);
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
OHCD_fCancelQueuedUrbs(
|
||
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
POHCD_ENDPOINT Endpoint
|
||
|
);
|
||
|
|
||
|
VOID
|
||
|
OHCD_ShutDown(
|
||
|
PHAL_SHUTDOWN_REGISTRATION ShutdownRegistration
|
||
|
);
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Implementation of all entry points:
|
||
|
// HCD_DriverEntry
|
||
|
// HCD_NewHostController
|
||
|
// HCD_SubmitRequest
|
||
|
// HCD_CancelRequest
|
||
|
//----------------------------------------------------------------------------
|
||
|
#pragma code_seg(".XPPCINIT")
|
||
|
VOID
|
||
|
HCD_DriverEntry(
|
||
|
IN PHCD_RESOURCE_REQUIREMENTS ResourceRequirements
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Performs one time initialization of driver. Returns the size of the
|
||
|
HCD portion of the extension.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
ResourceRequirements - tells the host controller how many resources
|
||
|
to preallocate.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
USB_DBG_ENTRY_PRINT(("Entering HCD_DriverEntry"));
|
||
|
USB_DBG_TRACE_PRINT(("Built on %s @%s", __DATE__,__TIME__));
|
||
|
|
||
|
OHCD_fPoolInit(ResourceRequirements);
|
||
|
|
||
|
USB_DBG_EXIT_PRINT(("Exiting HCD_DriverEntry"));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#ifdef SILVER
|
||
|
VOID
|
||
|
HCD_EnumHardware()
|
||
|
/*++
|
||
|
Routine Description:
|
||
|
|
||
|
The HCD layer is responsible for calling HAL to find its hardware.
|
||
|
This layer must do it, because only this layer knows the PCI PnP ID
|
||
|
information about the proper host controller type.
|
||
|
|
||
|
We call back USBD_NewHostController which allocates the USBD portion
|
||
|
of the host controller and calls back HCD_NewHostController to
|
||
|
initialize the hardware.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PCI_DEVICE_DESCRIPTOR pciDevices[HCD_MAX_HOST_CONTROLLERS];
|
||
|
ULONG hostControllerIndex=0;
|
||
|
ULONG hostControllerCount=HCD_MAX_HOST_CONTROLLERS;
|
||
|
//
|
||
|
// Initialize pciDevices array to HAL what we are looking for.
|
||
|
//
|
||
|
while(hostControllerCount--)
|
||
|
{
|
||
|
pciDevices[hostControllerCount].Bus=(ULONG)-1;
|
||
|
pciDevices[hostControllerCount].Slot=(ULONG)-1;
|
||
|
pciDevices[hostControllerCount].VendorID=(USHORT)-1;
|
||
|
pciDevices[hostControllerCount].DeviceID=(USHORT)-1;
|
||
|
pciDevices[hostControllerCount].BaseClass=PCI_CLASS_SERIAL_BUS_CTLR;
|
||
|
pciDevices[hostControllerCount].SubClass=PCI_SUBCLASS_SB_USB;
|
||
|
pciDevices[hostControllerCount].ProgIf=OHCI_PCI_PROGRAM_INTERFACE;
|
||
|
}
|
||
|
//
|
||
|
// Ask HAL to find our controllers
|
||
|
//
|
||
|
hostControllerCount=HCD_MAX_HOST_CONTROLLERS;
|
||
|
HalSetupPciDevice(pciDevices, &hostControllerCount);
|
||
|
|
||
|
//
|
||
|
// Kick off initialization by notifying USBD about each device.
|
||
|
// (Be sure not to change the order that we got from HAL.)
|
||
|
//
|
||
|
while(hostControllerIndex < hostControllerCount)
|
||
|
{
|
||
|
USBD_NewHostController(pciDevices+hostControllerIndex, sizeof(OHCD_DEVICE_EXTENSION));
|
||
|
hostControllerIndex++;
|
||
|
}
|
||
|
}
|
||
|
#else //!SILVER
|
||
|
VOID
|
||
|
HCD_EnumHardware()
|
||
|
/*++
|
||
|
Routine Description:
|
||
|
|
||
|
On the real hardware the resource information is just hardcoded.
|
||
|
HCD_MAX_HOST_CONTROLLERS is defined for 1 or 2.
|
||
|
|
||
|
We call back USBD_NewHostController which allocates the USBD portion
|
||
|
of the host controller and calls back HCD_NewHostController to
|
||
|
initialize the hardware.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PCI_DEVICE_DESCRIPTOR pciDevice;
|
||
|
|
||
|
//Just return if this is Xbox MCP version A1. USB cause interrupt storms on this
|
||
|
//revision and doesn't work at all anyway.
|
||
|
if (XboxHardwareInfo->McpRevision == 0xa1) return;
|
||
|
|
||
|
pciDevice.ResourceData.Address[0].Type = CmResourceTypeMemory;
|
||
|
pciDevice.ResourceData.Address[0].u.Memory.Length = XPCICFG_USB0_MEMORY_REGISTER_LENGTH_0;
|
||
|
pciDevice.ResourceData.Address[0].u.Memory.TranslatedAddress = (PVOID)XPCICFG_USB0_MEMORY_REGISTER_BASE_0;
|
||
|
pciDevice.ResourceData.Interrupt.Vector = HalGetInterruptVector(XPCICFG_USB0_IRQ, &pciDevice.ResourceData.Interrupt.Irql);
|
||
|
USBD_NewHostController(&pciDevice, sizeof(OHCD_DEVICE_EXTENSION));
|
||
|
if(1==HCD_MAX_HOST_CONTROLLERS) return;
|
||
|
|
||
|
pciDevice.ResourceData.Address[0].Type = CmResourceTypeMemory;
|
||
|
pciDevice.ResourceData.Address[0].u.Memory.Length = XPCICFG_USB1_MEMORY_REGISTER_LENGTH_0;
|
||
|
pciDevice.ResourceData.Address[0].u.Memory.TranslatedAddress = (PVOID)XPCICFG_USB1_MEMORY_REGISTER_BASE_0;
|
||
|
pciDevice.ResourceData.Interrupt.Vector = HalGetInterruptVector(XPCICFG_USB1_IRQ, &pciDevice.ResourceData.Interrupt.Irql);
|
||
|
USBD_NewHostController(&pciDevice, sizeof(OHCD_DEVICE_EXTENSION));
|
||
|
}
|
||
|
#endif //!SILVER
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
HCD_NewHostController(
|
||
|
IN PVOID HcdExtension,
|
||
|
IN UCHAR HostControllerNumber,
|
||
|
IN PPCI_DEVICE_DESCRIPTOR PciDevice
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
When the USBD goes through HAL
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DeviceObject - device object associated with host controller.
|
||
|
ResourceList - list of resources obtained from PCI bus.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
STATUS_SUCCESS. Otherwise we will KeBugcheck before returning.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
HC_COMMAND_STATUS HcCommandStatus;
|
||
|
KIRQL oldIrql;
|
||
|
POHCD_DEVICE_EXTENSION deviceExtension = (POHCD_DEVICE_EXTENSION)HcdExtension;
|
||
|
HC_FRAME_INTERVAL frameInterval;
|
||
|
BOOLEAN interruptConnected;
|
||
|
POHCI_OPERATIONAL_REGISTERS operationalRegisters = NULL;
|
||
|
|
||
|
//ULONG frameIntervalSniff1;
|
||
|
//ULONG frameIntervalSniff2;
|
||
|
//ULONG frameIntervalSniff3;
|
||
|
//ULONG frameIntervalSniff4;
|
||
|
//ULONG frameIntervalSniff5;
|
||
|
|
||
|
|
||
|
USB_DBG_ENTRY_PRINT(("Entering HCD_NewHostController"));
|
||
|
|
||
|
PROFILE_DECLARE_TIME_STAMP(ResetTiming);
|
||
|
|
||
|
//
|
||
|
// Before we go initializing fields zero the whole extension.
|
||
|
// That way we can use a NULL check on pointers to see if something
|
||
|
// has been initialized yet.
|
||
|
// (Not necessary, USBD does this for us.)
|
||
|
//RtlZeroMemory( (PVOID)deviceExtension, sizeof(OHCD_DEVICE_EXTENSION));
|
||
|
|
||
|
//
|
||
|
// Remember the host controller number, it will come in
|
||
|
// handy when need to search the global TD pool for stuff
|
||
|
// that our hardware lost!
|
||
|
// (one based for USBD, but we want 0 based.)
|
||
|
deviceExtension->HostControllerNumber = --HostControllerNumber;
|
||
|
|
||
|
//
|
||
|
// Set the resources for the card.
|
||
|
//
|
||
|
OHCD_fSetResources(deviceExtension, PciDevice);
|
||
|
|
||
|
//frameIntervalSniff1 = READ_REGISTER_ULONG(&deviceExtension->OperationalRegisters->HcFmInterval.ul);
|
||
|
//
|
||
|
// Do a quick check of version and stuff.
|
||
|
// This should only happen on preliminary development
|
||
|
// system hardware. It is used to verify that
|
||
|
// a development system is using an XBOX compatible
|
||
|
// USB driver. It will also be used on XBOX
|
||
|
// to verify that the hardware is what we think it
|
||
|
// should be.
|
||
|
//
|
||
|
OHCD_DEVSYS_CHECK_HARDWARE(deviceExtension);
|
||
|
|
||
|
//
|
||
|
// On a development system without the final XBOX
|
||
|
// BIOS, there may be an SMM driver or BIOS
|
||
|
// driver using the Host Controller. We need
|
||
|
// to take control.
|
||
|
//
|
||
|
OHCD_DEVSYS_TAKE_CONTROL(deviceExtension);
|
||
|
|
||
|
//
|
||
|
// Get the operational registers
|
||
|
//
|
||
|
operationalRegisters = deviceExtension->OperationalRegisters;
|
||
|
|
||
|
#ifdef OHCD_XBOX_HARDWARE_ONLY
|
||
|
//
|
||
|
// Set up the few root hub registers that should be done while
|
||
|
// the host controller is in the reset state (or before reseting).
|
||
|
//
|
||
|
WRITE_REGISTER_ULONG(&operationalRegisters->HcRhDescriptorA.ul, HC_RH_DESCRIPTOR_A_INIT_XBOX);
|
||
|
WRITE_REGISTER_ULONG(&operationalRegisters->HcRhDescriptorB.ul, HC_RH_DESCRIPTOR_B_INIT_XBOX);
|
||
|
WRITE_REGISTER_ULONG(&operationalRegisters->HcRhStatus.ul, HC_RH_STATUS_INIT_XBOX);
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Initialize the IsrDPC
|
||
|
//
|
||
|
KeInitializeDpc(&deviceExtension->IsrDpc, OHCD_IsrDpc, deviceExtension);
|
||
|
|
||
|
//
|
||
|
// Get the HCCA and the physical address of it
|
||
|
//
|
||
|
deviceExtension->HCCA = OHCD_PoolGetHcca(HostControllerNumber);
|
||
|
|
||
|
//
|
||
|
// Reset the host controller.
|
||
|
//
|
||
|
// TIMING NOTES:
|
||
|
// After writing the reset bit we must wait 10us before setting the hardware registers.
|
||
|
// However, we must complete setup and set it back to operational within 2 ms.
|
||
|
// We can easily do this in under 2ms as along as our quantum doesn't run out so
|
||
|
// raise IRQL to prevent it.
|
||
|
//
|
||
|
//
|
||
|
HcCommandStatus.ul = READ_REGISTER_ULONG(&operationalRegisters->HcCommandStatus.ul);
|
||
|
HcCommandStatus.HostControllerReset = 1;
|
||
|
PROFILE_BEGIN_TIMING(ResetTiming);
|
||
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
||
|
WRITE_REGISTER_ULONG(&operationalRegisters->HcCommandStatus.ul, HcCommandStatus.ul);
|
||
|
|
||
|
//
|
||
|
// Delay for 10 us
|
||
|
//
|
||
|
KeStallExecutionProcessor(10);
|
||
|
|
||
|
//frameIntervalSniff2 = READ_REGISTER_ULONG(&deviceExtension->OperationalRegisters->HcFmInterval.ul);
|
||
|
|
||
|
//
|
||
|
// Set up periodic schedule and HCCA
|
||
|
//
|
||
|
OHCD_ScheduleInitialize(deviceExtension);
|
||
|
|
||
|
//frameIntervalSniff3 = READ_REGISTER_ULONG(&deviceExtension->OperationalRegisters->HcFmInterval.ul);
|
||
|
//
|
||
|
// See header file with HC_CONTROL_REGISTER_START
|
||
|
// it saves a few instruction rather than using the
|
||
|
// bit fields to construct the ULONG.
|
||
|
//
|
||
|
// Basically, we set the state to operational, disable all of the lists,
|
||
|
// and initialize things like wakeup behavior.
|
||
|
//
|
||
|
WRITE_REGISTER_ULONG(&operationalRegisters->HcControl.ul,HC_CONTROL_REGISTER_START|HC_CONTROL_ISOCH_ENABLE_STATE);
|
||
|
|
||
|
|
||
|
//
|
||
|
// Apparently the FullSpeedMaximumPacketSize gets reset when moving to the
|
||
|
// operational state. This is slightly different then what the OpenHCI
|
||
|
// specification seems to imply. I read it has reseting this value to its
|
||
|
// default of 0x2edf when the
|
||
|
// So we moved these couple of lines out of OHCD_ScheduleInitialize
|
||
|
// and put them here.
|
||
|
//
|
||
|
|
||
|
|
||
|
frameInterval.ul = READ_REGISTER_ULONG(&operationalRegisters->HcFmInterval.ul);
|
||
|
//frameIntervalSniff4 = frameInterval.ul;
|
||
|
frameInterval.FrameInterval = OHCI_DEFAULT_FRAME_INTERVAL;
|
||
|
frameInterval.FullSpeedMaximumPacketSize = OHCI_CLOCKS_TO_BITS(OHCI_DEFAULT_FRAME_INTERVAL);
|
||
|
frameInterval.FrameIntervalToggle ^= 1;
|
||
|
WRITE_REGISTER_ULONG(&operationalRegisters->HcFmInterval.ul, frameInterval.ul);
|
||
|
|
||
|
|
||
|
//
|
||
|
// The 2ms time limit is up. If profiling
|
||
|
// is turned let's make sure we made it
|
||
|
// within a comfortable margin.
|
||
|
// We are not worried about being interrupt
|
||
|
// so we can lower the IRQL again.
|
||
|
//
|
||
|
PROFILE_END_TIMING(ResetTiming);
|
||
|
KeLowerIrql(oldIrql);
|
||
|
|
||
|
//frameIntervalSniff5 = READ_REGISTER_ULONG(&deviceExtension->OperationalRegisters->HcFmInterval.ul);
|
||
|
|
||
|
//
|
||
|
// Setup the interrupt vector
|
||
|
//
|
||
|
KeInitializeInterrupt (
|
||
|
&OHCD_InterruptObject[HostControllerNumber],
|
||
|
(PKSERVICE_ROUTINE) OHCD_InterruptService,
|
||
|
(PVOID) deviceExtension,
|
||
|
PciDevice->ResourceData.Interrupt.Vector,
|
||
|
PciDevice->ResourceData.Interrupt.Irql,
|
||
|
LevelSensitive,
|
||
|
TRUE
|
||
|
);
|
||
|
|
||
|
interruptConnected = KeConnectInterrupt(&OHCD_InterruptObject[HostControllerNumber]);
|
||
|
|
||
|
ASSERT(interruptConnected && "Failed to connect to interrupt");
|
||
|
|
||
|
USB_DBG_TRACE_PRINT(("Interrupt Resource Set:"));
|
||
|
USB_DBG_TRACE_PRINT((" Irql(Level) = %d", PciDevice->ResourceData.Interrupt.Irql));
|
||
|
USB_DBG_TRACE_PRINT((" Vector = %d", PciDevice->ResourceData.Interrupt.Vector));
|
||
|
USB_DBG_TRACE_PRINT((" Assigned InterruptObject at 0x%0.8x", &OHCD_InterruptObject[HostControllerNumber]));
|
||
|
USB_DBG_TRACE_PRINT((" IoConnectInterrupt returned 0x%0.8x", ntStatus));
|
||
|
|
||
|
//
|
||
|
// Register for shutdown notification (now that we have an interrupt object,
|
||
|
// but before enabling interrupts.
|
||
|
//
|
||
|
|
||
|
deviceExtension->ShutdownRegistration.NotificationRoutine = OHCD_ShutDown;
|
||
|
deviceExtension->ShutdownRegistration.Priority = 1;
|
||
|
InitializeListHead(&deviceExtension->ShutdownRegistration.ListEntry);
|
||
|
HalRegisterShutdownNotification(&deviceExtension->ShutdownRegistration, TRUE);
|
||
|
|
||
|
//
|
||
|
// Enable Interrupts
|
||
|
//
|
||
|
WRITE_REGISTER_ULONG(&operationalRegisters->HcInterruptEnable,
|
||
|
HCINT_SchedulingOverrun |
|
||
|
HCINT_WritebackDoneHead |
|
||
|
HCINT_UnrecoverableError |
|
||
|
HCINT_FrameNumberOverflow |
|
||
|
HCINT_MasterInterruptEnable
|
||
|
);
|
||
|
|
||
|
//USB_DBG_WARN_PRINT(("frameIntervalSniff1 = 0x%0.8x", frameIntervalSniff1));
|
||
|
//USB_DBG_WARN_PRINT(("frameIntervalSniff2 = 0x%0.8x", frameIntervalSniff2));
|
||
|
//USB_DBG_WARN_PRINT(("frameIntervalSniff3 = 0x%0.8x", frameIntervalSniff3));
|
||
|
//USB_DBG_WARN_PRINT(("frameIntervalSniff4 = 0x%0.8x", frameIntervalSniff4));
|
||
|
//USB_DBG_WARN_PRINT(("frameIntervalSniff5 = 0x%0.8x", frameIntervalSniff5));
|
||
|
|
||
|
//
|
||
|
// Now kick of detection of devices, by initializing the root hub.
|
||
|
//
|
||
|
OHCD_RootHubInitialize(deviceExtension);
|
||
|
|
||
|
|
||
|
USB_DBG_EXIT_PRINT(("Exiting HCD_NewHostController"));
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
#pragma code_seg(".XPPCODE")
|
||
|
|
||
|
USBD_STATUS
|
||
|
HCD_SubmitRequest(
|
||
|
IN PVOID HcdDeviceExtension,
|
||
|
IN PURB Urb
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Process URBs from the USBD layer.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DeviceObject - pointer to a device object
|
||
|
|
||
|
HcdUrb - pointer to a USB Request Block
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
USBD_STATUS usbdStatus = USBD_STATUS_SUCCESS;
|
||
|
POHCD_DEVICE_EXTENSION deviceExtension = (POHCD_DEVICE_EXTENSION)HcdDeviceExtension;
|
||
|
|
||
|
USB_DBG_ENTRY_PRINT(("Entering HCD_SubmitRequest"));
|
||
|
switch (Urb->Header.Function)
|
||
|
{
|
||
|
//
|
||
|
// Open Endpoint and Close Endpoint IRPs are serialized
|
||
|
// within USBD so we can execute them now.
|
||
|
//
|
||
|
case URB_FUNCTION_OPEN_ENDPOINT:
|
||
|
USB_DBG_TRACE_PRINT(("URB_FUNCTION_HCD_OPEN_ENDPOINT"));
|
||
|
usbdStatus = OHCD_fOpenEndpoint(deviceExtension, Urb);
|
||
|
break;
|
||
|
case URB_FUNCTION_CLOSE_ENDPOINT:
|
||
|
USB_DBG_TRACE_PRINT(("URB_FUNCTION_HCD_CLOSE_ENDPOINT"));
|
||
|
usbdStatus = OHCD_fCloseEndpoint(deviceExtension, Urb);
|
||
|
break;
|
||
|
case URB_FUNCTION_CONTROL_TRANSFER:
|
||
|
case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
|
||
|
USB_DBG_TRACE_PRINT(("URB_FUNCTION_<XXXX>_TRANSFER"));
|
||
|
ASSERT(Urb->CommonTransfer.EndpointHandle);
|
||
|
usbdStatus = OHCD_fQueueTransferRequest(deviceExtension, Urb);
|
||
|
break;
|
||
|
case URB_FUNCTION_GET_FRAME_NUMBER:
|
||
|
USB_DBG_TRACE_PRINT(("URB_FUNCTION_GET_CURRENT_FRAME_NUMBER"));
|
||
|
Urb->GetFrame.FrameNumber = OHCD_Get32BitFrameNumber(deviceExtension);
|
||
|
usbdStatus = USBD_STATUS_SUCCESS;
|
||
|
break;
|
||
|
case URB_FUNCTION_GET_ENDPOINT_STATE:
|
||
|
USB_DBG_TRACE_PRINT(("URB_FUNCTION_HCD_GET_ENDPOINT_STATE"));
|
||
|
usbdStatus=OHCD_fGetEndpointState(deviceExtension, Urb);
|
||
|
break;
|
||
|
case URB_FUNCTION_SET_ENDPOINT_STATE:
|
||
|
USB_DBG_TRACE_PRINT(("URB_FUNCTION_HCD_SET_ENDPOINT_STATE"));
|
||
|
usbdStatus=OHCD_fSetEndpointState(deviceExtension, Urb);
|
||
|
break;
|
||
|
case URB_FUNCTION_ABORT_ENDPOINT:
|
||
|
USB_DBG_TRACE_PRINT(("URB_FUNCTION_HCD_ABORT_ENDPOINT"));
|
||
|
usbdStatus=OHCD_fAbortEndpoint(deviceExtension, Urb);
|
||
|
break;
|
||
|
case URB_FUNCTION_ISOCH_OPEN_ENDPOINT:
|
||
|
usbdStatus=OHCD_ISOCH_OPEN_ENDPOINT(deviceExtension, Urb);
|
||
|
break;
|
||
|
case URB_FUNCTION_ISOCH_CLOSE_ENDPOINT:
|
||
|
usbdStatus=OHCD_ISOCH_CLOSE_ENDPOINT(deviceExtension, Urb);
|
||
|
break;
|
||
|
case URB_FUNCTION_ISOCH_ATTACH_BUFFER:
|
||
|
usbdStatus=OHCD_ISOCH_ATTACH_BUFFERS(deviceExtension, Urb);
|
||
|
break;
|
||
|
case URB_FUNCTION_ISOCH_START_TRANSFER:
|
||
|
usbdStatus=OHCD_ISOCH_START_TRANSFER(deviceExtension, Urb);
|
||
|
break;
|
||
|
case URB_FUNCTION_ISOCH_STOP_TRANSFER:
|
||
|
usbdStatus=OHCD_ISOCH_STOP_TRANSFER(deviceExtension, Urb);
|
||
|
break;
|
||
|
default:
|
||
|
ASSERT(FALSE && "Unsupported URB");
|
||
|
usbdStatus = USBD_STATUS_INVALID_URB_FUNCTION;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we are not returning a pending status
|
||
|
// than we should call CompleteUsbRequest.
|
||
|
//
|
||
|
if(!USBD_PENDING(usbdStatus ))
|
||
|
{
|
||
|
Urb->Header.Status = usbdStatus;
|
||
|
USBD_CompleteRequest(Urb);
|
||
|
}
|
||
|
USB_DBG_EXIT_PRINT(("Exiting HCD_SubmitRequest: usbdStatus = 0x%0.8x", usbdStatus));
|
||
|
return usbdStatus;
|
||
|
}
|
||
|
|
||
|
|
||
|
USBD_STATUS
|
||
|
HCD_CancelRequest(
|
||
|
IN PVOID HcdDeviceExtension,
|
||
|
IN PURB Urb
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Entry point called by USBD to cancel a transfer request.
|
||
|
|
||
|
The URB could be either in a queue waiting to be programmed,
|
||
|
or already programmed.
|
||
|
|
||
|
If it is in a queue, we can find it, remove it, and complete
|
||
|
as canceled right here.
|
||
|
|
||
|
If it has been programmed, then we need to pause the endpoint,
|
||
|
and place the Urb on the PendingCancels list. On the next
|
||
|
frame, we can access the endpoint from the DPC and cancel the URB.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DeviceExtension - DeviceObject for this USB controller.
|
||
|
|
||
|
HcdUrb - Pointer to previously submitted URB to cancel
|
||
|
|
||
|
Return Value:
|
||
|
USBD_STATUS_CANCELED - Urb has been canceled.
|
||
|
USBD_STATUS_CANCELING - Urb is in process of being canceled.
|
||
|
--*/
|
||
|
{
|
||
|
POHCD_ENDPOINT endpoint;
|
||
|
USBD_STATUS status = USBD_STATUS_CANCELED;
|
||
|
POHCD_DEVICE_EXTENSION deviceExtension = (POHCD_DEVICE_EXTENSION)HcdDeviceExtension;
|
||
|
KIRQL oldIrql;
|
||
|
|
||
|
USB_DBG_ENTRY_PRINT(("Entering HCD_CancelRequest"));
|
||
|
//
|
||
|
// Only control, bulk and interrupt transfers can be cancelled.
|
||
|
// if the URB is not a transfer (or an isochronous transfer).
|
||
|
// There is a bug in the caller's code (USBD, or class driver).
|
||
|
//
|
||
|
ASSERT(
|
||
|
(URB_FUNCTION_CONTROL_TRANSFER == Urb->Header.Function) ||
|
||
|
(URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER == Urb->Header.Function)
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// It is not inconceivable that an URB would legitimately get canceled twice.
|
||
|
// Just ignore it if it happens.
|
||
|
//
|
||
|
if(Urb->CommonTransfer.Hca.HcdUrbFlags & OHCD_URB_FLAG_CANCELED)
|
||
|
{
|
||
|
return USBD_STATUS_CANCELING;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We need to be at dispatch level while handling
|
||
|
// queues and stuff.
|
||
|
//
|
||
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
||
|
|
||
|
//
|
||
|
// If the URB has been queued, but not yet programmed
|
||
|
// we can handle everything here and now.
|
||
|
endpoint = (POHCD_ENDPOINT)Urb->CommonTransfer.EndpointHandle;
|
||
|
|
||
|
//
|
||
|
// If the endpoint is in the process of being closed
|
||
|
// ignore the cancel request.
|
||
|
//
|
||
|
if(OHCD_ENDPOINT_FLAG_CLOSING&endpoint->Flags)
|
||
|
{
|
||
|
status = USBD_STATUS_CANCELING;
|
||
|
goto ExitHCD_CancelRequest;
|
||
|
}
|
||
|
|
||
|
if(Urb->CommonTransfer.Hca.HcdUrbFlags & OHCD_URB_FLAG_QUEUED)
|
||
|
{
|
||
|
//
|
||
|
// Dispatch to the proper routine to dequeue the URB
|
||
|
//
|
||
|
switch(endpoint->EndpointType)
|
||
|
{
|
||
|
case USB_ENDPOINT_TYPE_CONTROL:
|
||
|
OHCD_fDequeueControlTransfer(deviceExtension, Urb);
|
||
|
break;
|
||
|
case USB_ENDPOINT_TYPE_BULK:
|
||
|
OHCD_fDequeueBulkTransfer(deviceExtension, Urb);
|
||
|
break;
|
||
|
case USB_ENDPOINT_TYPE_INTERRUPT:
|
||
|
OHCD_fDequeueInterruptTransfer(endpoint, Urb);
|
||
|
break;
|
||
|
default:
|
||
|
//ISOCH endpoint never expected here.
|
||
|
USB_DBG_ERROR_PRINT(("Unrecognized or Unsupported endpoint type in HCD_CancelRequest"));
|
||
|
//Execution should be able to continue.
|
||
|
status = USBD_STATUS_REQUEST_FAILED;
|
||
|
goto ExitHCD_CancelRequest;
|
||
|
}
|
||
|
endpoint->QueuedUrbCount--;
|
||
|
Urb->Header.Status = USBD_STATUS_CANCELED;
|
||
|
Urb->CommonTransfer.Hca.HcdUrbFlags |= OHCD_URB_FLAG_CANCELED;
|
||
|
USBD_CompleteRequest(Urb);
|
||
|
status = USBD_STATUS_SUCCESS;
|
||
|
} else
|
||
|
{
|
||
|
//
|
||
|
// If we are not queued than we ought to be programmed.
|
||
|
//
|
||
|
ASSERT(Urb->CommonTransfer.Hca.HcdUrbFlags & OHCD_URB_FLAG_PROGRAMMED);
|
||
|
|
||
|
//
|
||
|
// Mark the URB canceled.
|
||
|
//
|
||
|
Urb->CommonTransfer.Hca.HcdUrbFlags |= OHCD_URB_FLAG_CANCELED;
|
||
|
|
||
|
//
|
||
|
// Add the URB to the PendingCancels list.
|
||
|
//
|
||
|
Urb->CommonTransfer.Hca.HcdUrbLink = deviceExtension->PendingCancels;
|
||
|
deviceExtension->PendingCancels = Urb;
|
||
|
|
||
|
//
|
||
|
// Pause the endpoint, the IsrDPC
|
||
|
// will take care of the rest.
|
||
|
//
|
||
|
OHCD_fPauseEndpoint(deviceExtension, endpoint);
|
||
|
status = USBD_STATUS_CANCELING;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Done with queues and stuff
|
||
|
//
|
||
|
ExitHCD_CancelRequest:
|
||
|
KeLowerIrql(oldIrql);
|
||
|
|
||
|
USB_DBG_EXIT_PRINT(("Exiting HCD_CancelRequest: status = 0x%0.8x", status));
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
USBD_STATUS
|
||
|
FASTCALL
|
||
|
OHCD_fOpenEndpoint(
|
||
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
IN OUT PURB Urb
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Opens an endpoint. This is a multistep process:
|
||
|
|
||
|
1) Get an endpoint
|
||
|
|
||
|
2) Zero the memory.
|
||
|
|
||
|
3) Initialize the endpoint header
|
||
|
|
||
|
4) Insert Endpoint in schedule.
|
||
|
5) Update the handle, or cleanup if something failed.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DeviceExtension - DeviceObject for this USB controller.
|
||
|
|
||
|
HcdUrb - Pointer to URB which contains an open endpoint
|
||
|
request
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Error code several failure cases:
|
||
|
1) Cannot allocate Endpoint.
|
||
|
2) Cannot reserved descriptor blocks
|
||
|
3) Not enough bandwidth in schedule (isoch and interrupt only)
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
POHCD_ENDPOINT newEndpoint = NULL;
|
||
|
USBD_STATUS status = USBD_STATUS_SUCCESS;
|
||
|
KIRQL oldIrql;
|
||
|
|
||
|
USB_DBG_ENTRY_PRINT(("Entering OHCD_OpenEndpoint"));
|
||
|
|
||
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
||
|
|
||
|
//
|
||
|
// 1) Allocate an endpoint from the pool.
|
||
|
//
|
||
|
newEndpoint = OHCD_PoolAllocateEndpoint();
|
||
|
if(!newEndpoint)
|
||
|
{
|
||
|
USB_DBG_WARN_PRINT(("Attempt to exceed registered endpoints."));
|
||
|
status = USBD_STATUS_NO_MEMORY;
|
||
|
goto ExitOpenEndpoint;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 2) Zero memory and set flags
|
||
|
//
|
||
|
USB_DBG_ENTRY_PRINT(("Zeroing structure."));
|
||
|
RtlZeroMemory( (PVOID)newEndpoint, sizeof(OHCD_ENDPOINT));
|
||
|
|
||
|
//
|
||
|
// It is helpful to cache our physical address
|
||
|
//
|
||
|
newEndpoint->PhysicalAddress = OHCD_PoolGetPhysicalAddress(newEndpoint);
|
||
|
|
||
|
//
|
||
|
// Save off the endpoint type
|
||
|
//
|
||
|
newEndpoint->EndpointType = Urb->OpenEndpoint.EndpointType;
|
||
|
|
||
|
//
|
||
|
// Fill out polling interval, and bandwidth
|
||
|
//
|
||
|
newEndpoint->PollingInterval = Urb->OpenEndpoint.Interval;
|
||
|
newEndpoint->Bandwidth = USBD_CalculateUsbBandwidth(
|
||
|
Urb->OpenEndpoint.MaxPacketSize,
|
||
|
newEndpoint->EndpointType,
|
||
|
Urb->OpenEndpoint.LowSpeed
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Fill out everything in the control structure.
|
||
|
// Note the Direction here is set correctly for non-control endpoints.
|
||
|
// in the next step we have an if USB_ENDPOINT_TYPE_CONTROL and we fix it
|
||
|
// for the control endpoints.
|
||
|
//
|
||
|
newEndpoint->HcEndpointDescriptor.Control.FunctionAddress = Urb->OpenEndpoint.FunctionAddress;
|
||
|
newEndpoint->HcEndpointDescriptor.Control.EndpointAddress = Urb->OpenEndpoint.EndpointAddress;
|
||
|
if(USB_ENDPOINT_TYPE_CONTROL == newEndpoint->EndpointType)
|
||
|
{
|
||
|
newEndpoint->HcEndpointDescriptor.Control.Direction = 0;
|
||
|
}else
|
||
|
{
|
||
|
newEndpoint->HcEndpointDescriptor.Control.Direction = (USB_ENDPOINT_DIRECTION_MASK & Urb->OpenEndpoint.EndpointAddress) ? 2 : 1;
|
||
|
}
|
||
|
newEndpoint->HcEndpointDescriptor.Control.Speed = Urb->OpenEndpoint.LowSpeed;
|
||
|
newEndpoint->HcEndpointDescriptor.Control.Skip = 1;
|
||
|
newEndpoint->HcEndpointDescriptor.Control.Format = 0;
|
||
|
newEndpoint->HcEndpointDescriptor.Control.MaximumPacketSize = Urb->OpenEndpoint.MaxPacketSize;
|
||
|
|
||
|
//
|
||
|
// We do not have any URBs now either.
|
||
|
//
|
||
|
/* Unneccessary, we zeroed the structure right after allocating it.
|
||
|
newEndpoint->PendingUrbHeadP =
|
||
|
newEndpoint->PendingUrbTailP =
|
||
|
newEndpoint->PendingUrbCurrentP = NULL;
|
||
|
*/
|
||
|
|
||
|
//
|
||
|
// 3) Initialize Endpoint descriptor.
|
||
|
//
|
||
|
newEndpoint->HcEndpointDescriptor.NextED = 0;
|
||
|
newEndpoint->HcEndpointDescriptor.HeadPHaltCarry = 0;
|
||
|
newEndpoint->HcEndpointDescriptor.TailP = 0;
|
||
|
|
||
|
//
|
||
|
// Set the initial data toggle
|
||
|
//
|
||
|
if(Urb->OpenEndpoint.DataToggleBits)
|
||
|
{
|
||
|
ULONG dataToggleMask = 1 << newEndpoint->HcEndpointDescriptor.Control.EndpointAddress;
|
||
|
if(newEndpoint->HcEndpointDescriptor.Control.Direction == 2)
|
||
|
{
|
||
|
dataToggleMask <<= 16;
|
||
|
}
|
||
|
if((*Urb->OpenEndpoint.DataToggleBits)&dataToggleMask)
|
||
|
{
|
||
|
SET_TOGGLECARRY(&newEndpoint->HcEndpointDescriptor);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 4) Insert endpoint into schedule, and
|
||
|
//
|
||
|
if( USB_ENDPOINT_TYPE_CONTROL == newEndpoint->EndpointType ||
|
||
|
USB_ENDPOINT_TYPE_BULK == newEndpoint->EndpointType )
|
||
|
//
|
||
|
// Control or bulk
|
||
|
//
|
||
|
{
|
||
|
OHCD_ScheduleAddEndpointControlOrBulk(DeviceExtension, newEndpoint);
|
||
|
}
|
||
|
else
|
||
|
//
|
||
|
// Interrupt
|
||
|
//
|
||
|
{
|
||
|
ASSERT(USB_ENDPOINT_TYPE_INTERRUPT == newEndpoint->EndpointType);
|
||
|
//
|
||
|
// Scheduling Periodic Endpoints can fail due to lack of bandwidth.
|
||
|
//
|
||
|
status = OHCD_ScheduleAddEndpointPeriodic(DeviceExtension, newEndpoint);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 5) Udpate URB handle
|
||
|
//
|
||
|
if(USBD_SUCCESS(status))
|
||
|
{
|
||
|
Urb->OpenEndpoint.EndpointHandle = newEndpoint;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Urb->OpenEndpoint.EndpointHandle = NULL;
|
||
|
OHCD_PoolFreeEndpoint(newEndpoint);
|
||
|
}
|
||
|
|
||
|
ExitOpenEndpoint:
|
||
|
Urb->Header.Status = status;
|
||
|
KeLowerIrql(oldIrql);
|
||
|
USB_DBG_EXIT_PRINT(("Exiting OHCD_OpenEndpoint: usbdStatus = 0x%0.8x", status));
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
USBD_STATUS
|
||
|
FASTCALL
|
||
|
OHCD_fCloseEndpoint(
|
||
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
IN OUT PURB Urb
|
||
|
)
|
||
|
{
|
||
|
POHCD_ENDPOINT endpoint = (POHCD_ENDPOINT)Urb->CloseEndpoint.EndpointHandle;
|
||
|
KIRQL oldIrql;
|
||
|
|
||
|
USB_DBG_ENTRY_PRINT(("Entering OHCD_CloseEndpoint"));
|
||
|
|
||
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
||
|
endpoint->Flags |= OHCD_ENDPOINT_FLAG_CLOSING;
|
||
|
|
||
|
//
|
||
|
// Cancel any queued URBs (but not programmed) right now.
|
||
|
//
|
||
|
OHCD_fCancelQueuedUrbs(DeviceExtension, endpoint);
|
||
|
|
||
|
//
|
||
|
// Remove endpoint from schedule
|
||
|
//
|
||
|
ASSERT(USB_ENDPOINT_TYPE_ISOCHRONOUS != endpoint->EndpointType);
|
||
|
if( USB_ENDPOINT_TYPE_CONTROL == endpoint->EndpointType ||
|
||
|
USB_ENDPOINT_TYPE_BULK == endpoint->EndpointType )
|
||
|
//
|
||
|
// Control or bulk
|
||
|
//
|
||
|
{
|
||
|
OHCD_ScheduleRemoveEndpointControlOrBulk(DeviceExtension, endpoint);
|
||
|
}
|
||
|
else
|
||
|
//
|
||
|
// Interrupt
|
||
|
//
|
||
|
{
|
||
|
OHCD_ScheduleRemoveEndpointPeriodic(DeviceExtension, endpoint);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Add to endpoint reclamation queue, we do not need to pause it,
|
||
|
// but the pause routine is convenient.
|
||
|
//
|
||
|
OHCD_fPauseEndpoint(DeviceExtension, endpoint);
|
||
|
|
||
|
//
|
||
|
// Link the URB into the list of pending closes.
|
||
|
//
|
||
|
|
||
|
ASSERT(DeviceExtension->PendingCloses != Urb);
|
||
|
|
||
|
Urb->CloseEndpoint.HcdNextClose = DeviceExtension->PendingCloses;
|
||
|
DeviceExtension->PendingCloses = Urb;
|
||
|
|
||
|
KeLowerIrql(oldIrql);
|
||
|
|
||
|
USB_DBG_EXIT_PRINT(("Exiting OHCD_CloseEndpoint: usbdStatus = USBD_STATUS_PENDING"));
|
||
|
return USBD_STATUS_PENDING;
|
||
|
}
|
||
|
|
||
|
USBD_STATUS
|
||
|
FASTCALL
|
||
|
OHCD_fGetEndpointState(
|
||
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
IN OUT PURB Urb
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Gets the endpoint state. The state consists of two flags:
|
||
|
|
||
|
USB_ENDPOINT_TRANSFERS_QUEUED
|
||
|
USB_ENDPOINT_HALTED
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DeviceExtension - DeviceObject for this USB controller.
|
||
|
|
||
|
HcdUrb - Pointer to URB that we are servicing.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
USBD_STATUS_SUCCESS
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
POHCD_ENDPOINT endpoint = (POHCD_ENDPOINT)Urb->GetSetEndpointState.EndpointHandle;
|
||
|
|
||
|
USB_DBG_ENTRY_PRINT(("Entering OHCD_GetEndpointState"));
|
||
|
USB_DBG_TRACE_PRINT(("Endpoint state flags:"));
|
||
|
//
|
||
|
// Get whether or not is halted
|
||
|
//
|
||
|
Urb->GetSetEndpointState.EndpointState =
|
||
|
GET_HALTED(&endpoint->HcEndpointDescriptor) ? USB_ENDPOINT_STATE_HALTED : 0;
|
||
|
|
||
|
USB_DBG_TRACE_PRINT(("%s",(Urb->GetSetEndpointState.EndpointState&USB_ENDPOINT_STATE_HALTED)?" USB_ENDPOINT_HALTED":NULL));
|
||
|
|
||
|
//
|
||
|
// Now set the USB_ENDPOINT_TRANSFERS_QUEUED flag.
|
||
|
//
|
||
|
|
||
|
if(endpoint->QueuedUrbCount || endpoint->ProgrammedUrbCount)
|
||
|
{
|
||
|
Urb->GetSetEndpointState.EndpointState |= USB_ENDPOINT_STATE_TRANSFERS_QUEUED;
|
||
|
USB_DBG_TRACE_PRINT((" USB_ENDPOINT_STATE_TRANSFERS_QUEUED"));
|
||
|
}
|
||
|
USB_DBG_EXIT_PRINT(("Exiting OHCD_GetEndpointState"));
|
||
|
return USBD_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USBD_STATUS
|
||
|
FASTCALL
|
||
|
OHCD_fSetEndpointState(
|
||
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
IN OUT PURB Urb
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Sets the endpoint state. Basically, it can do only do things.
|
||
|
Restart a halted endpoint. Reset the data toggle on an endpoint.
|
||
|
|
||
|
1) If USB_ENDPOINT_RESET_DATA_TOGGLE is set, we will reset the data toggle.
|
||
|
2) If USB_ENDPOINT_HALTED is clear, we clear the halt bit.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DeviceExtension - DeviceObject for this USB controller.
|
||
|
|
||
|
HcdUrb - Pointer to URB that we are servicing.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
USBD_STATUS_SUCCESS
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
POHCD_ENDPOINT endpoint = (POHCD_ENDPOINT)Urb->GetSetEndpointState.EndpointHandle;
|
||
|
ULONG state = Urb->GetSetEndpointState.EndpointState;
|
||
|
|
||
|
USB_DBG_ENTRY_PRINT(("Entering OHCD_SetEndpointState"));
|
||
|
if(state & USB_ENDPOINT_STATE_DATA_TOGGLE_RESET)
|
||
|
{
|
||
|
USB_DBG_TRACE_PRINT(("Clearing the toggle carry."));
|
||
|
CLEAR_TOGGLECARRY(&endpoint->HcEndpointDescriptor);
|
||
|
}
|
||
|
if(state & USB_ENDPOINT_STATE_DATA_TOGGLE_SET)
|
||
|
{
|
||
|
USB_DBG_TRACE_PRINT(("Setting the toggle carry."));
|
||
|
SET_TOGGLECARRY(&endpoint->HcEndpointDescriptor);
|
||
|
}
|
||
|
if(!(state & USB_ENDPOINT_STATE_KEEP_HALT) && GET_HALTED(&endpoint->HcEndpointDescriptor))
|
||
|
{
|
||
|
USB_DBG_TRACE_PRINT(("Clearing a halt condition."));
|
||
|
CLEAR_HALTED(&endpoint->HcEndpointDescriptor);
|
||
|
}
|
||
|
USB_DBG_EXIT_PRINT(("Exiting OHCD_SetEndpointState"));
|
||
|
return USBD_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USBD_STATUS
|
||
|
FASTCALL
|
||
|
OHCD_fAbortEndpoint(
|
||
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
IN OUT PURB Urb
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Cancels all of the requests associated with a particular
|
||
|
endpoint.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DeviceExtension - DeviceObject for this USB controller.
|
||
|
|
||
|
Urb - Pointer to URB that we are servicing.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
USBD_STATUS_SUCCESS
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
POHCD_ENDPOINT endpoint = (POHCD_ENDPOINT)Urb->AbortEndpoint.EndpointHandle;
|
||
|
KIRQL oldIrql;
|
||
|
USBD_STATUS status = USBD_STATUS_SUCCESS;
|
||
|
|
||
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
||
|
//
|
||
|
// Cancel any queued URBs (but not programmed) right now.
|
||
|
//
|
||
|
OHCD_fCancelQueuedUrbs(DeviceExtension, endpoint);
|
||
|
|
||
|
//
|
||
|
// If there are urbs programmed, we need to queue
|
||
|
// the abort urb, and pause the endpoint.
|
||
|
//
|
||
|
if(endpoint->ProgrammedUrbCount)
|
||
|
{
|
||
|
//
|
||
|
// Queue Abort Urb
|
||
|
//
|
||
|
Urb->AbortEndpoint.HcdNextAbort = DeviceExtension->PendingAborts;
|
||
|
DeviceExtension->PendingAborts = Urb;
|
||
|
OHCD_fPauseEndpoint(DeviceExtension, endpoint);
|
||
|
status = USBD_STATUS_PENDING;
|
||
|
}
|
||
|
KeLowerIrql(oldIrql);
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
#pragma code_seg(".XPPCINIT")
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
OHCD_fSetResources(
|
||
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
PPCI_DEVICE_DESCRIPTOR PciDevice
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routines uses the Hal routines to configure
|
||
|
the PCI bus for the card. And store those settings.
|
||
|
|
||
|
This is two stages:
|
||
|
1) Initialize out device extension with proper base address.
|
||
|
2) Create an NT interrupt object.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DeviceExtension - DeviceExtension for this USB controller.
|
||
|
PciDevice - PCI BAR for device
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
Failures:
|
||
|
Assume everything works. Assert failures. Basically
|
||
|
any possible errors are configuration, these shouldn't
|
||
|
happen.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
USB_DBG_ENTRY_PRINT(("Entering OHCD_SetResources"));
|
||
|
//
|
||
|
// Record memory resource
|
||
|
//
|
||
|
ASSERT(PciDevice->ResourceData.Address[0].Type == CmResourceTypeMemory);
|
||
|
ASSERT(PciDevice->ResourceData.Address[0].u.Memory.Length >= sizeof(OHCI_OPERATIONAL_REGISTER_SIZE));
|
||
|
DeviceExtension->OperationalRegistersLength = PciDevice->ResourceData.Address[0].u.Memory.Length;
|
||
|
DeviceExtension->OperationalRegisters =
|
||
|
(POHCI_OPERATIONAL_REGISTERS)PciDevice->ResourceData.Address[0].u.Memory.TranslatedAddress;
|
||
|
USB_DBG_TRACE_PRINT(("Virtual Register Address = 0x%0.8x", DeviceExtension->OperationalRegisters));
|
||
|
|
||
|
USB_DBG_EXIT_PRINT(("Exiting OHCD_SetResources"));
|
||
|
return;
|
||
|
}
|
||
|
#pragma code_seg(".XPPCODE")
|
||
|
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
OHCD_fDequeueControlTransfer(
|
||
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
IN PURB Urb
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
// Walk the control transfer queue and find the URB
|
||
|
//
|
||
|
PURB urbOnQueue = DeviceExtension->ControlUrbHeadP;
|
||
|
PURB prevUrbOnQueue = NULL;
|
||
|
while(urbOnQueue != Urb)
|
||
|
{
|
||
|
//
|
||
|
// Serious error, if we got to the end of the queue and didn't find it.
|
||
|
//
|
||
|
ASSERT(urbOnQueue != DeviceExtension->ControlUrbTailP);
|
||
|
//
|
||
|
// Walk link for next iteration
|
||
|
//
|
||
|
prevUrbOnQueue = urbOnQueue;
|
||
|
urbOnQueue = urbOnQueue->CommonTransfer.Hca.HcdUrbLink;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Remove the Urb
|
||
|
//
|
||
|
if(NULL == prevUrbOnQueue)
|
||
|
{
|
||
|
DeviceExtension->ControlUrbHeadP = urbOnQueue->CommonTransfer.Hca.HcdUrbLink;
|
||
|
} else
|
||
|
{
|
||
|
prevUrbOnQueue->CommonTransfer.Hca.HcdUrbLink = urbOnQueue->CommonTransfer.Hca.HcdUrbLink;
|
||
|
}
|
||
|
//
|
||
|
// If it was the last URB, update the tail pointer too.
|
||
|
//
|
||
|
if(Urb == DeviceExtension->ControlUrbTailP)
|
||
|
{
|
||
|
DeviceExtension->ControlUrbTailP = prevUrbOnQueue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
OHCD_fDequeueBulkTransfer(
|
||
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
IN PURB Urb
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
// Walk the control transfer queue and find the URB
|
||
|
//
|
||
|
PURB urbOnQueue = DeviceExtension->BulkUrbHeadP;
|
||
|
PURB prevUrbOnQueue = NULL;
|
||
|
while(urbOnQueue != Urb)
|
||
|
{
|
||
|
//
|
||
|
// Serious error, if we got to the end of the queue and didn't find it.
|
||
|
//
|
||
|
ASSERT(urbOnQueue != DeviceExtension->BulkUrbTailP);
|
||
|
//
|
||
|
// Walk link for next iteration
|
||
|
//
|
||
|
prevUrbOnQueue = urbOnQueue;
|
||
|
urbOnQueue = urbOnQueue->CommonTransfer.Hca.HcdUrbLink;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Remove the Urb
|
||
|
//
|
||
|
if(NULL == prevUrbOnQueue)
|
||
|
{
|
||
|
DeviceExtension->BulkUrbHeadP = urbOnQueue->CommonTransfer.Hca.HcdUrbLink;
|
||
|
} else
|
||
|
{
|
||
|
prevUrbOnQueue->CommonTransfer.Hca.HcdUrbLink = urbOnQueue->CommonTransfer.Hca.HcdUrbLink;
|
||
|
}
|
||
|
//
|
||
|
// If it was the last URB, update the tail pointer too.
|
||
|
//
|
||
|
if(Urb == DeviceExtension->BulkUrbTailP)
|
||
|
{
|
||
|
DeviceExtension->BulkUrbTailP = prevUrbOnQueue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
OHCD_fDequeueInterruptTransfer(
|
||
|
IN POHCD_ENDPOINT Endpoint,
|
||
|
IN PURB Urb
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
// Walk the control transfer queue and find the URB
|
||
|
//
|
||
|
PURB urbOnQueue = Endpoint->PendingUrbHeadP;
|
||
|
PURB prevUrbOnQueue = NULL;
|
||
|
while(urbOnQueue != Urb)
|
||
|
{
|
||
|
//
|
||
|
// Serious error, if we got to the end of the queue and didn't find it.
|
||
|
//
|
||
|
ASSERT(urbOnQueue != Endpoint->PendingUrbTailP);
|
||
|
//
|
||
|
// Walk link for next iteration
|
||
|
//
|
||
|
prevUrbOnQueue = urbOnQueue;
|
||
|
urbOnQueue = urbOnQueue->CommonTransfer.Hca.HcdUrbLink;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Remove the Urb
|
||
|
//
|
||
|
if(NULL == prevUrbOnQueue)
|
||
|
{
|
||
|
Endpoint->PendingUrbHeadP = urbOnQueue->CommonTransfer.Hca.HcdUrbLink;
|
||
|
} else
|
||
|
{
|
||
|
prevUrbOnQueue->CommonTransfer.Hca.HcdUrbLink = urbOnQueue->CommonTransfer.Hca.HcdUrbLink;
|
||
|
}
|
||
|
//
|
||
|
// If it was the last URB, update the tail pointer too.
|
||
|
//
|
||
|
if(Urb == Endpoint->PendingUrbTailP)
|
||
|
{
|
||
|
Endpoint->PendingUrbTailP = prevUrbOnQueue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
OHCD_fCancelQueuedUrbs(
|
||
|
IN POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
POHCD_ENDPOINT Endpoint
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Called by OHCD_AbortEndpoint and OHCD_CloseEndpoint, to
|
||
|
cancel any Urbs which have been queued, but not yet programmed.
|
||
|
|
||
|
Different types of endpoints have URBs queued in different
|
||
|
places. We first switch on the endpoint type, to initialize
|
||
|
pointers to the queue pointers.
|
||
|
|
||
|
Then we walk the queue using pointers to pointers. I will
|
||
|
be the first to admit that this is style of code is not
|
||
|
terribly readable. However, it is efficient, so I tried to
|
||
|
just comment well.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DeviceExtension - DeviceExtension for this USB controller.
|
||
|
|
||
|
Endpoint - Endpoint to cancel URBs for.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
|
||
|
PURB *currentUrbP; //Pointer to official pointer to the URB we are currently processing
|
||
|
PURB *tailUrbP; //Pointer to official pointer to the tail URB in queue
|
||
|
PURB currentUrb = NULL; //Local pointer to current URB we are working.
|
||
|
PURB previousUrb; //Local pointer to the last URB prior to the current one that was not canceled.
|
||
|
PVOID endpointHandle = (PVOID) Endpoint;
|
||
|
|
||
|
//
|
||
|
// We need to synchronize.
|
||
|
//
|
||
|
ASSERT_DISPATCH_LEVEL();
|
||
|
|
||
|
//
|
||
|
// If there are any Urbs in the queue . . .
|
||
|
//
|
||
|
if(Endpoint->QueuedUrbCount)
|
||
|
{
|
||
|
//
|
||
|
// Where the heads and tails are stored
|
||
|
// depends on the endpoint type.
|
||
|
//
|
||
|
switch(Endpoint->EndpointType)
|
||
|
{
|
||
|
case USB_ENDPOINT_TYPE_CONTROL:
|
||
|
currentUrbP = &DeviceExtension->ControlUrbHeadP;
|
||
|
tailUrbP = &DeviceExtension->ControlUrbTailP;
|
||
|
break;
|
||
|
case USB_ENDPOINT_TYPE_BULK:
|
||
|
currentUrbP = &DeviceExtension->BulkUrbHeadP;
|
||
|
tailUrbP = &DeviceExtension->BulkUrbTailP;
|
||
|
break;
|
||
|
case USB_ENDPOINT_TYPE_INTERRUPT:
|
||
|
currentUrbP = &Endpoint->PendingUrbHeadP;
|
||
|
tailUrbP = &Endpoint->PendingUrbTailP;
|
||
|
break;
|
||
|
default:
|
||
|
//ISOCH endpoint never expected here.
|
||
|
USB_DBG_ERROR_PRINT(("Unrecognized endpoint type in OHCD_CancelQueuedUrbs"));
|
||
|
//This is a serious error, continued execution might be feasible.
|
||
|
return;
|
||
|
}
|
||
|
//
|
||
|
// Walk queue and remove any URB associated with Endpoint
|
||
|
//
|
||
|
do
|
||
|
{
|
||
|
currentUrb = *currentUrbP;
|
||
|
ASSERT(currentUrb); //A serious coding error if we hit this.
|
||
|
//
|
||
|
// If the URB belongs to this endpoint cancel it.
|
||
|
//
|
||
|
if(currentUrb->CommonTransfer.EndpointHandle == endpointHandle)
|
||
|
{
|
||
|
//
|
||
|
// Whoever was pointing at this urb, should now point to whoever
|
||
|
// this urb is pointing to.
|
||
|
//
|
||
|
*currentUrbP = currentUrb->CommonTransfer.Hca.HcdUrbLink;
|
||
|
//
|
||
|
// Cancel the Urb
|
||
|
//
|
||
|
currentUrb->Header.Status = USBD_STATUS_CANCELED;
|
||
|
Endpoint->QueuedUrbCount--;
|
||
|
USBD_CompleteRequest(currentUrb);
|
||
|
}
|
||
|
//
|
||
|
// Otherwise skip the urb.
|
||
|
//
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// This urb is now the official pointer to the next current URB.
|
||
|
// Since we didn't cancel it, it also the new previousUrb.
|
||
|
//
|
||
|
previousUrb = currentUrb;
|
||
|
currentUrbP = ¤tUrb->CommonTransfer.Hca.HcdUrbLink;
|
||
|
}
|
||
|
}while(*tailUrbP != currentUrb);
|
||
|
//
|
||
|
// The official pointer to the tail URB, should now
|
||
|
// point to the last URB we didn't cancel.
|
||
|
//
|
||
|
*tailUrbP = previousUrb;
|
||
|
//
|
||
|
// That should have dequeued every urb associated with the endpoint.
|
||
|
//
|
||
|
ASSERT(0 == Endpoint->QueuedUrbCount);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
OHCD_fPauseEndpoint(
|
||
|
POHCD_DEVICE_EXTENSION DeviceExtension,
|
||
|
POHCD_ENDPOINT Endpoint
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routines makes sure that endpoint gets paused if it is not already.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DeviceExtension - DeviceExtension for this USB controller.
|
||
|
|
||
|
Endpoint - Endpoint to pause.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
|
||
|
USB_DBG_ENTRY_PRINT(("Entering OHCD_PauseEndpoint"));
|
||
|
|
||
|
ASSERT_DISPATCH_LEVEL();
|
||
|
|
||
|
//
|
||
|
// We reference count pauses
|
||
|
//
|
||
|
Endpoint->PendingPauseCount++;
|
||
|
//
|
||
|
// If a pause is not already in progess,
|
||
|
// then pause it.
|
||
|
if(!(Endpoint->Flags & OHCD_ENDPOINT_FLAG_PAUSING))
|
||
|
{
|
||
|
//
|
||
|
// Set the Skip bit
|
||
|
//
|
||
|
Endpoint->HcEndpointDescriptor.Control.Skip = TRUE;
|
||
|
//
|
||
|
// Set the frame that we need to wait for.
|
||
|
//
|
||
|
Endpoint->PauseFrame = OHCD_Get32BitFrameNumber(DeviceExtension) + 1;
|
||
|
|
||
|
//
|
||
|
// Decide if we need to set the delay pause flag,
|
||
|
// if are called between an interrupt and the DPC
|
||
|
// to handle that interupt. THIS MUST BE AFTER SETTING
|
||
|
// THE SKIP BIT. The call to OHCD_Get32BitFrameNumber
|
||
|
// should ensure that the compiler doesn't reorder this.
|
||
|
//
|
||
|
if(DeviceExtension->IsrDpc_Context.InterruptsSignaled)
|
||
|
{
|
||
|
Endpoint->Flags |= OHCD_ENDPOINT_FLAG_DELAY_PAUSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Clear the status indicator for StartOfFrame.
|
||
|
//
|
||
|
WRITE_REGISTER_ULONG(&DeviceExtension->OperationalRegisters->HcInterruptStatus, HCINT_StartOfFrame);
|
||
|
//
|
||
|
// Enable the StartOfFrame interrupt
|
||
|
//
|
||
|
WRITE_REGISTER_ULONG(&DeviceExtension->OperationalRegisters->HcInterruptEnable, HCINT_StartOfFrame);
|
||
|
//
|
||
|
// Set the OHCD_ENDPOINT_FLAG_PAUSING_FLAG;
|
||
|
//
|
||
|
Endpoint->Flags |= OHCD_ENDPOINT_FLAG_PAUSING;
|
||
|
}
|
||
|
USB_DBG_EXIT_PRINT(("Exiting OHCD_PauseEndpoint"));
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
OHCD_ShutDown(
|
||
|
PHAL_SHUTDOWN_REGISTRATION ShutdownRegistration
|
||
|
)
|
||
|
/*++
|
||
|
Routine Description:
|
||
|
Called by the HAL in order to do a quick reboot. Basically,
|
||
|
our job is to shut off the host controller, so that no extraneous
|
||
|
calls happen during shutdown.
|
||
|
Arguments:
|
||
|
ShutdownRegistration - Shutdown registration that we passed to
|
||
|
HalRegisterShutdown. We can use CONTAINING_RECORD to
|
||
|
our device extension.
|
||
|
--*/
|
||
|
{
|
||
|
POHCD_DEVICE_EXTENSION deviceExtension;
|
||
|
KIRQL oldIrql;
|
||
|
|
||
|
deviceExtension = CONTAINING_RECORD(
|
||
|
ShutdownRegistration,
|
||
|
OHCD_DEVICE_EXTENSION,
|
||
|
ShutdownRegistration
|
||
|
);
|
||
|
|
||
|
KeRaiseIrql((KIRQL)OHCD_InterruptObject[deviceExtension->HostControllerNumber].Irql, &oldIrql);
|
||
|
|
||
|
//
|
||
|
// Disable all interrupts
|
||
|
//
|
||
|
WRITE_REGISTER_ULONG(
|
||
|
&deviceExtension->OperationalRegisters->HcInterruptDisable,
|
||
|
HCINT_SchedulingOverrun |
|
||
|
HCINT_WritebackDoneHead |
|
||
|
HCINT_UnrecoverableError |
|
||
|
HCINT_FrameNumberOverflow |
|
||
|
HCINT_MasterInterruptEnable
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Reset the Host Controller
|
||
|
//
|
||
|
WRITE_REGISTER_ULONG(
|
||
|
&deviceExtension->OperationalRegisters->HcControl.ul,
|
||
|
HC_CONTROL_REGISTER_STOP
|
||
|
);
|
||
|
|
||
|
KeLowerIrql(oldIrql);
|
||
|
}
|