windows-nt/Source/XPSP1/NT/drivers/wdm/usb/driver/wceusbsh/wceusbsh.c

1153 lines
29 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/* ++
Copyright (c) 1999-2000 Microsoft Corporation
Module Name:
wceusbsh.c
Abstract:
Main entrypoint for Windows CE USB Serial Host driver, for
... Windows CE USB sync devices:
SL11, Socket CF cards, HP Jornada, COMPAQ iPAQ, Casio Cassiopeia, etc.
... cables using the Anchor AN27x0 chipset (i.e. EZ-Link)
... ad-hoc USB NULL Modem Class
Environment:
kernel mode only
Author:
Jeff Midkiff (jeffmi)
Revision History:
07-15-99 : rev 1.00 ActiveSync 3.1 initial release
04-20-00 : rev 1.01 Cedar 3.0 Platform Builder
09-20-00 : rev 1.02 finally have some hardware
Notes:
o) WCE Devices currently do not handle remote wake, nor can we put the device in power-off state when not used, etc.
o) Pageable Code sections are marked as follows:
PAGEWCE0 - useable only during init/deinit
PAGEWCE1 - useable during normal runtime
-- */
#include "wceusbsh.h"
//
// This is currently missing from wdm.h,
// but IoUnregisterShutdownNotification is there
//
#if !defined( IoRegisterShutdownNotification )
NTKERNELAPI
NTSTATUS
IoRegisterShutdownNotification(
IN PDEVICE_OBJECT DeviceObject
);
#endif
NTSTATUS
Create(
IN PDEVICE_OBJECT PDevObj,
IN PIRP PIrp
);
NTSTATUS
Close(
IN PDEVICE_OBJECT PDevObj,
IN PIRP PIrp
);
NTSTATUS
Cleanup(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
VOID
Unload(
IN PDRIVER_OBJECT DriverObject
);
NTSTATUS
SetInformationFile(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
QueryInformationFile(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
Flush(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
Shutdown(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
VOID
KillAllPendingUserIrps(
PDEVICE_OBJECT PDevObj
);
NTSTATUS
SystemControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
//
// GLOBALS
//
BOOLEAN g_isWin9x = FALSE;
BOOLEAN g_ExposeComPort = FALSE;
LONG g_NumDevices;
LONG g_lIntTimout = DEFAULT_INT_PIPE_TIMEOUT;
ULONG g_ulAlternateSetting = 0;
ULONG g_ulMaxPipeErrors = DEFAULT_MAX_PIPE_DEVICE_ERRORS;
ULONG DebugLevel;
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGEWCE0, AddDevice)
#pragma alloc_text(PAGEWCE1, Unload)
#pragma alloc_text(PAGEWCE1, Flush)
#pragma alloc_text(PAGEWCE1, QueryInformationFile)
#pragma alloc_text(PAGEWCE1, SetInformationFile)
#pragma alloc_text(PAGEWCE1, Shutdown)
#pragma alloc_text(PAGEWCE1, UsbFreeReadBuffer)
#endif
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT PDrvObj,
IN PUNICODE_STRING PRegistryPath
)
{
#ifdef DBG
CHAR VersionHerald[] = "Windows CE USB Serial Host, Version %s built on %s\n";
CHAR VersionNumber[] = "1.02";
CHAR VersionTimestamp[] = __DATE__ " " __TIME__;
#endif
PAGED_CODE();
KdPrint((VersionHerald, VersionNumber, VersionTimestamp));
//
// determine OS
//
g_isWin9x = IsWin9x();
KdPrint(("This is Win %s\n", g_isWin9x ? "9x" : "NT" ));
PDrvObj->MajorFunction[IRP_MJ_CREATE] = Create;
PDrvObj->MajorFunction[IRP_MJ_CLOSE] = Close;
PDrvObj->MajorFunction[IRP_MJ_CLEANUP] = Cleanup;
PDrvObj->MajorFunction[IRP_MJ_READ] = Read;
PDrvObj->MajorFunction[IRP_MJ_WRITE] = Write;
PDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = SerialIoctl;
PDrvObj->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = Flush;
PDrvObj->MajorFunction[IRP_MJ_QUERY_INFORMATION] = QueryInformationFile;
PDrvObj->MajorFunction[IRP_MJ_SET_INFORMATION] = SetInformationFile;
PDrvObj->DriverExtension->AddDevice = AddDevice;
PDrvObj->MajorFunction[IRP_MJ_PNP] = Pnp;
PDrvObj->MajorFunction[IRP_MJ_POWER] = Power;
PDrvObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = SystemControl;
PDrvObj->MajorFunction[IRP_MJ_SHUTDOWN] = Shutdown;
PDrvObj->DriverUnload = Unload;
//
// initialize Globals
//
g_NumDevices = 0;
QueryRegistryParameters( PRegistryPath );
DbgDump(DBG_INIT, ("Create @ %p\n", Create));
DbgDump(DBG_INIT, ("Close @ %p\n", Close));
DbgDump(DBG_INIT, ("Cleanup @ %p\n", Cleanup));
DbgDump(DBG_INIT, ("Read @ %p\n", Read));
DbgDump(DBG_INIT, ("Write @ %p\n", Write));
DbgDump(DBG_INIT, ("SerialIoctl @ %p\n", SerialIoctl));
DbgDump(DBG_INIT, ("Flush @ %p\n", Flush));
DbgDump(DBG_INIT, ("QueryInformationFile @ %p\n", QueryInformationFile));
DbgDump(DBG_INIT, ("SetInformationFile @ %p\n", SetInformationFile));
DbgDump(DBG_INIT, ("AddDevice @ %p\n", AddDevice));
DbgDump(DBG_INIT, ("Pnp @ %p\n", Pnp));
DbgDump(DBG_INIT, ("Power @ %p\n", Power));
DbgDump(DBG_INIT, ("Shutdown @ %p\n", Shutdown));
DbgDump(DBG_INIT, ("Unload @ %p\n", Unload));
return STATUS_SUCCESS;
}
NTSTATUS
AddDevice(
IN PDRIVER_OBJECT PDrvObj,
IN PDEVICE_OBJECT PPDO
)
/*++
Routine Description:
Add our driver to the USB device stack.
This also creates our base device name and symbolic link.
Arguments:
PDrvObj - Pointer to our driver object
PPDO - Pointer to the PDO for the stack to which we should add ourselves
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
PDEVICE_OBJECT pDevObj = NULL;
PDEVICE_EXTENSION pDevExt = NULL;
LONG comPortNumber=0;
BOOLEAN bListsInitilized = FALSE;
ULONG UniqueErrorValue = 0;
DbgDump(DBG_INIT, (">AddDevice\n"));
PAGED_CODE();
//
// Create the FDO
//
if (PPDO == NULL) {
DbgDump(DBG_ERR, ("No PDO\n"));
return STATUS_NO_MORE_ENTRIES;
}
//
// create a named device object
// and unprotected symbolic link.
//
status = CreateDevObjAndSymLink(
PDrvObj,
PPDO,
&pDevObj,
DRV_NAME );
if ( (status != STATUS_SUCCESS) || !pDevObj ) {
DbgDump(DBG_ERR, ("CreateDevObjAndSymLink error: 0x%x\n", status));
UniqueErrorValue = ERR_NO_DEVICE_OBJ;
goto AddDeviceFailed;
}
DbgDump( DBG_INIT, ("DevObj: %p\n", pDevObj));
// init our device extension
//
pDevExt = pDevObj->DeviceExtension;
pDevExt->DeviceObject = pDevObj;
pDevExt->PDO = PPDO;
// init our states
//
InterlockedExchange((PULONG)&pDevExt->PnPState, PnPStateInitialized);
#ifdef POWER
pDevExt->DevicePowerState= PowerDeviceD0;
#endif
// set FDO flags
//
ASSERT( !(pDevObj->Flags & DO_POWER_PAGABLE) );
pDevObj->Flags |= (PPDO->Flags & DO_POWER_PAGABLE);
pDevObj->Flags |= DO_BUFFERED_IO;
pDevObj->Flags &= ~ DO_DEVICE_INITIALIZING;
//
// Create or initialize any other non-hardware resources here.
// These items get cleaned up in IRP_MN_REMOVE_DEVICE....
//
// Initialize locks
//
KeInitializeSpinLock(&pDevExt->ControlLock);
InitializeRemoveLock( &pDevExt->RemoveLock );
//
// Initialize USB Read Buffer, This value has an effect on performance.
// In addition to testing the endpoint's MaximumPacketSize (64 byte max),
// I tested 512, 1024, 2048, & 4096 across the EZ-Link, SL11, & CF.
// 1024, 2048, and 4096 all gave similiar results which were much faster than 64 bytes
// or even 512.
//
// EZ-Link Note: the pserial perf tests can sometimes go into timeout/retry/abort
// situatiuon in the 2nd phase of a test. This is because it closes and then re-opens (so therefore purges) the driver's read buffer.
// The driver's USB read buffer is purged of a full 960 byte device FIFO, already consumed by the driver.
// This is viewable in the debugger using DBG_READ_LENGTH. This does not happen with ActiveSync.
//
pDevExt->UsbReadBuffSize = USB_READBUFF_SIZE;
pDevExt->UsbReadBuff = ExAllocatePool( NonPagedPool, pDevExt->UsbReadBuffSize );
if ( !pDevExt->UsbReadBuff ) {
status = STATUS_INSUFFICIENT_RESOURCES;
UniqueErrorValue = ERR_NO_USBREAD_BUFF;
goto AddDeviceFailed;
}
pDevExt->MaximumTransferSize = DEFAULT_PIPE_MAX_TRANSFER_SIZE;
#if defined (USE_RING_BUFF)
// setup Ring Buffer
pDevExt->RingBuff.Size = RINGBUFF_SIZE;
pDevExt->RingBuff.pHead =
pDevExt->RingBuff.pTail =
pDevExt->RingBuff.pBase = ExAllocatePool( NonPagedPool, pDevExt->RingBuff.Size );
if ( !pDevExt->RingBuff.pBase ) {
status = STATUS_INSUFFICIENT_RESOURCES;
UniqueErrorValue = ERR_NO_RING_BUFF;
goto AddDeviceFailed;
}
#endif
// Initialize events
//
KeInitializeEvent( &pDevExt->PendingDataInEvent, NotificationEvent /*SynchronizationEvent*/, FALSE);
KeInitializeEvent( &pDevExt->PendingDataOutEvent, NotificationEvent /*SynchronizationEvent*/, FALSE);
KeInitializeEvent( &pDevExt->PendingIntEvent, NotificationEvent /*SynchronizationEvent*/, FALSE);
KeInitializeEvent( &pDevExt->PendingWorkItemsEvent, NotificationEvent /*SynchronizationEvent*/, FALSE);
//
// initialize nonpaged pools...
//
ExInitializeNPagedLookasideList(
&pDevExt->PacketPool, // Lookaside,
NULL, // Allocate OPTIONAL,
NULL, // Free OPTIONAL,
0, // Flags,
sizeof(USB_PACKET), // Size,
WCEUSB_POOL_TAG, // Tag,
0 ); // Depth
DbgDump(DBG_INIT, ("PacketPool: %p\n", &pDevExt->PacketPool));
ExInitializeNPagedLookasideList(
&pDevExt->BulkTransferUrbPool,
NULL, NULL, 0,
sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
WCEUSB_POOL_TAG,
0 );
DbgDump(DBG_INIT, ("BulkTransferUrbPool: %p\n", &pDevExt->BulkTransferUrbPool));
ExInitializeNPagedLookasideList(
&pDevExt->PipeRequestUrbPool,
NULL, NULL, 0,
sizeof(struct _URB_PIPE_REQUEST),
WCEUSB_POOL_TAG,
0 );
DbgDump(DBG_INIT, ("PipeRequestUrbPool: %p\n", &pDevExt->PipeRequestUrbPool));
ExInitializeNPagedLookasideList(
&pDevExt->VendorRequestUrbPool,
NULL, NULL, 0,
sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST),
WCEUSB_POOL_TAG,
0 );
DbgDump(DBG_INIT, ("VendorRequestUrbPool: %p\n", &pDevExt->VendorRequestUrbPool));
ExInitializeNPagedLookasideList(
&pDevExt->WorkItemPool,
NULL, NULL, 0,
sizeof(WCE_WORK_ITEM),
WCEUSB_POOL_TAG,
0 );
DbgDump(DBG_INIT, ("WorkItemPool: %p\n", &pDevExt->WorkItemPool));
bListsInitilized = TRUE;
//
// initialize pending I/O lists
//
InitializeListHead( &pDevExt->PendingReadPackets );
pDevExt->PendingReadCount = 0;
InitializeListHead( &pDevExt->PendingWritePackets );
pDevExt->PendingWriteCount = 0;
InitializeListHead(&pDevExt->UserReadQueue);
InitializeListHead( &pDevExt->PendingWorkItems );
pDevExt->PendingWorkItemsCount = 0;
//
// Win 2000 ONLY : setup external SerialPort (COMx) interface
// iff the user setup the magic reg key under
// HKLM\SYSTEM\ControlSet\Services\wceusbsh\Parameters\ExposeComPort:REG_DWORD:1
// This is NOT required for ActiveSync, only testing and is disabled by default.
//
// The Win9x CommXxx API *requires* going through VCOMM. Thus, we must
// be installed as a virtual modem, and use ccport.sys and wdmmdmld.vxd ... NFW.
//
if ( !g_isWin9x && g_ExposeComPort ) {
//
// N.B. we don't want to use the static port name from the registry because the device
// can come & go quickly (power up/down, etc.) and run into name collisions.
//comPortNumber = GetComPort(pDevObj, pDevExt->SerialPort.Com.Instance-1);
comPortNumber = GetFreeComPortNumber( );
if (-1 == comPortNumber) {
status = STATUS_DEVICE_DATA_ERROR;
UniqueErrorValue = ERR_COMM_SYMLINK;
goto AddDeviceFailed;
}
status = DoSerialPortNaming( pDevExt, comPortNumber );
if (status != STATUS_SUCCESS) {
UniqueErrorValue = ERR_COMM_SYMLINK;
DbgDump(DBG_ERR, ("DoSerialPortNaming error: 0x%x\n", status));
goto AddDeviceFailed;
}
status = IoRegisterShutdownNotification( pDevExt->DeviceObject );
if (status != STATUS_SUCCESS) {
UniqueErrorValue = ERR_COMM_SYMLINK;
DbgDump(DBG_ERR, ("IoRegisterShutdownNotification error: 0x%x\n", status));
TEST_TRAP();
goto AddDeviceFailed;
}
} else {
DbgDump(DBG_INIT, ("!GetFreeComPortNumber(%d, %d)\n", g_isWin9x, g_ExposeComPort));
}
//
// attach to device stack
//
pDevExt->NextDevice = IoAttachDeviceToDeviceStack(pDevObj, PPDO);
if ( !pDevExt->NextDevice ) {
status = STATUS_NO_SUCH_DEVICE;
DbgDump(DBG_ERR, ("IoAttachDeviceToDeviceStack error: 0x%x\n", status));
} else {
// set state after we attach to the stack
InterlockedExchange((PULONG)&pDevExt->PnPState, PnPStateAttached);
}
#if PERFORMANCE
InitPerfCounters();
#endif
AddDeviceFailed:
if (status != STATUS_SUCCESS) {
if (pDevObj != NULL) {
UsbFreeReadBuffer( pDevObj );
if (pDevExt) {
if (pDevExt->NextDevice) {
DbgDump(DBG_INIT, ("Detach from PDO\n"));
IoDetachDevice(pDevExt->NextDevice);
}
if ( bListsInitilized) {
//
// delete LookasideLists
//
DbgDump(DBG_INIT, ("Deleting LookasideLists\n"));
ExDeleteNPagedLookasideList( &pDevExt->PacketPool );
ExDeleteNPagedLookasideList( &pDevExt->BulkTransferUrbPool );
ExDeleteNPagedLookasideList( &pDevExt->PipeRequestUrbPool );
ExDeleteNPagedLookasideList( &pDevExt->VendorRequestUrbPool );
ExDeleteNPagedLookasideList( &pDevExt->WorkItemPool );
}
UndoSerialPortNaming(pDevExt);
}
ReleaseSlot( PtrToLong(NULL) );
DeleteDevObjAndSymLink(pDevObj);
}
}
if (STATUS_INSUFFICIENT_RESOURCES == status) {
DbgDump(DBG_ERR, ("AddDevice ERROR: 0x%x, %d\n", status, UniqueErrorValue));
LogError( PDrvObj,
NULL,
0, 0, 0,
UniqueErrorValue,
status,
SERIAL_INSUFFICIENT_RESOURCES,
(pDevExt && pDevExt->DeviceName.Buffer) ? pDevExt->DeviceName.Length + sizeof(WCHAR) : 0,
(pDevExt && pDevExt->DeviceName.Buffer) ? pDevExt->DeviceName.Buffer : NULL,
0, NULL );
} else if (STATUS_SUCCESS != status ) {
// handles all other failures
LogError( PDrvObj,
NULL,
0, 0, 0,
UniqueErrorValue,
status,
SERIAL_INIT_FAILED,
(pDevExt && pDevExt->DeviceName.Buffer) ? pDevExt->DeviceName.Length + sizeof(WCHAR) : 0,
(pDevExt && pDevExt->DeviceName.Buffer) ? pDevExt->DeviceName.Buffer : NULL,
0, NULL );
}
DbgDump(DBG_INIT, ("<AddDevice 0x%x\n", status));
return status;
}
NTSTATUS
Create(
IN PDEVICE_OBJECT PDevObj,
IN PIRP PIrp
)
{
PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
NTSTATUS status;
DbgDump(DBG_INIT|DBG_TRACE, (">Create (%p)\n", PDevObj));
if (!CanAcceptIoRequests(PDevObj, TRUE, FALSE) ||
!NT_SUCCESS(AcquireRemoveLock(&pDevExt->RemoveLock, IRP_MJ_CREATE)))
{
status = STATUS_DELETE_PENDING;
DbgDump(DBG_ERR, ("Create: 0x%x\n", status));
PIrp->IoStatus.Status = status;
IoCompleteRequest(PIrp, IO_NO_INCREMENT);
return status;
}
ASSERT_SERIAL_PORT(pDevExt->SerialPort);
//
// Serial devices do not allow multiple concurrent opens
//
if ( InterlockedIncrement( &pDevExt->SerialPort.Com.OpenCnt ) != 1 ) {
InterlockedDecrement( &pDevExt->SerialPort.Com.OpenCnt );
status = STATUS_ACCESS_DENIED;
DbgDump(DBG_ERR, ("OpenComPort ERROR: 0x%x\n", status));
goto CreateDone;
}
InterlockedExchange(&pDevExt->DeviceOpened, TRUE);
// Take out an additional reference on ourself.
// We are seeing a possible premature unload with open handles in ActiveSync.
// We dereference it in IRP_MJ_CLEANUP instead or IRP_MJ_CLOSE in case the app crashes
// where we wouldn't otherwise get it.
ObReferenceObject( PDevObj );
//
// reset the virtual serial port interface,
// but don't send anything on the bus yet
//
status = SerialResetDevice(pDevExt, PIrp, FALSE);
if (STATUS_SUCCESS == status) {
//
// CederRapier BUGBUG 13310: clean the read buffer when the app does CreateFile.
//
status = SerialPurgeRxClear(PDevObj, TRUE );
if ( NT_SUCCESS(status) ) {
#if !defined(DELAY_RXBUFF)
// this will subit the read a bit earlier, making the connection faster
if ( !pDevExt->IntPipe.hPipe ) {
DbgDump(DBG_INIT, ("Create: kick starting another USB Read\n" ));
status = UsbRead( pDevExt, FALSE );
} else {
DbgDump(DBG_INIT, ("Create: kick starting another USB INT Read\n" ));
status = UsbInterruptRead( pDevExt );
}
if ( NT_SUCCESS(status) ) {
// should be STATUS_PENDING
status = STATUS_SUCCESS;
}
#else
// signal to start the RX buffer in SerIoctl
InterlockedExchange(&pDevExt->StartUsbRead, 1);
#endif
} else {
DbgDump(DBG_ERR, ("SerialPurgeRxClear ERROR: %x\n", status));
TEST_TRAP();
}
}
if (STATUS_SUCCESS != status) {
//
// Let the user know that the device can not be opened.
//
DbgDump(DBG_ERR, ("*** UNRECOVERABLE CreateFile ERROR:0x%x, No longer Accepting Requests ***\n", status));
InterlockedExchange(&pDevExt->AcceptingRequests, FALSE);
InterlockedExchange(&pDevExt->DeviceOpened, FALSE);
IoInvalidateDeviceState( pDevExt->PDO );
LogError( NULL, PDevObj,
0, IRP_MJ_CREATE,
1, // retries
ERR_NO_CREATE_FILE,
status,
SERIAL_HARDWARE_FAILURE,
pDevExt->DeviceName.Length + sizeof(WCHAR),
pDevExt->DeviceName.Buffer,
0,
NULL );
}
CreateDone:
// we release this reference on Close.
if (STATUS_SUCCESS != status) {
ReleaseRemoveLock(&pDevExt->RemoveLock, IRP_MJ_CREATE);
}
PIrp->IoStatus.Status = status;
PIrp->IoStatus.Information = 0;
IoCompleteRequest(PIrp, IO_SERIAL_INCREMENT);
DbgDump(DBG_INIT|DBG_TRACE, ("<Create 0x%x\n", status));
return status;
}
NTSTATUS
Close(
IN PDEVICE_OBJECT PDevObj,
IN PIRP PIrp
)
{
PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
NTSTATUS status = STATUS_SUCCESS;
ULONG openCount;
DbgDump(DBG_INIT|DBG_TRACE, (">Close (%p)\n", PDevObj));
PAGED_CODE();
ASSERT_SERIAL_PORT(pDevExt->SerialPort);
//
// stop any pending I/O
//
InterlockedExchange(&pDevExt->DeviceOpened, FALSE);
status = StopIo(PDevObj);
if (STATUS_SUCCESS == status) {
if ( pDevExt->SerialPort.Com.OpenCnt ) {
openCount = InterlockedDecrement( &pDevExt->SerialPort.Com.OpenCnt );
if ( openCount != 0) {
status = STATUS_UNSUCCESSFUL;
DbgDump(DBG_WRN, ("Close ERROR: 0x%x RE: %d\n", status, openCount));
TEST_TRAP();
}
#ifdef DELAY_RXBUFF
// signal our RX buffer
InterlockedExchange(&pDevExt->StartUsbRead, 0);
#endif
}
} else {
DbgDump(DBG_ERR, ("StopIo ERROR: 0x%x\n", status));
TEST_TRAP();
}
PIrp->IoStatus.Status = status;
PIrp->IoStatus.Information = 0;
IoCompleteRequest(PIrp, IO_SERIAL_INCREMENT);
if (STATUS_SUCCESS == status) {
// Release the lock acquired in IRP_MJ_CREATE.
// Warning: if the app misses our PnP signal then could we hang on this reference?
ReleaseRemoveLock(&pDevExt->RemoveLock, IRP_MJ_CREATE);
}
DbgDump(DBG_INIT|DBG_TRACE, ("<Close 0x%x\n", status));
return status;
}
NTSTATUS
Cleanup(
IN PDEVICE_OBJECT PDevObj,
IN PIRP Irp
)
{
PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
NTSTATUS status = STATUS_SUCCESS;
DbgDump(DBG_INIT, (">Cleanup\n"));
//
// stop any pending I/O
//
InterlockedExchange(&pDevExt->DeviceOpened, FALSE);
status = StopIo(PDevObj);
if (STATUS_SUCCESS != status) {
DbgDump(DBG_ERR, ("StopIo ERROR: 0x%x\n", status));
TEST_TRAP();
}
#ifdef DELAY_RXBUFF
// signal our RX buffer
InterlockedExchange(&pDevExt->StartUsbRead, 0);
#endif
// Dereference the additional reference taken on IRP_MJ_CREATE.
ObDereferenceObject( PDevObj );
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
DbgDump(DBG_INIT, ("<Cleanup 0x%x\n", status));
return status;
}
VOID
KillAllPendingUserReads(
IN PDEVICE_OBJECT PDevObj,
IN PLIST_ENTRY PQueueToClean,
IN PIRP *PpCurrentOpIrp
)
/*++
Routine Description:
cancel all queued user reads.
Arguments:
PDevObj - A pointer to the serial device object.
PQueueToClean - A pointer to the queue which we're going to clean out.
PpCurrentOpIrp - Pointer to a pointer to the current irp.
Return Value:
None.
--*/
{
PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
KIRQL irql;
NTSTATUS status;
DbgDump( DBG_IRP, (">KillAllPendingUserReads\n"));
KeAcquireSpinLock( &pDevExt->ControlLock, &irql );
//
// Clean the list from back to front.
//
while (!IsListEmpty(PQueueToClean)) {
PIRP pCurrentLastIrp = CONTAINING_RECORD( PQueueToClean->Blink,
IRP,
Tail.Overlay.ListEntry);
RemoveEntryList(PQueueToClean->Blink);
KeReleaseSpinLock( &pDevExt->ControlLock, irql );
status = ManuallyCancelIrp( PDevObj, pCurrentLastIrp);
ASSERT(STATUS_SUCCESS == status );
KeAcquireSpinLock( &pDevExt->ControlLock, &irql );
}
//
// The queue is clean. Now go after the current if
// it's there.
//
if (*PpCurrentOpIrp) {
KeReleaseSpinLock( &pDevExt->ControlLock, irql );
status = ManuallyCancelIrp( PDevObj, *PpCurrentOpIrp );
ASSERT(STATUS_SUCCESS == status );
} else {
DbgDump(DBG_IRP, ("No current Irp\n"));
KeReleaseSpinLock( &pDevExt->ControlLock, irql );
}
DbgDump( DBG_IRP, ("<KillAllPendingUserReads\n"));
return;
}
VOID
Unload(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
Undo everything setup in DriverEntry
Arguments:
DriverObject
Return Value:
VOID
--*/
{
UNREFERENCED_PARAMETER( DriverObject );
DbgDump(DBG_INIT, (">Unload\n"));
PAGED_CODE();
// release global resources here
DbgDump(DBG_INIT, ("<Unload\n"));
}
NTSTATUS
Flush(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This is the dispatch routine for flush. Flushing works by placing
this request in the write queue. When this request reaches the
front of the write queue we simply complete it since this implies
that all previous writes have completed.
Arguments:
DeviceObject - Pointer to the device object for this device
Irp - Pointer to the IRP for the current request
Return Value:
Could return status success, cancelled, or pending.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
UNREFERENCED_PARAMETER( DeviceObject );
DbgDump( DBG_INIT|DBG_READ_LENGTH|DBG_WRITE_LENGTH, ("Flush\n"));
PAGED_CODE();
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0L;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
NTSTATUS
QueryInformationFile(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is used to query the end of file information on
the opened serial port. Any other file information request
is retured with an invalid parameter.
This routine always returns an end of file of 0.
Arguments:
DeviceObject - Pointer to the device object for this device
Irp - Pointer to the IRP for the current request
Return Value:
The function value is the final status of the call
--*/
{
//
// The status that gets returned to the caller and
// set in the Irp.
//
NTSTATUS Status;
//
// The current stack location. This contains all of the
// information we need to process this particular request.
//
PIO_STACK_LOCATION IrpSp;
UNREFERENCED_PARAMETER(DeviceObject);
DbgDump( DBG_INIT|DBG_READ_LENGTH, (">QueryInformationFile\n"));
PAGED_CODE();
IrpSp = IoGetCurrentIrpStackLocation(Irp);
Irp->IoStatus.Information = 0L;
Status = STATUS_SUCCESS;
if (IrpSp->Parameters.QueryFile.FileInformationClass == FileStandardInformation) {
PFILE_STANDARD_INFORMATION Buf = Irp->AssociatedIrp.SystemBuffer;
Buf->AllocationSize.QuadPart = 0;
Buf->EndOfFile = Buf->AllocationSize;
Buf->NumberOfLinks = 0;
Buf->DeletePending = FALSE;
Buf->Directory = FALSE;
Irp->IoStatus.Information = sizeof(FILE_STANDARD_INFORMATION);
} else if (IrpSp->Parameters.QueryFile.FileInformationClass == FilePositionInformation) {
((PFILE_POSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->CurrentByteOffset.QuadPart = 0;
Irp->IoStatus.Information = sizeof(FILE_POSITION_INFORMATION);
} else {
Status = STATUS_INVALID_PARAMETER;
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
}
IoCompleteRequest(Irp, IO_NO_INCREMENT);
DbgDump( DBG_INIT|DBG_READ_LENGTH, ("<QueryInformationFile\n"));
return Status;
}
NTSTATUS
SetInformationFile(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is used to set the end of file information on
the opened parallel port. Any other file information request
is retured with an invalid parameter.
This routine always ignores the actual end of file since
the query information code always returns an end of file of 0.
Arguments:
DeviceObject - Pointer to the device object for this device
Irp - Pointer to the IRP for the current request
Return Value:
The function value is the final status of the call
--*/
{
//
// The status that gets returned to the caller and
// set in the Irp.
//
NTSTATUS Status;
UNREFERENCED_PARAMETER(DeviceObject);
PAGED_CODE();
DbgDump( DBG_INIT|DBG_READ_LENGTH, (">SetInformationFile\n"));
Irp->IoStatus.Information = 0L;
if (IoGetCurrentIrpStackLocation(Irp)->Parameters.SetFile.FileInformationClass == FileEndOfFileInformation) {
// || (IoGetCurrentIrpStackLocation(Irp)->Parameters.SetFile.FileInformationClass == FileAllocationInformation)) { // FileAllocationInformationnot defined in wdm.h
Status = STATUS_SUCCESS;
} else {
Status = STATUS_INVALID_PARAMETER;
}
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
DbgDump( DBG_INIT|DBG_READ_LENGTH, ("<SetInformationFile\n"));
return Status;
}
NTSTATUS
Shutdown(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status = STATUS_SUCCESS;
DbgDump(DBG_INIT, (">Shutdown\n"));
PAGED_CODE();
//
// Special Case - If an app has an open handle to the device,
// and the system is being shut down in a controlled manner,
// and we have not been removed via PnP, then remove the COMx name
// from the Registry's COM Name Arbitrator DataBase for the next boot cycle.
// Win NT only; Win9x does not export COMx names.
//
// N.B: we have to do this in a Shutdown handler, and NOT in the PNP_POWER handler
// because the Registry entry is NOT saved in the Power down code path.
//
if ( !g_isWin9x && g_ExposeComPort &&
pDevExt->SerialPort.Com.PortNumber &&
(PnPStateStarted == pDevExt->PnPState) ) {
//
// remove our entry from ComDB
//
ReleaseCOMPort( pDevExt->SerialPort.Com.PortNumber );
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0L;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
DbgDump(DBG_INIT, ("<Shutdown\n"));
return status;
}
VOID
UsbFreeReadBuffer(
IN PDEVICE_OBJECT PDevObj
)
{
PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
DbgDump(DBG_USB, (">UsbFreeReadBuffer %p\n", PDevObj));
PAGED_CODE();
if ( pDevExt->UsbReadBuff != NULL ) {
ExFreePool(pDevExt->UsbReadBuff);
pDevExt->UsbReadBuff = NULL;
}
#if defined (USE_RING_BUFF)
if ( pDevExt->RingBuff.pBase != NULL ) {
ExFreePool(pDevExt->RingBuff.pBase);
pDevExt->RingBuff.pBase =
pDevExt->RingBuff.pHead =
pDevExt->RingBuff.pTail = NULL;
}
#endif // USE_RING_BUFF
DbgDump(DBG_USB, ("<UsbFreeReadBuffer\n"));
return;
}
NTSTATUS
SystemControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PDEVICE_EXTENSION pDevExt;
PAGED_CODE();
DbgDump(DBG_INIT, ("SystemControl\n"));
pDevExt = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(pDevExt->NextDevice, Irp);
}
// EOF