You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3631 lines
107 KiB
3631 lines
107 KiB
/*++
|
|
|
|
Copyright (c) 1991, 1992, 1993 - 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
pnp.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the code that handles the plug and play
|
|
IRPs for the serial driver.
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History :
|
|
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
#define ALLF 0xffffffff
|
|
|
|
static const PHYSICAL_ADDRESS SerialPhysicalZero = {0};
|
|
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGESRP0, SerialCreateDevObj)
|
|
#pragma alloc_text(PAGESRP0, SerialAddDevice)
|
|
#pragma alloc_text(PAGESRP0, SerialPnpDispatch)
|
|
#pragma alloc_text(PAGESRP0, SerialStartDevice)
|
|
#pragma alloc_text(PAGESRP0, SerialFinishStartDevice)
|
|
#pragma alloc_text(PAGESRP0, SerialGetPortInfo)
|
|
#pragma alloc_text(PAGESRP0, SerialDoExternalNaming)
|
|
#pragma alloc_text(PAGESRP0, SerialReportMaxBaudRate)
|
|
#pragma alloc_text(PAGESRP0, SerialControllerCallBack)
|
|
#pragma alloc_text(PAGESRP0, SerialItemCallBack)
|
|
#pragma alloc_text(PAGESRP0, SerialUndoExternalNaming)
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
//
|
|
// Instantiate the GUID
|
|
//
|
|
|
|
#if !defined(FAR)
|
|
#define FAR
|
|
#endif // !defined(FAR)
|
|
|
|
#include <initguid.h>
|
|
|
|
DEFINE_GUID(GUID_CLASS_COMPORT, 0x86e0d1e0L, 0x8089, 0x11d0, 0x9c, 0xe4, 0x08,
|
|
0x00, 0x3e, 0x30, 0x1f, 0x73);
|
|
|
|
|
|
#if DBG
|
|
|
|
UCHAR *SerSystemCapString[] = {
|
|
"PowerSystemUnspecified",
|
|
"PowerSystemWorking",
|
|
"PowerSystemSleeping1",
|
|
"PowerSystemSleeping2",
|
|
"PowerSystemSleeping3",
|
|
"PowerSystemHibernate",
|
|
"PowerSystemShutdown",
|
|
"PowerSystemMaximum"
|
|
};
|
|
|
|
UCHAR *SerDeviceCapString[] = {
|
|
"PowerDeviceUnspecified",
|
|
"PowerDeviceD0",
|
|
"PowerDeviceD1",
|
|
"PowerDeviceD2",
|
|
"PowerDeviceD3",
|
|
"PowerDeviceMaximum"
|
|
};
|
|
|
|
#endif // DBG
|
|
|
|
|
|
NTSTATUS
|
|
SerialSyncCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp,
|
|
IN PKEVENT SerialSyncEvent)
|
|
{
|
|
KeSetEvent(SerialSyncEvent, IO_NO_INCREMENT, FALSE);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SerialCreateDevObj(IN PDRIVER_OBJECT DriverObject,
|
|
OUT PDEVICE_OBJECT *NewDeviceObject)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will create and initialize a functional device object to
|
|
be attached to a Serial controller PDO.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - a pointer to the driver object this is created under
|
|
NewDeviceObject - a location to store the pointer to the new device object
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if everything was successful
|
|
reason for failure otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING deviceObjName;
|
|
PDEVICE_OBJECT deviceObject = NULL;
|
|
PSERIAL_DEVICE_EXTENSION pDevExt;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
static ULONG currentInstance = 0;
|
|
UNICODE_STRING instanceStr;
|
|
WCHAR instanceNumberBuffer[20];
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
SerialDump (SERTRACECALLS,("SERIAL: Enter SerialCreateDevObj\n"));
|
|
|
|
//
|
|
// Zero out allocated memory pointers so we know if they must be freed
|
|
//
|
|
|
|
RtlZeroMemory(&deviceObjName, sizeof(UNICODE_STRING));
|
|
|
|
deviceObjName.MaximumLength = DEVICE_OBJECT_NAME_LENGTH * sizeof(WCHAR);
|
|
deviceObjName.Buffer = ExAllocatePool(PagedPool, deviceObjName.MaximumLength
|
|
+ sizeof(WCHAR));
|
|
|
|
|
|
if (deviceObjName.Buffer == NULL) {
|
|
SerialLogError(DriverObject, NULL, SerialPhysicalZero, SerialPhysicalZero,
|
|
0, 0, 0, 19, STATUS_SUCCESS, SERIAL_INSUFFICIENT_RESOURCES,
|
|
0, NULL, 0, NULL);
|
|
SerialDump(SERERRORS, ("SERIAL: Couldn't allocate memory for device name"
|
|
"\n"));
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
RtlZeroMemory(deviceObjName.Buffer, deviceObjName.MaximumLength
|
|
+ sizeof(WCHAR));
|
|
|
|
#if defined(NEC_98)
|
|
RtlAppendUnicodeToString(&deviceObjName, L"\\Device\\Ser101");
|
|
#else
|
|
RtlAppendUnicodeToString(&deviceObjName, L"\\Device\\Serial");
|
|
#endif //defined(NEC_98)
|
|
|
|
RtlInitUnicodeString(&instanceStr, NULL);
|
|
|
|
instanceStr.MaximumLength = sizeof(instanceNumberBuffer);
|
|
instanceStr.Buffer = instanceNumberBuffer;
|
|
|
|
RtlIntegerToUnicodeString(currentInstance++, 10, &instanceStr);
|
|
|
|
RtlAppendUnicodeStringToString(&deviceObjName, &instanceStr);
|
|
|
|
|
|
//
|
|
// Create the device object
|
|
//
|
|
|
|
status = IoCreateDevice(DriverObject, sizeof(SERIAL_DEVICE_EXTENSION),
|
|
&deviceObjName, FILE_DEVICE_SERIAL_PORT,
|
|
FILE_DEVICE_SECURE_OPEN, TRUE, &deviceObject);
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
SerialDump(SERERRORS, ("SerialAddDevice: Create device failed - %x \n",
|
|
status));
|
|
goto SerialCreateDevObjError;
|
|
}
|
|
|
|
ASSERT(deviceObject != NULL);
|
|
|
|
|
|
//
|
|
// The device object has a pointer to an area of non-paged
|
|
// pool allocated for this device. This will be the device
|
|
// extension. Zero it out.
|
|
//
|
|
|
|
pDevExt = deviceObject->DeviceExtension;
|
|
RtlZeroMemory(pDevExt, sizeof(SERIAL_DEVICE_EXTENSION));
|
|
|
|
//
|
|
// Initialize the count of IRP's pending
|
|
//
|
|
|
|
pDevExt->PendingIRPCnt = 1;
|
|
|
|
|
|
//
|
|
// Initialize the count of DPC's pending
|
|
//
|
|
|
|
pDevExt->DpcCount = 1;
|
|
|
|
//
|
|
// Allocate Pool and save the nt device name in the device extension.
|
|
//
|
|
|
|
pDevExt->DeviceName.Buffer =
|
|
ExAllocatePool(PagedPool, deviceObjName.Length + sizeof(WCHAR));
|
|
|
|
if (!pDevExt->DeviceName.Buffer) {
|
|
|
|
SerialLogError(
|
|
DriverObject,
|
|
NULL,
|
|
SerialPhysicalZero,
|
|
SerialPhysicalZero,
|
|
0,
|
|
0,
|
|
0,
|
|
19,
|
|
STATUS_SUCCESS,
|
|
SERIAL_INSUFFICIENT_RESOURCES,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
);
|
|
SerialDump(
|
|
SERERRORS,
|
|
("SERIAL: Couldn't allocate memory for DeviceName\n"));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SerialCreateDevObjError;
|
|
}
|
|
|
|
pDevExt->DeviceName.MaximumLength = deviceObjName.Length
|
|
+ sizeof(WCHAR);
|
|
|
|
//
|
|
// Zero fill it.
|
|
//
|
|
|
|
RtlZeroMemory(pDevExt->DeviceName.Buffer,
|
|
pDevExt->DeviceName.MaximumLength);
|
|
|
|
RtlAppendUnicodeStringToString(&pDevExt->DeviceName, &deviceObjName);
|
|
|
|
pDevExt->NtNameForPort.Buffer = ExAllocatePool(PagedPool,
|
|
deviceObjName.MaximumLength);
|
|
|
|
if (pDevExt->NtNameForPort.Buffer == NULL) {
|
|
SerialDump(SERERRORS, ("SerialAddDevice: Cannot allocate memory for "
|
|
"NtName\n"));
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SerialCreateDevObjError;
|
|
}
|
|
|
|
pDevExt->NtNameForPort.MaximumLength = deviceObjName.MaximumLength;
|
|
RtlAppendUnicodeStringToString(&pDevExt->NtNameForPort,
|
|
&deviceObjName);
|
|
|
|
|
|
|
|
//
|
|
// Set up the device extension.
|
|
//
|
|
|
|
pDevExt->DeviceIsOpened = FALSE;
|
|
pDevExt->DeviceObject = deviceObject;
|
|
pDevExt->DriverObject = DriverObject;
|
|
pDevExt->DeviceObject = deviceObject;
|
|
pDevExt->PowerState = PowerDeviceD0;
|
|
|
|
pDevExt->TxFifoAmount = driverDefaults.TxFIFODefault;
|
|
pDevExt->CreatedSymbolicLink = TRUE;
|
|
pDevExt->OwnsPowerPolicy = TRUE;
|
|
|
|
InitializeListHead(&pDevExt->CommonInterruptObject);
|
|
InitializeListHead(&pDevExt->TopLevelSharers);
|
|
InitializeListHead(&pDevExt->MultiportSiblings);
|
|
InitializeListHead(&pDevExt->AllDevObjs);
|
|
InitializeListHead(&pDevExt->ReadQueue);
|
|
InitializeListHead(&pDevExt->WriteQueue);
|
|
InitializeListHead(&pDevExt->MaskQueue);
|
|
InitializeListHead(&pDevExt->PurgeQueue);
|
|
InitializeListHead(&pDevExt->StalledIrpQueue);
|
|
|
|
ExInitializeFastMutex(&pDevExt->OpenMutex);
|
|
ExInitializeFastMutex(&pDevExt->CloseMutex);
|
|
|
|
KeInitializeEvent(&pDevExt->PendingIRPEvent, SynchronizationEvent, FALSE);
|
|
KeInitializeEvent(&pDevExt->PendingDpcEvent, SynchronizationEvent, FALSE);
|
|
KeInitializeEvent(&pDevExt->PowerD0Event, SynchronizationEvent, FALSE);
|
|
|
|
|
|
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
*NewDeviceObject = deviceObject;
|
|
|
|
ExFreePool(deviceObjName.Buffer);
|
|
|
|
SerialDump (SERTRACECALLS,("SERIAL: Leave SerialCreateDevObj\n") );
|
|
return STATUS_SUCCESS;
|
|
|
|
|
|
SerialCreateDevObjError:
|
|
|
|
SerialDump (SERERRORS,("SERIAL: SerialCreateDevObj Error, Cleaning up\n") );
|
|
|
|
//
|
|
// Free the allocated strings for the NT and symbolic names if they exist.
|
|
//
|
|
|
|
if (deviceObjName.Buffer != NULL) {
|
|
ExFreePool(deviceObjName.Buffer);
|
|
}
|
|
|
|
if (deviceObject) {
|
|
|
|
if (pDevExt->NtNameForPort.Buffer != NULL) {
|
|
ExFreePool(pDevExt->NtNameForPort.Buffer);
|
|
}
|
|
|
|
if (pDevExt->DeviceName.Buffer != NULL) {
|
|
ExFreePool(pDevExt->DeviceName.Buffer);
|
|
}
|
|
|
|
IoDeleteDevice(deviceObject);
|
|
}
|
|
|
|
*NewDeviceObject = NULL;
|
|
|
|
SerialDump (SERTRACECALLS,("SERIAL: Leave SerialCreateDevObj\n") );
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SerialAddDevice(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PPdo)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a functional device object for com ports in the
|
|
system and attaches them to the physical device objects for the ports
|
|
|
|
|
|
Arguments:
|
|
|
|
DriverObject - a pointer to the object for this driver
|
|
|
|
PPdo - a pointer to the PDO in the stack we need to attach to
|
|
|
|
Return Value:
|
|
|
|
status from device creation and initialization
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT pNewDevObj = NULL;
|
|
PDEVICE_OBJECT pLowerDevObj = NULL;
|
|
NTSTATUS status;
|
|
PSERIAL_DEVICE_EXTENSION pDevExt;
|
|
|
|
PAGED_CODE();
|
|
|
|
SerialDump(SERTRACECALLS, ("SERIAL: Enter SerialAddDevice with PPdo "
|
|
"0x%x\n", PPdo));
|
|
|
|
if (PPdo == NULL) {
|
|
//
|
|
// Return no more devices
|
|
//
|
|
|
|
SerialDump(SERERRORS, ("SerialAddDevice: Enumeration request, returning"
|
|
" NO_MORE_ENTRIES\n"));
|
|
|
|
return (STATUS_NO_MORE_ENTRIES);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// create and initialize the new device object
|
|
//
|
|
|
|
status = SerialCreateDevObj(DriverObject, &pNewDevObj);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
SerialDump(SERERRORS,
|
|
("SerialAddDevice - error creating new devobj [%#08lx]\n",
|
|
status));
|
|
return status;
|
|
}
|
|
|
|
|
|
//
|
|
// Layer our DO on top of the lower device object
|
|
// The return value is a pointer to the device object to which the
|
|
// DO is actually attached.
|
|
//
|
|
|
|
pLowerDevObj = IoAttachDeviceToDeviceStack(pNewDevObj, PPdo);
|
|
|
|
|
|
//
|
|
// No status. Do the best we can.
|
|
//
|
|
ASSERT(pLowerDevObj != NULL);
|
|
|
|
|
|
pDevExt = pNewDevObj->DeviceExtension;
|
|
pDevExt->LowerDeviceObject = pLowerDevObj;
|
|
pDevExt->Pdo = PPdo;
|
|
|
|
|
|
|
|
//
|
|
// Specify that this driver only supports buffered IO. This basically
|
|
// means that the IO system copies the users data to and from
|
|
// system supplied buffers.
|
|
//
|
|
// Also specify that we are power pagable.
|
|
//
|
|
|
|
pNewDevObj->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
|
|
|
|
SerialDump(SERTRACECALLS, ("SERIAL: Leave SerialAddDevice\n"));
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SerialPnpDispatch(IN PDEVICE_OBJECT PDevObj, IN PIRP PIrp)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is a dispatch routine for the IRPs that come to the driver with the
|
|
IRP_MJ_PNP major code (plug-and-play IRPs).
|
|
|
|
Arguments:
|
|
|
|
PDevObj - Pointer to the device object for this device
|
|
|
|
PIrp - Pointer to the IRP for the current request
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status of the call
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PSERIAL_DEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
|
PDEVICE_OBJECT pLowerDevObj = pDevExt->LowerDeviceObject;
|
|
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(PIrp);
|
|
NTSTATUS status;
|
|
PDEVICE_CAPABILITIES pDevCaps;
|
|
|
|
PAGED_CODE();
|
|
|
|
if ((status = SerialIRPPrologue(PIrp, pDevExt)) != STATUS_SUCCESS) {
|
|
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
switch (pIrpStack->MinorFunction) {
|
|
case IRP_MN_QUERY_CAPABILITIES: {
|
|
PKEVENT pQueryCapsEvent;
|
|
SYSTEM_POWER_STATE cap;
|
|
|
|
SerialDump(SERPNPPOWER, ("SERIAL: Got IRP_MN_QUERY_DEVICE_CAPABILITIES "
|
|
"IRP\n"));
|
|
|
|
pQueryCapsEvent = ExAllocatePool(NonPagedPool, sizeof(KEVENT));
|
|
|
|
if (pQueryCapsEvent == NULL) {
|
|
PIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
KeInitializeEvent(pQueryCapsEvent, SynchronizationEvent, FALSE);
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(PIrp);
|
|
IoSetCompletionRoutine(PIrp, SerialSyncCompletion, pQueryCapsEvent,
|
|
TRUE, TRUE, TRUE);
|
|
|
|
status = IoCallDriver(pLowerDevObj, PIrp);
|
|
|
|
|
|
//
|
|
// Wait for lower drivers to be done with the Irp
|
|
//
|
|
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(pQueryCapsEvent, Executive, KernelMode, FALSE,
|
|
NULL);
|
|
}
|
|
|
|
ExFreePool(pQueryCapsEvent);
|
|
|
|
status = PIrp->IoStatus.Status;
|
|
|
|
if (pIrpStack->Parameters.DeviceCapabilities.Capabilities == NULL) {
|
|
goto errQueryCaps;
|
|
}
|
|
|
|
//
|
|
// Save off their power capabilities
|
|
//
|
|
|
|
SerialDump(SERPNPPOWER, ("SERIAL: Mapping power capabilities\n"));
|
|
|
|
pIrpStack = IoGetCurrentIrpStackLocation(PIrp);
|
|
|
|
pDevCaps = pIrpStack->Parameters.DeviceCapabilities.Capabilities;
|
|
|
|
for (cap = PowerSystemSleeping1; cap < PowerSystemMaximum;
|
|
cap++) {
|
|
SerialDump(SERPNPPOWER, (" SERIAL: %d: %s <--> %s\n",
|
|
cap, SerSystemCapString[cap],
|
|
SerDeviceCapString[pDevCaps->DeviceState[cap]]
|
|
));
|
|
|
|
pDevExt->DeviceStateMap[cap] = pDevCaps->DeviceState[cap];
|
|
}
|
|
|
|
pDevExt->DeviceStateMap[PowerSystemUnspecified]
|
|
= PowerDeviceUnspecified;
|
|
|
|
pDevExt->DeviceStateMap[PowerSystemWorking]
|
|
= PowerDeviceD0;
|
|
|
|
pDevExt->SystemWake = pDevCaps->SystemWake;
|
|
pDevExt->DeviceWake = pDevCaps->DeviceWake;
|
|
|
|
errQueryCaps:;
|
|
|
|
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|
//
|
|
// We just pass this down -- serenum enumerates our bus for us.
|
|
//
|
|
|
|
SerialDump (SERPNPPOWER, ("SERIAL: Got IRP_MN_QUERY_DEVICE_RELATIONS "
|
|
"Irp\n"));
|
|
|
|
switch (pIrpStack->Parameters.QueryDeviceRelations.Type) {
|
|
case BusRelations:
|
|
SerialDump(SERPNPPOWER, ("------- BusRelations Query\n"));
|
|
break;
|
|
|
|
case EjectionRelations:
|
|
SerialDump(SERPNPPOWER, ("------- EjectionRelations Query\n"));
|
|
break;
|
|
|
|
case PowerRelations:
|
|
SerialDump(SERPNPPOWER, ("------- PowerRelations Query\n"));
|
|
break;
|
|
|
|
case RemovalRelations:
|
|
SerialDump(SERPNPPOWER, ("------- RemovalRelations Query\n"));
|
|
break;
|
|
|
|
case TargetDeviceRelation:
|
|
SerialDump(SERPNPPOWER, ("------- TargetDeviceRelation Query\n"));
|
|
break;
|
|
|
|
default:
|
|
SerialDump(SERPNPPOWER, ("------- Unknown Query\n"));
|
|
break;
|
|
}
|
|
|
|
IoSkipCurrentIrpStackLocation(PIrp);
|
|
status = SerialIoCallDriver(pDevExt, pLowerDevObj, PIrp);
|
|
return status;
|
|
|
|
|
|
case IRP_MN_QUERY_INTERFACE:
|
|
SerialDump (SERPNPPOWER, ("SERIAL: Got IRP_MN_QUERY_INTERFACE Irp\n"));
|
|
break;
|
|
|
|
|
|
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
|
|
SerialDump (SERPNPPOWER, ("SERIAL: Got "
|
|
"IRP_MN_QUERY_RESOURCE_REQUIREMENTS Irp\n"));
|
|
break;
|
|
|
|
|
|
case IRP_MN_START_DEVICE: {
|
|
PVOID startLockPtr;
|
|
|
|
SerialDump (SERPNPPOWER, ("SERIAL: Got IRP_MN_START_DEVICE Irp\n"));
|
|
|
|
//
|
|
// SerialStartDevice will pass this Irp to the next driver,
|
|
// and process it as completion so just complete it here.
|
|
//
|
|
|
|
SerialLockPagableSectionByHandle(SerialGlobals.PAGESER_Handle);
|
|
|
|
//
|
|
// We used to make sure the stack was powered up, but now it
|
|
// is supposed to be done implicitly by start_device.
|
|
// If that wasn't the case we would just make this call:
|
|
//
|
|
// status = SerialGotoPowerState(PDevObj, pDevExt, PowerDeviceD0);
|
|
//
|
|
|
|
pDevExt->PowerState = PowerDeviceD0;
|
|
|
|
status = SerialStartDevice(PDevObj, PIrp);
|
|
|
|
(void)SerialGotoPowerState(PDevObj, pDevExt, PowerDeviceD3);
|
|
|
|
SerialUnlockPagableImageSection(SerialGlobals.PAGESER_Handle);
|
|
|
|
|
|
PIrp->IoStatus.Status = status;
|
|
|
|
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
|
|
case IRP_MN_READ_CONFIG:
|
|
SerialDump (SERPNPPOWER, ("SERIAL: Got IRP_MN_READ_CONFIG Irp\n"));
|
|
break;
|
|
|
|
|
|
case IRP_MN_WRITE_CONFIG:
|
|
SerialDump (SERPNPPOWER, ("SERIAL: Got IRP_MN_WRITE_CONFIG Irp\n"));
|
|
break;
|
|
|
|
|
|
case IRP_MN_EJECT:
|
|
SerialDump (SERPNPPOWER, ("SERIAL: Got IRP_MN_EJECT Irp\n"));
|
|
break;
|
|
|
|
|
|
case IRP_MN_SET_LOCK:
|
|
SerialDump (SERPNPPOWER, ("SERIAL: Got IRP_MN_SET_LOCK Irp\n"));
|
|
break;
|
|
|
|
|
|
case IRP_MN_QUERY_ID: {
|
|
UNICODE_STRING pIdBuf;
|
|
PWCHAR pPnpIdStr;
|
|
ULONG pnpIdStrLen;
|
|
ULONG isMulti = 0;
|
|
HANDLE pnpKey;
|
|
|
|
SerialDump(SERPNPPOWER, ("SERIAL: Got IRP_MN_QUERY_ID Irp\n"));
|
|
|
|
if (pIrpStack->Parameters.QueryId.IdType != BusQueryHardwareIDs
|
|
&& pIrpStack->Parameters.QueryId.IdType != BusQueryCompatibleIDs) {
|
|
IoSkipCurrentIrpStackLocation(PIrp);
|
|
return SerialIoCallDriver(pDevExt, pLowerDevObj, PIrp);
|
|
}
|
|
|
|
if (pIrpStack->Parameters.QueryId.IdType == BusQueryCompatibleIDs) {
|
|
PIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoSkipCurrentIrpStackLocation(PIrp);
|
|
return SerialIoCallDriver(pDevExt, pLowerDevObj, PIrp);
|
|
}
|
|
|
|
status = IoOpenDeviceRegistryKey(pDevExt->Pdo, PLUGPLAY_REGKEY_DEVICE,
|
|
STANDARD_RIGHTS_WRITE, &pnpKey);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
PIrp->IoStatus.Status = status;
|
|
|
|
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
|
return status;
|
|
|
|
}
|
|
|
|
status = SerialGetRegistryKeyValue (pnpKey, L"MultiportDevice",
|
|
sizeof(L"MultiportDevice"),
|
|
&isMulti,
|
|
sizeof (ULONG));
|
|
|
|
ZwClose(pnpKey);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
PIrp->IoStatus.Status = status;
|
|
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
pPnpIdStr = isMulti ? SERIAL_PNP_MULTI_ID_STR : SERIAL_PNP_ID_STR;
|
|
pnpIdStrLen = isMulti ? sizeof(SERIAL_PNP_MULTI_ID_STR)
|
|
: sizeof(SERIAL_PNP_ID_STR);
|
|
|
|
if (PIrp->IoStatus.Information != 0) {
|
|
ULONG curStrLen;
|
|
ULONG allocLen = 0;
|
|
PWSTR curStr = (PWSTR)PIrp->IoStatus.Information;
|
|
|
|
//
|
|
// We have to walk the strings to count the amount of space to
|
|
// reallocate
|
|
//
|
|
|
|
while ((curStrLen = wcslen(curStr)) != 0) {
|
|
allocLen += curStrLen * sizeof(WCHAR) + sizeof(UNICODE_NULL);
|
|
curStr += curStrLen + 1;
|
|
}
|
|
|
|
allocLen += sizeof(UNICODE_NULL);
|
|
|
|
pIdBuf.Buffer = ExAllocatePool(PagedPool, allocLen
|
|
+ pnpIdStrLen
|
|
+ sizeof(WCHAR));
|
|
|
|
if (pIdBuf.Buffer == NULL) {
|
|
//
|
|
// Clean up after other drivers since we are
|
|
// sending the irp back up.
|
|
//
|
|
|
|
ExFreePool((PWSTR)PIrp->IoStatus.Information);
|
|
|
|
|
|
PIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
PIrp->IoStatus.Information = 0;
|
|
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
pIdBuf.MaximumLength = (USHORT)(allocLen + pnpIdStrLen);
|
|
pIdBuf.Length = (USHORT)allocLen - sizeof(UNICODE_NULL);
|
|
|
|
RtlZeroMemory(pIdBuf.Buffer, pIdBuf.MaximumLength + sizeof(WCHAR));
|
|
RtlCopyMemory(pIdBuf.Buffer, (PWSTR)PIrp->IoStatus.Information,
|
|
allocLen);
|
|
RtlAppendUnicodeToString(&pIdBuf, pPnpIdStr);
|
|
|
|
//
|
|
// Free what the previous driver allocated
|
|
//
|
|
|
|
ExFreePool((PWSTR)PIrp->IoStatus.Information);
|
|
|
|
|
|
} else {
|
|
|
|
SerialDump(SERPNPPOWER, ("SERIAL: ID is sole ID\n"));
|
|
|
|
pIdBuf.Buffer = ExAllocatePool(PagedPool, pnpIdStrLen
|
|
+ sizeof(WCHAR) * 2);
|
|
|
|
if (pIdBuf.Buffer == NULL) {
|
|
PIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
PIrp->IoStatus.Information = 0;
|
|
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
pIdBuf.MaximumLength = (USHORT)pnpIdStrLen;
|
|
pIdBuf.Length = 0;
|
|
|
|
RtlZeroMemory(pIdBuf.Buffer, pIdBuf.MaximumLength + sizeof(WCHAR)
|
|
* 2);
|
|
|
|
RtlAppendUnicodeToString(&pIdBuf, pPnpIdStr);
|
|
}
|
|
|
|
PIrp->IoStatus.Information = (ULONG_PTR)pIdBuf.Buffer;
|
|
PIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(PIrp);
|
|
return SerialIoCallDriver(pDevExt, pLowerDevObj, PIrp);
|
|
}
|
|
|
|
case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: {
|
|
HANDLE pnpKey;
|
|
PKEVENT pResFiltEvent;
|
|
ULONG isMulti = 0;
|
|
PIO_RESOURCE_REQUIREMENTS_LIST pReqList;
|
|
PIO_RESOURCE_LIST pResList;
|
|
PIO_RESOURCE_DESCRIPTOR pResDesc;
|
|
ULONG i, j;
|
|
ULONG reqCnt;
|
|
ULONG gotISR;
|
|
ULONG gotInt;
|
|
ULONG listNum;
|
|
|
|
SerialDump(SERPNPPOWER, ("SERIAL: Got "
|
|
"IRP_MN_FILTER_RESOURCE_REQUIREMENTS Irp\n"));
|
|
SerialDump(SERPNPPOWER, ("------- for device %x\n", pLowerDevObj));
|
|
|
|
|
|
pResFiltEvent = ExAllocatePool(NonPagedPool, sizeof(KEVENT));
|
|
|
|
if (pResFiltEvent == NULL) {
|
|
PIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
KeInitializeEvent(pResFiltEvent, SynchronizationEvent, FALSE);
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(PIrp);
|
|
IoSetCompletionRoutine(PIrp, SerialSyncCompletion, pResFiltEvent,
|
|
TRUE, TRUE, TRUE);
|
|
|
|
status = IoCallDriver(pLowerDevObj, PIrp);
|
|
|
|
|
|
//
|
|
// Wait for lower drivers to be done with the Irp
|
|
//
|
|
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject (pResFiltEvent, Executive, KernelMode, FALSE,
|
|
NULL);
|
|
}
|
|
|
|
ExFreePool(pResFiltEvent);
|
|
|
|
if (PIrp->IoStatus.Information == 0) {
|
|
if (pIrpStack->Parameters.FilterResourceRequirements
|
|
.IoResourceRequirementList == 0) {
|
|
SerialDump(SERPNPPOWER, ("------- Can't filter NULL resources!"
|
|
"\n"));
|
|
status = PIrp->IoStatus.Status;
|
|
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
PIrp->IoStatus.Information = (ULONG_PTR)pIrpStack->Parameters
|
|
.FilterResourceRequirements
|
|
.IoResourceRequirementList;
|
|
|
|
}
|
|
|
|
status = IoOpenDeviceRegistryKey(pDevExt->Pdo, PLUGPLAY_REGKEY_DEVICE,
|
|
STANDARD_RIGHTS_WRITE, &pnpKey);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
PIrp->IoStatus.Status = status;
|
|
|
|
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
|
return status;
|
|
|
|
}
|
|
|
|
//
|
|
// No matter what we add our filter if we can and return success.
|
|
//
|
|
|
|
status = SerialGetRegistryKeyValue (pnpKey, L"MultiportDevice",
|
|
sizeof(L"MultiportDevice"),
|
|
&isMulti,
|
|
sizeof (ULONG));
|
|
|
|
ZwClose(pnpKey);
|
|
|
|
|
|
//
|
|
// Force ISR ports in IO_RES_REQ_LIST to shared status
|
|
// Force interrupts to shared status
|
|
//
|
|
|
|
//
|
|
// We will only process the first list -- multiport boards
|
|
// should not have alternative resources
|
|
//
|
|
|
|
pReqList = (PIO_RESOURCE_REQUIREMENTS_LIST)PIrp->IoStatus.Information;
|
|
pResList = &pReqList->List[0];
|
|
|
|
SerialDump(SERPNPPOWER, ("------- List has %x lists "
|
|
"(including alternatives)\n",
|
|
pReqList->AlternativeLists));
|
|
|
|
for (listNum = 0; listNum < (pReqList->AlternativeLists);
|
|
listNum++) {
|
|
#if defined(NEC_98)
|
|
for (j = 0; (j < pResList->Count); j++) {
|
|
pResDesc = &pResList->Descriptors[j];
|
|
|
|
if (isMulti && pResDesc->Type == CmResourceTypePort) {
|
|
pResDesc->ShareDisposition = CmResourceShareShared;
|
|
SerialDump(SERPNPPOWER, ("------- Sharing I/O port for "
|
|
"device %x\n", pLowerDevObj));
|
|
}
|
|
}
|
|
#else
|
|
gotISR = 0;
|
|
gotInt = 0;
|
|
|
|
SerialDump(SERPNPPOWER, ("------- List has %x resources in it\n",
|
|
pResList->Count));
|
|
|
|
for (j = 0; (j < pResList->Count); j++) {
|
|
pResDesc = &pResList->Descriptors[j];
|
|
|
|
switch (pResDesc->Type) {
|
|
case CmResourceTypePort:
|
|
if (isMulti
|
|
&& (pResDesc->u.Port.Length == SERIAL_STATUS_LENGTH)
|
|
&& !gotISR) {
|
|
gotISR = 1;
|
|
pResDesc->ShareDisposition = CmResourceShareShared;
|
|
SerialDump(SERPNPPOWER, ("------- Sharing I/O port for "
|
|
"device %x\n", pLowerDevObj));
|
|
}
|
|
break;
|
|
|
|
case CmResourceTypeInterrupt:
|
|
if (!gotInt) {
|
|
gotInt = 1;
|
|
if (pResDesc->ShareDisposition != CmResourceShareShared) {
|
|
pResDesc->ShareDisposition = CmResourceShareShared;
|
|
SerialDump(SERPNPPOWER, ("------- Sharing interrupt "
|
|
"for device %x\n",
|
|
pLowerDevObj));
|
|
} else {
|
|
pDevExt->InterruptShareable = TRUE;
|
|
SerialDump(SERPNPPOWER, ("------- Globally sharing "
|
|
" interrupt for device %x\n",
|
|
pLowerDevObj));
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we found what we need, we can break out of the loop
|
|
//
|
|
|
|
if ((isMulti && gotInt && gotISR) || (!isMulti && gotInt)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
pResList = (PIO_RESOURCE_LIST)((PUCHAR)pResList
|
|
+ sizeof(IO_RESOURCE_LIST)
|
|
+ sizeof(IO_RESOURCE_DESCRIPTOR)
|
|
* (pResList->Count - 1));
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
PIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
case IRP_MN_QUERY_PNP_DEVICE_STATE:
|
|
{
|
|
if (pDevExt->Flags & SERIAL_FLAGS_BROKENHW) {
|
|
(PNP_DEVICE_STATE)PIrp->IoStatus.Information |= PNP_DEVICE_FAILED;
|
|
|
|
PIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(PIrp);
|
|
return SerialIoCallDriver(pDevExt, pLowerDevObj, PIrp);
|
|
}
|
|
|
|
case IRP_MN_STOP_DEVICE:
|
|
{
|
|
ULONG pendingIRPs;
|
|
KIRQL oldIrql;
|
|
|
|
SerialDump(SERPNPPOWER, ("SERIAL: Got IRP_MN_STOP_DEVICE Irp\n"));
|
|
SerialDump(SERPNPPOWER, ("------- for device %x\n", pLowerDevObj));
|
|
|
|
|
|
|
|
ASSERT(!pDevExt->PortOnAMultiportCard);
|
|
|
|
|
|
SerialSetFlags(pDevExt, SERIAL_FLAGS_STOPPED);
|
|
SerialSetAccept(pDevExt,SERIAL_PNPACCEPT_STOPPED);
|
|
SerialClearAccept(pDevExt, SERIAL_PNPACCEPT_STOPPING);
|
|
|
|
pDevExt->PNPState = SERIAL_PNP_STOPPING;
|
|
|
|
//
|
|
// From this point on all non-PNP IRP's will be queued
|
|
//
|
|
|
|
//
|
|
// Decrement for entry here
|
|
//
|
|
|
|
InterlockedDecrement(&pDevExt->PendingIRPCnt);
|
|
|
|
//
|
|
// Decrement for stopping
|
|
//
|
|
|
|
pendingIRPs = InterlockedDecrement(&pDevExt->PendingIRPCnt);
|
|
|
|
if (pendingIRPs) {
|
|
KeWaitForSingleObject(&pDevExt->PendingIRPEvent, Executive,
|
|
KernelMode, FALSE, NULL);
|
|
}
|
|
|
|
//
|
|
// Re-increment the count for later
|
|
//
|
|
|
|
InterlockedIncrement(&pDevExt->PendingIRPCnt);
|
|
|
|
//
|
|
// We need to free resources...basically this is a remove
|
|
// without the detach from the stack.
|
|
//
|
|
|
|
if (pDevExt->Flags & SERIAL_FLAGS_STARTED) {
|
|
SerialReleaseResources(pDevExt);
|
|
}
|
|
|
|
//
|
|
// Pass the irp down
|
|
//
|
|
|
|
PIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoSkipCurrentIrpStackLocation(PIrp);
|
|
|
|
return IoCallDriver(pLowerDevObj, PIrp);
|
|
}
|
|
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
{
|
|
KIRQL oldIrql;
|
|
|
|
SerialDump(SERPNPPOWER, ("SERIAL: Got IRP_MN_QUERY_STOP_DEVICE Irp\n")
|
|
);
|
|
SerialDump(SERPNPPOWER, ("------- for device %x\n", pLowerDevObj));
|
|
|
|
//
|
|
// See if we should succeed a stop query
|
|
//
|
|
|
|
|
|
if (pDevExt->PortOnAMultiportCard) {
|
|
PIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
SerialDump(SERPNPPOWER, ("------- failing; multiport node\n"));
|
|
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
//
|
|
// If the device hasn't started yet, we ignore this request
|
|
// and just pass it down.
|
|
//
|
|
|
|
if (pDevExt->PNPState != SERIAL_PNP_STARTED) {
|
|
IoSkipCurrentIrpStackLocation(PIrp);
|
|
return SerialIoCallDriver(pDevExt, pLowerDevObj, PIrp);
|
|
}
|
|
|
|
//
|
|
// Lock around the open status
|
|
//
|
|
|
|
ExAcquireFastMutex(&pDevExt->OpenMutex);
|
|
|
|
if (pDevExt->DeviceIsOpened) {
|
|
ExReleaseFastMutex(&pDevExt->OpenMutex);
|
|
PIrp->IoStatus.Status = STATUS_DEVICE_BUSY;
|
|
SerialDump(SERPNPPOWER, ("------- failing; device open\n"));
|
|
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
|
return STATUS_DEVICE_BUSY;
|
|
}
|
|
|
|
pDevExt->PNPState = SERIAL_PNP_QSTOP;
|
|
|
|
SerialSetAccept(pDevExt, SERIAL_PNPACCEPT_STOPPING);
|
|
//
|
|
// Unlock around the open status
|
|
//
|
|
|
|
ExReleaseFastMutex(&pDevExt->OpenMutex);
|
|
|
|
PIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCopyCurrentIrpStackLocationToNext(PIrp);
|
|
return SerialIoCallDriver(pDevExt, pLowerDevObj, PIrp);
|
|
}
|
|
|
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|
SerialDump(SERPNPPOWER, ("SERIAL: Got "
|
|
"IRP_MN_CANCEL_STOP_DEVICE Irp\n"));
|
|
SerialDump(SERPNPPOWER, ("------- for device %x\n", pLowerDevObj));
|
|
|
|
if (pDevExt->PNPState == SERIAL_PNP_QSTOP) {
|
|
//
|
|
// Restore the device state
|
|
//
|
|
|
|
pDevExt->PNPState = SERIAL_PNP_STARTED;
|
|
SerialClearAccept(pDevExt, SERIAL_PNPACCEPT_STOPPING);
|
|
}
|
|
|
|
PIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCopyCurrentIrpStackLocationToNext(PIrp);
|
|
return SerialIoCallDriver(pDevExt, pLowerDevObj, PIrp);
|
|
|
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|
|
|
SerialDump(SERPNPPOWER, ("SERIAL: Got "
|
|
"IRP_MN_CANCEL_REMOVE_DEVICE Irp\n"));
|
|
SerialDump(SERPNPPOWER, ("------- for device %x\n", pLowerDevObj));
|
|
|
|
//
|
|
// Restore the device state
|
|
//
|
|
|
|
pDevExt->PNPState = SERIAL_PNP_STARTED;
|
|
SerialClearAccept(pDevExt, SERIAL_PNPACCEPT_REMOVING);
|
|
|
|
PIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCopyCurrentIrpStackLocationToNext(PIrp);
|
|
return SerialIoCallDriver(pDevExt, pLowerDevObj, PIrp);
|
|
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
{
|
|
KIRQL oldIrql;
|
|
SerialDump(SERPNPPOWER, ("SERIAL: Got "
|
|
"IRP_MN_QUERY_REMOVE_DEVICE Irp\n"));
|
|
SerialDump(SERPNPPOWER, ("------- for device %x\n", pLowerDevObj));
|
|
|
|
ExAcquireFastMutex(&pDevExt->OpenMutex);
|
|
|
|
//
|
|
// See if we should succeed a remove query
|
|
//
|
|
|
|
if (pDevExt->DeviceIsOpened) {
|
|
ExReleaseFastMutex(&pDevExt->OpenMutex);
|
|
PIrp->IoStatus.Status = STATUS_DEVICE_BUSY;
|
|
SerialDump(SERPNPPOWER, ("------- failing; device open\n"));
|
|
SerialCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
|
return STATUS_DEVICE_BUSY;
|
|
}
|
|
|
|
pDevExt->PNPState = SERIAL_PNP_QREMOVE;
|
|
SerialSetAccept(pDevExt, SERIAL_PNPACCEPT_REMOVING);
|
|
ExReleaseFastMutex(&pDevExt->OpenMutex);
|
|
|
|
PIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCopyCurrentIrpStackLocationToNext(PIrp);
|
|
return SerialIoCallDriver(pDevExt, pLowerDevObj, PIrp);
|
|
}
|
|
|
|
case IRP_MN_SURPRISE_REMOVAL:
|
|
{
|
|
ULONG pendingIRPs;
|
|
KIRQL oldIrql;
|
|
|
|
SerialDump(SERPNPPOWER, ("SERIAL: Got IRP_MN_SURPRISE_REMOVAL Irp\n"));
|
|
SerialDump(SERPNPPOWER, ("------- for device %x\n", pLowerDevObj));
|
|
|
|
//
|
|
// Prevent any new I/O to the device
|
|
//
|
|
|
|
SerialSetAccept(pDevExt, SERIAL_PNPACCEPT_SURPRISE_REMOVING);
|
|
|
|
//
|
|
// Dismiss all pending requests
|
|
//
|
|
|
|
SerialKillPendingIrps(PDevObj);
|
|
|
|
//
|
|
// Wait for any pending requests we raced on.
|
|
//
|
|
|
|
//
|
|
// Decrement once for ourselves
|
|
//
|
|
|
|
InterlockedDecrement(&pDevExt->PendingIRPCnt);
|
|
|
|
//
|
|
// Decrement for the remove
|
|
//
|
|
|
|
pendingIRPs = InterlockedDecrement(&pDevExt->PendingIRPCnt);
|
|
|
|
if (pendingIRPs) {
|
|
KeWaitForSingleObject(&pDevExt->PendingIRPEvent, Executive,
|
|
KernelMode, FALSE, NULL);
|
|
}
|
|
|
|
//
|
|
// Reset for subsequent remove
|
|
//
|
|
|
|
InterlockedIncrement(&pDevExt->PendingIRPCnt);
|
|
|
|
//
|
|
// Remove any external interfaces and release resources
|
|
//
|
|
|
|
SerialDisableInterfacesResources(PDevObj, FALSE);
|
|
|
|
PIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoSkipCurrentIrpStackLocation(PIrp);
|
|
|
|
return SerialIoCallDriver(pDevExt, pLowerDevObj, PIrp);
|
|
}
|
|
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
|
|
{
|
|
ULONG pendingIRPs;
|
|
KIRQL oldIrql;
|
|
|
|
SerialDump(SERPNPPOWER, ("SERIAL: Got IRP_MN_REMOVE_DEVICE Irp\n"));
|
|
SerialDump(SERPNPPOWER, ("------- for device %x\n", pLowerDevObj));
|
|
|
|
//
|
|
// If we get this, we have to remove
|
|
//
|
|
|
|
//
|
|
// Mark as not accepting requests
|
|
//
|
|
|
|
SerialSetAccept(pDevExt, SERIAL_PNPACCEPT_REMOVING);
|
|
|
|
//
|
|
// Complete all pending requests
|
|
//
|
|
|
|
SerialKillPendingIrps(PDevObj);
|
|
|
|
//
|
|
// Decrement for this Irp itself
|
|
//
|
|
|
|
InterlockedDecrement(&pDevExt->PendingIRPCnt);
|
|
|
|
//
|
|
// Wait for any pending requests we raced on -- this decrement
|
|
// is for our "placeholder".
|
|
//
|
|
|
|
pendingIRPs = InterlockedDecrement(&pDevExt->PendingIRPCnt);
|
|
|
|
if (pendingIRPs) {
|
|
KeWaitForSingleObject(&pDevExt->PendingIRPEvent, Executive,
|
|
KernelMode, FALSE, NULL);
|
|
}
|
|
|
|
//
|
|
// Remove us
|
|
//
|
|
|
|
SerialRemoveDevObj(PDevObj);
|
|
|
|
|
|
//
|
|
// Pass the irp down
|
|
//
|
|
|
|
PIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(PIrp);
|
|
|
|
//
|
|
// We do decrement here because we incremented on entry here.
|
|
//
|
|
|
|
IoCallDriver(pLowerDevObj, PIrp);
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
|
|
|
|
|
|
} // switch (pIrpStack->MinorFunction)
|
|
|
|
//
|
|
// Pass to driver beneath us
|
|
//
|
|
|
|
IoSkipCurrentIrpStackLocation(PIrp);
|
|
status = SerialIoCallDriver(pDevExt, pLowerDevObj, PIrp);
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
UINT32
|
|
SerialReportMaxBaudRate(ULONG Bauds)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the max baud rate given a selection of rates
|
|
|
|
Arguments:
|
|
|
|
Bauds - Bit-encoded list of supported bauds
|
|
|
|
|
|
Return Value:
|
|
|
|
The max baud rate listed in Bauds
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (Bauds & SERIAL_BAUD_128K) {
|
|
return (128U * 1024U);
|
|
}
|
|
|
|
if (Bauds & SERIAL_BAUD_115200) {
|
|
return 115200U;
|
|
}
|
|
|
|
if (Bauds & SERIAL_BAUD_56K) {
|
|
return (56U * 1024U);
|
|
}
|
|
|
|
if (Bauds & SERIAL_BAUD_57600) {
|
|
return 57600U;
|
|
}
|
|
|
|
if (Bauds & SERIAL_BAUD_38400) {
|
|
return 38400U;
|
|
}
|
|
|
|
if (Bauds & SERIAL_BAUD_19200) {
|
|
return 19200U;
|
|
}
|
|
|
|
if (Bauds & SERIAL_BAUD_14400) {
|
|
return 14400U;
|
|
}
|
|
|
|
if (Bauds & SERIAL_BAUD_9600) {
|
|
return 9600U;
|
|
}
|
|
|
|
if (Bauds & SERIAL_BAUD_7200) {
|
|
return 7200U;
|
|
}
|
|
|
|
if (Bauds & SERIAL_BAUD_4800) {
|
|
return 4800U;
|
|
}
|
|
|
|
if (Bauds & SERIAL_BAUD_2400) {
|
|
return 2400U;
|
|
}
|
|
|
|
if (Bauds & SERIAL_BAUD_1800) {
|
|
return 1800U;
|
|
}
|
|
|
|
if (Bauds & SERIAL_BAUD_1200) {
|
|
return 1200U;
|
|
}
|
|
|
|
if (Bauds & SERIAL_BAUD_600) {
|
|
return 600U;
|
|
}
|
|
|
|
if (Bauds & SERIAL_BAUD_300) {
|
|
return 300U;
|
|
}
|
|
|
|
if (Bauds & SERIAL_BAUD_150) {
|
|
return 150U;
|
|
}
|
|
|
|
if (Bauds & SERIAL_BAUD_134_5) {
|
|
return 135U; // Close enough
|
|
}
|
|
|
|
if (Bauds & SERIAL_BAUD_110) {
|
|
return 110U;
|
|
}
|
|
|
|
if (Bauds & SERIAL_BAUD_075) {
|
|
return 75U;
|
|
}
|
|
|
|
//
|
|
// We're in bad shape
|
|
//
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
SerialFinishStartDevice(IN PDEVICE_OBJECT PDevObj,
|
|
IN PCM_RESOURCE_LIST PResList,
|
|
IN PCM_RESOURCE_LIST PTrResList,
|
|
PSERIAL_USER_DATA PUserData)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does serial-specific procedures to start a device. It
|
|
does this either for a legacy device detected by its registry entries,
|
|
or for a PnP device after the start IRP has been sent down the stack.
|
|
|
|
|
|
Arguments:
|
|
|
|
PDevObj - Pointer to the devobj that is starting
|
|
|
|
PResList - Pointer to the untranslated resources needed by this device
|
|
|
|
PTrResList - Pointer to the translated resources needed by this device
|
|
|
|
PUserData - Pointer to the user-specified resources/attributes
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS on success, something else appropriate on failure
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PSERIAL_DEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
|
#if defined(NEC_98)
|
|
//
|
|
// This argument use at MACRO only.
|
|
//
|
|
PSERIAL_DEVICE_EXTENSION Extension = PDevObj->DeviceExtension;
|
|
#else
|
|
#endif //defined(NEC_98)
|
|
NTSTATUS status;
|
|
PCONFIG_DATA pConfig;
|
|
HANDLE pnpKey;
|
|
ULONG one = 1;
|
|
BOOLEAN allocedUserData = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// See if this is a restart, and if so don't reallocate the world
|
|
//
|
|
|
|
if ((pDevExt->Flags & SERIAL_FLAGS_STOPPED)
|
|
&& (pDevExt->Flags & SERIAL_FLAGS_STARTED)) {
|
|
SerialClearFlags(pDevExt, SERIAL_FLAGS_STOPPED);
|
|
|
|
pDevExt->PNPState = SERIAL_PNP_RESTARTING;
|
|
|
|
//
|
|
// Re-init resource-related things in the extension
|
|
//
|
|
|
|
pDevExt->TopLevelOurIsr = NULL;
|
|
pDevExt->TopLevelOurIsrContext = NULL;
|
|
|
|
pDevExt->OriginalController = SerialPhysicalZero;
|
|
pDevExt->OriginalInterruptStatus = SerialPhysicalZero;
|
|
|
|
pDevExt->OurIsr = NULL;
|
|
pDevExt->OurIsrContext = NULL;
|
|
|
|
pDevExt->Controller = NULL;
|
|
pDevExt->InterruptStatus = NULL;
|
|
pDevExt->Interrupt = NULL;
|
|
|
|
pDevExt->SpanOfController = 0;
|
|
pDevExt->SpanOfInterruptStatus = 0;
|
|
|
|
pDevExt->Vector = 0;
|
|
pDevExt->Irql = 0;
|
|
pDevExt->OriginalVector = 0;
|
|
pDevExt->OriginalIrql = 0;
|
|
pDevExt->AddressSpace = 0;
|
|
pDevExt->BusNumber = 0;
|
|
pDevExt->InterfaceType = 0;
|
|
|
|
pDevExt->CIsrSw = NULL;
|
|
|
|
ASSERT(PUserData == NULL);
|
|
|
|
PUserData = ExAllocatePool(PagedPool, sizeof(SERIAL_USER_DATA));
|
|
|
|
if (PUserData == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
allocedUserData = TRUE;
|
|
|
|
RtlZeroMemory(PUserData, sizeof(SERIAL_USER_DATA));
|
|
|
|
PUserData->DisablePort = FALSE;
|
|
PUserData->UserClockRate = pDevExt->ClockRate;
|
|
PUserData->TxFIFO = pDevExt->TxFifoAmount;
|
|
PUserData->PermitShareDefault = pDevExt->PermitShare;
|
|
|
|
|
|
//
|
|
// Map betweeen trigger and amount
|
|
//
|
|
|
|
switch (pDevExt->RxFifoTrigger) {
|
|
case SERIAL_1_BYTE_HIGH_WATER:
|
|
PUserData->RxFIFO = 1;
|
|
break;
|
|
|
|
case SERIAL_4_BYTE_HIGH_WATER:
|
|
PUserData->RxFIFO = 4;
|
|
break;
|
|
|
|
case SERIAL_8_BYTE_HIGH_WATER:
|
|
PUserData->RxFIFO = 8;
|
|
break;
|
|
|
|
case SERIAL_14_BYTE_HIGH_WATER:
|
|
PUserData->RxFIFO = 14;
|
|
break;
|
|
|
|
default:
|
|
PUserData->RxFIFO = 1;
|
|
}
|
|
} else {
|
|
//
|
|
// Mark as serenumerable -- toss status because we can
|
|
// still start without this key.
|
|
//
|
|
|
|
status = IoOpenDeviceRegistryKey(pDevExt->Pdo,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
STANDARD_RIGHTS_WRITE, &pnpKey);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
ULONG powerPolicy = 0;
|
|
|
|
//
|
|
// Find out if we own power policy
|
|
//
|
|
|
|
SerialGetRegistryKeyValue(pnpKey, L"SerialRelinquishPowerPolicy",
|
|
sizeof(L"SerialRelinquishPowerPolicy"),
|
|
&powerPolicy, sizeof(ULONG));
|
|
|
|
pDevExt->OwnsPowerPolicy = powerPolicy ? FALSE : TRUE;
|
|
|
|
|
|
ZwClose(pnpKey);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate the config record.
|
|
//
|
|
|
|
pConfig = ExAllocatePool (PagedPool, sizeof(CONFIG_DATA));
|
|
|
|
if (pConfig == NULL) {
|
|
|
|
SerialLogError(pDevExt->DriverObject, NULL, SerialPhysicalZero,
|
|
SerialPhysicalZero, 0, 0, 0, 37, STATUS_SUCCESS,
|
|
SERIAL_INSUFFICIENT_RESOURCES, 0, NULL, 0, NULL);
|
|
|
|
SerialDump(SERERRORS, ("SERIAL: Couldn't allocate memory for the\n"
|
|
"------ user configuration record\n"));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SerialFinishStartDeviceError;
|
|
}
|
|
|
|
RtlZeroMemory(pConfig, sizeof(CONFIG_DATA));
|
|
|
|
|
|
//
|
|
// Get the configuration info for the device.
|
|
//
|
|
|
|
status = SerialGetPortInfo(PDevObj, PResList, PTrResList, pConfig,
|
|
PUserData);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto SerialFinishStartDeviceError;
|
|
}
|
|
|
|
|
|
//
|
|
// See if we are in the proper power state.
|
|
//
|
|
|
|
|
|
|
|
if (pDevExt->PowerState != PowerDeviceD0) {
|
|
|
|
status = SerialGotoPowerState(pDevExt->Pdo, pDevExt, PowerDeviceD0);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto SerialFinishStartDeviceError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Find and initialize the controller
|
|
//
|
|
|
|
status = SerialFindInitController(PDevObj, pConfig);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto SerialFinishStartDeviceError;
|
|
}
|
|
|
|
|
|
//
|
|
// The hardware that is set up to NOT interrupt, connect an interrupt.
|
|
//
|
|
|
|
//
|
|
// If a device doesn't already have an interrupt and it has an isr then
|
|
// we attempt to connect to the interrupt if it is not shareing with other
|
|
// serial devices. If we fail to connect to an interrupt we will delete
|
|
// this device.
|
|
//
|
|
|
|
if (pDevExt != NULL) {
|
|
SerialDump(SERDIAG5, ("SERIAL: pDevExt: Interrupt %x\n"
|
|
"------- OurIsr %x\n",
|
|
pDevExt->Interrupt, pDevExt->OurIsr));
|
|
} else {
|
|
SerialDump(SERERRORS, ("SERIAL: SerialFinishStartDevice got NULL "
|
|
"pDevExt\n"));
|
|
}
|
|
|
|
if ((!pDevExt->Interrupt) && (pDevExt->OurIsr)) {
|
|
|
|
SerialDump(
|
|
SERDIAG5,
|
|
("SERIAL: About to connect to interrupt for port %wZ\n"
|
|
"------- address of extension is %x\n",
|
|
&pDevExt->DeviceName, pDevExt)
|
|
);
|
|
|
|
SerialDump(SERDIAG5, ("SERIAL: IoConnectInterrupt Args:\n"
|
|
"------- Interrupt %x\n"
|
|
"------- OurIsr %x\n"
|
|
"------- OurIsrContext %x\n"
|
|
"------- NULL\n"
|
|
"------- Vector %x\n"
|
|
"------- Irql %x\n"
|
|
"------- InterruptMode %x\n"
|
|
"------- InterruptShareable %x\n"
|
|
"------- ProcessorAffinity %x\n"
|
|
"------- FALSE\n",
|
|
&pDevExt->Interrupt,
|
|
SerialCIsrSw,
|
|
pDevExt->CIsrSw,
|
|
pDevExt->Vector,
|
|
pDevExt->Irql,
|
|
pConfig->InterruptMode,
|
|
pDevExt->InterruptShareable,
|
|
pConfig->Affinity
|
|
));
|
|
|
|
//
|
|
// Do a just in time construction of the ISR switch.
|
|
//
|
|
|
|
pDevExt->CIsrSw->IsrFunc = pDevExt->OurIsr;
|
|
pDevExt->CIsrSw->Context = pDevExt->OurIsrContext;
|
|
|
|
status = IoConnectInterrupt(&pDevExt->Interrupt, SerialCIsrSw,
|
|
pDevExt->CIsrSw, NULL,
|
|
pDevExt->Vector, pDevExt->Irql,
|
|
pDevExt->Irql,
|
|
pConfig->InterruptMode,
|
|
pDevExt->InterruptShareable,
|
|
pConfig->Affinity, FALSE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Hmmm, how'd that happen? Somebody either
|
|
// didn't report their resources, or they
|
|
// sneaked in since the last time I looked.
|
|
//
|
|
// Oh well, delete this device.
|
|
//
|
|
|
|
SerialDump(SERERRORS, ("SERIAL: Couldn't connect to interrupt for "
|
|
"%wZ\n", &pDevExt->DeviceName));
|
|
|
|
SerialDump(SERERRORS, ("SERIAL: IoConnectInterrupt Args:\n"
|
|
"------- Interrupt %x\n"
|
|
"------- OurIsr %x\n"
|
|
"------- OurIsrContext %x\n"
|
|
"------- NULL\n"
|
|
"------- Vector %x\n"
|
|
"------- Irql %x\n"
|
|
"------- InterruptMode %x\n"
|
|
"------- InterruptShareable %x\n"
|
|
"------- ProcessorAffinity %x\n"
|
|
"------- FALSE\n",
|
|
&pDevExt->Interrupt,
|
|
SerialCIsrSw,
|
|
pDevExt->CIsrSw,
|
|
pDevExt->Vector,
|
|
pDevExt->Irql,
|
|
pConfig->InterruptMode,
|
|
pDevExt->InterruptShareable,
|
|
pConfig->Affinity
|
|
));
|
|
|
|
|
|
|
|
SerialLogError(PDevObj->DriverObject, PDevObj,
|
|
pDevExt->OriginalController,
|
|
SerialPhysicalZero, 0, 0, 0, 1, status,
|
|
SERIAL_UNREPORTED_IRQL_CONFLICT,
|
|
pDevExt->DeviceName.Length + sizeof(WCHAR),
|
|
pDevExt->DeviceName.Buffer, 0, NULL);
|
|
|
|
status = SERIAL_UNREPORTED_IRQL_CONFLICT;
|
|
goto SerialFinishStartDeviceError;
|
|
|
|
}
|
|
}
|
|
|
|
SerialDump(SERDIAG5, ("Connected interrupt %08X\n", pDevExt->Interrupt));
|
|
|
|
|
|
//
|
|
// Add the PDevObj to the master list
|
|
//
|
|
|
|
InsertTailList(&SerialGlobals.AllDevObjs, &pDevExt->AllDevObjs);
|
|
|
|
|
|
//
|
|
// Reset the device.
|
|
//
|
|
|
|
|
|
//
|
|
// While the device isn't open, disable all interrupts.
|
|
//
|
|
|
|
DISABLE_ALL_INTERRUPTS (pDevExt->Controller);
|
|
|
|
|
|
if (pDevExt->Jensen) {
|
|
|
|
WRITE_MODEM_CONTROL(
|
|
pDevExt->Controller,
|
|
(UCHAR)SERIAL_MCR_OUT2
|
|
);
|
|
|
|
} else {
|
|
|
|
WRITE_MODEM_CONTROL(
|
|
pDevExt->Controller,
|
|
(UCHAR)0
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// This should set up everything as it should be when
|
|
// a device is to be opened. We do need to lower the
|
|
// modem lines, and disable the recalcitrant fifo
|
|
// so that it will show up if the user boots to dos.
|
|
//
|
|
|
|
KeSynchronizeExecution(
|
|
pDevExt->Interrupt,
|
|
SerialReset,
|
|
pDevExt
|
|
);
|
|
|
|
KeSynchronizeExecution( //Disables the fifo.
|
|
pDevExt->Interrupt,
|
|
SerialMarkClose,
|
|
pDevExt
|
|
);
|
|
|
|
KeSynchronizeExecution(
|
|
pDevExt->Interrupt,
|
|
SerialClrRTS,
|
|
pDevExt
|
|
);
|
|
|
|
KeSynchronizeExecution(
|
|
pDevExt->Interrupt,
|
|
SerialClrDTR,
|
|
pDevExt
|
|
);
|
|
|
|
if (pDevExt->PNPState == SERIAL_PNP_ADDED ) {
|
|
//
|
|
// Do the external naming now that the device is accessible.
|
|
//
|
|
|
|
status = SerialDoExternalNaming(pDevExt, pDevExt->DeviceObject->
|
|
DriverObject);
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
SerialDump (SERERRORS,("SERIAL: External Naming Failed - Status %x\n",
|
|
status));
|
|
|
|
//
|
|
// Allow the device to start anyhow
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
SerialDump(SERPNPPOWER, ("Not doing external naming -- state is %x\n",
|
|
pDevExt->PNPState));
|
|
}
|
|
|
|
SerialFinishStartDeviceError:;
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
|
|
SerialDump(SERDIAG1,("SERIAL: Cleaning up failed start\n"));
|
|
|
|
//
|
|
// Resources created by this routine will be cleaned up by the remove
|
|
//
|
|
|
|
if (pDevExt->PNPState == SERIAL_PNP_RESTARTING) {
|
|
//
|
|
// Kill all that lives and breathes -- we'll clean up the
|
|
// rest on the impending remove
|
|
//
|
|
|
|
SerialKillPendingIrps(PDevObj);
|
|
|
|
//
|
|
// In fact, pretend we're removing so we don't take any
|
|
// more irps
|
|
//
|
|
|
|
SerialSetAccept(pDevExt, SERIAL_PNPACCEPT_REMOVING);
|
|
SerialClearFlags(pDevExt, SERIAL_FLAGS_STARTED);
|
|
}
|
|
} else { // SUCCESS
|
|
|
|
//
|
|
// Fill in WMI hardware data
|
|
//
|
|
|
|
pDevExt->WmiHwData.IrqNumber = pDevExt->Irql;
|
|
pDevExt->WmiHwData.IrqLevel = pDevExt->Irql;
|
|
pDevExt->WmiHwData.IrqVector = pDevExt->Vector;
|
|
pDevExt->WmiHwData.IrqAffinityMask = pConfig->Affinity;
|
|
pDevExt->WmiHwData.InterruptType = pConfig->InterruptMode == Latched
|
|
? SERIAL_WMI_INTTYPE_LATCHED : SERIAL_WMI_INTTYPE_LEVEL;
|
|
pDevExt->WmiHwData.BaseIOAddress = (ULONG_PTR)pDevExt->Controller;
|
|
|
|
//
|
|
// Fill in WMI device state data (as defaults)
|
|
//
|
|
|
|
pDevExt->WmiCommData.BaudRate = pDevExt->CurrentBaud;
|
|
pDevExt->WmiCommData.BitsPerByte = (pDevExt->LineControl & 0x03) + 5;
|
|
pDevExt->WmiCommData.ParityCheckEnable = (pDevExt->LineControl & 0x08)
|
|
? TRUE : FALSE;
|
|
|
|
switch (pDevExt->LineControl & SERIAL_PARITY_MASK) {
|
|
case SERIAL_NONE_PARITY:
|
|
pDevExt->WmiCommData.Parity = SERIAL_WMI_PARITY_NONE;
|
|
break;
|
|
|
|
case SERIAL_ODD_PARITY:
|
|
pDevExt->WmiCommData.Parity = SERIAL_WMI_PARITY_ODD;
|
|
break;
|
|
|
|
case SERIAL_EVEN_PARITY:
|
|
pDevExt->WmiCommData.Parity = SERIAL_WMI_PARITY_EVEN;
|
|
break;
|
|
|
|
#if defined(NEC_98)
|
|
//
|
|
// COM1 of PC-9800 series is not support MarkParity and SpaceParity.
|
|
//
|
|
#else
|
|
case SERIAL_MARK_PARITY:
|
|
pDevExt->WmiCommData.Parity = SERIAL_WMI_PARITY_MARK;
|
|
break;
|
|
|
|
case SERIAL_SPACE_PARITY:
|
|
pDevExt->WmiCommData.Parity = SERIAL_WMI_PARITY_SPACE;
|
|
break;
|
|
#endif //defined(NEC_98)
|
|
|
|
default:
|
|
ASSERTMSG(0, "SERIAL: Illegal Parity setting for WMI");
|
|
pDevExt->WmiCommData.Parity = SERIAL_WMI_PARITY_NONE;
|
|
break;
|
|
}
|
|
|
|
pDevExt->WmiCommData.StopBits = pDevExt->LineControl & SERIAL_STOP_MASK
|
|
? (pDevExt->WmiCommData.BitsPerByte == 5 ? SERIAL_WMI_STOP_1_5
|
|
: SERIAL_WMI_STOP_2) : SERIAL_WMI_STOP_1;
|
|
pDevExt->WmiCommData.XoffCharacter = pDevExt->SpecialChars.XoffChar;
|
|
pDevExt->WmiCommData.XoffXmitThreshold = pDevExt->HandFlow.XoffLimit;
|
|
pDevExt->WmiCommData.XonCharacter = pDevExt->SpecialChars.XonChar;
|
|
pDevExt->WmiCommData.XonXmitThreshold = pDevExt->HandFlow.XonLimit;
|
|
pDevExt->WmiCommData.MaximumBaudRate
|
|
= SerialReportMaxBaudRate(pDevExt->SupportedBauds);
|
|
pDevExt->WmiCommData.MaximumOutputBufferSize = (UINT32)((ULONG)-1);
|
|
pDevExt->WmiCommData.MaximumInputBufferSize = (UINT32)((ULONG)-1);
|
|
pDevExt->WmiCommData.Support16BitMode = FALSE;
|
|
pDevExt->WmiCommData.SupportDTRDSR = TRUE;
|
|
pDevExt->WmiCommData.SupportIntervalTimeouts = TRUE;
|
|
pDevExt->WmiCommData.SupportParityCheck = TRUE;
|
|
pDevExt->WmiCommData.SupportRTSCTS = TRUE;
|
|
pDevExt->WmiCommData.SupportXonXoff = TRUE;
|
|
pDevExt->WmiCommData.SettableBaudRate = TRUE;
|
|
pDevExt->WmiCommData.SettableDataBits = TRUE;
|
|
pDevExt->WmiCommData.SettableFlowControl = TRUE;
|
|
pDevExt->WmiCommData.SettableParity = TRUE;
|
|
pDevExt->WmiCommData.SettableParityCheck = TRUE;
|
|
pDevExt->WmiCommData.SettableStopBits = TRUE;
|
|
pDevExt->WmiCommData.IsBusy = FALSE;
|
|
|
|
//
|
|
// Fill in wmi perf data (all zero's)
|
|
//
|
|
|
|
RtlZeroMemory(&pDevExt->WmiPerfData, sizeof(pDevExt->WmiPerfData));
|
|
|
|
|
|
if (pDevExt->PNPState == SERIAL_PNP_ADDED) {
|
|
PULONG countSoFar = &IoGetConfigurationInformation()->SerialCount;
|
|
(*countSoFar)++;
|
|
|
|
//
|
|
// Register for WMI
|
|
//
|
|
|
|
pDevExt->WmiLibInfo.GuidCount = sizeof(SerialWmiGuidList) /
|
|
sizeof(WMIGUIDREGINFO);
|
|
pDevExt->WmiLibInfo.GuidList = SerialWmiGuidList;
|
|
ASSERT (pDevExt->WmiLibInfo.GuidCount == SERIAL_WMI_GUID_LIST_SIZE);
|
|
|
|
pDevExt->WmiLibInfo.QueryWmiRegInfo = SerialQueryWmiRegInfo;
|
|
pDevExt->WmiLibInfo.QueryWmiDataBlock = SerialQueryWmiDataBlock;
|
|
pDevExt->WmiLibInfo.SetWmiDataBlock = SerialSetWmiDataBlock;
|
|
pDevExt->WmiLibInfo.SetWmiDataItem = SerialSetWmiDataItem;
|
|
pDevExt->WmiLibInfo.ExecuteWmiMethod = NULL;
|
|
pDevExt->WmiLibInfo.WmiFunctionControl = NULL;
|
|
|
|
IoWMIRegistrationControl(PDevObj, WMIREG_ACTION_REGISTER);
|
|
|
|
}
|
|
|
|
if (pDevExt->PNPState == SERIAL_PNP_RESTARTING) {
|
|
//
|
|
// Release the stalled IRP's
|
|
//
|
|
|
|
SerialUnstallIrps(pDevExt);
|
|
}
|
|
|
|
pDevExt->PNPState = SERIAL_PNP_STARTED;
|
|
SerialClearAccept(pDevExt, ~SERIAL_PNPACCEPT_OK);
|
|
SerialSetFlags(pDevExt, SERIAL_FLAGS_STARTED);
|
|
|
|
}
|
|
|
|
if (pConfig) {
|
|
ExFreePool (pConfig);
|
|
}
|
|
|
|
if ((PUserData != NULL) && allocedUserData) {
|
|
ExFreePool(PUserData);
|
|
}
|
|
|
|
SerialDump (SERTRACECALLS, ("SERIAL: leaving SerialFinishStartDevice\n"));
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
SerialStartDevice(IN PDEVICE_OBJECT PDevObj, IN PIRP PIrp)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine first passes the start device Irp down the stack then
|
|
it picks up the resources for the device, ititializes, puts it on any
|
|
appropriate lists (i.e shared interrupt or interrupt status) and
|
|
connects the interrupt.
|
|
|
|
Arguments:
|
|
|
|
PDevObj - Pointer to the device object for this device
|
|
|
|
PIrp - Pointer to the IRP for the current request
|
|
|
|
Return Value:
|
|
|
|
Return status
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(PIrp);
|
|
NTSTATUS status = STATUS_NOT_IMPLEMENTED;
|
|
PSERIAL_DEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
|
PDEVICE_OBJECT pLowerDevObj = pDevExt->LowerDeviceObject;
|
|
|
|
PAGED_CODE();
|
|
|
|
SerialDump (SERTRACECALLS, ("SERIAL: entering SerialStartDevice\n"));
|
|
|
|
|
|
//
|
|
// Pass this down to the next device object
|
|
//
|
|
|
|
KeInitializeEvent(&pDevExt->SerialStartEvent, SynchronizationEvent,
|
|
FALSE);
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(PIrp);
|
|
IoSetCompletionRoutine(PIrp, SerialSyncCompletion,
|
|
&pDevExt->SerialStartEvent, TRUE, TRUE, TRUE);
|
|
|
|
status = IoCallDriver(pLowerDevObj, PIrp);
|
|
|
|
|
|
//
|
|
// Wait for lower drivers to be done with the Irp
|
|
//
|
|
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject (&pDevExt->SerialStartEvent, Executive, KernelMode,
|
|
FALSE, NULL);
|
|
|
|
status = PIrp->IoStatus.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
SerialDump (SERERRORS, ("SERIAL: error with IoCallDriver %x\n", status));
|
|
return status;
|
|
}
|
|
|
|
|
|
//
|
|
// Do the serial specific items to start the device
|
|
//
|
|
|
|
status = SerialFinishStartDevice(PDevObj, pIrpStack->Parameters.StartDevice
|
|
.AllocatedResources,
|
|
pIrpStack->Parameters.StartDevice
|
|
.AllocatedResourcesTranslated, NULL);
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
SerialItemCallBack(
|
|
IN PVOID Context,
|
|
IN PUNICODE_STRING PathName,
|
|
IN INTERFACE_TYPE BusType,
|
|
IN ULONG BusNumber,
|
|
IN PKEY_VALUE_FULL_INFORMATION *BusInformation,
|
|
IN CONFIGURATION_TYPE ControllerType,
|
|
IN ULONG ControllerNumber,
|
|
IN PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
|
|
IN CONFIGURATION_TYPE PeripheralType,
|
|
IN ULONG PeripheralNumber,
|
|
IN PKEY_VALUE_FULL_INFORMATION *PeripheralInformation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to check if a particular item
|
|
is present in the registry.
|
|
|
|
Arguments:
|
|
|
|
Context - Pointer to a boolean.
|
|
|
|
PathName - unicode registry path. Not Used.
|
|
|
|
BusType - Internal, Isa, ...
|
|
|
|
BusNumber - Which bus if we are on a multibus system.
|
|
|
|
BusInformation - Configuration information about the bus. Not Used.
|
|
|
|
ControllerType - Controller type.
|
|
|
|
ControllerNumber - Which controller if there is more than one
|
|
controller in the system.
|
|
|
|
ControllerInformation - Array of pointers to the three pieces of
|
|
registry information.
|
|
|
|
PeripheralType - Should be a peripheral.
|
|
|
|
PeripheralNumber - Which peripheral - not used..
|
|
|
|
PeripheralInformation - Configuration information. Not Used.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
*((BOOLEAN *)Context) = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SerialControllerCallBack(
|
|
IN PVOID Context,
|
|
IN PUNICODE_STRING PathName,
|
|
IN INTERFACE_TYPE BusType,
|
|
IN ULONG BusNumber,
|
|
IN PKEY_VALUE_FULL_INFORMATION *BusInformation,
|
|
IN CONFIGURATION_TYPE ControllerType,
|
|
IN ULONG ControllerNumber,
|
|
IN PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
|
|
IN CONFIGURATION_TYPE PeripheralType,
|
|
IN ULONG PeripheralNumber,
|
|
IN PKEY_VALUE_FULL_INFORMATION *PeripheralInformation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to check if a particular item
|
|
is present in the registry.
|
|
|
|
Arguments:
|
|
|
|
Context - Pointer to a boolean.
|
|
|
|
PathName - unicode registry path. Not Used.
|
|
|
|
BusType - Internal, Isa, ...
|
|
|
|
BusNumber - Which bus if we are on a multibus system.
|
|
|
|
BusInformation - Configuration information about the bus. Not Used.
|
|
|
|
ControllerType - Controller type.
|
|
|
|
ControllerNumber - Which controller if there is more than one
|
|
controller in the system.
|
|
|
|
ControllerInformation - Array of pointers to the three pieces of
|
|
registry information.
|
|
|
|
PeripheralType - Should be a peripheral.
|
|
|
|
PeripheralNumber - Which peripheral - not used..
|
|
|
|
PeripheralInformation - Configuration information. Not Used.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
PCM_FULL_RESOURCE_DESCRIPTOR controllerData;
|
|
PSERIAL_PTR_CTX pContext = (PSERIAL_PTR_CTX)Context;
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (ControllerInformation[IoQueryDeviceConfigurationData]->DataLength == 0) {
|
|
pContext->isPointer = FALSE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
controllerData =
|
|
(PCM_FULL_RESOURCE_DESCRIPTOR)
|
|
(((PUCHAR)ControllerInformation[IoQueryDeviceConfigurationData])
|
|
+ ControllerInformation[IoQueryDeviceConfigurationData]->DataOffset);
|
|
|
|
//
|
|
// See if this is the exact port we are testing
|
|
//
|
|
for (i = 0; i < controllerData->PartialResourceList.Count; i++) {
|
|
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR partial
|
|
= &controllerData->PartialResourceList.PartialDescriptors[i];
|
|
|
|
switch (partial->Type) {
|
|
case CmResourceTypePort:
|
|
if (partial->u.Port.Start.QuadPart == pContext->Port.QuadPart) {
|
|
//
|
|
// Pointer on same controller. Bail out.
|
|
//
|
|
pContext->isPointer = SERIAL_FOUNDPOINTER_PORT;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
case CmResourceTypeInterrupt:
|
|
if (partial->u.Interrupt.Vector == pContext->Vector) {
|
|
//
|
|
// Pointer sharing this interrupt. Bail out.
|
|
//
|
|
pContext->isPointer = SERIAL_FOUNDPOINTER_VECTOR;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
pContext->isPointer = FALSE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
SerialGetPortInfo(IN PDEVICE_OBJECT PDevObj, IN PCM_RESOURCE_LIST PResList,
|
|
IN PCM_RESOURCE_LIST PTrResList, OUT PCONFIG_DATA PConfig,
|
|
IN PSERIAL_USER_DATA PUserData)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will get the configuration information and put
|
|
it and the translated values into CONFIG_DATA structures.
|
|
It first sets up with defaults and then queries the registry
|
|
to see if the user has overridden these defaults; if this is a legacy
|
|
multiport card, it uses the info in PUserData instead of groping the
|
|
registry again.
|
|
|
|
Arguments:
|
|
|
|
PDevObj - Pointer to the device object.
|
|
|
|
PResList - Pointer to the untranslated resources requested.
|
|
|
|
PTrResList - Pointer to the translated resources requested.
|
|
|
|
PConfig - Pointer to configuration info
|
|
|
|
PUserData - Pointer to data discovered in the registry for
|
|
legacy devices.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if consistant configuration was found - otherwise.
|
|
returns STATUS_SERIAL_NO_DEVICE_INITED.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSERIAL_DEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
|
PDEVICE_OBJECT pLowerDevObj = pDevExt->LowerDeviceObject;
|
|
NTSTATUS status = STATUS_NOT_IMPLEMENTED;
|
|
CONFIGURATION_TYPE pointer = PointerPeripheral;
|
|
CONFIGURATION_TYPE controllerType = SerialController;
|
|
|
|
HANDLE keyHandle;
|
|
ULONG count;
|
|
ULONG i;
|
|
INTERFACE_TYPE interfaceType;
|
|
|
|
PCM_PARTIAL_RESOURCE_LIST pPartialResourceList, pPartialTrResourceList;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialResourceDesc, pPartialTrResourceDesc;
|
|
|
|
PCM_FULL_RESOURCE_DESCRIPTOR pFullResourceDesc = NULL,
|
|
pFullTrResourceDesc = NULL;
|
|
|
|
ULONG defaultInterruptMode;
|
|
ULONG defaultAddressSpace;
|
|
ULONG defaultInterfaceType;
|
|
ULONG defaultClockRate;
|
|
ULONG zero = 0;
|
|
SERIAL_PTR_CTX foundPointerCtx;
|
|
ULONG isMulti = 0;
|
|
ULONG gotInt = 0;
|
|
ULONG gotISR = 0;
|
|
ULONG gotIO = 0;
|
|
ULONG ioResIndex = 0;
|
|
ULONG curIoIndex = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
SerialDump(SERTRACECALLS, ("SERIAL: entering SerialGetPortInfo\n"));
|
|
|
|
SerialDump(SERPNPPOWER, ("SERIAL: resource pointer is %x\n", PResList));
|
|
SerialDump(SERPNPPOWER, ("SERIAL: TR resource pointer is %x\n", PTrResList));
|
|
|
|
|
|
if ((PResList == NULL) || (PTrResList == NULL)) {
|
|
//
|
|
// This shouldn't happen in theory
|
|
//
|
|
|
|
ASSERT(PResList != NULL);
|
|
ASSERT(PTrResList != NULL);
|
|
|
|
//
|
|
// This status is as appropriate as I can think of
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Each resource list should have only one set of resources
|
|
//
|
|
|
|
ASSERT(PResList->Count == 1);
|
|
ASSERT(PTrResList->Count == 1);
|
|
|
|
//
|
|
// See if this is a multiport device. This way we allow other
|
|
// pseudo-serial devices with extra resources to specify another range
|
|
// of I/O ports. If this is not a multiport, we only look at the first
|
|
// range. If it is a multiport, we look at the first two ranges.
|
|
//
|
|
|
|
status = IoOpenDeviceRegistryKey(pDevExt->Pdo, PLUGPLAY_REGKEY_DEVICE,
|
|
STANDARD_RIGHTS_WRITE, &keyHandle);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
status = SerialGetRegistryKeyValue(keyHandle, L"MultiportDevice",
|
|
sizeof(L"MultiportDevice"), &isMulti,
|
|
sizeof (ULONG));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
isMulti = 0;
|
|
}
|
|
|
|
status = SerialGetRegistryKeyValue(keyHandle, L"SerialIoResourcesIndex",
|
|
sizeof(L"SerialIoResourcesIndex"),
|
|
&ioResIndex, sizeof(ULONG));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ioResIndex = 0;
|
|
}
|
|
|
|
ZwClose(keyHandle);
|
|
|
|
|
|
pFullResourceDesc = &PResList->List[0];
|
|
pFullTrResourceDesc = &PTrResList->List[0];
|
|
|
|
//
|
|
// Ok, if we have a full resource descriptor. Let's take it apart.
|
|
//
|
|
|
|
if (pFullResourceDesc) {
|
|
pPartialResourceList = &pFullResourceDesc->PartialResourceList;
|
|
pPartialResourceDesc = pPartialResourceList->PartialDescriptors;
|
|
count = pPartialResourceList->Count;
|
|
|
|
//
|
|
// Pull out the stuff that is in the full descriptor.
|
|
//
|
|
|
|
PConfig->InterfaceType = pFullResourceDesc->InterfaceType;
|
|
PConfig->BusNumber = pFullResourceDesc->BusNumber;
|
|
|
|
//
|
|
// Now run through the partial resource descriptors looking for the port,
|
|
// interrupt, and clock rate.
|
|
//
|
|
|
|
PConfig->ClockRate = 1843200;
|
|
PConfig->InterruptStatus = SerialPhysicalZero;
|
|
PConfig->SpanOfInterruptStatus = SERIAL_STATUS_LENGTH;
|
|
|
|
for (i = 0; i < count; i++, pPartialResourceDesc++) {
|
|
|
|
switch (pPartialResourceDesc->Type) {
|
|
case CmResourceTypePort: {
|
|
|
|
#if defined(NEC_98)
|
|
//
|
|
// NEC98 can't decide by length.
|
|
//
|
|
if (gotIO == 0) { // This is the serial register set
|
|
gotIO = 1;
|
|
PConfig->Controller = pPartialResourceDesc->u.Port.Start;
|
|
PConfig->SpanOfController = SERIAL_REGISTER_SPAN;
|
|
PConfig->AddressSpace = pPartialResourceDesc->Flags;
|
|
}
|
|
#else
|
|
if (pPartialResourceDesc->u.Port.Length
|
|
== SERIAL_STATUS_LENGTH && (gotISR == 0)) { // This is an ISR
|
|
if (isMulti) {
|
|
gotISR = 1;
|
|
PConfig->InterruptStatus
|
|
= pPartialResourceDesc->u.Port.Start;
|
|
PConfig->SpanOfInterruptStatus
|
|
= pPartialResourceDesc->u.Port.Length;
|
|
PConfig->AddressSpace = pPartialResourceDesc->Flags;
|
|
}
|
|
} else {
|
|
if (gotIO == 0) { // This is the serial register set
|
|
if (curIoIndex == ioResIndex) {
|
|
gotIO = 1;
|
|
PConfig->Controller
|
|
= pPartialResourceDesc->u.Port.Start;
|
|
PConfig->SpanOfController = SERIAL_REGISTER_SPAN;
|
|
PConfig->AddressSpace = pPartialResourceDesc->Flags;
|
|
} else {
|
|
curIoIndex++;
|
|
}
|
|
}
|
|
}
|
|
#endif //defined(NEC_98)
|
|
break;
|
|
}
|
|
|
|
case CmResourceTypeInterrupt: {
|
|
if (gotInt == 0) {
|
|
gotInt = 1;
|
|
PConfig->OriginalIrql = pPartialResourceDesc->u.Interrupt.Level;
|
|
PConfig->OriginalVector
|
|
= pPartialResourceDesc->u.Interrupt.Vector;
|
|
PConfig->Affinity = pPartialResourceDesc->u.Interrupt.Affinity;
|
|
|
|
if (pPartialResourceDesc->Flags
|
|
& CM_RESOURCE_INTERRUPT_LATCHED) {
|
|
PConfig->InterruptMode = Latched;
|
|
} else {
|
|
PConfig->InterruptMode = LevelSensitive;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CmResourceTypeDeviceSpecific: {
|
|
PCM_SERIAL_DEVICE_DATA sDeviceData;
|
|
|
|
sDeviceData = (PCM_SERIAL_DEVICE_DATA)(pPartialResourceDesc + 1);
|
|
PConfig->ClockRate = sDeviceData->BaudClock;
|
|
break;
|
|
}
|
|
|
|
|
|
default: {
|
|
break;
|
|
}
|
|
} // switch (pPartialResourceDesc->Type)
|
|
} // for (i = 0; i < count; i++, pPartialResourceDesc++)
|
|
} // if (pFullResourceDesc)
|
|
|
|
|
|
//
|
|
// Do the same for the translated resources
|
|
//
|
|
|
|
gotInt = 0;
|
|
gotISR = 0;
|
|
gotIO = 0;
|
|
curIoIndex = 0;
|
|
|
|
if (pFullTrResourceDesc) {
|
|
pPartialTrResourceList = &pFullTrResourceDesc->PartialResourceList;
|
|
pPartialTrResourceDesc = pPartialTrResourceList->PartialDescriptors;
|
|
count = pPartialTrResourceList->Count;
|
|
|
|
//
|
|
// Reload PConfig with the translated values for later use
|
|
//
|
|
|
|
PConfig->InterfaceType = pFullTrResourceDesc->InterfaceType;
|
|
PConfig->BusNumber = pFullTrResourceDesc->BusNumber;
|
|
|
|
PConfig->TrInterruptStatus = SerialPhysicalZero;
|
|
|
|
for (i = 0; i < count; i++, pPartialTrResourceDesc++) {
|
|
|
|
switch (pPartialTrResourceDesc->Type) {
|
|
case CmResourceTypePort: {
|
|
#if defined(NEC_98)
|
|
//
|
|
// NEC98 can't decide by length.
|
|
//
|
|
if (gotIO == 0) { // This is the serial register set
|
|
gotIO = 1;
|
|
PConfig->TrController = pPartialTrResourceDesc->u.Port.Start;
|
|
PConfig->AddressSpace = pPartialTrResourceDesc->Flags;
|
|
}
|
|
#else
|
|
if (pPartialTrResourceDesc->u.Port.Length
|
|
== SERIAL_STATUS_LENGTH && (gotISR == 0)) { // This is an ISR
|
|
if (isMulti) {
|
|
gotISR = 1;
|
|
PConfig->TrInterruptStatus = pPartialTrResourceDesc
|
|
->u.Port.Start;
|
|
}
|
|
} else { // This is the serial register set
|
|
if (gotIO == 0) {
|
|
if (curIoIndex == ioResIndex) {
|
|
gotIO = 1;
|
|
PConfig->TrController
|
|
= pPartialTrResourceDesc->u.Port.Start;
|
|
PConfig->AddressSpace
|
|
= pPartialTrResourceDesc->Flags;
|
|
} else {
|
|
curIoIndex++;
|
|
}
|
|
}
|
|
}
|
|
#endif //defined(NEC_98)
|
|
break;
|
|
}
|
|
|
|
case CmResourceTypeInterrupt: {
|
|
if (gotInt == 0) {
|
|
gotInt = 1;
|
|
PConfig->TrVector = pPartialTrResourceDesc->u.Interrupt.Vector;
|
|
PConfig->TrIrql = pPartialTrResourceDesc->u.Interrupt.Level;
|
|
PConfig->Affinity = pPartialTrResourceDesc->u.Interrupt.Affinity;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
break;
|
|
}
|
|
} // switch (pPartialTrResourceDesc->Type)
|
|
} // for (i = 0; i < count; i++, pPartialTrResourceDesc++)
|
|
} // if (pFullTrResourceDesc)
|
|
|
|
|
|
//
|
|
// Initialize a config data structure with default values for those that
|
|
// may not already be initialized.
|
|
//
|
|
|
|
PConfig->PortIndex = 0;
|
|
PConfig->DisablePort = 0;
|
|
PConfig->PermitSystemWideShare = FALSE;
|
|
PConfig->MaskInverted = 0;
|
|
PConfig->Indexed = 0;
|
|
PConfig->ForceFifoEnable = driverDefaults.ForceFifoEnableDefault;
|
|
PConfig->RxFIFO = driverDefaults.RxFIFODefault;
|
|
PConfig->TxFIFO = driverDefaults.TxFIFODefault;
|
|
PConfig->PermitShare = driverDefaults.PermitShareDefault;
|
|
PConfig->LogFifo = driverDefaults.LogFifoDefault;
|
|
PConfig->Jensen = driverDefaults.JensenDetected;
|
|
|
|
//
|
|
// Query the registry to look for the first bus on
|
|
// the system (that isn't the internal bus - we assume
|
|
// that the firmware code knows about those ports). We
|
|
// will use that as the default bus if no bustype or bus
|
|
// number is specified in the "user" configuration records.
|
|
//
|
|
|
|
defaultInterfaceType = (ULONG)Isa;
|
|
defaultClockRate = 1843200;
|
|
|
|
|
|
for (interfaceType = 0;
|
|
interfaceType < MaximumInterfaceType;
|
|
interfaceType++
|
|
) {
|
|
|
|
ULONG busZero = 0;
|
|
BOOLEAN foundOne = FALSE;
|
|
|
|
|
|
if (interfaceType != Internal) {
|
|
|
|
IoQueryDeviceDescription(
|
|
&interfaceType,
|
|
&busZero,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
SerialItemCallBack,
|
|
&foundOne
|
|
);
|
|
|
|
if (foundOne) {
|
|
defaultInterfaceType = (ULONG)interfaceType;
|
|
|
|
if (defaultInterfaceType == MicroChannel) {
|
|
|
|
defaultInterruptMode = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
|
|
}
|
|
break;
|
|
}
|
|
} // if (interfaceType != Internal)
|
|
} // for (interfaceType = 0
|
|
|
|
|
|
//
|
|
// Get any user data associated with the port now and override the
|
|
// values passed in if applicable. If this a legacy device, this
|
|
// is where we may actually get the parameters.
|
|
//
|
|
|
|
//
|
|
// Open the "Device Parameters" section of registry for this device object.
|
|
// If PUserData is NULL, this is PnP enumerated and we need to check,
|
|
// otherwise we are doing a legacy device and have the info already.
|
|
//
|
|
|
|
|
|
if (PUserData == NULL) {
|
|
status = IoOpenDeviceRegistryKey (pDevExt->Pdo,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
STANDARD_RIGHTS_READ,
|
|
&keyHandle);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
SerialDump(SERERRORS, ("SERIAL: IoOpenDeviceRegistryKey failed - %x "
|
|
"\n", status));
|
|
goto PortInfoCleanUp;
|
|
|
|
} else {
|
|
status = SerialGetRegistryKeyValue (keyHandle,
|
|
L"DisablePort",
|
|
sizeof(L"DisablePort"),
|
|
&PConfig->DisablePort,
|
|
sizeof (ULONG));
|
|
|
|
status = SerialGetRegistryKeyValue (keyHandle,
|
|
L"ForceFifoEnable",
|
|
sizeof(L"ForceFifoEnable"),
|
|
&PConfig->ForceFifoEnable,
|
|
sizeof (ULONG));
|
|
|
|
status = SerialGetRegistryKeyValue (keyHandle,
|
|
L"RxFIFO",
|
|
sizeof(L"RxFIFO"),
|
|
&PConfig->RxFIFO,
|
|
sizeof (ULONG));
|
|
|
|
status = SerialGetRegistryKeyValue (keyHandle,
|
|
L"TxFIFO",
|
|
sizeof(L"TxFIFO"),
|
|
&PConfig->TxFIFO,
|
|
sizeof (ULONG));
|
|
|
|
status = SerialGetRegistryKeyValue (keyHandle,
|
|
L"MaskInverted",
|
|
sizeof(L"MaskInverted"),
|
|
&PConfig->MaskInverted,
|
|
sizeof (ULONG));
|
|
|
|
status = SerialGetRegistryKeyValue (keyHandle,
|
|
L"Share System Interrupt",
|
|
sizeof(L"Share System Interrupt"),
|
|
&PConfig->PermitShare,
|
|
sizeof (ULONG));
|
|
|
|
status = SerialGetRegistryKeyValue (keyHandle,
|
|
L"PortIndex",
|
|
sizeof(L"PortIndex"),
|
|
&PConfig->PortIndex,
|
|
sizeof (ULONG));
|
|
|
|
status = SerialGetRegistryKeyValue(keyHandle, L"Indexed",
|
|
sizeof(L"Indexed"),
|
|
&PConfig->Indexed,
|
|
sizeof(ULONG));
|
|
|
|
status = SerialGetRegistryKeyValue (keyHandle,
|
|
L"ClockRate",
|
|
sizeof(L"ClockRate"),
|
|
&PConfig->ClockRate,
|
|
sizeof (ULONG));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
PConfig->ClockRate = defaultClockRate;
|
|
}
|
|
|
|
ZwClose (keyHandle);
|
|
}
|
|
} else {
|
|
//
|
|
// This was a legacy device, either use a driver default or copy over
|
|
// the user-specified values.
|
|
//
|
|
ULONG badValue = (ULONG)-1;
|
|
|
|
PConfig->DisablePort = (PUserData->DisablePort == badValue)
|
|
? 0
|
|
: PUserData->DisablePort;
|
|
PConfig->ForceFifoEnable = (PUserData->ForceFIFOEnable == badValue)
|
|
? PUserData->ForceFIFOEnableDefault
|
|
: PUserData->ForceFIFOEnable;
|
|
PConfig->RxFIFO = (PUserData->RxFIFO == badValue)
|
|
? PUserData->RxFIFODefault
|
|
: PUserData->RxFIFO;
|
|
PConfig->Indexed = (PUserData->UserIndexed == badValue)
|
|
? 0
|
|
: PUserData->UserIndexed;
|
|
PConfig->TxFIFO = (PUserData->TxFIFO == badValue)
|
|
? PUserData->TxFIFODefault
|
|
: PUserData->TxFIFO;
|
|
PConfig->MaskInverted = (PUserData->MaskInverted == badValue)
|
|
? 0
|
|
: PUserData->MaskInverted;
|
|
PConfig->ClockRate = (PUserData->UserClockRate == badValue)
|
|
? defaultClockRate
|
|
: PUserData->UserClockRate;
|
|
PConfig->PermitShare = PUserData->PermitShareDefault;
|
|
PConfig->PortIndex = PUserData->UserPortIndex;
|
|
}
|
|
|
|
//
|
|
// Do some error checking on the configuration info we have.
|
|
//
|
|
// Make sure that the interrupt is non zero (which we defaulted
|
|
// it to).
|
|
//
|
|
// Make sure that the portaddress is non zero (which we defaulted
|
|
// it to).
|
|
//
|
|
// Make sure that the DosDevices is not NULL (which we defaulted
|
|
// it to).
|
|
//
|
|
// We need to make sure that if an interrupt status
|
|
// was specified, that a port index was also specfied,
|
|
// and if so that the port index is <= maximum ports
|
|
// on a board.
|
|
//
|
|
// We should also validate that the bus type and number
|
|
// are correct.
|
|
//
|
|
// We will also validate that the interrupt mode makes
|
|
// sense for the bus.
|
|
//
|
|
|
|
if (!PConfig->Controller.LowPart) {
|
|
|
|
//
|
|
// Ehhhh! Lose Game.
|
|
//
|
|
|
|
SerialLogError(
|
|
PDevObj->DriverObject,
|
|
NULL,
|
|
PConfig->Controller,
|
|
SerialPhysicalZero,
|
|
0,
|
|
0,
|
|
0,
|
|
58,
|
|
STATUS_SUCCESS,
|
|
SERIAL_INVALID_USER_CONFIG,
|
|
0,
|
|
NULL,
|
|
sizeof(L"PortAddress") + sizeof(WCHAR),
|
|
L"PortAddress"
|
|
);
|
|
|
|
SerialDump (SERERRORS,
|
|
("SERIAL: Bogus port address %x\n",
|
|
PConfig->Controller.LowPart));
|
|
|
|
status = SERIAL_INVALID_USER_CONFIG;
|
|
goto PortInfoCleanUp;
|
|
}
|
|
|
|
|
|
if (!PConfig->OriginalVector) {
|
|
|
|
//
|
|
// Ehhhh! Lose Game.
|
|
//
|
|
|
|
SerialLogError(
|
|
pDevExt->DriverObject,
|
|
NULL,
|
|
PConfig->Controller,
|
|
SerialPhysicalZero,
|
|
0,
|
|
0,
|
|
0,
|
|
59,
|
|
STATUS_SUCCESS,
|
|
SERIAL_INVALID_USER_CONFIG,
|
|
pDevExt->DeviceName.Length,
|
|
pDevExt->DeviceName.Buffer,
|
|
sizeof (L"Interrupt"),
|
|
L"Interrupt"
|
|
);
|
|
|
|
SerialDump (SERERRORS,("SERIAL: Bogus vector %x\n",
|
|
PConfig->OriginalVector));
|
|
|
|
status = SERIAL_INVALID_USER_CONFIG;
|
|
goto PortInfoCleanUp;
|
|
}
|
|
|
|
|
|
if (PConfig->InterruptStatus.LowPart != 0) {
|
|
|
|
if (PConfig->PortIndex == MAXULONG) {
|
|
|
|
//
|
|
// Ehhhh! Lose Game.
|
|
//
|
|
|
|
SerialLogError(
|
|
pDevExt->DriverObject,
|
|
NULL,
|
|
PConfig->Controller,
|
|
SerialPhysicalZero,
|
|
0,
|
|
0,
|
|
0,
|
|
30,
|
|
STATUS_SUCCESS,
|
|
SERIAL_INVALID_PORT_INDEX,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
SerialDump (SERERRORS,
|
|
("SERIAL: Bogus port index %x\n", PConfig->PortIndex));
|
|
|
|
status = SERIAL_INVALID_PORT_INDEX;
|
|
goto PortInfoCleanUp;
|
|
|
|
} else if (!PConfig->PortIndex) {
|
|
|
|
//
|
|
// So sorry, you must have a non-zero port index.
|
|
//
|
|
|
|
SerialLogError(
|
|
pDevExt->DriverObject,
|
|
NULL,
|
|
PConfig->Controller,
|
|
SerialPhysicalZero,
|
|
0,
|
|
0,
|
|
0,
|
|
31,
|
|
STATUS_SUCCESS,
|
|
SERIAL_INVALID_PORT_INDEX,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
SerialDump(
|
|
SERERRORS,
|
|
("SERIAL: Port index must be > 0 for any\n"
|
|
"------- port on a multiport card: %x\n",
|
|
PConfig->PortIndex)
|
|
);
|
|
|
|
status = SERIAL_INVALID_PORT_INDEX;
|
|
goto PortInfoCleanUp;
|
|
|
|
} else {
|
|
|
|
if (PConfig->Indexed) {
|
|
|
|
if (PConfig->PortIndex > SERIAL_MAX_PORTS_INDEXED) {
|
|
|
|
SerialLogError(
|
|
pDevExt->DriverObject,
|
|
NULL,
|
|
PConfig->Controller,
|
|
SerialPhysicalZero,
|
|
0,
|
|
0,
|
|
0,
|
|
32,
|
|
STATUS_SUCCESS,
|
|
SERIAL_PORT_INDEX_TOO_HIGH,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
SerialDump (SERERRORS,
|
|
("SERIAL: port index to large %x\n",
|
|
PConfig->PortIndex));
|
|
|
|
status = SERIAL_PORT_INDEX_TOO_HIGH;
|
|
goto PortInfoCleanUp;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (PConfig->PortIndex > SERIAL_MAX_PORTS_NONINDEXED) {
|
|
|
|
SerialLogError(
|
|
pDevExt->DriverObject,
|
|
NULL,
|
|
PConfig->Controller,
|
|
SerialPhysicalZero,
|
|
0,
|
|
0,
|
|
0,
|
|
33,
|
|
STATUS_SUCCESS,
|
|
SERIAL_PORT_INDEX_TOO_HIGH,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
SerialDump (SERERRORS,
|
|
("SERIAL: port index to large %x\n",
|
|
PConfig->PortIndex));
|
|
|
|
status = SERIAL_PORT_INDEX_TOO_HIGH;
|
|
goto PortInfoCleanUp;
|
|
}
|
|
|
|
}
|
|
|
|
} // else (if !PConfig->PortIndex)
|
|
|
|
} // if (PConfig->InterruptStatus != 0)
|
|
|
|
|
|
//
|
|
// We don't want to cause the hal to have a bad day,
|
|
// so let's check the interface type and bus number.
|
|
//
|
|
// We only need to check the registry if they aren't
|
|
// equal to the defaults.
|
|
//
|
|
|
|
if (PConfig->BusNumber != 0) {
|
|
|
|
BOOLEAN foundIt;
|
|
|
|
if (PConfig->InterfaceType >= MaximumInterfaceType) {
|
|
|
|
//
|
|
// Ehhhh! Lose Game.
|
|
//
|
|
|
|
SerialLogError(
|
|
pDevExt->DriverObject,
|
|
NULL,
|
|
PConfig->Controller,
|
|
SerialPhysicalZero,
|
|
0,
|
|
0,
|
|
0,
|
|
34,
|
|
STATUS_SUCCESS,
|
|
SERIAL_UNKNOWN_BUS,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
SerialDump (SERERRORS,
|
|
("SERIAL: Invalid Bus type %x\n", PConfig->BusNumber));
|
|
|
|
status = SERIAL_UNKNOWN_BUS;
|
|
goto PortInfoCleanUp;
|
|
}
|
|
|
|
IoQueryDeviceDescription(
|
|
(INTERFACE_TYPE *)&PConfig->InterfaceType,
|
|
&zero,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
SerialItemCallBack,
|
|
&foundIt
|
|
);
|
|
|
|
if (!foundIt) {
|
|
|
|
SerialLogError(
|
|
pDevExt->DriverObject,
|
|
NULL,
|
|
PConfig->Controller,
|
|
SerialPhysicalZero,
|
|
0,
|
|
0,
|
|
0,
|
|
35,
|
|
STATUS_SUCCESS,
|
|
SERIAL_BUS_NOT_PRESENT,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
);
|
|
SerialDump(
|
|
SERERRORS,
|
|
("SERIAL: There aren't that many of those\n"
|
|
"------- busses on this system,%x\n",
|
|
PConfig->BusNumber)
|
|
);
|
|
|
|
status = SERIAL_BUS_NOT_PRESENT;
|
|
goto PortInfoCleanUp;
|
|
|
|
}
|
|
|
|
} // if (PConfig->BusNumber != 0)
|
|
|
|
|
|
if ((PConfig->InterfaceType == MicroChannel) &&
|
|
(PConfig->InterruptMode == CM_RESOURCE_INTERRUPT_LATCHED)) {
|
|
|
|
SerialLogError(
|
|
pDevExt->DriverObject,
|
|
NULL,
|
|
PConfig->Controller,
|
|
SerialPhysicalZero,
|
|
0,
|
|
0,
|
|
0,
|
|
36,
|
|
STATUS_SUCCESS,
|
|
SERIAL_BUS_INTERRUPT_CONFLICT,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
);
|
|
SerialDump(
|
|
SERERRORS,
|
|
("SERIAL: Latched interrupts and MicroChannel\n"
|
|
"------- busses don't mix\n")
|
|
);
|
|
|
|
status = SERIAL_BUS_INTERRUPT_CONFLICT;
|
|
goto PortInfoCleanUp;
|
|
}
|
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Dump out the port configuration.
|
|
//
|
|
|
|
SerialDump(SERDIAG1, ("SERIAL: Com Port address: %x\n",
|
|
PConfig->Controller.LowPart));
|
|
|
|
SerialDump(SERDIAG1, ("SERIAL: Com Interrupt Status: %x\n",
|
|
PConfig->InterruptStatus));
|
|
|
|
SerialDump(SERDIAG1, ("SERIAL: Com Port Index: %x\n",
|
|
PConfig->PortIndex));
|
|
|
|
SerialDump(SERDIAG1, ("SERIAL: Com Port ClockRate: %x\n",
|
|
PConfig->ClockRate));
|
|
|
|
SerialDump(SERDIAG1, ("SERIAL: Com Port BusNumber: %x\n",
|
|
PConfig->BusNumber));
|
|
|
|
SerialDump(SERDIAG1, ("SERIAL: Com AddressSpace: %x\n",
|
|
PConfig->AddressSpace));
|
|
|
|
SerialDump(SERDIAG1, ("SERIAL: Com InterruptMode: %x\n",
|
|
PConfig->InterruptMode));
|
|
|
|
SerialDump(SERDIAG1, ("SERIAL: Com InterfaceType: %x\n",
|
|
PConfig->InterfaceType));
|
|
|
|
SerialDump(SERDIAG1, ("SERIAL: Com OriginalVector: %x\n",
|
|
PConfig->OriginalVector));
|
|
|
|
SerialDump(SERDIAG1, ("SERIAL: Com OriginalIrql: %x\n",
|
|
PConfig->OriginalIrql));
|
|
|
|
SerialDump(SERDIAG1, ("SERIAL: Com Indexed: %x\n",
|
|
PConfig->Indexed));
|
|
|
|
PortInfoCleanUp:;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
SerialDoExternalNaming(IN PSERIAL_DEVICE_EXTENSION PDevExt,
|
|
IN PDRIVER_OBJECT PDrvObj)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will be used to create a symbolic link
|
|
to the driver name in the given object directory.
|
|
|
|
It will also create an entry in the device map for
|
|
this device - IF we could create the symbolic link.
|
|
|
|
Arguments:
|
|
|
|
Extension - Pointer to the device extension.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
HANDLE keyHandle;
|
|
WCHAR *pRegName = NULL;
|
|
UNICODE_STRING linkName;
|
|
PDEVICE_OBJECT pLowerDevObj, pDevObj;
|
|
ULONG bufLen;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
pDevObj = PDevExt->DeviceObject;
|
|
pLowerDevObj = PDevExt->LowerDeviceObject;
|
|
|
|
status = IoOpenDeviceRegistryKey(PDevExt->Pdo, PLUGPLAY_REGKEY_DEVICE,
|
|
STANDARD_RIGHTS_READ, &keyHandle);
|
|
|
|
//
|
|
// Check to see if we are allowed to do external naming; if not,
|
|
// then we just return success
|
|
//
|
|
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
|
|
SerialGetRegistryKeyValue(keyHandle, L"SerialSkipExternalNaming",
|
|
sizeof(L"SerialSkipExternalNaming"),
|
|
&PDevExt->SkipNaming, sizeof(ULONG));
|
|
|
|
if (PDevExt->SkipNaming) {
|
|
ZwClose(keyHandle);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
RtlZeroMemory(&linkName, sizeof(UNICODE_STRING));
|
|
|
|
linkName.MaximumLength = SYMBOLIC_NAME_LENGTH*sizeof(WCHAR);
|
|
linkName.Buffer = ExAllocatePool(PagedPool, linkName.MaximumLength
|
|
+ sizeof(WCHAR));
|
|
|
|
if (linkName.Buffer == NULL) {
|
|
SerialLogError(PDrvObj, pDevObj, SerialPhysicalZero, SerialPhysicalZero,
|
|
0, 0, 0, 19, STATUS_SUCCESS, SERIAL_INSUFFICIENT_RESOURCES,
|
|
0, NULL, 0, NULL);
|
|
SerialDump(SERERRORS, ("SERIAL: Couldn't allocate memory for device name"
|
|
"\n"));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
ZwClose(keyHandle);
|
|
goto SerialDoExternalNamingError;
|
|
|
|
}
|
|
|
|
RtlZeroMemory(linkName.Buffer, linkName.MaximumLength + sizeof(WCHAR));
|
|
|
|
|
|
pRegName = ExAllocatePool(PagedPool, SYMBOLIC_NAME_LENGTH * sizeof(WCHAR)
|
|
+ sizeof(WCHAR));
|
|
|
|
if (pRegName == NULL) {
|
|
SerialLogError(PDrvObj, pDevObj, SerialPhysicalZero, SerialPhysicalZero,
|
|
0, 0, 0, 19, STATUS_SUCCESS, SERIAL_INSUFFICIENT_RESOURCES,
|
|
0, NULL, 0, NULL);
|
|
SerialDump(SERERRORS, ("SERIAL: Couldn't allocate memory for buffer"
|
|
"\n"));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
ZwClose(keyHandle);
|
|
goto SerialDoExternalNamingError;
|
|
|
|
}
|
|
|
|
//
|
|
// Fetch PortName which contains the suggested REG_SZ symbolic name.
|
|
//
|
|
|
|
status = SerialGetRegistryKeyValue(keyHandle, L"PortName",
|
|
sizeof(L"PortName"), pRegName,
|
|
SYMBOLIC_NAME_LENGTH * sizeof(WCHAR));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// This is for PCMCIA which currently puts the name under Identifier.
|
|
//
|
|
|
|
status = SerialGetRegistryKeyValue(keyHandle, L"Identifier",
|
|
sizeof(L"Identifier"),
|
|
pRegName, SYMBOLIC_NAME_LENGTH
|
|
* sizeof(WCHAR));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Hmm. Either we have to pick a name or bail...
|
|
//
|
|
// ...we will bail.
|
|
//
|
|
|
|
SerialDump(SERERRORS, ("SERIAL: Getting PortName/Identifier failed - "
|
|
"%x\n", status));
|
|
ZwClose (keyHandle);
|
|
goto SerialDoExternalNamingError;
|
|
}
|
|
|
|
}
|
|
|
|
ZwClose (keyHandle);
|
|
|
|
bufLen = wcslen(pRegName) * sizeof(WCHAR) + sizeof(UNICODE_NULL);
|
|
|
|
PDevExt->WmiIdentifier.Buffer = ExAllocatePool(PagedPool, bufLen);
|
|
|
|
if (PDevExt->WmiIdentifier.Buffer == NULL) {
|
|
SerialLogError(PDrvObj, pDevObj, SerialPhysicalZero, SerialPhysicalZero,
|
|
0, 0, 0, 19, STATUS_SUCCESS, SERIAL_INSUFFICIENT_RESOURCES,
|
|
0, NULL, 0, NULL);
|
|
SerialDump(SERERRORS,
|
|
("SERIAL: Couldn't allocate memory for WMI name\n"));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SerialDoExternalNamingError;
|
|
}
|
|
|
|
RtlZeroMemory(PDevExt->WmiIdentifier.Buffer, bufLen);
|
|
|
|
PDevExt->WmiIdentifier.Length = 0;
|
|
PDevExt->WmiIdentifier.MaximumLength = (USHORT)bufLen - sizeof(WCHAR);
|
|
RtlAppendUnicodeToString(&PDevExt->WmiIdentifier, pRegName);
|
|
|
|
|
|
//
|
|
// Create the "\\DosDevices\\<symbolicName>" string
|
|
//
|
|
RtlAppendUnicodeToString(&linkName, L"\\");
|
|
RtlAppendUnicodeToString(&linkName, DEFAULT_DIRECTORY);
|
|
RtlAppendUnicodeToString(&linkName, L"\\");
|
|
RtlAppendUnicodeToString(&linkName, pRegName);
|
|
|
|
//
|
|
// Allocate Pool and save the symbolic link name in the device extension.
|
|
//
|
|
PDevExt->SymbolicLinkName.MaximumLength = linkName.Length + sizeof(WCHAR);
|
|
PDevExt->SymbolicLinkName.Buffer
|
|
= ExAllocatePool(PagedPool, PDevExt->SymbolicLinkName.MaximumLength);
|
|
|
|
if (!PDevExt->SymbolicLinkName.Buffer) {
|
|
|
|
SerialLogError(PDrvObj, pDevObj, SerialPhysicalZero, SerialPhysicalZero,
|
|
0, 0, 0, 19, STATUS_SUCCESS, SERIAL_INSUFFICIENT_RESOURCES,
|
|
0, NULL, 0, NULL);
|
|
SerialDump(SERERRORS,
|
|
("SERIAL: Couldn't allocate memory for symbolic link name\n"));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SerialDoExternalNamingError;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Zero fill it.
|
|
//
|
|
|
|
RtlZeroMemory(PDevExt->SymbolicLinkName.Buffer,
|
|
PDevExt->SymbolicLinkName.MaximumLength);
|
|
|
|
RtlAppendUnicodeStringToString(&PDevExt->SymbolicLinkName,
|
|
&linkName);
|
|
|
|
PDevExt->DosName.Buffer = ExAllocatePool(PagedPool, 64 + sizeof(WCHAR));
|
|
|
|
if (!PDevExt->DosName.Buffer) {
|
|
|
|
SerialLogError(PDrvObj, pDevObj, SerialPhysicalZero, SerialPhysicalZero,
|
|
0, 0, 0, 19, STATUS_SUCCESS, SERIAL_INSUFFICIENT_RESOURCES,
|
|
0, NULL, 0, NULL);
|
|
SerialDump(SERERRORS,
|
|
("SERIAL: Couldn't allocate memory for Dos name\n"));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SerialDoExternalNamingError;
|
|
}
|
|
|
|
|
|
PDevExt->DosName.MaximumLength = 64 + sizeof(WCHAR);
|
|
|
|
//
|
|
// Zero fill it.
|
|
//
|
|
|
|
PDevExt->DosName.Length = 0;
|
|
|
|
RtlZeroMemory(PDevExt->DosName.Buffer,
|
|
PDevExt->DosName.MaximumLength);
|
|
|
|
RtlAppendUnicodeToString(&PDevExt->DosName, pRegName);
|
|
RtlZeroMemory(((PUCHAR)(&PDevExt->DosName.Buffer[0]))
|
|
+ PDevExt->DosName.Length, sizeof(WCHAR));
|
|
|
|
SerialDump(SERDIAG1, ("SERIAL: DosName is %wZ\n",
|
|
&PDevExt->DosName));
|
|
|
|
//
|
|
|
|
|
|
status = IoCreateSymbolicLink (&PDevExt->SymbolicLinkName,
|
|
&PDevExt->DeviceName);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Oh well, couldn't create the symbolic link. No point
|
|
// in trying to create the device map entry.
|
|
//
|
|
|
|
SerialLogError(PDrvObj, pDevObj, SerialPhysicalZero, SerialPhysicalZero,
|
|
0, 0, 0, 52, status, SERIAL_NO_SYMLINK_CREATED,
|
|
PDevExt->DeviceName.Length + sizeof(WCHAR),
|
|
PDevExt->DeviceName.Buffer, 0, NULL);
|
|
|
|
SerialDump(
|
|
SERERRORS,
|
|
("SERIAL: Couldn't create the symbolic link\n"
|
|
"------- for port %wZ\n",
|
|
&PDevExt->DeviceName)
|
|
);
|
|
|
|
goto SerialDoExternalNamingError;
|
|
|
|
}
|
|
|
|
PDevExt->CreatedSymbolicLink = TRUE;
|
|
|
|
status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP, L"SERIALCOMM",
|
|
PDevExt->DeviceName.Buffer, REG_SZ,
|
|
PDevExt->DosName.Buffer,
|
|
PDevExt->DosName.Length + sizeof(WCHAR));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
SerialLogError(PDrvObj, pDevObj, SerialPhysicalZero, SerialPhysicalZero,
|
|
0, 0, 0, 53, status, SERIAL_NO_DEVICE_MAP_CREATED,
|
|
PDevExt->DeviceName.Length + sizeof(WCHAR),
|
|
PDevExt->DeviceName.Buffer, 0, NULL);
|
|
|
|
SerialDump(SERERRORS, ("SERIAL: Couldn't create the device map entry\n"
|
|
"------- for port %wZ\n", &PDevExt->DeviceName));
|
|
|
|
goto SerialDoExternalNamingError;
|
|
}
|
|
|
|
PDevExt->CreatedSerialCommEntry = TRUE;
|
|
|
|
//
|
|
// Make the device visible via a device association as well.
|
|
// The reference string is the eight digit device index
|
|
//
|
|
|
|
status = IoRegisterDeviceInterface(PDevExt->Pdo, (LPGUID)&GUID_CLASS_COMPORT,
|
|
NULL, &PDevExt->DeviceClassSymbolicName);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
SerialDump(SERERRORS, ("SERIAL: Couldn't register class association\n"
|
|
"------- for port %wZ\n",
|
|
&PDevExt->DeviceName));
|
|
|
|
PDevExt->DeviceClassSymbolicName.Buffer = NULL;
|
|
goto SerialDoExternalNamingError;
|
|
}
|
|
|
|
|
|
//
|
|
// Now set the symbolic link for the association
|
|
//
|
|
|
|
status = IoSetDeviceInterfaceState(&PDevExt->DeviceClassSymbolicName,
|
|
TRUE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
SerialDump(SERERRORS, ("SERIAL: Couldn't set class association\n"
|
|
"------- for port %wZ\n",
|
|
&PDevExt->DeviceName));
|
|
}
|
|
|
|
SerialDoExternalNamingError:;
|
|
|
|
//
|
|
// Clean up error conditions
|
|
//
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
if (PDevExt->DosName.Buffer != NULL) {
|
|
ExFreePool(PDevExt->DosName.Buffer);
|
|
PDevExt->DosName.Buffer = NULL;
|
|
}
|
|
|
|
if (PDevExt->CreatedSymbolicLink == TRUE) {
|
|
IoDeleteSymbolicLink(&PDevExt->SymbolicLinkName);
|
|
PDevExt->CreatedSymbolicLink = FALSE;
|
|
}
|
|
|
|
if (PDevExt->SymbolicLinkName.Buffer != NULL) {
|
|
ExFreePool(PDevExt->SymbolicLinkName.Buffer);
|
|
PDevExt->SymbolicLinkName.Buffer = NULL;
|
|
}
|
|
|
|
if (PDevExt->DeviceName.Buffer != NULL) {
|
|
RtlDeleteRegistryValue(RTL_REGISTRY_DEVICEMAP, SERIAL_DEVICE_MAP,
|
|
PDevExt->DeviceName.Buffer);
|
|
}
|
|
|
|
if (PDevExt->DeviceClassSymbolicName.Buffer) {
|
|
IoSetDeviceInterfaceState (&PDevExt->DeviceClassSymbolicName, FALSE);
|
|
}
|
|
|
|
if (PDevExt->WmiIdentifier.Buffer != NULL) {
|
|
ExFreePool(PDevExt->WmiIdentifier.Buffer);
|
|
PDevExt->WmiIdentifier.Buffer = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Always clean up our temp buffers.
|
|
//
|
|
|
|
if (linkName.Buffer != NULL) {
|
|
ExFreePool(linkName.Buffer);
|
|
}
|
|
|
|
if (pRegName != NULL) {
|
|
ExFreePool(pRegName);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
SerialUndoExternalNaming(IN PSERIAL_DEVICE_EXTENSION Extension)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will be used to delete a symbolic link
|
|
to the driver name in the given object directory.
|
|
|
|
It will also delete an entry in the device map for
|
|
this device if the symbolic link had been created.
|
|
|
|
Arguments:
|
|
|
|
Extension - Pointer to the device extension.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
SerialDump(
|
|
SERDIAG3,
|
|
("SERIAL: In SerialUndoExternalNaming for\n"
|
|
"------- extension: %x of port %wZ\n",
|
|
Extension,&Extension->DeviceName)
|
|
);
|
|
|
|
//
|
|
// Maybe there is nothing for us to do
|
|
//
|
|
|
|
if (Extension->SkipNaming) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We're cleaning up here. One reason we're cleaning up
|
|
// is that we couldn't allocate space for the directory
|
|
// name or the symbolic link.
|
|
//
|
|
|
|
if (Extension->SymbolicLinkName.Buffer &&
|
|
Extension->CreatedSymbolicLink) {
|
|
|
|
if (Extension->DeviceClassSymbolicName.Buffer) {
|
|
status = IoSetDeviceInterfaceState (&Extension->
|
|
DeviceClassSymbolicName,
|
|
FALSE);
|
|
|
|
//
|
|
// IoRegisterDeviceClassInterface() allocated this string for us,
|
|
// and we no longer need it.
|
|
//
|
|
|
|
ExFreePool( Extension->DeviceClassSymbolicName.Buffer );
|
|
}
|
|
|
|
|
|
IoDeleteSymbolicLink (&Extension->SymbolicLinkName);
|
|
|
|
}
|
|
|
|
if (Extension->WmiIdentifier.Buffer) {
|
|
ExFreePool(Extension->WmiIdentifier.Buffer);
|
|
Extension->WmiIdentifier.MaximumLength
|
|
= Extension->WmiIdentifier.Length = 0;
|
|
Extension->WmiIdentifier.Buffer = NULL;
|
|
}
|
|
|
|
//
|
|
// We're cleaning up here. One reason we're cleaning up
|
|
// is that we couldn't allocate space for the NtNameOfPort.
|
|
//
|
|
|
|
if ((Extension->DeviceName.Buffer != NULL)
|
|
&& Extension->CreatedSerialCommEntry) {
|
|
|
|
status = RtlDeleteRegistryValue(RTL_REGISTRY_DEVICEMAP, SERIAL_DEVICE_MAP,
|
|
Extension->DeviceName.Buffer);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
SerialLogError(
|
|
Extension->DeviceObject->DriverObject,
|
|
Extension->DeviceObject,
|
|
Extension->OriginalController,
|
|
SerialPhysicalZero,
|
|
0,
|
|
0,
|
|
0,
|
|
55,
|
|
status,
|
|
SERIAL_NO_DEVICE_MAP_DELETED,
|
|
Extension->DeviceName.Length+sizeof(WCHAR),
|
|
Extension->DeviceName.Buffer,
|
|
0,
|
|
NULL
|
|
);
|
|
SerialDump(
|
|
SERERRORS,
|
|
("SERIAL: Couldn't delete value entry %wZ\n",
|
|
&Extension->DeviceName)
|
|
);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|