1042 lines
27 KiB
C
1042 lines
27 KiB
C
/*++
|
||
|
||
Copyright (c) 1999-2000 Microsoft Corporation
|
||
|
||
Module Name :
|
||
|
||
common.c
|
||
|
||
Abstract:
|
||
|
||
Common code for the Windows CE
|
||
USB Serial Host and Filter drivers
|
||
|
||
Author:
|
||
|
||
Jeff Midkiff (jeffmi) 08-24-99
|
||
|
||
--*/
|
||
#include <stdio.h>
|
||
|
||
#include "wceusbsh.h"
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGEWCE0, QueryRegistryParameters)
|
||
#pragma alloc_text(PAGEWCE0, CreateDevObjAndSymLink)
|
||
#pragma alloc_text(PAGEWCE0, DeleteDevObjAndSymLink)
|
||
#pragma alloc_text(PAGEWCE0, IsWin9x)
|
||
|
||
#pragma alloc_text(PAGEWCE1, LogError)
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
QueryRegistryParameters(
|
||
IN PUNICODE_STRING RegistryPath
|
||
)
|
||
/*++
|
||
|
||
This routine queryies the Registry for our Parameters key.
|
||
We are given the RegistryPath to our driver during DriverEntry,
|
||
but don't yet have an extension, so we store the values in globals
|
||
until we get our device extension.
|
||
|
||
The values are setup from our INF.
|
||
|
||
On WinNT this is under
|
||
HKLM\SYSTEM\ControlSet\Services\wceusbsh\Parameters
|
||
|
||
On Win98 this is under
|
||
HKLM\System\CurrentControlSet\Services\Class\WCESUSB\000*
|
||
|
||
|
||
Returns - nothing; use defaults
|
||
|
||
--*/
|
||
{
|
||
#define NUM_REG_ENTRIES 6
|
||
RTL_QUERY_REGISTRY_TABLE rtlQueryRegTbl[ NUM_REG_ENTRIES + 1 ];
|
||
|
||
ULONG sizeOfUl = sizeof( ULONG );
|
||
ULONG ulAlternateSetting = DEFAULT_ALTERNATE_SETTING;
|
||
LONG lIntTimout = DEFAULT_INT_PIPE_TIMEOUT;
|
||
ULONG ulMaxPipeErrors = DEFAULT_MAX_PIPE_DEVICE_ERRORS;
|
||
ULONG ulDebugLevel = DBG_OFF;
|
||
ULONG ulExposeComPort = FALSE;
|
||
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT( RegistryPath != NULL );
|
||
|
||
RtlZeroMemory( rtlQueryRegTbl, sizeof(rtlQueryRegTbl) );
|
||
|
||
//
|
||
// Setup the query table
|
||
// Note: the 1st table entry is the \Parameters subkey,
|
||
// and the last table entry is NULL
|
||
//
|
||
rtlQueryRegTbl[0].QueryRoutine = NULL;
|
||
rtlQueryRegTbl[0].Flags = RTL_QUERY_REGISTRY_SUBKEY;
|
||
rtlQueryRegTbl[0].Name = L"Parameters";
|
||
rtlQueryRegTbl[0].EntryContext = NULL;
|
||
rtlQueryRegTbl[0].DefaultType = (ULONG_PTR)NULL;
|
||
rtlQueryRegTbl[0].DefaultData = NULL;
|
||
rtlQueryRegTbl[0].DefaultLength = (ULONG_PTR)NULL;
|
||
|
||
rtlQueryRegTbl[1].QueryRoutine = NULL;
|
||
rtlQueryRegTbl[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
rtlQueryRegTbl[1].Name = L"DebugLevel";
|
||
rtlQueryRegTbl[1].EntryContext = &DebugLevel;
|
||
rtlQueryRegTbl[1].DefaultType = REG_DWORD;
|
||
rtlQueryRegTbl[1].DefaultData = &ulDebugLevel;
|
||
rtlQueryRegTbl[1].DefaultLength = sizeOfUl;
|
||
|
||
rtlQueryRegTbl[2].QueryRoutine = NULL;
|
||
rtlQueryRegTbl[2].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
rtlQueryRegTbl[2].Name = L"AlternateSetting";
|
||
rtlQueryRegTbl[2].EntryContext = &g_ulAlternateSetting;
|
||
rtlQueryRegTbl[2].DefaultType = REG_DWORD;
|
||
rtlQueryRegTbl[2].DefaultData = &ulAlternateSetting;
|
||
rtlQueryRegTbl[2].DefaultLength = sizeOfUl;
|
||
|
||
rtlQueryRegTbl[3].QueryRoutine = NULL;
|
||
rtlQueryRegTbl[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
rtlQueryRegTbl[3].Name = L"InterruptTimeout";
|
||
rtlQueryRegTbl[3].EntryContext = &g_lIntTimout;
|
||
rtlQueryRegTbl[3].DefaultType = REG_DWORD;
|
||
rtlQueryRegTbl[3].DefaultData = &lIntTimout;
|
||
rtlQueryRegTbl[3].DefaultLength = sizeOfUl;
|
||
|
||
rtlQueryRegTbl[4].QueryRoutine = NULL;
|
||
rtlQueryRegTbl[4].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
rtlQueryRegTbl[4].Name = L"MaxPipeErrors";
|
||
rtlQueryRegTbl[4].EntryContext = &g_ulMaxPipeErrors;
|
||
rtlQueryRegTbl[4].DefaultType = REG_DWORD;
|
||
rtlQueryRegTbl[4].DefaultData = &ulMaxPipeErrors;
|
||
rtlQueryRegTbl[4].DefaultLength = sizeOfUl;
|
||
|
||
rtlQueryRegTbl[5].QueryRoutine = NULL;
|
||
rtlQueryRegTbl[5].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
rtlQueryRegTbl[5].Name = L"ExposeComPort";
|
||
rtlQueryRegTbl[5].EntryContext = &g_ExposeComPort;
|
||
rtlQueryRegTbl[5].DefaultType = REG_DWORD;
|
||
rtlQueryRegTbl[5].DefaultData = &ulExposeComPort;
|
||
rtlQueryRegTbl[5].DefaultLength = sizeOfUl;
|
||
|
||
//
|
||
// query the Registry
|
||
//
|
||
status = RtlQueryRegistryValues(
|
||
RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, // RelativeTo
|
||
RegistryPath->Buffer, // Path
|
||
rtlQueryRegTbl, // QueryTable
|
||
NULL, // Context
|
||
NULL ); // Environment
|
||
|
||
if ( !NT_SUCCESS( status ) ) {
|
||
//
|
||
// if registry query failed then use defaults
|
||
//
|
||
DbgDump( DBG_INIT, ("RtlQueryRegistryValues error: 0x%x\n", status) );
|
||
|
||
g_ulAlternateSetting = ulAlternateSetting;
|
||
g_lIntTimout = lIntTimout;
|
||
g_ulMaxPipeErrors = ulMaxPipeErrors;
|
||
DebugLevel = DBG_OFF;
|
||
|
||
}
|
||
|
||
DbgDump( DBG_INIT, ("DebugLevel = 0x%x\n", DebugLevel));
|
||
|
||
DbgDump( DBG_INIT, ("AlternateSetting = %d\n", g_ulAlternateSetting));
|
||
DbgDump( DBG_INIT, ("MaxPipeErrors = %d\n", g_ulMaxPipeErrors));
|
||
DbgDump( DBG_INIT, ("INT Timeout = %d\n", g_lIntTimout));
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
VOID
|
||
ReleaseSlot(
|
||
IN LONG Slot
|
||
)
|
||
{
|
||
LONG lNumDevices = InterlockedDecrement(&g_NumDevices);
|
||
UNREFERENCED_PARAMETER( Slot );
|
||
|
||
ASSERT( lNumDevices >= 0);
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
AcquireSlot(
|
||
OUT PULONG PSlot
|
||
)
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
|
||
*PSlot = InterlockedIncrement(&g_NumDevices);
|
||
|
||
if (*PSlot == (ULONG)0) {
|
||
status = STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CreateDevObjAndSymLink(
|
||
IN PDRIVER_OBJECT PDrvObj,
|
||
IN PDEVICE_OBJECT PPDO,
|
||
IN PDEVICE_OBJECT *PpDevObj,
|
||
IN PCHAR PDevName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Creates a named device object and symbolic link
|
||
for the next available device instance. Saves both the \\Device\\PDevName%n
|
||
and \\DosDevices\\PDevName%n in the device extension.
|
||
|
||
Also registers our device interface with PnP system.
|
||
|
||
Arguments:
|
||
|
||
PDrvObj - Pointer to our driver object
|
||
PPDO - Pointer to the PDO for the stack to which we should add ourselves
|
||
PDevName - device name to use
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
PDEVICE_EXTENSION pDevExt = NULL;
|
||
NTSTATUS status;
|
||
ULONG deviceInstance;
|
||
ULONG bufferLen;
|
||
BOOLEAN gotSlot = FALSE;
|
||
|
||
ANSI_STRING asDevName;
|
||
ANSI_STRING asDosDevName;
|
||
|
||
UNICODE_STRING usDeviceName = {0}; // seen only in kernel-mode namespace
|
||
UNICODE_STRING usDosDevName = {0}; // seen in user-mode namespace
|
||
|
||
CHAR dosDeviceNameBuffer[DOS_NAME_MAX];
|
||
CHAR deviceNameBuffer[DOS_NAME_MAX];
|
||
|
||
DbgDump(DBG_INIT, (">CreateDevObjAndSymLink\n"));
|
||
PAGED_CODE();
|
||
ASSERT( PPDO );
|
||
|
||
//
|
||
// init the callers device obj
|
||
//
|
||
*PpDevObj = NULL;
|
||
|
||
//
|
||
// Get the next device instance number
|
||
//
|
||
status = AcquireSlot(&deviceInstance);
|
||
if (status != STATUS_SUCCESS) {
|
||
DbgDump(DBG_ERR, ("AcquireSlot error: 0x%x\n", status));
|
||
goto CreateDeviceObjectError;
|
||
} else {
|
||
gotSlot = TRUE;
|
||
}
|
||
|
||
//
|
||
// concat device name & instance number
|
||
//
|
||
ASSERT( *PDevName != (CHAR)NULL);
|
||
sprintf(dosDeviceNameBuffer, "%s%s%03d", "\\DosDevices\\", PDevName,
|
||
deviceInstance);
|
||
sprintf(deviceNameBuffer, "%s%s%03d", "\\Device\\", PDevName,
|
||
deviceInstance);
|
||
|
||
// convert names to ANSI string
|
||
RtlInitAnsiString(&asDevName, deviceNameBuffer);
|
||
RtlInitAnsiString(&asDosDevName, dosDeviceNameBuffer);
|
||
|
||
usDeviceName.Length = 0;
|
||
usDeviceName.Buffer = NULL;
|
||
|
||
usDosDevName.Length = 0;
|
||
usDosDevName.Buffer = NULL;
|
||
|
||
//
|
||
// convert names to UNICODE
|
||
//
|
||
status = RtlAnsiStringToUnicodeString(&usDeviceName, &asDevName, TRUE);
|
||
if (status != STATUS_SUCCESS) {
|
||
DbgDump(DBG_ERR, ("RtlAnsiStringToUnicodeString error: 0x%x\n", status));
|
||
goto CreateDeviceObjectError;
|
||
}
|
||
|
||
status = RtlAnsiStringToUnicodeString(&usDosDevName, &asDosDevName, TRUE);
|
||
if (status != STATUS_SUCCESS) {
|
||
DbgDump(DBG_ERR, ("RtlAnsiStringToUnicodeString error: 0x%x\n", status));
|
||
goto CreateDeviceObjectError;
|
||
}
|
||
|
||
//
|
||
// create the named devive object
|
||
// Note: we may want to change this to a non-exclusive later
|
||
// so xena to come in without the filter.
|
||
//
|
||
status = IoCreateDevice( PDrvObj,
|
||
sizeof(DEVICE_EXTENSION),
|
||
&usDeviceName,
|
||
FILE_DEVICE_SERIAL_PORT,
|
||
0,
|
||
TRUE, // Note: SerialPorts are exclusive
|
||
PpDevObj);
|
||
|
||
if (status != STATUS_SUCCESS) {
|
||
DbgDump(DBG_ERR, ("IoCreateDevice error: 0x%x\n", status));
|
||
TEST_TRAP();
|
||
goto CreateDeviceObjectError;
|
||
}
|
||
|
||
//
|
||
// get pointer to device extension
|
||
//
|
||
pDevExt = (PDEVICE_EXTENSION) (*PpDevObj)->DeviceExtension;
|
||
|
||
RtlZeroMemory(pDevExt, sizeof(DEVICE_EXTENSION)); // (redundant)
|
||
|
||
//
|
||
// init SERIAL_PORT_INTERFACE
|
||
//
|
||
pDevExt->SerialPort.Type = WCE_SERIAL_PORT_TYPE;
|
||
|
||
//
|
||
// create symbolic link
|
||
//
|
||
status = IoCreateUnprotectedSymbolicLink(&usDosDevName, &usDeviceName);
|
||
if (status != STATUS_SUCCESS) {
|
||
DbgDump(DBG_ERR, ("IoCreateUnprotectedSymbolicLink error: 0x%x\n", status));
|
||
goto CreateDeviceObjectError;
|
||
}
|
||
|
||
DbgDump(DBG_INIT, ("SymbolicLink: %ws\n", usDosDevName.Buffer));
|
||
|
||
//
|
||
// Make the device visible via a device association as well.
|
||
// The reference string is the eight digit device index
|
||
//
|
||
status = IoRegisterDeviceInterface(
|
||
PPDO,
|
||
(LPGUID)&GUID_WCE_SERIAL_USB,
|
||
NULL,
|
||
&pDevExt->DeviceClassSymbolicName );
|
||
|
||
if (status != STATUS_SUCCESS) {
|
||
DbgDump(DBG_ERR, ("IoRegisterDeviceInterface error: 0x%x\n", status));
|
||
pDevExt->DeviceClassSymbolicName.Buffer = NULL;
|
||
goto CreateDeviceObjectError;
|
||
}
|
||
|
||
DbgDump(DBG_INIT, ("DeviceClassSymbolicName: %ws\n", pDevExt->DeviceClassSymbolicName.Buffer));
|
||
|
||
//
|
||
// save the Dos Device link name in our extension
|
||
//
|
||
strcpy(pDevExt->DosDeviceName, dosDeviceNameBuffer);
|
||
|
||
pDevExt->SymbolicLink = TRUE;
|
||
|
||
|
||
//
|
||
// save (kernel) device name in extension
|
||
//
|
||
bufferLen = RtlAnsiStringToUnicodeSize(&asDevName);
|
||
|
||
pDevExt->DeviceName.Length = 0;
|
||
pDevExt->DeviceName.MaximumLength = (USHORT)bufferLen;
|
||
|
||
pDevExt->DeviceName.Buffer = ExAllocatePool(PagedPool, bufferLen);
|
||
if (pDevExt->DeviceName.Buffer == NULL) {
|
||
//
|
||
// Skip out. We have worse problems than missing
|
||
// the name if we have no memory at this point.
|
||
//
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
DbgDump(DBG_ERR, ("CreateDevObjAndSymLink ERROR: 0x%x\n", status));
|
||
goto CreateDeviceObjectError;
|
||
}
|
||
|
||
RtlAnsiStringToUnicodeString(&pDevExt->DeviceName, &asDevName, FALSE);
|
||
// save 1's based device instance number
|
||
pDevExt->SerialPort.Com.Instance = deviceInstance;
|
||
|
||
CreateDeviceObjectError:;
|
||
|
||
//
|
||
// free Unicode strings
|
||
//
|
||
RtlFreeUnicodeString(&usDeviceName);
|
||
RtlFreeUnicodeString(&usDosDevName);
|
||
|
||
//
|
||
// Delete the devobj if there was an error
|
||
//
|
||
if (status != STATUS_SUCCESS) {
|
||
|
||
if ( *PpDevObj ) {
|
||
|
||
DeleteDevObjAndSymLink( *PpDevObj );
|
||
|
||
*PpDevObj = NULL;
|
||
|
||
}
|
||
|
||
if (gotSlot) {
|
||
ReleaseSlot(deviceInstance);
|
||
}
|
||
}
|
||
|
||
DbgDump(DBG_INIT, ("<CreateDevObjAndSymLink 0x%x\n", status));
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
DeleteDevObjAndSymLink(
|
||
IN PDEVICE_OBJECT PDevObj
|
||
)
|
||
{
|
||
PDEVICE_EXTENSION pDevExt;
|
||
UNICODE_STRING usDevLink;
|
||
ANSI_STRING asDevLink;
|
||
NTSTATUS NtStatus = STATUS_SUCCESS;
|
||
|
||
DbgDump(DBG_INIT, (">DeleteDevObjAndSymLink\n"));
|
||
PAGED_CODE();
|
||
ASSERT( PDevObj );
|
||
|
||
pDevExt = (PDEVICE_EXTENSION) PDevObj->DeviceExtension;
|
||
ASSERT( pDevExt );
|
||
|
||
// get rid of the symbolic link
|
||
if ( pDevExt->SymbolicLink ) {
|
||
|
||
RtlInitAnsiString( &asDevLink, pDevExt->DosDeviceName );
|
||
|
||
NtStatus = RtlAnsiStringToUnicodeString( &usDevLink,
|
||
&asDevLink, TRUE);
|
||
|
||
ASSERT(STATUS_SUCCESS == NtStatus);
|
||
NtStatus = IoDeleteSymbolicLink(&usDevLink);
|
||
|
||
}
|
||
|
||
if (pDevExt->DeviceClassSymbolicName.Buffer)
|
||
{
|
||
NtStatus = IoSetDeviceInterfaceState(&pDevExt->DeviceClassSymbolicName, FALSE);
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
DbgDump(DBG_WRN, ("IoSetDeviceInterfaceState.3: OFF\n"));
|
||
}
|
||
|
||
ExFreePool( pDevExt->DeviceClassSymbolicName.Buffer );
|
||
pDevExt->DeviceClassSymbolicName.Buffer = NULL;
|
||
}
|
||
|
||
if (pDevExt->DeviceName.Buffer != NULL) {
|
||
ExFreePool(pDevExt->DeviceName.Buffer);
|
||
RtlInitUnicodeString(&pDevExt->DeviceName, NULL);
|
||
}
|
||
|
||
//
|
||
// Wait to do this untill here as this triggers the unload routine
|
||
// at which point everything better have been deallocated
|
||
//
|
||
IoDeleteDevice( PDevObj );
|
||
|
||
DbgDump(DBG_INIT, ("<DeleteDevObjAndSymLink\n"));
|
||
|
||
return NtStatus;
|
||
}
|
||
|
||
#if 0
|
||
|
||
VOID
|
||
SetBooleanLocked(
|
||
IN OUT PBOOLEAN PDest,
|
||
IN BOOLEAN Src,
|
||
IN PKSPIN_LOCK PSpinLock
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to assign a BOOLEAN value with spinlock protection.
|
||
|
||
Arguments:
|
||
|
||
PDest - A pointer to Lval.
|
||
|
||
Src - Rval.
|
||
|
||
PSpinLock - Pointer to the spin lock we should hold.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
KIRQL tmpIrql;
|
||
|
||
KeAcquireSpinLock(PSpinLock, &tmpIrql);
|
||
*PDest = Src;
|
||
KeReleaseSpinLock(PSpinLock, tmpIrql);
|
||
}
|
||
#endif
|
||
|
||
|
||
VOID
|
||
SetPVoidLocked(
|
||
IN OUT PVOID *PDest,
|
||
IN OUT PVOID Src,
|
||
IN PKSPIN_LOCK PSpinLock
|
||
)
|
||
{
|
||
KIRQL tmpIrql;
|
||
|
||
KeAcquireSpinLock(PSpinLock, &tmpIrql);
|
||
*PDest = Src;
|
||
KeReleaseSpinLock(PSpinLock, tmpIrql);
|
||
}
|
||
|
||
|
||
//
|
||
// Note: had to use ExWorkItems to be binary compatible with Win98.
|
||
// The WorkerRoutine must take as it's only parameter a PWCE_WORK_ITEM
|
||
// and extract any parameters. When the WorkerRoutine is complete is MUST
|
||
// call DequeueWorkItem to free it back to the worker pool & signal any waiters.
|
||
//
|
||
NTSTATUS
|
||
QueueWorkItem(
|
||
IN PDEVICE_OBJECT PDevObj,
|
||
IN PWCE_WORKER_THREAD_ROUTINE WorkerRoutine,
|
||
IN PVOID Context,
|
||
IN ULONG Flags
|
||
)
|
||
{
|
||
PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
||
NTSTATUS status = STATUS_INVALID_PARAMETER;
|
||
PWCE_WORK_ITEM pWorkItem;
|
||
KIRQL irql;
|
||
|
||
DbgDump(DBG_WORK_ITEMS, (">QueueWorkItem\n" ));
|
||
|
||
//
|
||
// N.B: you need to ensure your driver does not queue anything when it is stopped.
|
||
//
|
||
KeAcquireSpinLock(&pDevExt->ControlLock, &irql);
|
||
|
||
if ( !CanAcceptIoRequests(PDevObj, FALSE, TRUE) ) {
|
||
|
||
status = STATUS_DELETE_PENDING;
|
||
DbgDump(DBG_ERR, ("QueueWorkItem: 0x%x\n", status));
|
||
|
||
} else if ( PDevObj && WorkerRoutine ) {
|
||
|
||
pWorkItem = ExAllocateFromNPagedLookasideList( &pDevExt->WorkItemPool );
|
||
|
||
if ( pWorkItem ) {
|
||
|
||
status = AcquireRemoveLock(&pDevExt->RemoveLock, pWorkItem);
|
||
if ( !NT_SUCCESS(status) ) {
|
||
DbgDump(DBG_ERR, ("QueueWorkItem: 0x%x\n", status));
|
||
TEST_TRAP();
|
||
ExFreeToNPagedLookasideList( &pDevExt->WorkItemPool, pWorkItem );
|
||
KeReleaseSpinLock(&pDevExt->ControlLock, irql);
|
||
return status;
|
||
}
|
||
|
||
RtlZeroMemory( pWorkItem, sizeof(*pWorkItem) );
|
||
|
||
// bump the pending count
|
||
InterlockedIncrement(&pDevExt->PendingWorkItemsCount);
|
||
|
||
DbgDump(DBG_WORK_ITEMS, ("PendingWorkItemsCount: %d\n", pDevExt->PendingWorkItemsCount));
|
||
|
||
//
|
||
// put the worker on our pending list
|
||
//
|
||
InsertTailList(&pDevExt->PendingWorkItems,
|
||
&pWorkItem->ListEntry );
|
||
|
||
//
|
||
// store parameters
|
||
//
|
||
pWorkItem->DeviceObject = PDevObj;
|
||
pWorkItem->Context = Context;
|
||
pWorkItem->Flags = Flags;
|
||
|
||
ExInitializeWorkItem( &pWorkItem->Item,
|
||
(PWORKER_THREAD_ROUTINE)WorkerRoutine,
|
||
(PVOID)pWorkItem // Context passed to WorkerRoutine
|
||
);
|
||
|
||
// finally, queue the worker
|
||
ExQueueWorkItem( &pWorkItem->Item,
|
||
CriticalWorkQueue );
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
DbgDump(DBG_ERR, ("AllocateWorkItem failed!\n"));
|
||
TEST_TRAP()
|
||
}
|
||
}
|
||
|
||
KeReleaseSpinLock(&pDevExt->ControlLock, irql);
|
||
|
||
DbgDump(DBG_WORK_ITEMS, ("<QueueWorkItem 0x%x\n", status ));
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
VOID
|
||
DequeueWorkItem(
|
||
IN PDEVICE_OBJECT PDevObj,
|
||
IN PWCE_WORK_ITEM PWorkItem
|
||
)
|
||
{
|
||
PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
||
KIRQL irql;
|
||
|
||
DbgDump(DBG_WORK_ITEMS, (">DequeueWorkItem\n" ));
|
||
|
||
//
|
||
// remove the worker from the pending list
|
||
//
|
||
KeAcquireSpinLock( &pDevExt->ControlLock, &irql );
|
||
|
||
RemoveEntryList( &PWorkItem->ListEntry );
|
||
|
||
KeReleaseSpinLock( &pDevExt->ControlLock, irql);
|
||
|
||
//
|
||
// free the worker back to pool
|
||
//
|
||
ExFreeToNPagedLookasideList( &pDevExt->WorkItemPool, PWorkItem );
|
||
|
||
//
|
||
// signal event if this is the last one
|
||
//
|
||
if (0 == InterlockedDecrement( &pDevExt->PendingWorkItemsCount) ) {
|
||
DbgDump(DBG_WORK_ITEMS, ("PendingWorkItemsEvent signalled\n" ));
|
||
KeSetEvent( &pDevExt->PendingWorkItemsEvent, IO_NO_INCREMENT, FALSE);
|
||
}
|
||
DbgDump(DBG_WORK_ITEMS, ("PendingWorkItemsCount: %d\n", pDevExt->PendingWorkItemsCount));
|
||
ASSERT(pDevExt->PendingWorkItemsCount >= 0);
|
||
|
||
ReleaseRemoveLock(&pDevExt->RemoveLock, PWorkItem);
|
||
|
||
DbgDump(DBG_WORK_ITEMS, ("<DequeueWorkItem\n" ));
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
#pragma warning( push )
|
||
#pragma warning( disable : 4706 ) // assignment w/i conditional expression
|
||
NTSTATUS
|
||
WaitForPendingItem(
|
||
IN PDEVICE_OBJECT PDevObj,
|
||
IN PKEVENT PPendingEvent,
|
||
IN PULONG PPendingCount
|
||
)
|
||
{
|
||
PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
||
LARGE_INTEGER timeOut = {0,0};
|
||
LONG itemsLeft;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
|
||
DbgDump(DBG_PNP, (">WaitForPendingItem\n"));
|
||
|
||
if ( !PDevObj || !PPendingEvent || !PPendingCount ) {
|
||
|
||
status = STATUS_INVALID_PARAMETER;
|
||
DbgDump(DBG_ERR, ("WaitForPendingItem: STATUS_INVALID_PARAMETER\n"));
|
||
TEST_TRAP();
|
||
|
||
} else {
|
||
|
||
//
|
||
// wait for pending item to signal it's complete
|
||
//
|
||
while ( itemsLeft = InterlockedExchange( PPendingCount, *PPendingCount) ) {
|
||
|
||
DbgDump(DBG_PNP|DBG_EVENTS, ("Pending Items Remain: %d\n", itemsLeft ) );
|
||
|
||
timeOut.QuadPart = MILLISEC_TO_100NANOSEC( DEFAULT_PENDING_TIMEOUT );
|
||
|
||
DbgDump(DBG_PNP|DBG_EVENTS, ("Waiting for %d msec...\n", timeOut.QuadPart/10000));
|
||
|
||
PAGED_CODE();
|
||
|
||
KeWaitForSingleObject( PPendingEvent,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
&timeOut );
|
||
|
||
}
|
||
|
||
DbgDump(DBG_PNP, ("Pending Items: %d\n", itemsLeft ) );
|
||
}
|
||
|
||
DbgDump(DBG_PNP, ("<WaitForPendingItem (0x%x)\n", status));
|
||
|
||
return status;
|
||
}
|
||
#pragma warning( pop )
|
||
|
||
|
||
BOOLEAN
|
||
CanAcceptIoRequests(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN BOOLEAN AcquireLock,
|
||
IN BOOLEAN CheckOpened
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Check device extension status flags.
|
||
Can NOT accept a new I/O request if device:
|
||
1) is removed,
|
||
2) has never been started,
|
||
3) is stopped,
|
||
4) has a remove request pending, or
|
||
5) has a stop device pending
|
||
|
||
** Called with the SpinLock held, else AcquireLock should be TRUE **
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - pointer to the device object
|
||
AcquireLock - if TRUE then we need to acquire the lock
|
||
CheckOpened - normally set to TRUE during I/O.
|
||
Special cases where FALSE include:
|
||
IRP_MN_QUERY_PNP_DEVICE_STATE
|
||
IRP_MJ_CREATE
|
||
|
||
Return Value:
|
||
|
||
TRUE/FALSE
|
||
|
||
--*/
|
||
{
|
||
PDEVICE_EXTENSION pDevExt = DeviceObject->DeviceExtension;
|
||
BOOLEAN bRc = FALSE;
|
||
KIRQL irql;
|
||
|
||
if (AcquireLock) {
|
||
KeAcquireSpinLock(&pDevExt->ControlLock, &irql);
|
||
}
|
||
|
||
if ( !InterlockedCompareExchange(&pDevExt->DeviceRemoved, FALSE, FALSE) &&
|
||
InterlockedCompareExchange(&pDevExt->AcceptingRequests, TRUE, TRUE) &&
|
||
InterlockedCompareExchange((PULONG)&pDevExt->PnPState, PnPStateStarted, PnPStateStarted) &&
|
||
(CheckOpened ? InterlockedCompareExchange(&pDevExt->DeviceOpened, TRUE, TRUE) : TRUE)
|
||
)
|
||
{
|
||
bRc = TRUE;
|
||
}
|
||
#if defined(DBG)
|
||
else DbgDump(DBG_WRN|DBG_PNP, ("CanAcceptIoRequests = FALSE\n"));
|
||
#endif
|
||
|
||
if (AcquireLock) {
|
||
KeReleaseSpinLock(&pDevExt->ControlLock, irql);
|
||
}
|
||
|
||
return bRc;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
IsWin9x(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determine whether or not we are running on Win9x (vs. NT).
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
TRUE iff we're running on Win9x.
|
||
|
||
--*/
|
||
{
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
UNICODE_STRING keyName;
|
||
HANDLE hKey;
|
||
NTSTATUS status;
|
||
BOOLEAN result;
|
||
|
||
PAGED_CODE();
|
||
|
||
/*
|
||
* Try to open the COM Name Arbiter, which exists only on NT.
|
||
*/
|
||
RtlInitUnicodeString(&keyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\COM Name Arbiter");
|
||
InitializeObjectAttributes( &objectAttributes,
|
||
&keyName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
(PSECURITY_DESCRIPTOR)NULL);
|
||
|
||
status = ZwOpenKey(&hKey, KEY_QUERY_VALUE, &objectAttributes);
|
||
if (NT_SUCCESS(status)){
|
||
status = ZwClose(hKey);
|
||
ASSERT(NT_SUCCESS(status));
|
||
result = FALSE;
|
||
}
|
||
else {
|
||
result = TRUE;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
LogError(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PDEVICE_OBJECT DeviceObject OPTIONAL,
|
||
IN ULONG SequenceNumber,
|
||
IN UCHAR MajorFunctionCode,
|
||
IN UCHAR RetryCount,
|
||
IN ULONG UniqueErrorValue,
|
||
IN NTSTATUS FinalStatus,
|
||
IN NTSTATUS SpecificIOStatus,
|
||
IN ULONG LengthOfInsert1,
|
||
IN PWCHAR Insert1,
|
||
IN ULONG LengthOfInsert2,
|
||
IN PWCHAR Insert2
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Stolen from serial.sys
|
||
|
||
This routine allocates an error log entry, copies the supplied data
|
||
to it, and requests that it be written to the error log file.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - A pointer to the driver object for the device.
|
||
|
||
DeviceObject - A pointer to the device object associated with the
|
||
device that had the error, early in initialization, one may not
|
||
yet exist.
|
||
|
||
SequenceNumber - A ulong value that is unique to an IRP over the
|
||
life of the irp in this driver - 0 generally means an error not
|
||
associated with an irp.
|
||
|
||
MajorFunctionCode - If there is an error associated with the irp,
|
||
this is the major function code of that irp.
|
||
|
||
RetryCount - The number of times a particular operation has been
|
||
retried.
|
||
|
||
UniqueErrorValue - A unique long word that identifies the particular
|
||
call to this function.
|
||
|
||
FinalStatus - The final status given to the irp that was associated
|
||
with this error. If this log entry is being made during one of
|
||
the retries this value will be STATUS_SUCCESS.
|
||
|
||
SpecificIOStatus - The IO status for a particular error.
|
||
|
||
LengthOfInsert1 - The length in bytes (including the terminating NULL)
|
||
of the first insertion string.
|
||
|
||
Insert1 - The first insertion string.
|
||
|
||
LengthOfInsert2 - The length in bytes (including the terminating NULL)
|
||
of the second insertion string. NOTE, there must
|
||
be a first insertion string for their to be
|
||
a second insertion string.
|
||
|
||
Insert2 - The second insertion string.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_ERROR_LOG_PACKET errorLogEntry;
|
||
|
||
PVOID objectToUse = NULL;
|
||
SHORT dumpToAllocate = 0;
|
||
PUCHAR ptrToFirstInsert = NULL;
|
||
PUCHAR ptrToSecondInsert = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
DbgDump(DBG_ERR, (">LogError\n"));
|
||
|
||
if (Insert1 == NULL) {
|
||
LengthOfInsert1 = 0;
|
||
}
|
||
|
||
if (Insert2 == NULL) {
|
||
LengthOfInsert2 = 0;
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT(DeviceObject)) {
|
||
|
||
objectToUse = DeviceObject;
|
||
|
||
} else if (ARGUMENT_PRESENT(DriverObject)) {
|
||
|
||
objectToUse = DriverObject;
|
||
|
||
}
|
||
|
||
errorLogEntry = IoAllocateErrorLogEntry(
|
||
objectToUse,
|
||
(UCHAR)(sizeof(IO_ERROR_LOG_PACKET) +
|
||
dumpToAllocate
|
||
+ LengthOfInsert1 +
|
||
LengthOfInsert2)
|
||
);
|
||
|
||
if ( errorLogEntry != NULL ) {
|
||
|
||
errorLogEntry->ErrorCode = SpecificIOStatus;
|
||
errorLogEntry->SequenceNumber = SequenceNumber;
|
||
errorLogEntry->MajorFunctionCode = MajorFunctionCode;
|
||
errorLogEntry->RetryCount = RetryCount;
|
||
errorLogEntry->UniqueErrorValue = UniqueErrorValue;
|
||
errorLogEntry->FinalStatus = FinalStatus;
|
||
errorLogEntry->DumpDataSize = dumpToAllocate;
|
||
ptrToFirstInsert = (PUCHAR)&errorLogEntry->DumpData[0];
|
||
|
||
ptrToSecondInsert = ptrToFirstInsert + LengthOfInsert1;
|
||
|
||
if (LengthOfInsert1) {
|
||
|
||
errorLogEntry->NumberOfStrings = 1;
|
||
errorLogEntry->StringOffset = (USHORT)(ptrToFirstInsert -
|
||
(PUCHAR)errorLogEntry);
|
||
RtlCopyMemory(
|
||
ptrToFirstInsert,
|
||
Insert1,
|
||
LengthOfInsert1
|
||
);
|
||
|
||
if (LengthOfInsert2) {
|
||
|
||
errorLogEntry->NumberOfStrings = 2;
|
||
RtlCopyMemory(
|
||
ptrToSecondInsert,
|
||
Insert2,
|
||
LengthOfInsert2
|
||
);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
IoWriteErrorLogEntry(errorLogEntry);
|
||
|
||
}
|
||
|
||
DbgDump(DBG_ERR, ("<LogError\n"));
|
||
return;
|
||
}
|
||
|
||
|
||
#if defined(DBG)
|
||
PCHAR
|
||
PnPMinorFunctionString (
|
||
UCHAR MinorFunction
|
||
)
|
||
{
|
||
switch (MinorFunction) {
|
||
case IRP_MN_START_DEVICE:
|
||
return "IRP_MN_START_DEVICE";
|
||
case IRP_MN_QUERY_REMOVE_DEVICE:
|
||
return "IRP_MN_QUERY_REMOVE_DEVICE";
|
||
case IRP_MN_REMOVE_DEVICE:
|
||
return "IRP_MN_REMOVE_DEVICE";
|
||
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
||
return "IRP_MN_CANCEL_REMOVE_DEVICE";
|
||
case IRP_MN_STOP_DEVICE:
|
||
return "IRP_MN_STOP_DEVICE";
|
||
case IRP_MN_QUERY_STOP_DEVICE:
|
||
return "IRP_MN_QUERY_STOP_DEVICE";
|
||
case IRP_MN_CANCEL_STOP_DEVICE:
|
||
return "IRP_MN_CANCEL_STOP_DEVICE";
|
||
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
||
return "IRP_MN_QUERY_DEVICE_RELATIONS";
|
||
case IRP_MN_QUERY_INTERFACE:
|
||
return "IRP_MN_QUERY_INTERFACE";
|
||
case IRP_MN_QUERY_CAPABILITIES:
|
||
return "IRP_MN_QUERY_CAPABILITIES";
|
||
case IRP_MN_QUERY_RESOURCES:
|
||
return "IRP_MN_QUERY_RESOURCES";
|
||
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
|
||
return "IRP_MN_QUERY_RESOURCE_REQUIREMENTS";
|
||
case IRP_MN_QUERY_DEVICE_TEXT:
|
||
return "IRP_MN_QUERY_DEVICE_TEXT";
|
||
case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
|
||
return "IRP_MN_FILTER_RESOURCE_REQUIREMENTS";
|
||
case IRP_MN_READ_CONFIG:
|
||
return "IRP_MN_READ_CONFIG";
|
||
case IRP_MN_WRITE_CONFIG:
|
||
return "IRP_MN_WRITE_CONFIG";
|
||
case IRP_MN_EJECT:
|
||
return "IRP_MN_EJECT";
|
||
case IRP_MN_SET_LOCK:
|
||
return "IRP_MN_SET_LOCK";
|
||
case IRP_MN_QUERY_ID:
|
||
return "IRP_MN_QUERY_ID";
|
||
case IRP_MN_QUERY_PNP_DEVICE_STATE:
|
||
return "IRP_MN_QUERY_PNP_DEVICE_STATE";
|
||
case IRP_MN_QUERY_BUS_INFORMATION:
|
||
return "IRP_MN_QUERY_BUS_INFORMATION";
|
||
case IRP_MN_DEVICE_USAGE_NOTIFICATION:
|
||
return "IRP_MN_DEVICE_USAGE_NOTIFICATION";
|
||
case IRP_MN_SURPRISE_REMOVAL:
|
||
return "IRP_MN_SURPRISE_REMOVAL";
|
||
default:
|
||
return ((PCHAR)("unknown IRP_MN_ 0x%x\n", MinorFunction));
|
||
}
|
||
}
|
||
#endif
|
||
|
||
// EOF
|