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.
 
 
 
 
 
 

1445 lines
48 KiB

/*-------------------------------------------------------------------
| isr.c - Interrupt(or Timer) Service Routine, RocketPort & VS.
1-21-99 fix broken EV_TXEMPTY events due to 1-18-99 spinlock changes. kpb
1-18-99 implement better write spinlocking to avoid blue-screen
with wait on tx option.
1-18-99 implement wait on tx option for VS.
9-24-98 add RING emulation.
Copyright 1993-98 Comtrol Corporation. All rights reserved.
|--------------------------------------------------------------------*/
#include "precomp.h"
// #define LOAD_TESTING
// #define SOFT_LOOP_BACK
// local prototypes
static BOOLEAN SerialPoll(void);
static void ServiceRocket(PSERIAL_DEVICE_EXTENSION extension);
static void ServiceVS(PSERIAL_DEVICE_EXTENSION extension);
static void RocketRead(PSERIAL_DEVICE_EXTENSION extension);
static void VSRead(PSERIAL_DEVICE_EXTENSION extension);
static void RocketRefresh(void);
static void ring_check(PSERIAL_DEVICE_EXTENSION extension,
BYTE *data,
int len);
#ifdef S_VS
#define USE_MEMCHR_SCAN
#ifdef USE_MEMCHR_SCAN
#define search_match(buf, cnt, chr) \
(memchr(buf, chr, cnt) != NULL)
#else
static int search_match(BYTE *buf, int count, BYTE eventchar);
#endif
#endif
#ifdef S_RK
/*---------------------------------------------------------------------------
Function : SerialISR
Purpose: This is the Interrupt Service Routine for RocketPort.
Call: SerialISR(InterruptObject,Context)
PDEVICE_OBJECT DeviceObject: Pointer to the Device Object
Context: Pointer to the extensionst Packet
Return: STATUS_SUCCESS: always
Comments: This function is the device driver ISR entry point.
The interrupt from the first active board is used to poll the
ports for any work to be done.
|---------------------------------------------------------------------------*/
BOOLEAN SerialISR(
IN PKINTERRUPT InterruptObject,
IN PVOID Context)
{
CONTROLLER_T *CtlP;
unsigned char CtlInt; /* controller interrupt status */
//static int trace_cnt = 0;
// ++trace_cnt;
// if (trace_cnt < 5)
// {
// {
// char str[20];
// Sprintf(str, "isr trace:%d\n", trace_cnt);
// q_put(&Driver.DebugQ, (BYTE *) str, strlen(str));
// // showed IRQL:2, ms doc says should be at DISPATCH_LEVEL
// }
// }
CtlP = Driver.irq_ext->CtlP; // &sController[0];
if (CtlP->BusType == Isa)
{
CtlInt = sGetControllerIntStatus(CtlP);
}
else if (CtlP->BusType == PCIBus)
{
CtlInt = sPCIGetControllerIntStatus(CtlP);
if ((CtlInt & PCI_PER_INT_STATUS) ==0)
return FALSE; // Not our Interupt PCI devices share interrupts
}
SerialPoll();
if (CtlP->BusType == Isa)
{
sControllerEOI(CtlP);
}
else if (CtlP->BusType == PCIBus)
sPCIControllerEOI(CtlP);
return TRUE;
}
#endif
/*---------------------------------------------------------------------------
Function : TimerDpc
Purpose: This is the Timer routine, alternative to interrupts for polling.
Call: SerialTimerDpc(InterruptObject,Context)
PDEVICE_OBJECT DeviceObject: Pointer to the Device Object
Context: Pointer to the extensionst Packet
Return: STATUS_SUCCESS: always
|---------------------------------------------------------------------------*/
VOID TimerDpc(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemContext1,
IN PVOID SystemContext2)
{
KIRQL OldIrql;
#ifdef USE_SYNC_LOCKS
KeAcquireSpinLock(&Driver.TimerLock, &OldIrql);
#endif
SerialPoll(); // poll the rocketport for work to do
#ifdef USE_SYNC_LOCKS
KeReleaseSpinLock(&Driver.TimerLock, OldIrql );
#endif
// setup the Timer again.
KeSetTimer(&Driver.PollTimer,
Driver.PollIntervalTime,
&Driver.TimerDpc);
return;
}
/*---------------------------------------------------------------------------
Function : SerialPoll
Purpose: This is called from ISR or Timer routine. Common routine to
periodically service the rocketport card.
Return: FALSE if not our interrupt(sharing allowed so causes the
OS to pass on to next handler(if present).
TRUE if it was our interrupt. Return value does not matter
if running off from Kernal TIMER.
|---------------------------------------------------------------------------*/
static BOOLEAN SerialPoll(void)
{
PSERIAL_DEVICE_EXTENSION extension;
PSERIAL_DEVICE_EXTENSION board_ext;
// periodically we will re-calculate the timer base of NT.
// we do it periodically, so that we don't waste a bunch of
// CPU time, we only do it every 128 ticks..
// We use this information so that our timers can have a
// valid tick-base. The timers could do these system calls
// everytime, but this would get expense CPU wise, so we
// calculate the basic tick rate in milliseconds so that
// timer routines can do something simple like
// ticktime += msTickBase
++Driver.TickBaseCnt;
if (Driver.TickBaseCnt > 128)
{
ULONG msBase;
Driver.TickBaseCnt = 0;
KeQuerySystemTime(&Driver.IsrSysTime);
msBase = (ULONG)(Driver.IsrSysTime.QuadPart - Driver.LastIsrSysTime.QuadPart);
// msBase now has 100ns ticks since last time we did this(128 ticks ago)
msBase = (msBase / 128);
// now msBase has the average 100ns time for 1 of our ISR ticks.
// covert this to 100us units
msBase = (msBase / 1000);
if (msBase < 10) // make at least 1ms
msBase = 10;
if (msBase > 200) // ensure it is less than 20ms
msBase = 200;
// store it for timer use
Driver.Tick100usBase = msBase;
Driver.LastIsrSysTime.QuadPart = Driver.IsrSysTime.QuadPart;
}
++Driver.PollCnt;
if (Driver.Stop_Poll) // flag to stop poll access
return TRUE; // signal it was our interrupt
if ((Driver.PollCnt & 0x7f) == 0) // every 128 ticks(about once a sec)
{
RocketRefresh(); // general background activity
}
#ifdef LOAD_TESTING
if (Driver.load_testing != 0)
{
unsigned int i,j;
for (j=1000; j<Driver.load_testing; j++)
{
for (i=0; i<10000; i++)
{
//ustat = sGetModemStatus(extension->ChP);
ustat = i+1;
}
}
}
#endif
// main poll service loop, service each board...
board_ext = Driver.board_ext;
while (board_ext != NULL)
{
if ((!board_ext->FdoStarted) || (!board_ext->config->HardwareStarted))
{
board_ext = board_ext->board_ext; // next in chain
continue; // Check next port on this board
}
#ifdef S_VS
if (board_ext->pm->state == ST_ACTIVE)
{
port_poll(board_ext->pm); // poll x times per second
hdlc_poll(board_ext->hd);
}
else
{
port_state_handler(board_ext->pm);
}
#endif
// main poll service loop, service each board...
extension = board_ext->port_ext;
while (extension != NULL)
{
// If device not open, don't do anything
if ( !extension->DeviceIsOpen )
{
extension = extension->port_ext; // next in chain
continue; // Check next port on this board
}
#ifdef S_RK
ServiceRocket(extension);
#else
ServiceVS(extension);
#endif
extension = extension->port_ext; // next in chain
} // while port extension
board_ext = board_ext->board_ext; // next in chain
} // while board extension
return TRUE; // signal it was our interrupt
}
#ifdef S_VS
/*---------------------------------------------------------------------------
ServiceVS - Service the VS virtual hardware(queues, nic handling..)
|---------------------------------------------------------------------------*/
static void ServiceVS(PSERIAL_DEVICE_EXTENSION extension)
{
SerPort *sp;
ULONG wCount;
int wrote_some_data;
sp = extension->Port;
#ifdef SOFT_LOOP_BACK
if (sp->mcr_value & MCR_LOOP_SET_ON)
{
int room, out_cnt, wrap_cnt;
Queue *qin, *qout;
//--------- Do a simple loopback emulation
if (!q_empty(&sp->QOut)) // if output queue has data
{
qin = &sp->QIn;
qout = &sp->QOut;
room = q_room(qin); // chk if room to dump it in
out_cnt = q_count(qout);
if (out_cnt > room)
out_cnt = room;
if (out_cnt > (int)(extension->BaudRate / 1000)) // assume 10ms tick
{
out_cnt = (int)(extension->BaudRate / 1000);
}
if (out_cnt != 0)
{
if (q_room_put_till_wrap(qin) < out_cnt) // need a two part move
{
wrap_cnt = q_room_put_till_wrap(qin);
// read in the data to the buffer, first block
q_get(qout, &qin->QBase[qin->QPut], wrap_cnt);
// read in the data to the buffer, second block
q_get(qout, qin->QBase, out_cnt - wrap_cnt);
}
else // single move will do, no wrap
{
// read in the data to the buffer, 1 block
q_get(qout, &qin->QBase[qin->QPut], out_cnt);
}
q_putted(qin, out_cnt); // update queue indexes
} // room to put it
} // output q not empty
}
#endif
//////////////////////////////////////
// If there is any data in the Rx FIFO
// Read the data and do error checking
if(!q_empty(&extension->Port->QIn))
VSRead(extension);
if (extension->port_config->RingEmulate)
{
if (extension->ring_timer != 0) // RI on
{
--extension->ring_timer;
if (extension->ring_timer != 0) // RI on
sp->msr_value |= MSR_RING_ON;
else
{
//MyKdPrint(D_Test,("RING OFF!\n"))
sp->msr_value &= ~MSR_RING_ON;
}
}
}
if (sp->old_msr_value != sp->msr_value) // delta change bits
{
WORD diff, ModemStatus;
diff = sp->old_msr_value ^ sp->msr_value;
sp->old_msr_value = sp->msr_value;
if (Driver.TraceOptions & 8) // trace output data
{
char str[20];
Sprintf(str, "msr:%x\n", sp->msr_value);
q_put(&Driver.DebugQ, (BYTE *) str, strlen(str));
}
// Check on modem changes and update the modem status
if (diff & (MSR_CD_ON | MSR_CTS_ON | MSR_RING_ON | MSR_DSR_ON | MSR_TX_FLOWED_OFF))
{
// make a bit set that ioctl can use in report
ModemStatus = 0;
if (sp->msr_value & MSR_CTS_ON)
{
ModemStatus |= SERIAL_CTS_STATE;
if (extension->HandFlow.ControlHandShake & SERIAL_CTS_HANDSHAKE)
extension->TXHolding &= ~SERIAL_TX_CTS; // set holding
}
else
{
if (extension->HandFlow.ControlHandShake & SERIAL_CTS_HANDSHAKE)
extension->TXHolding |= SERIAL_TX_CTS; // set holding
}
if (sp->msr_value & MSR_DSR_ON)
{
ModemStatus |= SERIAL_DSR_STATE;
if (extension->HandFlow.ControlHandShake & SERIAL_DSR_HANDSHAKE)
extension->TXHolding &= ~SERIAL_TX_DSR; // set holding
}
else
{
if (extension->HandFlow.ControlHandShake & SERIAL_DSR_HANDSHAKE)
extension->TXHolding |= SERIAL_TX_DSR; // set holding
}
if (sp->msr_value & MSR_RING_ON)
ModemStatus |= SERIAL_RI_STATE;
if (sp->msr_value & MSR_CD_ON)
{
ModemStatus |= SERIAL_DCD_STATE;
if (extension->HandFlow.ControlHandShake & SERIAL_DCD_HANDSHAKE)
extension->TXHolding &= ~SERIAL_TX_DCD; // set holding
}
else
{
if (extension->HandFlow.ControlHandShake & SERIAL_DCD_HANDSHAKE)
extension->TXHolding |= SERIAL_TX_DCD; // set holding
}
if (sp->msr_value & MSR_TX_FLOWED_OFF)
{
// handle holding detection if xon,xoff tx control activated
if (extension->HandFlow.FlowReplace & SERIAL_AUTO_TRANSMIT)
{
extension->TXHolding |= SERIAL_TX_XOFF; // holding
}
}
else if (extension->TXHolding & SERIAL_TX_XOFF)
{
extension->TXHolding &= ~SERIAL_TX_XOFF; // not holding
}
extension->ModemStatus = (ULONG) ModemStatus;
// following is for built in NT virtual 16450 uart support
// virtual uart depends on escape commands in data stream to
// detect modem-signal changes.
if (extension->escapechar != 0)
{
UCHAR msr;
if (q_room(&extension->RxQ) > 2)
{
q_put_one(&extension->RxQ, extension->escapechar);
q_put_one(&extension->RxQ, SERIAL_LSRMST_MST);
msr = (UCHAR)extension->ModemStatus;
if (diff & MSR_CD_ON) msr |= 8; // SERIAL_MSR_DDCD
if (diff & MSR_RING_ON) msr |= 4; // SERIAL_MSR_TERI
if (diff & MSR_DSR_ON) msr |= 2; // SERIAL_MSR_DDSR
if (diff & MSR_CTS_ON) msr |= 1; // SERIAL_MSR_DCTS
q_put_one(&extension->RxQ, msr);
} // q_room
} // if escapechar
// Check if there are any modem events in the WaitMask
if (extension->IsrWaitMask & ( SERIAL_EV_RING |
SERIAL_EV_CTS |
SERIAL_EV_DSR |
SERIAL_EV_RLSD ))
{
if( (extension->IsrWaitMask & SERIAL_EV_RING) &&
(diff & MSR_RING_ON) )
{ extension->HistoryMask |= SERIAL_EV_RING;
}
if ((extension->IsrWaitMask & SERIAL_EV_CTS) &&
(diff & MSR_CTS_ON) )
{ extension->HistoryMask |= SERIAL_EV_CTS;
}
if( (extension->IsrWaitMask & SERIAL_EV_DSR) &&
(diff & MSR_DSR_ON) )
{ extension->HistoryMask |= SERIAL_EV_DSR;
}
if( (extension->IsrWaitMask & SERIAL_EV_RLSD) &&
(diff & MSR_CD_ON) )
{ extension->HistoryMask |= SERIAL_EV_RLSD;
}
} // isrwaitmask
} // diff
} // old_msr != msr
////////////////////////////////////////////////////////////
// At this point, all receive events should be chalked up.
// Some events have been checked in VSRead()
// Any Tx related WaitMask events will be reported in Tx Dpc
// Abort all pending reads and writes if an error and ERROR_ABORT
if( (extension->HandFlow.ControlHandShake & SERIAL_ERROR_ABORT) &&
(extension->ErrorWord) )
{
KeInsertQueueDpc(&extension->CommErrorDpc,NULL,NULL);
}
// Tell the app about any Wait events that have occurred if needed
if (extension->WaitIsISRs && extension->HistoryMask)
{
*extension->IrpMaskLocation = extension->HistoryMask;
// Done with these
extension->WaitIsISRs = 0;
extension->HistoryMask = 0;
extension->CurrentWaitIrp->IoStatus.Information = sizeof(ULONG);
KeInsertQueueDpc(&extension->CommWaitDpc,NULL,NULL);
}
//-------- check for data to move from input queue to irp-buffer
if (extension->ReadPending && // we are given control to fill
extension->NumberNeededForRead && // more to be filled
extension->CurrentReadIrp) // rug not pulled out from our feet
{
if (extension->RxQ.QPut != extension->RxQ.QGet) // not empty
{
// move data from input queue to IRP buffer.
extension->CountOnLastRead |=
SerialGetCharsFromIntBuffer(extension);
if (extension->NumberNeededForRead == 0) // IRP complete!
{
extension->CurrentReadIrp->IoStatus.Information =
IoGetCurrentIrpStackLocation(
extension->CurrentReadIrp
)->Parameters.Read.Length;
extension->CurrentReadIrp->IoStatus.Status = STATUS_SUCCESS;
// We're finished with this read
extension->ReadPending = FALSE;
KeInsertQueueDpc( &extension->CompleteReadDpc, NULL, NULL );
} // irp complete
} // more data to read out of input queue
} // end of Read completion
wrote_some_data = 0;
if (extension->WriteBelongsToIsr == 1) // its ours to process
{
// own the cur write irp, have data to write
if (extension->WriteLength)
{
wrote_some_data = 1;
extension->ISR_Flags |= TX_NOT_EMPTY; // use to detect fifo empty
// Send it all ,WriteTxBlk will chk fifo
wCount = q_put( &extension->Port->QOut,
(PUCHAR)((extension->CurrentWriteIrp)->AssociatedIrp.SystemBuffer)+
(extension->CurrentWriteIrp)->IoStatus.Information,
extension->WriteLength);
extension->OurStats.TransmittedCount += wCount;
extension->WriteLength -= wCount;
(extension->CurrentWriteIrp)->IoStatus.Information += wCount;
if(!extension->WriteLength)//No more to write Close the DPC call
{
if (!extension->port_config->WaitOnTx)
{
extension->WriteBelongsToIsr = 2;
KeInsertQueueDpc( &extension->CompleteWriteDpc, NULL, NULL );
}
}
} // if (extension->WriteLength) // data to write
}
if (!wrote_some_data)
{
if (extension->ISR_Flags & TX_NOT_EMPTY)
{
//----- check for EV_TXEMPTY condition
// and no pending writes
// check to see if tx-fifo is empty
if ((q_empty(&extension->Port->QOut)) &&
(PortGetTxCntRemote(extension->Port) == 0))
{
if (IsListEmpty(&extension->WriteQueue))
{
extension->ISR_Flags &= ~TX_NOT_EMPTY;
// do we have an ev_txempty thing to take care of?
if (extension->IrpMaskLocation &&
(extension->IsrWaitMask & SERIAL_EV_TXEMPTY) )
{
// app has wait irp pending
if (extension->CurrentWaitIrp)
{
extension->HistoryMask |= SERIAL_EV_TXEMPTY;
}
}
} // no more write irps queued up
// see if we need to finish waitontx write irp
if (extension->port_config->WaitOnTx)
{
if (extension->WriteBelongsToIsr == 1) // its ours to process
{
extension->WriteBelongsToIsr = 2;
KeInsertQueueDpc( &extension->CompleteWriteDpc, NULL, NULL );
}
}
} // tx fifo is empty
} // TX_NOT_EMPTY
} // !wrote_some_data
// Tell the app about any Wait events that have occurred if needed
if (extension->WaitIsISRs && extension->HistoryMask)
{
#ifdef COMMENT_OUT
if (Driver.TraceOptions & 8) // trace output data
{
char str[20];
Sprintf(str, "ISR Event:%xH\n", extension->HistoryMask);
q_put(&Driver.DebugQ, (BYTE *) str, strlen(str));
}
#endif
*extension->IrpMaskLocation = extension->HistoryMask;
// Done with these
extension->WaitIsISRs = 0;
extension->HistoryMask = 0;
extension->CurrentWaitIrp->IoStatus.Information = sizeof(ULONG);
KeInsertQueueDpc(&extension->CommWaitDpc,NULL,NULL);
}
}
#endif
#ifdef S_RK
/*---------------------------------------------------------------------------
ServiceRocket - Handle the RocketPort hardware service
|---------------------------------------------------------------------------*/
static void ServiceRocket(PSERIAL_DEVICE_EXTENSION extension)
{
ULONG ustat;
ULONG wCount;
int wrote_some_data;
ustat = sGetChanIntID(extension->ChP);
//////////////////////////////////////
// If there is any data in the Rx FIFO
// Read the data and do error checking
if (ustat & RXF_TRIG)
{
RocketRead(extension);
}
// Check on modem changes and update the modem status
if (ustat & (DELTA_CD|DELTA_CTS|DELTA_DSR))
{
// Read and update the modem status in the extension
SetExtensionModemStatus(extension);
}
// handle RPortPlus RI signal
if (extension->board_ext->config->IsRocketPortPlus)
{
if (sGetRPlusModemRI(extension->ChP) != 0) // RI on
{
extension->ModemStatus |= SERIAL_RI_STATE;
}
else
{
extension->ModemStatus &= ~SERIAL_RI_STATE;
}
}
#ifdef RING_FAKE
if (extension->port_config->RingEmulate)
{
if (extension->ring_timer != 0) // RI on
{
--extension->ring_timer;
if (extension->ring_timer != 0) // RI on
extension->ModemStatus |= SERIAL_RI_STATE;
else
extension->ModemStatus &= ~SERIAL_RI_STATE;
}
}
#endif
if (extension->EventModemStatus != extension->ModemStatus)
{
// xor to show changed bits
ustat = extension->EventModemStatus ^ extension->ModemStatus;
// update change
extension->EventModemStatus = extension->ModemStatus;
// following is for built in NT virtual 16450 uart support
// virtual uart depends on escape commands in data stream to
// detect modem-signal changes.
if (extension->escapechar != 0)
{
UCHAR msr;
// we are assuming we have room to put the following!
if (q_room(&extension->RxQ) > 2)
{
q_put_one(&extension->RxQ, extension->escapechar);
q_put_one(&extension->RxQ, SERIAL_LSRMST_MST);
msr = (UCHAR)extension->ModemStatus;
if (ustat & SERIAL_DCD_STATE) msr |= 8; // SERIAL_MSR_DDCD
if (ustat & SERIAL_RI_STATE) msr |= 4; // SERIAL_MSR_TERI
if (ustat & SERIAL_DSR_STATE) msr |= 2; // SERIAL_MSR_DDSR
if (ustat & SERIAL_CTS_STATE) msr |= 1; // SERIAL_MSR_DCTS
q_put_one(&extension->RxQ, msr);
}
}
// Check if there are any modem events in the WaitMask
if(extension->IsrWaitMask & ( SERIAL_EV_RING |
SERIAL_EV_CTS |
SERIAL_EV_DSR |
SERIAL_EV_RLSD )
)
{
if( (extension->IsrWaitMask & SERIAL_EV_RING) &&
(ustat & SERIAL_RI_STATE) )
{ extension->HistoryMask |= SERIAL_EV_RING;
}
if( (extension->IsrWaitMask & SERIAL_EV_CTS) &&
(ustat & SERIAL_CTS_STATE) )
{ extension->HistoryMask |= SERIAL_EV_CTS;
}
if( (extension->IsrWaitMask & SERIAL_EV_DSR) &&
(ustat & SERIAL_DSR_STATE) )
{ extension->HistoryMask |= SERIAL_EV_DSR;
}
if( (extension->IsrWaitMask & SERIAL_EV_RLSD) &&
(ustat & SERIAL_DCD_STATE) )
{ extension->HistoryMask |= SERIAL_EV_RLSD;
}
}
} // end if modem-control detect change
////////////////////////////////////////////////////////////
// At this point, all receive events should be chalked up.
// Some events have been checked in RocketRead()
// Any Tx related WaitMask events will be reported in Tx Dpc
// Abort all pending reads and writes if an error and ERROR_ABORT
if( (extension->HandFlow.ControlHandShake & SERIAL_ERROR_ABORT) &&
(extension->ErrorWord) )
{
KeInsertQueueDpc(&extension->CommErrorDpc,NULL,NULL);
}
//-------- check for data to move from input queue to irp-buffer
if (extension->ReadPending && // we are given control to fill
extension->NumberNeededForRead && // more to be filled
extension->CurrentReadIrp) // rug not pulled out from our feet
{
if (extension->RxQ.QPut != extension->RxQ.QGet) // not empty
{
// move data from input queue to IRP buffer.
extension->CountOnLastRead |=
SerialGetCharsFromIntBuffer(extension);
if (extension->NumberNeededForRead == 0) // IRP complete!
{
extension->CurrentReadIrp->IoStatus.Information =
IoGetCurrentIrpStackLocation(
extension->CurrentReadIrp
)->Parameters.Read.Length;
extension->CurrentReadIrp->IoStatus.Status = STATUS_SUCCESS;
// We're finished with this read
extension->ReadPending = FALSE;
KeInsertQueueDpc( &extension->CompleteReadDpc, NULL, NULL );
} // irp complete
} // more data to read out of input queue
} // end of Read completion
wrote_some_data = 0;
//-------- do BREAK handling
if ( extension->TXHolding & SERIAL_TX_BREAK )
{
// Check if we need to start the break
if(extension->DevStatus & COM_REQUEST_BREAK)
{
// Make sure Transmitter is empty before slamming BREAK
// Check the bit twice in case of time between buf and txshr load
if( (sGetChanStatusLo(extension->ChP) & TXSHRMT) &&
(sGetChanStatusLo(extension->ChP) & TXSHRMT) )
{
sSendBreak(extension->ChP);
extension->DevStatus &= ~COM_REQUEST_BREAK;
}
}
}
else if (extension->WriteBelongsToIsr == 1) // its ours to process
{
//----- Not holding due to BREAK so try to enqueue Tx data
if (extension->WriteLength)
{
wrote_some_data = 1;
if (extension->Option & OPTION_RS485_SOFTWARE_TOGGLE)
{
if ((extension->DTRRTSStatus & SERIAL_RTS_STATE) == 0)
{
sSetRTS(extension->ChP);
extension->DTRRTSStatus |= SERIAL_RTS_STATE;
}
}
extension->ISR_Flags |= TX_NOT_EMPTY; // use to detect fifo empty
// Send it all ,WriteTxBlk will chk fifo
wCount = sWriteTxBlk( extension->ChP,
(PUCHAR)((extension->CurrentWriteIrp)->AssociatedIrp.SystemBuffer)+
(extension->CurrentWriteIrp)->IoStatus.Information,
extension->WriteLength);
extension->OurStats.TransmittedCount += wCount;
extension->WriteLength -= wCount;
(extension->CurrentWriteIrp)->IoStatus.Information += wCount;
if(!extension->WriteLength)//No more to write Close the DPC call
{
if (!extension->port_config->WaitOnTx)
{
extension->WriteBelongsToIsr = 2;
KeInsertQueueDpc( &extension->CompleteWriteDpc, NULL, NULL );
}
}
} // if (extension->WriteLength) // data to write
} // end if !TXholding and WriteBelongsToIsr == 1
if (!wrote_some_data)
{
if (extension->ISR_Flags & TX_NOT_EMPTY)
{
//----- check for EV_TXEMPTY condition
// and no pending writes
// check to see if tx-fifo truely empty
// need to check twice due to hardware quirks
if ( (sGetTxCnt(extension->ChP) == 0) &&
(sGetChanStatusLo(extension->ChP) & TXSHRMT) )
{
if (IsListEmpty(&extension->WriteQueue))
{
extension->ISR_Flags &= ~TX_NOT_EMPTY;
// do we have an ev_txempty thing to take care of?
if (extension->IrpMaskLocation &&
(extension->IsrWaitMask & SERIAL_EV_TXEMPTY) )
{
// app has wait irp pending
if (extension->CurrentWaitIrp)
{
extension->HistoryMask |= SERIAL_EV_TXEMPTY;
}
}
if (extension->Option & OPTION_RS485_SOFTWARE_TOGGLE)
{
if ((extension->DTRRTSStatus & SERIAL_RTS_STATE) != 0)
{
extension->DTRRTSStatus &= ~SERIAL_RTS_STATE;
sClrRTS(extension->ChP);
}
}
} // no more write irps queued up
// see if we need to finish waitontx write irp
if (extension->port_config->WaitOnTx)
{
if (extension->WriteBelongsToIsr == 1) // its ours to process
{
extension->WriteBelongsToIsr = 2;
KeInsertQueueDpc( &extension->CompleteWriteDpc, NULL, NULL );
}
}
} // tx fifo went empty
} // TX_NOT_EMPTY
} // !wrote_some_data
// Tell the app about any Wait events that have occurred if needed
if (extension->WaitIsISRs && extension->HistoryMask)
{
#ifdef COMMENT_OUT
if (Driver.TraceOptions & 8) // trace output data
{
char str[20];
Sprintf(str, "ISR Event:%xH\n", extension->HistoryMask);
q_put(&Driver.DebugQ, (BYTE *) str, strlen(str));
}
#endif
*extension->IrpMaskLocation = extension->HistoryMask;
// Done with these
extension->WaitIsISRs = 0;
//extension->IrpMaskLocation = NULL;
extension->HistoryMask = 0;
extension->CurrentWaitIrp->IoStatus.Information = sizeof(ULONG);
KeInsertQueueDpc(&extension->CommWaitDpc,NULL,NULL);
}
}
/*-----------------------------------------------------------------------------
Function : RocketRead
Purpose: Moves data from Rocket's Rx FIFO to RxIn of dev's extension
NOTES: The error checking assumes that if no replacement is required,
the errored chars are ignored.
The RXMATCH feature is used for EventChar detection.
Return: None
|-----------------------------------------------------------------------------*/
static void RocketRead(PSERIAL_DEVICE_EXTENSION extension)
{
int WrapCount; // Number of bytes in wrap (2 stage copy)
int RxFree;
int sCount;
unsigned int ChanStatus;
unsigned int StatusWord;
int OriginalCount; // Used to determine if Rx event occurred
// Save off the original Rx buff ptr. Test later for Rx event
OriginalCount = extension->RxQ.QPut;
// Get count before reading status
// NOTE: Should always have a count if we entered this code
sCount = sGetRxCnt(extension->ChP);
if (sCount == 0)
{
//GTrace("Error, RXF_TRIG lied");
return;
}
// Have count, now get status
ChanStatus = sGetChanStatus(extension->ChP) &
(STATMODE | RXFOVERFL | RXBREAK |
RXFRAME | RX2MATCH | RX1MATCH | RXPARITY);
// Make sure we're in statmode if errors are pending in the FIFO
if (ChanStatus)
{
if (ChanStatus & RX1MATCH) // Must signal Rx Match immediately
{
if (extension->IsrWaitMask & SERIAL_EV_RXFLAG)
extension->HistoryMask |= SERIAL_EV_RXFLAG;
ChanStatus &= ~RX1MATCH;
}
if (ChanStatus)
sEnRxStatusMode(extension->ChP);
}
// See how much space we have in RxBuf (host side buffer)
RxFree = q_room(&extension->RxQ);
if (RxFree > 20) // plenty of space in RX queue
{
RxFree -= 20; // leave some space for virtual insertion stuff
extension->ReadByIsr++; // Increment statistics Read flag
//------ Adjust count to maximum we can put in RxIn buffer
if (RxFree < sCount)
sCount = RxFree;
}
else // no more room in server buffer input queue
{
extension->HistoryMask |= (extension->IsrWaitMask & SERIAL_EV_RX80FULL);
// No room in host side buffer, only do the software flow ctl check
// check for overflow
if (ChanStatus & RXFOVERFL)
{
// extension->ErrorWord |= SERIAL_ERROR_OVERRUN;
extension->ErrorWord |= SERIAL_ERROR_QUEUEOVERRUN;
extension->HistoryMask |= (extension->IsrWaitMask & SERIAL_EV_ERR);
++extension->OurStats.BufferOverrunErrorCount;
}
goto FlowControlCheck;
}
//--------------------------- Attempt to read any pending data
// ChanStatus indicates any pending errors or matches
if (ChanStatus)
{
// Loop on reading Rocket FIFO
// sCount represents Rocket data, RxFree represents host buffer
while(sCount)
{
// Get stat byte and data
StatusWord = sReadRxWord( sGetTxRxDataIO(extension->ChP));
sCount--;
++extension->OurStats.ReceivedCount; // keep status
switch(StatusWord & (STMPARITYH | STMFRAMEH | STMBREAKH) )
{
case STMPARITYH:
{
if (extension->HandFlow.FlowReplace & SERIAL_ERROR_CHAR)
{
q_put_one(&extension->RxQ,
extension->SpecialChars.ErrorChar);
}
else // queue the character received(add 12-03-96)
{
q_put_one(&extension->RxQ, (UCHAR)StatusWord);
}
extension->ErrorWord |= SERIAL_ERROR_PARITY;
extension->HistoryMask |= (extension->IsrWaitMask & SERIAL_EV_ERR);
++extension->OurStats.ParityErrorCount;
break;
}
case STMFRAMEH:
{
if (extension->HandFlow.FlowReplace & SERIAL_ERROR_CHAR)
{
q_put_one(&extension->RxQ,
extension->SpecialChars.ErrorChar);
}
else // queue the character received(add 12-03-96)
{
q_put_one(&extension->RxQ, (UCHAR)StatusWord);
}
extension->ErrorWord |= SERIAL_ERROR_FRAMING;
extension->HistoryMask |= (extension->IsrWaitMask & SERIAL_EV_ERR);
++extension->OurStats.FrameErrorCount;
break;
}
// PARITY can be set along with BREAK, BREAK overrides PARITY
case ( STMBREAKH | STMPARITYH ):
case STMBREAKH:
{
if (extension->HandFlow.FlowReplace & SERIAL_BREAK_CHAR)
{
q_put_one(&extension->RxQ,
extension->SpecialChars.BreakChar);
}
extension->ErrorWord |= SERIAL_ERROR_BREAK;
extension->HistoryMask |= (extension->IsrWaitMask & SERIAL_EV_BREAK);
break;
}
default:
{
if (extension->TXHolding & ST_XOFF_FAKE)
{
if ((UCHAR)StatusWord == extension->SpecialChars.XonChar)
{
extension->TXHolding &= ~ST_XOFF_FAKE;
extension->TXHolding &= ~SERIAL_TX_XOFF;
sEnTransmit(extension->ChP); // Start up the transmitter
sDisRxCompare2(extension->ChP); // turn off match
sEnTxSoftFlowCtl(extension->ChP); // turn on Tx software flow control
// override an actual XOFF from remote
sClrTxXOFF(extension->ChP);
}
else
{ q_put_one(&extension->RxQ, (UCHAR)StatusWord); } // queue normal char
}
else
{ q_put_one(&extension->RxQ, (UCHAR)StatusWord); } // queue normal char
if (extension->escapechar != 0)
{
if ((UCHAR)StatusWord == extension->escapechar)
{
// Modem status escape convention for virtual port
// support, escape the escape char.
{ q_put_one(&extension->RxQ, SERIAL_LSRMST_ESCAPE); }
}
}
}
} // end switch
//------ check for near overflow condition due to insertions
if (q_room(&extension->RxQ) < 10)
sCount = 0; // stop reading hardware!
} // end while sCount
//--- if rx-data all read down, turn off slow status mode
if(!(sGetChanStatusLo(extension->ChP) & RDA))
{
sDisRxStatusMode(extension->ChP);
}
// Overflow is reported immediately, statmode can't do it properly
if (ChanStatus & RXFOVERFL)
{ extension->ErrorWord |= SERIAL_ERROR_OVERRUN;
extension->HistoryMask |= (extension->IsrWaitMask & SERIAL_EV_ERR);
++extension->OurStats.SerialOverrunErrorCount;
}
} // end if ChanStatus
else
{
//--------------------------------------------------------
// No pending errors or matches in the FIFO, read the data normally (fast)
// Check for wrap condition first
WrapCount = q_room_put_till_wrap(&extension->RxQ);
if (sCount > WrapCount) // then 2 moves required
{
// This will require a wrap
sReadRxBlk(extension->ChP,
extension->RxQ.QBase + extension->RxQ.QPut,
WrapCount);
// Do the second copy...
sReadRxBlk(extension->ChP,
extension->RxQ.QBase,
sCount-WrapCount);
#ifdef RING_FAKE
if (extension->port_config->RingEmulate)
{
if ((extension->ModemStatus & SERIAL_DCD_STATE) == 0) // if CD off
{
ring_check(extension, extension->RxQ.QBase + extension->RxQ.QPut,
WrapCount);
ring_check(extension, extension->RxQ.QBase,
sCount-WrapCount);
}
}
#endif
}
else // only one move required
{
// no queue wrap required
sReadRxBlk(extension->ChP,
extension->RxQ.QBase + extension->RxQ.QPut,
sCount);
#ifdef RING_FAKE
if (extension->port_config->RingEmulate)
{
if ((extension->ModemStatus & SERIAL_DCD_STATE) == 0) // if CD off
{
ring_check(extension, extension->RxQ.QBase + extension->RxQ.QPut,
sCount);
}
}
#endif
}
extension->RxQ.QPut = (extension->RxQ.QPut + sCount) % extension->RxQ.QSize;
extension->OurStats.ReceivedCount += sCount;
} // end fast read
FlowControlCheck: ;
///////////////////////////////////////
// Software and DTR input flow control checking
if( (extension->HandFlow.FlowReplace & SERIAL_AUTO_RECEIVE) ||
(extension->HandFlow.ControlHandShake & SERIAL_DTR_HANDSHAKE )
)
{
// check for flow control conditions
if (extension->DevStatus & COM_RXFLOW_ON)
{
// do we need to stop Rx?
if(sGetRxCnt(extension->ChP) >= RX_HIWATER)
{
if(extension->HandFlow.FlowReplace & SERIAL_AUTO_RECEIVE)
{
// send XOFF
sWriteTxPrioByte(extension->ChP,
extension->SpecialChars.XoffChar);
extension->DevStatus &= ~COM_RXFLOW_ON;
extension->RXHolding |= SERIAL_RX_XOFF;
}
if(extension->HandFlow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
{
// drop DTR
sClrDTR(extension->ChP);
extension->DTRRTSStatus &= ~SERIAL_DTR_STATE;
extension->DevStatus &= ~COM_RXFLOW_ON;
extension->RXHolding |= SERIAL_RX_DSR;
}
}
}
else // Rx flow is stopped
{
// can we resume Rx?
if(sGetRxCnt(extension->ChP) <= RX_LOWATER)
{
if(extension->HandFlow.FlowReplace & SERIAL_AUTO_RECEIVE)
{
// send XON
sWriteTxPrioByte(extension->ChP,
extension->SpecialChars.XonChar);
extension->DevStatus |= COM_RXFLOW_ON;
extension->RXHolding &= ~SERIAL_RX_XOFF;
}
if(extension->HandFlow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
{
// raise DTR
sSetDTR(extension->ChP);
extension->DTRRTSStatus |= SERIAL_DTR_STATE;
extension->DevStatus |= COM_RXFLOW_ON;
extension->RXHolding &= ~SERIAL_RX_DSR;
}
}
}
} // end of software and DTR input flow control check
// Should we mark a Rx event?
if ( OriginalCount != extension->RxQ.QPut )
extension->HistoryMask|=(extension->IsrWaitMask & SERIAL_EV_RXCHAR);
}
#endif
#ifdef RING_FAKE
/*------------------------------------------------------------------------------
ring_check - scan the rx data for a modem "RING<CR>" or "2<CR>" string.
If found, trigger a emulated hardware RING signal.
|------------------------------------------------------------------------------*/
static void ring_check(PSERIAL_DEVICE_EXTENSION extension,
BYTE *data,
int len)
{
int i;
for (i=0; i<len; i++)
{
switch (data[i])
{
case '2':
if (len <= 2)
extension->ring_char = '2';
else extension->ring_char = 0;
break;
case 'R':
extension->ring_char = 'R';
break;
case 'I':
if (extension->ring_char == 'R')
extension->ring_char = 'I';
else extension->ring_char = 0;
break;
case 'N':
if (extension->ring_char == 'I')
extension->ring_char = 'N';
else extension->ring_char = 0;
break;
case 'G':
if (extension->ring_char == 'N')
extension->ring_char = 'G';
else extension->ring_char = 0;
break;
case 0xd:
if ( (extension->ring_char == 'G') ||
((extension->ring_char == '2') && (len <= 2)) )
{
//MyKdPrint(D_Init,("RING!\n"))
// OK, look s like the data stream says a "RING" took place.
// so setup a timer which will cause a hardware RING to be made
// set to .5 sec for 10ms scanrate, .05sec for 1ms scanrate
extension->ring_timer = 50;
}
extension->ring_char = 0;
break;
default:
extension->ring_char = 0;
break;
}
}
}
#endif
/*-----------------------------------------------------------------------------
RocketRefresh - This runs every 255 ticks or so, in order to perform
background activities. We will go read the modem status, and update
the ModemCtl field. The monitor program reads this variable, and we
don't want to waste time reading it too often, so we just update it
occasionally here.
|-----------------------------------------------------------------------------*/
static void RocketRefresh(void)
{
PSERIAL_DEVICE_EXTENSION extension;
PSERIAL_DEVICE_EXTENSION board_ext;
#ifdef S_RK
board_ext = Driver.board_ext;
while (board_ext)
{
if ((!board_ext->FdoStarted) || (!board_ext->config->HardwareStarted))
{
board_ext = board_ext->board_ext;
continue;
}
extension = board_ext->port_ext;
while (extension)
{
// Read and update the modem status in the extension
SetExtensionModemStatus(extension);
extension = extension->port_ext; // next in chain
} // while port extension
board_ext = board_ext->board_ext;
} // while board extension
#endif
debug_poll(); // handle turn off of debug on inactivity timeout
}
#ifdef S_VS
/*-----------------------------------------------------------------------------
Function : VSRead
Purpose: Moves data from VS's Rx FIFO to RxIn of dev's extension
NOTES: The error checking assumes that if no replacement is required,
the errored chars are ignored.
The RXMATCH feature is used for EventChar detection.
Return: None
|-----------------------------------------------------------------------------*/
static void VSRead(PSERIAL_DEVICE_EXTENSION extension)
{
int WrapCount; // Number of bytes in wrap (2 stage copy)
int RxFree;
int sCount;
LONG OriginalCount; // Used to determine if Rx event occurred
// Save off the original Rx buff ptr. Test later for Rx event
OriginalCount = extension->RxQ.QPut;
// Get count before reading status
// NOTE: Should always have a count if we entered this code
sCount=PortGetRxCnt(extension->Port);
if (sCount == 0)
{
//MyTrace("Error, RXF_TRIG lied");
return;
}
// See how much space we have in RxBuf (host side buffer)
RxFree = q_room(&extension->RxQ);
// if no space in RxBuf, don't read from RocketPort
if (RxFree > 20) // plenty of space in RX queue
{
RxFree -= 20; // leave some space for virtual insertion stuff
extension->ReadByIsr++; // Increment statistics Read flag
//------ Adjust count to maximum we can put in RxIn buffer
if (RxFree < sCount)
sCount = RxFree;
}
else // no more room in server buffer input queue
{
extension->HistoryMask |= (extension->IsrWaitMask & SERIAL_EV_RX80FULL);
// No room in host side buffer, only do the software flow ctl check
// check for overflow
if (extension->Port->esr_reg & ESR_OVERFLOW_ERROR)
{
// extension->ErrorWord |= SERIAL_ERROR_OVERRUN;
extension->ErrorWord |= SERIAL_ERROR_QUEUEOVERRUN;
extension->HistoryMask |= (extension->IsrWaitMask & SERIAL_EV_ERR);
extension->Port->esr_reg = 0; // reset to zero on read
++extension->OurStats.BufferOverrunErrorCount;
}
goto FlowControlCheck;
}
//------ report any rx error conditions.
if (extension->Port->esr_reg)
{
if (extension->Port->esr_reg & ESR_OVERFLOW_ERROR)
{
extension->ErrorWord |= SERIAL_ERROR_OVERRUN;
extension->HistoryMask |= (extension->IsrWaitMask & SERIAL_EV_ERR);
++extension->OurStats.SerialOverrunErrorCount;
}
else if (extension->Port->esr_reg & ESR_BREAK_ERROR)
{
extension->ErrorWord |= SERIAL_ERROR_BREAK;
extension->HistoryMask |= (extension->IsrWaitMask & SERIAL_EV_BREAK);
}
else if (extension->Port->esr_reg & ESR_FRAME_ERROR)
{
extension->ErrorWord |= SERIAL_ERROR_FRAMING;
extension->HistoryMask |= (extension->IsrWaitMask & SERIAL_EV_ERR);
++extension->OurStats.FrameErrorCount;
}
else if (extension->Port->esr_reg & ESR_PARITY_ERROR)
{
extension->ErrorWord |= SERIAL_ERROR_PARITY;
extension->HistoryMask |= (extension->IsrWaitMask&SERIAL_EV_ERR);
++extension->OurStats.ParityErrorCount;
}
extension->Port->esr_reg = 0; // reset to zero on read
}
//--------------------------------------------------------
// No pending errors or matches in the FIFO, read the data normally (fast)
// Check for wrap condition first
WrapCount = q_room_put_till_wrap(&extension->RxQ);
if (sCount > WrapCount) // then 2 moves required
{
q_get(&extension->Port->QIn,
extension->RxQ.QBase + extension->RxQ.QPut,
WrapCount);
// Do the second copy...
q_get(&extension->Port->QIn,
extension->RxQ.QBase,
sCount-WrapCount);
if (extension->IsrWaitMask & SERIAL_EV_RXFLAG)
{
if (search_match(extension->RxQ.QBase + extension->RxQ.QPut,
WrapCount,extension->SpecialChars.EventChar))
extension->HistoryMask |= SERIAL_EV_RXFLAG;
if (search_match(extension->RxQ.QBase,
sCount-WrapCount,extension->SpecialChars.EventChar))
extension->HistoryMask |= SERIAL_EV_RXFLAG;
}
#ifdef RING_FAKE
if (extension->port_config->RingEmulate)
{
if ((extension->ModemStatus & SERIAL_DCD_STATE) == 0) // if CD off
{
ring_check(extension, extension->RxQ.QBase + extension->RxQ.QPut,
WrapCount);
ring_check(extension, extension->RxQ.QBase,
sCount-WrapCount);
}
}
#endif
}
else // only one move required
{
q_get(&extension->Port->QIn,
extension->RxQ.QBase + extension->RxQ.QPut,
sCount);
if (extension->IsrWaitMask & SERIAL_EV_RXFLAG)
{
if (search_match(extension->RxQ.QBase + extension->RxQ.QPut,
sCount,extension->SpecialChars.EventChar))
extension->HistoryMask |= SERIAL_EV_RXFLAG;
}
#ifdef RING_FAKE
if (extension->port_config->RingEmulate)
{
if ((extension->ModemStatus & SERIAL_DCD_STATE) == 0) // if CD off
{
ring_check(extension, extension->RxQ.QBase + extension->RxQ.QPut,
sCount);
}
}
#endif
}
extension->RxQ.QPut = (extension->RxQ.QPut + sCount) % extension->RxQ.QSize;
extension->OurStats.ReceivedCount += sCount;
extension->Port->Status |= S_UPDATE_ROOM;
#ifdef NEW_Q
extension->Port->nGetLocal += sCount;
#endif
FlowControlCheck: ;
//----- Should we mark a Rx event?
if ( OriginalCount != extension->RxQ.QPut )
extension->HistoryMask|=(extension->IsrWaitMask & SERIAL_EV_RXCHAR);
}
#ifndef USE_MEMCHR_SCAN
/*------------------------------------------------------------------
search_match -
|------------------------------------------------------------------*/
static int search_match(BYTE *buf, int count, BYTE eventchar)
{
int i;
for (i=0; i<count; i++)
{
if (buf[i] == eventchar)
return 1; // found
}
return 0; // not found
}
#endif
#endif