/*------------------------------------------------------------------- | 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; jChP); 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" or "2" 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; iring_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