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.
1160 lines
41 KiB
1160 lines
41 KiB
/*--------------------------------------------------------------------------
|
|
*
|
|
* Copyright (C) Cyclades Corporation, 1996-2001.
|
|
* All rights reserved.
|
|
*
|
|
* Cyclom-Y Port Driver
|
|
*
|
|
* This file: cyyintr.c
|
|
*
|
|
* Description: This module contains the code related to interrupt
|
|
* handling in the Cyclom-Y Port driver.
|
|
*
|
|
* Notes: This code supports Windows 2000 and Windows XP,
|
|
* x86 and IA64 processors.
|
|
*
|
|
* Complies with Cyclades SW Coding Standard rev 1.3.
|
|
*
|
|
*--------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*-------------------------------------------------------------------------
|
|
*
|
|
* Change History
|
|
*
|
|
*--------------------------------------------------------------------------
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------
|
|
*/
|
|
#include "precomp.h"
|
|
|
|
// FANNY: THIS WAS IN CYINIT.C. IT WILL PROBABLY DESAPPEAR FROM HERE TOO.
|
|
//extern const unsigned long CyyCDOffset[];
|
|
const unsigned long CyyCDOffset[] = { // CD1400 offsets within the board
|
|
0x00000000,0x00000400,0x00000800,0x00000C00,
|
|
0x00000200,0x00000600,0x00000A00,0x00000E00
|
|
};
|
|
|
|
|
|
|
|
//ADDED TO DEBUG_RTPR
|
|
extern PDRIVER_OBJECT CyyDO;
|
|
//END DEBUG_RTPR
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
//#pragma alloc_text(PAGESER,CyyIsr)
|
|
//#pragma alloc_text(PAGESER,CyyPutChar)
|
|
//#pragma alloc_text(PAGESER,CyyProcessLSR)
|
|
//#pragma alloc_text(PAGESER,CyyTxStart)
|
|
//#pragma alloc_text(PAGESER,CyySendXon)
|
|
//#pragma alloc_text(PAGESER,CyySendXoff)
|
|
#endif
|
|
|
|
|
|
BOOLEAN
|
|
CyyIsr(
|
|
IN PKINTERRUPT InterruptObject,
|
|
IN PVOID Context
|
|
)
|
|
/*--------------------------------------------------------------------------
|
|
CyyIsr()
|
|
|
|
Routine Description: This is the interrupt service routine for the
|
|
Cyclom-Y Port driver.
|
|
|
|
Arguments:
|
|
|
|
InterruptObject - Pointer to interrupt object (not used).
|
|
|
|
Context - Pointer to the device extension for this device.
|
|
|
|
Return Value: This function will return TRUE if the serial port is
|
|
the source of this interrupt, FALSE otherwise.
|
|
--------------------------------------------------------------------------*/
|
|
{
|
|
PCYY_DISPATCH Dispatch = Context;
|
|
PCYY_DEVICE_EXTENSION Extension;
|
|
|
|
BOOLEAN ServicedAnInterrupt = FALSE;
|
|
|
|
PUCHAR chip;
|
|
PUCHAR mappedboard = NULL;
|
|
ULONG bus = Dispatch->IsPci;
|
|
unsigned char status, save_xir, save_car, x, rxchar=0;
|
|
ULONG i,channel,chipindex,portindex;
|
|
UCHAR misr;
|
|
|
|
BOOLEAN thisChipInterrupted;
|
|
PCYY_DEVICE_EXTENSION interlockedExtension[CYY_CHANNELS_PER_CHIP];
|
|
|
|
UNREFERENCED_PARAMETER(InterruptObject);
|
|
|
|
//DbgPrint("Isr>\n");
|
|
|
|
// Loop polling all chips in the board
|
|
for(portindex = 0 ; portindex < CYY_MAX_PORTS ;) {
|
|
|
|
if (!(Extension=Dispatch->Extensions[portindex]) ||
|
|
!(chip=Dispatch->Cd1400[portindex])) {
|
|
portindex++;
|
|
continue;
|
|
}
|
|
chipindex = portindex/4;
|
|
mappedboard = Extension->BoardMemory;
|
|
thisChipInterrupted = FALSE;
|
|
interlockedExtension[0] = interlockedExtension[1]
|
|
= interlockedExtension[2] = interlockedExtension[3] = 0;
|
|
|
|
|
|
while ((status = CD1400_READ(chip,bus,SVRR)) != 0x00) {
|
|
ServicedAnInterrupt = TRUE;
|
|
thisChipInterrupted = TRUE;
|
|
|
|
if (status & 0x01) {
|
|
//Reception
|
|
save_xir = CD1400_READ(chip,bus,RIR);
|
|
channel = (ULONG) (save_xir & 0x03);
|
|
save_car = CD1400_READ(chip,bus,CAR);
|
|
CD1400_WRITE(chip,bus,CAR,save_xir);
|
|
Extension = Dispatch->Extensions[channel + CYY_CHANNELS_PER_CHIP*chipindex];
|
|
x = CD1400_READ(chip,bus,RIVR) & 0x07;
|
|
if (Extension) {
|
|
//
|
|
// Apply lock so if close happens concurrently we don't miss the DPC
|
|
// queueing
|
|
//
|
|
if (interlockedExtension[channel] == NULL) {
|
|
interlockedExtension[channel] = Extension;
|
|
InterlockedIncrement(&Extension->DpcCount);
|
|
LOGENTRY(LOG_CNT, 'DpI3', 0, Extension->DpcCount, 0); // Added in bld 2128
|
|
}
|
|
if (x == 0x07) { // exception
|
|
x = CD1400_READ(chip,bus,RDSR); // status
|
|
CyyDbgPrintEx(DPFLTR_WARNING_LEVEL, "exception %x\n",x);
|
|
|
|
if (Extension->DeviceIsOpened &&
|
|
(Extension->PowerState == PowerDeviceD0)) {
|
|
|
|
if (x & CYY_LSR_ERROR){
|
|
BOOLEAN ProcessRxChar;
|
|
|
|
if (!(x & CYY_LSR_OE)) {
|
|
rxchar = CD1400_READ(chip,bus,RDSR); // error data
|
|
}
|
|
|
|
// TODO: SERIAL SAMPLE FOR W2000 HAS ADDED
|
|
// CHECKING FOR EscapeChar TO BREAK TO RX LOOP
|
|
// IN CASE OF ERROR.
|
|
ProcessRxChar = CyyProcessLSR(Extension,x,rxchar);
|
|
|
|
if (ProcessRxChar) {
|
|
x = 1; // 1 character
|
|
i = 0; // prepare for for(;;)
|
|
goto Handle_rxchar;
|
|
}
|
|
} // end error handling
|
|
} // end if DeviceIsOpened..
|
|
|
|
} else { // good reception
|
|
x = CD1400_READ(chip,bus,RDCR);
|
|
if (Extension->DeviceIsOpened &&
|
|
(Extension->PowerState == PowerDeviceD0)) {
|
|
for(i = 0 ; i < x ; i++) { // read from FIFO
|
|
|
|
rxchar = CD1400_READ(chip,bus,RDSR);
|
|
Handle_rxchar:
|
|
Extension->PerfStats.ReceivedCount++;
|
|
Extension->WmiPerfData.ReceivedCount++;
|
|
rxchar &= Extension->ValidDataMask;
|
|
|
|
if (!rxchar && // NULL stripping
|
|
(Extension->HandFlow.FlowReplace &
|
|
SERIAL_NULL_STRIPPING)) {
|
|
continue;
|
|
}
|
|
|
|
if((Extension->HandFlow.FlowReplace &
|
|
SERIAL_AUTO_TRANSMIT) &&
|
|
((rxchar == Extension->SpecialChars.XonChar) ||
|
|
(rxchar == Extension->SpecialChars.XoffChar))) {
|
|
if (rxchar == Extension->SpecialChars.XoffChar) {
|
|
Extension->TXHolding |= CYY_TX_XOFF;
|
|
if ((Extension->HandFlow.FlowReplace &
|
|
SERIAL_RTS_MASK) ==
|
|
SERIAL_TRANSMIT_TOGGLE) {
|
|
|
|
CyyInsertQueueDpc(
|
|
&Extension->StartTimerLowerRTSDpc,
|
|
NULL,
|
|
NULL,
|
|
Extension
|
|
)?Extension->CountOfTryingToLowerRTS++:0;
|
|
}
|
|
} else {
|
|
if (Extension->TXHolding & CYY_TX_XOFF) {
|
|
Extension->TXHolding &= ~CYY_TX_XOFF;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
// Check to see if we should note the receive
|
|
// character or special character event.
|
|
if (Extension->IsrWaitMask) {
|
|
if (Extension->IsrWaitMask & SERIAL_EV_RXCHAR) {
|
|
Extension->HistoryMask |= SERIAL_EV_RXCHAR;
|
|
}
|
|
if ((Extension->IsrWaitMask & SERIAL_EV_RXFLAG) &&
|
|
(Extension->SpecialChars.EventChar == rxchar)) {
|
|
|
|
Extension->HistoryMask |= SERIAL_EV_RXFLAG;
|
|
}
|
|
|
|
if (Extension->IrpMaskLocation && Extension->HistoryMask) {
|
|
*Extension->IrpMaskLocation = Extension->HistoryMask;
|
|
Extension->IrpMaskLocation = NULL;
|
|
Extension->HistoryMask = 0;
|
|
|
|
Extension->CurrentWaitIrp->IoStatus.Information =
|
|
sizeof(ULONG);
|
|
CyyInsertQueueDpc(&Extension->CommWaitDpc,NULL,NULL,Extension);
|
|
}
|
|
}
|
|
CyyPutChar(Extension,rxchar);
|
|
|
|
// If we're doing line status and modem
|
|
// status insertion then we need to insert
|
|
// a zero following the character we just
|
|
// placed into the buffer to mark that this
|
|
// was reception of what we are using to
|
|
// escape.
|
|
|
|
if (Extension->EscapeChar &&
|
|
(Extension->EscapeChar == rxchar)) {
|
|
CyyPutChar(Extension,SERIAL_LSRMST_ESCAPE);
|
|
}
|
|
} // end for
|
|
} else { // device is being closed, discard rx chars
|
|
for(i = 0 ; i < x ; i++) rxchar = CD1400_READ(chip,bus,RDSR);
|
|
} // end if device is opened else closed
|
|
}
|
|
} else {
|
|
// No Extension
|
|
if (x == 0x07) { // exception
|
|
x = CD1400_READ(chip,bus,RDSR); // status
|
|
} else { // good char
|
|
x = CD1400_READ(chip,bus,RDCR); // number of chars
|
|
for(i = 0 ; i < x ; i++) rxchar = CD1400_READ(chip,bus,RDSR);
|
|
}
|
|
}
|
|
CD1400_WRITE(chip,bus,RIR,(save_xir & 0x3f)); // end service
|
|
CD1400_WRITE(chip,bus,CAR,save_car);
|
|
|
|
} // end reception
|
|
|
|
if (status & 0x02) {
|
|
//Transmission
|
|
save_xir = CD1400_READ(chip,bus,TIR);
|
|
channel = (ULONG) (save_xir & 0x03);
|
|
save_car = CD1400_READ(chip,bus,CAR);
|
|
CD1400_WRITE(chip,bus,CAR,save_xir);
|
|
Extension = Dispatch->Extensions[channel + CYY_CHANNELS_PER_CHIP*chipindex];
|
|
if (Extension) {
|
|
//
|
|
// Apply lock so if close happens concurrently we don't miss the DPC
|
|
// queueing
|
|
//
|
|
if (interlockedExtension[channel] == NULL) {
|
|
interlockedExtension[channel] = Extension;
|
|
InterlockedIncrement(&Extension->DpcCount);
|
|
LOGENTRY(LOG_CNT, 'DpI3', 0, Extension->DpcCount, 0); // Added in build 2128
|
|
}
|
|
Extension->HoldingEmpty = TRUE;
|
|
if( Extension->DeviceIsOpened &&
|
|
(Extension->PowerState == PowerDeviceD0)) {
|
|
|
|
if (Extension->BreakCmd != NO_BREAK) {
|
|
|
|
if (Extension->BreakCmd == SEND_BREAK) {
|
|
if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
|
|
SERIAL_TRANSMIT_TOGGLE) {
|
|
CyySetRTS(Extension);
|
|
}
|
|
CD1400_WRITE(chip,bus,TDR,(unsigned char) 0x00); // escape sequence
|
|
CD1400_WRITE(chip,bus,TDR,(unsigned char) 0x81); // Send Break
|
|
Extension->TXHolding |= CYY_TX_BREAK;
|
|
Extension->HoldingEmpty = FALSE;
|
|
Extension->BreakCmd = DISABLE_ETC;
|
|
} else if (Extension->BreakCmd == STOP_BREAK){
|
|
if (Extension->TXHolding & CYY_TX_BREAK) {
|
|
CD1400_WRITE(chip,bus,TDR,(unsigned char) 0x00); // escape sequence
|
|
CD1400_WRITE(chip,bus,TDR,(unsigned char) 0x83); // Stop Break
|
|
Extension->HoldingEmpty = FALSE;
|
|
Extension->TXHolding &= ~CYY_TX_BREAK;
|
|
}
|
|
Extension->BreakCmd = DISABLE_ETC;
|
|
} else if (Extension->BreakCmd == DISABLE_ETC) {
|
|
UCHAR cor2;
|
|
cor2 = CD1400_READ(chip,bus,COR2);
|
|
CD1400_WRITE(chip,bus, COR2,cor2 & ~EMBED_TX_ENABLE); // disable ETC bit
|
|
CyyCDCmd(Extension,CCR_CORCHG_COR2); // COR2 changed
|
|
Extension->BreakCmd = NO_BREAK;
|
|
|
|
if (!Extension->TXHolding &&
|
|
(Extension->TransmitImmediate ||
|
|
Extension->WriteLength) &&
|
|
Extension->HoldingEmpty) {
|
|
|
|
//CyyTxStart(Extension); no need for CyyTxStart from here.
|
|
|
|
} else {
|
|
UCHAR srer = CD1400_READ(chip,bus,SRER);
|
|
CD1400_WRITE(chip,bus,SRER,srer & (~SRER_TXRDY));
|
|
|
|
//
|
|
// The following routine will lower the rts if we
|
|
// are doing transmit toggleing and there is no
|
|
// reason to keep it up.
|
|
//
|
|
|
|
Extension->CountOfTryingToLowerRTS++;
|
|
CyyPerhapsLowerRTS(Extension);
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// This is not a Send Break.
|
|
// Check if there are bytes to be transmitted
|
|
|
|
if (Extension->WriteLength || Extension->TransmitImmediate) {
|
|
|
|
Extension->EmptiedTransmit = TRUE;
|
|
if (Extension->HandFlow.ControlHandShake &
|
|
SERIAL_OUT_HANDSHAKEMASK) {
|
|
CyyHandleModemUpdate(Extension,TRUE);
|
|
}
|
|
|
|
if (Extension->TransmitImmediate&&(!Extension->TXHolding ||
|
|
(Extension->TXHolding == CYY_TX_XOFF) )) {
|
|
|
|
Extension->TransmitImmediate = FALSE;
|
|
|
|
if ((Extension->HandFlow.FlowReplace &
|
|
SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE) {
|
|
|
|
CyySetRTS(Extension);
|
|
Extension->PerfStats.TransmittedCount++;
|
|
Extension->WmiPerfData.TransmittedCount++;
|
|
CD1400_WRITE(chip,bus,TDR,(unsigned char)(Extension->ImmediateChar));
|
|
|
|
CyyInsertQueueDpc(
|
|
&Extension->StartTimerLowerRTSDpc,NULL,NULL,
|
|
Extension)? Extension->CountOfTryingToLowerRTS++:0;
|
|
} else {
|
|
Extension->PerfStats.TransmittedCount++;
|
|
Extension->WmiPerfData.TransmittedCount++;
|
|
CD1400_WRITE(chip,bus,TDR,(unsigned char)(Extension->ImmediateChar));
|
|
}
|
|
|
|
Extension->HoldingEmpty = FALSE;
|
|
|
|
CyyInsertQueueDpc(
|
|
&Extension->CompleteImmediateDpc,
|
|
NULL,
|
|
NULL,
|
|
Extension
|
|
);
|
|
} else if (!Extension->TXHolding) {
|
|
|
|
ULONG amountToWrite;
|
|
|
|
amountToWrite =
|
|
(Extension->TxFifoAmount < Extension->WriteLength)?
|
|
Extension->TxFifoAmount:Extension->WriteLength;
|
|
|
|
if ((Extension->HandFlow.FlowReplace &
|
|
SERIAL_RTS_MASK) ==
|
|
SERIAL_TRANSMIT_TOGGLE) {
|
|
|
|
// We have to raise if we're sending
|
|
// this character.
|
|
|
|
CyySetRTS(Extension);
|
|
|
|
for(i = 0 ; i < amountToWrite ; i++) { // write to FIFO
|
|
CD1400_WRITE(chip,bus,TDR,((unsigned char *)
|
|
(Extension->WriteCurrentChar))[i]);
|
|
}
|
|
Extension->PerfStats.TransmittedCount += amountToWrite;
|
|
Extension->WmiPerfData.TransmittedCount += amountToWrite;
|
|
|
|
CyyInsertQueueDpc(
|
|
&Extension->StartTimerLowerRTSDpc,
|
|
NULL,
|
|
NULL,
|
|
Extension
|
|
)?Extension->CountOfTryingToLowerRTS++:0;
|
|
|
|
} else {
|
|
|
|
for(i = 0 ; i < amountToWrite ; i++) { // write to FIFO
|
|
CD1400_WRITE(chip,bus,TDR,((unsigned char *)
|
|
(Extension->WriteCurrentChar))[i]);
|
|
}
|
|
Extension->PerfStats.TransmittedCount += amountToWrite;
|
|
Extension->WmiPerfData.TransmittedCount += amountToWrite;
|
|
}
|
|
|
|
Extension->HoldingEmpty = FALSE;
|
|
Extension->WriteCurrentChar += amountToWrite;
|
|
Extension->WriteLength -= amountToWrite;
|
|
|
|
if (!Extension->WriteLength) {
|
|
|
|
PIO_STACK_LOCATION IrpSp;
|
|
//
|
|
// No More characters left. This
|
|
// write is complete. Take care
|
|
// when updating the information field,
|
|
// we could have an xoff counter masquerading
|
|
// as a write irp.
|
|
//
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(
|
|
Extension->CurrentWriteIrp
|
|
);
|
|
|
|
Extension->CurrentWriteIrp->IoStatus.Information =
|
|
(IrpSp->MajorFunction == IRP_MJ_WRITE)?
|
|
(IrpSp->Parameters.Write.Length):
|
|
(1);
|
|
|
|
CyyInsertQueueDpc(
|
|
&Extension->CompleteWriteDpc,
|
|
NULL,
|
|
NULL,
|
|
Extension
|
|
);
|
|
} // end write complete
|
|
} // end of if(!TXHolding)
|
|
|
|
} else { // nothing to be transmitted - disable interrupts.
|
|
UCHAR srer;
|
|
Extension->EmptiedTransmit = TRUE;
|
|
srer = CD1400_READ(chip,bus,SRER);
|
|
CD1400_WRITE(chip,bus,SRER,srer & (~SRER_TXRDY));
|
|
}
|
|
|
|
} // end of if(break)
|
|
|
|
} else { // Device is closed. Disable interrupts
|
|
UCHAR srer = CD1400_READ(chip,bus,SRER);
|
|
CD1400_WRITE(chip,bus,SRER,srer & (~SRER_TXRDY));
|
|
Extension->EmptiedTransmit = TRUE;
|
|
}
|
|
} else {
|
|
// Device was not created, no extension attached.
|
|
UCHAR srer = CD1400_READ(chip,bus,SRER);
|
|
CD1400_WRITE(chip,bus,SRER,srer & (~SRER_TXRDY));
|
|
} // end if Extension
|
|
CD1400_WRITE(chip,bus,TIR,(save_xir & 0x3f)); // end service
|
|
CD1400_WRITE(chip,bus,CAR,save_car);
|
|
|
|
} // end transmission
|
|
|
|
if (status & 0x04) {
|
|
//Modem
|
|
save_xir = CD1400_READ(chip,bus,MIR);
|
|
channel = (ULONG) (save_xir & 0x03);
|
|
save_car = CD1400_READ(chip,bus,CAR);
|
|
CD1400_WRITE(chip,bus,CAR,save_xir);
|
|
|
|
//CyyDump(CYYDIAG5,("modem\n"));
|
|
|
|
Extension = Dispatch->Extensions[channel + CYY_CHANNELS_PER_CHIP*chipindex];
|
|
if (Extension) {
|
|
//
|
|
// Apply lock so if close happens concurrently we don't miss the DPC
|
|
// queueing
|
|
//
|
|
if (interlockedExtension[channel] == NULL) {
|
|
interlockedExtension[channel] = Extension;
|
|
InterlockedIncrement(&Extension->DpcCount);
|
|
LOGENTRY(LOG_CNT, 'DpI3', 0, Extension->DpcCount, 0); // Added in build 2128
|
|
}
|
|
if (Extension->DeviceIsOpened &&
|
|
(Extension->PowerState == PowerDeviceD0)) {
|
|
misr = CD1400_READ(chip,bus,MISR);
|
|
CyyHandleModemUpdateForModem(Extension,FALSE,misr);
|
|
}
|
|
}
|
|
CD1400_WRITE(chip,bus,MIR,(save_xir & 0x3f)); // end service
|
|
CD1400_WRITE(chip,bus,CAR,save_car);
|
|
|
|
} // end modem
|
|
} // end READ SVRR
|
|
if (thisChipInterrupted) {
|
|
for (channel=0; channel<CYY_CHANNELS_PER_CHIP; channel++) {
|
|
if (Extension = interlockedExtension[channel]) {
|
|
LONG pendingCnt;
|
|
|
|
//
|
|
// Increment once more. This is just a quick test to see if we
|
|
// have a chance of causing the event to fire... we don't want
|
|
// to run a DPC on every ISR if we don't have to....
|
|
//
|
|
|
|
retryDPCFiring:;
|
|
|
|
InterlockedIncrement(&Extension->DpcCount);
|
|
LOGENTRY(LOG_CNT, 'DpI4', 0, Extension->DpcCount, 0); // Added in build 2128
|
|
|
|
//
|
|
// Decrement and see if the lock above looks like the only one left.
|
|
//
|
|
|
|
pendingCnt = InterlockedDecrement(&Extension->DpcCount);
|
|
// LOGENTRY(LOG_CNT, 'DpD5', 0, Extension->DpcCount, 0); // Added in build 2128
|
|
|
|
if (pendingCnt == 1) {
|
|
KeInsertQueueDpc(&Extension->IsrUnlockPagesDpc, NULL, NULL);
|
|
} else {
|
|
if (InterlockedDecrement(&Extension->DpcCount) == 0) {
|
|
|
|
// LOGENTRY(LOG_CNT, 'DpD6', &Extension->IsrUnlockPagesDpc, // Added in bld 2128
|
|
// Extension->DpcCount, 0);
|
|
|
|
//
|
|
// We missed it. Retry...
|
|
//
|
|
|
|
InterlockedIncrement(&Extension->DpcCount);
|
|
goto retryDPCFiring;
|
|
}
|
|
}
|
|
|
|
} // if (Extension = interlockedExtension[])
|
|
} // for (;channel<CYY_CHANNELS_PER_CHIP;)
|
|
|
|
portindex = (chipindex+1)*4;
|
|
continue;
|
|
|
|
} // if (thisChipInterrupted)
|
|
|
|
portindex++;
|
|
|
|
} // for(;portindex<CYY_MAX_PORTS;);
|
|
|
|
if (mappedboard) {
|
|
CYY_CLEAR_INTERRUPT(mappedboard,Dispatch->IsPci);
|
|
}
|
|
|
|
//DbgPrint("<Isr\n");
|
|
|
|
return ServicedAnInterrupt;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
CyyPutChar(
|
|
IN PCYY_DEVICE_EXTENSION Extension,
|
|
IN UCHAR CharToPut
|
|
)
|
|
/*--------------------------------------------------------------------------
|
|
CyyPutChar()
|
|
|
|
Routine Description: This routine, which only runs at device level,
|
|
takes care of placing a character into the typeahead (receive) buffer.
|
|
|
|
Arguments:
|
|
|
|
Extension - The serial device extension.
|
|
|
|
Return Value: None.
|
|
--------------------------------------------------------------------------*/
|
|
{
|
|
|
|
CYY_LOCKED_PAGED_CODE();
|
|
|
|
// If we have dsr sensitivity enabled then
|
|
// we need to check the modem status register
|
|
// to see if it has changed.
|
|
|
|
if (Extension->HandFlow.ControlHandShake & SERIAL_DSR_SENSITIVITY) {
|
|
CyyHandleModemUpdate(Extension,FALSE);
|
|
|
|
if (Extension->RXHolding & CYY_RX_DSR) {
|
|
// We simply act as if we haven't seen the character if
|
|
// dsr line is low.
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If the xoff counter is non-zero then decrement it.
|
|
// If the counter then goes to zero, complete that irp.
|
|
|
|
if (Extension->CountSinceXoff) {
|
|
Extension->CountSinceXoff--;
|
|
if (!Extension->CountSinceXoff) {
|
|
Extension->CurrentXoffIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
Extension->CurrentXoffIrp->IoStatus.Information = 0;
|
|
CyyInsertQueueDpc(&Extension->XoffCountCompleteDpc,NULL,NULL,Extension);
|
|
}
|
|
}
|
|
|
|
// Check to see if we are copying into the
|
|
// users buffer or into the interrupt buffer.
|
|
//
|
|
// If we are copying into the user buffer
|
|
// then we know there is always room for one more.
|
|
// (We know this because if there wasn't room
|
|
// then that read would have completed and we
|
|
// would be using the interrupt buffer.)
|
|
//
|
|
// If we are copying into the interrupt buffer
|
|
// then we will need to check if we have enough
|
|
// room.
|
|
|
|
if (Extension->ReadBufferBase != Extension->InterruptReadBuffer) {
|
|
|
|
// Increment the following value so
|
|
// that the interval timer (if one exists
|
|
// for this read) can know that a character
|
|
// has been read.
|
|
|
|
Extension->ReadByIsr++;
|
|
|
|
// We are in the user buffer. Place the character into the buffer.
|
|
// See if the read is complete.
|
|
|
|
*Extension->CurrentCharSlot = CharToPut;
|
|
|
|
if (Extension->CurrentCharSlot == Extension->LastCharSlot) {
|
|
|
|
// We've filled up the users buffer.
|
|
// Switch back to the interrupt buffer
|
|
// and send off a DPC to Complete the read.
|
|
//
|
|
// It is inherent that when we were using
|
|
// a user buffer that the interrupt buffer
|
|
// was empty.
|
|
|
|
Extension->ReadBufferBase = Extension->InterruptReadBuffer;
|
|
Extension->CurrentCharSlot = Extension->InterruptReadBuffer;
|
|
Extension->FirstReadableChar = Extension->InterruptReadBuffer;
|
|
Extension->LastCharSlot = Extension->InterruptReadBuffer +
|
|
(Extension->BufferSize - 1);
|
|
Extension->CharsInInterruptBuffer = 0;
|
|
|
|
Extension->CurrentReadIrp->IoStatus.Information =
|
|
IoGetCurrentIrpStackLocation(
|
|
Extension->CurrentReadIrp
|
|
)->Parameters.Read.Length;
|
|
|
|
CyyInsertQueueDpc(&Extension->CompleteReadDpc,NULL,NULL,Extension);
|
|
} else {
|
|
// Not done with the users read.
|
|
Extension->CurrentCharSlot++;
|
|
}
|
|
} else {
|
|
// We need to see if we reached our flow
|
|
// control threshold. If we have then
|
|
// we turn on whatever flow control the
|
|
// owner has specified. If no flow
|
|
// control was specified, well..., we keep
|
|
// trying to receive characters and hope that
|
|
// we have enough room. Note that no matter
|
|
// what flow control protocol we are using, it
|
|
// will not prevent us from reading whatever
|
|
// characters are available.
|
|
|
|
if ((Extension->HandFlow.ControlHandShake
|
|
& SERIAL_DTR_MASK) ==
|
|
SERIAL_DTR_HANDSHAKE) {
|
|
|
|
// If we are already doing a
|
|
// dtr hold then we don't have
|
|
// to do anything else.
|
|
|
|
if (!(Extension->RXHolding &
|
|
CYY_RX_DTR)) {
|
|
|
|
if ((Extension->BufferSize -
|
|
Extension->HandFlow.XoffLimit)
|
|
<= (Extension->CharsInInterruptBuffer+1)) {
|
|
|
|
Extension->RXHolding |= CYY_RX_DTR;
|
|
|
|
CyyClrDTR(Extension);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((Extension->HandFlow.FlowReplace
|
|
& SERIAL_RTS_MASK) ==
|
|
SERIAL_RTS_HANDSHAKE) {
|
|
|
|
// If we are already doing a
|
|
// rts hold then we don't have
|
|
// to do anything else.
|
|
|
|
if (!(Extension->RXHolding & CYY_RX_RTS)) {
|
|
|
|
if ((Extension->BufferSize -
|
|
Extension->HandFlow.XoffLimit)
|
|
<= (Extension->CharsInInterruptBuffer+1)) {
|
|
|
|
Extension->RXHolding |= CYY_RX_RTS;
|
|
|
|
CyyClrRTS(Extension);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Extension->HandFlow.FlowReplace & SERIAL_AUTO_RECEIVE) {
|
|
// If we are already doing a
|
|
// xoff hold then we don't have
|
|
// to do anything else.
|
|
|
|
if (!(Extension->RXHolding & CYY_RX_XOFF)) {
|
|
|
|
if ((Extension->BufferSize -
|
|
Extension->HandFlow.XoffLimit)
|
|
<= (Extension->CharsInInterruptBuffer+1)) {
|
|
|
|
Extension->RXHolding |= CYY_RX_XOFF;
|
|
|
|
// If necessary cause an
|
|
// off to be sent.
|
|
|
|
CyyProdXonXoff(Extension,FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Extension->CharsInInterruptBuffer < Extension->BufferSize) {
|
|
|
|
*Extension->CurrentCharSlot = CharToPut;
|
|
Extension->CharsInInterruptBuffer++;
|
|
|
|
// If we've become 80% full on this character
|
|
// and this is an interesting event, note it.
|
|
|
|
if (Extension->CharsInInterruptBuffer == Extension->BufferSizePt8) {
|
|
|
|
if (Extension->IsrWaitMask & SERIAL_EV_RX80FULL) {
|
|
|
|
Extension->HistoryMask |= SERIAL_EV_RX80FULL;
|
|
|
|
if (Extension->IrpMaskLocation) {
|
|
|
|
*Extension->IrpMaskLocation = Extension->HistoryMask;
|
|
Extension->IrpMaskLocation = NULL;
|
|
Extension->HistoryMask = 0;
|
|
|
|
Extension->CurrentWaitIrp->IoStatus.Information = sizeof(ULONG);
|
|
CyyInsertQueueDpc(&Extension->CommWaitDpc,NULL,NULL,Extension);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Point to the next available space
|
|
// for a received character. Make sure
|
|
// that we wrap around to the beginning
|
|
// of the buffer if this last character
|
|
// received was placed at the last slot
|
|
// in the buffer.
|
|
|
|
if (Extension->CurrentCharSlot == Extension->LastCharSlot) {
|
|
Extension->CurrentCharSlot = Extension->InterruptReadBuffer;
|
|
} else {
|
|
Extension->CurrentCharSlot++;
|
|
}
|
|
} else {
|
|
// We have a new character but no room for it.
|
|
|
|
Extension->PerfStats.BufferOverrunErrorCount++;
|
|
Extension->WmiPerfData.BufferOverrunErrorCount++;
|
|
Extension->ErrorWord |= SERIAL_ERROR_QUEUEOVERRUN;
|
|
|
|
if (Extension->HandFlow.FlowReplace & SERIAL_ERROR_CHAR) {
|
|
|
|
// Place the error character into the last
|
|
// valid place for a character. Be careful!,
|
|
// that place might not be the previous location!
|
|
|
|
if (Extension->CurrentCharSlot == Extension->InterruptReadBuffer) {
|
|
*(Extension->InterruptReadBuffer+
|
|
(Extension->BufferSize-1)) =
|
|
Extension->SpecialChars.ErrorChar;
|
|
} else {
|
|
*(Extension->CurrentCharSlot-1) =
|
|
Extension->SpecialChars.ErrorChar;
|
|
}
|
|
}
|
|
// If the application has requested it, abort all reads
|
|
// and writes on an error.
|
|
|
|
if (Extension->HandFlow.ControlHandShake & SERIAL_ERROR_ABORT) {
|
|
CyyInsertQueueDpc(&Extension->CommErrorDpc,NULL,NULL,Extension);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
CyyProcessLSR(
|
|
IN PCYY_DEVICE_EXTENSION Extension,
|
|
IN UCHAR Rdsr,
|
|
IN UCHAR RxChar
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine, which only runs at device level, reads the
|
|
ISR and totally processes everything that might have
|
|
changed.
|
|
|
|
Arguments:
|
|
|
|
Extension - The serial device extension.
|
|
|
|
Return Value:
|
|
|
|
TRUE if RxChar still needs to be processed.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
BOOLEAN StillProcessRxChar=TRUE;
|
|
UCHAR LineStatus=0;
|
|
|
|
CYY_LOCKED_PAGED_CODE();
|
|
|
|
if (Rdsr & CYY_LSR_OE)
|
|
LineStatus |= SERIAL_LSR_OE;
|
|
if (Rdsr & CYY_LSR_FE)
|
|
LineStatus |= SERIAL_LSR_FE;
|
|
if (Rdsr & CYY_LSR_PE)
|
|
LineStatus |= SERIAL_LSR_PE;
|
|
if (Rdsr & CYY_LSR_BI)
|
|
LineStatus |= SERIAL_LSR_BI;
|
|
|
|
|
|
if (Extension->EscapeChar) {
|
|
|
|
CyyPutChar(
|
|
Extension,
|
|
Extension->EscapeChar
|
|
);
|
|
|
|
CyyPutChar(
|
|
Extension,
|
|
(UCHAR)((LineStatus & SERIAL_LSR_OE)?
|
|
(SERIAL_LSRMST_LSR_NODATA):(SERIAL_LSRMST_LSR_DATA))
|
|
);
|
|
|
|
CyyPutChar(
|
|
Extension,
|
|
LineStatus
|
|
);
|
|
|
|
if (!(LineStatus & SERIAL_LSR_OE)) {
|
|
Extension->PerfStats.ReceivedCount++;
|
|
Extension->WmiPerfData.ReceivedCount++;
|
|
CyyPutChar(
|
|
Extension,
|
|
RxChar
|
|
);
|
|
StillProcessRxChar = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if (LineStatus & SERIAL_LSR_OE) {
|
|
|
|
Extension->PerfStats.SerialOverrunErrorCount++;
|
|
Extension->WmiPerfData.SerialOverrunErrorCount++;
|
|
Extension->ErrorWord |= SERIAL_ERROR_OVERRUN;
|
|
|
|
if (Extension->HandFlow.FlowReplace &
|
|
SERIAL_ERROR_CHAR) {
|
|
|
|
CyyPutChar(
|
|
Extension,
|
|
Extension->SpecialChars.ErrorChar
|
|
);
|
|
}
|
|
StillProcessRxChar = FALSE;
|
|
}
|
|
|
|
if (LineStatus & SERIAL_LSR_BI) {
|
|
|
|
Extension->ErrorWord |= SERIAL_ERROR_BREAK;
|
|
|
|
if (Extension->HandFlow.FlowReplace &
|
|
SERIAL_BREAK_CHAR) {
|
|
|
|
CyyPutChar(
|
|
Extension,
|
|
Extension->SpecialChars.BreakChar
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Framing errors only count if they
|
|
// occur exclusive of a break being
|
|
// received.
|
|
//
|
|
|
|
if (LineStatus & SERIAL_LSR_PE) {
|
|
Extension->PerfStats.ParityErrorCount++;
|
|
Extension->WmiPerfData.ParityErrorCount++;
|
|
Extension->ErrorWord |= SERIAL_ERROR_PARITY;
|
|
if (Extension->HandFlow.FlowReplace &
|
|
SERIAL_ERROR_CHAR) {
|
|
|
|
CyyPutChar(
|
|
Extension,
|
|
Extension->SpecialChars.ErrorChar
|
|
);
|
|
StillProcessRxChar = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
if (LineStatus & SERIAL_LSR_FE) {
|
|
|
|
Extension->PerfStats.FrameErrorCount++;
|
|
Extension->WmiPerfData.FrameErrorCount++;
|
|
Extension->ErrorWord |= SERIAL_ERROR_FRAMING;
|
|
|
|
if (Extension->HandFlow.FlowReplace &
|
|
SERIAL_ERROR_CHAR) {
|
|
|
|
CyyPutChar(
|
|
Extension,
|
|
Extension->SpecialChars.ErrorChar
|
|
);
|
|
StillProcessRxChar = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If the application has requested it,
|
|
// abort all the reads and writes
|
|
// on an error.
|
|
//
|
|
|
|
if (Extension->HandFlow.ControlHandShake &
|
|
SERIAL_ERROR_ABORT) {
|
|
|
|
CyyInsertQueueDpc(
|
|
&Extension->CommErrorDpc,
|
|
NULL,
|
|
NULL,
|
|
Extension
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Check to see if we have a wait
|
|
// pending on the comm error events. If we
|
|
// do then we schedule a dpc to satisfy
|
|
// that wait.
|
|
//
|
|
|
|
if (Extension->IsrWaitMask) {
|
|
|
|
if ((Extension->IsrWaitMask & SERIAL_EV_ERR) &&
|
|
(LineStatus & (SERIAL_LSR_OE |
|
|
SERIAL_LSR_PE |
|
|
SERIAL_LSR_FE))) {
|
|
Extension->HistoryMask |= SERIAL_EV_ERR;
|
|
|
|
}
|
|
|
|
if ((Extension->IsrWaitMask & SERIAL_EV_BREAK) &&
|
|
(LineStatus & SERIAL_LSR_BI)) {
|
|
|
|
Extension->HistoryMask |= SERIAL_EV_BREAK;
|
|
|
|
}
|
|
|
|
if (Extension->IrpMaskLocation &&
|
|
Extension->HistoryMask) {
|
|
|
|
*Extension->IrpMaskLocation =
|
|
Extension->HistoryMask;
|
|
Extension->IrpMaskLocation = NULL;
|
|
Extension->HistoryMask = 0;
|
|
|
|
Extension->CurrentWaitIrp->IoStatus.Information =
|
|
sizeof(ULONG);
|
|
CyyInsertQueueDpc(
|
|
&Extension->CommWaitDpc,
|
|
NULL,
|
|
NULL,
|
|
Extension
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return StillProcessRxChar;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
CyyTxStart(
|
|
IN PVOID Context
|
|
)
|
|
/*--------------------------------------------------------------------------
|
|
CyyTxStart()
|
|
|
|
Description: Enable Tx interrupt.
|
|
|
|
Parameters:
|
|
|
|
Exetnsion: Pointer to device extension.
|
|
|
|
Return Value: None
|
|
--------------------------------------------------------------------------*/
|
|
{
|
|
PCYY_DEVICE_EXTENSION Extension = Context;
|
|
PUCHAR chip = Extension->Cd1400;
|
|
ULONG bus = Extension->IsPci;
|
|
UCHAR srer;
|
|
|
|
if (Extension->PowerState == PowerDeviceD0) {
|
|
CD1400_WRITE(chip,bus,CAR,Extension->CdChannel & 0x03);
|
|
srer = CD1400_READ (chip,bus,SRER);
|
|
CD1400_WRITE(chip,bus,SRER,srer | SRER_TXRDY);
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CyySendXon(
|
|
IN PVOID Context
|
|
)
|
|
/*--------------------------------------------------------------------------
|
|
CyySendXon()
|
|
|
|
Description: Send a Xon.
|
|
|
|
Parameters:
|
|
|
|
Exetension: Pointer to device extension.
|
|
|
|
Return Value: Always FALSE.
|
|
--------------------------------------------------------------------------*/
|
|
{
|
|
PCYY_DEVICE_EXTENSION Extension = Context;
|
|
PUCHAR chip = Extension->Cd1400;
|
|
ULONG bus = Extension->IsPci;
|
|
|
|
if(!(Extension->TXHolding & ~CYY_TX_XOFF)) {
|
|
if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
|
|
SERIAL_TRANSMIT_TOGGLE) {
|
|
|
|
CyySetRTS(Extension);
|
|
|
|
Extension->PerfStats.TransmittedCount++;
|
|
Extension->WmiPerfData.TransmittedCount++;
|
|
CD1400_WRITE(chip,bus,CAR,Extension->CdChannel & 0x03);
|
|
CyyCDCmd(Extension,CCR_SENDSC_SCHR1);
|
|
|
|
CyyInsertQueueDpc(&Extension->StartTimerLowerRTSDpc,NULL,
|
|
NULL,Extension)?Extension->CountOfTryingToLowerRTS++:0;
|
|
} else {
|
|
Extension->PerfStats.TransmittedCount++;
|
|
Extension->WmiPerfData.TransmittedCount++;
|
|
CD1400_WRITE(chip,bus,CAR,Extension->CdChannel & 0x03);
|
|
CyyCDCmd(Extension,CCR_SENDSC_SCHR1);
|
|
}
|
|
|
|
// If we send an xon, by definition we can't be holding by Xoff.
|
|
|
|
Extension->TXHolding &= ~CYY_TX_XOFF;
|
|
Extension->RXHolding &= ~CYY_RX_XOFF;
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
CyySendXoff(
|
|
IN PVOID Context
|
|
)
|
|
/*--------------------------------------------------------------------------
|
|
CyySendXoff()
|
|
|
|
Description: Send a Xoff.
|
|
|
|
Parameters:
|
|
|
|
Extension: Pointer to device extension.
|
|
|
|
Return Value: Always FALSE.
|
|
--------------------------------------------------------------------------*/
|
|
{
|
|
PCYY_DEVICE_EXTENSION Extension = Context;
|
|
PUCHAR chip = Extension->Cd1400;
|
|
ULONG bus = Extension->IsPci;
|
|
|
|
if(!Extension->TXHolding) {
|
|
if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
|
|
SERIAL_TRANSMIT_TOGGLE) {
|
|
|
|
CyySetRTS(Extension);
|
|
|
|
Extension->PerfStats.TransmittedCount++;
|
|
Extension->WmiPerfData.TransmittedCount++;
|
|
CD1400_WRITE(chip,bus,CAR,Extension->CdChannel & 0x03);
|
|
CyyCDCmd(Extension,CCR_SENDSC_SCHR2);
|
|
|
|
CyyInsertQueueDpc(&Extension->StartTimerLowerRTSDpc,NULL,
|
|
NULL,Extension)?Extension->CountOfTryingToLowerRTS++:0;
|
|
} else {
|
|
Extension->PerfStats.TransmittedCount++;
|
|
Extension->WmiPerfData.TransmittedCount++;
|
|
CD1400_WRITE(chip,bus,CAR,Extension->CdChannel & 0x03);
|
|
CyyCDCmd(Extension,CCR_SENDSC_SCHR2);
|
|
}
|
|
|
|
// no xoff is sent if the transmission is already held up.
|
|
// If xoff continue mode is set, we don't actually stop sending
|
|
|
|
if (!(Extension->HandFlow.FlowReplace & SERIAL_XOFF_CONTINUE)) {
|
|
Extension->TXHolding |= CYY_TX_XOFF;
|
|
|
|
if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
|
|
SERIAL_TRANSMIT_TOGGLE) {
|
|
|
|
CyyInsertQueueDpc(&Extension->StartTimerLowerRTSDpc,NULL,
|
|
NULL,Extension)?Extension->CountOfTryingToLowerRTS++:0;
|
|
}
|
|
}
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|