1035 lines
27 KiB
C
1035 lines
27 KiB
C
|
/* ++
|
|||
|
|
|||
|
Copyright (c) 1999-2000 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
PNP.C
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
WinCE Host PnP functions
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
kernel mode only
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
07-14-99 : created
|
|||
|
|
|||
|
Authors:
|
|||
|
|
|||
|
Jeff Midkiff (jeffmi)
|
|||
|
|
|||
|
-- */
|
|||
|
|
|||
|
#include "wceusbsh.h"
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
StartDevice(
|
|||
|
IN PDEVICE_OBJECT PDevObj,
|
|||
|
IN PIRP PIrp
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
StopDevice(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RemoveDevice(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SyncCompletion(
|
|||
|
IN PDEVICE_OBJECT PDevObj,
|
|||
|
IN PIRP PIrp,
|
|||
|
IN PKEVENT PSyncEvent
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGEWCE1, StartDevice)
|
|||
|
#pragma alloc_text(PAGEWCE1, StopIo)
|
|||
|
#pragma alloc_text(PAGEWCE1, StopDevice)
|
|||
|
#pragma alloc_text(PAGEWCE1, RemoveDevice)
|
|||
|
#pragma alloc_text(PAGEWCE1, Power)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
StartDevice(
|
|||
|
IN PDEVICE_OBJECT PDevObj,
|
|||
|
IN PIRP PIrp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine handles IRP_MN_START_DEVICE to either
|
|||
|
to start a newly enumerated device or to restart
|
|||
|
an existing device that was stopped.
|
|||
|
|
|||
|
PnP Manager postpones exposing device interfaces
|
|||
|
and blocks create requests for the device until
|
|||
|
the start IRP succeeds.
|
|||
|
|
|||
|
See: Setup, Plug & Play, Power Management: Preliminary Windows 2000 DDK
|
|||
|
Section 3.1 Starting a Device
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject
|
|||
|
Irp
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
|||
|
NTSTATUS status = STATUS_SUCCESS;
|
|||
|
PNP_STATE oldPnPState;
|
|||
|
KEVENT event;
|
|||
|
|
|||
|
DbgDump(DBG_PNP, (">StartDevice (%x)\n", PDevObj));
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
oldPnPState = pDevExt->PnPState;
|
|||
|
|
|||
|
//
|
|||
|
// Pass the Start Irp down the stack.
|
|||
|
// We do are Start on the way back up.
|
|||
|
//
|
|||
|
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
|
|||
|
|
|||
|
IoCopyCurrentIrpStackLocationToNext( PIrp );
|
|||
|
|
|||
|
IoSetCompletionRoutine( PIrp,
|
|||
|
SyncCompletion,
|
|||
|
&event,
|
|||
|
TRUE, TRUE, TRUE );
|
|||
|
|
|||
|
status = IoCallDriver( pDevExt->NextDevice, PIrp );
|
|||
|
|
|||
|
//
|
|||
|
// SyncCompletion simple signals the event
|
|||
|
// and returns STATUS_MORE_PROCESSING_REQUIRED,
|
|||
|
// so we still own the Irp.
|
|||
|
//
|
|||
|
if ( status == STATUS_PENDING ) {
|
|||
|
KeWaitForSingleObject( &event, Suspended, KernelMode, FALSE, NULL );
|
|||
|
}
|
|||
|
|
|||
|
status = PIrp->IoStatus.Status;
|
|||
|
|
|||
|
if (status != STATUS_SUCCESS) {
|
|||
|
DbgDump(DBG_PNP, ("ERROR: StartDevice returned 0x%x\n", status));
|
|||
|
goto ExitStartDevice;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The USB stack started OK, start our device...
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Initialize our DPC's
|
|||
|
//
|
|||
|
KeInitializeDpc(&pDevExt->TotalReadTimeoutDpc,
|
|||
|
ReadTimeout,
|
|||
|
pDevExt);
|
|||
|
|
|||
|
KeInitializeDpc( &pDevExt->IntervalReadTimeoutDpc,
|
|||
|
IntervalReadTimeout,
|
|||
|
pDevExt);
|
|||
|
|
|||
|
//
|
|||
|
// Initialize timers
|
|||
|
//
|
|||
|
KeInitializeTimer(&pDevExt->ReadRequestTotalTimer);
|
|||
|
KeInitializeTimer(&pDevExt->ReadRequestIntervalTimer);
|
|||
|
|
|||
|
//
|
|||
|
// Get our USB_DEVICE_DESCRIPTOR
|
|||
|
//
|
|||
|
status = UsbGetDeviceDescriptor(PDevObj);
|
|||
|
if (status != STATUS_SUCCESS) {
|
|||
|
DbgDump(DBG_ERR, ("UsbGetDeviceDescriptor error: 0x%x\n", status));
|
|||
|
goto ExitStartDevice;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Configure USB stack
|
|||
|
//
|
|||
|
status = UsbConfigureDevice( PDevObj );
|
|||
|
if (status != STATUS_SUCCESS) {
|
|||
|
DbgDump(DBG_ERR, ("UsbConfigureDevice error: 0x%x\n", status));
|
|||
|
goto ExitStartDevice;
|
|||
|
}
|
|||
|
|
|||
|
// set state
|
|||
|
InterlockedExchange((PULONG)&pDevExt->PnPState, PnPStateStarted);
|
|||
|
InterlockedExchange(&pDevExt->DeviceRemoved, FALSE);
|
|||
|
InterlockedExchange(&pDevExt->AcceptingRequests, TRUE);
|
|||
|
|
|||
|
//
|
|||
|
// reset logical Serial interface
|
|||
|
//
|
|||
|
status = SerialResetDevice(pDevExt, PIrp, FALSE);
|
|||
|
if ( STATUS_SUCCESS != status ) {
|
|||
|
DbgDump(DBG_ERR, ("SerialResetDevice ERROR: 0x%x\n", status));
|
|||
|
TEST_TRAP();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// allocate our read endpoint context
|
|||
|
//
|
|||
|
status = AllocUsbRead( pDevExt );
|
|||
|
if ( STATUS_SUCCESS != status ) {
|
|||
|
DbgDump(DBG_ERR, ("AllocUsbRead ERROR: 0x%x\n", status));
|
|||
|
TEST_TRAP();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// allocate our interrupt endpoint context
|
|||
|
//
|
|||
|
if ( pDevExt->IntPipe.hPipe ) {
|
|||
|
status = AllocUsbInterrupt( pDevExt );
|
|||
|
if ( STATUS_SUCCESS != status ) {
|
|||
|
DbgDump(DBG_ERR, ("AllocUsbRead ERROR: 0x%x\n", status));
|
|||
|
TEST_TRAP();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now set the interface state active
|
|||
|
//
|
|||
|
status = IoSetDeviceInterfaceState(&pDevExt->DeviceClassSymbolicName, TRUE);
|
|||
|
if ( STATUS_SUCCESS != status ) {
|
|||
|
DbgDump(DBG_ERR, ("IoSetDeviceInterfaceState error: 0x%x\n", status));
|
|||
|
TEST_TRAP();
|
|||
|
} else{
|
|||
|
DbgDump(DBG_WRN, ("IoSetDeviceInterfaceState: ON\n"));
|
|||
|
}
|
|||
|
|
|||
|
ExitStartDevice:
|
|||
|
if ( STATUS_SUCCESS != status ) {
|
|||
|
pDevExt->PnPState = oldPnPState;
|
|||
|
UsbFreeReadBuffer( PDevObj );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// complete the Irp
|
|||
|
//
|
|||
|
PIrp->IoStatus.Status = status;
|
|||
|
|
|||
|
DbgDump(DBG_PNP, ("<StartDevice(0x%x)\n", status));
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
StopIo(
|
|||
|
IN PDEVICE_OBJECT DeviceObject
|
|||
|
)
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION pDevExt = DeviceObject->DeviceExtension;
|
|||
|
NTSTATUS status = STATUS_SUCCESS;
|
|||
|
KIRQL irql;
|
|||
|
|
|||
|
|
|||
|
DbgDump(DBG_PNP|DBG_INIT, (">StopIo\n"));
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if ((pDevExt->PnPState < PnPStateInitialized) || (pDevExt->PnPState > PnPStateMax)) {
|
|||
|
DbgDump(DBG_ERR, ("StopIo:STATUS_INVALID_PARAMETER\n"));
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
InterlockedExchange(&pDevExt->DeviceOpened, FALSE);
|
|||
|
|
|||
|
//
|
|||
|
// cancel any pending user Read Irps
|
|||
|
//
|
|||
|
KillAllPendingUserReads( DeviceObject,
|
|||
|
&pDevExt->UserReadQueue,
|
|||
|
&pDevExt->UserReadIrp);
|
|||
|
|
|||
|
//
|
|||
|
// cancel our USB INT irp
|
|||
|
//
|
|||
|
if (pDevExt->IntIrp)
|
|||
|
{
|
|||
|
status = CancelUsbInterruptIrp(DeviceObject);
|
|||
|
if (STATUS_SUCCESS == status) {
|
|||
|
|
|||
|
InterlockedExchange(&pDevExt->IntState, IRP_STATE_COMPLETE);
|
|||
|
|
|||
|
} else {
|
|||
|
DbgDump(DBG_ERR, ("CancelUsbInterruptIrp ERROR: 0x%x\n", status));
|
|||
|
TEST_TRAP();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// cancel our USB Read irp
|
|||
|
//
|
|||
|
if (pDevExt->UsbReadIrp)
|
|||
|
{
|
|||
|
status = CancelUsbReadIrp(DeviceObject);
|
|||
|
if (STATUS_SUCCESS == status) {
|
|||
|
|
|||
|
InterlockedExchange(&pDevExt->UsbReadState, IRP_STATE_COMPLETE);
|
|||
|
|
|||
|
} else {
|
|||
|
DbgDump(DBG_ERR, ("CancelUsbReadIrp ERROR: 0x%x\n", status));
|
|||
|
TEST_TRAP();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// cancel pending USB Writes
|
|||
|
//
|
|||
|
CleanUpPacketList( DeviceObject,
|
|||
|
&pDevExt->PendingWritePackets,
|
|||
|
&pDevExt->PendingDataOutEvent );
|
|||
|
|
|||
|
//
|
|||
|
// cancel pending USB Reads
|
|||
|
//
|
|||
|
CleanUpPacketList(DeviceObject,
|
|||
|
&pDevExt->PendingReadPackets,
|
|||
|
&pDevExt->PendingDataInEvent );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// cancel the pending serial port Irp
|
|||
|
//
|
|||
|
if (pDevExt->SerialPort.ControlIrp) {
|
|||
|
if ( !IoCancelIrp(pDevExt->SerialPort.ControlIrp) ) {
|
|||
|
//
|
|||
|
// We can get here if we are holding the Irp, i.e. we didn't set a cancel routine.
|
|||
|
// Wait for the default timeout, which was set on the corresponding Urb (Set/Clear DTR/RTS).
|
|||
|
//
|
|||
|
LARGE_INTEGER timeOut;
|
|||
|
|
|||
|
timeOut.QuadPart = MILLISEC_TO_100NANOSEC( DEFAULT_PENDING_TIMEOUT );
|
|||
|
|
|||
|
DbgDump(DBG_ERR, ("!IoCancelIrp(%p)\n", pDevExt->SerialPort.ControlIrp));
|
|||
|
|
|||
|
KeDelayExecutionThread(KernelMode, FALSE, &timeOut);
|
|||
|
|
|||
|
TEST_TRAP();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// cancel the pending serial port wait mask Irp
|
|||
|
//
|
|||
|
if (pDevExt->SerialPort.CurrentWaitMaskIrp) {
|
|||
|
if ( !IoCancelIrp(pDevExt->SerialPort.CurrentWaitMaskIrp) ) {
|
|||
|
// We should never get here because we set a cancel routine on this Irp
|
|||
|
DbgDump(DBG_ERR, ("!IoCancelIrp(%p)\n", pDevExt->SerialPort.CurrentWaitMaskIrp));
|
|||
|
TEST_TRAP();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// wait for pending Work Items to complets
|
|||
|
//
|
|||
|
status = WaitForPendingItem(DeviceObject,
|
|||
|
&pDevExt->PendingWorkItemsEvent,
|
|||
|
&pDevExt->PendingWorkItemsCount );
|
|||
|
if ( STATUS_SUCCESS != status ) {
|
|||
|
DbgDump(DBG_ERR, ("WaitForPendingItem ERROR: 0x%x\n", status));
|
|||
|
TEST_TRAP();
|
|||
|
}
|
|||
|
|
|||
|
ASSERT( 0 == pDevExt->PendingReadCount );
|
|||
|
ASSERT( 0 == pDevExt->PendingWriteCount );
|
|||
|
ASSERT( 0 == pDevExt->PendingDataOutCount );
|
|||
|
ASSERT( 0 == pDevExt->PendingIntCount );
|
|||
|
ASSERT( 0 == pDevExt->PendingWorkItemsCount );
|
|||
|
ASSERT( NULL == pDevExt->SerialPort.ControlIrp );
|
|||
|
ASSERT( NULL == pDevExt->SerialPort.CurrentWaitMaskIrp );
|
|||
|
|
|||
|
DbgDump(DBG_PNP|DBG_INIT, ("<StopIo(%x)\n", status));
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
StopDevice(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine handles IRP_MN_STOP_DEVICE.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject
|
|||
|
Irp
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION pDevExt = DeviceObject->DeviceExtension;
|
|||
|
NTSTATUS status = STATUS_SUCCESS;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( Irp );
|
|||
|
|
|||
|
DbgDump(DBG_PNP, (">StopDevice (%x)\n", DeviceObject));
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// if we are not already in stopped state
|
|||
|
//
|
|||
|
if ((pDevExt->PnPState != PnPStateStopped) ||
|
|||
|
(pDevExt->PnPState != PnPStateSupriseRemove)) {
|
|||
|
|
|||
|
//
|
|||
|
// Signal that we are no longer AcceptingRequests
|
|||
|
//
|
|||
|
InterlockedExchange(&pDevExt->AcceptingRequests, FALSE);
|
|||
|
|
|||
|
//
|
|||
|
// set the interface state inactive
|
|||
|
//
|
|||
|
if (pDevExt->DeviceClassSymbolicName.Buffer )
|
|||
|
{
|
|||
|
status = IoSetDeviceInterfaceState(&pDevExt->DeviceClassSymbolicName, FALSE);
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
DbgDump(DBG_WRN, ("IoSetDeviceInterfaceState.2: OFF\n"));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
status = StopIo(DeviceObject);
|
|||
|
if (STATUS_SUCCESS != status) {
|
|||
|
DbgDump(DBG_ERR, ("StopIo ERROR: 0x%x\n", status));
|
|||
|
TEST_TRAP();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// free the Read Irp
|
|||
|
//
|
|||
|
if (pDevExt->UsbReadIrp) {
|
|||
|
|
|||
|
ASSERT( (IRP_STATE_COMPLETE == pDevExt->UsbReadState)
|
|||
|
|| (IRP_STATE_CANCELLED== pDevExt->UsbReadState) );
|
|||
|
|
|||
|
ExFreePool(pDevExt->UsbReadIrp);
|
|||
|
pDevExt->UsbReadIrp = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// free the INT Irp
|
|||
|
//
|
|||
|
if (pDevExt->IntIrp) {
|
|||
|
|
|||
|
ASSERT( (IRP_STATE_COMPLETE == pDevExt->IntState)
|
|||
|
|| (IRP_STATE_CANCELLED== pDevExt->IntState) );
|
|||
|
|
|||
|
ExFreePool(pDevExt->IntIrp);
|
|||
|
pDevExt->IntIrp = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// free the INT Urb
|
|||
|
//
|
|||
|
if (pDevExt->IntUrb) {
|
|||
|
ExFreeToNPagedLookasideList( &pDevExt->BulkTransferUrbPool, pDevExt->IntUrb );
|
|||
|
pDevExt->IntUrb = NULL;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
DbgDump(DBG_PNP, ("<StopDevice(0x%x)\n", status));
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
CleanUpPacketList(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PLIST_ENTRY PListHead,
|
|||
|
IN PKEVENT PEvent
|
|||
|
)
|
|||
|
/* ++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Walks the pending packet list and
|
|||
|
cancels the packet's timer and Irp
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject
|
|||
|
PListHead - pointer to head of packet List
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
-- */
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION pDevExt = DeviceObject->DeviceExtension;
|
|||
|
PUSB_PACKET pPacket;
|
|||
|
KIRQL irql;
|
|||
|
PLIST_ENTRY pleHead, pleCurrent, pleNext;
|
|||
|
NTSTATUS status = STATUS_SUCCESS;
|
|||
|
|
|||
|
DbgDump(DBG_PNP|DBG_IRP, (">CleanUpPacketLists (%x)\n", DeviceObject));
|
|||
|
|
|||
|
// acquire lock
|
|||
|
KeAcquireSpinLock( &pDevExt->ControlLock, &irql );
|
|||
|
|
|||
|
if ( !PListHead || !PEvent) {
|
|||
|
DbgDump(DBG_ERR, ("CleanUpPacketLists: !(Head|Event)\n"));
|
|||
|
TEST_TRAP();
|
|||
|
KeReleaseSpinLock( &pDevExt->ControlLock, irql );
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
// walk the list...
|
|||
|
for ( pleHead = PListHead, // get 1st ListEntry
|
|||
|
pleCurrent = pleHead->Flink,
|
|||
|
pleNext = pleCurrent->Flink;
|
|||
|
|
|||
|
pleCurrent != pleHead, // done when we loop back to head
|
|||
|
!pleHead, // or hit a trashed list
|
|||
|
!pleCurrent,
|
|||
|
!pleNext;
|
|||
|
|
|||
|
pleCurrent = pleNext, // get the next in the list
|
|||
|
pleNext = pleCurrent->Flink
|
|||
|
)
|
|||
|
{
|
|||
|
// did the list get trashed?
|
|||
|
ASSERT( pleHead );
|
|||
|
ASSERT( pleCurrent );
|
|||
|
ASSERT( pleNext );
|
|||
|
|
|||
|
// extract packet pointer
|
|||
|
pPacket = CONTAINING_RECORD( pleCurrent,
|
|||
|
USB_PACKET,
|
|||
|
ListEntry );
|
|||
|
|
|||
|
if ( pPacket &&
|
|||
|
pPacket->DeviceExtension &&
|
|||
|
pPacket->Irp ) {
|
|||
|
|
|||
|
// cancel packet's timer
|
|||
|
KeCancelTimer( &pPacket->TimerObj);
|
|||
|
|
|||
|
if ( !IoCancelIrp( pPacket->Irp ) ) {
|
|||
|
//
|
|||
|
// This means USB has the Irp in a non-canceable state.
|
|||
|
// We need to wait for either the pending read event, or the cancel event.
|
|||
|
//
|
|||
|
DbgDump(DBG_IRP, ("CleanUpPacketLists: Irp (%p) was not cancelled\n", pPacket->Irp));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// we need to wait for the Irp to complete from USB
|
|||
|
//
|
|||
|
DbgDump(DBG_IRP, ("Waiting for Irp (%p) to complete...\n", pPacket->Irp ));
|
|||
|
|
|||
|
KeReleaseSpinLock( &pDevExt->ControlLock, irql );
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
KeWaitForSingleObject( PEvent,
|
|||
|
Executive,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
NULL );
|
|||
|
|
|||
|
KeAcquireSpinLock( &pDevExt->ControlLock, &irql );
|
|||
|
|
|||
|
DbgDump(DBG_IRP, ("...Irp (%p) signalled completion.\n", pPacket->Irp ));
|
|||
|
|
|||
|
} else {
|
|||
|
// it was completed already
|
|||
|
DbgDump(DBG_WRN, ("CleanUpPacketLists: No Packet\n" ));
|
|||
|
|
|||
|
if ( pPacket &&
|
|||
|
(!pPacket->ListEntry.Flink || !pPacket->ListEntry.Blink)) {
|
|||
|
|
|||
|
DbgDump(DBG_ERR, ("CleanUpPacketLists: corrupt List!!\n" ));
|
|||
|
TEST_TRAP();
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The Irp should percolate back to our R/W completion
|
|||
|
// routine, which puts the packet back in packet pool.
|
|||
|
//
|
|||
|
}
|
|||
|
|
|||
|
#if DBG
|
|||
|
if ( !pleHead || !pleCurrent || !pleNext) {
|
|||
|
DbgDump(DBG_ERR, ("CleanUpPacketLists: corrupt List!!\n" ));
|
|||
|
TEST_TRAP();
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
KeReleaseSpinLock( &pDevExt->ControlLock, irql );
|
|||
|
|
|||
|
DbgDump(DBG_PNP|DBG_IRP, ("<CleanUpPacketLists (0x%x)\n", STATUS_SUCCESS));
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RemoveDevice(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine handles IRP_MN_REMOVE_DEVICE.
|
|||
|
|
|||
|
See: Setup, Plug & Play, Power Management: Preliminary Windows 2000 DDK
|
|||
|
Section 3.3.3.1 Removing a Device in a Function Driver
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject
|
|||
|
Irp
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION pDevExt = DeviceObject->DeviceExtension;
|
|||
|
NTSTATUS status = STATUS_SUCCESS;
|
|||
|
|
|||
|
DbgDump(DBG_PNP|DBG_TRACE, (">RemoveDevice (%x)\n", DeviceObject));
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// stop the device
|
|||
|
//
|
|||
|
status = StopDevice( DeviceObject, Irp );
|
|||
|
|
|||
|
InterlockedExchange((PULONG)&pDevExt->PnPState, PnPStateRemoved);
|
|||
|
|
|||
|
//
|
|||
|
// Pass the Irp down the stack now that we've done our work.
|
|||
|
// REMOVE_DEVICE must be handled first by the driver at the top of the device stack (this device)
|
|||
|
// and then by each next-lower driver (USBD) in the stack. A driver is not required to wait for underlying drivers to
|
|||
|
// finish their remove operations before continuing with its remove activities.
|
|||
|
//
|
|||
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|||
|
status = IoCallDriver( pDevExt->NextDevice, Irp );
|
|||
|
|
|||
|
//
|
|||
|
// wait for any pending I/O
|
|||
|
//
|
|||
|
ReleaseRemoveLockAndWait(&pDevExt->RemoveLock, Irp);
|
|||
|
|
|||
|
//
|
|||
|
// cleanup any resources...
|
|||
|
//
|
|||
|
UsbFreeReadBuffer( DeviceObject );
|
|||
|
|
|||
|
// free up notification buffer
|
|||
|
if(pDevExt->IntBuff) {
|
|||
|
ExFreePool(pDevExt->IntBuff);
|
|||
|
pDevExt->IntBuff = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// delete LookasideLists
|
|||
|
//
|
|||
|
ExDeleteNPagedLookasideList( &pDevExt->PacketPool );
|
|||
|
ExDeleteNPagedLookasideList( &pDevExt->BulkTransferUrbPool );
|
|||
|
ExDeleteNPagedLookasideList( &pDevExt->PipeRequestUrbPool );
|
|||
|
ExDeleteNPagedLookasideList( &pDevExt->VendorRequestUrbPool );
|
|||
|
ExDeleteNPagedLookasideList( &pDevExt->WorkItemPool );
|
|||
|
|
|||
|
if ( !g_isWin9x && g_ExposeComPort ) {
|
|||
|
// cleanup "COMx:" namespace
|
|||
|
UndoSerialPortNaming(pDevExt);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Dump PERF data
|
|||
|
//
|
|||
|
#if PERFORMANCE
|
|||
|
if (DebugLevel & DBG_PERF )
|
|||
|
{
|
|||
|
DumpPerfCounters();
|
|||
|
|
|||
|
DbgPrint("USB IN wMaxPacketSize: %d\n", pDevExt->ReadPipe.MaxPacketSize);
|
|||
|
DbgPrint("USB OUT wMaxPacketSize: %d\n\n", pDevExt->WritePipe.MaxPacketSize );
|
|||
|
if ( pDevExt->IntPipe.hPipe) {
|
|||
|
DbgPrint("USB INT wMaxPacketSize: %d\n", pDevExt->IntPipe.MaxPacketSize);
|
|||
|
DbgPrint("USB INT Timeout: %d msec\n\n", -(pDevExt->IntReadTimeOut.QuadPart) / 10000 );
|
|||
|
}
|
|||
|
|
|||
|
DbgPrint("TTL User Write Bytes : %d\n", pDevExt->TtlWriteBytes );
|
|||
|
DbgPrint("TTL User Write Requests: %d\n\n", pDevExt->TtlWriteRequests );
|
|||
|
|
|||
|
DbgPrint("TTL User Read Bytes: %d\n", pDevExt->TtlReadBytes );
|
|||
|
DbgPrint("TTL User Read Requests: %d\n\n", pDevExt->TtlReadRequests );
|
|||
|
|
|||
|
DbgPrint("TTL USB Read Bytes: %d\n", pDevExt->TtlUSBReadBytes );
|
|||
|
DbgPrint("TTL USB Read Requests: %d\n\n", pDevExt->TtlUSBReadRequests );
|
|||
|
|
|||
|
DbgPrint("USB Read Buffer Size: %d\n", pDevExt->UsbReadBuffSize );
|
|||
|
// Note: this signals the error condition: USB overran the *UsbReadBuffer* pending down the stack.
|
|||
|
DbgPrint("USB Read Buffer Overruns: %d\n\n", pDevExt->TtlUSBReadBuffOverruns );
|
|||
|
|
|||
|
#if USE_RING_BUFF
|
|||
|
DbgPrint("Internal RingBuffer Size: %d\n", pDevExt->RingBuff.Size );
|
|||
|
DbgPrint("Internal RingBuffer Overruns: %d\n\n", pDevExt->TtlRingBuffOverruns);
|
|||
|
#endif
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
DbgDump(DBG_PNP|DBG_TRACE, ("<RemoveDevice (0x%x)\n", status));
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
Pnp(
|
|||
|
IN PDEVICE_OBJECT PDevObj,
|
|||
|
IN PIRP PIrp
|
|||
|
)
|
|||
|
{
|
|||
|
NTSTATUS status = STATUS_SUCCESS;
|
|||
|
PIO_STACK_LOCATION pIrpSp;
|
|||
|
PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
|||
|
|
|||
|
PVOID IoBuffer;
|
|||
|
ULONG InputBufferLength;
|
|||
|
UCHAR MinorFunction;
|
|||
|
BOOLEAN PassDown = TRUE;
|
|||
|
|
|||
|
DbgDump(DBG_PNP|DBG_TRACE, (">Pnp)\n"));
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
status = AcquireRemoveLock(&pDevExt->RemoveLock, PIrp);
|
|||
|
if ( !NT_SUCCESS(status) ) {
|
|||
|
DbgDump(DBG_ERR, ("Pnp:(0x%x)\n", status));
|
|||
|
PIrp->IoStatus.Status = status;
|
|||
|
IoCompleteRequest(PIrp, IO_NO_INCREMENT);
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
pIrpSp = IoGetCurrentIrpStackLocation(PIrp);
|
|||
|
MinorFunction = pIrpSp->MinorFunction;
|
|||
|
IoBuffer = PIrp->AssociatedIrp.SystemBuffer;
|
|||
|
InputBufferLength = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
|
|||
|
|
|||
|
DbgDump(DBG_PNP, ("%s\n", PnPMinorFunctionString(MinorFunction)));
|
|||
|
|
|||
|
switch (MinorFunction) {
|
|||
|
|
|||
|
case IRP_MN_START_DEVICE:
|
|||
|
//
|
|||
|
// We cannot send the device any Non-PnP IRPs until
|
|||
|
// START_DEVICE has been propogated down the device stack
|
|||
|
//
|
|||
|
ASSERT( (PnPStateAttached == pDevExt->PnPState) ||
|
|||
|
(PnPStateStopped == pDevExt->PnPState) );
|
|||
|
|
|||
|
status = StartDevice(PDevObj, PIrp);
|
|||
|
PassDown = FALSE;
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_STOP_DEVICE:
|
|||
|
|
|||
|
status = StopDevice(PDevObj, PIrp);
|
|||
|
|
|||
|
InterlockedExchange((PULONG)&pDevExt->PnPState, PnPStateStopped);
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_SURPRISE_REMOVAL:
|
|||
|
//
|
|||
|
// * Win 2000 only *
|
|||
|
//
|
|||
|
status = StopDevice(PDevObj, PIrp);
|
|||
|
|
|||
|
InterlockedExchange((PULONG)&pDevExt->PnPState, PnPStateSupriseRemove);
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_REMOVE_DEVICE:
|
|||
|
//
|
|||
|
// sent when the device has been removed and probably physically detached
|
|||
|
// from the computer. As with STOP_DEVICE, the driver cannot
|
|||
|
// assume it has received any previous query and may have to
|
|||
|
// explicitly cancel any pending I/O IRPs it has staged.
|
|||
|
//
|
|||
|
status = RemoveDevice(PDevObj, PIrp);
|
|||
|
|
|||
|
//
|
|||
|
// detach device from stack &
|
|||
|
//
|
|||
|
IoDetachDevice(pDevExt->NextDevice);
|
|||
|
|
|||
|
//
|
|||
|
// delete our FDO and symbolic link
|
|||
|
//
|
|||
|
DeleteDevObjAndSymLink(PDevObj);
|
|||
|
|
|||
|
//
|
|||
|
// A function driver does not specify an IoCompletion routine for a remove IRP,
|
|||
|
// nor does it complete the IRP. Remove IRPs are completed by the parent bus driver.
|
|||
|
// The device object & extension are now gone... don't touch it.
|
|||
|
//
|
|||
|
PassDown = FALSE;
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|||
|
InterlockedExchange((PULONG)&pDevExt->PnPState, PnPStateRemovePending);
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|||
|
InterlockedExchange((PULONG)&pDevExt->PnPState, PnPStateStarted);
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|||
|
InterlockedExchange((PULONG)&pDevExt->PnPState, PnPStateStopPending);
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|||
|
InterlockedExchange((PULONG)&pDevExt->PnPState, PnPStateStarted);
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_QUERY_CAPABILITIES: {
|
|||
|
KEVENT Event;
|
|||
|
|
|||
|
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
|
|||
|
|
|||
|
IoCopyCurrentIrpStackLocationToNext(PIrp);
|
|||
|
|
|||
|
IoSetCompletionRoutine( PIrp, SyncCompletion, &Event, TRUE, TRUE, TRUE);
|
|||
|
|
|||
|
status = IoCallDriver(pDevExt->NextDevice, PIrp);
|
|||
|
if (status == STATUS_PENDING) {
|
|||
|
KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL);
|
|||
|
}
|
|||
|
|
|||
|
status = PIrp->IoStatus.Status;
|
|||
|
if ( STATUS_SUCCESS == status ) {
|
|||
|
//
|
|||
|
// add in our capabilities
|
|||
|
//
|
|||
|
PDEVICE_CAPABILITIES pDevCaps = NULL;
|
|||
|
|
|||
|
pDevCaps = pIrpSp->Parameters.DeviceCapabilities.Capabilities;
|
|||
|
|
|||
|
//
|
|||
|
// touch Device PnP capabilities here...
|
|||
|
//
|
|||
|
pDevCaps->LockSupported = 0;
|
|||
|
pDevCaps->Removable = 1;
|
|||
|
pDevCaps->DockDevice = 0;
|
|||
|
pDevCaps->SilentInstall = 1;
|
|||
|
pDevCaps->SurpriseRemovalOK = 1;
|
|||
|
|
|||
|
//
|
|||
|
// touch Device Power capabilities here...
|
|||
|
//
|
|||
|
}
|
|||
|
PassDown = FALSE;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: {
|
|||
|
if (g_isWin9x) {
|
|||
|
status = PIrp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
|
|||
|
PassDown = FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case IRP_MN_QUERY_PNP_DEVICE_STATE: {
|
|||
|
//
|
|||
|
// If the device took too many device errors then UsbResetOrAbortPipeWorkItem
|
|||
|
// disabled the device and called IoInvalidateDeviceState.
|
|||
|
// We only handle this Irp if we were disabled or marked as removed
|
|||
|
//
|
|||
|
KIRQL irql;
|
|||
|
#if PnP_AS
|
|||
|
BOOLEAN bDisableInterface = FALSE;
|
|||
|
#endif
|
|||
|
|
|||
|
KeAcquireSpinLock(&pDevExt->ControlLock, &irql);
|
|||
|
|
|||
|
if (InterlockedCompareExchange(&pDevExt->DeviceRemoved, TRUE, TRUE)) {
|
|||
|
//
|
|||
|
// Do not set the PNP_DEVICE_REMOVED bit, else DevMan will mark the driver as banged out
|
|||
|
// until the next reboot; but stop taking requests.
|
|||
|
//
|
|||
|
DbgDump(DBG_WRN, ("PnP State: PNP_DEVICE_REMOVED\n"));
|
|||
|
|
|||
|
InterlockedExchange(&pDevExt->AcceptingRequests, FALSE);
|
|||
|
#if PnP_AS
|
|||
|
bDisableInterface = TRUE;
|
|||
|
#endif
|
|||
|
|
|||
|
} else if ( !CanAcceptIoRequests(PDevObj, FALSE, FALSE) ) {
|
|||
|
|
|||
|
DbgDump(DBG_WRN, ("PnP State: PNP_DEVICE_FAILED\n"));
|
|||
|
|
|||
|
PIrp->IoStatus.Information |= PNP_DEVICE_FAILED;
|
|||
|
|
|||
|
status = PIrp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
#if PnP_AS
|
|||
|
bDisableInterface = TRUE;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
KeReleaseSpinLock(&pDevExt->ControlLock, irql);
|
|||
|
|
|||
|
#if PnP_AS
|
|||
|
// This is a great place to disable the interface, but unfortunately ActiveSync 3.1 will not reopen the device afterwards...
|
|||
|
// It misses about every other PnP this way. By *not* disableing the interface here then AS's only indication that anything is wrong is
|
|||
|
// by noticing that it's Read/Write/Serial requests get rejected, and AS will eventually timeout after some time dT ...
|
|||
|
// sometimes more than 5 seconds on Read/Writes. However, it does not sense Timeouts on Serial IOCTLS so will keep
|
|||
|
// sending us Serial requests, which will cause the bugcheck 0xCE in Set DTR. Disabeling the interface has the desired effect of
|
|||
|
// disallowing apps from sending us *ANY* requests.
|
|||
|
// This is an AS bug - there is pending email with kentce about this.
|
|||
|
if (bDisableInterface && pDevExt->DeviceClassSymbolicName.Buffer) {
|
|||
|
//
|
|||
|
// set the interface state to inactive to let ActiveSync know to release the handle. Must be done @ PASSIVE_LEVEL
|
|||
|
//
|
|||
|
status = IoSetDeviceInterfaceState(&pDevExt->DeviceClassSymbolicName, FALSE );
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
DbgDump(DBG_WRN, ("IoSetDeviceInterfaceState.1: OFF\n"));
|
|||
|
}
|
|||
|
}
|
|||
|
#endif // PnP_AS
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (IRP_MN_REMOVE_DEVICE != MinorFunction) {
|
|||
|
|
|||
|
ReleaseRemoveLock(&pDevExt->RemoveLock, PIrp);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (PassDown) {
|
|||
|
|
|||
|
IoCopyCurrentIrpStackLocationToNext(PIrp);
|
|||
|
|
|||
|
status = IoCallDriver(pDevExt->NextDevice, PIrp);
|
|||
|
|
|||
|
} else if (IRP_MN_REMOVE_DEVICE != MinorFunction) {
|
|||
|
|
|||
|
IoCompleteRequest(PIrp, IO_NO_INCREMENT);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
DbgDump(DBG_PNP|DBG_TRACE, ("<Pnp (0x%x)\n", status));
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SyncCompletion(
|
|||
|
IN PDEVICE_OBJECT PDevObj,
|
|||
|
IN PIRP PIrp,
|
|||
|
IN PKEVENT PSyncEvent
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is used to signal an event.
|
|||
|
It is used as a default completion routine.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
PDevObj - Pointer to Device Object
|
|||
|
PIrp - Pointer to IRP that is being completed
|
|||
|
PSyncEvent - Pointer to event that we should set
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_MORE_PROCESSING_REQUIRED
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
UNREFERENCED_PARAMETER( PDevObj );
|
|||
|
UNREFERENCED_PARAMETER( PIrp );
|
|||
|
|
|||
|
KeSetEvent( PSyncEvent, IO_NO_INCREMENT, FALSE );
|
|||
|
|
|||
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
Power(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
|||
|
|
|||
|
DbgDump(DBG_PNP, (">PnpPower (%p, %p)\n", DeviceObject, Irp));
|
|||
|
|
|||
|
//
|
|||
|
// If the device has been removed, the driver should not pass
|
|||
|
// the IRP down to the next lower driver.
|
|||
|
//
|
|||
|
if ( PnPStateRemoved == pDevExt->PnPState ) {
|
|||
|
|
|||
|
PoStartNextPowerIrp(Irp);
|
|||
|
Irp->IoStatus.Status = STATUS_DELETE_PENDING;
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT );
|
|||
|
|
|||
|
return STATUS_DELETE_PENDING;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// passthrough
|
|||
|
//
|
|||
|
PoStartNextPowerIrp( Irp );
|
|||
|
IoSkipCurrentIrpStackLocation( Irp );
|
|||
|
|
|||
|
DbgDump( DBG_PNP, ("<PnpPower\n") );
|
|||
|
|
|||
|
return PoCallDriver( pDevExt->NextDevice, Irp );
|
|||
|
}
|
|||
|
|
|||
|
// EOF
|