Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

711 lines
20 KiB

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Copyright (c) 1991, 1992, 1993 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
Revision History :
-----------------------------------------------------------------------------*/
#include "precomp.h"
BOOLEAN SerialMarkOpen(IN PVOID Context);
BOOLEAN SerialNullSynch(IN PVOID Context);
BOOLEAN GetFifoStatus(IN PVOID Context);
#ifdef ALLOC_PRAGMA
#endif
typedef struct _SERIAL_CHECK_OPEN
{
PPORT_DEVICE_EXTENSION pPort;
NTSTATUS *StatusOfOpen;
} SERIAL_CHECK_OPEN,*PSERIAL_CHECK_OPEN;
typedef struct _FIFO_STATUS
{
PPORT_DEVICE_EXTENSION pPort;
ULONG BytesInTxFIFO;
ULONG BytesInRxFIFO;
} FIFO_STATUS,*PFIFO_STATUS;
// 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
-----------------------------------------------------------------------------*/
{
PPORT_DEVICE_EXTENSION pPort = DeviceObject->DeviceExtension;
SERIAL_CHECK_OPEN checkOpen;
NTSTATUS status;
SerialDump(SERIRPPATH, ("Dispatch entry for: %x\n", Irp));
SerialDump(SERDIAG3, ("In SerialCreateOpen\n"));
SpxIRPCounter(pPort, Irp, IRP_SUBMITTED); // Increment counter for performance stats.
// Before we do anything, let's make sure they aren't trying
// to create a directory. This is a silly, but what's a driver to do!?
if(IoGetCurrentIrpStackLocation(Irp)->Parameters.Create.Options & FILE_DIRECTORY_FILE)
{
Irp->IoStatus.Status = STATUS_NOT_A_DIRECTORY;
Irp->IoStatus.Information = 0;
SerialDump(SERIRPPATH, ("Complete Irp: %x\n",Irp));
SpxIRPCounter(pPort, Irp, IRP_COMPLETED); // Increment counter for performance stats.
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_NOT_A_DIRECTORY;
}
// Do not allow any software to open the card object.
if(DeviceObject->DeviceType != FILE_DEVICE_SERIAL_PORT)
{
Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
Irp->IoStatus.Information = 0;
SpxIRPCounter(pPort, Irp, IRP_COMPLETED); // Increment counter for performance stats.
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return(STATUS_ACCESS_DENIED);
}
if(pPort->DeviceIsOpen) // Is port already open?
{
status = STATUS_ACCESS_DENIED; // Yes, deny access
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
SpxIRPCounter(pPort, Irp, IRP_COMPLETED);
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return(status);
}
// Create a buffer for the RX data when no reads are outstanding.
pPort->InterruptReadBuffer = NULL;
pPort->BufferSize = 0;
switch(MmQuerySystemSize())
{
case MmLargeSystem:
pPort->BufferSize = 4096;
break;
case MmMediumSystem:
pPort->BufferSize = 1024;
break;
case MmSmallSystem:
pPort->BufferSize = 128;
default:
break;
}
if(pPort->BufferSize)
{
pPort->BufferSizes.pINBuffer = SpxAllocateMem(NonPagedPool, pPort->BufferSize);
pPort->BufferSizes.INBufferSize = pPort->BufferSize;
}
else
{
pPort->BufferSize = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Information = 0;
SerialDump(SERIRPPATH, ("Complete Irp: %x\n",Irp));
SpxIRPCounter(pPort, Irp, IRP_COMPLETED); // Increment counter for performance stats.
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INSUFFICIENT_RESOURCES;
}
// On a new open we "flush" the read queue by initializing the count of characters.
pPort->CharsInInterruptBuffer = 0;
pPort->ReadBufferBase = pPort->InterruptReadBuffer;
pPort->CurrentCharSlot = pPort->InterruptReadBuffer;
pPort->FirstReadableChar = pPort->InterruptReadBuffer;
pPort->TotalCharsQueued = 0;
// We set up the default xon/xoff limits.
pPort->HandFlow.XoffLimit = pPort->BufferSize >> 3;
pPort->HandFlow.XonLimit = pPort->BufferSize >> 1;
pPort->BufferSizePt8 = ((3*(pPort->BufferSize>>2)) + (pPort->BufferSize>>4));
SpxDbgMsg(SPX_MISC_DBG, ("%s: 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",
PRODUCT_NAME,
pPort->BufferSize,
pPort->HandFlow.XoffLimit,
pPort->HandFlow.XonLimit,
pPort->BufferSizePt8 ));
pPort->IrpMaskLocation = NULL;
pPort->HistoryMask = 0;
pPort->IsrWaitMask = 0;
pPort->SendXonChar = FALSE;
pPort->SendXoffChar = FALSE;
// Clear out the statistics.
KeSynchronizeExecution(pPort->Interrupt, SerialClearStats, pPort);
// The escape char replacement must be reset upon every open
pPort->EscapeChar = 0;
GetPortSettings(pPort->DeviceObject); // Get Saved Port Settings if present.
// Synchronize with the ISR and let it know that the device has been successfully opened.
KeSynchronizeExecution(pPort->Interrupt, SerialMarkOpen, pPort);
status = STATUS_SUCCESS;
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0L;
SerialDump(SERIRPPATH, ("Complete Irp: %x\n", Irp));
SpxIRPCounter(pPort, Irp, IRP_COMPLETED); // Increment counter for performance stats.
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
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;
LARGE_INTEGER charTime; // Holds a character time.
FIFO_STATUS FifoStatus;
// Just what it says. This is the serial specific device
// extension of the device object create for the serial driver.
PPORT_DEVICE_EXTENSION pPort = DeviceObject->DeviceExtension;
SerialDump(SERIRPPATH, ("Dispatch entry for: %x\n", Irp));
SerialDump(SERDIAG3, ("In SerialClose\n"));
SpxIRPCounter(pPort, Irp, IRP_SUBMITTED); // Increment counter for performance stats.
charTime.QuadPart = -SerialGetCharTime(pPort).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.
pPort->DeviceIsOpen = FALSE;
// Synchronize with the isr to turn off break if it is already on.
KeSynchronizeExecution(pPort->Interrupt, SerialTurnOffBreak, pPort);
// Wait until all characters have been emptied out of the hardware.
FifoStatus.pPort = pPort;
// Get the number of characters left to send in the Tx FIFO
if(KeSynchronizeExecution(pPort->Interrupt, GetFifoStatus, &FifoStatus))
{
ULONG i = 0;
// Wait the appropriate time
for(i = 0; i<FifoStatus.BytesInTxFIFO; i++)
KeDelayExecutionThread(KernelMode, FALSE, &charTime);
}
// Synchronize with the ISR to let it know that interrupts are no longer important.
KeSynchronizeExecution(pPort->Interrupt, SerialMarkClose, pPort);
// 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(pPort);
SerialClrRTS(pPort);
// Clean out the holding reasons (since we are closed).
pPort->RXHolding = 0;
pPort->TXHolding = 0;
// All is done. The port has been disabled from interrupting
// so there is no point in keeping the memory around.
pPort->BufferSize = 0;
SpxFreeMem(pPort->BufferSizes.pINBuffer);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0L;
SerialDump(SERIRPPATH, ("Complete Irp: %x\n",Irp));
SpxIRPCounter(pPort, Irp, IRP_COMPLETED); // Increment counter for performance stats.
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
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.
-----------------------------------------------------------------------------*/
{
PPORT_DEVICE_EXTENSION pPort = Context;
SerialReset(pPort);
// Set Buffer sizes.
pPort->pUartLib->UL_BufferControl_XXXX(pPort->pUart, &pPort->BufferSizes, UL_BC_OP_SET, UL_BC_BUFFER | UL_BC_IN | UL_BC_OUT);
// Apply settings.
ApplyInitialPortSettings(pPort);
// Enable interrupts.
pPort->UartConfig.InterruptEnable = UC_IE_RX_INT | UC_IE_TX_INT | UC_IE_RX_STAT_INT | UC_IE_MODEM_STAT_INT;
pPort->pUartLib->UL_SetConfig_XXXX(pPort->pUart, &pPort->UartConfig, UC_INT_ENABLE_MASK);
SpxDbgMsg(SERINFO,("%s: PORT OPENED: (%.8X)\n", PRODUCT_NAME, pPort->Controller));
pPort->DeviceIsOpen = TRUE;
pPort->ErrorWord = 0;
#ifdef WMI_SUPPORT
UPDATE_WMI_XMIT_THRESHOLDS(pPort->WmiCommData, pPort->HandFlow);
pPort->WmiCommData.IsBusy = TRUE;
#endif
return FALSE;
}
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.
Arguments:
Context - Really a pointer to the device extension.
Return Value:
This routine always returns FALSE.
-----------------------------------------------------------------------------*/
{
PPORT_DEVICE_EXTENSION pPort = Context;
// CONCERN!!
// We used to disable interrupts here by writing OUT2 to zero, this bit has
// no effect on the PCI device so what happens if we get an interrupt after
// the port has been closed?
// Just reset the device
SpxDbgMsg(SPX_TRACE_CALLS, ("%s: Serial Mark Close\n", PRODUCT_NAME));
pPort->pUartLib->UL_ResetUart_XXXX(pPort->pUart); // Reset UART and turn off interrupts.
ApplyInitialPortSettings(pPort);
pPort->DeviceIsOpen = FALSE;
#ifdef WMI_SUPPORT
pPort->WmiCommData.IsBusy = FALSE;
#endif
pPort->BufferSizes.pINBuffer = NULL; // We are now finished with the IN Buffer
pPort->BufferSizes.INBufferSize = 0;
pPort->pUartLib->UL_BufferControl_XXXX(pPort->pUart, &pPort->BufferSizes, UL_BC_OP_SET, UL_BC_BUFFER | UL_BC_IN);
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
-----------------------------------------------------------------------------*/
{
PPORT_DEVICE_EXTENSION pPort = DeviceObject->DeviceExtension;
KIRQL oldIrql;
SerialDump(SERIRPPATH,("Dispatch entry for: %x\n", Irp));
SpxIRPCounter(pPort, Irp, IRP_SUBMITTED); // Increment counter for performance stats.
// First kill all the reads and writes.
SerialKillAllReadsOrWrites(DeviceObject, &pPort->WriteQueue, &pPort->CurrentWriteIrp);
SerialKillAllReadsOrWrites(DeviceObject, &pPort->ReadQueue, &pPort->CurrentReadIrp);
// Next get rid of purges.
SerialKillAllReadsOrWrites(DeviceObject, &pPort->PurgeQueue, &pPort->CurrentPurgeIrp);
// Get rid of any mask operations.
SerialKillAllReadsOrWrites(DeviceObject, &pPort->MaskQueue, &pPort->CurrentMaskIrp);
// Now get rid a pending wait mask irp.
IoAcquireCancelSpinLock(&oldIrql);
if(pPort->CurrentWaitIrp)
{
PDRIVER_CANCEL cancelRoutine;
cancelRoutine = pPort->CurrentWaitIrp->CancelRoutine;
pPort->CurrentWaitIrp->Cancel = TRUE;
if(cancelRoutine)
{
pPort->CurrentWaitIrp->CancelIrql = oldIrql;
pPort->CurrentWaitIrp->CancelRoutine = NULL;
cancelRoutine(DeviceObject, pPort->CurrentWaitIrp);
}
}
else
{
IoReleaseCancelSpinLock(oldIrql);
}
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0L;
SerialDump(SERIRPPATH,("Complete Irp: %x\n", Irp));
SpxIRPCounter(pPort, Irp, IRP_COMPLETED); // Increment counter for performance stats.
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
LARGE_INTEGER
SerialGetCharTime(IN PPORT_DEVICE_EXTENSION pPort)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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;
switch(pPort->UartConfig.FrameConfig & UC_FCFG_DATALEN_MASK)
{
case UC_FCFG_DATALEN_5:
dataSize = 5;
break;
case UC_FCFG_DATALEN_6:
dataSize = 6;
break;
case UC_FCFG_DATALEN_7:
dataSize = 7;
break;
case UC_FCFG_DATALEN_8:
dataSize = 8;
break;
default:
break;
}
if((pPort->UartConfig.FrameConfig & UC_FCFG_PARITY_MASK) == UC_FCFG_NO_PARITY)
paritySize = 0;
else
paritySize = 1;
if((pPort->UartConfig.FrameConfig & UC_FCFG_STOPBITS_MASK) == UC_FCFG_STOPBITS_1)
stopSize = 1;
else
stopSize = 2; // Even if it is 1.5, for sanities sake were going to say 2.
// First we calculate the number of 100 nanosecond intervals
// are in a single bit time (Approximately).
bitTime = (10000000 + (pPort->UartConfig.TxBaud - 1)) / pPort->UartConfig.TxBaud;
charTime = bitTime + ((dataSize + paritySize + stopSize) * bitTime);
tmp.QuadPart = charTime;
return tmp;
}
BOOLEAN
GetFifoStatus(IN PVOID Context)
{
PFIFO_STATUS pFifoStatus = Context;
PPORT_DEVICE_EXTENSION pPort = pFifoStatus->pPort;
GET_BUFFER_STATE GetBufferState;
// Get the FIFO status.
pPort->pUartLib->UL_BufferControl_XXXX(pPort->pUart, &GetBufferState, UL_BC_OP_GET, UL_BC_FIFO | UL_BC_IN | UL_BC_OUT);
pFifoStatus->BytesInTxFIFO = GetBufferState.BytesInTxFIFO;
pFifoStatus->BytesInRxFIFO = GetBufferState.BytesInRxFIFO;
if(pFifoStatus->BytesInTxFIFO || pFifoStatus->BytesInRxFIFO)
return TRUE;
return FALSE;
}
BOOLEAN
ApplyInitialPortSettings(IN PVOID Context)
{
PPORT_DEVICE_EXTENSION pPort = Context;
UART_CONFIG UartConfig = {0};
// Set FIFO Flow Control Levels
pPort->UartConfig.LoFlowCtrlThreshold = pPort->LoFlowCtrlThreshold;
pPort->UartConfig.HiFlowCtrlThreshold = pPort->HiFlowCtrlThreshold;
// Apply Flow control thresholds.
pPort->pUartLib->UL_SetConfig_XXXX(pPort->pUart, &pPort->UartConfig, UC_FC_THRESHOLD_SETTING_MASK);
// Fill BufferSizes Struct and apply FIFO settings.
pPort->BufferSizes.TxFIFOSize = pPort->TxFIFOSize;
pPort->BufferSizes.RxFIFOSize = pPort->RxFIFOSize;
pPort->BufferSizes.TxFIFOTrigLevel = (BYTE)pPort->TxFIFOTrigLevel;
pPort->BufferSizes.RxFIFOTrigLevel = (BYTE)pPort->RxFIFOTrigLevel;
// Set Buffer sizes and FIFO depths.
pPort->pUartLib->UL_BufferControl_XXXX(pPort->pUart, &pPort->BufferSizes, UL_BC_OP_SET, UL_BC_FIFO | UL_BC_IN | UL_BC_OUT);
// Just do a quick get config to see if flow threshold have
// changed as a result of changing the FIFO triggers.
pPort->pUartLib->UL_GetConfig_XXXX(pPort->pUart, &UartConfig);
// Update FIFO Flow Control Levels in port extension
pPort->LoFlowCtrlThreshold = UartConfig.LoFlowCtrlThreshold;
pPort->HiFlowCtrlThreshold = UartConfig.HiFlowCtrlThreshold;
// Set FIFO Flow Control Levels
pPort->UartConfig.LoFlowCtrlThreshold = pPort->LoFlowCtrlThreshold;
pPort->UartConfig.HiFlowCtrlThreshold = pPort->HiFlowCtrlThreshold;
// Set UART up with special chars.
pPort->UartConfig.XON = pPort->SpecialChars.XonChar;
pPort->UartConfig.XOFF = pPort->SpecialChars.XoffChar;
// Apply any special UART Settings and Flow control thresholds.
pPort->pUartLib->UL_SetConfig_XXXX(pPort->pUart, &pPort->UartConfig, UC_SPECIAL_MODE_MASK | UC_SPECIAL_CHARS_MASK | UC_FC_THRESHOLD_SETTING_MASK);
SerialSetLineControl(pPort);
SerialSetBaud(pPort);
SerialSetupNewHandFlow(pPort, &pPort->HandFlow);
//SerialHandleModemUpdate(pPort, FALSE);
return FALSE;
}
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.
-----------------------------------------------------------------------------*/
{
PPORT_DEVICE_EXTENSION pPort = Context;
SerialDump(SERDIAG3, ("Serial Reset\n"));
pPort->pUartLib->UL_ResetUart_XXXX(pPort->pUart); // Reset UART
// Now we know that nothing could be transmitting at this point
// so we set the HoldingEmpty indicator.
pPort->HoldingEmpty = TRUE;
return FALSE;
}
BOOLEAN SerialResetAndVerifyUart(PDEVICE_OBJECT pDevObj)
{
if(pDevObj->DeviceType == FILE_DEVICE_CONTROLLER)
{
PCARD_DEVICE_EXTENSION pCard = (PCARD_DEVICE_EXTENSION) pDevObj->DeviceExtension;
if(pCard->UartLib.UL_VerifyUart_XXXX(pCard->pFirstUart) == UL_STATUS_SUCCESS) // Verify UART
return TRUE;
else
return FALSE;
}
else if(pDevObj->DeviceType == FILE_DEVICE_SERIAL_PORT)
{
PPORT_DEVICE_EXTENSION pPort = (PPORT_DEVICE_EXTENSION) pDevObj->DeviceExtension;
if(pPort->pUartLib->UL_VerifyUart_XXXX(pPort->pUart) == UL_STATUS_SUCCESS) // Verify UART
return TRUE;
else
return FALSE;
}
return FALSE;
}