1191 lines
26 KiB
C
1191 lines
26 KiB
C
/*++
|
||
|
||
Copyright (c) 1991, 1992, 1993 - 1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
openclos.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the code that is very specific to
|
||
opening, closing, and cleaning up in the serial driver.
|
||
|
||
Author:
|
||
|
||
Anthony V. Ercolano 26-Sep-1991
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
|
||
|
||
BOOLEAN
|
||
SerialMarkOpen(
|
||
IN PVOID Context
|
||
);
|
||
|
||
BOOLEAN
|
||
SerialCheckOpen(
|
||
IN PVOID Context
|
||
);
|
||
|
||
BOOLEAN
|
||
SerialNullSynch(
|
||
IN PVOID Context
|
||
);
|
||
|
||
NTSTATUS
|
||
SerialCreateOpen(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
|
||
//
|
||
// Paged for open and PnP transactions
|
||
//
|
||
|
||
#pragma alloc_text(PAGESER,SerialGetCharTime)
|
||
#pragma alloc_text(PAGESER,SerialCleanup)
|
||
#pragma alloc_text(PAGESER,SerialClose)
|
||
#pragma alloc_text(PAGESER, SerialCheckOpen)
|
||
#pragma alloc_text(PAGESER, SerialMarkOpen)
|
||
|
||
//
|
||
// Always paged
|
||
//
|
||
|
||
#pragma alloc_text(PAGESRP0,SerialCreateOpen)
|
||
#pragma alloc_text(PAGESRP0, SerialDrainUART)
|
||
#endif // ALLOC_PRAGMA
|
||
|
||
typedef struct _SERIAL_CHECK_OPEN {
|
||
PSERIAL_DEVICE_EXTENSION Extension;
|
||
NTSTATUS *StatusOfOpen;
|
||
} SERIAL_CHECK_OPEN,*PSERIAL_CHECK_OPEN;
|
||
|
||
//
|
||
// Just a bogus little routine to make sure that we
|
||
// can synch with the ISR.
|
||
//
|
||
|
||
BOOLEAN
|
||
SerialNullSynch(
|
||
IN PVOID Context
|
||
) {
|
||
|
||
UNREFERENCED_PARAMETER(Context);
|
||
return FALSE;
|
||
}
|
||
|
||
NTSTATUS
|
||
SerialCreateOpen(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
We connect up to the interrupt for the create/open and initialize
|
||
the structures needed to maintain an open for a device.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to the device object for this device
|
||
|
||
Irp - Pointer to the IRP for the current request
|
||
|
||
Return Value:
|
||
|
||
The function value is the final status of the call
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSERIAL_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
|
||
SERIAL_CHECK_OPEN checkOpen;
|
||
NTSTATUS localStatus;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (extension->PNPState != SERIAL_PNP_STARTED) {
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Lock out changes to PnP state until we have our open state decided
|
||
//
|
||
|
||
ExAcquireFastMutex(&extension->OpenMutex);
|
||
|
||
if ((localStatus = SerialIRPPrologue(Irp, extension)) != STATUS_SUCCESS) {
|
||
ExReleaseFastMutex(&extension->OpenMutex);
|
||
SerialCompleteRequest(extension, Irp, IO_NO_INCREMENT);
|
||
return localStatus;
|
||
}
|
||
|
||
if (InterlockedIncrement(&extension->OpenCount) != 1) {
|
||
ExReleaseFastMutex(&extension->OpenMutex);
|
||
InterlockedDecrement(&extension->OpenCount);
|
||
Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||
SerialCompleteRequest(extension, Irp, IO_NO_INCREMENT);
|
||
return STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
SerialDump(
|
||
SERIRPPATH,
|
||
("SERIAL: Dispatch entry for: %x\n",Irp)
|
||
);
|
||
SerialDump(
|
||
SERDIAG3,
|
||
("SERIAL: In SerialCreateOpen\n")
|
||
);
|
||
|
||
//
|
||
// Before we do anything, let's make sure they aren't trying
|
||
// to create a directory. what's a driver to do!?
|
||
//
|
||
|
||
if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Create.Options &
|
||
FILE_DIRECTORY_FILE) {
|
||
ExReleaseFastMutex(&extension->OpenMutex);
|
||
|
||
Irp->IoStatus.Status = STATUS_NOT_A_DIRECTORY;
|
||
Irp->IoStatus.Information = 0;
|
||
|
||
SerialDump(
|
||
SERIRPPATH,
|
||
("SERIAL: Complete Irp: %x\n",Irp)
|
||
);
|
||
|
||
|
||
|
||
InterlockedDecrement(&extension->OpenCount);
|
||
SerialCompleteRequest(extension, Irp, IO_NO_INCREMENT);
|
||
|
||
return STATUS_NOT_A_DIRECTORY;
|
||
|
||
}
|
||
|
||
//
|
||
// Create a buffer for the RX data when no reads are outstanding.
|
||
//
|
||
|
||
extension->InterruptReadBuffer = NULL;
|
||
extension->BufferSize = 0;
|
||
|
||
switch (MmQuerySystemSize()) {
|
||
|
||
case MmLargeSystem: {
|
||
|
||
extension->BufferSize = 4096;
|
||
extension->InterruptReadBuffer = ExAllocatePool(
|
||
NonPagedPool,
|
||
extension->BufferSize
|
||
);
|
||
|
||
if (extension->InterruptReadBuffer) {
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
case MmMediumSystem: {
|
||
|
||
extension->BufferSize = 1024;
|
||
extension->InterruptReadBuffer = ExAllocatePool(
|
||
NonPagedPool,
|
||
extension->BufferSize
|
||
);
|
||
|
||
if (extension->InterruptReadBuffer) {
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
case MmSmallSystem: {
|
||
|
||
extension->BufferSize = 128;
|
||
extension->InterruptReadBuffer = ExAllocatePool(
|
||
NonPagedPool,
|
||
extension->BufferSize
|
||
);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (!extension->InterruptReadBuffer) {
|
||
ExReleaseFastMutex(&extension->OpenMutex);
|
||
|
||
extension->BufferSize = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
Irp->IoStatus.Information = 0;
|
||
|
||
SerialDump(
|
||
SERIRPPATH,
|
||
("SERIAL: Complete Irp: %x\n",Irp)
|
||
);
|
||
|
||
InterlockedDecrement(&extension->OpenCount);
|
||
SerialCompleteRequest(extension, Irp, IO_NO_INCREMENT);
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
}
|
||
|
||
//
|
||
// Ok, it looks like we really are going to open. Lock down the
|
||
// driver.
|
||
//
|
||
SerialLockPagableSectionByHandle(SerialGlobals.PAGESER_Handle);
|
||
|
||
//
|
||
// Power up the stack
|
||
//
|
||
|
||
(void)SerialGotoPowerState(DeviceObject, extension, PowerDeviceD0);
|
||
|
||
//
|
||
// Not currently waiting for wake up
|
||
//
|
||
|
||
extension->SendWaitWake = FALSE;
|
||
|
||
//
|
||
// On a new open we "flush" the read queue by initializing the
|
||
// count of characters.
|
||
//
|
||
|
||
extension->CharsInInterruptBuffer = 0;
|
||
extension->LastCharSlot = extension->InterruptReadBuffer +
|
||
(extension->BufferSize - 1);
|
||
|
||
extension->ReadBufferBase = extension->InterruptReadBuffer;
|
||
extension->CurrentCharSlot = extension->InterruptReadBuffer;
|
||
extension->FirstReadableChar = extension->InterruptReadBuffer;
|
||
|
||
extension->TotalCharsQueued = 0;
|
||
|
||
//
|
||
// We set up the default xon/xoff limits.
|
||
//
|
||
|
||
extension->HandFlow.XoffLimit = extension->BufferSize >> 3;
|
||
extension->HandFlow.XonLimit = extension->BufferSize >> 1;
|
||
|
||
extension->WmiCommData.XoffXmitThreshold = extension->HandFlow.XoffLimit;
|
||
extension->WmiCommData.XonXmitThreshold = extension->HandFlow.XonLimit;
|
||
|
||
extension->BufferSizePt8 = ((3*(extension->BufferSize>>2))+
|
||
(extension->BufferSize>>4));
|
||
|
||
//
|
||
// Mark the device as busy for WMI
|
||
//
|
||
|
||
extension->WmiCommData.IsBusy = TRUE;
|
||
|
||
extension->IrpMaskLocation = NULL;
|
||
extension->HistoryMask = 0;
|
||
extension->IsrWaitMask = 0;
|
||
|
||
extension->SendXonChar = FALSE;
|
||
extension->SendXoffChar = FALSE;
|
||
|
||
#if !DBG
|
||
//
|
||
// Clear out the statistics.
|
||
//
|
||
|
||
KeSynchronizeExecution(
|
||
extension->Interrupt,
|
||
SerialClearStats,
|
||
extension
|
||
);
|
||
#endif
|
||
|
||
//
|
||
// The escape char replacement must be reset upon every open.
|
||
//
|
||
|
||
extension->EscapeChar = 0;
|
||
|
||
if (!extension->PermitShare) {
|
||
|
||
if (!extension->InterruptShareable) {
|
||
|
||
checkOpen.Extension = extension;
|
||
checkOpen.StatusOfOpen = &Irp->IoStatus.Status;
|
||
|
||
KeSynchronizeExecution(
|
||
extension->Interrupt,
|
||
SerialCheckOpen,
|
||
&checkOpen
|
||
);
|
||
|
||
} else {
|
||
|
||
KeSynchronizeExecution(
|
||
extension->Interrupt,
|
||
SerialMarkOpen,
|
||
extension
|
||
);
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Synchronize with the ISR and let it know that the device
|
||
// has been successfully opened.
|
||
//
|
||
|
||
KeSynchronizeExecution(
|
||
extension->Interrupt,
|
||
SerialMarkOpen,
|
||
extension
|
||
);
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
//
|
||
// We have been marked open, so now the PnP state can change
|
||
//
|
||
|
||
ExReleaseFastMutex(&extension->OpenMutex);
|
||
|
||
localStatus = Irp->IoStatus.Status;
|
||
Irp->IoStatus.Information=0L;
|
||
|
||
SerialDump(
|
||
SERIRPPATH,
|
||
("SERIAL: Complete Irp: %x\n",Irp)
|
||
);
|
||
|
||
if (!NT_SUCCESS(localStatus)) {
|
||
if (extension->InterruptReadBuffer != NULL) {
|
||
ExFreePool(extension->InterruptReadBuffer);
|
||
extension->InterruptReadBuffer = NULL;
|
||
}
|
||
|
||
InterlockedDecrement(&extension->OpenCount);
|
||
}
|
||
|
||
SerialCompleteRequest(extension, Irp, IO_NO_INCREMENT);
|
||
|
||
return localStatus;
|
||
|
||
}
|
||
|
||
VOID
|
||
SerialDrainUART(IN PSERIAL_DEVICE_EXTENSION PDevExt,
|
||
IN PLARGE_INTEGER PDrainTime)
|
||
{
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Wait until all characters have been emptied out of the hardware.
|
||
//
|
||
|
||
while ((READ_LINE_STATUS(PDevExt->Controller) &
|
||
(SERIAL_LSR_THRE | SERIAL_LSR_TEMT))
|
||
!= (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) {
|
||
|
||
KeDelayExecutionThread(KernelMode, FALSE, PDrainTime);
|
||
}
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SerialClose(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
We simply disconnect the interrupt for now.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to the device object for this device
|
||
|
||
Irp - Pointer to the IRP for the current request
|
||
|
||
Return Value:
|
||
|
||
The function value is the final status of the call
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// This "timer value" is used to wait 10 character times
|
||
// after the hardware is empty before we actually "run down"
|
||
// all of the flow control/break junk.
|
||
//
|
||
LARGE_INTEGER tenCharDelay;
|
||
|
||
//
|
||
// Holds a character time.
|
||
//
|
||
LARGE_INTEGER charTime;
|
||
|
||
//
|
||
// Just what it says. This is the serial specific device
|
||
// extension of the device object create for the serial driver.
|
||
//
|
||
PSERIAL_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
|
||
|
||
NTSTATUS status;
|
||
|
||
//
|
||
// Number of opens still active
|
||
//
|
||
|
||
LONG openCount;
|
||
|
||
//
|
||
// Number of DPC's still pending
|
||
//
|
||
|
||
ULONG pendingDPCs;
|
||
|
||
ULONG flushCount;
|
||
|
||
//
|
||
// Grab a mutex
|
||
//
|
||
|
||
ExAcquireFastMutex(&extension->CloseMutex);
|
||
|
||
|
||
//
|
||
// We succeed a close on a removing device
|
||
//
|
||
|
||
if ((status = SerialIRPPrologue(Irp, extension)) != STATUS_SUCCESS) {
|
||
SerialDump(SERERRORS, ("SERIAL: Close prologue failed for: %x\n",Irp));
|
||
if (status == STATUS_DELETE_PENDING) {
|
||
extension->BufferSize = 0;
|
||
ExFreePool(extension->InterruptReadBuffer);
|
||
extension->InterruptReadBuffer = NULL;
|
||
status = Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
SerialCompleteRequest(extension, Irp, IO_NO_INCREMENT);
|
||
openCount = InterlockedDecrement(&extension->OpenCount);
|
||
ASSERT(openCount == 0);
|
||
ExReleaseFastMutex(&extension->CloseMutex);
|
||
return status;
|
||
}
|
||
|
||
ASSERT(extension->OpenCount == 1);
|
||
|
||
if (extension->OpenCount != 1) {
|
||
SerialDump(SERERRORS, ("SERIAL: Close open count bad for: 0x%x\n",Irp));
|
||
SerialDump(SERERRORS, ("------: Count: %x Addr: 0x%x\n",
|
||
extension->OpenCount, &extension->OpenCount));
|
||
ExReleaseFastMutex(&extension->CloseMutex);
|
||
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
SerialCompleteRequest(extension, Irp, IO_NO_INCREMENT);
|
||
return STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
|
||
SerialDump(
|
||
SERIRPPATH,
|
||
("SERIAL: Dispatch entry for: %x\n",Irp)
|
||
);
|
||
SerialDump(
|
||
SERDIAG3,
|
||
("SERIAL: In SerialClose\n")
|
||
);
|
||
|
||
charTime.QuadPart = -SerialGetCharTime(extension).QuadPart;
|
||
|
||
//
|
||
// Do this now so that if the isr gets called it won't do anything
|
||
// to cause more chars to get sent. We want to run down the hardware.
|
||
//
|
||
|
||
extension->DeviceIsOpened = FALSE;
|
||
|
||
//
|
||
// Synchronize with the isr to turn off break if it
|
||
// is already on.
|
||
//
|
||
|
||
KeSynchronizeExecution(
|
||
extension->Interrupt,
|
||
SerialTurnOffBreak,
|
||
extension
|
||
);
|
||
|
||
//
|
||
// Wait a reasonable amount of time (20 * fifodepth) until all characters
|
||
// have been emptied out of the hardware.
|
||
//
|
||
|
||
#if defined(NEC_98)
|
||
//
|
||
// all characters not have been emptied out of the hardware.
|
||
//
|
||
#else
|
||
for (flushCount = (20 * 16); flushCount != 0; flushCount--) {
|
||
if ((READ_LINE_STATUS(extension->Controller) &
|
||
(SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) !=
|
||
(SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) {
|
||
|
||
KeDelayExecutionThread(KernelMode, FALSE, &charTime);
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (flushCount == 0) {
|
||
SerialMarkHardwareBroken(extension);
|
||
}
|
||
#endif //defined(NEC_98)
|
||
|
||
//
|
||
// Synchronize with the ISR to let it know that interrupts are
|
||
// no longer important.
|
||
//
|
||
|
||
KeSynchronizeExecution(
|
||
extension->Interrupt,
|
||
SerialMarkClose,
|
||
extension
|
||
);
|
||
|
||
|
||
//
|
||
// If the driver has automatically transmitted an Xoff in
|
||
// the context of automatic receive flow control then we
|
||
// should transmit an Xon.
|
||
//
|
||
|
||
if (extension->RXHolding & SERIAL_RX_XOFF) {
|
||
|
||
//
|
||
// Loop until the holding register is empty.
|
||
//
|
||
|
||
while (!(READ_LINE_STATUS(extension->Controller) &
|
||
SERIAL_LSR_THRE)) {
|
||
|
||
KeDelayExecutionThread(
|
||
KernelMode,
|
||
FALSE,
|
||
&charTime
|
||
);
|
||
|
||
}
|
||
|
||
WRITE_TRANSMIT_HOLDING(
|
||
extension->Controller,
|
||
extension->SpecialChars.XonChar
|
||
);
|
||
|
||
//
|
||
// Wait a reasonable amount of time for the characters
|
||
// to be emptied out of the hardware.
|
||
//
|
||
|
||
for (flushCount = (20 * 16); flushCount != 0; flushCount--) {
|
||
if ((READ_LINE_STATUS(extension->Controller) &
|
||
(SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) !=
|
||
(SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) {
|
||
|
||
KeDelayExecutionThread(KernelMode, FALSE, &charTime);
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (flushCount == 0) {
|
||
SerialMarkHardwareBroken(extension);
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// The hardware is empty. Delay 10 character times before
|
||
// shut down all the flow control.
|
||
//
|
||
|
||
tenCharDelay.QuadPart = charTime.QuadPart * 10;
|
||
|
||
KeDelayExecutionThread(
|
||
KernelMode,
|
||
TRUE,
|
||
&tenCharDelay
|
||
);
|
||
|
||
SerialClrDTR(extension);
|
||
|
||
//
|
||
// We have to be very careful how we clear the RTS line.
|
||
// Transmit toggling might have been on at some point.
|
||
//
|
||
// We know that there is nothing left that could start
|
||
// out the "polling" execution path. We need to
|
||
// check the counter that indicates that the execution
|
||
// path is active. If it is then we loop delaying one
|
||
// character time. After each delay we check to see if
|
||
// the counter has gone to zero. When it has we know that
|
||
// the execution path should be just about finished. We
|
||
// make sure that we still aren't in the routine that
|
||
// synchronized execution with the ISR by synchronizing
|
||
// ourselve with the ISR.
|
||
//
|
||
|
||
if (extension->CountOfTryingToLowerRTS) {
|
||
|
||
do {
|
||
|
||
KeDelayExecutionThread(
|
||
KernelMode,
|
||
FALSE,
|
||
&charTime
|
||
);
|
||
|
||
} while (extension->CountOfTryingToLowerRTS);
|
||
|
||
KeSynchronizeExecution(
|
||
extension->Interrupt,
|
||
SerialNullSynch,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// The execution path should no longer exist that
|
||
// is trying to push down the RTS. Well just
|
||
// make sure it's down by falling through to
|
||
// code that forces it down.
|
||
//
|
||
|
||
}
|
||
|
||
SerialClrRTS(extension);
|
||
|
||
//
|
||
// Clean out the holding reasons (since we are closed).
|
||
//
|
||
|
||
extension->RXHolding = 0;
|
||
extension->TXHolding = 0;
|
||
|
||
//
|
||
// Mark device as not busy for WMI
|
||
//
|
||
|
||
extension->WmiCommData.IsBusy = FALSE;
|
||
|
||
//
|
||
// All is done. The port has been disabled from interrupting
|
||
// so there is no point in keeping the memory around.
|
||
//
|
||
|
||
extension->BufferSize = 0;
|
||
ExFreePool(extension->InterruptReadBuffer);
|
||
extension->InterruptReadBuffer = NULL;
|
||
|
||
//
|
||
// Stop waiting for wakeup
|
||
//
|
||
|
||
extension->SendWaitWake = FALSE;
|
||
|
||
if (extension->PendingWakeIrp != NULL) {
|
||
IoCancelIrp(extension->PendingWakeIrp);
|
||
}
|
||
|
||
//
|
||
// Power down our device stack
|
||
//
|
||
|
||
(void)SerialGotoPowerState(DeviceObject, extension, PowerDeviceD3);
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
Irp->IoStatus.Information=0L;
|
||
|
||
SerialDump(
|
||
SERIRPPATH,
|
||
("SERIAL: Complete Irp: %x\n",Irp)
|
||
);
|
||
SerialCompleteRequest(extension, Irp, IO_NO_INCREMENT);
|
||
|
||
//
|
||
// Unlock the pages. If this is the last reference to the section
|
||
// then the driver code will be flushed out.
|
||
//
|
||
|
||
//
|
||
// First, we have to let the DPC's drain. No more should be queued
|
||
// since we aren't taking interrupts now....
|
||
//
|
||
|
||
pendingDPCs = InterlockedDecrement(&extension->DpcCount);
|
||
|
||
if (pendingDPCs) {
|
||
SerialDump(SERDIAG1, ("SERIAL: Drainging DPC's: %x\n",Irp));
|
||
KeWaitForSingleObject(&extension->PendingDpcEvent, Executive,
|
||
KernelMode, FALSE, NULL);
|
||
}
|
||
|
||
|
||
SerialDump(SERDIAG1, ("SERIAL: DPC's drained: %x\n",Irp));
|
||
|
||
|
||
|
||
//
|
||
// Pages must be locked to release the mutex, so don't unlock
|
||
// them until after we release the mutex
|
||
//
|
||
|
||
ExReleaseFastMutex(&extension->CloseMutex);
|
||
|
||
//
|
||
// Reset for next open
|
||
//
|
||
|
||
InterlockedIncrement(&extension->DpcCount);
|
||
|
||
openCount = InterlockedDecrement(&extension->OpenCount);
|
||
|
||
ASSERT(openCount == 0);
|
||
SerialUnlockPagableImageSection(SerialGlobals.PAGESER_Handle);
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
SerialCheckOpen(
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will traverse the circular doubly linked list
|
||
of devices that are using the same interrupt object. It will look
|
||
for other devices that are open. If it doesn't find any
|
||
it will indicate that it is ok to open this device.
|
||
|
||
If it finds another device open we have two cases:
|
||
|
||
1) The device we are trying to open is on a multiport card.
|
||
|
||
If the already open device is part of a multiport device
|
||
this code will indicate it is ok to open. We do this on the
|
||
theory that the multiport devices are daisy chained
|
||
and the cards can correctly arbitrate the interrupt
|
||
line. Note this assumption could be wrong. Somebody
|
||
could put two non-daisychained multiports on the
|
||
same interrupt. However, only a total clod would do
|
||
such a thing, and in my opinion deserves everthing they
|
||
get.
|
||
|
||
2) The device we are trying to open is not on a multiport card.
|
||
|
||
We indicate that it is not ok to open.
|
||
|
||
Arguments:
|
||
|
||
Context - This is a structure that contains a pointer to the
|
||
extension of the device we are trying to open, and
|
||
a pointer to an NTSTATUS that will indicate whether
|
||
the device was opened or not.
|
||
|
||
Return Value:
|
||
|
||
This routine always returns FALSE.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSERIAL_DEVICE_EXTENSION extensionToOpen =
|
||
((PSERIAL_CHECK_OPEN)Context)->Extension;
|
||
NTSTATUS *status = ((PSERIAL_CHECK_OPEN)Context)->StatusOfOpen;
|
||
PLIST_ENTRY firstEntry = &extensionToOpen->CommonInterruptObject;
|
||
PLIST_ENTRY currentEntry = firstEntry;
|
||
PSERIAL_DEVICE_EXTENSION currentExtension;
|
||
|
||
do {
|
||
|
||
currentExtension = CONTAINING_RECORD(
|
||
currentEntry,
|
||
SERIAL_DEVICE_EXTENSION,
|
||
CommonInterruptObject
|
||
);
|
||
|
||
if (currentExtension->DeviceIsOpened) {
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
currentEntry = currentExtension->CommonInterruptObject.Flink;
|
||
|
||
} while (currentEntry != firstEntry);
|
||
|
||
if (currentEntry == firstEntry) {
|
||
|
||
//
|
||
// We searched the whole list and found no other opens
|
||
// mark the status as successful and call the regular
|
||
// opening routine.
|
||
//
|
||
|
||
*status = STATUS_SUCCESS;
|
||
SerialMarkOpen(extensionToOpen);
|
||
|
||
} else {
|
||
|
||
if (!extensionToOpen->PortOnAMultiportCard) {
|
||
|
||
*status = STATUS_SHARED_IRQ_BUSY;
|
||
|
||
} else {
|
||
|
||
if (!currentExtension->PortOnAMultiportCard) {
|
||
|
||
*status = STATUS_SHARED_IRQ_BUSY;
|
||
|
||
} else {
|
||
|
||
*status = STATUS_SUCCESS;
|
||
SerialMarkOpen(extensionToOpen);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
BOOLEAN
|
||
SerialMarkOpen(
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine merely sets a boolean to true to mark the fact that
|
||
somebody opened the device and its worthwhile to pay attention
|
||
to interrupts.
|
||
|
||
Arguments:
|
||
|
||
Context - Really a pointer to the device extension.
|
||
|
||
Return Value:
|
||
|
||
This routine always returns 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)
|
||
|
||
SerialReset(extension);
|
||
|
||
//
|
||
// Prepare for the opening by re-enabling interrupts.
|
||
//
|
||
// We do this my modifying the OUT2 line in the modem control.
|
||
// In PC's this bit is "anded" with the interrupt line.
|
||
//
|
||
// For the Jensen, we will ALWAYS leave the line high. That's
|
||
// the way the hardware engineers want it.
|
||
//
|
||
|
||
WRITE_MODEM_CONTROL(
|
||
extension->Controller,
|
||
(UCHAR)(READ_MODEM_CONTROL(extension->Controller) | SERIAL_MCR_OUT2)
|
||
);
|
||
|
||
extension->DeviceIsOpened = TRUE;
|
||
extension->ErrorWord = 0;
|
||
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
SerialDisableUART(IN PVOID Context)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine disables the UART and puts it in a "safe" state when
|
||
not in use (like a close or powerdown).
|
||
|
||
Arguments:
|
||
|
||
Context - Really a pointer to the device extension.
|
||
|
||
Return Value:
|
||
|
||
This routine always returns 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)
|
||
|
||
//
|
||
// Prepare for the closing by stopping interrupts.
|
||
//
|
||
// We do this by adjusting the OUT2 line in the modem control.
|
||
// In PC's this bit is "anded" with the interrupt line.
|
||
//
|
||
// The line should stay high on the Jensen because that's the
|
||
// way the hardware engineers did it.
|
||
//
|
||
|
||
if (!extension->Jensen) {
|
||
WRITE_MODEM_CONTROL(extension->Controller,
|
||
(UCHAR)(READ_MODEM_CONTROL(extension->Controller)
|
||
& ~SERIAL_MCR_OUT2));
|
||
}
|
||
|
||
if (extension->FifoPresent) {
|
||
WRITE_FIFO_CONTROL(extension->Controller, (UCHAR)0);
|
||
}
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
SerialMarkClose(
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine merely sets a boolean to false to mark the fact that
|
||
somebody closed the device and it's no longer worthwhile to pay attention
|
||
to interrupts. It also disables the UART.
|
||
|
||
Arguments:
|
||
|
||
Context - Really a pointer to the device extension.
|
||
|
||
Return Value:
|
||
|
||
This routine always returns FALSE.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSERIAL_DEVICE_EXTENSION extension = Context;
|
||
|
||
SerialDisableUART(Context);
|
||
extension->DeviceIsOpened = FALSE;
|
||
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SerialCleanup(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to kill all longstanding IO operations.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to the device object for this device
|
||
|
||
Irp - Pointer to the IRP for the current request
|
||
|
||
Return Value:
|
||
|
||
The function value is the final status of the call
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSERIAL_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
|
||
NTSTATUS status;
|
||
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// We succeed a cleanup on a removing device
|
||
//
|
||
|
||
if ((status = SerialIRPPrologue(Irp, extension)) != STATUS_SUCCESS) {
|
||
if (status == STATUS_DELETE_PENDING) {
|
||
status = Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
}
|
||
SerialCompleteRequest(extension, Irp, IO_NO_INCREMENT);
|
||
return status;
|
||
}
|
||
|
||
SerialDump(
|
||
SERIRPPATH,
|
||
("SERIAL: Dispatch entry for: %x\n",Irp)
|
||
);
|
||
|
||
SerialKillPendingIrps(DeviceObject);
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
Irp->IoStatus.Information=0L;
|
||
|
||
SerialDump(
|
||
SERIRPPATH,
|
||
("SERIAL: Complete Irp: %x\n",Irp)
|
||
);
|
||
SerialCompleteRequest(extension, Irp, IO_NO_INCREMENT);
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
LARGE_INTEGER
|
||
SerialGetCharTime(
|
||
IN PSERIAL_DEVICE_EXTENSION Extension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function will return the number of 100 nanosecond intervals
|
||
there are in one character time (based on the present form
|
||
of flow control.
|
||
|
||
Arguments:
|
||
|
||
Extension - Just what it says.
|
||
|
||
Return Value:
|
||
|
||
100 nanosecond intervals in a character time.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG dataSize;
|
||
ULONG paritySize;
|
||
ULONG stopSize;
|
||
ULONG charTime;
|
||
ULONG bitTime;
|
||
LARGE_INTEGER tmp;
|
||
|
||
|
||
if ((Extension->LineControl & SERIAL_DATA_MASK) == SERIAL_5_DATA) {
|
||
dataSize = 5;
|
||
} else if ((Extension->LineControl & SERIAL_DATA_MASK)
|
||
== SERIAL_6_DATA) {
|
||
dataSize = 6;
|
||
} else if ((Extension->LineControl & SERIAL_DATA_MASK)
|
||
== SERIAL_7_DATA) {
|
||
dataSize = 7;
|
||
} else if ((Extension->LineControl & SERIAL_DATA_MASK)
|
||
== SERIAL_8_DATA) {
|
||
dataSize = 8;
|
||
}
|
||
|
||
paritySize = 1;
|
||
if ((Extension->LineControl & SERIAL_PARITY_MASK)
|
||
== SERIAL_NONE_PARITY) {
|
||
|
||
paritySize = 0;
|
||
|
||
}
|
||
|
||
if (Extension->LineControl & SERIAL_2_STOP) {
|
||
|
||
//
|
||
// Even if it is 1.5, for sanities sake were going
|
||
// to say 2.
|
||
//
|
||
|
||
stopSize = 2;
|
||
|
||
} else {
|
||
|
||
stopSize = 1;
|
||
|
||
}
|
||
|
||
//
|
||
// First we calculate the number of 100 nanosecond intervals
|
||
// are in a single bit time (Approximately).
|
||
//
|
||
|
||
bitTime = (10000000+(Extension->CurrentBaud-1))/Extension->CurrentBaud;
|
||
charTime = bitTime + ((dataSize+paritySize+stopSize)*bitTime);
|
||
|
||
tmp.QuadPart = charTime;
|
||
return tmp;
|
||
|
||
}
|