3801 lines
101 KiB
C
3801 lines
101 KiB
C
/*++
|
||
|
||
Copyright (c) 1991, 1992, 1993 - 1997 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
|
||
|
||
--*/
|
||
|
||
#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
|
||
|
||
//
|
||
// All our global variables except DebugLevel stashed in one
|
||
// little package
|
||
//
|
||
SERIAL_GLOBALS SerialGlobals;
|
||
|
||
static const PHYSICAL_ADDRESS SerialPhysicalZero = {0};
|
||
|
||
//
|
||
// We use this to query into the registry as to whether we
|
||
// should break at driver entry.
|
||
//
|
||
|
||
SERIAL_FIRMWARE_DATA driverDefaults;
|
||
|
||
//
|
||
// This is exported from the kernel. It is used to point
|
||
// to the address that the kernel debugger is using.
|
||
//
|
||
extern PUCHAR *KdComPortInUse;
|
||
|
||
//
|
||
// INIT - only needed during init and then can be disposed
|
||
// PAGESRP0 - always paged / never locked
|
||
// PAGESER - must be locked when a device is open, else paged
|
||
//
|
||
//
|
||
// INIT is used for DriverEntry() specific code
|
||
//
|
||
// PAGESRP0 is used for code that is not often called and has nothing
|
||
// to do with I/O performance. An example, IRP_MJ_PNP/IRP_MN_START_DEVICE
|
||
// support functions
|
||
//
|
||
// PAGESER is used for code that needs to be locked after an open for both
|
||
// performance and IRQL reasons.
|
||
//
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT,DriverEntry)
|
||
|
||
#pragma alloc_text(PAGESRP0, SerialInitMultiPort)
|
||
#pragma alloc_text(PAGESRP0, SerialInitOneController)
|
||
#pragma alloc_text(PAGESRP0, SerialInitController)
|
||
#pragma alloc_text(PAGESRP0, SerialFindInitController)
|
||
#pragma alloc_text(PAGESRP0, SerialGetMappedAddress)
|
||
#pragma alloc_text(PAGESRP0, SerialRemoveDevObj)
|
||
#pragma alloc_text(PAGESRP0, SerialReleaseResources)
|
||
#pragma alloc_text(PAGESRP0, SerialUnload)
|
||
#pragma alloc_text(PAGESRP0, SerialMemCompare)
|
||
|
||
|
||
//
|
||
// PAGESER handled is keyed off of SerialReset, so SerialReset
|
||
// must remain in PAGESER for things to work properly
|
||
//
|
||
|
||
#pragma alloc_text(PAGESER, SerialGetDivisorFromBaud)
|
||
#pragma alloc_text(PAGESER, SerialReset)
|
||
#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:
|
||
|
||
Always STATUS_SUCCESS
|
||
|
||
--*/
|
||
|
||
{
|
||
RTL_QUERY_REGISTRY_TABLE jensenTable[2] = {0};
|
||
UNICODE_STRING jensenData;
|
||
UNICODE_STRING jensenValue;
|
||
BOOLEAN jensenDetected;
|
||
PUCHAR jensenBuffer;
|
||
|
||
//
|
||
// Lock the paged code in their frames
|
||
//
|
||
|
||
PVOID lockPtr = MmLockPagableCodeSection(SerialReset);
|
||
|
||
PAGED_CODE();
|
||
|
||
|
||
ASSERT(SerialGlobals.PAGESER_Handle == NULL);
|
||
#if DBG
|
||
SerialGlobals.PAGESER_Count = 0;
|
||
#endif
|
||
SerialGlobals.PAGESER_Handle = lockPtr;
|
||
|
||
SerialGlobals.RegistryPath.MaximumLength = RegistryPath->MaximumLength;
|
||
SerialGlobals.RegistryPath.Length = RegistryPath->Length;
|
||
SerialGlobals.RegistryPath.Buffer
|
||
= ExAllocatePool(PagedPool, SerialGlobals.RegistryPath.MaximumLength);
|
||
|
||
if (SerialGlobals.RegistryPath.Buffer == NULL) {
|
||
MmUnlockPagableImageSection(lockPtr);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlZeroMemory(SerialGlobals.RegistryPath.Buffer,
|
||
SerialGlobals.RegistryPath.MaximumLength);
|
||
RtlMoveMemory(SerialGlobals.RegistryPath.Buffer,
|
||
RegistryPath->Buffer, RegistryPath->Length);
|
||
|
||
//
|
||
// Initialize all our globals
|
||
//
|
||
|
||
InitializeListHead(&SerialGlobals.AllDevObjs);
|
||
|
||
//
|
||
// Call to find out default values to use for all the devices that the
|
||
// driver controls, including whether or not to break on entry.
|
||
//
|
||
|
||
SerialGetConfigDefaults(&driverDefaults, RegistryPath);
|
||
|
||
#if DBG
|
||
//
|
||
// Set global debug output level
|
||
//
|
||
SerialDebugLevel = driverDefaults.DebugLevel;
|
||
#endif
|
||
|
||
//
|
||
// Break on entry if requested via registry
|
||
//
|
||
|
||
if (driverDefaults.ShouldBreakOnEntry) {
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
|
||
//
|
||
// 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)));
|
||
|
||
|
||
//
|
||
// Initialize the Driver Object with driver's entry points
|
||
//
|
||
|
||
DriverObject->DriverUnload = SerialUnload;
|
||
DriverObject->DriverExtension->AddDevice = SerialAddDevice;
|
||
|
||
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_INTERNAL_DEVICE_CONTROL]
|
||
= SerialInternalIoControl;
|
||
DriverObject->MajorFunction[IRP_MJ_CREATE] = SerialCreateOpen;
|
||
DriverObject->MajorFunction[IRP_MJ_CLOSE] = SerialClose;
|
||
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = SerialCleanup;
|
||
DriverObject->MajorFunction[IRP_MJ_PNP] = SerialPnpDispatch;
|
||
DriverObject->MajorFunction[IRP_MJ_POWER] = SerialPowerDispatch;
|
||
|
||
DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION]
|
||
= SerialQueryInformationFile;
|
||
DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION]
|
||
= SerialSetInformationFile;
|
||
|
||
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL]
|
||
= SerialSystemControlDispatch;
|
||
|
||
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 machine's problems
|
||
// is that it's a Jensen.
|
||
//
|
||
|
||
jensenDetected = FALSE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Check to see if this is a Jensen alpha. If it is, then
|
||
// we'll 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"
|
||
L"\\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")
|
||
);
|
||
|
||
}
|
||
|
||
driverDefaults.JensenDetected = jensenDetected;
|
||
|
||
#if !defined(NO_LEGACY_DRIVERS)
|
||
|
||
#define SerialDoLegacyConversion() (~0)
|
||
|
||
//
|
||
// Enumerate and Initialize legacy devices if necessary. This should go away
|
||
// and be done by setup.
|
||
//
|
||
|
||
if (SerialDoLegacyConversion()) {
|
||
#if DBG
|
||
InterlockedIncrement(&SerialGlobals.PAGESER_Count);
|
||
#endif
|
||
(void)SerialEnumerateLegacy(DriverObject, RegistryPath, &driverDefaults);
|
||
#if DBG
|
||
InterlockedDecrement(&SerialGlobals.PAGESER_Count);
|
||
#endif
|
||
}
|
||
#endif // NO_LEGACY_DRIVERS
|
||
|
||
//
|
||
// Unlock pageable text
|
||
//
|
||
MmUnlockPagableImageSection(lockPtr);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
|
||
BOOLEAN
|
||
SerialCleanLists(IN PVOID Context)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Removes a device object from any of the serial linked lists it may
|
||
appear on.
|
||
|
||
Arguments:
|
||
|
||
Context - Actually a PSERIAL_DEVICE_EXTENSION (for the devobj being
|
||
removed).
|
||
|
||
Return Value:
|
||
|
||
Always TRUE
|
||
|
||
--*/
|
||
{
|
||
PSERIAL_DEVICE_EXTENSION pDevExt = (PSERIAL_DEVICE_EXTENSION)Context;
|
||
|
||
//
|
||
// If we are a multiport device, remove our entry
|
||
//
|
||
|
||
if (pDevExt->PortOnAMultiportCard) {
|
||
PSERIAL_MULTIPORT_DISPATCH pDispatch
|
||
= (PSERIAL_MULTIPORT_DISPATCH)pDevExt->OurIsrContext;
|
||
|
||
SerialDump(SERPNPPOWER,("SERIAL: CLEAN: removing multiport isr ext\n"));
|
||
|
||
pDispatch->Extensions[pDevExt->PortIndex - 1] = NULL;
|
||
|
||
if (pDevExt->Indexed == FALSE) {
|
||
pDispatch->UsablePortMask &= ~(1 << (pDevExt->PortIndex - 1));
|
||
pDispatch->MaskInverted &= ~(pDevExt->NewMaskInverted);
|
||
}
|
||
}
|
||
|
||
if (!IsListEmpty(&pDevExt->TopLevelSharers)) {
|
||
|
||
SerialDump(SERPNPPOWER,("SERIAL: CLEAN: Device is a sharer\n"));
|
||
|
||
//
|
||
// If we have siblings, the first becomes the sharer
|
||
//
|
||
|
||
if (!IsListEmpty(&pDevExt->MultiportSiblings)) {
|
||
PSERIAL_DEVICE_EXTENSION pNewRoot;
|
||
|
||
SerialDump(SERPNPPOWER,("SERIAL: CLEAN: Transferring to siblings\n"));
|
||
|
||
pNewRoot = CONTAINING_RECORD(pDevExt->MultiportSiblings.Flink,
|
||
SERIAL_DEVICE_EXTENSION,
|
||
MultiportSiblings);
|
||
|
||
//
|
||
// He should not be on there already
|
||
//
|
||
|
||
ASSERT(IsListEmpty(&pNewRoot->TopLevelSharers));
|
||
InsertTailList(&pDevExt->TopLevelSharers, &pNewRoot->TopLevelSharers);
|
||
|
||
}
|
||
|
||
//
|
||
// Remove ourselves
|
||
//
|
||
|
||
RemoveEntryList(&pDevExt->TopLevelSharers);
|
||
InitializeListHead(&pDevExt->TopLevelSharers);
|
||
|
||
//
|
||
// Now check the master list to see if anyone is left...
|
||
//
|
||
|
||
if (!IsListEmpty(&pDevExt->CIsrSw->SharerList)) {
|
||
//
|
||
// Others are chained on this interrupt, so we don't want to
|
||
// disconnect it.
|
||
//
|
||
|
||
pDevExt->Interrupt = NULL;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If this is part of a multiport board and we still have
|
||
// siblings, remove us from that list
|
||
//
|
||
|
||
if (!IsListEmpty(&pDevExt->MultiportSiblings)) {
|
||
SerialDump(SERPNPPOWER,("SERIAL: CLEAN: Has multiport siblings\n"));
|
||
RemoveEntryList(&pDevExt->MultiportSiblings);
|
||
InitializeListHead(&pDevExt->MultiportSiblings);
|
||
}
|
||
|
||
|
||
if (!IsListEmpty(&pDevExt->CommonInterruptObject)) {
|
||
|
||
SerialDump(SERPNPPOWER,("SERIAL: CLEAN: Common intobj member\n"));
|
||
|
||
RemoveEntryList(&pDevExt->CommonInterruptObject);
|
||
InitializeListHead(&pDevExt->CommonInterruptObject);
|
||
|
||
//
|
||
// Others are sharing this interrupt object so we detach ourselves
|
||
// from it this way instead of disconnecting.
|
||
//
|
||
|
||
pDevExt->Interrupt = NULL;
|
||
}
|
||
|
||
//
|
||
// AllDevObjs should never be empty since we have a sentinal
|
||
//
|
||
|
||
ASSERT(!IsListEmpty(&pDevExt->AllDevObjs));
|
||
|
||
RemoveEntryList(&pDevExt->AllDevObjs);
|
||
InitializeListHead(&pDevExt->AllDevObjs);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
SerialReleaseResources(IN PSERIAL_DEVICE_EXTENSION PDevExt)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Releases resources (not pool) stored in the device extension.
|
||
|
||
Arguments:
|
||
|
||
PDevExt - Pointer to the device extension to release resources from.
|
||
|
||
Return Value:
|
||
|
||
VOID
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
SerialDump(SERTRACECALLS,("SERIAL: Enter SerialReleaseResources\n"));
|
||
|
||
//
|
||
// Remove us from any lists we may be on
|
||
//
|
||
|
||
if (PDevExt->Interrupt != NULL) {
|
||
KeSynchronizeExecution(PDevExt->Interrupt, SerialCleanLists, PDevExt);
|
||
}
|
||
|
||
//
|
||
// SerialCleanLists can remove our interrupt from us...
|
||
//
|
||
|
||
if (PDevExt->Interrupt != NULL) {
|
||
//
|
||
// Stop servicing interrupts if we are the owner
|
||
//
|
||
|
||
SerialDump(SERPNPPOWER,
|
||
("SERIAL: Release - disconnecting interrupt %08X\n",
|
||
PDevExt->Interrupt));
|
||
|
||
IoDisconnectInterrupt(PDevExt->Interrupt);
|
||
PDevExt->Interrupt = NULL;
|
||
|
||
if (PDevExt->CIsrSw != NULL) {
|
||
ExFreePool(PDevExt->CIsrSw);
|
||
PDevExt->CIsrSw = NULL;
|
||
}
|
||
}
|
||
|
||
if (PDevExt->PortOnAMultiportCard) {
|
||
ULONG i;
|
||
|
||
//
|
||
// If we are the last device, free this memory
|
||
//
|
||
|
||
for (i = 0; i < SERIAL_MAX_PORTS_INDEXED; i++) {
|
||
if (((PSERIAL_MULTIPORT_DISPATCH)PDevExt->OurIsrContext)
|
||
->Extensions[i] != NULL) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (i == SERIAL_MAX_PORTS_INDEXED) {
|
||
SerialDump(SERPNPPOWER,("SERIAL: Release - freeing multi context\n"));
|
||
ExFreePool(PDevExt->OurIsrContext);
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Stop handling timers
|
||
//
|
||
|
||
SerialCancelTimer(&PDevExt->ReadRequestTotalTimer, PDevExt);
|
||
SerialCancelTimer(&PDevExt->ReadRequestIntervalTimer, PDevExt);
|
||
SerialCancelTimer(&PDevExt->WriteRequestTotalTimer, PDevExt);
|
||
SerialCancelTimer(&PDevExt->ImmediateTotalTimer, PDevExt);
|
||
SerialCancelTimer(&PDevExt->XoffCountTimer, PDevExt);
|
||
SerialCancelTimer(&PDevExt->LowerRTSTimer, PDevExt);
|
||
|
||
//
|
||
// Stop servicing DPC's
|
||
//
|
||
|
||
SerialRemoveQueueDpc(&PDevExt->CompleteWriteDpc, PDevExt);
|
||
SerialRemoveQueueDpc(&PDevExt->CompleteReadDpc, PDevExt);
|
||
SerialRemoveQueueDpc(&PDevExt->TotalReadTimeoutDpc, PDevExt);
|
||
SerialRemoveQueueDpc(&PDevExt->IntervalReadTimeoutDpc, PDevExt);
|
||
SerialRemoveQueueDpc(&PDevExt->TotalWriteTimeoutDpc, PDevExt);
|
||
SerialRemoveQueueDpc(&PDevExt->CommErrorDpc, PDevExt);
|
||
SerialRemoveQueueDpc(&PDevExt->CompleteImmediateDpc, PDevExt);
|
||
SerialRemoveQueueDpc(&PDevExt->TotalImmediateTimeoutDpc, PDevExt);
|
||
SerialRemoveQueueDpc(&PDevExt->CommWaitDpc, PDevExt);
|
||
SerialRemoveQueueDpc(&PDevExt->XoffCountTimeoutDpc, PDevExt);
|
||
SerialRemoveQueueDpc(&PDevExt->XoffCountCompleteDpc, PDevExt);
|
||
SerialRemoveQueueDpc(&PDevExt->StartTimerLowerRTSDpc, PDevExt);
|
||
SerialRemoveQueueDpc(&PDevExt->PerhapsLowerRTSDpc, PDevExt);
|
||
|
||
|
||
|
||
//
|
||
// If necessary, unmap the device registers.
|
||
//
|
||
|
||
if (PDevExt->UnMapRegisters) {
|
||
MmUnmapIoSpace(PDevExt->Controller, PDevExt->SpanOfController);
|
||
}
|
||
|
||
if (PDevExt->UnMapStatus) {
|
||
MmUnmapIoSpace(PDevExt->InterruptStatus,
|
||
PDevExt->SpanOfInterruptStatus);
|
||
}
|
||
|
||
SerialDump(SERTRACECALLS,("SERIAL: Leave SerialReleaseResources\n"));
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SerialPrepareRemove(IN PDEVICE_OBJECT PDevObj)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Removes a serial device object from the system.
|
||
|
||
Arguments:
|
||
|
||
PDevObj - A pointer to the Device Object we want removed.
|
||
|
||
Return Value:
|
||
|
||
Always TRUE
|
||
|
||
--*/
|
||
{
|
||
PSERIAL_DEVICE_EXTENSION pDevExt
|
||
= (PSERIAL_DEVICE_EXTENSION)PDevObj->DeviceExtension;
|
||
POWER_STATE state;
|
||
ULONG pendingIRPs;
|
||
|
||
PAGED_CODE();
|
||
|
||
SerialDump(SERTRACECALLS,("SERIAL: Enter SerialPrepareRemove\n"));
|
||
|
||
//
|
||
// Mark as not accepting requests
|
||
//
|
||
|
||
SerialSetAccept(pDevExt, SERIAL_PNPACCEPT_REMOVING);
|
||
|
||
//
|
||
// Complete all pending requests
|
||
//
|
||
|
||
SerialKillPendingIrps(PDevObj);
|
||
|
||
//
|
||
// Wait for any pending requests we raced on.
|
||
//
|
||
|
||
pendingIRPs = InterlockedDecrement(&pDevExt->PendingIRPCnt);
|
||
|
||
if (pendingIRPs) {
|
||
KeWaitForSingleObject(&pDevExt->PendingIRPEvent, Executive, KernelMode,
|
||
FALSE, NULL);
|
||
}
|
||
|
||
state.DeviceState = PowerDeviceD3;
|
||
|
||
PoSetPowerState(PDevObj, DevicePowerState, state);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
VOID
|
||
SerialDisableInterfacesResources(IN PDEVICE_OBJECT PDevObj,
|
||
BOOLEAN DisableUART)
|
||
{
|
||
PSERIAL_DEVICE_EXTENSION pDevExt
|
||
= (PSERIAL_DEVICE_EXTENSION)PDevObj->DeviceExtension;
|
||
|
||
#if defined(NEC_98)
|
||
//
|
||
// This argument use at MACRO only.
|
||
//
|
||
PSERIAL_DEVICE_EXTENSION Extension = pDevExt;
|
||
#else
|
||
#endif //defined(NEC_98)
|
||
|
||
PAGED_CODE();
|
||
|
||
SerialDump(SERTRACECALLS,("SERIAL: Enter SerialDisableInterfaces\n"));
|
||
|
||
//
|
||
// Only do these many things if the device has started and still
|
||
// has resources allocated
|
||
//
|
||
|
||
if (pDevExt->Flags & SERIAL_FLAGS_STARTED) {
|
||
if (!(pDevExt->Flags & SERIAL_FLAGS_STOPPED)) {
|
||
|
||
if (DisableUART) {
|
||
//
|
||
// Mask off interrupts
|
||
//
|
||
|
||
DISABLE_ALL_INTERRUPTS(pDevExt->Controller);
|
||
|
||
}
|
||
|
||
SerialReleaseResources(pDevExt);
|
||
}
|
||
|
||
//
|
||
// Remove us from WMI consideration
|
||
//
|
||
|
||
IoWMIRegistrationControl(PDevObj, WMIREG_ACTION_DEREGISTER);
|
||
}
|
||
|
||
//
|
||
// Undo external names
|
||
//
|
||
|
||
SerialUndoExternalNaming(pDevExt);
|
||
|
||
SerialDump(SERTRACECALLS,("SERIAL: Exit SerialDisableInterfaces\n"));
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SerialRemoveDevObj(IN PDEVICE_OBJECT PDevObj)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Removes a serial device object from the system.
|
||
|
||
Arguments:
|
||
|
||
PDevObj - A pointer to the Device Object we want removed.
|
||
|
||
Return Value:
|
||
|
||
Always TRUE
|
||
|
||
--*/
|
||
{
|
||
PSERIAL_DEVICE_EXTENSION pDevExt
|
||
= (PSERIAL_DEVICE_EXTENSION)PDevObj->DeviceExtension;
|
||
|
||
PAGED_CODE();
|
||
|
||
SerialDump(SERTRACECALLS,("SERIAL: Enter SerialRemoveDevObj\n"));
|
||
|
||
if (!(pDevExt->DevicePNPAccept & SERIAL_PNPACCEPT_SURPRISE_REMOVING)) {
|
||
//
|
||
// Disable all external interfaces and release resources
|
||
//
|
||
|
||
SerialDisableInterfacesResources(PDevObj, TRUE);
|
||
}
|
||
|
||
IoDetachDevice(pDevExt->LowerDeviceObject);
|
||
|
||
//
|
||
// Free memory allocated in the extension
|
||
//
|
||
|
||
if (pDevExt->NtNameForPort.Buffer != NULL) {
|
||
ExFreePool(pDevExt->NtNameForPort.Buffer);
|
||
}
|
||
|
||
if (pDevExt->DeviceName.Buffer != NULL) {
|
||
ExFreePool(pDevExt->DeviceName.Buffer);
|
||
}
|
||
|
||
if (pDevExt->SymbolicLinkName.Buffer != NULL) {
|
||
ExFreePool(pDevExt->SymbolicLinkName.Buffer);
|
||
}
|
||
|
||
if (pDevExt->DosName.Buffer != NULL) {
|
||
ExFreePool(pDevExt->DosName.Buffer);
|
||
}
|
||
|
||
if (pDevExt->ObjectDirectory.Buffer) {
|
||
ExFreePool(pDevExt->ObjectDirectory.Buffer);
|
||
}
|
||
|
||
//
|
||
// Delete the devobj
|
||
//
|
||
|
||
IoDeleteDevice(PDevObj);
|
||
|
||
|
||
SerialDump(SERTRACECALLS,("SERIAL: Leave SerialRemoveDevObj\n"));
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
VOID
|
||
SerialKillPendingIrps(PDEVICE_OBJECT PDevObj)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine kills any irps pending for the passed device object.
|
||
|
||
Arguments:
|
||
|
||
PDevObj - Pointer to the device object whose irps must die.
|
||
|
||
Return Value:
|
||
|
||
VOID
|
||
|
||
--*/
|
||
{
|
||
PSERIAL_DEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
||
KIRQL oldIrql;
|
||
|
||
SerialDump (SERTRACECALLS,("SERIAL: Enter SerialKillPendingIrps\n"));
|
||
|
||
//
|
||
// First kill all the reads and writes.
|
||
//
|
||
|
||
SerialKillAllReadsOrWrites(PDevObj, &pDevExt->WriteQueue,
|
||
&pDevExt->CurrentWriteIrp);
|
||
|
||
SerialKillAllReadsOrWrites(PDevObj, &pDevExt->ReadQueue,
|
||
&pDevExt->CurrentReadIrp);
|
||
|
||
//
|
||
// Next get rid of purges.
|
||
//
|
||
|
||
SerialKillAllReadsOrWrites(PDevObj, &pDevExt->PurgeQueue,
|
||
&pDevExt->CurrentPurgeIrp);
|
||
|
||
//
|
||
// Get rid of any mask operations.
|
||
//
|
||
|
||
SerialKillAllReadsOrWrites(PDevObj, &pDevExt->MaskQueue,
|
||
&pDevExt->CurrentMaskIrp);
|
||
|
||
//
|
||
// Now get rid a pending wait mask irp.
|
||
//
|
||
|
||
IoAcquireCancelSpinLock(&oldIrql);
|
||
|
||
if (pDevExt->CurrentWaitIrp) {
|
||
|
||
PDRIVER_CANCEL cancelRoutine;
|
||
|
||
cancelRoutine = pDevExt->CurrentWaitIrp->CancelRoutine;
|
||
pDevExt->CurrentWaitIrp->Cancel = TRUE;
|
||
|
||
if (cancelRoutine) {
|
||
|
||
pDevExt->CurrentWaitIrp->CancelIrql = oldIrql;
|
||
pDevExt->CurrentWaitIrp->CancelRoutine = NULL;
|
||
|
||
cancelRoutine(PDevObj, pDevExt->CurrentWaitIrp);
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
IoReleaseCancelSpinLock(oldIrql);
|
||
|
||
}
|
||
|
||
//
|
||
// Cancel any pending wait-wake irps
|
||
//
|
||
|
||
if (pDevExt->PendingWakeIrp != NULL) {
|
||
IoCancelIrp(pDevExt->PendingWakeIrp);
|
||
pDevExt->PendingWakeIrp = NULL;
|
||
}
|
||
|
||
//
|
||
// Finally, dump any stalled IRPS
|
||
//
|
||
|
||
SerialKillAllStalled(PDevObj);
|
||
|
||
|
||
SerialDump (SERTRACECALLS,("SERIAL: Leave SerialKillPendingIrps\n"));
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
SerialSingleToMulti(PVOID Context)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine converts a root device set up to be a single port
|
||
device to a multiport device while that device is running.
|
||
|
||
Arguments:
|
||
|
||
Context - Actually a pointer to the device extension of the root
|
||
device we are turning into a multiport device.
|
||
|
||
Return Value:
|
||
|
||
Always TRUE
|
||
|
||
--*/
|
||
{
|
||
PSERIAL_DEVICE_EXTENSION pDevExt = (PSERIAL_DEVICE_EXTENSION)Context;
|
||
PSERIAL_MULTIPORT_DISPATCH pOurIsrContext;
|
||
PSERIAL_MULTIPORT_DISPATCH pNewIsrContext
|
||
= (PSERIAL_MULTIPORT_DISPATCH)pDevExt->NewExtension;
|
||
PVOID isrFunc;
|
||
|
||
SerialDump (SERTRACECALLS,("SERIAL: Enter SerialSingleToMulti\n"));
|
||
|
||
//
|
||
// Stomp OurIsrContext since we are going from one to many
|
||
// thus our previous context was just pDevExt and doesn't
|
||
// need to be released (i.e., no call to ExFreePool() is needed).
|
||
//
|
||
|
||
pOurIsrContext = pDevExt->OurIsrContext = pDevExt->TopLevelOurIsrContext
|
||
= pNewIsrContext;
|
||
|
||
//
|
||
// We are now multiport
|
||
//
|
||
|
||
pDevExt->PortOnAMultiportCard = TRUE;
|
||
|
||
//
|
||
// Update our personal extensions slot
|
||
//
|
||
|
||
pOurIsrContext->Extensions[pDevExt->PortIndex - 1] = pDevExt;
|
||
pOurIsrContext->InterruptStatus = pDevExt->InterruptStatus;
|
||
|
||
|
||
//
|
||
// We have to pick a new ISR and a new context.
|
||
// As soon as this is done, the ISR will change, so we have to
|
||
// be ready to handle things there.
|
||
//
|
||
|
||
if (pDevExt->Indexed == FALSE) {
|
||
pOurIsrContext->UsablePortMask = 1 << (pDevExt->PortIndex - 1);
|
||
pOurIsrContext->MaskInverted = pDevExt->MaskInverted;
|
||
isrFunc = SerialBitMappedMultiportIsr;
|
||
} else {
|
||
isrFunc = SerialIndexedMultiportIsr;
|
||
}
|
||
|
||
pDevExt->OurIsr = isrFunc;
|
||
pDevExt->TopLevelOurIsr = isrFunc;
|
||
|
||
if (pDevExt->CIsrSw->IsrFunc != SerialSharerIsr) {
|
||
pDevExt->CIsrSw->IsrFunc = isrFunc;
|
||
pDevExt->CIsrSw->Context = pOurIsrContext;
|
||
}
|
||
|
||
SerialDump (SERTRACECALLS,("SERIAL: Leave SerialSingleToMulti\n"));
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
SerialAddToMulti(PVOID Context)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine adds a new port to a multiport device while that device is
|
||
running.
|
||
|
||
Arguments:
|
||
|
||
Context - Actually a pointer to the device extension of the root
|
||
device we are adding a port to.
|
||
|
||
Return Value:
|
||
|
||
Always TRUE
|
||
|
||
--*/
|
||
{
|
||
PSERIAL_DEVICE_EXTENSION pDevExt = (PSERIAL_DEVICE_EXTENSION)Context;
|
||
PSERIAL_MULTIPORT_DISPATCH pOurIsrContext
|
||
= (PSERIAL_MULTIPORT_DISPATCH)pDevExt->OurIsrContext;
|
||
PSERIAL_DEVICE_EXTENSION pNewExt
|
||
= (PSERIAL_DEVICE_EXTENSION)pDevExt->NewExtension;
|
||
|
||
|
||
SerialDump (SERTRACECALLS,("SERIAL: Enter SerialAddToMulti\n"));
|
||
|
||
if (pDevExt->Indexed == FALSE) {
|
||
pOurIsrContext->UsablePortMask |= 1 << (pDevExt->NewPortIndex - 1);
|
||
pOurIsrContext->MaskInverted |= pDevExt->NewMaskInverted;
|
||
}
|
||
|
||
//
|
||
// Add us to the linked list of common interrupt objects if we are not
|
||
// already in it. We may be if there is another device besides our
|
||
// multiport card.
|
||
//
|
||
|
||
if (IsListEmpty(&pNewExt->CommonInterruptObject)) {
|
||
InsertTailList(&pDevExt->CommonInterruptObject,
|
||
&pNewExt->CommonInterruptObject);
|
||
}
|
||
|
||
//
|
||
// Give us the list of contexts also
|
||
//
|
||
|
||
pNewExt->OurIsrContext = pOurIsrContext;
|
||
|
||
|
||
//
|
||
// Add us to the list of our siblings
|
||
//
|
||
InsertTailList(&pDevExt->MultiportSiblings, &pNewExt->MultiportSiblings);
|
||
|
||
SerialDump(SERDIAG1, ("SERIAL: Adding to multi...\n"));
|
||
SerialDump(SERDIAG1, ("------: old ext %x\n", pDevExt));
|
||
|
||
//
|
||
// Map us in so the ISR can find us.
|
||
//
|
||
|
||
pOurIsrContext->Extensions[pDevExt->NewPortIndex - 1]
|
||
= pDevExt->NewExtension;
|
||
|
||
pNewExt->TopLevelOurIsr = pDevExt->TopLevelOurIsr;
|
||
pNewExt->TopLevelOurIsrContext = pDevExt->TopLevelOurIsrContext;
|
||
|
||
SerialDump (SERTRACECALLS,("SERIAL: Leave SerialAddToMulti\n"));
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SerialInitMultiPort(IN PSERIAL_DEVICE_EXTENSION PDevExt,
|
||
IN PCONFIG_DATA PConfigData, IN PDEVICE_OBJECT PDevObj)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes a multiport device by adding a port to an existing
|
||
one.
|
||
|
||
Arguments:
|
||
|
||
PDevExt - pointer to the device extension of the root of the multiport
|
||
device.
|
||
|
||
PConfigData - pointer to the config data for the new port
|
||
|
||
PDevObj - pointer to the devobj for the new port
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS on success, appropriate error on failure.
|
||
|
||
--*/
|
||
{
|
||
PSERIAL_DEVICE_EXTENSION pOurIsrContext
|
||
= (PSERIAL_DEVICE_EXTENSION)PDevExt->OurIsrContext;
|
||
PSERIAL_DEVICE_EXTENSION pNewExt
|
||
= (PSERIAL_DEVICE_EXTENSION)PDevObj->DeviceExtension;
|
||
NTSTATUS status;
|
||
PSERIAL_MULTIPORT_DISPATCH pDispatch;
|
||
|
||
PAGED_CODE();
|
||
|
||
|
||
SerialDump (SERTRACECALLS,("SERIAL: Enter SerialInitMultiPort\n"));
|
||
|
||
//
|
||
// Allow him to share our CISRsw and interrupt object
|
||
//
|
||
|
||
pNewExt->CIsrSw = PDevExt->CIsrSw;
|
||
pNewExt->Interrupt = PDevExt->Interrupt;
|
||
|
||
//
|
||
// First, see if we can initialize the one we have found
|
||
//
|
||
|
||
status = SerialInitOneController(PDevObj, PConfigData);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
SerialDump (SERTRACECALLS,("SERIAL: Leave SerialInitMultiPort\n"));
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// OK. He's good to go. Find the root controller. He may
|
||
// currently be a single, so we have to change him to multi.
|
||
//
|
||
|
||
if (PDevExt->PortOnAMultiportCard != TRUE) {
|
||
|
||
pDispatch = PDevExt->NewExtension
|
||
= ExAllocatePool(NonPagedPool, sizeof(SERIAL_MULTIPORT_DISPATCH));
|
||
|
||
if (pDispatch == NULL) {
|
||
// FAIL and CLEANUP
|
||
SerialDump (SERTRACECALLS,("SERIAL: Leave SerialInitMultiPort\n"));
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlZeroMemory(pDispatch, sizeof(*pDispatch));
|
||
KeSynchronizeExecution(PDevExt->Interrupt, SerialSingleToMulti, PDevExt);
|
||
}
|
||
|
||
//
|
||
// Update some important fields
|
||
//
|
||
|
||
((PSERIAL_DEVICE_EXTENSION)PDevObj->DeviceExtension)->PortOnAMultiportCard
|
||
= TRUE;
|
||
((PSERIAL_DEVICE_EXTENSION)PDevObj->DeviceExtension)->OurIsr = NULL;
|
||
|
||
|
||
PDevExt->NewPortIndex = PConfigData->PortIndex;
|
||
PDevExt->NewMaskInverted = PConfigData->MaskInverted;
|
||
PDevExt->NewExtension = PDevObj->DeviceExtension;
|
||
|
||
//
|
||
// Now, we can add the new guy. He will be hooked in
|
||
// immediately, so we need to be able to handle interrupts.
|
||
//
|
||
|
||
KeSynchronizeExecution(PDevExt->Interrupt, SerialAddToMulti, PDevExt);
|
||
|
||
SerialDump (SERTRACECALLS,("SERIAL: Leave SerialInitMultiPort\n"));
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SerialInitController(IN PDEVICE_OBJECT PDevObj, IN PCONFIG_DATA PConfigData)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Really too many things to mention here. In general initializes
|
||
kernel synchronization structures, allocates the typeahead buffer,
|
||
sets up defaults, etc.
|
||
|
||
Arguments:
|
||
|
||
PDevObj - Device object for the device to be started
|
||
|
||
PConfigData - Pointer to a record for a single port.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCCESS if everything went ok. A !NT_SUCCESS status
|
||
otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSERIAL_DEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
||
#if defined(NEC_98)
|
||
PSERIAL_DEVICE_EXTENSION Extension = PDevObj->DeviceExtension;
|
||
#else
|
||
#endif //defined(NEC_98)
|
||
|
||
//
|
||
// 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;
|
||
|
||
//
|
||
// Indicates that a conflict was detected for resources
|
||
// used by this device.
|
||
//
|
||
|
||
BOOLEAN conflictDetected = FALSE;
|
||
|
||
//
|
||
// Indicates if we allocated an ISR switch
|
||
//
|
||
|
||
BOOLEAN allocedISRSw = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: Initializing for configuration record of %wZ\n",
|
||
&pDevExt->DeviceName)
|
||
);
|
||
|
||
//
|
||
// This compare is done using **untranslated** values since that is what
|
||
// the kernel shoves in regardless of the architecture.
|
||
//
|
||
|
||
if ((*KdComPortInUse) == ((PUCHAR)(PConfigData->Controller.LowPart))) {
|
||
SerialDump(SERERRORS, ("SERIAL: Kernel debugger is using port at address "
|
||
"%x\n"
|
||
"------ Serial driver will not load port\n",
|
||
*KdComPortInUse));
|
||
|
||
SerialLogError(
|
||
PDevObj->DriverObject,
|
||
NULL,
|
||
PConfigData->TrController,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
3,
|
||
STATUS_SUCCESS,
|
||
SERIAL_KERNEL_DEBUGGER_ACTIVE,
|
||
pDevExt->DeviceName.Length+sizeof(WCHAR),
|
||
pDevExt->DeviceName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
|
||
if (pDevExt->CIsrSw == NULL) {
|
||
if ((pDevExt->CIsrSw
|
||
= ExAllocatePool(NonPagedPool, sizeof(SERIAL_CISR_SW))) == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
InitializeListHead(&pDevExt->CIsrSw->SharerList);
|
||
|
||
allocedISRSw = TRUE;
|
||
}
|
||
|
||
#if defined(NEC_98)
|
||
//
|
||
// intiallize device extension
|
||
//
|
||
pDevExt->DivisorLatch16550 = 0;
|
||
pDevExt->ModemControl16550 = 0;
|
||
pDevExt->LineControl16550 = SERIAL_8_DATA;
|
||
pDevExt->ModeSet71051 = 0;
|
||
pDevExt->CommandSet71051 = COMMAND_DEFAULT_SET;
|
||
pDevExt->InterruptEnable16550 = 0;
|
||
pDevExt->CommonBaseAddress = 0;
|
||
pDevExt->ModeSetRegisterAddress = 0;
|
||
pDevExt->CommandSetRegisterAddress = 0;
|
||
|
||
#else
|
||
#endif //defined(NEC_98)
|
||
//
|
||
// Propagate that it is a jensen.
|
||
//
|
||
|
||
pDevExt->Jensen = PConfigData->Jensen;
|
||
|
||
|
||
//
|
||
// Initialize the spinlock associated with fields read (& set)
|
||
// by IO Control functions and the flags spinlock.
|
||
//
|
||
|
||
KeInitializeSpinLock(&pDevExt->ControlLock);
|
||
KeInitializeSpinLock(&pDevExt->FlagsLock);
|
||
|
||
|
||
//
|
||
// Initialize the timers used to timeout operations.
|
||
//
|
||
|
||
KeInitializeTimer(&pDevExt->ReadRequestTotalTimer);
|
||
KeInitializeTimer(&pDevExt->ReadRequestIntervalTimer);
|
||
KeInitializeTimer(&pDevExt->WriteRequestTotalTimer);
|
||
KeInitializeTimer(&pDevExt->ImmediateTotalTimer);
|
||
KeInitializeTimer(&pDevExt->XoffCountTimer);
|
||
KeInitializeTimer(&pDevExt->LowerRTSTimer);
|
||
|
||
|
||
//
|
||
// Intialialize the dpcs that will be used to complete
|
||
// or timeout various IO operations.
|
||
//
|
||
|
||
KeInitializeDpc(&pDevExt->CompleteWriteDpc, SerialCompleteWrite, pDevExt);
|
||
KeInitializeDpc(&pDevExt->CompleteReadDpc, SerialCompleteRead, pDevExt);
|
||
KeInitializeDpc(&pDevExt->TotalReadTimeoutDpc, SerialReadTimeout, pDevExt);
|
||
KeInitializeDpc(&pDevExt->IntervalReadTimeoutDpc, SerialIntervalReadTimeout,
|
||
pDevExt);
|
||
KeInitializeDpc(&pDevExt->TotalWriteTimeoutDpc, SerialWriteTimeout, pDevExt);
|
||
KeInitializeDpc(&pDevExt->CommErrorDpc, SerialCommError, pDevExt);
|
||
KeInitializeDpc(&pDevExt->CompleteImmediateDpc, SerialCompleteImmediate,
|
||
pDevExt);
|
||
KeInitializeDpc(&pDevExt->TotalImmediateTimeoutDpc, SerialTimeoutImmediate,
|
||
pDevExt);
|
||
KeInitializeDpc(&pDevExt->CommWaitDpc, SerialCompleteWait, pDevExt);
|
||
KeInitializeDpc(&pDevExt->XoffCountTimeoutDpc, SerialTimeoutXoff, pDevExt);
|
||
KeInitializeDpc(&pDevExt->XoffCountCompleteDpc, SerialCompleteXoff, pDevExt);
|
||
KeInitializeDpc(&pDevExt->StartTimerLowerRTSDpc, SerialStartTimerLowerRTS,
|
||
pDevExt);
|
||
KeInitializeDpc(&pDevExt->PerhapsLowerRTSDpc, SerialInvokePerhapsLowerRTS,
|
||
pDevExt);
|
||
KeInitializeDpc(&pDevExt->IsrUnlockPagesDpc, SerialUnlockPages, pDevExt);
|
||
|
||
#if 0 // DBG
|
||
//
|
||
// Init debug stuff
|
||
//
|
||
|
||
pDevExt->DpcQueued[0].Dpc = &pDevExt->CompleteWriteDpc;
|
||
pDevExt->DpcQueued[1].Dpc = &pDevExt->CompleteReadDpc;
|
||
pDevExt->DpcQueued[2].Dpc = &pDevExt->TotalReadTimeoutDpc;
|
||
pDevExt->DpcQueued[3].Dpc = &pDevExt->IntervalReadTimeoutDpc;
|
||
pDevExt->DpcQueued[4].Dpc = &pDevExt->TotalWriteTimeoutDpc;
|
||
pDevExt->DpcQueued[5].Dpc = &pDevExt->CommErrorDpc;
|
||
pDevExt->DpcQueued[6].Dpc = &pDevExt->CompleteImmediateDpc;
|
||
pDevExt->DpcQueued[7].Dpc = &pDevExt->TotalImmediateTimeoutDpc;
|
||
pDevExt->DpcQueued[8].Dpc = &pDevExt->CommWaitDpc;
|
||
pDevExt->DpcQueued[9].Dpc = &pDevExt->XoffCountTimeoutDpc;
|
||
pDevExt->DpcQueued[10].Dpc = &pDevExt->XoffCountCompleteDpc;
|
||
pDevExt->DpcQueued[11].Dpc = &pDevExt->StartTimerLowerRTSDpc;
|
||
pDevExt->DpcQueued[12].Dpc = &pDevExt->PerhapsLowerRTSDpc;
|
||
pDevExt->DpcQueued[13].Dpc = &pDevExt->IsrUnlockPagesDpc;
|
||
|
||
#endif
|
||
|
||
if (!((PConfigData->ClockRate == 1843200) ||
|
||
(PConfigData->ClockRate == 3072000) ||
|
||
(PConfigData->ClockRate == 4233600) ||
|
||
(PConfigData->ClockRate == 8000000))) {
|
||
|
||
SerialLogError(
|
||
PDevObj->DriverObject,
|
||
PDevObj,
|
||
PConfigData->TrController,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
6,
|
||
STATUS_SUCCESS,
|
||
SERIAL_UNSUPPORTED_CLOCK_RATE,
|
||
pDevExt->DeviceName.Length+sizeof(WCHAR),
|
||
pDevExt->DeviceName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Invalid clock rate specified for %wZ\n",
|
||
&pDevExt->DeviceName)
|
||
);
|
||
|
||
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.
|
||
//
|
||
|
||
pDevExt->ClockRate = PConfigData->ClockRate;
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// Map the memory for the control registers for the serial device
|
||
// into virtual memory.
|
||
//
|
||
pDevExt->Controller =
|
||
SerialGetMappedAddress(PConfigData->InterfaceType,
|
||
PConfigData->BusNumber,
|
||
PConfigData->TrController,
|
||
PConfigData->SpanOfController,
|
||
(BOOLEAN)PConfigData->AddressSpace,
|
||
&pDevExt->UnMapRegisters);
|
||
|
||
if (!pDevExt->Controller) {
|
||
|
||
SerialLogError(
|
||
PDevObj->DriverObject,
|
||
pDevExt->DeviceObject,
|
||
PConfigData->TrController,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
7,
|
||
STATUS_SUCCESS,
|
||
SERIAL_REGISTERS_NOT_MAPPED,
|
||
pDevExt->DeviceName.Length+sizeof(WCHAR),
|
||
pDevExt->DeviceName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Could not map memory for device registers for %wZ\n",
|
||
&pDevExt->DeviceName)
|
||
);
|
||
|
||
pDevExt->UnMapRegisters = FALSE;
|
||
status = STATUS_NONE_MAPPED;
|
||
goto ExtensionCleanup;
|
||
|
||
}
|
||
|
||
pDevExt->AddressSpace = PConfigData->AddressSpace;
|
||
pDevExt->OriginalController = PConfigData->Controller;
|
||
pDevExt->SpanOfController = PConfigData->SpanOfController;
|
||
|
||
|
||
//
|
||
// if we have an interrupt status then map it.
|
||
//
|
||
|
||
pDevExt->InterruptStatus =
|
||
(PUCHAR)PConfigData->TrInterruptStatus.QuadPart;
|
||
|
||
if (pDevExt->InterruptStatus) {
|
||
|
||
pDevExt->InterruptStatus
|
||
= SerialGetMappedAddress(PConfigData->InterfaceType,
|
||
PConfigData->BusNumber,
|
||
PConfigData->TrInterruptStatus,
|
||
PConfigData->SpanOfInterruptStatus,
|
||
(BOOLEAN)PConfigData->AddressSpace,
|
||
&pDevExt->UnMapStatus);
|
||
|
||
|
||
if (!pDevExt->InterruptStatus) {
|
||
|
||
SerialLogError(
|
||
PDevObj->DriverObject,
|
||
PDevObj,
|
||
PConfigData->TrController,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
8,
|
||
STATUS_SUCCESS,
|
||
SERIAL_REGISTERS_NOT_MAPPED,
|
||
pDevExt->DeviceName.Length+sizeof(WCHAR),
|
||
pDevExt->DeviceName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
SerialDump(SERERRORS, ("SERIAL: Could not map memory for interrupt "
|
||
"status for %wZ\n",
|
||
&pDevExt->DeviceName));
|
||
|
||
//
|
||
// Manually unmap the other register here if necessary
|
||
//
|
||
|
||
if (pDevExt->UnMapRegisters) {
|
||
MmUnmapIoSpace((PVOID)PConfigData->TrController.QuadPart,
|
||
PConfigData->SpanOfController);
|
||
}
|
||
|
||
pDevExt->UnMapRegisters = FALSE;
|
||
pDevExt->UnMapStatus = FALSE;
|
||
status = STATUS_NONE_MAPPED;
|
||
goto ExtensionCleanup;
|
||
|
||
}
|
||
|
||
pDevExt->OriginalInterruptStatus = PConfigData->InterruptStatus;
|
||
pDevExt->SpanOfInterruptStatus = PConfigData->SpanOfInterruptStatus;
|
||
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Shareable interrupt?
|
||
//
|
||
|
||
if ((BOOLEAN)PConfigData->PermitSystemWideShare) {
|
||
pDevExt->InterruptShareable = TRUE;
|
||
}
|
||
|
||
//
|
||
// Save off the interface type and the bus number.
|
||
//
|
||
|
||
pDevExt->InterfaceType = PConfigData->InterfaceType;
|
||
pDevExt->BusNumber = PConfigData->BusNumber;
|
||
|
||
pDevExt->PortIndex = PConfigData->PortIndex;
|
||
pDevExt->Indexed = (BOOLEAN)PConfigData->Indexed;
|
||
pDevExt->MaskInverted = PConfigData->MaskInverted;
|
||
|
||
//
|
||
// Get the translated interrupt vector, level, and affinity
|
||
//
|
||
|
||
pDevExt->OriginalIrql = PConfigData->OriginalIrql;
|
||
pDevExt->OriginalVector = PConfigData->OriginalVector;
|
||
|
||
|
||
//
|
||
// PnP uses the passed translated values rather than calling
|
||
// HalGetInterruptVector()
|
||
//
|
||
|
||
pDevExt->Vector = PConfigData->TrVector;
|
||
pDevExt->Irql = (UCHAR)PConfigData->TrIrql;
|
||
|
||
//
|
||
// Set up the Isr.
|
||
//
|
||
|
||
pDevExt->OurIsr = SerialISR;
|
||
pDevExt->OurIsrContext = pDevExt;
|
||
|
||
|
||
//
|
||
// If the user said to permit sharing within the device, propagate this
|
||
// through.
|
||
//
|
||
|
||
pDevExt->PermitShare = PConfigData->PermitShare;
|
||
|
||
|
||
//
|
||
// 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 (PConfigData->RxFIFO) {
|
||
|
||
case 1:
|
||
|
||
pDevExt->RxFifoTrigger = SERIAL_1_BYTE_HIGH_WATER;
|
||
break;
|
||
|
||
case 4:
|
||
|
||
pDevExt->RxFifoTrigger = SERIAL_4_BYTE_HIGH_WATER;
|
||
break;
|
||
|
||
case 8:
|
||
|
||
pDevExt->RxFifoTrigger = SERIAL_8_BYTE_HIGH_WATER;
|
||
break;
|
||
|
||
case 14:
|
||
|
||
pDevExt->RxFifoTrigger = SERIAL_14_BYTE_HIGH_WATER;
|
||
break;
|
||
|
||
default:
|
||
|
||
pDevExt->RxFifoTrigger = SERIAL_1_BYTE_HIGH_WATER;
|
||
break;
|
||
|
||
}
|
||
|
||
|
||
if ((PConfigData->TxFIFO > 16) ||
|
||
(PConfigData->TxFIFO < 1)) {
|
||
|
||
pDevExt->TxFifoAmount = 1;
|
||
|
||
} else {
|
||
|
||
pDevExt->TxFifoAmount = PConfigData->TxFIFO;
|
||
|
||
}
|
||
|
||
|
||
if (!SerialDoesPortExist(
|
||
pDevExt,
|
||
&pDevExt->DeviceName,
|
||
PConfigData->ForceFifoEnable,
|
||
PConfigData->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",
|
||
&pDevExt->DeviceName)
|
||
);
|
||
|
||
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 (PConfigData->DisablePort) {
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: disabled port %wZ as requested in configuration\n",
|
||
&pDevExt->DeviceName)
|
||
);
|
||
|
||
status = STATUS_NO_SUCH_DEVICE;
|
||
|
||
SerialLogError(
|
||
PDevObj->DriverObject,
|
||
PDevObj,
|
||
PConfigData->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
57,
|
||
STATUS_SUCCESS,
|
||
SERIAL_DISABLED_PORT,
|
||
pDevExt->DeviceName.Length+sizeof(WCHAR),
|
||
pDevExt->DeviceName.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.
|
||
//
|
||
|
||
pDevExt->SpecialChars.XonChar = SERIAL_DEF_XON;
|
||
pDevExt->SpecialChars.XoffChar = SERIAL_DEF_XOFF;
|
||
pDevExt->HandFlow.ControlHandShake = SERIAL_DTR_CONTROL;
|
||
pDevExt->HandFlow.FlowReplace = SERIAL_RTS_CONTROL;
|
||
|
||
|
||
//
|
||
// Default Line control protocol. 7E1
|
||
//
|
||
// Seven data bits.
|
||
// Even parity.
|
||
// 1 Stop bits.
|
||
//
|
||
|
||
pDevExt->LineControl = SERIAL_7_DATA |
|
||
SERIAL_EVEN_PARITY |
|
||
SERIAL_NONE_PARITY;
|
||
|
||
pDevExt->ValidDataMask = 0x7f;
|
||
pDevExt->CurrentBaud = 1200;
|
||
|
||
|
||
//
|
||
// We set up the default xon/xoff limits.
|
||
//
|
||
// This may be a bogus value. It looks like the BufferSize
|
||
// is not set up until the device is actually opened.
|
||
//
|
||
|
||
pDevExt->HandFlow.XoffLimit = pDevExt->BufferSize >> 3;
|
||
pDevExt->HandFlow.XonLimit = pDevExt->BufferSize >> 1;
|
||
|
||
pDevExt->BufferSizePt8 = ((3*(pDevExt->BufferSize>>2))+
|
||
(pDevExt->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",
|
||
pDevExt->BufferSize,
|
||
pDevExt->HandFlow.XoffLimit,
|
||
pDevExt->HandFlow.XonLimit,
|
||
pDevExt->BufferSizePt8)
|
||
);
|
||
|
||
|
||
//
|
||
// Go through all the "named" baud rates to find out which ones
|
||
// can be supported with this port.
|
||
//
|
||
|
||
pDevExt->SupportedBauds = SERIAL_BAUD_USER;
|
||
|
||
{
|
||
|
||
SHORT junk;
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)75,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->SupportedBauds |= SERIAL_BAUD_075;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)110,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->SupportedBauds |= SERIAL_BAUD_110;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)135,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->SupportedBauds |= SERIAL_BAUD_134_5;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)150,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->SupportedBauds |= SERIAL_BAUD_150;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)300,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->SupportedBauds |= SERIAL_BAUD_300;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)600,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->SupportedBauds |= SERIAL_BAUD_600;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)1200,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->SupportedBauds |= SERIAL_BAUD_1200;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)1800,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->SupportedBauds |= SERIAL_BAUD_1800;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)2400,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->SupportedBauds |= SERIAL_BAUD_2400;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)4800,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->SupportedBauds |= SERIAL_BAUD_4800;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)7200,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->SupportedBauds |= SERIAL_BAUD_7200;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)9600,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->SupportedBauds |= SERIAL_BAUD_9600;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)14400,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->SupportedBauds |= SERIAL_BAUD_14400;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)19200,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->SupportedBauds |= SERIAL_BAUD_19200;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)38400,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->SupportedBauds |= SERIAL_BAUD_38400;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)56000,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->SupportedBauds |= SERIAL_BAUD_56K;
|
||
|
||
}
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)57600,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->SupportedBauds |= SERIAL_BAUD_57600;
|
||
|
||
}
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)115200,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->SupportedBauds |= SERIAL_BAUD_115200;
|
||
|
||
}
|
||
|
||
if (!NT_ERROR(SerialGetDivisorFromBaud(
|
||
pDevExt->ClockRate,
|
||
(LONG)128000,
|
||
&junk
|
||
))) {
|
||
|
||
pDevExt->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.
|
||
//
|
||
|
||
pDevExt->DeviceIsOpened = FALSE;
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
pDevExt->ShortIntervalAmount.QuadPart = -1;
|
||
pDevExt->LongIntervalAmount.QuadPart = -10000000;
|
||
pDevExt->CutOverAmount.QuadPart = 200000000;
|
||
|
||
|
||
//
|
||
// Common error path cleanup. If the status is
|
||
// bad, get rid of the device extension, device object
|
||
// and any memory associated with it.
|
||
//
|
||
|
||
ExtensionCleanup: ;
|
||
if (!NT_SUCCESS(status)) {
|
||
if (allocedISRSw) {
|
||
ExFreePool(pDevExt->CIsrSw);
|
||
pDevExt->CIsrSw = NULL;
|
||
}
|
||
|
||
if (pDevExt->UnMapRegisters) {
|
||
MmUnmapIoSpace(pDevExt->Controller, pDevExt->SpanOfController);
|
||
}
|
||
|
||
if (pDevExt->UnMapStatus) {
|
||
MmUnmapIoSpace(pDevExt->InterruptStatus,
|
||
pDevExt->SpanOfInterruptStatus);
|
||
}
|
||
|
||
}
|
||
|
||
return status;
|
||
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SerialInitOneController(IN PDEVICE_OBJECT PDevObj, IN PCONFIG_DATA PConfigData)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will call the real port initialization code. It sets
|
||
up the ISR and Context correctly for a one port device.
|
||
|
||
Arguments:
|
||
|
||
All args are simply passed along.
|
||
|
||
Return Value:
|
||
|
||
Status returned from the controller initialization routine.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS status;
|
||
PSERIAL_DEVICE_EXTENSION pDevExt;
|
||
|
||
PAGED_CODE();
|
||
|
||
status = SerialInitController(PDevObj, PConfigData);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
pDevExt = PDevObj->DeviceExtension;
|
||
|
||
//
|
||
// We successfully initialized the single controller.
|
||
// Stick the isr routine and the parameter for it
|
||
// back into the extension.
|
||
//
|
||
|
||
pDevExt->OurIsr = SerialISR;
|
||
pDevExt->OurIsrContext = pDevExt;
|
||
pDevExt->TopLevelOurIsr = SerialISR;
|
||
pDevExt->TopLevelOurIsrContext = pDevExt;
|
||
|
||
}
|
||
|
||
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;
|
||
|
||
PAGED_CODE();
|
||
|
||
#if defined (NEC_98)
|
||
{
|
||
//
|
||
// For the 101 board, we will found the just fit port.
|
||
// The 101 board is multifunction port, therefore need it.
|
||
// Ch2 = 0x0yb0 as start port address
|
||
// Ch3 = 0x0yb2 as start port address
|
||
//
|
||
#define PortOffsetCh3 0x02
|
||
#define ModeOffsetCh2 0x03
|
||
#define ModeOffsetCh3 0x0b
|
||
#define ModeOffsetCh3_nonPnP 0x09 // non PnP
|
||
|
||
ULONG IrqLevelCh2[] = {3,5,6,9}; //irq of 101 borad at Ch2
|
||
ULONG IrqLevelCh3[] = {3,10,12,13}; //irq of 101 borad at CH3
|
||
ULONG i;
|
||
PUCHAR Port = 0;
|
||
|
||
Extension->CommonBaseAddress = NULL;
|
||
Extension->ModeSetRegisterAddress = NULL;
|
||
Extension->CommandSetRegisterAddress = NULL;
|
||
|
||
Port = (PUCHAR)(Extension->Controller);
|
||
if ((READ_PORT_UCHAR((PUCHAR)Port)) == 0xff){
|
||
returnValue = FALSE;
|
||
return returnValue;
|
||
}
|
||
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: IRQL is : %x\n",Extension->OriginalIrql)
|
||
);
|
||
|
||
i = (ULONG)((UCHAR)(Extension->Controller));
|
||
if (i & 0x02) {
|
||
//
|
||
// When Ch3 of non PnP mode, entry of I/O start address is an even number by the NTDETECT.COM.
|
||
//
|
||
Extension->ModeSetRegisterAddress = ((Extension->Controller) + ModeOffsetCh3_nonPnP);
|
||
Extension->CommandSetRegisterAddress = ((Extension->Controller) + ModeOffsetCh3_nonPnP);
|
||
Extension->CommonBaseAddress = Extension->Controller;
|
||
}
|
||
|
||
if (Extension->CommonBaseAddress == NULL) {
|
||
//
|
||
// Read irq sense at Ch2.
|
||
//
|
||
for (i = 0; i < 4; i++) {
|
||
if (Extension->OriginalIrql == IrqLevelCh2[i]) {
|
||
// There is Ch2 exist.
|
||
Extension->CommonBaseAddress = Extension->Controller;
|
||
Extension->ModeSetRegisterAddress = ((Extension->Controller) + ModeOffsetCh2);
|
||
Extension->CommandSetRegisterAddress = ((Extension->Controller) + ModeOffsetCh2);
|
||
}
|
||
}
|
||
if (Extension->CommonBaseAddress == NULL) {
|
||
for (i = 0; i < 4; i++) {
|
||
//
|
||
// Read irq sense at Ch3.
|
||
//
|
||
if (Extension->OriginalIrql == IrqLevelCh3[i]) {
|
||
// There is Ch3 exist.
|
||
Extension->ModeSetRegisterAddress = ((Extension->Controller) + ModeOffsetCh3);
|
||
Extension->CommandSetRegisterAddress = ((Extension->Controller) + ModeOffsetCh3);
|
||
Extension->Controller = Extension->CommonBaseAddress = ((Extension->Controller) + PortOffsetCh3);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (Extension->CommonBaseAddress == NULL) {
|
||
//
|
||
// There is not found port.
|
||
//
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: There is not found port for 101\n")
|
||
);
|
||
|
||
returnValue = FALSE;
|
||
return returnValue;
|
||
}
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: Found the common Base address: %x ModeReg: %x CommandReg: %x\n",
|
||
Extension->CommonBaseAddress,
|
||
Extension->ModeSetRegisterAddress,
|
||
Extension->CommandSetRegisterAddress)
|
||
);
|
||
}
|
||
#else
|
||
#endif
|
||
|
||
//
|
||
// 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)
|
||
);
|
||
|
||
#if defined (NEC_98)
|
||
oldIERContents = READ_INTERRUPT_ENABLE(Extension->Controller);
|
||
#else
|
||
oldIERContents = READ_INTERRUPT_ENABLE(Extension->Controller);
|
||
#endif
|
||
//
|
||
// 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
|
||
);
|
||
|
||
#if defined (NEC_98)
|
||
value1 = READ_INTERRUPT_ENABLE(Extension->Controller);
|
||
#else
|
||
value1 = READ_INTERRUPT_ENABLE(Extension->Controller);
|
||
#endif
|
||
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 the
|
||
// interrupts.
|
||
//
|
||
|
||
if (Extension->InterruptStatus) {
|
||
if (Extension->Indexed) {
|
||
WRITE_PORT_UCHAR(Extension->InterruptStatus, (UCHAR)0xFF);
|
||
} else {
|
||
//
|
||
// Either we are standalone or already mapped
|
||
//
|
||
|
||
if (Extension->OurIsrContext == Extension) {
|
||
//
|
||
// This is a standalone
|
||
//
|
||
|
||
WRITE_PORT_UCHAR(Extension->InterruptStatus,
|
||
(UCHAR)(1 << (Extension->PortIndex - 1)));
|
||
} else {
|
||
//
|
||
// One of many
|
||
//
|
||
|
||
WRITE_PORT_UCHAR(Extension->InterruptStatus,
|
||
(UCHAR)((PSERIAL_MULTIPORT_DISPATCH)Extension->
|
||
OurIsrContext)->UsablePortMask);
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
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;
|
||
#if defined(NEC_98)
|
||
//
|
||
// This argument use at MACRO only.
|
||
//
|
||
PSERIAL_DEVICE_EXTENSION Extension = Context;
|
||
#else
|
||
#endif //defined(NEC_98)
|
||
UCHAR regContents;
|
||
UCHAR oldModemControl;
|
||
ULONG i;
|
||
SERIAL_LOCKED_PAGED_CODE();
|
||
|
||
//
|
||
// 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++
|
||
) {
|
||
|
||
if (IsNotNEC_98) {
|
||
READ_RECEIVE_BUFFER(extension->Controller);
|
||
if (!(READ_LINE_STATUS(extension->Controller) & 1)) {
|
||
|
||
break;
|
||
|
||
}
|
||
} else {
|
||
//
|
||
// I get incorrect data when read enpty buffer.
|
||
// But do not read no data! for PC98!
|
||
//
|
||
if (!(READ_LINE_STATUS(extension->Controller) & 1)) {
|
||
|
||
break;
|
||
|
||
}
|
||
READ_RECEIVE_BUFFER(extension->Controller);
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
|
||
SERIAL_LOCKED_PAGED_CODE();
|
||
|
||
//
|
||
// 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 defined(NEC_98)
|
||
//
|
||
// This code check the baud that NEC98 support.
|
||
//
|
||
if (*AppropriateDivisor != -1) {
|
||
if (DesiredBaud <= 50) {
|
||
*AppropriateDivisor = -1;
|
||
} else if (DesiredBaud >= 9600) {
|
||
switch (DesiredBaud) {
|
||
case 9600:
|
||
case 19200:
|
||
case 38400:
|
||
case 57600:
|
||
case 115200:
|
||
break;
|
||
default:
|
||
*AppropriateDivisor = -1;
|
||
}
|
||
} else {
|
||
if (153600L % DesiredBaud) {
|
||
if (DesiredBaud != 110) {
|
||
*AppropriateDivisor = -1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#else
|
||
#endif //defined(NEC_98)
|
||
}
|
||
|
||
|
||
if (*AppropriateDivisor == -1) {
|
||
|
||
status = STATUS_INVALID_PARAMETER;
|
||
|
||
}
|
||
|
||
return status;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
SerialUnload(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is defunct since all device objects are removed before
|
||
the driver is unloaded.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Pointer to the driver object controling all of the
|
||
devices.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PVOID lockPtr;
|
||
|
||
PAGED_CODE();
|
||
|
||
lockPtr = MmLockPagableCodeSection(SerialUnload);
|
||
|
||
//
|
||
// Unnecessary since our BSS is going away, but do it anyhow to be safe
|
||
//
|
||
|
||
SerialGlobals.PAGESER_Handle = NULL;
|
||
|
||
if (SerialGlobals.RegistryPath.Buffer != NULL) {
|
||
ExFreePool(SerialGlobals.RegistryPath.Buffer);
|
||
SerialGlobals.RegistryPath.Buffer = NULL;
|
||
}
|
||
|
||
SerialDump(
|
||
SERDIAG3,
|
||
("SERIAL: In SerialUnload\n")
|
||
);
|
||
|
||
MmUnlockPagableImageSection(lockPtr);
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
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;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Map the device base address into the virtual address space
|
||
// if the address is in memory space.
|
||
//
|
||
|
||
if (!AddressSpace) {
|
||
|
||
address = MmMapIoSpace(
|
||
IoAddress,
|
||
NumberOfBytes,
|
||
FALSE
|
||
);
|
||
|
||
*MappedAddress = (BOOLEAN)((address)?(TRUE):(FALSE));
|
||
|
||
|
||
} else {
|
||
|
||
address = (PVOID)IoAddress.LowPart;
|
||
*MappedAddress = FALSE;
|
||
|
||
}
|
||
|
||
return address;
|
||
|
||
}
|
||
|
||
|
||
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;
|
||
|
||
PAGED_CODE();
|
||
|
||
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;
|
||
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
SerialBecomeSharer(PVOID Context)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will take a device extension for a serial port and
|
||
allow it to share interrupts with other serial ports.
|
||
|
||
Arguments:
|
||
|
||
Context - The device extension of the port who is to start sharing
|
||
interrupts.
|
||
|
||
Return Value:
|
||
|
||
Always TRUE.
|
||
|
||
--*/
|
||
{
|
||
PSERIAL_DEVICE_EXTENSION pDevExt = (PSERIAL_DEVICE_EXTENSION)Context;
|
||
PSERIAL_DEVICE_EXTENSION pNewExt
|
||
= (PSERIAL_DEVICE_EXTENSION)pDevExt->NewExtension;
|
||
PSERIAL_CISR_SW pCIsrSw = pDevExt->CIsrSw;
|
||
|
||
//
|
||
// See if we need to configure the pre-existing node to become
|
||
// a sharer.
|
||
//
|
||
|
||
if (IsListEmpty(&pCIsrSw->SharerList)) {
|
||
pCIsrSw->IsrFunc = SerialSharerIsr;
|
||
pCIsrSw->Context = &pCIsrSw->SharerList;
|
||
InsertTailList(&pCIsrSw->SharerList, &pDevExt->TopLevelSharers);
|
||
}
|
||
|
||
//
|
||
// They share an interrupt object and a context
|
||
//
|
||
|
||
pNewExt->Interrupt = pDevExt->Interrupt;
|
||
pNewExt->CIsrSw = pDevExt->CIsrSw;
|
||
|
||
//
|
||
// Add to list of sharers
|
||
//
|
||
|
||
InsertTailList(&pCIsrSw->SharerList, &pNewExt->TopLevelSharers);
|
||
|
||
//
|
||
// Add to list of those who share this interrupt object --
|
||
// we may already be on if this port is part of a multiport board
|
||
//
|
||
|
||
if (IsListEmpty(&pNewExt->CommonInterruptObject)) {
|
||
InsertTailList(&pDevExt->CommonInterruptObject,
|
||
&pNewExt->CommonInterruptObject);
|
||
}
|
||
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SerialFindInitController(IN PDEVICE_OBJECT PDevObj, IN PCONFIG_DATA PConfig)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function discovers what type of controller is responsible for
|
||
the given port and initializes the controller and port.
|
||
|
||
Arguments:
|
||
|
||
PDevObj - Pointer to the devobj for the port we are about to init.
|
||
|
||
PConfig - Pointer to configuration data for the port we are about to init.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS on success, appropriate error value on failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSERIAL_DEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
||
PDEVICE_OBJECT pDeviceObject;
|
||
PSERIAL_DEVICE_EXTENSION pExtension;
|
||
PHYSICAL_ADDRESS serialPhysicalMax;
|
||
SERIAL_LIST_DATA listAddition;
|
||
BOOLEAN didInit = FALSE;
|
||
PLIST_ENTRY pCurDevObj;
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
|
||
SerialDump(SERTRACECALLS, ("SERIAL: entering SerialFindInitController\n"));
|
||
|
||
serialPhysicalMax.LowPart = (ULONG)~0;
|
||
serialPhysicalMax.HighPart = ~0;
|
||
|
||
SerialDump(SERDIAG1, ("SERIAL: Attempting to init %wZ\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",
|
||
&pDevExt->DeviceName,
|
||
PConfig->Controller.LowPart,
|
||
PConfig->InterruptStatus.LowPart,
|
||
PConfig->BusNumber,
|
||
PConfig->InterfaceType,
|
||
PConfig->AddressSpace,
|
||
PConfig->InterruptMode)
|
||
);
|
||
|
||
//
|
||
// We don't support any boards whose memory wraps around
|
||
// the physical address space.
|
||
//
|
||
|
||
if (SerialMemCompare(
|
||
PConfig->Controller,
|
||
PConfig->SpanOfController,
|
||
serialPhysicalMax,
|
||
(ULONG)0
|
||
) != AddressesAreDisjoint) {
|
||
|
||
SerialLogError(
|
||
PDevObj->DriverObject,
|
||
NULL,
|
||
PConfig->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
43,
|
||
STATUS_SUCCESS,
|
||
SERIAL_DEVICE_TOO_HIGH,
|
||
pDevExt->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
pDevExt->SymbolicLinkName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Error in config record for %wZ\n"
|
||
"------ registers wrap around physical memory\n",
|
||
&pDevExt->DeviceName)
|
||
);
|
||
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
|
||
}
|
||
|
||
|
||
if (SerialMemCompare(
|
||
PConfig->InterruptStatus,
|
||
PConfig->SpanOfInterruptStatus,
|
||
serialPhysicalMax,
|
||
(ULONG)0
|
||
) != AddressesAreDisjoint) {
|
||
|
||
SerialLogError(
|
||
PDevObj->DriverObject,
|
||
NULL,
|
||
PConfig->Controller,
|
||
SerialPhysicalZero,
|
||
0,
|
||
0,
|
||
0,
|
||
44,
|
||
STATUS_SUCCESS,
|
||
SERIAL_STATUS_TOO_HIGH,
|
||
pDevExt->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
pDevExt->SymbolicLinkName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Error in config record for %wZ\n"
|
||
"------ status raps around physical memory\n",
|
||
&pDevExt->DeviceName)
|
||
);
|
||
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
|
||
//
|
||
// Make sure that the interrupt status address doesn't
|
||
// overlap the controller registers
|
||
//
|
||
|
||
if (SerialMemCompare(
|
||
PConfig->InterruptStatus,
|
||
PConfig->SpanOfInterruptStatus,
|
||
SerialPhysicalZero,
|
||
(ULONG)0
|
||
) != AddressesAreEqual) {
|
||
|
||
if (SerialMemCompare(
|
||
PConfig->InterruptStatus,
|
||
PConfig->SpanOfInterruptStatus,
|
||
PConfig->Controller,
|
||
PConfig->SpanOfController
|
||
) != AddressesAreDisjoint) {
|
||
|
||
SerialLogError(
|
||
PDevObj->DriverObject,
|
||
NULL,
|
||
PConfig->Controller,
|
||
PConfig->InterruptStatus,
|
||
0,
|
||
0,
|
||
0,
|
||
45,
|
||
STATUS_SUCCESS,
|
||
SERIAL_STATUS_CONTROL_CONFLICT,
|
||
pDevExt->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
pDevExt->SymbolicLinkName.Buffer,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Error in config record for %wZ\n"
|
||
"------- Interrupt status overlaps regular registers\n",
|
||
&pDevExt->DeviceName)
|
||
);
|
||
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
}
|
||
|
||
|
||
#if defined(NEC_98)
|
||
#else
|
||
//
|
||
// Loop through all of the driver's device objects making
|
||
// sure that this new record doesn't overlap with any of them.
|
||
//
|
||
|
||
if (!IsListEmpty(&SerialGlobals.AllDevObjs)) {
|
||
pCurDevObj = SerialGlobals.AllDevObjs.Flink;
|
||
pExtension = CONTAINING_RECORD(pCurDevObj, SERIAL_DEVICE_EXTENSION,
|
||
AllDevObjs);
|
||
} else {
|
||
pCurDevObj = NULL;
|
||
pExtension = NULL;
|
||
}
|
||
|
||
|
||
while (pCurDevObj != NULL
|
||
&& pCurDevObj != &SerialGlobals.AllDevObjs) {
|
||
//
|
||
// We only care about this list if the elements are on the
|
||
// same bus as this new entry.
|
||
//
|
||
|
||
if ((pExtension->InterfaceType == PConfig->InterfaceType) &&
|
||
(pExtension->AddressSpace == PConfig->AddressSpace) &&
|
||
(pExtension->BusNumber == PConfig->BusNumber)) {
|
||
|
||
SerialDump(
|
||
SERDIAG1,
|
||
("SERIAL: Comparing it to %wZ\n"
|
||
"------- already in the device list\n"
|
||
"------- PortAddress is %x\n"
|
||
"------- Interrupt Status is %x\n"
|
||
"------- BusNumber is %d\n"
|
||
"------- BusType is %d\n"
|
||
"------- AddressSpace is %d\n",
|
||
&pExtension->DeviceName,
|
||
pExtension->OriginalController.LowPart,
|
||
pExtension->OriginalInterruptStatus.LowPart,
|
||
pExtension->BusNumber,
|
||
pExtension->InterfaceType,
|
||
pExtension->AddressSpace
|
||
)
|
||
);
|
||
|
||
//
|
||
// Check to see if the controller addresses are not equal.
|
||
//
|
||
|
||
if (SerialMemCompare(
|
||
PConfig->Controller,
|
||
PConfig->SpanOfController,
|
||
pExtension->OriginalController,
|
||
pExtension->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.
|
||
//
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Error in config record for %wZ\n"
|
||
"------- Register address overlaps with\n"
|
||
"------- previous serial device\n",
|
||
&pDevExt->DeviceName)
|
||
);
|
||
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
|
||
//
|
||
// If we have an interrupt status, make sure that
|
||
// it doesn't overlap with the old controllers
|
||
// registers.
|
||
//
|
||
|
||
if (SerialMemCompare(
|
||
PConfig->InterruptStatus,
|
||
PConfig->SpanOfInterruptStatus,
|
||
SerialPhysicalZero,
|
||
(ULONG)0
|
||
) != AddressesAreEqual) {
|
||
|
||
//
|
||
// Check it against the existing device's controller address
|
||
//
|
||
|
||
if (SerialMemCompare(
|
||
PConfig->InterruptStatus,
|
||
PConfig->SpanOfInterruptStatus,
|
||
pExtension->OriginalController,
|
||
pExtension->SpanOfController
|
||
) != AddressesAreDisjoint) {
|
||
|
||
SerialLogError(
|
||
PDevObj->DriverObject,
|
||
NULL,
|
||
PConfig->Controller,
|
||
pExtension->OriginalController,
|
||
0,
|
||
0,
|
||
0,
|
||
47,
|
||
STATUS_SUCCESS,
|
||
SERIAL_STATUS_OVERLAP,
|
||
pDevExt->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
pDevExt->SymbolicLinkName.Buffer,
|
||
pExtension->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
pExtension->SymbolicLinkName.Buffer
|
||
);
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Error in config record for %wZ\n"
|
||
"------- status address overlaps with\n"
|
||
"------- previous serial device registers\n",
|
||
&pDevExt->DeviceName)
|
||
);
|
||
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
//
|
||
// If the old configuration record has an interrupt
|
||
// status, the addresses should not overlap.
|
||
//
|
||
|
||
if (SerialMemCompare(
|
||
PConfig->InterruptStatus,
|
||
PConfig->SpanOfInterruptStatus,
|
||
SerialPhysicalZero,
|
||
(ULONG)0
|
||
) != AddressesAreEqual) {
|
||
|
||
if (SerialMemCompare(
|
||
PConfig->InterruptStatus,
|
||
PConfig->SpanOfInterruptStatus,
|
||
pExtension->OriginalInterruptStatus,
|
||
pExtension->SpanOfInterruptStatus
|
||
) == AddressesOverlap) {
|
||
|
||
SerialLogError(
|
||
PDevObj->DriverObject,
|
||
NULL,
|
||
PConfig->Controller,
|
||
pExtension->OriginalController,
|
||
0,
|
||
0,
|
||
0,
|
||
48,
|
||
STATUS_SUCCESS,
|
||
SERIAL_STATUS_STATUS_OVERLAP,
|
||
pDevExt->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
pDevExt->SymbolicLinkName.Buffer,
|
||
pExtension->SymbolicLinkName.Length
|
||
+ sizeof(WCHAR),
|
||
pExtension->SymbolicLinkName.Buffer
|
||
);
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Error in config record for %wZ\n"
|
||
"------- status address overlaps with\n"
|
||
"------- previous serial status register\n",
|
||
&pDevExt->DeviceName)
|
||
);
|
||
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
}
|
||
} // if ((pExtension->InterfaceType == pDevExt->InterfaceType) &&
|
||
|
||
|
||
//
|
||
// 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(
|
||
pExtension->OriginalInterruptStatus,
|
||
pExtension->SpanOfInterruptStatus,
|
||
SerialPhysicalZero,
|
||
(ULONG)0
|
||
) != AddressesAreEqual) {
|
||
|
||
if (SerialMemCompare(
|
||
PConfig->Controller,
|
||
PConfig->SpanOfController,
|
||
pExtension->OriginalInterruptStatus,
|
||
pExtension->SpanOfInterruptStatus
|
||
) == AddressesOverlap) {
|
||
|
||
SerialLogError(
|
||
PDevObj->DriverObject,
|
||
NULL,
|
||
PConfig->Controller,
|
||
pExtension->OriginalController,
|
||
0,
|
||
0,
|
||
0,
|
||
49,
|
||
STATUS_SUCCESS,
|
||
SERIAL_CONTROL_STATUS_OVERLAP,
|
||
pDevExt->SymbolicLinkName.Length
|
||
+ sizeof(WCHAR),
|
||
pDevExt->SymbolicLinkName.Buffer,
|
||
pExtension->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
pExtension->SymbolicLinkName.Buffer
|
||
);
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Error in config record for %wZ\n"
|
||
"------- register address overlaps with\n"
|
||
"------- previous serial status register\n",
|
||
&pDevExt->DeviceName)
|
||
);
|
||
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
}
|
||
}
|
||
|
||
pCurDevObj = pCurDevObj->Flink;
|
||
|
||
if (pCurDevObj != NULL) {
|
||
pExtension = CONTAINING_RECORD(pCurDevObj, SERIAL_DEVICE_EXTENSION,
|
||
AllDevObjs);
|
||
}
|
||
} // while (pCurDevObj != NULL && pCurDevObj != &SerialGlobals.AllDevObjs)
|
||
|
||
|
||
|
||
//
|
||
// Now, we will check if this is a port on a multiport card.
|
||
// The conditions are same ISR set and same IRQL/Vector
|
||
//
|
||
|
||
//
|
||
// Loop through all previously attached devices
|
||
//
|
||
|
||
if (!IsListEmpty(&SerialGlobals.AllDevObjs)) {
|
||
pCurDevObj = SerialGlobals.AllDevObjs.Flink;
|
||
pExtension = CONTAINING_RECORD(pCurDevObj, SERIAL_DEVICE_EXTENSION,
|
||
AllDevObjs);
|
||
} else {
|
||
pCurDevObj = NULL;
|
||
pExtension = NULL;
|
||
}
|
||
|
||
|
||
//
|
||
// 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(
|
||
PConfig->InterruptStatus,
|
||
PConfig->SpanOfInterruptStatus,
|
||
SerialPhysicalZero,
|
||
(ULONG)0
|
||
) != AddressesAreEqual) &&
|
||
(pCurDevObj != NULL)) {
|
||
|
||
ASSERT(pExtension != NULL);
|
||
|
||
//
|
||
// We have an interrupt status. Loop through all
|
||
// previous records, look for an existing interrupt status
|
||
// the same as the current interrupt status.
|
||
//
|
||
do {
|
||
|
||
//
|
||
// We only care about this list if the elements are on the
|
||
// same bus as this new entry. (Their interrupts must therefore
|
||
// 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 ((pExtension->InterfaceType == PConfig->InterfaceType) &&
|
||
(pExtension->AddressSpace == PConfig->AddressSpace) &&
|
||
(pExtension->BusNumber == PConfig->BusNumber)) {
|
||
|
||
//
|
||
// If the interrupt status is the same, then same card.
|
||
//
|
||
|
||
if (SerialMemCompare(
|
||
pExtension->OriginalInterruptStatus,
|
||
pExtension->SpanOfInterruptStatus,
|
||
PConfig->InterruptStatus,
|
||
PConfig->SpanOfInterruptStatus
|
||
) == AddressesAreEqual) {
|
||
|
||
//
|
||
// Same card. Now make sure that they
|
||
// are using the same interrupt parameters.
|
||
//
|
||
|
||
if ((PConfig->OriginalIrql != pExtension->OriginalIrql) ||
|
||
(PConfig->OriginalVector != pExtension->OriginalVector)) {
|
||
|
||
//
|
||
// We won't put this into the configuration
|
||
// list.
|
||
//
|
||
SerialLogError(
|
||
PDevObj->DriverObject,
|
||
NULL,
|
||
PConfig->Controller,
|
||
pExtension->OriginalController,
|
||
0,
|
||
0,
|
||
0,
|
||
50,
|
||
STATUS_SUCCESS,
|
||
SERIAL_MULTI_INTERRUPT_CONFLICT,
|
||
pDevExt->SymbolicLinkName.Length+sizeof(WCHAR),
|
||
pDevExt->SymbolicLinkName.Buffer,
|
||
pExtension->SymbolicLinkName.Length
|
||
+ sizeof(WCHAR),
|
||
pExtension->SymbolicLinkName.Buffer
|
||
);
|
||
|
||
SerialDump(
|
||
SERERRORS,
|
||
("SERIAL: Configuration error for %wZ\n"
|
||
"------- Same multiport - different interrupts\n",
|
||
&pDevExt->DeviceName)
|
||
);
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
|
||
}
|
||
|
||
//
|
||
// We should never get this far on a restart since we don't
|
||
// support stop on ISA multiport devices!
|
||
//
|
||
|
||
ASSERT(pDevExt->PNPState == SERIAL_PNP_ADDED);
|
||
|
||
//
|
||
//
|
||
// Initialize the device as part of a multiport board
|
||
//
|
||
|
||
SerialDump(SERDIAG1, ("SERIAL: Aha! It is a multiport node\n"));
|
||
SerialDump(SERDIAG1, ("------: Matched to %x\n", pExtension));
|
||
|
||
status = SerialInitMultiPort(pExtension, PConfig, PDevObj);
|
||
|
||
//
|
||
// A port can be one of three things:
|
||
// A standalone
|
||
// A non-root on a multiport
|
||
// A root on a multiport
|
||
//
|
||
// It can only share an interrupt if it is a root
|
||
// or if it is a standalone. Since this was a non-root
|
||
// we don't need to check if it shares an interrupt
|
||
// and we can return.
|
||
//
|
||
return status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// No match, check some more
|
||
//
|
||
|
||
pCurDevObj = pCurDevObj->Flink;
|
||
if (pCurDevObj != NULL) {
|
||
pExtension = CONTAINING_RECORD(pCurDevObj,SERIAL_DEVICE_EXTENSION,
|
||
AllDevObjs);
|
||
}
|
||
|
||
} while (pCurDevObj != NULL && pCurDevObj != &SerialGlobals.AllDevObjs);
|
||
}
|
||
#endif //(NEC_98)
|
||
|
||
|
||
SerialDump(SERDIAG1, ("SERIAL: Aha! It is a standalone node or first multi"
|
||
"\n"));
|
||
status = SerialInitOneController(PDevObj, PConfig);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// The device is initialized. Now we need to check if
|
||
// this device shares an interrupt with anyone.
|
||
//
|
||
|
||
|
||
//
|
||
// Loop through all previously attached devices
|
||
//
|
||
|
||
if (!IsListEmpty(&SerialGlobals.AllDevObjs)) {
|
||
pCurDevObj = SerialGlobals.AllDevObjs.Flink;
|
||
pExtension = CONTAINING_RECORD(pCurDevObj, SERIAL_DEVICE_EXTENSION,
|
||
AllDevObjs);
|
||
} else {
|
||
pCurDevObj = NULL;
|
||
pExtension = NULL;
|
||
}
|
||
|
||
//
|
||
// Go through the list again looking for previous devices
|
||
// with the same interrupt. The first one found will either be a root
|
||
// or standalone. Order of insertion is important here!
|
||
//
|
||
|
||
if (!PConfig->Jensen && (pCurDevObj != NULL)) {
|
||
do {
|
||
|
||
//
|
||
// We only care about interrupts that are on
|
||
// the same bus.
|
||
//
|
||
|
||
if ((pExtension->InterfaceType == PConfig->InterfaceType) &&
|
||
(pExtension->BusNumber == PConfig->BusNumber)) {
|
||
|
||
if ((pExtension->OriginalIrql == PConfig->OriginalIrql) &&
|
||
(pExtension->OriginalVector == PConfig->OriginalVector)) {
|
||
pExtension->NewExtension = pDevExt;
|
||
|
||
//
|
||
// We will share another's CIsrSw so we can free the one
|
||
// allocated for us during init
|
||
//
|
||
|
||
ExFreePool(pDevExt->CIsrSw);
|
||
|
||
SerialDump(SERDIAG1, ("Becoming sharer: %08X %08X %08X\n",
|
||
pExtension, pExtension->OriginalIrql,
|
||
&pExtension->CIsrSw->SharerList));
|
||
|
||
KeSynchronizeExecution(pExtension->Interrupt,
|
||
SerialBecomeSharer, pExtension);
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// No match, check some more
|
||
//
|
||
|
||
pCurDevObj = pCurDevObj->Flink;
|
||
|
||
if (pCurDevObj != NULL) {
|
||
pExtension = CONTAINING_RECORD(pCurDevObj, SERIAL_DEVICE_EXTENSION,
|
||
AllDevObjs);
|
||
}
|
||
} while (pCurDevObj != NULL
|
||
&& pCurDevObj != &SerialGlobals.AllDevObjs);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|