7962 lines
215 KiB
C
7962 lines
215 KiB
C
/*++
|
||
|
||
Copyright (c) 1991, 1992, 1993 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
initunlo.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the code that is very specific to initialization
|
||
and unload operations in the serial driver
|
||
|
||
Author:
|
||
|
||
Anthony V. Ercolano 26-Sep-1991
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History :
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
|
||
//
|
||
// This is the actual definition of SerialDebugLevel.
|
||
// Note that it is only defined if this is a "debug"
|
||
// build.
|
||
//
|
||
#if DBG
|
||
extern ULONG SerialDebugLevel = 0;
|
||
#endif
|
||
|
||
static const PHYSICAL_ADDRESS SerialPhysicalZero = {0};
|
||
|
||
NTSTATUS
|
||
DriverEntry(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
);
|
||
|
||
NTSTATUS
|
||
SerialInitializeController(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PCONFIG_DATA ConfigData,
|
||
IN BOOLEAN MapInterruptStatus,
|
||
OUT PSERIAL_DEVICE_EXTENSION *DeviceExtension
|
||
);
|
||
|
||
BOOLEAN
|
||
SerialDoesPortExist(
|
||
IN PSERIAL_DEVICE_EXTENSION Extension,
|
||
PUNICODE_STRING InsertString,
|
||
IN ULONG ForceFifo,
|
||
IN ULONG LogFifo
|
||
);
|
||
|
||
VOID
|
||
SerialCleanupMultiPort(
|
||
PSERIAL_DEVICE_EXTENSION *ExtensionList,
|
||
ULONG NumberOfDevices
|
||
);
|
||
|
||
VOID
|
||
SerialGetConfigInfo(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath,
|
||
ULONG ForceFifoEnableDefault,
|
||
ULONG RxFifoDefault,
|
||
ULONG TxFifoDefault,
|
||
ULONG PermitShareDefault,
|
||
ULONG LogFifoDefault,
|
||
OUT PLIST_ENTRY ConfigList
|
||
);
|
||
|
||
BOOLEAN
|
||
SerialPutInConfigList(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN OUT PLIST_ENTRY ConfigList,
|
||
IN PCONFIG_DATA New,
|
||
IN BOOLEAN FirmwareAddition
|
||
);
|
||
|
||
BOOLEAN
|
||
SerialResetSynch(
|
||
IN PVOID Context
|
||
);
|
||
|
||
PVOID
|
||
SerialGetMappedAddress(
|
||
IN INTERFACE_TYPE BusType,
|
||
IN ULONG BusNumber,
|
||
PHYSICAL_ADDRESS IoAddress,
|
||
ULONG NumberOfBytes,
|
||
ULONG AddressSpace,
|
||
PBOOLEAN MappedAddress
|
||
);
|
||
|
||
VOID
|
||
SerialSetupExternalNaming(
|
||
IN PSERIAL_DEVICE_EXTENSION Extension
|
||
);
|
||
|
||
VOID
|
||
SerialCleanupExternalNaming(
|
||
IN PSERIAL_DEVICE_EXTENSION Extension
|
||
);
|
||
|
||
typedef enum _SERIAL_MEM_COMPARES {
|
||
AddressesAreEqual,
|
||
AddressesOverlap,
|
||
AddressesAreDisjoint
|
||
} SERIAL_MEM_COMPARES,*PSERIAL_MEM_COMPARES;
|
||
|
||
SERIAL_MEM_COMPARES
|
||
SerialMemCompare(
|
||
IN PHYSICAL_ADDRESS A,
|
||
IN ULONG SpanOfA,
|
||
IN PHYSICAL_ADDRESS B,
|
||
IN ULONG SpanOfB
|
||
);
|
||
|
||
VOID
|
||
SerialPropagateDeleteSharers(
|
||
IN PSERIAL_DEVICE_EXTENSION Extension,
|
||
IN OUT PULONG CountSoFar OPTIONAL,
|
||
IN PKINTERRUPT Interrupt OPTIONAL
|
||
);
|
||
|
||
VOID
|
||
SerialInitializeRootInterrupt(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PCONFIG_DATA ConfigData
|
||
);
|
||
|
||
NTSTATUS
|
||
SerialInitializeRootMultiPort(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PCONFIG_DATA ConfigData,
|
||
OUT PSERIAL_DEVICE_EXTENSION *DeviceExtension
|
||
);
|
||
|
||
NTSTATUS
|
||
SerialInitializeOneController(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PCONFIG_DATA ConfigData,
|
||
IN BOOLEAN MapInterruptStatus,
|
||
OUT PSERIAL_DEVICE_EXTENSION *Extension
|
||
);
|
||
|
||
VOID
|
||
SerialLogError(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PDEVICE_OBJECT DeviceObject OPTIONAL,
|
||
IN PHYSICAL_ADDRESS P1,
|
||
IN PHYSICAL_ADDRESS P2,
|
||
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
|
||
);
|
||
|
||
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
|
||
);
|
||
|
||
NTSTATUS
|
||
SerialConfigCallBack(
|
||
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
|
||
);
|
||
|
||
VOID
|
||
SerialUnReportResourcesDevice(
|
||
IN PSERIAL_DEVICE_EXTENSION Extension
|
||
);
|
||
|
||
VOID
|
||
SerialReportResourcesDevice(
|
||
IN PSERIAL_DEVICE_EXTENSION Extension,
|
||
OUT BOOLEAN *ConflictDetected
|
||
);
|
||
|
||
ULONG
|
||
SerialCheckForShare(
|
||
IN PUNICODE_STRING PathName
|
||
);
|
||
|
||
//
|
||
// This is exported from the kernel. It is used to point
|
||
// to the address that the kernel debugger is using.
|
||
//
|
||
extern PUCHAR *KdComPortInUse;
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT,DriverEntry)
|
||
#pragma alloc_text(INIT,SerialInitializeRootInterrupt)
|
||
#pragma alloc_text(INIT,SerialInitializeRootMultiPort)
|
||
#pragma alloc_text(INIT,SerialInitializeOneController)
|
||
#pragma alloc_text(INIT,SerialInitializeController)
|
||
#pragma alloc_text(INIT,SerialDoesPortExist)
|
||
#pragma alloc_text(INIT,SerialItemCallBack)
|
||
#pragma alloc_text(INIT,SerialConfigCallBack)
|
||
#pragma alloc_text(INIT,SerialGetConfigInfo)
|
||
#pragma alloc_text(INIT,SerialPutInConfigList)
|
||
#pragma alloc_text(INIT,SerialGetMappedAddress)
|
||
#pragma alloc_text(INIT,SerialSetupExternalNaming)
|
||
#pragma alloc_text(INIT,SerialReportResourcesDevice)
|
||
#pragma alloc_text(INIT,SerialCheckForShare)
|
||
#pragma alloc_text(PAGESER,SerialMemCompare)
|
||
#pragma alloc_text(PAGESER,SerialGetDivisorFromBaud)
|
||
#pragma alloc_text(PAGESER,SerialUnload)
|
||
#pragma alloc_text(PAGESER,SerialPropagateDeleteSharers)
|
||
#pragma alloc_text(PAGESER,SerialReset)
|
||
#pragma alloc_text(PAGESER,SerialCleanupDevice)
|
||
#pragma alloc_text(PAGESER,SerialCleanupExternalNaming)
|
||
#pragma alloc_text(PAGESER,SerialLogError)
|
||
#pragma alloc_text(PAGESER,SerialUnReportResourcesDevice)
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
DriverEntry(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The entry point that the system point calls to initialize
|
||
any driver.
|
||
|
||
This routine will gather the configuration information,
|
||
report resource usage, attempt to initialize all serial
|
||
devices, connect to interrupts for ports. If the above
|
||
goes reasonably well it will fill in the dispatch points,
|
||
reset the serial devices and then return to the system.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Just what it says, really of little use
|
||
to the driver itself, it is something that the IO system
|
||
cares more about.
|
||
|
||
PathToRegistry - points to the entry for this driver
|
||
in the current control set of the registry.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if we could initialize a single device,
|
||
otherwise STATUS_SERIAL_NO_DEVICE_INITED.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// Holds status information return by various OS and driver
|
||
// initialization routines.
|
||
//
|
||
NTSTATUS status;
|
||
|
||
//
|
||
// List head for configuration records.
|
||
//
|
||
LIST_ENTRY configList;
|
||
|
||
//
|
||
// Pointer to a device object in the device object chain
|
||
// hanging off of the driver object.
|
||
//
|
||
PDEVICE_OBJECT currentDevice;
|
||
|
||
//
|
||
// Holds a pointer to a ulong that the Io system maintains
|
||
// of the count of serial devices.
|
||
//
|
||
PULONG countSoFar;
|
||
|
||
//
|
||
// We use this to query into the registry as to whether we
|
||
// should break at driver entry.
|
||
//
|
||
RTL_QUERY_REGISTRY_TABLE paramTable[8];
|
||
ULONG zero = 0;
|
||
ULONG debugLevel = 0;
|
||
ULONG shouldBreak = 0;
|
||
ULONG forceFifoEnableDefault;
|
||
ULONG rxFIFODefault;
|
||
ULONG txFIFODefault;
|
||
ULONG permitShareDefault;
|
||
ULONG logFifoDefault;
|
||
ULONG notThereDefault = 1234567;
|
||
PWCHAR path;
|
||
PVOID lockPtr;
|
||
|
||
lockPtr = MmLockPagableCodeSection(SerialUnload);
|
||
|
||
//
|
||
// Since the registry path parameter is a "counted" UNICODE string, it
|
||
// might not be zero terminated. For a very short time allocate memory
|
||
// to hold the registry path zero terminated so that we can use it to
|
||
// delve into the registry.
|
||
//
|
||
// NOTE NOTE!!!! This is not an architected way of breaking into
|
||
// a driver. It happens to work for this driver because the author
|
||
// likes to do things this way.
|
||
//
|
||
|
||
if (path = ExAllocatePool(
|
||
PagedPool,
|
||
RegistryPath->Length+sizeof(WCHAR)
|
||
)) {
|
||
|
||
RtlZeroMemory(
|
||
¶mTable[0],
|
||
sizeof(paramTable)
|
||
);
|
||
RtlZeroMemory(
|
||
path,
|
||
RegistryPath->Length+sizeof(WCHAR)
|
||
);
|
||
RtlMoveMemory(
|
||
path,
|
||
RegistryPath->Buffer,
|
||
RegistryPath->Length
|
||
);
|
||
paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
paramTable[0].Name = L"BreakOnEntry";
|
||
paramTable[0].EntryContext = &shouldBreak;
|
||
paramTable[0].DefaultType = REG_DWORD;
|
||
paramTable[0].DefaultData = &zero;
|
||
paramTable[0].DefaultLength = sizeof(ULONG);
|
||
paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
paramTable[1].Name = L"DebugLevel";
|
||
paramTable[1].EntryContext = &debugLevel;
|
||
paramTable[1].DefaultType = REG_DWORD;
|
||
paramTable[1].DefaultData = &zero;
|
||
paramTable[1].DefaultLength = sizeof(ULONG);
|
||
paramTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
paramTable[2].Name = L"ForceFifoEnable";
|
||
paramTable[2].EntryContext = &forceFifoEnableDefault;
|
||
paramTable[2].DefaultType = REG_DWORD;
|
||
paramTable[2].DefaultData = ¬ThereDefault;
|
||
paramTable[2].DefaultLength = sizeof(ULONG);
|
||
paramTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
paramTable[3].Name = L"RxFIFO";
|
||
paramTable[3].EntryContext = &rxFIFODefault;
|
||
paramTable[3].DefaultType = REG_DWORD;
|
||
paramTable[3].DefaultData = ¬ThereDefault;
|
||
paramTable[3].DefaultLength = sizeof(ULONG);
|
||
paramTable[4].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
paramTable[4].Name = L"TxFIFO";
|
||
paramTable[4].EntryContext = &txFIFODefault;
|
||
paramTable[4].DefaultType = REG_DWORD;
|
||
paramTable[4].DefaultData = ¬ThereDefault;
|
||
paramTable[4].DefaultLength = sizeof(ULONG);
|
||
paramTable[5].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
paramTable[5].Name = L"PermitShare";
|
||
paramTable[5].EntryContext = &permitShareDefault;
|
||
paramTable[5].DefaultType = REG_DWORD;
|
||
paramTable[5].DefaultData = ¬ThereDefault;
|
||
paramTable[5].DefaultLength = sizeof(ULONG);
|
||
paramTable[6].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
paramTable[6].Name = L"LogFifo";
|
||
paramTable[6].EntryContext = &logFifoDefault;
|
||
paramTable[6].DefaultType = REG_DWORD;
|
||
paramTable[6].DefaultData = ¬ThereDefault;
|
||
paramTable[6].DefaultLength = sizeof(ULONG);
|
||
|
||
if (!NT_SUCCESS(RtlQueryRegistryValues(
|
||
RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
|
||
path,
|
||
¶mTable[0],
|
||
NULL,
|
||
NULL
|
||
))) {
|
||
|
||
shouldBreak = 0;
|
||
debugLevel = 0;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
#if DBG
|
||
SerialDebugLevel = debugLevel;
|
||
#endif
|
||
|
||
if (shouldBreak) {
|
||
|
||
DbgBreakPoint();
|
||
|
||
}
|
||
|
||
//
|
||
// Check to see if there was a forcefifo or an rxfifo size.
|
||
// If there isn't then write out values so that they could
|
||
// be adjusted later.
|
||
//
|
||
|
||
if (forceFifoEnableDefault == notThereDefault) {
|
||
|
||
forceFifoEnableDefault = 1;
|
||
RtlWriteRegistryValue(
|
||
RTL_REGISTRY_ABSOLUTE,
|
||
path,
|
||
L"ForceFifoEnable",
|
||
REG_DWORD,
|
||
&forceFifoEnableDefault,
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
}
|
||
|
||
if (rxFIFODefault == notThereDefault) {
|
||
|
||
rxFIFODefault = 8;
|
||
RtlWriteRegistryValue(
|
||
RTL_REGISTRY_ABSOLUTE,
|
||
path,
|
||
L"RxFIFO",
|
||
REG_DWORD,
|
||
&rxFIFODefault,
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
}
|
||
|
||
if (txFIFODefault == notThereDefault) {
|
||
|
||
txFIFODefault = 1;
|
||
|
||
RtlWriteRegistryValue(
|
||
RTL_REGISTRY_ABSOLUTE,
|
||
path,
|
||
L"TxFIFO",
|
||
REG_DWORD,
|
||
&txFIFODefault,
|
||
sizeof(ULONG)
|
||
);
|
||
}
|
||
|
||
|
||
if (permitShareDefault == notThereDefault) {
|
||
|
||
permitShareDefault = 0;
|
||
|
||
//
|
||
// Only share if the user actual changes switch.
|
||
//
|
||
|
||
RtlWriteRegistryValue(
|
||
RTL_REGISTRY_ABSOLUTE,
|
||
path,
|
||
L"PermitShare",
|
||
REG_DWORD,
|
||
&permitShareDefault,
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
}
|
||
|
||
|
||
if (logFifoDefault == notThereDefault) {
|
||
|
||
//
|
||
// Wasn't there. After this load don't log
|
||
// the message anymore. However this first
|
||
// time log the message.
|
||
//
|
||
|
||
logFifoDefault = 0;
|
||
|
||
RtlWriteRegistryValue(
|
||
RTL_REGISTRY_ABSOLUTE,
|
||
path,
|
||
L"LogFifo",
|
||
REG_DWORD,
|
||
&logFifoDefault,
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
logFifoDefault = 1;
|
||
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// We don't need that path anymore.
|
||
//
|
||
|
||
if (path) {
|
||
|
||
ExFreePool(path);
|
||
|
||
}
|
||
|
||
//
|
||
// Just dump out how big the extension is.
|
||
//
|
||
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: The number of bytes in the extension is: %d\n",
|
||
sizeof(SERIAL_DEVICE_EXTENSION))
|
||
);
|
||
|
||
|
||
countSoFar = &IoGetConfigurationInformation()->SerialCount;
|
||
|
||
SerialGetConfigInfo(
|
||
DriverObject,
|
||
RegistryPath,
|
||
forceFifoEnableDefault,
|
||
rxFIFODefault,
|
||
txFIFODefault,
|
||
permitShareDefault,
|
||
logFifoDefault,
|
||
&configList
|
||
);
|
||
|
||
//
|
||
// Initialize each item in the list of configuration records.
|
||
//
|
||
|
||
while (!IsListEmpty(&configList)) {
|
||
|
||
PCONFIG_DATA currentConfig;
|
||
PLIST_ENTRY head;
|
||
|
||
head = RemoveHeadList(&configList);
|
||
|
||
currentConfig = CONTAINING_RECORD(
|
||
head,
|
||
CONFIG_DATA,
|
||
ConfigList
|
||
);
|
||
|
||
SerialInitializeRootInterrupt(
|
||
DriverObject,
|
||
currentConfig
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// We've initialized all of the hardware that this driver
|
||
// will ever know about. All of the hardware that we know
|
||
// about is set up to NOT interrupt. We now go through
|
||
// all of the devices and connect an interrupt object for
|
||
// all.
|
||
//
|
||
|
||
currentDevice = DriverObject->DeviceObject;
|
||
|
||
while (currentDevice) {
|
||
|
||
PSERIAL_DEVICE_EXTENSION extension = currentDevice->DeviceExtension;
|
||
|
||
//
|
||
// This loop will only connect the interrupt for the
|
||
// "root" controller. When we initialize a root controller
|
||
// we then propagate that interrupt object to all associate
|
||
// controllers. If a device doesn't already have an interrupt
|
||
// and it has an isr then we attempt to connect to the
|
||
// interrupt. Note that if we fail to connect to an interrupt
|
||
// we will delete all of the associated devices.
|
||
//
|
||
|
||
if ((!extension->Interrupt) &&
|
||
(extension->OurIsr)) {
|
||
|
||
SerialDump(
|
||
SERDIAG5,
|
||
("SERIAL: About to connect to interrupt for port %wZ\n"
|
||
"------- address of extension is %x\n",
|
||
&extension->DeviceName,extension)
|
||
);
|
||
status = IoConnectInterrupt(
|
||
&extension->Interrupt,
|
||
extension->OurIsr,
|
||
extension->OurIsrContext,
|
||
NULL,
|
||
extension->Vector,
|
||
extension->Irql,
|
||
extension->Irql,
|
||
extension->InterruptMode,
|
||
extension->InterruptShareable,
|
||
extension->ProcessorAffinity,
|
||
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 as well as
|
||
// any of the devices that were hoping to
|
||
// share this interrupt.
|
||
//
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't connect to interrupt for %wZ\n",
|
||
&extension->DeviceName)
|
||
);
|
||
SerialLogError(
|
||
extension->DeviceObject->DriverObject,
|
||
extension->DeviceObject,
|
||
extension->OriginalController,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
1,
|
||
status,
|
||
SERIAL_UNREPORTED_IRQL_CONFLICT,
|
||
extension->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
extension->SymbolicLinkName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialPropagateDeleteSharers(
|
||
extension,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// The above call deleted all the associated
|
||
// device objects. Who knows what the device
|
||
// list looks like now! Start over from
|
||
// the beginning of the device list.
|
||
//
|
||
|
||
currentDevice = DriverObject->DeviceObject;
|
||
|
||
} else {
|
||
|
||
SerialPropagateDeleteSharers(
|
||
extension,
|
||
countSoFar,
|
||
extension->Interrupt
|
||
);
|
||
|
||
currentDevice = DriverObject->DeviceObject;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// We've already done this device. We can go on
|
||
// to the next device.
|
||
//
|
||
|
||
currentDevice = currentDevice->NextDevice;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Well if we connected to any interrupts then we should
|
||
// have some device objects. Go through all of the devices
|
||
// and reset each device.
|
||
//
|
||
|
||
currentDevice = DriverObject->DeviceObject;
|
||
|
||
while (currentDevice) {
|
||
|
||
PDEVICE_OBJECT nextDevice = currentDevice->NextDevice;
|
||
PSERIAL_DEVICE_EXTENSION extension = currentDevice->DeviceExtension;
|
||
|
||
//
|
||
// While the device isn't open, disable all interrupts.
|
||
//
|
||
DISABLE_ALL_INTERRUPTS(extension->Controller);
|
||
|
||
if (extension->Jensen) {
|
||
|
||
WRITE_MODEM_CONTROL(
|
||
extension->Controller,
|
||
(UCHAR)SERIAL_MCR_OUT2
|
||
);
|
||
|
||
} else {
|
||
|
||
WRITE_MODEM_CONTROL(
|
||
extension->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 stupid fifo so that it
|
||
// will show up if the user boots to dos.
|
||
//
|
||
|
||
KeSynchronizeExecution(
|
||
extension->Interrupt,
|
||
SerialReset,
|
||
currentDevice->DeviceExtension
|
||
);
|
||
KeSynchronizeExecution( //Disables the fifo.
|
||
extension->Interrupt,
|
||
SerialMarkClose,
|
||
currentDevice->DeviceExtension
|
||
);
|
||
KeSynchronizeExecution(
|
||
extension->Interrupt,
|
||
SerialClrRTS,
|
||
currentDevice->DeviceExtension
|
||
);
|
||
KeSynchronizeExecution(
|
||
extension->Interrupt,
|
||
SerialClrDTR,
|
||
currentDevice->DeviceExtension
|
||
);
|
||
|
||
currentDevice = nextDevice;
|
||
|
||
}
|
||
|
||
if (DriverObject->DeviceObject) {
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// Initialize the Driver Object with driver's entry points
|
||
//
|
||
|
||
DriverObject->DriverUnload = SerialUnload;
|
||
DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = SerialFlush;
|
||
DriverObject->MajorFunction[IRP_MJ_WRITE] = SerialWrite;
|
||
DriverObject->MajorFunction[IRP_MJ_READ] = SerialRead;
|
||
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = SerialIoControl;
|
||
DriverObject->MajorFunction[IRP_MJ_CREATE] = SerialCreateOpen;
|
||
DriverObject->MajorFunction[IRP_MJ_CLOSE] = SerialClose;
|
||
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = SerialCleanup;
|
||
DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] =
|
||
SerialQueryInformationFile;
|
||
DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] =
|
||
SerialSetInformationFile;
|
||
|
||
} else {
|
||
|
||
status = STATUS_SERIAL_NO_DEVICE_INITED;
|
||
|
||
}
|
||
|
||
MmUnlockPagableImageSection(lockPtr);
|
||
return status;
|
||
}
|
||
|
||
VOID
|
||
SerialPropagateDeleteSharers(
|
||
IN PSERIAL_DEVICE_EXTENSION Extension,
|
||
IN OUT PULONG CountSoFar OPTIONAL,
|
||
IN PKINTERRUPT Interrupt OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will either propagate the interrupt object
|
||
to all extensions sharing the same interrupt, or it will
|
||
delete all devices sharing the same interrupt.
|
||
|
||
Arguments:
|
||
|
||
Extension - "Listhead" of all devices sharing the same
|
||
interrupt.
|
||
|
||
CountSoFar - If interrupt is present and this is present,
|
||
we will increment the longword pointed to
|
||
by this pointer for each device extension
|
||
we stick the interrupt into.
|
||
|
||
If interrupt is *not* present and this
|
||
pointer *is* present, we will decrement the
|
||
longword pointed to by this value for
|
||
each device we delete.
|
||
|
||
If this isn't present, well, then, I guess
|
||
we won't do anything with it.
|
||
|
||
Interrupt - If this is present, we propagate it to
|
||
all devices on that want to share the interrupt.
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ASSERT(Extension->OurIsr);
|
||
|
||
SerialDump(
|
||
SERDIAG3,
|
||
("SERIAL: In SerialPropagateDeleteSharers\n"
|
||
"------- Extension: %x CountSoFar: %d Interrupt: %x\n",
|
||
Extension,CountSoFar?*CountSoFar:0,Interrupt)
|
||
);
|
||
|
||
if (Interrupt) {
|
||
|
||
PLIST_ENTRY currentEntry;
|
||
PLIST_ENTRY firstEntry;
|
||
|
||
SerialDump(
|
||
SERDIAG5,
|
||
("SERIAL: In the report propagate path\n")
|
||
);
|
||
|
||
//
|
||
// Were supposed to place the interrupt object
|
||
// in every associated device object.
|
||
//
|
||
|
||
currentEntry = &Extension->CommonInterruptObject;
|
||
firstEntry = currentEntry;
|
||
|
||
do {
|
||
|
||
PSERIAL_DEVICE_EXTENSION currentExtension;
|
||
|
||
currentExtension = CONTAINING_RECORD(
|
||
currentEntry,
|
||
SERIAL_DEVICE_EXTENSION,
|
||
CommonInterruptObject
|
||
);
|
||
|
||
currentExtension->Interrupt = Interrupt;
|
||
|
||
|
||
if (ARGUMENT_PRESENT(CountSoFar)) {
|
||
|
||
*CountSoFar += 1;
|
||
|
||
}
|
||
|
||
currentEntry = currentExtension->CommonInterruptObject.Flink;
|
||
|
||
} while (currentEntry != firstEntry);
|
||
|
||
} else {
|
||
|
||
LIST_ENTRY listHead;
|
||
|
||
//
|
||
// We are supposed to delete all of the devices
|
||
// in the linked list.
|
||
//
|
||
// First we make a local list head that doesn't
|
||
// have the current extension as part of the list.
|
||
//
|
||
// The we cleanup and delete the "root" device.
|
||
//
|
||
// The we traverse all of the associated device
|
||
// extensions and null out the interrupt object
|
||
// (this way subsequent cleanup code won't attempt
|
||
// to disconnect the interrupt object) then we
|
||
// cleanup and delete the device.
|
||
//
|
||
|
||
SerialDump(
|
||
SERDIAG5,
|
||
("SERIAL: In the deletion/unreport path\n")
|
||
);
|
||
|
||
InitializeListHead(&listHead);
|
||
|
||
if (!IsListEmpty(&Extension->CommonInterruptObject)) {
|
||
|
||
PLIST_ENTRY old = Extension->CommonInterruptObject.Flink;
|
||
|
||
RemoveEntryList(&Extension->CommonInterruptObject);
|
||
InsertTailList(
|
||
old,
|
||
&listHead
|
||
);
|
||
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT(CountSoFar)) {
|
||
|
||
//
|
||
// An implication of decrementing the
|
||
// count is that the device has reported
|
||
// it's resources already. Now unreport
|
||
// them.
|
||
//
|
||
|
||
*CountSoFar -= 1;
|
||
SerialUnReportResourcesDevice(Extension);
|
||
|
||
}
|
||
|
||
SerialCleanupDevice(Extension);
|
||
IoDeleteDevice(Extension->DeviceObject);
|
||
|
||
while (!IsListEmpty(&listHead)) {
|
||
|
||
PLIST_ENTRY head;
|
||
PSERIAL_DEVICE_EXTENSION currentExtension;
|
||
|
||
head = RemoveHeadList(&listHead);
|
||
|
||
currentExtension = CONTAINING_RECORD(
|
||
head,
|
||
SERIAL_DEVICE_EXTENSION,
|
||
CommonInterruptObject
|
||
);
|
||
|
||
currentExtension->Interrupt = NULL;
|
||
|
||
if (ARGUMENT_PRESENT(CountSoFar)) {
|
||
|
||
*CountSoFar -= 1;
|
||
SerialUnReportResourcesDevice(currentExtension);
|
||
|
||
}
|
||
|
||
SerialCleanupDevice(currentExtension);
|
||
IoDeleteDevice(currentExtension->DeviceObject);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
VOID
|
||
SerialInitializeRootInterrupt(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PCONFIG_DATA ConfigData
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine attempts to build a list suitable for dispatching
|
||
to multiple extensions for devices that want to share an interrupt.
|
||
Note that this includes the degenerate case of a single port who
|
||
wont be sharing.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Simply passed on to the controller initialization.
|
||
|
||
ConfigData - Root of a "tree" of configuration records.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSERIAL_DEVICE_EXTENSION rootExtension = NULL;
|
||
PCONFIG_DATA originalConfig = ConfigData;
|
||
PCONFIG_DATA currentConfig = ConfigData;
|
||
LIST_ENTRY listHead;
|
||
|
||
SerialDump(
|
||
SERDIAG3,
|
||
("SERIAL: In SerialInitializeRootInterrupt\n")
|
||
);
|
||
|
||
//
|
||
// This makes the listhead imbedded in the root config
|
||
// record a local list head. That list no longer has the
|
||
// original config record as part of the list.
|
||
//
|
||
|
||
InitializeListHead(&listHead);
|
||
|
||
if (!IsListEmpty(&ConfigData->SameInterrupt)) {
|
||
|
||
PLIST_ENTRY old = ConfigData->SameInterrupt.Flink;
|
||
|
||
RemoveEntryList(&ConfigData->SameInterrupt);
|
||
InsertTailList(
|
||
old,
|
||
&listHead
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// If we are on a MicroChannel bus then all the configs can simply
|
||
// share the interrupt.
|
||
//
|
||
|
||
if (ConfigData->InterfaceType == MicroChannel) {
|
||
|
||
//
|
||
// We know that all of the configs on this "chain"
|
||
// are using the MicroChannel.
|
||
//
|
||
|
||
while (currentConfig) {
|
||
|
||
if (!IsListEmpty(¤tConfig->SameInterruptStatus)) {
|
||
|
||
//
|
||
// This is a multiport card, call its intialization.
|
||
//
|
||
|
||
SerialDump(
|
||
SERDIAG5,
|
||
("SERIAL: Attempting to make %wZ with controller at %x\n"
|
||
"------- and status at %x a same interrupt root of multiports\n"
|
||
"------- On a MicroChannel.\n",
|
||
¤tConfig->NtNameForPort,currentConfig->Controller.LowPart,
|
||
currentConfig->InterruptStatus.LowPart)
|
||
);
|
||
SerialInitializeRootMultiPort(
|
||
DriverObject,
|
||
currentConfig,
|
||
&rootExtension
|
||
);
|
||
|
||
} else {
|
||
|
||
SerialDump(
|
||
SERDIAG5,
|
||
("SERIAL: Attempting to make %wZ with controller at %x\n"
|
||
"------- A same interrupt single controller On a MicroChannel.\n",
|
||
¤tConfig->NtNameForPort,currentConfig->Controller.LowPart)
|
||
);
|
||
SerialInitializeOneController(
|
||
DriverObject,
|
||
currentConfig,
|
||
FALSE,
|
||
&rootExtension
|
||
);
|
||
|
||
}
|
||
SerialDump(
|
||
SERDIAG5,
|
||
("SERIAL: It came back with a same interrupt rootExtension of %x\n",
|
||
rootExtension)
|
||
);
|
||
|
||
if (!IsListEmpty(&listHead)) {
|
||
|
||
PLIST_ENTRY head;
|
||
|
||
head = RemoveHeadList(&listHead);
|
||
|
||
currentConfig = CONTAINING_RECORD(
|
||
head,
|
||
CONFIG_DATA,
|
||
SameInterrupt
|
||
);
|
||
|
||
} else {
|
||
|
||
currentConfig = NULL;
|
||
rootExtension = NULL;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// We have to set up to do "shareing" of interrupt resources.
|
||
//
|
||
|
||
//
|
||
// We first keep trying to initialize one of the
|
||
// configs on the chain until one succeeds.
|
||
//
|
||
|
||
while ((!rootExtension) && currentConfig) {
|
||
|
||
NTSTATUS status;
|
||
|
||
if (!IsListEmpty(¤tConfig->SameInterruptStatus)) {
|
||
|
||
//
|
||
// This is a multiport card, call its intialization.
|
||
//
|
||
|
||
SerialDump(
|
||
SERDIAG5,
|
||
("SERIAL: Attempting to make %wZ with controller at %x\n"
|
||
"------- and status at %x a same interrupt sharer root controller\n",
|
||
¤tConfig->NtNameForPort,currentConfig->Controller.LowPart,
|
||
currentConfig->InterruptStatus.LowPart)
|
||
);
|
||
status = SerialInitializeRootMultiPort(
|
||
DriverObject,
|
||
currentConfig,
|
||
&rootExtension
|
||
);
|
||
|
||
} else {
|
||
|
||
SerialDump(
|
||
SERDIAG5,
|
||
("SERIAL: Attempting to make %wZ with controller at %x\n"
|
||
"------- A single same interrupt sharer root controller.\n",
|
||
¤tConfig->NtNameForPort,currentConfig->Controller.LowPart)
|
||
);
|
||
status = SerialInitializeOneController(
|
||
DriverObject,
|
||
currentConfig,
|
||
FALSE,
|
||
&rootExtension
|
||
);
|
||
|
||
}
|
||
|
||
SerialDump(
|
||
SERDIAG5,
|
||
("SERIAL: It came back with a same interrupt rootExtension of %x\n",
|
||
rootExtension)
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Well that one didn't work. Try the next one.
|
||
//
|
||
|
||
if (!IsListEmpty(&listHead)) {
|
||
|
||
PLIST_ENTRY head;
|
||
|
||
head = RemoveHeadList(&listHead);
|
||
|
||
currentConfig = CONTAINING_RECORD(
|
||
head,
|
||
CONFIG_DATA,
|
||
SameInterrupt
|
||
);
|
||
|
||
} else {
|
||
|
||
currentConfig = NULL;
|
||
rootExtension = NULL;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// We save off the isr to use and the context to the
|
||
// isr into the following fields. Unless the
|
||
// device is actually sharing the interrupt with
|
||
// another "card" this field will not be
|
||
// needed.
|
||
//
|
||
|
||
rootExtension->TopLevelOurIsr = rootExtension->OurIsr;
|
||
rootExtension->TopLevelOurIsrContext = rootExtension->OurIsrContext;
|
||
|
||
}
|
||
|
||
|
||
}
|
||
|
||
if (rootExtension) {
|
||
|
||
//
|
||
// We have a root extension! Now try to
|
||
// all intialize all the other configs on this
|
||
// interrupt.
|
||
//
|
||
|
||
ULONG numberOfSharers = 1;
|
||
|
||
while (!IsListEmpty(&listHead)) {
|
||
|
||
NTSTATUS status;
|
||
PLIST_ENTRY head;
|
||
PSERIAL_DEVICE_EXTENSION newExtension;
|
||
|
||
head = RemoveHeadList(&listHead);
|
||
|
||
currentConfig = CONTAINING_RECORD(
|
||
head,
|
||
CONFIG_DATA,
|
||
SameInterrupt
|
||
);
|
||
|
||
if (!IsListEmpty(¤tConfig->SameInterruptStatus)) {
|
||
|
||
//
|
||
// This is a multiport card, call its intialization.
|
||
//
|
||
|
||
SerialDump(
|
||
SERDIAG5,
|
||
("SERIAL: Attempting to make %wZ with controller at %x\n"
|
||
"------- and status at %x a same interrupt sharer multiports\n",
|
||
¤tConfig->NtNameForPort,currentConfig->Controller.LowPart,
|
||
currentConfig->InterruptStatus.LowPart)
|
||
);
|
||
status = SerialInitializeRootMultiPort(
|
||
DriverObject,
|
||
currentConfig,
|
||
&newExtension
|
||
);
|
||
|
||
} else {
|
||
|
||
SerialDump(
|
||
SERDIAG5,
|
||
("SERIAL: Attempting to make %wZ with controller at %x\n"
|
||
"------- A single same interrupt sharer controller.\n",
|
||
¤tConfig->NtNameForPort,currentConfig->Controller.LowPart)
|
||
);
|
||
status = SerialInitializeOneController(
|
||
DriverObject,
|
||
currentConfig,
|
||
FALSE,
|
||
&newExtension
|
||
);
|
||
|
||
}
|
||
|
||
SerialDump(
|
||
SERDIAG5,
|
||
("SERIAL: It came back with a same interrupt newExtension of %x\n",
|
||
rootExtension)
|
||
);
|
||
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
PLIST_ENTRY rootTail;
|
||
PLIST_ENTRY newTail;
|
||
|
||
//
|
||
// Propagate the isr routine and context
|
||
// up to the sharing list.
|
||
//
|
||
|
||
newExtension->TopLevelOurIsr = newExtension->OurIsr;
|
||
newExtension->TopLevelOurIsrContext = newExtension->OurIsrContext;
|
||
newExtension->OurIsr = NULL;
|
||
newExtension->OurIsrContext = NULL;
|
||
|
||
//
|
||
// Append this top level extension onto the list of
|
||
// other top level interrupt sharers.
|
||
//
|
||
|
||
InsertTailList(
|
||
&rootExtension->TopLevelSharers,
|
||
&newExtension->TopLevelSharers
|
||
);
|
||
|
||
//
|
||
// Link together the lists of extensions that will
|
||
// be using the same interrupt object (not necessarily)
|
||
// the same "interrupt service routine" (actually dispatchers).
|
||
//
|
||
|
||
rootTail =
|
||
rootExtension->CommonInterruptObject.Blink;
|
||
newTail =
|
||
newExtension->CommonInterruptObject.Blink;
|
||
|
||
rootExtension->CommonInterruptObject.Blink =
|
||
newTail;
|
||
newExtension->CommonInterruptObject.Blink =
|
||
rootTail;
|
||
rootTail->Flink =
|
||
&newExtension->CommonInterruptObject;
|
||
newTail->Flink =
|
||
&rootExtension->CommonInterruptObject;
|
||
|
||
numberOfSharers++;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// All done initializing the other sharers.
|
||
//
|
||
// If none of the others actually initialized
|
||
// the we simply degenerate into the interrupt
|
||
// handling for the root extension. (This requires
|
||
// no additional work.)
|
||
//
|
||
|
||
if (numberOfSharers > 1) {
|
||
|
||
//
|
||
// Replace the Isr and context for the root
|
||
// with the pointer to the "sharer" dispatcher
|
||
// and a pointer to the list of share entries
|
||
// as context.
|
||
//
|
||
|
||
SerialDump(
|
||
SERDIAG5,
|
||
("SERIAL: We do have more than one sharer for the interrupt.\n"
|
||
"------- The controlling extension should be %x\n",
|
||
rootExtension)
|
||
);
|
||
rootExtension->OurIsr = SerialSharerIsr;
|
||
rootExtension->OurIsrContext = &rootExtension->TopLevelSharers;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
SerialInitializeRootMultiPort(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PCONFIG_DATA ConfigData,
|
||
OUT PSERIAL_DEVICE_EXTENSION *DeviceExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine attempts to initialize all the ports on a
|
||
multiport board and to build a structure so that an
|
||
isr can dispatch to the particular ports device extension.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Simply passed on to the controller initialization
|
||
routine.
|
||
|
||
ConfigData - A linked list of configuration information for all
|
||
the ports on a multiport card.
|
||
|
||
DeviceExtension - Will point to the first successfully initialized
|
||
port on the multiport card.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSERIAL_DEVICE_EXTENSION rootExtension = NULL;
|
||
PCONFIG_DATA originalConfig = ConfigData;
|
||
PCONFIG_DATA currentConfig = ConfigData;
|
||
ULONG indexed;
|
||
ULONG portIndex;
|
||
ULONG maskInverted;
|
||
LIST_ENTRY listHead;
|
||
NTSTATUS status;
|
||
|
||
SerialDump(
|
||
SERDIAG3,
|
||
("SERIAL: In SerialInitializeRootMultiPort\n")
|
||
);
|
||
*DeviceExtension = NULL;
|
||
|
||
//
|
||
// This makes the listhead imbedded in the root config
|
||
// record a local list head. The old head of the list
|
||
// (the current config) will no longer be part of the list.
|
||
//
|
||
|
||
InitializeListHead(&listHead);
|
||
|
||
if (!IsListEmpty(&ConfigData->SameInterruptStatus)) {
|
||
|
||
PLIST_ENTRY old = ConfigData->SameInterruptStatus.Flink;
|
||
|
||
RemoveEntryList(&ConfigData->SameInterruptStatus);
|
||
InsertTailList(
|
||
old,
|
||
&listHead
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// The indexed field is valid for all ports on the chain.
|
||
//
|
||
|
||
indexed = ConfigData->Indexed;
|
||
SerialDump(
|
||
SERDIAG5,
|
||
("SERIAL: This indexed value for this multiport is: %d\n",indexed)
|
||
);
|
||
|
||
maskInverted = ConfigData->MaskInverted;
|
||
|
||
//
|
||
// We first keep trying to initialize one of the
|
||
// ports on the chain until one succeeds.
|
||
//
|
||
|
||
while ((!rootExtension) && currentConfig) {
|
||
|
||
portIndex = currentConfig->PortIndex;
|
||
|
||
SerialDump(
|
||
SERDIAG5,
|
||
("SERIAL: Attempting to make %wZ with controller at %x\n"
|
||
"------- and status at %x a root of multiports\n",
|
||
¤tConfig->NtNameForPort,currentConfig->Controller.LowPart,
|
||
currentConfig->InterruptStatus.LowPart)
|
||
);
|
||
status = SerialInitializeOneController(
|
||
DriverObject,
|
||
currentConfig,
|
||
TRUE,
|
||
&rootExtension
|
||
);
|
||
SerialDump(
|
||
SERDIAG5,
|
||
("SERIAL: Multiport came back with a same interrupt rootExtension of %x\n",
|
||
rootExtension)
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Well that one didn't work. Try the next one.
|
||
//
|
||
|
||
if (!IsListEmpty(&listHead)) {
|
||
|
||
PLIST_ENTRY head;
|
||
|
||
head = RemoveHeadList(&listHead);
|
||
|
||
currentConfig = CONTAINING_RECORD(
|
||
head,
|
||
CONFIG_DATA,
|
||
SameInterruptStatus
|
||
);
|
||
|
||
} else {
|
||
|
||
currentConfig = NULL;
|
||
rootExtension = NULL;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (rootExtension) {
|
||
|
||
//
|
||
// Well we have at least one controller. We build a local
|
||
// dispatch structure. If we end up being able to
|
||
// intialize another port then we will allocate
|
||
// the dispatch structure out of pool. If we can't
|
||
// intialize anymore ports then this degenerates into a
|
||
// single port case.
|
||
//
|
||
|
||
ULONG numberOfPorts = 1;
|
||
SERIAL_MULTIPORT_DISPATCH dispatch;
|
||
|
||
rootExtension->PortOnAMultiportCard = TRUE;
|
||
|
||
RtlZeroMemory(
|
||
&dispatch,
|
||
sizeof(SERIAL_MULTIPORT_DISPATCH)
|
||
);
|
||
|
||
if (!indexed) {
|
||
|
||
dispatch.UsablePortMask = 1 << (portIndex-1);
|
||
dispatch.MaskInverted = maskInverted;
|
||
|
||
}
|
||
|
||
dispatch.InterruptStatus = rootExtension->InterruptStatus;
|
||
|
||
dispatch.Extensions[portIndex-1] = rootExtension;
|
||
|
||
while (!IsListEmpty(&listHead)) {
|
||
|
||
PLIST_ENTRY head;
|
||
PSERIAL_DEVICE_EXTENSION newExtension;
|
||
|
||
head = RemoveHeadList(&listHead);
|
||
|
||
currentConfig = CONTAINING_RECORD(
|
||
head,
|
||
CONFIG_DATA,
|
||
SameInterruptStatus
|
||
);
|
||
|
||
portIndex = currentConfig->PortIndex;
|
||
maskInverted = currentConfig->MaskInverted;
|
||
|
||
if (NT_SUCCESS(SerialInitializeOneController(
|
||
DriverObject,
|
||
currentConfig,
|
||
FALSE,
|
||
&newExtension
|
||
))) {
|
||
|
||
numberOfPorts++;
|
||
newExtension->PortOnAMultiportCard = TRUE;
|
||
|
||
if (!indexed) {
|
||
|
||
dispatch.UsablePortMask |= 1 << (portIndex-1);
|
||
dispatch.MaskInverted |= maskInverted;
|
||
|
||
}
|
||
|
||
dispatch.Extensions[portIndex-1] = newExtension;
|
||
|
||
InsertTailList(
|
||
&rootExtension->CommonInterruptObject,
|
||
&newExtension->CommonInterruptObject
|
||
);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// If the number of ports is still one that means we
|
||
// couldn't initialize any more extensions. This then
|
||
// degenerates into a single port card. Note that there
|
||
// is no work to do in that case, it was set up in
|
||
// SerialInitializeSingleController.
|
||
//
|
||
|
||
if (numberOfPorts > 1) {
|
||
|
||
//
|
||
// Now allocate the dispatch structure out of pool
|
||
// since it certain that we will actually use it.
|
||
//
|
||
|
||
rootExtension->OurIsrContext = ExAllocatePool(
|
||
NonPagedPool,
|
||
sizeof(SERIAL_MULTIPORT_DISPATCH)
|
||
);
|
||
|
||
if (!rootExtension->OurIsrContext) {
|
||
|
||
//
|
||
// Darn! Couldn't allocate the dispatch structure.
|
||
//
|
||
// Seems as though the safest thing to do in
|
||
// this case is to act as though none of the ports
|
||
// initialized. Go through and delete all of the
|
||
// devices that were initialized.
|
||
//
|
||
// This should be fairly safe since the initialize controller
|
||
// code completely disables the port from interrupting.
|
||
//
|
||
|
||
ULONG i;
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't allocate memory for %wZ\n"
|
||
"------- multiport dispatch structure"
|
||
"------- deleting all associated devices",
|
||
&rootExtension->DeviceName)
|
||
);
|
||
|
||
//
|
||
// We couldn't allocate memory for it, hopefully
|
||
// the logger can make some headway.
|
||
//
|
||
|
||
SerialLogError(
|
||
rootExtension->DeviceObject->DriverObject,
|
||
rootExtension->DeviceObject,
|
||
rootExtension->OriginalController,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
2,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
for (
|
||
i = 0;
|
||
numberOfPorts;
|
||
i++
|
||
) {
|
||
|
||
if (dispatch.Extensions[i]) {
|
||
|
||
SerialCleanupDevice(dispatch.Extensions[i]);
|
||
IoDeleteDevice(
|
||
dispatch.Extensions[i]->DeviceObject
|
||
);
|
||
numberOfPorts--;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
} else {
|
||
|
||
ULONG i;
|
||
PSERIAL_MULTIPORT_DISPATCH allocatedDispatch =
|
||
rootExtension->OurIsrContext;
|
||
|
||
//
|
||
// Go throught the list of extensions and NULL
|
||
// their pointers to the isr. The only extension
|
||
// that will truely have an isr is the root.
|
||
//
|
||
|
||
allocatedDispatch->UsablePortMask = dispatch.UsablePortMask;
|
||
allocatedDispatch->MaskInverted = dispatch.MaskInverted;
|
||
allocatedDispatch->InterruptStatus = dispatch.InterruptStatus;
|
||
|
||
for (
|
||
i = 0;
|
||
i < SERIAL_MAX_PORTS_NONINDEXED;
|
||
i++
|
||
) {
|
||
|
||
allocatedDispatch->Extensions[i] = dispatch.Extensions[i];
|
||
|
||
if (dispatch.Extensions[i]) {
|
||
dispatch.Extensions[i]->OurIsr = NULL;
|
||
}
|
||
|
||
}
|
||
|
||
if (indexed) {
|
||
|
||
rootExtension->OurIsr = SerialIndexedMultiportIsr;
|
||
|
||
} else {
|
||
|
||
rootExtension->OurIsr = SerialBitMappedMultiportIsr;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
*DeviceExtension = rootExtension;
|
||
return STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
return STATUS_UNSUCCESSFUL;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
SerialInitializeOneController(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PCONFIG_DATA ConfigData,
|
||
IN BOOLEAN MapInterruptStatus,
|
||
OUT PSERIAL_DEVICE_EXTENSION *Extension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will call the real port initializatio code.
|
||
If all was successful, it will save off in the extension
|
||
the isr that should be used as well as a pointer to
|
||
the extension itself.
|
||
|
||
This is the only routine responsible for deleting the
|
||
configuration information subsequent to getting it all
|
||
from the registry.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Simply passed on to the controller initialization
|
||
routine.
|
||
|
||
ConfigData - Pointer to a record for a single port.
|
||
|
||
MapInterruptStatus - Simply passed on to the controller initialization
|
||
routine.
|
||
|
||
Extension - Points to the device extension of the successfully
|
||
initialized controller.
|
||
|
||
Return Value:
|
||
|
||
Status returned from the controller initialization routine.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS status;
|
||
|
||
status = SerialInitializeController(
|
||
DriverObject,
|
||
ConfigData,
|
||
MapInterruptStatus,
|
||
Extension
|
||
);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// We successfully initialized the single controller.
|
||
// Stick the isr routine and the parameter for it
|
||
// back into the extension.
|
||
//
|
||
|
||
(*Extension)->OurIsr = SerialISR;
|
||
(*Extension)->OurIsrContext = *Extension;
|
||
|
||
} else {
|
||
|
||
*Extension = NULL;
|
||
|
||
}
|
||
|
||
return status;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
SerialInitializeController(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PCONFIG_DATA ConfigData,
|
||
IN BOOLEAN MapInterruptStatus,
|
||
OUT PSERIAL_DEVICE_EXTENSION *DeviceExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Really too many things to mention here. In general, it forms
|
||
and sets up names, creates the device, initializes kernel
|
||
synchronization structures, allocates the typeahead buffer,
|
||
sets up defaults, etc.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Just used to create the device object.
|
||
|
||
ConfigData - Pointer to a record for a single port.
|
||
|
||
NOTE: This routine will deallocate the config data.
|
||
|
||
MapInterruptStatus - If true, we will attempt to map the
|
||
interrupt status register associated
|
||
with this port..
|
||
|
||
DeviceExtension - Points to the device extension of the successfully
|
||
initialized controller.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCCESS if everything went ok. A !NT_SUCCESS status
|
||
otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// This will hold the string that we need to use to describe
|
||
// the name of the device to the IO system.
|
||
//
|
||
UNICODE_STRING uniNameString;
|
||
|
||
//
|
||
// Holds the NT Status that is returned from each call to the
|
||
// kernel and executive.
|
||
//
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// Points to the device object (not the extension) created
|
||
// for this device.
|
||
//
|
||
PDEVICE_OBJECT deviceObject;
|
||
|
||
//
|
||
// Points to the device extension for the device object
|
||
// (see above) created for the device we are initializing.
|
||
//
|
||
PSERIAL_DEVICE_EXTENSION extension = NULL;
|
||
|
||
//
|
||
// Indicates that we successfully reported the resources
|
||
// used by this device.
|
||
//
|
||
BOOLEAN reportedResources = FALSE;
|
||
|
||
//
|
||
// Indicates that a conflict was detected for resources
|
||
// used by this device.
|
||
//
|
||
BOOLEAN conflictDetected = FALSE;
|
||
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: Initializing for configuration record of %wZ\n",
|
||
&ConfigData->NtNameForPort)
|
||
);
|
||
if ((*KdComPortInUse) ==
|
||
|
||
((PUCHAR)(ConfigData->Controller.LowPart))) {
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Kernel debugger is using port at address %x\n"
|
||
"------ Serial driver will not load port %wZ\n",
|
||
*KdComPortInUse,&ConfigData->SymbolicLinkName)
|
||
);
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
ConfigData->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
3,
|
||
STATUS_SUCCESS,
|
||
SERIAL_KERNEL_DEBUGGER_ACTIVE,
|
||
ConfigData->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
ConfigData->SymbolicLinkName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
ExFreePool(ConfigData->ObjectDirectory.Buffer);
|
||
ExFreePool(ConfigData->NtNameForPort.Buffer);
|
||
ExFreePool(ConfigData->SymbolicLinkName.Buffer);
|
||
ExFreePool(ConfigData);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
}
|
||
|
||
//
|
||
// Form a name like \Device\Serial0.
|
||
//
|
||
// First we allocate space for the name.
|
||
//
|
||
|
||
RtlInitUnicodeString(
|
||
&uniNameString,
|
||
NULL
|
||
);
|
||
|
||
uniNameString.MaximumLength = sizeof(L"\\Device\\") +
|
||
ConfigData->NtNameForPort.Length+sizeof(WCHAR);
|
||
uniNameString.Buffer = ExAllocatePool(
|
||
PagedPool,
|
||
uniNameString.MaximumLength
|
||
);
|
||
|
||
//
|
||
// The only reason the above could have failed is if
|
||
// there wasn't enough system memory to form the UNICODE
|
||
// string.
|
||
//
|
||
|
||
if (!uniNameString.Buffer) {
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Could not form Unicode name string for %wZ\n",
|
||
&ConfigData->NtNameForPort)
|
||
);
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
ConfigData->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
4,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
ExFreePool(ConfigData->ObjectDirectory.Buffer);
|
||
ExFreePool(ConfigData->NtNameForPort.Buffer);
|
||
ExFreePool(ConfigData->SymbolicLinkName.Buffer);
|
||
ExFreePool(ConfigData);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
}
|
||
|
||
//
|
||
// Actually form the Name.
|
||
//
|
||
|
||
RtlZeroMemory(
|
||
uniNameString.Buffer,
|
||
uniNameString.MaximumLength
|
||
);
|
||
|
||
RtlAppendUnicodeToString(
|
||
&uniNameString,
|
||
L"\\Device\\"
|
||
);
|
||
|
||
RtlAppendUnicodeStringToString(
|
||
&uniNameString,
|
||
&ConfigData->NtNameForPort
|
||
);
|
||
|
||
//
|
||
// Create the device object for this device.
|
||
//
|
||
|
||
status = IoCreateDevice(
|
||
DriverObject,
|
||
sizeof(SERIAL_DEVICE_EXTENSION),
|
||
&uniNameString,
|
||
FILE_DEVICE_SERIAL_PORT,
|
||
0,
|
||
TRUE,
|
||
&deviceObject
|
||
);
|
||
|
||
//
|
||
// If we couldn't create the device object, then there
|
||
// is no point in going on.
|
||
//
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Could not create a device for %wZ\n",
|
||
&ConfigData->NtNameForPort)
|
||
);
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
ConfigData->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
5,
|
||
status,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
ExFreePool(ConfigData->ObjectDirectory.Buffer);
|
||
ExFreePool(ConfigData->NtNameForPort.Buffer);
|
||
ExFreePool(ConfigData->SymbolicLinkName.Buffer);
|
||
ExFreePool(ConfigData);
|
||
ExFreePool(uniNameString.Buffer);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
}
|
||
|
||
//
|
||
// The device object has a pointer to an area of non-paged
|
||
// pool allocated for this device. This will be the device
|
||
// extension.
|
||
//
|
||
|
||
extension = deviceObject->DeviceExtension;
|
||
|
||
//
|
||
// Zero all of the memory associated with the device
|
||
// extension.
|
||
//
|
||
|
||
RtlZeroMemory(
|
||
extension,
|
||
sizeof(SERIAL_DEVICE_EXTENSION)
|
||
);
|
||
|
||
//
|
||
// Propagate that it is a jensen.
|
||
//
|
||
|
||
extension->Jensen = ConfigData->Jensen;
|
||
|
||
//
|
||
// So far, we don't know if this extension will be
|
||
// shareing it's interrupt object with any other serial
|
||
// port.
|
||
//
|
||
|
||
InitializeListHead(&extension->TopLevelSharers);
|
||
InitializeListHead(&extension->CommonInterruptObject);
|
||
|
||
//
|
||
// Save off our name.
|
||
//
|
||
|
||
RtlInitUnicodeString(
|
||
&extension->DeviceName,
|
||
NULL
|
||
);
|
||
|
||
extension->DeviceName.Length = uniNameString.Length;
|
||
extension->DeviceName.MaximumLength = uniNameString.MaximumLength;
|
||
extension->DeviceName.Buffer = uniNameString.Buffer;
|
||
|
||
//
|
||
// Just initialize the names so that we don't try
|
||
// to "clean" them up if we cant intialize the
|
||
// controller all the way.
|
||
//
|
||
|
||
RtlInitUnicodeString(
|
||
&extension->ObjectDirectory,
|
||
NULL
|
||
);
|
||
RtlInitUnicodeString(
|
||
&extension->NtNameForPort,
|
||
NULL
|
||
);
|
||
RtlInitUnicodeString(
|
||
&extension->SymbolicLinkName,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Initialize the list heads for the read, write, and mask queues.
|
||
//
|
||
// These lists will hold all of the queued IRP's for the device.
|
||
//
|
||
|
||
InitializeListHead(&extension->ReadQueue);
|
||
InitializeListHead(&extension->WriteQueue);
|
||
InitializeListHead(&extension->MaskQueue);
|
||
InitializeListHead(&extension->PurgeQueue);
|
||
|
||
//
|
||
// Initialize the spinlock associated with fields read (& set)
|
||
// by IO Control functions.
|
||
//
|
||
|
||
KeInitializeSpinLock(&extension->ControlLock);
|
||
|
||
//
|
||
// Initialize the timers used to timeout operations.
|
||
//
|
||
|
||
KeInitializeTimer(&extension->ReadRequestTotalTimer);
|
||
KeInitializeTimer(&extension->ReadRequestIntervalTimer);
|
||
KeInitializeTimer(&extension->WriteRequestTotalTimer);
|
||
KeInitializeTimer(&extension->ImmediateTotalTimer);
|
||
KeInitializeTimer(&extension->XoffCountTimer);
|
||
KeInitializeTimer(&extension->LowerRTSTimer);
|
||
|
||
//
|
||
// Intialialize the dpcs that will be used to complete
|
||
// or timeout various IO operations.
|
||
//
|
||
|
||
KeInitializeDpc(
|
||
&extension->CompleteWriteDpc,
|
||
SerialCompleteWrite,
|
||
extension
|
||
);
|
||
|
||
KeInitializeDpc(
|
||
&extension->CompleteReadDpc,
|
||
SerialCompleteRead,
|
||
extension
|
||
);
|
||
|
||
KeInitializeDpc(
|
||
&extension->TotalReadTimeoutDpc,
|
||
SerialReadTimeout,
|
||
extension
|
||
);
|
||
|
||
KeInitializeDpc(
|
||
&extension->IntervalReadTimeoutDpc,
|
||
SerialIntervalReadTimeout,
|
||
extension
|
||
);
|
||
|
||
KeInitializeDpc(
|
||
&extension->TotalWriteTimeoutDpc,
|
||
SerialWriteTimeout,
|
||
extension
|
||
);
|
||
|
||
KeInitializeDpc(
|
||
&extension->CommErrorDpc,
|
||
SerialCommError,
|
||
extension
|
||
);
|
||
|
||
KeInitializeDpc(
|
||
&extension->CompleteImmediateDpc,
|
||
SerialCompleteImmediate,
|
||
extension
|
||
);
|
||
|
||
KeInitializeDpc(
|
||
&extension->TotalImmediateTimeoutDpc,
|
||
SerialTimeoutImmediate,
|
||
extension
|
||
);
|
||
|
||
KeInitializeDpc(
|
||
&extension->CommWaitDpc,
|
||
SerialCompleteWait,
|
||
extension
|
||
);
|
||
|
||
KeInitializeDpc(
|
||
&extension->XoffCountTimeoutDpc,
|
||
SerialTimeoutXoff,
|
||
extension
|
||
);
|
||
|
||
KeInitializeDpc(
|
||
&extension->XoffCountCompleteDpc,
|
||
SerialCompleteXoff,
|
||
extension
|
||
);
|
||
|
||
KeInitializeDpc(
|
||
&extension->StartTimerLowerRTSDpc,
|
||
SerialStartTimerLowerRTS,
|
||
extension
|
||
);
|
||
|
||
KeInitializeDpc(
|
||
&extension->PerhapsLowerRTSDpc,
|
||
SerialInvokePerhapsLowerRTS,
|
||
extension
|
||
);
|
||
|
||
if (!((ConfigData->ClockRate == 1843200) ||
|
||
(ConfigData->ClockRate == 3072000) ||
|
||
(ConfigData->ClockRate == 4233600) ||
|
||
(ConfigData->ClockRate == 8000000))) {
|
||
|
||
SerialLogError(
|
||
extension->DeviceObject->DriverObject,
|
||
extension->DeviceObject,
|
||
ConfigData->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
6,
|
||
STATUS_SUCCESS,
|
||
SERIAL_UNSUPPORTED_CLOCK_RATE,
|
||
ConfigData->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
ConfigData->SymbolicLinkName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Invalid clock rate specified for %wZ\n",
|
||
&ConfigData->NtNameForPort)
|
||
);
|
||
status = STATUS_SERIAL_NO_DEVICE_INITED;
|
||
goto ExtensionCleanup;
|
||
|
||
}
|
||
|
||
//
|
||
// Save the value of clock input to the part. We use this to calculate
|
||
// the divisor latch value. The value is in Hertz.
|
||
//
|
||
|
||
extension->ClockRate = ConfigData->ClockRate;
|
||
|
||
//
|
||
// Get a "back pointer" to the device object and 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.
|
||
//
|
||
|
||
extension->DeviceObject = deviceObject;
|
||
deviceObject->Flags |= DO_BUFFERED_IO;
|
||
|
||
//
|
||
// Map the memory for the control registers for the serial device
|
||
// into virtual memory.
|
||
//
|
||
|
||
extension->Controller = SerialGetMappedAddress(
|
||
ConfigData->InterfaceType,
|
||
ConfigData->BusNumber,
|
||
ConfigData->Controller,
|
||
ConfigData->SpanOfController,
|
||
(BOOLEAN)ConfigData->AddressSpace,
|
||
&extension->UnMapRegisters
|
||
);
|
||
|
||
|
||
if (!extension->Controller) {
|
||
|
||
SerialLogError(
|
||
extension->DeviceObject->DriverObject,
|
||
extension->DeviceObject,
|
||
ConfigData->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
7,
|
||
STATUS_SUCCESS,
|
||
SERIAL_REGISTERS_NOT_MAPPED,
|
||
ConfigData->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
ConfigData->SymbolicLinkName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Could not map memory for device registers for %wZ\n",
|
||
&ConfigData->NtNameForPort)
|
||
);
|
||
extension->UnMapRegisters = FALSE;
|
||
status = STATUS_NONE_MAPPED;
|
||
goto ExtensionCleanup;
|
||
|
||
}
|
||
|
||
extension->AddressSpace = ConfigData->AddressSpace;
|
||
extension->OriginalController = ConfigData->Controller;
|
||
extension->SpanOfController = ConfigData->SpanOfController;
|
||
|
||
//
|
||
// if we were requested to map the interrupt status do so.
|
||
//
|
||
|
||
if (MapInterruptStatus) {
|
||
|
||
extension->InterruptStatus = SerialGetMappedAddress(
|
||
ConfigData->InterfaceType,
|
||
ConfigData->BusNumber,
|
||
ConfigData->InterruptStatus,
|
||
ConfigData->SpanOfInterruptStatus,
|
||
(BOOLEAN)ConfigData->AddressSpace,
|
||
&extension->UnMapStatus
|
||
);
|
||
|
||
|
||
if (!extension->InterruptStatus) {
|
||
|
||
SerialLogError(
|
||
extension->DeviceObject->DriverObject,
|
||
extension->DeviceObject,
|
||
ConfigData->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
8,
|
||
STATUS_SUCCESS,
|
||
SERIAL_REGISTERS_NOT_MAPPED,
|
||
ConfigData->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
ConfigData->SymbolicLinkName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Could not map memory for interrupt status for %wZ\n",
|
||
&ConfigData->NtNameForPort)
|
||
);
|
||
extension->UnMapRegisters = FALSE;
|
||
status = STATUS_NONE_MAPPED;
|
||
goto ExtensionCleanup;
|
||
|
||
}
|
||
|
||
extension->OriginalInterruptStatus = ConfigData->InterruptStatus;
|
||
extension->SpanOfInterruptStatus = ConfigData->SpanOfInterruptStatus;
|
||
|
||
}
|
||
|
||
|
||
if (ConfigData->PermitSystemWideShare) {
|
||
|
||
extension->InterruptShareable = TRUE;
|
||
|
||
} else {
|
||
|
||
extension->InterruptShareable = FALSE;
|
||
|
||
}
|
||
|
||
//
|
||
// Save off the interface type and the bus number.
|
||
//
|
||
|
||
extension->InterfaceType = ConfigData->InterfaceType;
|
||
extension->BusNumber = ConfigData->BusNumber;
|
||
|
||
//
|
||
// From the Hal, get the interrupt vector and level.
|
||
//
|
||
|
||
extension->InterruptMode = ConfigData->InterruptMode;
|
||
extension->OriginalIrql = ConfigData->OriginalIrql;
|
||
extension->OriginalVector = ConfigData->OriginalVector;
|
||
extension->Vector = HalGetInterruptVector(
|
||
ConfigData->InterfaceType,
|
||
ConfigData->BusNumber,
|
||
ConfigData->OriginalIrql,
|
||
ConfigData->OriginalVector,
|
||
&extension->Irql,
|
||
&extension->ProcessorAffinity
|
||
);
|
||
|
||
//
|
||
// If the user said to permit sharing within the device, propagate this
|
||
// through.
|
||
//
|
||
extension->PermitShare = ConfigData->PermitShare;
|
||
|
||
//
|
||
// Report it's resources. We do this now because we are just
|
||
// about to touch the resources for the first time.
|
||
//
|
||
|
||
SerialReportResourcesDevice(
|
||
extension,
|
||
&conflictDetected
|
||
);
|
||
|
||
if (conflictDetected) {
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Reporting resources for %wZ with extension %x\n"
|
||
"------- detected a conflict\n",
|
||
&extension->NtNameForPort,extension)
|
||
);
|
||
SerialLogError(
|
||
extension->DeviceObject->DriverObject,
|
||
extension->DeviceObject,
|
||
ConfigData->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
9,
|
||
STATUS_SUCCESS,
|
||
SERIAL_RESOURCE_CONFLICT,
|
||
ConfigData->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
ConfigData->SymbolicLinkName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// This status won't propagate far.
|
||
//
|
||
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto ExtensionCleanup;
|
||
|
||
}
|
||
|
||
reportedResources = TRUE;
|
||
|
||
//
|
||
// Before we test whether the port exists (which will enable the FIFO)
|
||
// convert the rx trigger value to what should be used in the register.
|
||
//
|
||
// If a bogus value was given - crank them down to 1.
|
||
//
|
||
|
||
switch (ConfigData->RxFIFO) {
|
||
|
||
case 1:
|
||
|
||
extension->RxFifoTrigger = SERIAL_1_BYTE_HIGH_WATER;
|
||
break;
|
||
|
||
case 4:
|
||
|
||
extension->RxFifoTrigger = SERIAL_4_BYTE_HIGH_WATER;
|
||
break;
|
||
|
||
case 8:
|
||
|
||
extension->RxFifoTrigger = SERIAL_8_BYTE_HIGH_WATER;
|
||
break;
|
||
|
||
case 14:
|
||
|
||
extension->RxFifoTrigger = SERIAL_14_BYTE_HIGH_WATER;
|
||
break;
|
||
|
||
default:
|
||
|
||
extension->RxFifoTrigger = SERIAL_1_BYTE_HIGH_WATER;
|
||
break;
|
||
|
||
}
|
||
|
||
if ((ConfigData->TxFIFO > 16) ||
|
||
(ConfigData->TxFIFO < 1)) {
|
||
|
||
extension->TxFifoAmount = 1;
|
||
|
||
} else {
|
||
|
||
extension->TxFifoAmount = ConfigData->TxFIFO;
|
||
|
||
}
|
||
|
||
if (!SerialDoesPortExist(
|
||
extension,
|
||
&ConfigData->SymbolicLinkName,
|
||
ConfigData->ForceFifoEnable,
|
||
ConfigData->LogFifo
|
||
)) {
|
||
|
||
//
|
||
// We couldn't verify that there was actually a
|
||
// port. No need to log an error as the port exist
|
||
// code will log exactly why.
|
||
//
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Does Port exist test failed for %wZ\n",
|
||
&ConfigData->NtNameForPort)
|
||
);
|
||
status = STATUS_NO_SUCH_DEVICE;
|
||
goto ExtensionCleanup;
|
||
|
||
}
|
||
|
||
//
|
||
// If the user requested that we disable the port, then
|
||
// do it now. Log the fact that the port has been disabled.
|
||
//
|
||
|
||
if (ConfigData->DisablePort) {
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: disabled port %wZ as requested in configuration\n",
|
||
&ConfigData->NtNameForPort)
|
||
);
|
||
status = STATUS_NO_SUCH_DEVICE;
|
||
SerialLogError(
|
||
extension->DeviceObject->DriverObject,
|
||
extension->DeviceObject,
|
||
ConfigData->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
57,
|
||
STATUS_SUCCESS,
|
||
SERIAL_DISABLED_PORT,
|
||
ConfigData->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
ConfigData->SymbolicLinkName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
goto ExtensionCleanup;
|
||
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Set up the default device control fields.
|
||
// Note that if the values are changed after
|
||
// the file is open, they do NOT revert back
|
||
// to the old value at file close.
|
||
//
|
||
|
||
extension->SpecialChars.XonChar = SERIAL_DEF_XON;
|
||
extension->SpecialChars.XoffChar = SERIAL_DEF_XOFF;
|
||
extension->HandFlow.ControlHandShake = SERIAL_DTR_CONTROL;
|
||
|
||
extension->HandFlow.FlowReplace = SERIAL_RTS_CONTROL;
|
||
|
||
//
|
||
// Default Line control protocol. 7E1
|
||
//
|
||
// Seven data bits.
|
||
// Even parity.
|
||
// 1 Stop bits.
|
||
//
|
||
|
||
extension->LineControl = SERIAL_7_DATA |
|
||
SERIAL_EVEN_PARITY |
|
||
SERIAL_NONE_PARITY;
|
||
|
||
extension->ValidDataMask = 0x7f;
|
||
extension->CurrentBaud = 1200;
|
||
|
||
|
||
//
|
||
// We set up the default xon/xoff limits.
|
||
//
|
||
|
||
extension->HandFlow.XoffLimit = extension->BufferSize >> 3;
|
||
extension->HandFlow.XonLimit = extension->BufferSize >> 1;
|
||
|
||
extension->BufferSizePt8 = ((3*(extension->BufferSize>>2))+
|
||
(extension->BufferSize>>4));
|
||
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: The default interrupt read buffer size is: %d\n"
|
||
"------ The XoffLimit is : %d\n"
|
||
"------ The XonLimit is : %d\n"
|
||
"------ The pt 8 size is : %d\n",
|
||
extension->BufferSize,
|
||
extension->HandFlow.XoffLimit,
|
||
extension->HandFlow.XonLimit,
|
||
extension->BufferSizePt8)
|
||
);
|
||
|
||
//
|
||
// Go through all the "named" baud rates to find out which ones
|
||
// can be supported with this port.
|
||
//
|
||
|
||
extension->SupportedBauds = SERIAL_BAUD_USER;
|
||
|
||
{
|
||
|
||
SHORT junk;
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)75,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_075;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)110,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_110;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)135,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_134_5;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)150,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_150;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)300,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_300;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)600,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_600;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)1200,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_1200;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)1800,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_1800;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)2400,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_2400;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)4800,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_4800;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)7200,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_7200;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)9600,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_9600;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)14400,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_14400;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)19200,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_19200;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)38400,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_38400;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)56000,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_56K;
|
||
|
||
}
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)57600,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_57600;
|
||
|
||
}
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)115200,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_115200;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
(LONG)128000,
|
||
&junk
|
||
))) {
|
||
|
||
extension->SupportedBauds |= SERIAL_BAUD_128K;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Mark this device as not being opened by anyone. We keep a
|
||
// variable around so that spurious interrupts are easily
|
||
// dismissed by the ISR.
|
||
//
|
||
|
||
extension->DeviceIsOpened = FALSE;
|
||
|
||
//
|
||
// This call will set up the naming necessary for
|
||
// external applications to get to the driver. It
|
||
// will also set up the device map.
|
||
//
|
||
|
||
|
||
extension->ObjectDirectory = ConfigData->ObjectDirectory;
|
||
extension->NtNameForPort = ConfigData->NtNameForPort;
|
||
extension->SymbolicLinkName = ConfigData->SymbolicLinkName;
|
||
SerialSetupExternalNaming(extension);
|
||
|
||
//
|
||
// Store values into the extension for interval timing.
|
||
//
|
||
|
||
//
|
||
// If the interval timer is less than a second then come
|
||
// in with a short "polling" loop.
|
||
//
|
||
// For large (> then 2 seconds) use a 1 second poller.
|
||
//
|
||
|
||
extension->ShortIntervalAmount.QuadPart = -1;
|
||
extension->LongIntervalAmount.QuadPart = -10000000;
|
||
extension->CutOverAmount.QuadPart = 200000000;
|
||
|
||
|
||
//
|
||
// Pass pack the extension to the caller.
|
||
//
|
||
|
||
*DeviceExtension = extension;
|
||
|
||
//
|
||
// Common error path cleanup. If the status is
|
||
// bad, get rid of the device extension, device object
|
||
// and any memory associated with it.
|
||
//
|
||
|
||
ExtensionCleanup: ;
|
||
|
||
ExFreePool(ConfigData);
|
||
|
||
if (NT_ERROR(status)) {
|
||
|
||
if (extension) {
|
||
|
||
if (reportedResources) {
|
||
|
||
SerialUnReportResourcesDevice(extension);
|
||
|
||
}
|
||
|
||
SerialCleanupDevice(extension);
|
||
IoDeleteDevice(deviceObject);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return status;
|
||
|
||
}
|
||
|
||
BOOLEAN
|
||
SerialDoesPortExist(
|
||
IN PSERIAL_DEVICE_EXTENSION Extension,
|
||
IN PUNICODE_STRING InsertString,
|
||
IN ULONG ForceFifo,
|
||
IN ULONG LogFifo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine examines several of what might be the serial device
|
||
registers. It ensures that the bits that should be zero are zero.
|
||
|
||
In addition, this routine will determine if the device supports
|
||
fifo's. If it does it will enable the fifo's and turn on a boolean
|
||
in the extension that indicates the fifo's presence.
|
||
|
||
NOTE: If there is indeed a serial port at the address specified
|
||
it will absolutely have interrupts inhibited upon return
|
||
from this routine.
|
||
|
||
NOTE: Since this routine should be called fairly early in
|
||
the device driver initialization, the only element
|
||
that needs to be filled in is the base register address.
|
||
|
||
NOTE: These tests all assume that this code is the only
|
||
code that is looking at these ports or this memory.
|
||
|
||
This is a not to unreasonable assumption even on
|
||
multiprocessor systems.
|
||
|
||
Arguments:
|
||
|
||
Extension - A pointer to a serial device extension.
|
||
InsertString - String to place in an error log entry.
|
||
ForceFifo - !0 forces the fifo to be left on if found.
|
||
LogFifo - !0 forces a log message if fifo found.
|
||
|
||
Return Value:
|
||
|
||
Will return true if the port really exists, otherwise it
|
||
will return false.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
|
||
UCHAR regContents;
|
||
BOOLEAN returnValue = TRUE;
|
||
UCHAR oldIERContents;
|
||
UCHAR oldLCRContents;
|
||
USHORT value1;
|
||
USHORT value2;
|
||
KIRQL oldIrql;
|
||
|
||
|
||
//
|
||
// Save of the line control.
|
||
//
|
||
|
||
oldLCRContents = READ_LINE_CONTROL(Extension->Controller);
|
||
|
||
//
|
||
// Make sure that we are *aren't* accessing the divsior latch.
|
||
//
|
||
|
||
WRITE_LINE_CONTROL(
|
||
Extension->Controller,
|
||
(UCHAR)(oldLCRContents & ~SERIAL_LCR_DLAB)
|
||
);
|
||
|
||
oldIERContents = READ_INTERRUPT_ENABLE(Extension->Controller);
|
||
|
||
//
|
||
// Go up to power level for a very short time to prevent
|
||
// any interrupts from this device from coming in.
|
||
//
|
||
|
||
KeRaiseIrql(
|
||
POWER_LEVEL,
|
||
&oldIrql
|
||
);
|
||
|
||
WRITE_INTERRUPT_ENABLE(
|
||
Extension->Controller,
|
||
0x0f
|
||
);
|
||
|
||
value1 = READ_INTERRUPT_ENABLE(Extension->Controller);
|
||
value1 = value1 << 8;
|
||
value1 |= READ_RECEIVE_BUFFER(Extension->Controller);
|
||
|
||
READ_DIVISOR_LATCH(
|
||
Extension->Controller,
|
||
&value2
|
||
);
|
||
|
||
WRITE_LINE_CONTROL(
|
||
Extension->Controller,
|
||
oldLCRContents
|
||
);
|
||
|
||
//
|
||
// Put the ier back to where it was before. If we are on a
|
||
// level sensitive port this should prevent the interrupts
|
||
// from coming in. If we are on a latched, we don't care
|
||
// cause the interrupts generated will just get dropped.
|
||
//
|
||
|
||
WRITE_INTERRUPT_ENABLE(
|
||
Extension->Controller,
|
||
oldIERContents
|
||
);
|
||
|
||
KeLowerIrql(oldIrql);
|
||
|
||
if (value1 == value2) {
|
||
|
||
SerialLogError(
|
||
Extension->DeviceObject->DriverObject,
|
||
Extension->DeviceObject,
|
||
Extension->OriginalController,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
62,
|
||
STATUS_SUCCESS,
|
||
SERIAL_DLAB_INVALID,
|
||
InsertString->Length+sizeof(WCHAR),
|
||
InsertString->Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
returnValue = FALSE;
|
||
goto AllDone;
|
||
|
||
}
|
||
|
||
AllDone: ;
|
||
|
||
|
||
//
|
||
// If we think that there is a serial device then we determine
|
||
// if a fifo is present.
|
||
//
|
||
|
||
if (returnValue) {
|
||
|
||
//
|
||
// Well, we think it's a serial device. Absolutely
|
||
// positively, prevent interrupts from occuring.
|
||
//
|
||
// We disable all the interrupt enable bits, and
|
||
// push down all the lines in the modem control
|
||
// We only needed to push down OUT2 which in
|
||
// PC's must also be enabled to get an interrupt.
|
||
//
|
||
|
||
DISABLE_ALL_INTERRUPTS(Extension->Controller);
|
||
|
||
if (Extension->Jensen) {
|
||
|
||
WRITE_MODEM_CONTROL(
|
||
Extension->Controller,
|
||
(UCHAR)SERIAL_MCR_OUT2
|
||
);
|
||
|
||
} else {
|
||
|
||
WRITE_MODEM_CONTROL(
|
||
Extension->Controller,
|
||
(UCHAR)0
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// See if this is a 16550. We do this by writing to
|
||
// what would be the fifo control register with a bit
|
||
// pattern that tells the device to enable fifo's.
|
||
// We then read the iterrupt Id register to see if the
|
||
// bit pattern is present that identifies the 16550.
|
||
//
|
||
|
||
WRITE_FIFO_CONTROL(
|
||
Extension->Controller,
|
||
SERIAL_FCR_ENABLE
|
||
);
|
||
|
||
regContents = READ_INTERRUPT_ID_REG(Extension->Controller);
|
||
|
||
if (regContents & SERIAL_IIR_FIFOS_ENABLED) {
|
||
|
||
//
|
||
// Save off that the device supports fifos.
|
||
//
|
||
|
||
Extension->FifoPresent = TRUE;
|
||
|
||
//
|
||
// There is a fine new "super" IO chip out there that
|
||
// will get stuck with a line status interrupt if you
|
||
// attempt to clear the fifo and enable it at the same
|
||
// time if data is present. The best workaround seems
|
||
// to be that you should turn off the fifo read a single
|
||
// byte, and then re-enable the fifo.
|
||
//
|
||
|
||
WRITE_FIFO_CONTROL(
|
||
Extension->Controller,
|
||
(UCHAR)0
|
||
);
|
||
|
||
READ_RECEIVE_BUFFER(Extension->Controller);
|
||
|
||
//
|
||
// There are fifos on this card. Set the value of the
|
||
// receive fifo to interrupt when 4 characters are present.
|
||
//
|
||
|
||
WRITE_FIFO_CONTROL(
|
||
Extension->Controller,
|
||
(UCHAR)(SERIAL_FCR_ENABLE | Extension->RxFifoTrigger |
|
||
SERIAL_FCR_RCVR_RESET | SERIAL_FCR_TXMT_RESET)
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// The !Extension->FifoPresent is included in the test so that
|
||
// broken chips like the WinBond will still work after we test
|
||
// for the fifo.
|
||
//
|
||
|
||
if (!ForceFifo || !Extension->FifoPresent) {
|
||
|
||
Extension->FifoPresent = FALSE;
|
||
WRITE_FIFO_CONTROL(
|
||
Extension->Controller,
|
||
(UCHAR)0
|
||
);
|
||
|
||
}
|
||
|
||
if (Extension->FifoPresent) {
|
||
|
||
if (LogFifo) {
|
||
|
||
SerialLogError(
|
||
Extension->DeviceObject->DriverObject,
|
||
Extension->DeviceObject,
|
||
Extension->OriginalController,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
15,
|
||
STATUS_SUCCESS,
|
||
SERIAL_FIFO_PRESENT,
|
||
InsertString->Length+sizeof(WCHAR),
|
||
InsertString->Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
}
|
||
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: Fifo's detected at port address: %x\n",
|
||
Extension->Controller)
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// In case we are dealing with a bitmasked multiportcard,
|
||
// that has the mask register enabled, enable all
|
||
// interrupts.
|
||
//
|
||
|
||
if (Extension->InterruptStatus) {
|
||
|
||
WRITE_PORT_UCHAR(
|
||
Extension->InterruptStatus,
|
||
(UCHAR)0xff
|
||
);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return returnValue;
|
||
|
||
}
|
||
|
||
BOOLEAN
|
||
SerialReset(
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This places the hardware in a standard configuration.
|
||
|
||
NOTE: This assumes that it is called at interrupt level.
|
||
|
||
|
||
Arguments:
|
||
|
||
Context - The device extension for serial device
|
||
being managed.
|
||
|
||
Return Value:
|
||
|
||
Always FALSE.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSERIAL_DEVICE_EXTENSION extension = Context;
|
||
UCHAR regContents;
|
||
UCHAR oldModemControl;
|
||
ULONG i;
|
||
|
||
//
|
||
// Adjust the out2 bit.
|
||
// This will also prevent any interrupts from occuring.
|
||
//
|
||
|
||
oldModemControl = READ_MODEM_CONTROL(extension->Controller);
|
||
|
||
if (extension->Jensen) {
|
||
|
||
WRITE_MODEM_CONTROL(
|
||
extension->Controller,
|
||
(UCHAR)(oldModemControl | SERIAL_MCR_OUT2)
|
||
);
|
||
|
||
} else {
|
||
|
||
|
||
WRITE_MODEM_CONTROL(
|
||
extension->Controller,
|
||
(UCHAR)(oldModemControl & ~SERIAL_MCR_OUT2)
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Reset the fifo's if there are any.
|
||
//
|
||
|
||
if (extension->FifoPresent) {
|
||
|
||
|
||
//
|
||
// There is a fine new "super" IO chip out there that
|
||
// will get stuck with a line status interrupt if you
|
||
// attempt to clear the fifo and enable it at the same
|
||
// time if data is present. The best workaround seems
|
||
// to be that you should turn off the fifo read a single
|
||
// byte, and then re-enable the fifo.
|
||
//
|
||
|
||
WRITE_FIFO_CONTROL(
|
||
extension->Controller,
|
||
(UCHAR)0
|
||
);
|
||
|
||
READ_RECEIVE_BUFFER(extension->Controller);
|
||
|
||
WRITE_FIFO_CONTROL(
|
||
extension->Controller,
|
||
(UCHAR)(SERIAL_FCR_ENABLE | extension->RxFifoTrigger |
|
||
SERIAL_FCR_RCVR_RESET | SERIAL_FCR_TXMT_RESET)
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Make sure that the line control set up correct.
|
||
//
|
||
// 1) Make sure that the Divisor latch select is set
|
||
// up to select the transmit and receive register.
|
||
//
|
||
// 2) Make sure that we aren't in a break state.
|
||
//
|
||
|
||
regContents = READ_LINE_CONTROL(extension->Controller);
|
||
regContents &= ~(SERIAL_LCR_DLAB | SERIAL_LCR_BREAK);
|
||
|
||
WRITE_LINE_CONTROL(
|
||
extension->Controller,
|
||
regContents
|
||
);
|
||
|
||
//
|
||
// Read the receive buffer until the line status is
|
||
// clear. (Actually give up after a 5 reads.)
|
||
//
|
||
|
||
for (i = 0;
|
||
i < 5;
|
||
i++
|
||
) {
|
||
|
||
READ_RECEIVE_BUFFER(extension->Controller);
|
||
if (!(READ_LINE_STATUS(extension->Controller) & 1)) {
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Read the modem status until the low 4 bits are
|
||
// clear. (Actually give up after a 5 reads.)
|
||
//
|
||
|
||
for (i = 0;
|
||
i < 1000;
|
||
i++
|
||
) {
|
||
|
||
if (!(READ_MODEM_STATUS(extension->Controller) & 0x0f)) {
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Now we set the line control, modem control, and the
|
||
// baud to what they should be.
|
||
//
|
||
|
||
SerialSetLineControl(extension);
|
||
|
||
SerialSetupNewHandFlow(
|
||
extension,
|
||
&extension->HandFlow
|
||
);
|
||
|
||
SerialHandleModemUpdate(
|
||
extension,
|
||
FALSE
|
||
);
|
||
|
||
{
|
||
SHORT appropriateDivisor;
|
||
SERIAL_IOCTL_SYNC s;
|
||
|
||
SerialGetDivisorFromBaud(
|
||
extension->ClockRate,
|
||
extension->CurrentBaud,
|
||
&appropriateDivisor
|
||
);
|
||
s.Extension = extension;
|
||
s.Data = (PVOID)appropriateDivisor;
|
||
SerialSetBaud(&s);
|
||
}
|
||
|
||
//
|
||
// Enable which interrupts we want to receive.
|
||
//
|
||
// NOTE NOTE: This does not actually let interrupts
|
||
// occur. We must still raise the OUT2 bit in the
|
||
// modem control register. We will do that on open.
|
||
//
|
||
|
||
ENABLE_ALL_INTERRUPTS(extension->Controller);
|
||
|
||
//
|
||
// Read the interrupt id register until the low bit is
|
||
// set. (Actually give up after a 5 reads.)
|
||
//
|
||
|
||
for (i = 0;
|
||
i < 5;
|
||
i++
|
||
) {
|
||
|
||
if (READ_INTERRUPT_ID_REG(extension->Controller) & 0x01) {
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Now we know that nothing could be transmitting at this point
|
||
// so we set the HoldingEmpty indicator.
|
||
//
|
||
|
||
extension->HoldingEmpty = TRUE;
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
NTSTATUS
|
||
SerialGetDivisorFromBaud(
|
||
IN ULONG ClockRate,
|
||
IN LONG DesiredBaud,
|
||
OUT PSHORT AppropriateDivisor
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will determine a divisor based on an unvalidated
|
||
baud rate.
|
||
|
||
Arguments:
|
||
|
||
ClockRate - The clock input to the controller.
|
||
|
||
DesiredBaud - The baud rate for whose divisor we seek.
|
||
|
||
AppropriateDivisor - Given that the DesiredBaud is valid, the
|
||
LONG pointed to by this parameter will be set to the appropriate
|
||
value. NOTE: The long is undefined if the DesiredBaud is not
|
||
supported.
|
||
|
||
Return Value:
|
||
|
||
This function will return STATUS_SUCCESS if the baud is supported.
|
||
If the value is not supported it will return a status such that
|
||
NT_ERROR(Status) == FALSE.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
SHORT calculatedDivisor;
|
||
ULONG denominator;
|
||
ULONG remainder;
|
||
|
||
//
|
||
// Allow up to a 1 percent error
|
||
//
|
||
|
||
ULONG maxRemain18 = 18432;
|
||
ULONG maxRemain30 = 30720;
|
||
ULONG maxRemain42 = 42336;
|
||
ULONG maxRemain80 = 80000;
|
||
ULONG maxRemain;
|
||
|
||
//
|
||
// Reject any non-positive bauds.
|
||
//
|
||
|
||
denominator = DesiredBaud*(ULONG)16;
|
||
|
||
if (DesiredBaud <= 0) {
|
||
|
||
*AppropriateDivisor = -1;
|
||
|
||
} else if ((LONG)denominator < DesiredBaud) {
|
||
|
||
//
|
||
// If the desired baud was so huge that it cause the denominator
|
||
// calculation to wrap, don't support it.
|
||
//
|
||
|
||
*AppropriateDivisor = -1;
|
||
|
||
} else {
|
||
|
||
if (ClockRate == 1843200) {
|
||
maxRemain = maxRemain18;
|
||
} else if (ClockRate == 3072000) {
|
||
maxRemain = maxRemain30;
|
||
} else if (ClockRate == 4233600) {
|
||
maxRemain = maxRemain42;
|
||
} else {
|
||
maxRemain = maxRemain80;
|
||
}
|
||
|
||
calculatedDivisor = (SHORT)(ClockRate / denominator);
|
||
remainder = ClockRate % denominator;
|
||
|
||
//
|
||
// Round up.
|
||
//
|
||
|
||
if (((remainder*2) > ClockRate) && (DesiredBaud != 110)) {
|
||
|
||
calculatedDivisor++;
|
||
}
|
||
|
||
|
||
//
|
||
// Only let the remainder calculations effect us if
|
||
// the baud rate is > 9600.
|
||
//
|
||
|
||
if (DesiredBaud >= 9600) {
|
||
|
||
//
|
||
// If the remainder is less than the maximum remainder (wrt
|
||
// the ClockRate) or the remainder + the maximum remainder is
|
||
// greater than or equal to the ClockRate then assume that the
|
||
// baud is ok.
|
||
//
|
||
|
||
if ((remainder >= maxRemain) && ((remainder+maxRemain) < ClockRate)) {
|
||
calculatedDivisor = -1;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Don't support a baud that causes the denominator to
|
||
// be larger than the clock.
|
||
//
|
||
|
||
if (denominator > ClockRate) {
|
||
|
||
calculatedDivisor = -1;
|
||
|
||
}
|
||
|
||
//
|
||
// Ok, Now do some special casing so that things can actually continue
|
||
// working on all platforms.
|
||
//
|
||
|
||
if (ClockRate == 1843200) {
|
||
|
||
if (DesiredBaud == 56000) {
|
||
calculatedDivisor = 2;
|
||
}
|
||
|
||
} else if (ClockRate == 3072000) {
|
||
|
||
if (DesiredBaud == 14400) {
|
||
calculatedDivisor = 13;
|
||
}
|
||
|
||
} else if (ClockRate == 4233600) {
|
||
|
||
if (DesiredBaud == 9600) {
|
||
calculatedDivisor = 28;
|
||
} else if (DesiredBaud == 14400) {
|
||
calculatedDivisor = 18;
|
||
} else if (DesiredBaud == 19200) {
|
||
calculatedDivisor = 14;
|
||
} else if (DesiredBaud == 38400) {
|
||
calculatedDivisor = 7;
|
||
} else if (DesiredBaud == 56000) {
|
||
calculatedDivisor = 5;
|
||
}
|
||
|
||
} else if (ClockRate == 8000000) {
|
||
|
||
if (DesiredBaud == 14400) {
|
||
calculatedDivisor = 35;
|
||
} else if (DesiredBaud == 56000) {
|
||
calculatedDivisor = 9;
|
||
}
|
||
|
||
}
|
||
|
||
*AppropriateDivisor = calculatedDivisor;
|
||
|
||
}
|
||
|
||
|
||
if (*AppropriateDivisor == -1) {
|
||
|
||
status = STATUS_INVALID_PARAMETER;
|
||
|
||
}
|
||
|
||
return status;
|
||
|
||
}
|
||
|
||
VOID
|
||
SerialUnload(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine cleans up all of the memory associated with
|
||
any of the devices belonging to the driver. It will
|
||
loop through the device list.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Pointer to the driver object controling all of the
|
||
devices.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PDEVICE_OBJECT currentDevice = DriverObject->DeviceObject;
|
||
PVOID lockPtr;
|
||
|
||
lockPtr = MmLockPagableCodeSection(SerialUnload);
|
||
|
||
SerialDump(
|
||
SERDIAG3,
|
||
("SERIAL: In SerialUnload\n")
|
||
);
|
||
|
||
while (currentDevice) {
|
||
|
||
PSERIAL_DEVICE_EXTENSION extension = currentDevice->DeviceExtension;
|
||
|
||
//
|
||
// Look for a device that actually has an isr.
|
||
// if we find one then that is a "root" controller.
|
||
//
|
||
|
||
if (extension->OurIsr) {
|
||
|
||
SerialDump(
|
||
SERDIAG5,
|
||
("SERIAL: About to do a propagate delete on\n"
|
||
"------- extension: %x for port %wZ\n",
|
||
extension,&extension->DeviceName)
|
||
);
|
||
SerialPropagateDeleteSharers(
|
||
extension,
|
||
&IoGetConfigurationInformation()->SerialCount,
|
||
NULL
|
||
);
|
||
|
||
currentDevice = DriverObject->DeviceObject;
|
||
|
||
} else {
|
||
|
||
currentDevice = currentDevice->NextDevice;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
MmUnlockPagableImageSection(lockPtr);
|
||
|
||
}
|
||
|
||
ULONG
|
||
SerialCheckForShare(
|
||
IN PUNICODE_STRING PathName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks the firmware registry location, passed as an
|
||
argument, to see if system wide sharing of the interrupt is supposed
|
||
to be honored for this device.
|
||
|
||
Arguments:
|
||
|
||
PathName - registry path location to check.
|
||
|
||
Return Value:
|
||
|
||
TRUE - if the registry value is present and non-zero
|
||
FALSE otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG zero = 0;
|
||
ULONG share;
|
||
NTSTATUS status;
|
||
PRTL_QUERY_REGISTRY_TABLE parms;
|
||
PWSTR name;
|
||
|
||
//
|
||
// Setup the query for sharing interrupt
|
||
//
|
||
|
||
parms = ExAllocatePool(
|
||
NonPagedPool,
|
||
sizeof(RTL_QUERY_REGISTRY_TABLE)*2
|
||
);
|
||
|
||
if (!parms) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Allocate sufficient space for null terminating name
|
||
//
|
||
|
||
name = ExAllocatePool(
|
||
NonPagedPool,
|
||
PathName->Length+4
|
||
);
|
||
|
||
if (!name) {
|
||
ExFreePool(parms);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Set all structures to zeros
|
||
//
|
||
|
||
RtlZeroMemory(
|
||
parms,
|
||
sizeof(RTL_QUERY_REGISTRY_TABLE)*2
|
||
);
|
||
RtlZeroMemory(
|
||
name,
|
||
PathName->Length + 4
|
||
);
|
||
|
||
//
|
||
// Initialize query structure
|
||
//
|
||
|
||
parms[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parms[0].Name = L"Share System Interrupt";
|
||
parms[0].EntryContext = &share;
|
||
parms[0].DefaultType = REG_DWORD;
|
||
parms[0].DefaultData = &zero;
|
||
parms[0].DefaultLength = sizeof(ULONG);
|
||
|
||
//
|
||
// Set up registry path name. It will now be null terminated.
|
||
//
|
||
|
||
RtlCopyMemory(
|
||
name,
|
||
PathName->Buffer,
|
||
PathName->Length
|
||
);
|
||
|
||
//
|
||
// Perform the query
|
||
//
|
||
|
||
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
|
||
name,
|
||
parms,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
ExFreePool(parms);
|
||
ExFreePool(name);
|
||
|
||
return share ? (ULONG) TRUE : (ULONG) FALSE;
|
||
}
|
||
|
||
VOID
|
||
SerialCleanupDevice(
|
||
IN PSERIAL_DEVICE_EXTENSION Extension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will deallocate all of the memory used for
|
||
a particular device. It will also disconnect any resources
|
||
if need be.
|
||
|
||
Arguments:
|
||
|
||
Extension - Pointer to the device extension which is getting
|
||
rid of all it's resources.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
SerialDump(
|
||
SERDIAG3,
|
||
("SERIAL: in SerialCleanup for extension: %x\n",Extension)
|
||
);
|
||
|
||
if (Extension) {
|
||
|
||
//
|
||
// Disconnect the interrupt object first so that some spurious
|
||
// interrupt doesn't cause us to dereference some memory we've
|
||
// already given up.
|
||
//
|
||
|
||
if (Extension->Interrupt) {
|
||
|
||
SerialDump(
|
||
SERDIAG5,
|
||
("SERIAL: Extension has interrupt %x\n",Extension)
|
||
);
|
||
IoDisconnectInterrupt(Extension->Interrupt);
|
||
|
||
}
|
||
|
||
KeCancelTimer(&Extension->ReadRequestTotalTimer);
|
||
KeCancelTimer(&Extension->ReadRequestIntervalTimer);
|
||
KeCancelTimer(&Extension->WriteRequestTotalTimer);
|
||
KeCancelTimer(&Extension->ImmediateTotalTimer);
|
||
KeCancelTimer(&Extension->XoffCountTimer);
|
||
KeCancelTimer(&Extension->LowerRTSTimer);
|
||
KeRemoveQueueDpc(&Extension->CompleteWriteDpc);
|
||
KeRemoveQueueDpc(&Extension->CompleteReadDpc);
|
||
KeRemoveQueueDpc(&Extension->TotalReadTimeoutDpc);
|
||
KeRemoveQueueDpc(&Extension->IntervalReadTimeoutDpc);
|
||
KeRemoveQueueDpc(&Extension->TotalWriteTimeoutDpc);
|
||
KeRemoveQueueDpc(&Extension->CommErrorDpc);
|
||
KeRemoveQueueDpc(&Extension->CompleteImmediateDpc);
|
||
KeRemoveQueueDpc(&Extension->TotalImmediateTimeoutDpc);
|
||
KeRemoveQueueDpc(&Extension->CommWaitDpc);
|
||
KeRemoveQueueDpc(&Extension->XoffCountTimeoutDpc);
|
||
KeRemoveQueueDpc(&Extension->XoffCountCompleteDpc);
|
||
KeRemoveQueueDpc(&Extension->StartTimerLowerRTSDpc);
|
||
KeRemoveQueueDpc(&Extension->PerhapsLowerRTSDpc);
|
||
|
||
//
|
||
// Get rid of all external naming as well as removing
|
||
// the device map entry.
|
||
//
|
||
|
||
SerialCleanupExternalNaming(Extension);
|
||
|
||
//
|
||
// Delallocate the memory for the various names.
|
||
// NOTE: If we have an extension - Then we must
|
||
// have a device name stored away. Which is *not*
|
||
// true for the other names.
|
||
//
|
||
|
||
ExFreePool(Extension->DeviceName.Buffer);
|
||
|
||
if (Extension->ObjectDirectory.Buffer) {
|
||
|
||
ExFreePool(Extension->ObjectDirectory.Buffer);
|
||
|
||
}
|
||
|
||
if (Extension->NtNameForPort.Buffer) {
|
||
|
||
ExFreePool(Extension->NtNameForPort.Buffer);
|
||
|
||
}
|
||
|
||
if (Extension->SymbolicLinkName.Buffer) {
|
||
|
||
ExFreePool(Extension->SymbolicLinkName.Buffer);
|
||
|
||
}
|
||
|
||
//
|
||
// If necessary, unmap the device registers.
|
||
//
|
||
|
||
if (Extension->UnMapRegisters) {
|
||
|
||
MmUnmapIoSpace(
|
||
Extension->Controller,
|
||
Extension->SpanOfController
|
||
);
|
||
|
||
}
|
||
|
||
if (Extension->UnMapStatus) {
|
||
|
||
MmUnmapIoSpace(
|
||
Extension->InterruptStatus,
|
||
Extension->SpanOfInterruptStatus
|
||
);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
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
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
*((BOOLEAN *)Context) = TRUE;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// This structure is only used to communicate between the
|
||
// code that queries what the firmware found and the code
|
||
// that is calling the quering of the firmware data.
|
||
//
|
||
typedef struct SERIAL_FIRMWARE_DATA {
|
||
PDRIVER_OBJECT DriverObject;
|
||
ULONG ControllersFound;
|
||
ULONG ForceFifoEnableDefault;
|
||
ULONG RxFIFODefault;
|
||
ULONG TxFIFODefault;
|
||
ULONG PermitShareDefault;
|
||
ULONG PermitSystemWideShare;
|
||
ULONG LogFifoDefault;
|
||
UNICODE_STRING Directory;
|
||
UNICODE_STRING NtNameSuffix;
|
||
UNICODE_STRING DirectorySymbolicName;
|
||
LIST_ENTRY ConfigList;
|
||
} SERIAL_FIRMWARE_DATA,*PSERIAL_FIRMWARE_DATA;
|
||
|
||
|
||
NTSTATUS
|
||
SerialConfigCallBack(
|
||
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 used to acquire all of the configuration
|
||
information for each serial controller found by the firmware
|
||
|
||
Arguments:
|
||
|
||
Context - Pointer to the list head of the list of configuration
|
||
records that we are building up.
|
||
|
||
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 - Should always be SerialController.
|
||
|
||
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 - Undefined for this call.
|
||
|
||
PeripheralNumber - Undefined for this call.
|
||
|
||
PeripheralInformation - Undefined for this call.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if everything went ok, or STATUS_INSUFFICIENT_RESOURCES
|
||
if it couldn't map the base csr or acquire the device object, or
|
||
all of the resource information couldn't be acquired.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// So we don't have to typecast the context.
|
||
//
|
||
PSERIAL_FIRMWARE_DATA config = Context;
|
||
|
||
//
|
||
// Pointer to the configuration stuff for this controller.
|
||
//
|
||
PCONFIG_DATA controller;
|
||
|
||
//
|
||
// We use the following two variables to determine if
|
||
// we have a pointer peripheral.
|
||
//
|
||
CONFIGURATION_TYPE pointer = PointerPeripheral;
|
||
BOOLEAN foundPointer = FALSE;
|
||
|
||
//
|
||
// Two booleans to help us determine that we got enough configuration
|
||
// data.
|
||
//
|
||
BOOLEAN foundPort = FALSE;
|
||
BOOLEAN foundInterrupt = FALSE;
|
||
|
||
//
|
||
// Simple iteration variable.
|
||
//
|
||
ULONG i;
|
||
|
||
PCM_FULL_RESOURCE_DESCRIPTOR controllerData;
|
||
|
||
ASSERT(ControllerType == SerialController);
|
||
|
||
config->ControllersFound++;
|
||
|
||
//
|
||
// Bail out if some fool wrote a hal and passed me data with no length.
|
||
//
|
||
|
||
if (!ControllerInformation[IoQueryDeviceConfigurationData]->DataLength) {
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
controllerData =
|
||
(PCM_FULL_RESOURCE_DESCRIPTOR)
|
||
(((PUCHAR)ControllerInformation[IoQueryDeviceConfigurationData]) +
|
||
ControllerInformation[IoQueryDeviceConfigurationData]->DataOffset);
|
||
|
||
//
|
||
// First things first. Call up IoQueryDeviceDescription
|
||
// again to see if there is a pointer peripheral. If there
|
||
// is then we simply ignore this controller.
|
||
//
|
||
|
||
IoQueryDeviceDescription(
|
||
&BusType,
|
||
&BusNumber,
|
||
&ControllerType,
|
||
&ControllerNumber,
|
||
&pointer,
|
||
NULL,
|
||
SerialItemCallBack,
|
||
&foundPointer
|
||
);
|
||
|
||
if (foundPointer) {
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
//
|
||
// Allocate the memory for the controller config data out of paged pool
|
||
// since we will only be accessing it at initialization time.
|
||
//
|
||
|
||
controller = ExAllocatePool(
|
||
PagedPool,
|
||
sizeof(CONFIG_DATA)
|
||
);
|
||
|
||
if (!controller) {
|
||
|
||
SerialLogError(
|
||
config->DriverObject,
|
||
NULL,
|
||
SerialPhysicalZero,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
16,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't allocate memory for the configuration data\n"
|
||
"------ for firmware data\n")
|
||
);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
}
|
||
|
||
RtlZeroMemory(
|
||
controller,
|
||
sizeof(CONFIG_DATA)
|
||
);
|
||
InitializeListHead(&controller->ConfigList);
|
||
InitializeListHead(&controller->SameInterrupt);
|
||
InitializeListHead(&controller->SameInterruptStatus);
|
||
|
||
controller->InterfaceType = BusType;
|
||
controller->BusNumber = BusNumber;
|
||
|
||
//
|
||
// Stick in the default fifo enable an rx trigger for the firmware
|
||
// found comm ports.
|
||
//
|
||
|
||
controller->ForceFifoEnable = config->ForceFifoEnableDefault;
|
||
controller->RxFIFO = config->RxFIFODefault;
|
||
controller->TxFIFO = config->TxFIFODefault;
|
||
controller->PermitShare = config->PermitShareDefault;
|
||
controller->LogFifo = config->LogFifoDefault;
|
||
if (controller->InterfaceType == MicroChannel) {
|
||
controller->PermitSystemWideShare = TRUE;
|
||
} else {
|
||
|
||
controller->PermitSystemWideShare = config->PermitSystemWideShare;
|
||
|
||
if (SerialCheckForShare(PathName)) {
|
||
|
||
controller->PermitSystemWideShare = TRUE;
|
||
|
||
}
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// We need to get the following information out of the partial
|
||
// resource descriptors.
|
||
//
|
||
// The irql and vector.
|
||
//
|
||
// The base address and span covered by the serial controllers
|
||
// registers.
|
||
//
|
||
// It is not defined how these appear in the partial resource
|
||
// lists, so we will just loop over all of them. If we find
|
||
// something we don't recognize, we drop that information on
|
||
// the floor. When we have finished going through all the
|
||
// partial information, we validate that we got the above
|
||
// two.
|
||
//
|
||
// The other additional piece of data that we seek is the
|
||
// baud rate input clock speed. Unless it is specified
|
||
// in the device specific portion of the resource list we
|
||
// will default it to 1.8432Mhz.
|
||
//
|
||
|
||
controller->ClockRate = 1843200;
|
||
for (
|
||
i = 0;
|
||
i < controllerData->PartialResourceList.Count;
|
||
i++
|
||
) {
|
||
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR partial =
|
||
&controllerData->PartialResourceList.PartialDescriptors[i];
|
||
|
||
switch (partial->Type) {
|
||
|
||
case CmResourceTypePort: {
|
||
|
||
foundPort = TRUE;
|
||
|
||
//
|
||
// No matter what the registry says, we
|
||
// know how long the register set is.
|
||
//
|
||
|
||
controller->SpanOfController = SERIAL_REGISTER_SPAN;
|
||
controller->Controller = partial->u.Port.Start;
|
||
controller->AddressSpace = partial->Flags;
|
||
|
||
break;
|
||
}
|
||
case CmResourceTypeInterrupt: {
|
||
|
||
foundInterrupt = TRUE;
|
||
if (partial->Flags & CM_RESOURCE_INTERRUPT_LATCHED) {
|
||
|
||
controller->InterruptMode = Latched;
|
||
|
||
} else {
|
||
|
||
controller->InterruptMode = LevelSensitive;
|
||
|
||
}
|
||
|
||
controller->OriginalIrql = partial->u.Interrupt.Level;
|
||
controller->OriginalVector = partial->u.Interrupt.Vector;
|
||
|
||
break;
|
||
|
||
}
|
||
case CmResourceTypeDeviceSpecific: {
|
||
|
||
PCM_SERIAL_DEVICE_DATA sDeviceData;
|
||
|
||
sDeviceData = (PCM_SERIAL_DEVICE_DATA)(partial + 1);
|
||
|
||
controller->ClockRate = sDeviceData->BaudClock;
|
||
|
||
break;
|
||
|
||
}
|
||
default: {
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (foundPort && foundInterrupt) {
|
||
|
||
WCHAR ntNumberBuffer[100];
|
||
WCHAR symbolicNumberBuffer[100];
|
||
UNICODE_STRING ntNumberString;
|
||
UNICODE_STRING symbolicNumberString;
|
||
|
||
ntNumberString.Length = 0;
|
||
ntNumberString.MaximumLength = 100;
|
||
ntNumberString.Buffer = &ntNumberBuffer[0];
|
||
|
||
symbolicNumberString.Length = 0;
|
||
symbolicNumberString.MaximumLength = 100;
|
||
symbolicNumberString.Buffer = &symbolicNumberBuffer[0];
|
||
|
||
//
|
||
// Everthing is great so far. We now need to form the
|
||
// Nt Names and symbolic link names.
|
||
//
|
||
|
||
if (!NT_SUCCESS(RtlIntegerToUnicodeString(
|
||
config->ControllersFound - 1,
|
||
10,
|
||
&ntNumberString
|
||
))) {
|
||
|
||
SerialLogError(
|
||
config->DriverObject,
|
||
NULL,
|
||
controller->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
17,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't convert NT controller number to\n"
|
||
"------ to unicode for firmware data: %d\n",
|
||
config->ControllersFound - 1)
|
||
);
|
||
//
|
||
// Oh well, ignore this controller.
|
||
//
|
||
ExFreePool(controller);
|
||
|
||
} else {
|
||
|
||
if (!NT_SUCCESS(RtlIntegerToUnicodeString(
|
||
config->ControllersFound,
|
||
10,
|
||
&symbolicNumberString
|
||
))) {
|
||
|
||
SerialLogError(
|
||
config->DriverObject,
|
||
NULL,
|
||
controller->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
18,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't allocate convert symbolic controller number to\n"
|
||
"------ to unicode for firmware data: %d\n",
|
||
config->ControllersFound)
|
||
);
|
||
ExFreePool(controller);
|
||
|
||
} else {
|
||
|
||
UNICODE_STRING Temp;
|
||
|
||
//
|
||
// Ok, we have the non-constant portions of the
|
||
// names all figured out. Now allocate memory
|
||
// for what will be used later.
|
||
//
|
||
|
||
//
|
||
// Save off a copy of the object directory name.
|
||
//
|
||
|
||
//
|
||
// Init the destination.
|
||
//
|
||
RtlInitUnicodeString(
|
||
&controller->ObjectDirectory,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// This will get its length.
|
||
//
|
||
RtlInitUnicodeString(
|
||
&Temp,
|
||
DEFAULT_DIRECTORY
|
||
);
|
||
|
||
//
|
||
// Now allocate that much.
|
||
//
|
||
|
||
controller->ObjectDirectory.Buffer =
|
||
ExAllocatePool(
|
||
PagedPool,
|
||
Temp.Length+sizeof(WCHAR)
|
||
);
|
||
|
||
if (!controller->ObjectDirectory.Buffer) {
|
||
|
||
SerialLogError(
|
||
config->DriverObject,
|
||
NULL,
|
||
controller->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
19,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't allocate memory for object\n"
|
||
"------ directory for NT firmware data: %d\n",
|
||
config->ControllersFound - 1)
|
||
);
|
||
ExFreePool(controller);
|
||
return STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
controller->ObjectDirectory.MaximumLength =
|
||
Temp.Length+sizeof(WCHAR);
|
||
|
||
//
|
||
// Zero fill it.
|
||
//
|
||
|
||
RtlZeroMemory(
|
||
controller->ObjectDirectory.Buffer,
|
||
controller->ObjectDirectory.MaximumLength
|
||
);
|
||
|
||
RtlAppendUnicodeStringToString(
|
||
&controller->ObjectDirectory,
|
||
&Temp
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Init the destination.
|
||
//
|
||
RtlInitUnicodeString(
|
||
&controller->NtNameForPort,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// This will get its length.
|
||
//
|
||
RtlInitUnicodeString(
|
||
&Temp,
|
||
DEFAULT_NT_SUFFIX
|
||
);
|
||
|
||
//
|
||
// Allocate enough for the suffix and the number.
|
||
//
|
||
|
||
controller->NtNameForPort.Buffer =
|
||
ExAllocatePool(
|
||
PagedPool,
|
||
Temp.Length +
|
||
ntNumberString.Length + sizeof(WCHAR)
|
||
);
|
||
|
||
if (!controller->NtNameForPort.Buffer) {
|
||
|
||
SerialLogError(
|
||
config->DriverObject,
|
||
NULL,
|
||
controller->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
20,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't allocate memory for NT\n"
|
||
"------ name for NT firmware data: %d\n",
|
||
config->ControllersFound - 1)
|
||
);
|
||
ExFreePool(controller->ObjectDirectory.Buffer);
|
||
ExFreePool(controller);
|
||
return STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
controller->NtNameForPort.MaximumLength =
|
||
Temp.Length+ntNumberString.Length+sizeof(WCHAR);
|
||
|
||
RtlZeroMemory(
|
||
controller->NtNameForPort.Buffer,
|
||
controller->NtNameForPort.MaximumLength
|
||
);
|
||
|
||
RtlAppendUnicodeStringToString(
|
||
&controller->NtNameForPort,
|
||
&Temp
|
||
);
|
||
|
||
RtlAppendUnicodeStringToString(
|
||
&controller->NtNameForPort,
|
||
&ntNumberString
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Now form that name that will be used as a
|
||
// symbolic link to the actual device name
|
||
// we just formed.
|
||
//
|
||
|
||
RtlInitUnicodeString(
|
||
&controller->SymbolicLinkName,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// This will get its length.
|
||
//
|
||
RtlInitUnicodeString(
|
||
&Temp,
|
||
DEFAULT_SERIAL_NAME
|
||
);
|
||
|
||
//
|
||
// Allocate enough for the suffix and the number.
|
||
//
|
||
|
||
controller->SymbolicLinkName.Buffer =
|
||
ExAllocatePool(
|
||
PagedPool,
|
||
Temp.Length +
|
||
symbolicNumberString.Length+sizeof(WCHAR)
|
||
);
|
||
|
||
if (!controller->SymbolicLinkName.Buffer) {
|
||
|
||
SerialLogError(
|
||
config->DriverObject,
|
||
NULL,
|
||
controller->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
21,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't allocate memory for symbolic\n"
|
||
"------ name for NT firmware data: %d\n",
|
||
config->ControllersFound - 1)
|
||
);
|
||
ExFreePool(controller->ObjectDirectory.Buffer);
|
||
ExFreePool(controller->NtNameForPort.Buffer);
|
||
ExFreePool(controller);
|
||
return STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
controller->SymbolicLinkName.MaximumLength =
|
||
Temp.Length+symbolicNumberString.Length+sizeof(WCHAR);
|
||
|
||
RtlZeroMemory(
|
||
controller->SymbolicLinkName.Buffer,
|
||
controller->SymbolicLinkName.MaximumLength
|
||
);
|
||
|
||
RtlAppendUnicodeStringToString(
|
||
&controller->SymbolicLinkName,
|
||
&Temp
|
||
);
|
||
|
||
RtlAppendUnicodeStringToString(
|
||
&controller->SymbolicLinkName,
|
||
&symbolicNumberString
|
||
);
|
||
|
||
}
|
||
|
||
InsertTailList(
|
||
&config->ConfigList,
|
||
&controller->ConfigList
|
||
);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
SerialLogError(
|
||
config->DriverObject,
|
||
NULL,
|
||
controller->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
22,
|
||
STATUS_SUCCESS,
|
||
SERIAL_NOT_ENOUGH_CONFIG_INFO,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
ExFreePool(controller);
|
||
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
VOID
|
||
SerialGetConfigInfo(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath,
|
||
ULONG ForceFifoEnableDefault,
|
||
ULONG RxFIFODefault,
|
||
ULONG TxFIFODefault,
|
||
ULONG PermitShareDefault,
|
||
ULONG LogFifoDefault,
|
||
OUT PLIST_ENTRY ConfigList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will "return" a list of configuration
|
||
records for the serial ports to initialize.
|
||
|
||
It will first query the firmware data. It will then
|
||
look for "user" specified comm ports in the registry.
|
||
It will place the user specified comm ports in the
|
||
the passed in list.
|
||
|
||
After it finds all of the user specified port, it will
|
||
attempt to add the firmware comm ports into the passed
|
||
in lists. The insert in the list code detects conflicts
|
||
and rejects a new comm port. In this way we can prevent
|
||
firmware found comm ports from overiding information
|
||
specified by the "user". Note, this means if the user
|
||
specified data is incorrect in its use of the interrupt
|
||
(which should *always* be correct from the firmware)
|
||
that port likely will not work. But, then, we "trust"
|
||
the user.
|
||
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Not used.
|
||
|
||
RegistryPath - Path to this drivers service node in
|
||
the current control set.
|
||
|
||
ForceFifoEnableDefault - Gotten from the services node.
|
||
|
||
RxFifoDefault - Gotten from the services node.
|
||
|
||
TxFifoDefault - Gotten from the services node.
|
||
|
||
PermitShareDefault - Gotten from the services node.
|
||
|
||
LogFifoDefault - Gotten from services node.
|
||
|
||
ConfigList - Listhead (which will be intialized) for a list
|
||
of configuration records for ports to control.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if consistant configuration was found - otherwise.
|
||
returns STATUS_SERIAL_NO_DEVICE_INITED.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
SERIAL_FIRMWARE_DATA firmware;
|
||
|
||
PRTL_QUERY_REGISTRY_TABLE parameters = NULL;
|
||
|
||
INTERFACE_TYPE interfaceType;
|
||
ULONG defaultInterfaceType;
|
||
|
||
|
||
//
|
||
// Default values for user data.
|
||
//
|
||
ULONG maxUlong = MAXULONG;
|
||
ULONG zero = 0;
|
||
ULONG clockRate = 1843200;
|
||
ULONG defaultInterruptMode;
|
||
ULONG defaultAddressSpace = CM_RESOURCE_PORT_IO;
|
||
|
||
//
|
||
// Where user data from the registry will be placed.
|
||
//
|
||
|
||
PHYSICAL_ADDRESS userPort;
|
||
ULONG userVector;
|
||
ULONG userLevel;
|
||
PHYSICAL_ADDRESS userInterruptStatus;
|
||
ULONG userPortIndex;
|
||
ULONG userBusNumber;
|
||
ULONG userInterfaceType;
|
||
ULONG userClockRate;
|
||
ULONG userIndexed;
|
||
ULONG userAddressSpace;
|
||
ULONG userInterruptMode;
|
||
ULONG firmwareFound;
|
||
ULONG disablePort;
|
||
ULONG forceFifoEnable;
|
||
ULONG rxFIFO;
|
||
ULONG txFIFO;
|
||
ULONG maskInverted;
|
||
ULONG defaultPermitSystemWideShare = FALSE;
|
||
UNICODE_STRING userSymbolicLink;
|
||
|
||
UNICODE_STRING parametersPath;
|
||
OBJECT_ATTRIBUTES parametersAttributes;
|
||
HANDLE parametersKey;
|
||
PKEY_BASIC_INFORMATION userSubKey = NULL;
|
||
ULONG i;
|
||
|
||
RTL_QUERY_REGISTRY_TABLE jensenTable[2] = {0};
|
||
UNICODE_STRING jensenData;
|
||
UNICODE_STRING jensenValue;
|
||
BOOLEAN jensenDetected;
|
||
PUCHAR jensenBuffer;
|
||
|
||
|
||
if (!(jensenBuffer = ExAllocatePool(
|
||
PagedPool,
|
||
512
|
||
))) {
|
||
|
||
//
|
||
// We couldn't allocate 512 bytes of paged pool. If that's
|
||
// so, then it's likely that the least of this machines problem's
|
||
// is that it's a Jensen.
|
||
//
|
||
|
||
jensenDetected = FALSE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Check to see if this is a Jensen alpha. If we do, then
|
||
// well have to change the way we enable and disable interrupts
|
||
//
|
||
|
||
jensenData.Length = 0;
|
||
jensenData.MaximumLength = 512;
|
||
jensenData.Buffer = (PWCHAR)&jensenBuffer[0];
|
||
RtlInitUnicodeString(
|
||
&jensenValue,
|
||
L"Jensen"
|
||
);
|
||
jensenTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT |
|
||
RTL_QUERY_REGISTRY_REQUIRED;
|
||
jensenTable[0].Name = L"Identifier";
|
||
jensenTable[0].EntryContext = &jensenData;
|
||
|
||
if (!NT_SUCCESS(RtlQueryRegistryValues(
|
||
RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
|
||
L"\\REGISTRY\\MACHINE\\HARDWARE\\DESCRIPTION\\SYSTEM",
|
||
&jensenTable[0],
|
||
NULL,
|
||
NULL
|
||
))) {
|
||
|
||
//
|
||
// How odd, no identifer string! We'll it's probably not a jensen.
|
||
//
|
||
|
||
jensenDetected = FALSE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Skip past the DEC-XX Portion of the name string.
|
||
// Be carful and make sure we have at least that much data.
|
||
//
|
||
|
||
if (jensenData.Length <= (sizeof(WCHAR)*6)) {
|
||
|
||
jensenDetected = FALSE;
|
||
|
||
} else {
|
||
|
||
jensenData.Length -= (sizeof(WCHAR)*6);
|
||
jensenData.MaximumLength -= (sizeof(WCHAR)*6);
|
||
jensenData.Buffer = (PWCHAR)&jensenBuffer[sizeof(WCHAR)*6];
|
||
jensenDetected = RtlEqualUnicodeString(
|
||
&jensenData,
|
||
&jensenValue,
|
||
FALSE
|
||
);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
ExFreePool(jensenBuffer);
|
||
|
||
}
|
||
|
||
if (jensenDetected) {
|
||
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: Jensen Detected\n")
|
||
);
|
||
|
||
}
|
||
|
||
InitializeListHead(ConfigList);
|
||
|
||
RtlZeroMemory(
|
||
&firmware,
|
||
sizeof(SERIAL_FIRMWARE_DATA)
|
||
);
|
||
|
||
firmware.DriverObject = DriverObject;
|
||
firmware.ForceFifoEnableDefault = ForceFifoEnableDefault;
|
||
firmware.RxFIFODefault = RxFIFODefault;
|
||
firmware.TxFIFODefault = TxFIFODefault;
|
||
firmware.PermitShareDefault = PermitShareDefault;
|
||
firmware.PermitSystemWideShare = defaultPermitSystemWideShare;
|
||
firmware.LogFifoDefault = LogFifoDefault;
|
||
InitializeListHead(&firmware.ConfigList);
|
||
RtlInitUnicodeString(
|
||
&firmware.Directory,
|
||
DEFAULT_DIRECTORY
|
||
);
|
||
RtlInitUnicodeString(
|
||
&firmware.NtNameSuffix,
|
||
DEFAULT_NT_SUFFIX
|
||
);
|
||
RtlInitUnicodeString(
|
||
&firmware.DirectorySymbolicName,
|
||
DEFAULT_SERIAL_NAME
|
||
);
|
||
|
||
//
|
||
// First we query the hardware registry for all of
|
||
// the firmware defined serial ports. We loop over
|
||
// all of the busses.
|
||
//
|
||
|
||
for (
|
||
interfaceType = 0;
|
||
interfaceType < MaximumInterfaceType;
|
||
interfaceType++
|
||
) {
|
||
|
||
CONFIGURATION_TYPE sc = SerialController;
|
||
|
||
IoQueryDeviceDescription(
|
||
&interfaceType,
|
||
NULL,
|
||
&sc,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
SerialConfigCallBack,
|
||
&firmware
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Query the registry one more time. This time we
|
||
// 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;
|
||
defaultInterruptMode = CM_RESOURCE_INTERRUPT_LATCHED;
|
||
|
||
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;
|
||
|
||
//
|
||
// Microchannel machines can permit the interrupt to be
|
||
// shared system wide.
|
||
//
|
||
|
||
defaultPermitSystemWideShare = TRUE;
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Gonna get the user data now. Allocate the
|
||
// structures that we will be using throughout
|
||
// the search for user data. We will deallocate
|
||
// them before we leave this routine.
|
||
//
|
||
|
||
userSymbolicLink.Buffer = NULL;
|
||
parametersPath.Buffer = NULL;
|
||
|
||
//
|
||
// Allocate the rtl query table. This should have an entry for each value
|
||
// we retrieve from the registry as well as a terminating zero entry as
|
||
// well the first "goto subkey" entry.
|
||
//
|
||
|
||
parameters = ExAllocatePool(
|
||
PagedPool,
|
||
sizeof(RTL_QUERY_REGISTRY_TABLE)*20
|
||
);
|
||
|
||
if (!parameters) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
SerialPhysicalZero,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
23,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't allocate table for rtl query\n"
|
||
"------ to parameters for %wZ",
|
||
RegistryPath)
|
||
);
|
||
|
||
goto DoFirmwareAdd;
|
||
|
||
}
|
||
|
||
RtlZeroMemory(
|
||
parameters,
|
||
sizeof(RTL_QUERY_REGISTRY_TABLE)*20
|
||
);
|
||
|
||
//
|
||
// Allocate the place where the users symbolic link name
|
||
// for the port will go.
|
||
//
|
||
|
||
//
|
||
// We will initially allocate space for 257 wchars.
|
||
// we will then set the maximum size to 256
|
||
// This way the rtl routine could return a 256
|
||
// WCHAR wide string with no null terminator.
|
||
// We'll remember that the buffer is one WCHAR
|
||
// longer then it says it is so that we can always
|
||
// have a NULL terminator at the end.
|
||
//
|
||
|
||
RtlInitUnicodeString(
|
||
&userSymbolicLink,
|
||
NULL
|
||
);
|
||
userSymbolicLink.MaximumLength = sizeof(WCHAR)*256;
|
||
userSymbolicLink.Buffer = ExAllocatePool(
|
||
PagedPool,
|
||
sizeof(WCHAR)*257
|
||
);
|
||
|
||
if (!userSymbolicLink.Buffer) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
SerialPhysicalZero,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
24,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't allocate buffer for the symbolic link\n"
|
||
"------ for parameters items in %wZ",
|
||
RegistryPath)
|
||
);
|
||
|
||
goto DoFirmwareAdd;
|
||
|
||
}
|
||
|
||
//
|
||
// Form a path to our drivers Parameters subkey.
|
||
//
|
||
|
||
RtlInitUnicodeString(
|
||
¶metersPath,
|
||
NULL
|
||
);
|
||
|
||
parametersPath.MaximumLength = RegistryPath->Length +
|
||
sizeof(L"\\") +
|
||
sizeof(L"Parameters");
|
||
|
||
parametersPath.Buffer = ExAllocatePool(
|
||
PagedPool,
|
||
parametersPath.MaximumLength
|
||
);
|
||
|
||
if (!parametersPath.Buffer) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
SerialPhysicalZero,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
25,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't allocate string for path\n"
|
||
"------ to parameters for %wZ",
|
||
RegistryPath)
|
||
);
|
||
|
||
goto DoFirmwareAdd;
|
||
|
||
}
|
||
|
||
//
|
||
// Form the parameters path.
|
||
//
|
||
|
||
RtlZeroMemory(
|
||
parametersPath.Buffer,
|
||
parametersPath.MaximumLength
|
||
);
|
||
RtlAppendUnicodeStringToString(
|
||
¶metersPath,
|
||
RegistryPath
|
||
);
|
||
RtlAppendUnicodeToString(
|
||
¶metersPath,
|
||
L"\\"
|
||
);
|
||
RtlAppendUnicodeToString(
|
||
¶metersPath,
|
||
L"Parameters"
|
||
);
|
||
|
||
userSubKey = ExAllocatePool(
|
||
PagedPool,
|
||
sizeof(KEY_BASIC_INFORMATION)+(sizeof(WCHAR)*256)
|
||
);
|
||
|
||
if (!userSubKey) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
SerialPhysicalZero,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
26,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't allocate memory basic information\n"
|
||
"------ structure to enumerate subkeys for %wZ",
|
||
¶metersPath)
|
||
);
|
||
|
||
goto DoFirmwareAdd;
|
||
|
||
}
|
||
|
||
//
|
||
// Open the key given by our registry path & Parameters.
|
||
//
|
||
|
||
InitializeObjectAttributes(
|
||
¶metersAttributes,
|
||
¶metersPath,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
if (!NT_SUCCESS(ZwOpenKey(
|
||
¶metersKey,
|
||
MAXIMUM_ALLOWED,
|
||
¶metersAttributes
|
||
))) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
SerialPhysicalZero,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
27,
|
||
STATUS_SUCCESS,
|
||
SERIAL_NO_PARAMETERS_INFO,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't open the drivers Parameters key %wZ\n",
|
||
RegistryPath)
|
||
);
|
||
goto DoFirmwareAdd;
|
||
|
||
}
|
||
|
||
//
|
||
// Gather all of the "user specified" information from
|
||
// the registry.
|
||
//
|
||
|
||
parameters[0].Flags = RTL_QUERY_REGISTRY_SUBKEY;
|
||
|
||
parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parameters[1].Name = L"PortAddress";
|
||
parameters[1].EntryContext = &userPort.LowPart;
|
||
parameters[1].DefaultType = REG_DWORD;
|
||
parameters[1].DefaultData = &zero;
|
||
parameters[1].DefaultLength = sizeof(ULONG);
|
||
|
||
parameters[2].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parameters[2].Name = L"Interrupt";
|
||
parameters[2].EntryContext = &userVector;
|
||
parameters[2].DefaultType = REG_DWORD;
|
||
parameters[2].DefaultData = &zero;
|
||
parameters[2].DefaultLength = sizeof(ULONG);
|
||
|
||
parameters[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parameters[3].Name = firmware.Directory.Buffer;
|
||
parameters[3].EntryContext = &userSymbolicLink;
|
||
parameters[3].DefaultType = REG_SZ;
|
||
parameters[3].DefaultData = L"";
|
||
parameters[3].DefaultLength = 0;
|
||
|
||
parameters[4].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parameters[4].Name = L"InterruptStatus";
|
||
parameters[4].EntryContext = &userInterruptStatus.LowPart;
|
||
parameters[4].DefaultType = REG_DWORD;
|
||
parameters[4].DefaultData = &zero;
|
||
parameters[4].DefaultLength = sizeof(ULONG);
|
||
|
||
parameters[5].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parameters[5].Name = L"PortIndex";
|
||
parameters[5].EntryContext = &userPortIndex;
|
||
parameters[5].DefaultType = REG_DWORD;
|
||
parameters[5].DefaultData = &zero;
|
||
parameters[5].DefaultLength = sizeof(ULONG);
|
||
|
||
parameters[6].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parameters[6].Name = L"BusNumber";
|
||
parameters[6].EntryContext = &userBusNumber;
|
||
parameters[6].DefaultType = REG_DWORD;
|
||
parameters[6].DefaultData = &zero;
|
||
parameters[6].DefaultLength = sizeof(ULONG);
|
||
|
||
parameters[7].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parameters[7].Name = L"BusType";
|
||
parameters[7].EntryContext = &userInterfaceType;
|
||
parameters[7].DefaultType = REG_DWORD;
|
||
parameters[7].DefaultData = &defaultInterfaceType;
|
||
parameters[7].DefaultLength = sizeof(ULONG);
|
||
|
||
parameters[8].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parameters[8].Name = L"ClockRate";
|
||
parameters[8].EntryContext = &userClockRate;
|
||
parameters[8].DefaultType = REG_DWORD;
|
||
parameters[8].DefaultData = &clockRate;
|
||
parameters[8].DefaultLength = sizeof(ULONG);
|
||
|
||
parameters[9].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parameters[9].Name = L"Indexed";
|
||
parameters[9].EntryContext = &userIndexed;
|
||
parameters[9].DefaultType = REG_DWORD;
|
||
parameters[9].DefaultData = &zero;
|
||
parameters[9].DefaultLength = sizeof(ULONG);
|
||
|
||
parameters[10].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parameters[10].Name = L"InterruptMode";
|
||
parameters[10].EntryContext = &userInterruptMode;
|
||
parameters[10].DefaultType = REG_DWORD;
|
||
parameters[10].DefaultData = &defaultInterruptMode;
|
||
parameters[10].DefaultLength = sizeof(ULONG);
|
||
|
||
parameters[11].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parameters[11].Name = L"AddressSpace";
|
||
parameters[11].EntryContext = &userAddressSpace;
|
||
parameters[11].DefaultType = REG_DWORD;
|
||
parameters[11].DefaultData = &defaultAddressSpace;
|
||
parameters[11].DefaultLength = sizeof(ULONG);
|
||
|
||
parameters[12].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parameters[12].Name = L"InterruptLevel";
|
||
parameters[12].EntryContext = &userLevel;
|
||
parameters[12].DefaultType = REG_DWORD;
|
||
parameters[12].DefaultData = &zero;
|
||
parameters[12].DefaultLength = sizeof(ULONG);
|
||
|
||
parameters[13].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parameters[13].Name = L"FirmwareFound";
|
||
parameters[13].EntryContext = &firmwareFound;
|
||
parameters[13].DefaultType = REG_DWORD;
|
||
parameters[13].DefaultData = &zero;
|
||
parameters[13].DefaultLength = sizeof(ULONG);
|
||
|
||
parameters[14].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parameters[14].Name = L"DisablePort";
|
||
parameters[14].EntryContext = &disablePort;
|
||
parameters[14].DefaultType = REG_DWORD;
|
||
parameters[14].DefaultData = &zero;
|
||
parameters[14].DefaultLength = sizeof(ULONG);
|
||
|
||
parameters[15].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parameters[15].Name = L"ForceFifoEnable";
|
||
parameters[15].EntryContext = &forceFifoEnable;
|
||
parameters[15].DefaultType = REG_DWORD;
|
||
parameters[15].DefaultData = &ForceFifoEnableDefault;
|
||
parameters[15].DefaultLength = sizeof(ULONG);
|
||
|
||
parameters[16].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parameters[16].Name = L"RxFIFO";
|
||
parameters[16].EntryContext = &rxFIFO;
|
||
parameters[16].DefaultType = REG_DWORD;
|
||
parameters[16].DefaultData = &RxFIFODefault;
|
||
parameters[16].DefaultLength = sizeof(ULONG);
|
||
|
||
parameters[17].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parameters[17].Name = L"TxFIFO";
|
||
parameters[17].EntryContext = &txFIFO;
|
||
parameters[17].DefaultType = REG_DWORD;
|
||
parameters[17].DefaultData = &TxFIFODefault;
|
||
parameters[17].DefaultLength = sizeof(ULONG);
|
||
|
||
parameters[18].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
parameters[18].Name = L"MaskInverted";
|
||
parameters[18].EntryContext = &maskInverted;
|
||
parameters[18].DefaultType = REG_DWORD;
|
||
parameters[18].DefaultData = &zero;
|
||
parameters[18].DefaultLength = sizeof(ULONG);
|
||
|
||
|
||
|
||
i = 0;
|
||
while (TRUE) {
|
||
|
||
NTSTATUS status;
|
||
ULONG actuallyReturned;
|
||
|
||
//
|
||
// We lie about the length of the buffer, so that we can
|
||
// MAKE SURE that the name it returns can be padded with
|
||
// a NULL.
|
||
//
|
||
|
||
status = ZwEnumerateKey(
|
||
parametersKey,
|
||
i,
|
||
KeyBasicInformation,
|
||
userSubKey,
|
||
sizeof(KEY_BASIC_INFORMATION)+(sizeof(WCHAR)*255),
|
||
&actuallyReturned
|
||
);
|
||
|
||
if (status == STATUS_NO_MORE_ENTRIES) {
|
||
|
||
break;
|
||
}
|
||
|
||
if (status == STATUS_BUFFER_OVERFLOW) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
SerialPhysicalZero,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
28,
|
||
STATUS_SUCCESS,
|
||
SERIAL_UNABLE_TO_ACCESS_CONFIG,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Overflowed the enumerate buffer\n"
|
||
"------- for subkey #%d of %wZ\n",
|
||
i,parametersPath)
|
||
);
|
||
i++;
|
||
continue;
|
||
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
SerialPhysicalZero,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
29,
|
||
status,
|
||
SERIAL_UNABLE_TO_ACCESS_CONFIG,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Bad status returned: %x \n"
|
||
"------- on enumeration for subkey # %d of %wZ\n",
|
||
status,i,parametersPath)
|
||
);
|
||
i++;
|
||
continue;
|
||
|
||
}
|
||
|
||
//
|
||
// Pad the name returned with a null.
|
||
//
|
||
|
||
RtlZeroMemory(
|
||
((PUCHAR)(&userSubKey->Name[0]))+userSubKey->NameLength,
|
||
sizeof(WCHAR)
|
||
);
|
||
|
||
parameters[0].Name = &userSubKey->Name[0];
|
||
|
||
//
|
||
// Make sure that the physical addresses start
|
||
// out clean.
|
||
//
|
||
RtlZeroMemory(
|
||
&userPort,
|
||
sizeof(userPort)
|
||
);
|
||
RtlZeroMemory(
|
||
&userInterruptStatus,
|
||
sizeof(userInterruptStatus)
|
||
);
|
||
|
||
status = RtlQueryRegistryValues(
|
||
RTL_REGISTRY_ABSOLUTE,
|
||
parametersPath.Buffer,
|
||
parameters,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
PCONFIG_DATA newConfig;
|
||
|
||
//
|
||
// Well! Some supposedly valid information was found!
|
||
//
|
||
// We'll see about that.
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
//
|
||
// Let's just jam the WCHAR null at the end of the
|
||
// user symbolic link. Remember that we left room for
|
||
// one when we allocated it's buffer.
|
||
//
|
||
|
||
RtlZeroMemory(
|
||
((PUCHAR)(&userSymbolicLink.Buffer[0]))+userSymbolicLink.Length,
|
||
sizeof(WCHAR)
|
||
);
|
||
|
||
if (!userPort.LowPart) {
|
||
|
||
//
|
||
// Ehhhh! Lose Game.
|
||
//
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
userPort,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
58,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INVALID_USER_CONFIG,
|
||
userSubKey->NameLength+sizeof(WCHAR),
|
||
&userSubKey->Name[0],
|
||
(wcslen(parameters[1].Name)*sizeof(WCHAR))+sizeof(WCHAR),
|
||
parameters[1].Name
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Bogus port address %ws\n",
|
||
parameters[1].Name)
|
||
);
|
||
i++;
|
||
continue;
|
||
|
||
}
|
||
|
||
if (!userVector) {
|
||
|
||
//
|
||
// Ehhhh! Lose Game.
|
||
//
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
userPort,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
59,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INVALID_USER_CONFIG,
|
||
userSubKey->NameLength+sizeof(WCHAR),
|
||
&userSubKey->Name[0],
|
||
(wcslen(parameters[2].Name)*sizeof(WCHAR))+sizeof(WCHAR),
|
||
parameters[2].Name
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Bogus vector %ws\n",
|
||
parameters[2].Name)
|
||
);
|
||
i++;
|
||
continue;
|
||
|
||
}
|
||
|
||
if (!userSymbolicLink.Length) {
|
||
|
||
//
|
||
// Ehhhh! Lose Game.
|
||
//
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
userPort,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
60,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INVALID_USER_CONFIG,
|
||
userSubKey->NameLength+sizeof(WCHAR),
|
||
&userSubKey->Name[0],
|
||
(wcslen(parameters[3].Name)*sizeof(WCHAR))+sizeof(WCHAR),
|
||
parameters[3].Name
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: bogus value for %ws\n",
|
||
parameters[3].Name)
|
||
);
|
||
i++;
|
||
continue;
|
||
|
||
}
|
||
|
||
if (userInterruptStatus.LowPart != 0) {
|
||
|
||
if (userPortIndex == MAXULONG) {
|
||
|
||
//
|
||
// Ehhhh! Lose Game.
|
||
//
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
userPort,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
30,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INVALID_PORT_INDEX,
|
||
userSymbolicLink.Length+sizeof(WCHAR),
|
||
userSymbolicLink.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Bogus port index %ws\n",
|
||
parameters[0].Name)
|
||
);
|
||
i++;
|
||
continue;
|
||
|
||
} else if (!userPortIndex) {
|
||
|
||
//
|
||
// So sorry, you must have a non-zero port index.
|
||
//
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
userPort,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
31,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INVALID_PORT_INDEX,
|
||
userSymbolicLink.Length+sizeof(WCHAR),
|
||
userSymbolicLink.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Port index must be > 0 for any\n"
|
||
"------- port on a multiport card: %ws\n",
|
||
parameters[0].Name)
|
||
);
|
||
i++;
|
||
continue;
|
||
|
||
} else {
|
||
|
||
if (userIndexed) {
|
||
|
||
if (userPortIndex > SERIAL_MAX_PORTS_INDEXED) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
userPort,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
32,
|
||
STATUS_SUCCESS,
|
||
SERIAL_PORT_INDEX_TOO_HIGH,
|
||
userSymbolicLink.Length+sizeof(WCHAR),
|
||
userSymbolicLink.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: port index to large %ws\n",
|
||
parameters[0].Name)
|
||
);
|
||
i++;
|
||
continue;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
if (userPortIndex > SERIAL_MAX_PORTS_NONINDEXED) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
userPort,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
33,
|
||
STATUS_SUCCESS,
|
||
SERIAL_PORT_INDEX_TOO_HIGH,
|
||
userSymbolicLink.Length+sizeof(WCHAR),
|
||
userSymbolicLink.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: port index to large %ws\n",
|
||
parameters[0].Name)
|
||
);
|
||
i++;
|
||
continue;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// 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 ((userBusNumber != 0) ||
|
||
(userInterfaceType != defaultInterfaceType)) {
|
||
|
||
BOOLEAN foundIt;
|
||
if (userInterfaceType >= MaximumInterfaceType) {
|
||
|
||
//
|
||
// Ehhhh! Lose Game.
|
||
//
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
userPort,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
34,
|
||
STATUS_SUCCESS,
|
||
SERIAL_UNKNOWN_BUS,
|
||
userSymbolicLink.Length+sizeof(WCHAR),
|
||
userSymbolicLink.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Invalid Bus type %ws\n",
|
||
parameters[0].Name)
|
||
);
|
||
i++;
|
||
continue;
|
||
|
||
}
|
||
|
||
IoQueryDeviceDescription(
|
||
(INTERFACE_TYPE *)&userInterfaceType,
|
||
&zero,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
SerialItemCallBack,
|
||
&foundIt
|
||
);
|
||
|
||
if (!foundIt) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
userPort,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
35,
|
||
STATUS_SUCCESS,
|
||
SERIAL_BUS_NOT_PRESENT,
|
||
userSymbolicLink.Length+sizeof(WCHAR),
|
||
userSymbolicLink.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: There aren't that many of those\n"
|
||
"------- busses on this system,%ws\n",
|
||
parameters[0].Name)
|
||
);
|
||
i++;
|
||
continue;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if ((userInterfaceType == MicroChannel) &&
|
||
(userInterruptMode == CM_RESOURCE_INTERRUPT_LATCHED)) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
userPort,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
36,
|
||
STATUS_SUCCESS,
|
||
SERIAL_BUS_INTERRUPT_CONFLICT,
|
||
userSymbolicLink.Length+sizeof(WCHAR),
|
||
userSymbolicLink.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Latched interrupts and MicroChannel\n"
|
||
"------- busses don't mix,%ws\n",
|
||
parameters[0].Name)
|
||
);
|
||
i++;
|
||
continue;
|
||
|
||
}
|
||
|
||
//
|
||
// Well ok, I guess we can take the data.
|
||
// There be other tests later on to make
|
||
// sure it doesn't have any other kinds
|
||
// of conflicts.
|
||
//
|
||
|
||
//
|
||
// Allocate the config record.
|
||
//
|
||
|
||
newConfig = ExAllocatePool(
|
||
PagedPool,
|
||
sizeof(CONFIG_DATA)
|
||
);
|
||
|
||
if (!newConfig) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
userPort,
|
||
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"
|
||
"------ for %ws\n",
|
||
parameters[0].Name)
|
||
);
|
||
|
||
i++;
|
||
continue;
|
||
|
||
}
|
||
|
||
RtlZeroMemory(
|
||
newConfig,
|
||
sizeof(CONFIG_DATA)
|
||
);
|
||
|
||
//
|
||
// Save off a copy of the object directory name.
|
||
//
|
||
|
||
//
|
||
// Init the destination.
|
||
//
|
||
RtlInitUnicodeString(
|
||
&newConfig->ObjectDirectory,
|
||
DEFAULT_DIRECTORY
|
||
);
|
||
newConfig->ObjectDirectory.MaximumLength += sizeof(WCHAR);
|
||
|
||
//
|
||
// Now allocate that much.
|
||
//
|
||
|
||
newConfig->ObjectDirectory.Buffer =
|
||
ExAllocatePool(
|
||
PagedPool,
|
||
newConfig->ObjectDirectory.MaximumLength
|
||
);
|
||
|
||
if (!newConfig->ObjectDirectory.Buffer) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
userPort,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
38,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't allocate memory for object\n"
|
||
"------ directory for NT user data for: %ws\n",
|
||
parameters[0].Name)
|
||
);
|
||
ExFreePool(newConfig);
|
||
i++;
|
||
continue;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Zero fill it.
|
||
//
|
||
|
||
RtlZeroMemory(
|
||
newConfig->ObjectDirectory.Buffer,
|
||
newConfig->ObjectDirectory.MaximumLength
|
||
);
|
||
|
||
newConfig->ObjectDirectory.Length = 0;
|
||
RtlAppendUnicodeToString(
|
||
&newConfig->ObjectDirectory,
|
||
DEFAULT_DIRECTORY
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Init the destination.
|
||
//
|
||
RtlInitUnicodeString(
|
||
&newConfig->NtNameForPort,
|
||
&userSubKey->Name[0]
|
||
);
|
||
|
||
//
|
||
// Allocate the space for the name.
|
||
//
|
||
|
||
newConfig->NtNameForPort.Length = 0;
|
||
newConfig->NtNameForPort.MaximumLength += sizeof(WCHAR);
|
||
newConfig->NtNameForPort.Buffer =
|
||
ExAllocatePool(
|
||
PagedPool,
|
||
newConfig->NtNameForPort.MaximumLength
|
||
);
|
||
|
||
if (!newConfig->NtNameForPort.Buffer) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
userPort,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
39,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't allocate memory for NT\n"
|
||
"------ name for NT user data name: %ws\n",
|
||
parameters[0].Name)
|
||
);
|
||
ExFreePool(newConfig->ObjectDirectory.Buffer);
|
||
ExFreePool(newConfig);
|
||
i++;
|
||
continue;
|
||
|
||
} else {
|
||
|
||
RtlZeroMemory(
|
||
newConfig->NtNameForPort.Buffer,
|
||
newConfig->NtNameForPort.MaximumLength
|
||
);
|
||
|
||
RtlAppendUnicodeToString(
|
||
&newConfig->NtNameForPort,
|
||
&userSubKey->Name[0]
|
||
);
|
||
|
||
}
|
||
|
||
newConfig->SymbolicLinkName = userSymbolicLink;
|
||
newConfig->SymbolicLinkName.MaximumLength += sizeof(WCHAR);
|
||
|
||
newConfig->SymbolicLinkName.Buffer =
|
||
ExAllocatePool(
|
||
PagedPool,
|
||
newConfig->SymbolicLinkName.MaximumLength
|
||
);
|
||
|
||
if (!newConfig->SymbolicLinkName.Buffer) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
userPort,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
40,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't allocate memory for symbolic\n"
|
||
"------ name from user data\n"
|
||
"------ %ws\n",
|
||
parameters[0].Name)
|
||
);
|
||
ExFreePool(newConfig->ObjectDirectory.Buffer);
|
||
ExFreePool(newConfig->NtNameForPort.Buffer);
|
||
ExFreePool(newConfig);
|
||
i++;
|
||
continue;
|
||
|
||
} else {
|
||
|
||
RtlZeroMemory(
|
||
newConfig->SymbolicLinkName.Buffer,
|
||
newConfig->SymbolicLinkName.MaximumLength
|
||
);
|
||
|
||
newConfig->SymbolicLinkName.Length = 0;
|
||
RtlAppendUnicodeStringToString(
|
||
&newConfig->SymbolicLinkName,
|
||
&userSymbolicLink
|
||
);
|
||
|
||
}
|
||
|
||
InitializeListHead(&newConfig->ConfigList);
|
||
InitializeListHead(&newConfig->SameInterrupt);
|
||
InitializeListHead(&newConfig->SameInterruptStatus);
|
||
newConfig->Controller = userPort;
|
||
newConfig->InterruptStatus = userInterruptStatus;
|
||
newConfig->SpanOfController = SERIAL_REGISTER_SPAN;
|
||
newConfig->SpanOfInterruptStatus = SERIAL_STATUS_LENGTH;
|
||
newConfig->PortIndex = userPortIndex;
|
||
newConfig->ClockRate = userClockRate;
|
||
newConfig->BusNumber = userBusNumber;
|
||
newConfig->AddressSpace = userAddressSpace;
|
||
newConfig->InterruptMode = userInterruptMode;
|
||
newConfig->InterfaceType = userInterfaceType;
|
||
newConfig->OriginalVector = userVector;
|
||
newConfig->DisablePort = disablePort;
|
||
newConfig->ForceFifoEnable = forceFifoEnable;
|
||
newConfig->RxFIFO = rxFIFO;
|
||
newConfig->TxFIFO = txFIFO;
|
||
newConfig->MaskInverted = maskInverted;
|
||
newConfig->PermitShare = PermitShareDefault;
|
||
newConfig->LogFifo = LogFifoDefault;
|
||
newConfig->PermitSystemWideShare = defaultPermitSystemWideShare;
|
||
if (!userLevel) {
|
||
newConfig->OriginalIrql = userVector;
|
||
} else {
|
||
newConfig->OriginalIrql = userLevel;
|
||
}
|
||
newConfig->Indexed = userIndexed;
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: 'user registry info - userPort: %x\n",
|
||
userPort.LowPart)
|
||
);
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: 'user registry info - userInterruptStatus: %x\n",
|
||
userInterruptStatus.LowPart)
|
||
);
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: 'user registry info - userPortIndex: %d\n",
|
||
userPortIndex)
|
||
);
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: 'user registry info - userClockRate: %d\n",
|
||
userClockRate)
|
||
);
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: 'user registry info - userBusNumber: %d\n",
|
||
userBusNumber)
|
||
);
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: 'user registry info - userAddressSpace: %d\n",
|
||
userAddressSpace)
|
||
);
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: 'user registry info - userInterruptMode: %d\n",
|
||
userInterruptMode)
|
||
);
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: 'user registry info - userInterfaceType: %d\n",
|
||
userInterfaceType)
|
||
);
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: 'user registry info - userVector: %d\n",
|
||
userVector)
|
||
);
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: 'user registry info - userLevel: %d\n",
|
||
userLevel)
|
||
);
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: 'user registry info - userIndexed: %d\n",
|
||
userIndexed)
|
||
);
|
||
|
||
if (!SerialPutInConfigList(
|
||
DriverObject,
|
||
ConfigList,
|
||
newConfig,
|
||
FALSE
|
||
)) {
|
||
|
||
//
|
||
// Dispose of this configuration record.
|
||
//
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Conflict detected amoungst user data %ws\n",
|
||
parameters[0].Name)
|
||
);
|
||
|
||
ExFreePool(newConfig->ObjectDirectory.Buffer);
|
||
ExFreePool(newConfig->NtNameForPort.Buffer);
|
||
ExFreePool(newConfig->SymbolicLinkName.Buffer);
|
||
ExFreePool(newConfig);
|
||
|
||
}
|
||
|
||
i++;
|
||
|
||
} else {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
SerialPhysicalZero,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
61,
|
||
status,
|
||
SERIAL_INVALID_USER_CONFIG,
|
||
userSubKey->NameLength+sizeof(WCHAR),
|
||
&userSubKey->Name[0],
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Bad status returned: %x \n"
|
||
"------- for the value entries of\n"
|
||
"------- %ws\n",
|
||
status,parameters[0].Name)
|
||
);
|
||
|
||
i++;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
ZwClose(parametersKey);
|
||
|
||
DoFirmwareAdd:;
|
||
|
||
//
|
||
// All done with the user specified information. Now try
|
||
// to add the firmware specified data to the configuration.
|
||
// If a conflict is detected then we simply dispose of that
|
||
// firmware collected data.
|
||
//
|
||
|
||
while (!IsListEmpty(&firmware.ConfigList)) {
|
||
|
||
PLIST_ENTRY head;
|
||
PCONFIG_DATA firmwareData;
|
||
|
||
head = RemoveHeadList(&firmware.ConfigList);
|
||
|
||
firmwareData = CONTAINING_RECORD(
|
||
head,
|
||
CONFIG_DATA,
|
||
ConfigList
|
||
);
|
||
|
||
firmwareData->Jensen = jensenDetected;
|
||
|
||
if (!SerialPutInConfigList(
|
||
DriverObject,
|
||
ConfigList,
|
||
firmwareData,
|
||
TRUE
|
||
)) {
|
||
|
||
//
|
||
// Dispose of this configuration record.
|
||
//
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
firmwareData->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
42,
|
||
STATUS_SUCCESS,
|
||
SERIAL_USER_OVERRIDE,
|
||
firmwareData->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
firmwareData->SymbolicLinkName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Conflict detected with user data for firmware port %wZ\n"
|
||
"------ User data will overides firmware data\n",
|
||
&firmwareData->NtNameForPort)
|
||
);
|
||
ExFreePool(firmwareData->ObjectDirectory.Buffer);
|
||
ExFreePool(firmwareData->NtNameForPort.Buffer);
|
||
ExFreePool(firmwareData->SymbolicLinkName.Buffer);
|
||
ExFreePool(firmwareData);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (userSubKey) {
|
||
|
||
ExFreePool(userSubKey);
|
||
|
||
}
|
||
|
||
if (userSymbolicLink.Buffer) {
|
||
|
||
ExFreePool(userSymbolicLink.Buffer);
|
||
|
||
}
|
||
|
||
if (parametersPath.Buffer) {
|
||
|
||
ExFreePool(parametersPath.Buffer);
|
||
|
||
}
|
||
|
||
if (parameters) {
|
||
|
||
ExFreePool(parameters);
|
||
|
||
}
|
||
}
|
||
|
||
BOOLEAN
|
||
SerialPutInConfigList(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN OUT PLIST_ENTRY ConfigList,
|
||
IN PCONFIG_DATA New,
|
||
IN BOOLEAN FirmwareAddition
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Given an interrupt value, port address, interrupt status address,
|
||
and an already defined list of configuration records, this routine
|
||
will perform a check to make sure that the new record doesn't
|
||
conflict with old records. (Note that we also include a port
|
||
index, but this has no bearing on validation.)
|
||
|
||
If everything checks out it will create a new configuration
|
||
record if the new record isn't part of multiport card or
|
||
if it is part of a multiport card it will create a configuration
|
||
record if the specifiers for that multiport card don't already
|
||
exist.
|
||
|
||
NOTE: It is assumed throughout this code that no address is
|
||
specified as 0.
|
||
|
||
We assume nothing is zero because that for interrupt
|
||
status that means none was specified.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Used to log errors.
|
||
|
||
ConfigList - Listhead for a list of configuration records for
|
||
ports to control.
|
||
|
||
New = Pointer to new configuration record to add.
|
||
|
||
FirmwareAddition - The configuration being added was found by the
|
||
firmware.
|
||
|
||
Return Value:
|
||
|
||
This will return STATUS_SUCCESS this new port information
|
||
does not conflict with old port information. Otherwise it
|
||
will return STATUS_SERIAL_NO_DEVICE_INITED.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PHYSICAL_ADDRESS serialPhysicalMax;
|
||
|
||
serialPhysicalMax.LowPart = (ULONG)~0;
|
||
serialPhysicalMax.HighPart = ~0;
|
||
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: Attempting to add %wZ\n"
|
||
"------- to the config list\n"
|
||
"------- PortAddress is %x\n"
|
||
"------- Interrupt Status is %x\n"
|
||
"------- BusNumber is %d\n"
|
||
"------- BusType is %d\n"
|
||
"------- AddressSpace is %d\n"
|
||
"------- Interrupt Mode is %d\n",
|
||
&New->NtNameForPort,
|
||
New->Controller.LowPart,
|
||
New->InterruptStatus.LowPart,
|
||
New->BusNumber,
|
||
New->InterfaceType,
|
||
New->AddressSpace,
|
||
New->InterruptMode
|
||
)
|
||
);
|
||
|
||
//
|
||
// We don't support any boards whose memory wraps around
|
||
// the physical address space.
|
||
//
|
||
|
||
if (SerialMemCompare(
|
||
New->Controller,
|
||
New->SpanOfController,
|
||
serialPhysicalMax,
|
||
(ULONG)0
|
||
) != AddressesAreDisjoint) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
New->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
43,
|
||
STATUS_SUCCESS,
|
||
SERIAL_DEVICE_TOO_HIGH,
|
||
New->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
New->SymbolicLinkName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Error in config record for %wZ\n"
|
||
"------ registers rap around physical memory\n",
|
||
&New->NtNameForPort)
|
||
);
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
if (SerialMemCompare(
|
||
New->InterruptStatus,
|
||
New->SpanOfInterruptStatus,
|
||
serialPhysicalMax,
|
||
(ULONG)0
|
||
) != AddressesAreDisjoint) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
New->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
44,
|
||
STATUS_SUCCESS,
|
||
SERIAL_STATUS_TOO_HIGH,
|
||
New->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
New->SymbolicLinkName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Error in config record for %wZ\n"
|
||
"------ status raps around physical memory\n",
|
||
&New->NtNameForPort)
|
||
);
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
//
|
||
// Make sure that the interrupt status address doesn't
|
||
// overlap the controller registers
|
||
//
|
||
|
||
if (SerialMemCompare(
|
||
New->InterruptStatus,
|
||
New->SpanOfInterruptStatus,
|
||
SerialPhysicalZero,
|
||
(ULONG)0
|
||
) != AddressesAreEqual) {
|
||
|
||
if (SerialMemCompare(
|
||
New->InterruptStatus,
|
||
New->SpanOfInterruptStatus,
|
||
New->Controller,
|
||
New->SpanOfController
|
||
) != AddressesAreDisjoint) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
New->Controller,
|
||
New->InterruptStatus,
|
||
0,
|
||
0,
|
||
0,
|
||
45,
|
||
STATUS_SUCCESS,
|
||
SERIAL_STATUS_CONTROL_CONFLICT,
|
||
New->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
New->SymbolicLinkName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Error in cofig record for %wZ\n"
|
||
"------- Interrupt status overlaps regular registers\n",
|
||
&New->NtNameForPort)
|
||
);
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Loop through all of the old configuration records making
|
||
// sure that this new record doesn't overlap with any of
|
||
// the old records.
|
||
//
|
||
|
||
if (!IsListEmpty(ConfigList)) {
|
||
|
||
PLIST_ENTRY CurrentConfigListEntry = ConfigList->Flink;
|
||
|
||
do {
|
||
|
||
PCONFIG_DATA CurrentSameIntConfig = CONTAINING_RECORD(
|
||
CurrentConfigListEntry,
|
||
CONFIG_DATA,
|
||
ConfigList
|
||
);
|
||
|
||
//
|
||
// We only care about this list if the elements are on the
|
||
// same bus as this new entry.
|
||
//
|
||
|
||
if ((CurrentSameIntConfig->InterfaceType == New->InterfaceType) &&
|
||
(CurrentSameIntConfig->AddressSpace == New->AddressSpace) &&
|
||
(CurrentSameIntConfig->BusNumber == New->BusNumber)) {
|
||
|
||
PLIST_ENTRY RootSameIntListEntry = &CurrentSameIntConfig->SameInterrupt;
|
||
PLIST_ENTRY CurrentSameIntListEntry = RootSameIntListEntry;
|
||
|
||
do {
|
||
|
||
PLIST_ENTRY RootSameStatusListEntry = &CONTAINING_RECORD(
|
||
CurrentSameIntListEntry,
|
||
CONFIG_DATA,
|
||
SameInterrupt
|
||
)->SameInterruptStatus;
|
||
PLIST_ENTRY CurrentSameStatusListEntry = RootSameStatusListEntry;
|
||
|
||
do {
|
||
|
||
PCONFIG_DATA OldConfig = CONTAINING_RECORD(
|
||
CurrentSameStatusListEntry,
|
||
CONFIG_DATA,
|
||
SameInterruptStatus
|
||
);
|
||
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: Comparing it to %wZ\n"
|
||
"------- already in the config list\n"
|
||
"------- PortAddress is %x\n"
|
||
"------- Interrupt Status is %x\n"
|
||
"------- BusNumber is %d\n"
|
||
"------- BusType is %d\n"
|
||
"------- AddressSpace is %d\n",
|
||
&OldConfig->NtNameForPort,
|
||
OldConfig->Controller.LowPart,
|
||
OldConfig->InterruptStatus.LowPart,
|
||
OldConfig->BusNumber,
|
||
OldConfig->InterfaceType,
|
||
OldConfig->AddressSpace
|
||
)
|
||
);
|
||
|
||
if (SerialMemCompare(
|
||
New->Controller,
|
||
New->SpanOfController,
|
||
OldConfig->Controller,
|
||
OldConfig->SpanOfController
|
||
) != AddressesAreDisjoint) {
|
||
|
||
//
|
||
// We don't want to log an error if the addresses
|
||
// are the same and the name is the same and
|
||
// the new item is from the firmware.
|
||
//
|
||
|
||
if (!((SerialMemCompare(
|
||
New->Controller,
|
||
New->SpanOfController,
|
||
OldConfig->Controller,
|
||
OldConfig->SpanOfController
|
||
) == AddressesAreEqual) &&
|
||
FirmwareAddition &&
|
||
RtlEqualUnicodeString(
|
||
&New->SymbolicLinkName,
|
||
&OldConfig->SymbolicLinkName,
|
||
TRUE
|
||
))) {
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
New->Controller,
|
||
OldConfig->Controller,
|
||
0,
|
||
0,
|
||
0,
|
||
46,
|
||
STATUS_SUCCESS,
|
||
SERIAL_CONTROL_OVERLAP,
|
||
New->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
New->SymbolicLinkName.Buffer,
|
||
OldConfig->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
OldConfig->SymbolicLinkName.Buffer
|
||
);
|
||
}
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Error in config record for %wZ\n"
|
||
"------- Register address overlaps with\n"
|
||
"------- previous serial device\n",
|
||
&New->NtNameForPort)
|
||
);
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
//
|
||
// If we have an interrupt status, make sure that
|
||
// it doesn't overlap with the old controllers
|
||
// registers.
|
||
//
|
||
|
||
if (SerialMemCompare(
|
||
New->InterruptStatus,
|
||
New->SpanOfInterruptStatus,
|
||
SerialPhysicalZero,
|
||
(ULONG)0
|
||
) != AddressesAreEqual) {
|
||
|
||
if (SerialMemCompare(
|
||
New->InterruptStatus,
|
||
New->SpanOfInterruptStatus,
|
||
OldConfig->Controller,
|
||
OldConfig->SpanOfController
|
||
) != AddressesAreDisjoint) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
New->Controller,
|
||
OldConfig->Controller,
|
||
0,
|
||
0,
|
||
0,
|
||
47,
|
||
STATUS_SUCCESS,
|
||
SERIAL_STATUS_OVERLAP,
|
||
New->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
New->SymbolicLinkName.Buffer,
|
||
OldConfig->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
OldConfig->SymbolicLinkName.Buffer
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Error in config record for %wZ\n"
|
||
"------- status address overlaps with\n"
|
||
"------- previous serial device registers\n",
|
||
&New->NtNameForPort)
|
||
);
|
||
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
//
|
||
// If the old configuration record has an interrupt
|
||
// status, the addresses should not overlap.
|
||
//
|
||
|
||
if (SerialMemCompare(
|
||
OldConfig->InterruptStatus,
|
||
OldConfig->SpanOfInterruptStatus,
|
||
SerialPhysicalZero,
|
||
(ULONG)0
|
||
) != AddressesAreEqual) {
|
||
|
||
if (SerialMemCompare(
|
||
New->InterruptStatus,
|
||
New->SpanOfInterruptStatus,
|
||
OldConfig->InterruptStatus,
|
||
OldConfig->SpanOfInterruptStatus
|
||
) == AddressesOverlap) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
New->Controller,
|
||
OldConfig->Controller,
|
||
0,
|
||
0,
|
||
0,
|
||
48,
|
||
STATUS_SUCCESS,
|
||
SERIAL_STATUS_STATUS_OVERLAP,
|
||
New->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
New->SymbolicLinkName.Buffer,
|
||
OldConfig->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
OldConfig->SymbolicLinkName.Buffer
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Error in config record for %wZ\n"
|
||
"------- status address overlaps with\n"
|
||
"------- previous serial status register\n",
|
||
&New->NtNameForPort)
|
||
);
|
||
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// If the old configuration record has a status
|
||
// address make sure that it doesn't overlap with
|
||
// the new controllers address. (Interrupt status
|
||
// overlap is take care of above.
|
||
//
|
||
|
||
if (SerialMemCompare(
|
||
OldConfig->InterruptStatus,
|
||
OldConfig->SpanOfInterruptStatus,
|
||
SerialPhysicalZero,
|
||
(ULONG)0
|
||
) != AddressesAreEqual) {
|
||
|
||
if (SerialMemCompare(
|
||
New->Controller,
|
||
New->SpanOfController,
|
||
OldConfig->InterruptStatus,
|
||
OldConfig->SpanOfInterruptStatus
|
||
) == AddressesOverlap) {
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
New->Controller,
|
||
OldConfig->Controller,
|
||
0,
|
||
0,
|
||
0,
|
||
49,
|
||
STATUS_SUCCESS,
|
||
SERIAL_CONTROL_STATUS_OVERLAP,
|
||
New->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
New->SymbolicLinkName.Buffer,
|
||
OldConfig->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
OldConfig->SymbolicLinkName.Buffer
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Error in config record for %wZ\n"
|
||
"------- register address overlaps with\n"
|
||
"------- previous serial status register\n",
|
||
&New->NtNameForPort)
|
||
);
|
||
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
CurrentSameStatusListEntry = CurrentSameStatusListEntry->Flink;
|
||
|
||
} while (CurrentSameStatusListEntry != RootSameStatusListEntry);
|
||
|
||
CurrentSameIntListEntry = CurrentSameIntListEntry->Flink;
|
||
|
||
} while (CurrentSameIntListEntry != RootSameIntListEntry);
|
||
|
||
}
|
||
|
||
CurrentConfigListEntry = CurrentConfigListEntry->Flink;
|
||
|
||
} while (CurrentConfigListEntry != ConfigList);
|
||
}
|
||
|
||
//
|
||
// If there is an interrupt status then we
|
||
// loop through the config list again to look
|
||
// for a config record with the same interrupt
|
||
// status (on the same bus).
|
||
//
|
||
|
||
if ((SerialMemCompare(
|
||
New->InterruptStatus,
|
||
New->SpanOfInterruptStatus,
|
||
SerialPhysicalZero,
|
||
(ULONG)0
|
||
) != AddressesAreEqual) &&
|
||
!IsListEmpty(ConfigList)) {
|
||
|
||
//
|
||
// We have an interrupt status. Loop through all
|
||
// previous records, look for an existing interrupt status
|
||
// the same as the current interrupt status.
|
||
//
|
||
|
||
PLIST_ENTRY CurrentConfigListEntry = ConfigList->Flink;
|
||
|
||
do {
|
||
|
||
PCONFIG_DATA CurrentSameIntConfig = CONTAINING_RECORD(
|
||
CurrentConfigListEntry,
|
||
CONFIG_DATA,
|
||
ConfigList
|
||
);
|
||
|
||
//
|
||
// We only care about this list if the elements are on the
|
||
// same bus as this new entry. (There interrupts must therfore
|
||
// also be the on the same bus. We will check that momentarily).
|
||
//
|
||
// We don't check here for the dissimilar interrupts since that
|
||
// could cause us to miss the error of having the same interrupt
|
||
// status but different interrupts - which is bizzare.
|
||
//
|
||
|
||
if ((CurrentSameIntConfig->InterfaceType == New->InterfaceType) &&
|
||
(CurrentSameIntConfig->AddressSpace == New->AddressSpace) &&
|
||
(CurrentSameIntConfig->BusNumber == New->BusNumber)) {
|
||
|
||
PLIST_ENTRY RootSameIntListEntry = &CurrentSameIntConfig->SameInterrupt;
|
||
PLIST_ENTRY CurrentSameIntListEntry = RootSameIntListEntry;
|
||
|
||
do {
|
||
|
||
PLIST_ENTRY RootSameStatusListEntry = &CONTAINING_RECORD(
|
||
CurrentSameIntListEntry,
|
||
CONFIG_DATA,
|
||
SameInterrupt
|
||
)->SameInterruptStatus;
|
||
PLIST_ENTRY CurrentSameStatusListEntry = RootSameStatusListEntry;
|
||
|
||
do {
|
||
|
||
PCONFIG_DATA OldConfig = CONTAINING_RECORD(
|
||
CurrentSameStatusListEntry,
|
||
CONFIG_DATA,
|
||
SameInterruptStatus
|
||
);
|
||
|
||
//
|
||
// If the interrupt status
|
||
//
|
||
|
||
if (SerialMemCompare(
|
||
OldConfig->InterruptStatus,
|
||
OldConfig->SpanOfInterruptStatus,
|
||
New->InterruptStatus,
|
||
New->SpanOfInterruptStatus
|
||
) == AddressesAreEqual) {
|
||
|
||
//
|
||
// Same card. Now make sure that they
|
||
// are using the same interrupt parameters.
|
||
//
|
||
|
||
if ((New->OriginalIrql != OldConfig->OriginalIrql) ||
|
||
(New->OriginalVector != OldConfig->OriginalVector)) {
|
||
|
||
//
|
||
// We won't put this into the configuration
|
||
// list.
|
||
//
|
||
|
||
SerialLogError(
|
||
DriverObject,
|
||
NULL,
|
||
New->Controller,
|
||
OldConfig->Controller,
|
||
0,
|
||
0,
|
||
0,
|
||
50,
|
||
STATUS_SUCCESS,
|
||
SERIAL_MULTI_INTERRUPT_CONFLICT,
|
||
New->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
New->SymbolicLinkName.Buffer,
|
||
OldConfig->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
OldConfig->SymbolicLinkName.Buffer
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Configuration error for %wZ\n"
|
||
"------- Same multiport - different interrupts\n",
|
||
&New->NtNameForPort)
|
||
);
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
//
|
||
// Place this new record on the SameInterruptStatus
|
||
// as the old record.
|
||
//
|
||
|
||
InitializeListHead(&New->SameInterruptStatus);
|
||
|
||
InsertTailList(
|
||
&OldConfig->SameInterruptStatus,
|
||
&New->SameInterruptStatus
|
||
);
|
||
|
||
return TRUE;
|
||
|
||
}
|
||
|
||
CurrentSameStatusListEntry = CurrentSameStatusListEntry->Flink;
|
||
|
||
} while (CurrentSameStatusListEntry != RootSameStatusListEntry);
|
||
|
||
CurrentSameIntListEntry = CurrentSameIntListEntry->Flink;
|
||
|
||
} while (CurrentSameIntListEntry != RootSameIntListEntry);
|
||
|
||
}
|
||
|
||
CurrentConfigListEntry = CurrentConfigListEntry->Flink;
|
||
|
||
} while (CurrentConfigListEntry != ConfigList);
|
||
|
||
}
|
||
|
||
//
|
||
// Go through the list again looking for previous devices
|
||
// with the same interrupt.
|
||
//
|
||
|
||
if (!New->Jensen && !IsListEmpty(ConfigList)) {
|
||
|
||
PLIST_ENTRY CurrentConfigListEntry = ConfigList->Flink;
|
||
|
||
do {
|
||
|
||
PCONFIG_DATA OldConfig = CONTAINING_RECORD(
|
||
CurrentConfigListEntry,
|
||
CONFIG_DATA,
|
||
ConfigList
|
||
);
|
||
|
||
//
|
||
// We only care about interrupts that are on
|
||
// the same bus.
|
||
//
|
||
|
||
if ((OldConfig->InterfaceType == New->InterfaceType) &&
|
||
(OldConfig->BusNumber == New->BusNumber)) {
|
||
|
||
if ((OldConfig->OriginalIrql == New->OriginalIrql) &&
|
||
(OldConfig->OriginalVector == New->OriginalVector)) {
|
||
|
||
InsertTailList(
|
||
&OldConfig->SameInterrupt,
|
||
&New->SameInterrupt
|
||
);
|
||
|
||
return TRUE;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
CurrentConfigListEntry = CurrentConfigListEntry->Flink;
|
||
|
||
} while (CurrentConfigListEntry != ConfigList);
|
||
|
||
}
|
||
|
||
//
|
||
// This port doesn't appear to be sharing with
|
||
// anything. Just put it on the config list.
|
||
//
|
||
|
||
InsertTailList(
|
||
ConfigList,
|
||
&New->ConfigList
|
||
);
|
||
|
||
return TRUE;
|
||
|
||
}
|
||
|
||
PVOID
|
||
SerialGetMappedAddress(
|
||
IN INTERFACE_TYPE BusType,
|
||
IN ULONG BusNumber,
|
||
PHYSICAL_ADDRESS IoAddress,
|
||
ULONG NumberOfBytes,
|
||
ULONG AddressSpace,
|
||
PBOOLEAN MappedAddress
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine maps an IO address to system address space.
|
||
|
||
Arguments:
|
||
|
||
BusType - what type of bus - eisa, mca, isa
|
||
IoBusNumber - which IO bus (for machines with multiple buses).
|
||
IoAddress - base device address to be mapped.
|
||
NumberOfBytes - number of bytes for which address is valid.
|
||
AddressSpace - Denotes whether the address is in io space or memory.
|
||
MappedAddress - indicates whether the address was mapped.
|
||
This only has meaning if the address returned
|
||
is non-null.
|
||
|
||
Return Value:
|
||
|
||
Mapped address
|
||
|
||
--*/
|
||
|
||
{
|
||
PHYSICAL_ADDRESS cardAddress;
|
||
PVOID address;
|
||
|
||
if (!HalTranslateBusAddress(
|
||
BusType,
|
||
BusNumber,
|
||
IoAddress,
|
||
&AddressSpace,
|
||
&cardAddress
|
||
)) {
|
||
|
||
return NULL;
|
||
|
||
}
|
||
|
||
//
|
||
// Map the device base address into the virtual address space
|
||
// if the address is in memory space.
|
||
//
|
||
|
||
if (!AddressSpace) {
|
||
|
||
address = MmMapIoSpace(
|
||
cardAddress,
|
||
NumberOfBytes,
|
||
FALSE
|
||
);
|
||
|
||
*MappedAddress = (BOOLEAN)((address)?(TRUE):(FALSE));
|
||
|
||
|
||
} else {
|
||
|
||
address = (PVOID)cardAddress.LowPart;
|
||
*MappedAddress = FALSE;
|
||
|
||
}
|
||
|
||
return address;
|
||
|
||
}
|
||
|
||
VOID
|
||
SerialSetupExternalNaming(
|
||
IN PSERIAL_DEVICE_EXTENSION Extension
|
||
)
|
||
|
||
/*++
|
||
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
UNICODE_STRING fullLinkName;
|
||
NTSTATUS status;
|
||
|
||
//
|
||
// Form the full symbolic link name we wish to create.
|
||
//
|
||
|
||
RtlInitUnicodeString(
|
||
&fullLinkName,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Allocate some pool for the name.
|
||
//
|
||
|
||
fullLinkName.MaximumLength = (sizeof(L"\\")*2) +
|
||
Extension->ObjectDirectory.Length+
|
||
Extension->SymbolicLinkName.Length+
|
||
sizeof(WCHAR);
|
||
|
||
|
||
|
||
fullLinkName.Buffer = ExAllocatePool(
|
||
PagedPool,
|
||
fullLinkName.MaximumLength
|
||
);
|
||
|
||
if (!fullLinkName.Buffer) {
|
||
|
||
//
|
||
// Couldn't allocate space for the name.
|
||
//
|
||
|
||
SerialLogError(
|
||
Extension->DeviceObject->DriverObject,
|
||
Extension->DeviceObject,
|
||
Extension->OriginalController,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
51,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't allocate space for the symbolic \n"
|
||
"------- name for creating the link\n"
|
||
"------- for port %wZ\n",
|
||
&Extension->DeviceName)
|
||
);
|
||
|
||
} else {
|
||
|
||
RtlZeroMemory(
|
||
fullLinkName.Buffer,
|
||
fullLinkName.MaximumLength
|
||
);
|
||
|
||
RtlAppendUnicodeToString(
|
||
&fullLinkName,
|
||
L"\\"
|
||
);
|
||
|
||
RtlAppendUnicodeStringToString(
|
||
&fullLinkName,
|
||
&Extension->ObjectDirectory
|
||
);
|
||
|
||
RtlAppendUnicodeToString(
|
||
&fullLinkName,
|
||
L"\\"
|
||
);
|
||
|
||
RtlAppendUnicodeStringToString(
|
||
&fullLinkName,
|
||
&Extension->SymbolicLinkName
|
||
);
|
||
|
||
|
||
status = IoCreateSymbolicLink(
|
||
&fullLinkName,
|
||
&Extension->DeviceName
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Oh well, couldn't create the symbolic link. No point
|
||
// in trying to create the device map entry.
|
||
//
|
||
|
||
SerialLogError(
|
||
Extension->DeviceObject->DriverObject,
|
||
Extension->DeviceObject,
|
||
Extension->OriginalController,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
52,
|
||
status,
|
||
SERIAL_NO_SYMLINK_CREATED,
|
||
Extension->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
Extension->SymbolicLinkName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't create the symbolic link\n"
|
||
"------- for port %wZ\n",
|
||
&Extension->DeviceName)
|
||
);
|
||
|
||
} else {
|
||
|
||
Extension->CreatedSymbolicLink = TRUE;
|
||
|
||
status = RtlWriteRegistryValue(
|
||
RTL_REGISTRY_DEVICEMAP,
|
||
L"SERIALCOMM",
|
||
Extension->NtNameForPort.Buffer,
|
||
REG_SZ,
|
||
Extension->SymbolicLinkName.Buffer,
|
||
Extension->SymbolicLinkName.Length+sizeof(WCHAR)
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
SerialLogError(
|
||
Extension->DeviceObject->DriverObject,
|
||
Extension->DeviceObject,
|
||
Extension->OriginalController,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
53,
|
||
status,
|
||
SERIAL_NO_DEVICE_MAP_CREATED,
|
||
Extension->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
Extension->SymbolicLinkName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't create the device map entry\n"
|
||
"------- for port %wZ\n",
|
||
&Extension->DeviceName)
|
||
);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
ExFreePool(fullLinkName.Buffer);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
VOID
|
||
SerialCleanupExternalNaming(
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
UNICODE_STRING fullLinkName;
|
||
|
||
SerialDump(
|
||
SERDIAG3,
|
||
("SERIAL: In SerialCleanupExternalNaming for\n"
|
||
"------- extension: %x of port %wZ\n",
|
||
Extension,&Extension->DeviceName)
|
||
);
|
||
|
||
//
|
||
// 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->ObjectDirectory.Buffer &&
|
||
Extension->SymbolicLinkName.Buffer &&
|
||
Extension->CreatedSymbolicLink) {
|
||
|
||
//
|
||
// Form the full symbolic link name we wish to create.
|
||
//
|
||
|
||
RtlInitUnicodeString(
|
||
&fullLinkName,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Allocate some pool for the name.
|
||
//
|
||
|
||
fullLinkName.MaximumLength = (sizeof(L"\\")*2) +
|
||
Extension->ObjectDirectory.Length+
|
||
Extension->SymbolicLinkName.Length+
|
||
sizeof(WCHAR);
|
||
|
||
fullLinkName.Buffer = ExAllocatePool(
|
||
PagedPool,
|
||
fullLinkName.MaximumLength
|
||
);
|
||
|
||
if (!fullLinkName.Buffer) {
|
||
|
||
//
|
||
// Couldn't allocate space for the name. Just go on
|
||
// to the device map stuff.
|
||
//
|
||
|
||
SerialLogError(
|
||
Extension->DeviceObject->DriverObject,
|
||
Extension->DeviceObject,
|
||
Extension->OriginalController,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
54,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't allocate space for the symbolic \n"
|
||
"------- name for creating the link\n"
|
||
"------- for port %wZ on cleanup\n",
|
||
&Extension->DeviceName)
|
||
);
|
||
|
||
} else {
|
||
|
||
RtlZeroMemory(
|
||
fullLinkName.Buffer,
|
||
fullLinkName.MaximumLength
|
||
);
|
||
|
||
RtlAppendUnicodeToString(
|
||
&fullLinkName,
|
||
L"\\"
|
||
);
|
||
|
||
RtlAppendUnicodeStringToString(
|
||
&fullLinkName,
|
||
&Extension->ObjectDirectory
|
||
);
|
||
|
||
RtlAppendUnicodeToString(
|
||
&fullLinkName,
|
||
L"\\"
|
||
);
|
||
|
||
RtlAppendUnicodeStringToString(
|
||
&fullLinkName,
|
||
&Extension->SymbolicLinkName
|
||
);
|
||
|
||
IoDeleteSymbolicLink(&fullLinkName);
|
||
|
||
ExFreePool(fullLinkName.Buffer);
|
||
|
||
}
|
||
|
||
//
|
||
// We're cleaning up here. One reason we're cleaning up
|
||
// is that we couldn't allocate space for the NtNameOfPort.
|
||
//
|
||
|
||
if (Extension->NtNameForPort.Buffer) {
|
||
|
||
NTSTATUS status;
|
||
|
||
status = RtlDeleteRegistryValue(
|
||
RTL_REGISTRY_DEVICEMAP,
|
||
L"SERIALCOMM",
|
||
Extension->NtNameForPort.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->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
Extension->SymbolicLinkName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Couldn't delete value entry %wZ\n",
|
||
&Extension->DeviceName)
|
||
);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
SERIAL_MEM_COMPARES
|
||
SerialMemCompare(
|
||
IN PHYSICAL_ADDRESS A,
|
||
IN ULONG SpanOfA,
|
||
IN PHYSICAL_ADDRESS B,
|
||
IN ULONG SpanOfB
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Compare two phsical address.
|
||
|
||
Arguments:
|
||
|
||
A - One half of the comparison.
|
||
|
||
SpanOfA - In units of bytes, the span of A.
|
||
|
||
B - One half of the comparison.
|
||
|
||
SpanOfB - In units of bytes, the span of B.
|
||
|
||
|
||
Return Value:
|
||
|
||
The result of the comparison.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
LARGE_INTEGER a;
|
||
LARGE_INTEGER b;
|
||
|
||
LARGE_INTEGER lower;
|
||
ULONG lowerSpan;
|
||
LARGE_INTEGER higher;
|
||
|
||
a = A;
|
||
b = B;
|
||
|
||
if (a.QuadPart == b.QuadPart) {
|
||
|
||
return AddressesAreEqual;
|
||
|
||
}
|
||
|
||
if (a.QuadPart > b.QuadPart) {
|
||
|
||
higher = a;
|
||
lower = b;
|
||
lowerSpan = SpanOfB;
|
||
|
||
} else {
|
||
|
||
higher = b;
|
||
lower = a;
|
||
lowerSpan = SpanOfA;
|
||
|
||
}
|
||
|
||
if ((higher.QuadPart - lower.QuadPart) >= lowerSpan) {
|
||
|
||
return AddressesAreDisjoint;
|
||
|
||
}
|
||
|
||
return AddressesOverlap;
|
||
|
||
}
|
||
|
||
VOID
|
||
SerialLogError(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PDEVICE_OBJECT DeviceObject OPTIONAL,
|
||
IN PHYSICAL_ADDRESS P1,
|
||
IN PHYSICAL_ADDRESS P2,
|
||
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:
|
||
|
||
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.
|
||
|
||
P1,P2 - If phyical addresses for the controller ports involved
|
||
with the error are available, put them through as dump data.
|
||
|
||
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;
|
||
SHORT dumpToAllocate = 0;
|
||
PUCHAR ptrToFirstInsert;
|
||
PUCHAR ptrToSecondInsert;
|
||
|
||
|
||
if (ARGUMENT_PRESENT(DeviceObject)) {
|
||
|
||
objectToUse = DeviceObject;
|
||
|
||
} else {
|
||
|
||
objectToUse = DriverObject;
|
||
|
||
}
|
||
|
||
if (SerialMemCompare(
|
||
P1,
|
||
(ULONG)1,
|
||
SerialPhysicalZero,
|
||
(ULONG)1
|
||
) != AddressesAreEqual) {
|
||
|
||
dumpToAllocate = (SHORT)sizeof(PHYSICAL_ADDRESS);
|
||
|
||
}
|
||
|
||
if (SerialMemCompare(
|
||
P2,
|
||
(ULONG)1,
|
||
SerialPhysicalZero,
|
||
(ULONG)1
|
||
) != AddressesAreEqual) {
|
||
|
||
dumpToAllocate += (SHORT)sizeof(PHYSICAL_ADDRESS);
|
||
|
||
}
|
||
|
||
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;
|
||
|
||
if (dumpToAllocate) {
|
||
|
||
RtlCopyMemory(
|
||
&errorLogEntry->DumpData[0],
|
||
&P1,
|
||
sizeof(PHYSICAL_ADDRESS)
|
||
);
|
||
|
||
if (dumpToAllocate > sizeof(PHYSICAL_ADDRESS)) {
|
||
|
||
RtlCopyMemory(
|
||
((PUCHAR)&errorLogEntry->DumpData[0])+sizeof(PHYSICAL_ADDRESS),
|
||
&P2,
|
||
sizeof(PHYSICAL_ADDRESS)
|
||
);
|
||
|
||
ptrToFirstInsert =
|
||
((PUCHAR)&errorLogEntry->DumpData[0])+(2*sizeof(PHYSICAL_ADDRESS));
|
||
|
||
} else {
|
||
|
||
ptrToFirstInsert =
|
||
((PUCHAR)&errorLogEntry->DumpData[0])+sizeof(PHYSICAL_ADDRESS);
|
||
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
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);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
VOID
|
||
SerialUnReportResourcesDevice(
|
||
IN PSERIAL_DEVICE_EXTENSION Extension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine *un*reports the resources used for a device that
|
||
is "ready" to run. If some conflict was detected, it doesn't
|
||
matter, the reources are *un*reported.
|
||
|
||
Arguments:
|
||
|
||
Extension - The device extension of the device we are *un*reporting
|
||
resources for.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
CM_RESOURCE_LIST resourceList;
|
||
ULONG sizeOfResourceList = 0;
|
||
UNICODE_STRING className;
|
||
BOOLEAN junkBoolean;
|
||
|
||
SerialDump(
|
||
SERDIAG3,
|
||
("SERIAL: In SerialUnreportResourcesDevice\n"
|
||
"------- for extension %x of port %wZ\n",
|
||
Extension,&Extension->DeviceName)
|
||
);
|
||
RtlZeroMemory(
|
||
&resourceList,
|
||
sizeof(CM_RESOURCE_LIST)
|
||
);
|
||
|
||
resourceList.Count = 0;
|
||
|
||
RtlInitUnicodeString(
|
||
&className,
|
||
L"LOADED SERIAL DRIVER RESOURCES"
|
||
);
|
||
|
||
IoReportResourceUsage(
|
||
&className,
|
||
Extension->DeviceObject->DriverObject,
|
||
NULL,
|
||
0,
|
||
Extension->DeviceObject,
|
||
&resourceList,
|
||
sizeof(CM_RESOURCE_LIST),
|
||
FALSE,
|
||
&junkBoolean
|
||
);
|
||
|
||
}
|
||
|
||
VOID
|
||
SerialReportResourcesDevice(
|
||
IN PSERIAL_DEVICE_EXTENSION Extension,
|
||
OUT BOOLEAN *ConflictDetected
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine reports the resources used for a device that
|
||
is "ready" to run. If some conflict was detected, it doesn't
|
||
matter, the reources are reported.
|
||
|
||
Arguments:
|
||
|
||
Extension - The device extension of the device we are reporting
|
||
resources for.
|
||
|
||
ConflictDetected - Pointer to a boolean that we will pass
|
||
to the resource reporting code.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PCM_RESOURCE_LIST resourceList;
|
||
ULONG sizeOfResourceList;
|
||
ULONG countOfPartials;
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR partial;
|
||
UNICODE_STRING className;
|
||
|
||
SerialDump(
|
||
SERDIAG3,
|
||
("SERIAL: In SerialReportResourcesDevice\n"
|
||
"------- for extension %x of port %wZ\n",
|
||
Extension,&Extension->DeviceName)
|
||
);
|
||
|
||
//
|
||
// The resource list for a device will consist of
|
||
//
|
||
// The resource list record itself with a count
|
||
// of one for the single "built in" full resource
|
||
// descriptor.
|
||
//
|
||
// The built-in full resource descriptor will contain
|
||
// the bus type and busnumber and the built in partial
|
||
// resource list.
|
||
//
|
||
// The built in partial resource list will have at
|
||
// least a count of 2:
|
||
//
|
||
// 1) The interrupt that this device will be
|
||
// coming in on.
|
||
//
|
||
// 2) The base register physical address and it's span.
|
||
//
|
||
// The built in partial resource list will have a
|
||
// count of 3 if it has an interrupt status address
|
||
// That interrupt status address will consist of
|
||
// the physical address and the span (normally 1).
|
||
//
|
||
|
||
countOfPartials = Extension->InterruptStatus?3:2;
|
||
sizeOfResourceList = sizeof(CM_RESOURCE_LIST) +
|
||
(sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR)*
|
||
(countOfPartials-1));
|
||
|
||
resourceList = ExAllocatePool(
|
||
PagedPool,
|
||
sizeOfResourceList
|
||
);
|
||
|
||
if (!resourceList) {
|
||
|
||
//
|
||
// Oh well, can't allocate the memory. Act as though
|
||
// we succeeded.
|
||
//
|
||
|
||
SerialLogError(
|
||
Extension->DeviceObject->DriverObject,
|
||
Extension->DeviceObject,
|
||
Extension->OriginalController,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
56,
|
||
STATUS_SUCCESS,
|
||
SERIAL_INSUFFICIENT_RESOURCES,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
return;
|
||
|
||
}
|
||
|
||
RtlZeroMemory(
|
||
resourceList,
|
||
sizeOfResourceList
|
||
);
|
||
|
||
resourceList->Count = 1;
|
||
|
||
|
||
resourceList->List[0].InterfaceType = Extension->InterfaceType;
|
||
resourceList->List[0].BusNumber = Extension->BusNumber;
|
||
resourceList->List[0].PartialResourceList.Count = countOfPartials;
|
||
partial = &resourceList->List[0].PartialResourceList.PartialDescriptors[0];
|
||
|
||
//
|
||
// Account for the space used by the controller.
|
||
//
|
||
|
||
partial->Type = CmResourceTypePort;
|
||
partial->ShareDisposition = CmResourceShareDeviceExclusive;
|
||
partial->Flags = (USHORT)Extension->AddressSpace;
|
||
partial->u.Port.Start = Extension->OriginalController;
|
||
partial->u.Port.Length = Extension->SpanOfController;
|
||
|
||
partial++;
|
||
|
||
//
|
||
// Report the interrupt information.
|
||
//
|
||
|
||
partial->Type = CmResourceTypeInterrupt;
|
||
|
||
if (Extension->InterruptShareable) {
|
||
|
||
partial->ShareDisposition = CmResourceShareShared;
|
||
|
||
} else {
|
||
|
||
partial->ShareDisposition = CmResourceShareDriverExclusive;
|
||
|
||
}
|
||
|
||
if (Extension->InterruptMode == Latched) {
|
||
|
||
partial->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
|
||
|
||
} else {
|
||
|
||
partial->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
|
||
|
||
}
|
||
|
||
partial->u.Interrupt.Vector = Extension->OriginalVector;
|
||
partial->u.Interrupt.Level = Extension->OriginalIrql;
|
||
|
||
//
|
||
// We have an interrupt status register. Report it.
|
||
//
|
||
|
||
if (countOfPartials == 3) {
|
||
|
||
partial++;
|
||
|
||
partial->Type = CmResourceTypePort;
|
||
partial->ShareDisposition = CmResourceShareDriverExclusive;
|
||
partial->Flags = (USHORT)Extension->AddressSpace;
|
||
partial->u.Port.Start = Extension->OriginalInterruptStatus;
|
||
partial->u.Port.Length = Extension->SpanOfInterruptStatus;
|
||
|
||
}
|
||
|
||
RtlInitUnicodeString(
|
||
&className,
|
||
L"LOADED SERIAL DRIVER RESOURCES"
|
||
);
|
||
|
||
IoReportResourceUsage(
|
||
&className,
|
||
Extension->DeviceObject->DriverObject,
|
||
NULL,
|
||
0,
|
||
Extension->DeviceObject,
|
||
resourceList,
|
||
sizeOfResourceList,
|
||
FALSE,
|
||
ConflictDetected
|
||
);
|
||
|
||
ExFreePool(resourceList);
|
||
|
||
}
|