Beispiel #1
0
wxString RaceAnalyzerComm::readScript(){

	wxMutexLocker lock(_commMutex);

	wxString script = "";
	int scriptPage = 0;
	int to = 0;
	CComm *serialPort = GetSerialPort();
	if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);

	FlushReceiveBuffer(serialPort);
	while(!to){

		//wxString cmd = wxString::Format("println(getScriptPage(%d))",scriptPage++);
		wxString cmd = wxString::Format("readScriptPage %d",scriptPage++);
		wxString buffer = SendCommand(serialPort, cmd);

		wxString scriptFrag = GetParam(buffer,"script", true);

		VERBOSE(FMT("escaped: %s", scriptFrag.ToAscii()));
		Unescape(scriptFrag);
		VERBOSE(FMT("unescaped: %s", scriptFrag.ToAscii()));
		size_t scriptFragmentLen = scriptFrag.Length();

		if (scriptFragmentLen > 0 ) script+=scriptFrag;
		//the last page is a 'partial page'
		if (scriptFragmentLen < SCRIPT_PAGE_LENGTH ) break;
	}
	CloseSerialPort();
	if (to){
		throw CommException(CommException::COMM_TIMEOUT);
	}
	return script;
}
Beispiel #2
0
NTSTATUS
SerialCreate( IN PDEVICE_OBJECT DeviceObject,
              IN PIRP Irp )
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
   PDIGI_DEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
   PDIGI_CONTROLLER_EXTENSION ControllerExt = DeviceExt->ParentControllerExt;

   NTSTATUS Status=STATUS_SUCCESS;

   PFEP_CHANNEL_STRUCTURE ChInfo;

   DIGI_XFLAG IFlag;
   USHORT Rmax, Tmax, Rhigh;
   KIRQL OldIrql;
   UCHAR MStatSet, MStatClear, HFlowSet, HFlowClear;

#if DBG
   LARGE_INTEGER CurrentSystemTime;
#endif

   if (DeviceObject==ControllerExt->ControllerDeviceObject)
   {
      /*
      ** All controller opens succeed.
      */
      DigiDump( (DIGIIRP|DIGIFLOW|DIGICREATE), ("ControllerCreate\n"));
      Irp->IoStatus.Status = STATUS_SUCCESS;
      Irp->IoStatus.Information = 0;
      DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
      return STATUS_SUCCESS;
   }

#if DBG
   KeQuerySystemTime( &CurrentSystemTime );
#endif
   DigiDump( (DIGIIRP|DIGIFLOW|DIGICREATE), ("Entering SerialCreate: port = %s\tIRP = 0x%x\t%u:%u\n",
                                             DeviceExt->DeviceDbgString, Irp, CurrentSystemTime.HighPart, CurrentSystemTime.LowPart) );

   ASSERT( IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_CREATE );

   InterlockedIncrement(&ControllerExt->PerfData.OpenRequests);

   KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql );

   DeviceExt->DeviceState = DIGI_DEVICE_STATE_OPEN;
   DeviceExt->WaitMask = 0L;
   DeviceExt->HistoryWait = 0L;
   DeviceExt->TotalCharsQueued = 0L;
   DeviceExt->EscapeChar = 0;
   DeviceExt->SpecialFlags = 0;
   DeviceExt->UnscannedRXFLAGPosition = MAXULONG;

   KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );

   //
   // Okay, lets make sure the port on the controller is in a known
   // state.
   //

   ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
                                     DeviceExt->ChannelInfo.Offset);

   FlushTransmitBuffer( ControllerExt, DeviceExt );
   FlushReceiveBuffer( ControllerExt, DeviceExt );

   // Set default flow control limits.
   EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
   Rmax = READ_REGISTER_USHORT( &ChInfo->rmax );
   Tmax = READ_REGISTER_USHORT( &ChInfo->tmax );
   DisableWindow( ControllerExt );

   DeviceExt->XonLimit = (((LONG)Rmax + 1) / 4);
   Rhigh = Rmax - (USHORT)DeviceExt->XonLimit;
   DeviceExt->XoffLimit = DeviceExt->XonLimit;	// We preserve XoffLimit in Win32 semantics
   WriteCommandWord( DeviceExt, SET_RCV_LOW, (USHORT)DeviceExt->XonLimit );
   WriteCommandWord( DeviceExt, SET_RCV_HIGH, Rhigh );
   WriteCommandWord( DeviceExt, SET_TX_LOW, (USHORT)((Tmax + 1) / 4) );

   //
   // Based on 10ms polling frequency and 115.2Kbps bit rate,
   // we acquire up to 115.2 bytes per polling iteration.
   // Thus, to avoid flow control, we need notification up to
   // two polling iterations prior to reaching XoffLimit.
   // However, we need at least 50 bytes to make the interaction worthwhile.
   // DH Calculate limit dynamically based on communication characteristics.
   //
   if( Rhigh > 2*115 + 50 )
      DeviceExt->ReceiveNotificationLimit = Rhigh - 2*115;
   else
   if( Rmax >= 100 )
      DeviceExt->ReceiveNotificationLimit = 50;
   else // shouldn't happen
      DeviceExt->ReceiveNotificationLimit = Rmax / 2;

   //
   // Initialize requested queue sizes.
   //
   DeviceExt->RequestedQSize.InSize = (ULONG)(Rmax + 1);
   DeviceExt->RequestedQSize.OutSize = (ULONG)(Tmax + 1);

   //
   // Set where RxChar and RxFlag were last seen in the buffer to a
   // bogus value so we catch the condition where the 1st character in
   // is RxFlag, and so we give notification for the 1st character
   // received.
   //
   DeviceExt->PreviousRxChar = MAXULONG;
   DeviceExt->UnscannedRXFLAGPosition = MAXULONG;

   //
   // Set the Xon & Xoff characters for this device to default values.
   //

   WriteCommandBytes( DeviceExt, SET_XON_XOFF_CHARACTERS,
                      DEFAULT_XON_CHAR, DEFAULT_XOFF_CHAR );

   MStatClear = MStatSet = 0;
   HFlowClear = HFlowSet = 0;

   //
   // We have some RTS flow control to worry about.
   //
   // Don't forget that flow control is sticky across
   // open requests.
   //
   if( (DeviceExt->FlowReplace & SERIAL_RTS_MASK) ==
       SERIAL_RTS_HANDSHAKE )
   {
      //
      // This is normal RTS input flow control
      //
      HFlowSet |= ControllerExt->ModemSignalTable[RTS_SIGNAL];
   }
   else if( (DeviceExt->FlowReplace & SERIAL_RTS_MASK) ==
            SERIAL_RTS_CONTROL )
   {
      //
      // We need to make sure RTS is asserted when certain 'things'
      // occur, or when we are in a certain state.
      //
      MStatSet |= ControllerExt->ModemSignalTable[RTS_SIGNAL];
   }
   else if( (DeviceExt->FlowReplace & SERIAL_RTS_MASK) ==
            SERIAL_TRANSMIT_TOGGLE )
   {
   }
   else
   {
      //
      // RTS Control Mode is in a Disabled state.
      //
      MStatClear |= ControllerExt->ModemSignalTable[RTS_SIGNAL];
   }

   //
   // We have some DTR flow control to worry about.
   //
   // Don't forget that flow control is sticky across
   // open requests.
   //
   if( (DeviceExt->ControlHandShake & SERIAL_DTR_MASK) ==
       SERIAL_DTR_HANDSHAKE )
   {
      //
      // This is normal DTR input flow control
      //
      HFlowSet |= ControllerExt->ModemSignalTable[DTR_SIGNAL];
   }
   else if( (DeviceExt->ControlHandShake & SERIAL_DTR_MASK) ==
            SERIAL_DTR_CONTROL )
   {
      //
      // We need to make sure DTR is asserted when certain 'things'
      // occur, or when we are in a certain state.
      //
      MStatSet |= ControllerExt->ModemSignalTable[DTR_SIGNAL];

   }
   else
   {
      //
      // DTR Control Mode is in a Disabled state.
      //
      MStatClear |= ControllerExt->ModemSignalTable[DTR_SIGNAL];
   }

   //
   // CTS, DSR, and DCD output handshaking is sticky across OPEN requests.
   //
   if( (DeviceExt->ControlHandShake & SERIAL_CTS_HANDSHAKE) )
   {
      HFlowSet |= ControllerExt->ModemSignalTable[CTS_SIGNAL];
   }
   else
   {
      HFlowClear |= ControllerExt->ModemSignalTable[CTS_SIGNAL];
   }

   if( (DeviceExt->ControlHandShake & SERIAL_DSR_HANDSHAKE) )
   {
      HFlowSet |= ControllerExt->ModemSignalTable[DSR_SIGNAL];
   }
   else
   {
      HFlowClear |= ControllerExt->ModemSignalTable[DSR_SIGNAL];
   }

   if( (DeviceExt->ControlHandShake & SERIAL_DCD_HANDSHAKE) )
   {
      HFlowSet |= ControllerExt->ModemSignalTable[DCD_SIGNAL];
   }
   else
   {
      HFlowClear |= ControllerExt->ModemSignalTable[DCD_SIGNAL];
   }

   //
   // Make sure we enable/disable flow controls before trying to
   // explicitly set/clear modem control lines.
   //
   if( HFlowSet || HFlowClear )
   {
      DeviceExt->WriteOnlyModemSignalMask = (~HFlowSet) &
               (ControllerExt->ModemSignalTable[DTR_SIGNAL]|ControllerExt->ModemSignalTable[RTS_SIGNAL]);
      WriteCommandBytes( DeviceExt, SET_HDW_FLOW_CONTROL,
                         HFlowSet, HFlowClear );
   }

   if( MStatSet || MStatClear )
   {
      DeviceExt->CurrentModemSignals |= MStatSet;
      DeviceExt->CurrentModemSignals &= ~MStatClear;
      DeviceExt->WriteOnlyModemSignalValue |= MStatSet;
      DeviceExt->WriteOnlyModemSignalValue &= ~MStatClear;

      WriteCommandBytes( DeviceExt, SET_MODEM_LINES,
                         MStatSet, MStatClear );
   }

   //
   // Make sure we get break notification through the event queue to
   // begin with.
   //
   IFlag.Mask = (USHORT)(~( IFLAG_PARMRK | IFLAG_INPCK | IFLAG_DOSMODE ));
   IFlag.Src = IFLAG_BRKINT;
   IFlag.Command = SET_IFLAGS;
   SetXFlag( DeviceExt, &IFlag );

   //
   // Okay, were done, lets get the heck out of dodge.
   //
   Irp->IoStatus.Status = Status;
   Irp->IoStatus.Information = 0L;

   //
   // We do this check here to make sure the controller has had
   // a chance to catch up.  Running on fast machines doesn't always
   // give the controller a chance.
   //
   EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );

   if( READ_REGISTER_USHORT( &ChInfo->rlow )
         == 0 )
      DigiDump( DIGIINIT, ("ChInfo->rlow == 0\n"));

   if( READ_REGISTER_USHORT( &ChInfo->rhigh )
         == 0 )
      DigiDump( DIGIINIT, ("ChInfo->rhigh == 0\n"));

   if( READ_REGISTER_USHORT( &ChInfo->tlow )
         == 0 )
      DigiDump( DIGIINIT, ("ChInfo->tlow == 0\n"));

   //
   // Enable IDATA so we get notified when new data has arrived.
   //
   WRITE_REGISTER_UCHAR( &ChInfo->idata, TRUE );

   DisableWindow( ControllerExt );

   DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );

   InterlockedIncrement(&ControllerExt->PerfData.OpenPorts);

   DigiDump( (DIGIFLOW|DIGICREATE), ("Exiting SerialCreate: port = %s\n",
                                     DeviceExt->DeviceDbgString) );
   return( STATUS_SUCCESS );
}  // end SerialCreate
Beispiel #3
0
VOID
DigiServiceEvent( IN PDIGI_CONTROLLER_EXTENSION ControllerExt,
                  IN USHORT Ein,
                  IN USHORT Eout )

/*++

Routine Description:


Arguments:


Return Value:


--*/

{
   const USHORT Emax = 0x03FC;

   DigiDump( (DIGIFLOW|DIGIEVENT), ("Entering DigiServiceEvent\n") );

   // Event registers should be in range and DWORD-aligned.
   ASSERT( Ein <= Emax && (Ein & 3) == 0 );
   ASSERT( Eout <= Emax && (Eout & 3) == 0 );

   DigiDump( DIGIEVENT, ("---------  Ein(0x%.4x) != Eout(0x%.4x)\n",
                         Ein, Eout ) );

   for( ; Eout != Ein ; Eout += 4, Eout &= Emax )
   {
      PDIGI_DEVICE_EXTENSION DeviceExt;
      PFEP_EVENT pEvent;
      FEP_EVENT Event;
      ULONG EventReason;

      pEvent = (PFEP_EVENT)(ControllerExt->VirtualAddress +
                  ControllerExt->EventQueue.Offset + Eout );

      EnableWindow( ControllerExt, ControllerExt->EventQueue.Window );

      READ_REGISTER_BUFFER_UCHAR( (PUCHAR)pEvent, (PUCHAR)&Event, sizeof(Event) );

      DisableWindow( ControllerExt );

      if( (Event.Channel <= 0xDF) &&
          (Event.Channel < ControllerExt->NumberOfPorts) )
      {
         DeviceExt = ControllerExt->DeviceObjectArray[Event.Channel]->DeviceExtension;
      }
      else // bad command?
      {
         DeviceExt = NULL;
         DigiDump( DIGIEVENT, ("Event on unknown channel %d (flags = 0x%.2x), ignored\n", Event.Channel, Event.Flags) );
         continue;
      }

      DigiDump( DIGIEVENT, ("---------  Channel = %d\tFlags = 0x%.2x\n"
                            "---------  Current = 0x%.2x\tPrev. = 0x%.2x\n",
                           Event.Channel, Event.Flags, Event.CurrentModem,
                           Event.PreviousModem) );

      //
      // OK, let's process the event
      //

      if( Event.Flags & ~(FEP_ALL_EVENT_FLAGS) )
      {
         DigiDump( DIGIERRORS, ("Unknown event queue flag 0x%.2x\n",
               Event.Flags & ~(FEP_ALL_EVENT_FLAGS) ) );
         // Process the event bits that we *do* understand.
      }

      // Modem signals are always processed, regardless of whether the port is open.
      if( Event.Flags & FEP_MODEM_CHANGE_SIGNAL )
      {
         DigiDump( (DIGIMODEM|DIGIEVENT|DIGIWAIT),
                     ("---------  Modem Change Event (%s:%d)\n",
                      __FILE__, __LINE__ ) );

         KeAcquireSpinLockAtDpcLevel( &DeviceExt->ControlAccess );

         DigiDump( (DIGIMODEM|DIGIWAIT),
                             ("   CurrentModem = 0x%x\tPreviousModem = 0x%x\n",
                              Event.CurrentModem, Event.PreviousModem ));

         DeviceExt->CurrentModemSignals = Event.CurrentModem;

         KeReleaseSpinLockFromDpcLevel( &DeviceExt->ControlAccess );
      }

      // If the port isn't open, don't bother with the rest.
      if( DeviceExt->DeviceState != DIGI_DEVICE_STATE_OPEN )
      {
         // If we might return to OPEN, don't touch anything!
         if( DeviceExt->DeviceState != DIGI_DEVICE_STATE_CLEANUP)
         {
            if( Event.Flags & (FEP_RX_PRESENT | FEP_RECEIVE_BUFFER_OVERRUN | FEP_UART_RECEIVE_OVERRUN) )
            {
               PFEP_CHANNEL_STRUCTURE ChInfo;

               FlushReceiveBuffer( ControllerExt, DeviceExt );

               ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
                                                DeviceExt->ChannelInfo.Offset);

               // Notify us when more data comes in.
               EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
               WRITE_REGISTER_UCHAR( &ChInfo->idata, TRUE );
               DisableWindow( ControllerExt );
            }
            // Don't flush transmit (might kill end of data).
         }
         continue;
      }

      // Reset event notifications.
      EventReason = 0;

      if( Event.Flags & FEP_EV_BREAK )
      {
         EventReason |= SERIAL_EV_BREAK;

         KeAcquireSpinLockAtDpcLevel( &DeviceExt->ControlAccess );
         DeviceExt->ErrorWord |= SERIAL_ERROR_BREAK;
         KeReleaseSpinLockFromDpcLevel( &DeviceExt->ControlAccess );
      }

      if( (Event.Flags & (FEP_TX_LOW | FEP_TX_EMPTY) ) )
      {
         PLIST_ENTRY WriteQueue;

#if DBG
         switch( Event.Flags & (FEP_TX_LOW | FEP_TX_EMPTY) )
         {
            case FEP_TX_LOW:
               DigiDump( (DIGIEVENT|DIGIWRITE), ("%s:\tTXLOW event\n", DeviceExt->DeviceDbgString) );
               break;
            case FEP_TX_EMPTY:
               DigiDump( (DIGIEVENT|DIGIWRITE), ("%s:\tTXEMPTY event\n", DeviceExt->DeviceDbgString) );
               break;
            default:
               DigiDump( (DIGIEVENT|DIGIWRITE), ("%s:\tTXLOW and TXEMPTY events\n", DeviceExt->DeviceDbgString) );
               break;
         }
#endif

         WriteQueue = &DeviceExt->WriteQueue;

         KeAcquireSpinLockAtDpcLevel( &DeviceExt->ControlAccess );

         if( !IsListEmpty( WriteQueue ) )
         {
            PIRP Irp;

            Irp = CONTAINING_RECORD( WriteQueue->Flink,
                                     IRP,
                                     Tail.Overlay.ListEntry );

            if( Irp->IoStatus.Information != MAXULONG )
            {
               PIO_STACK_LOCATION IrpSp;

               IrpSp = IoGetCurrentIrpStackLocation( Irp );

               if( IrpSp->MajorFunction == IRP_MJ_WRITE
               ||  (  IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL
                   && Irp->IoStatus.Information == 0
                   )
                 )
               {
                  DigiDump( DIGIEVENT, ("---------  WriteQueue list NOT empty\n") );

                  if( IrpSp->MajorFunction == IRP_MJ_WRITE )
                  {
                     ASSERT( Irp->IoStatus.Information < IrpSp->Parameters.Write.Length );
                  }
                  else
                  {
                     ASSERT(  IrpSp->Parameters.DeviceIoControl.IoControlCode ==
                                 IOCTL_SERIAL_IMMEDIATE_CHAR
                           || IrpSp->Parameters.DeviceIoControl.IoControlCode ==
                                 IOCTL_SERIAL_XOFF_COUNTER );
                  }

                  if( WriteTxBuffer( DeviceExt ) == STATUS_SUCCESS )
                  {
                     KIRQL OldIrql = DISPATCH_LEVEL;

                     DigiDump( DIGIEVENT, ("---------  Write complete.  Successfully completing Irp.\n"
                                           "---------  #bytes completing = %d\n",
                                           Irp->IoStatus.Information ) );

                     DIGI_INC_REFERENCE( Irp );
                     DigiTryToCompleteIrp( DeviceExt, &OldIrql,
                                           STATUS_SUCCESS, WriteQueue,
                                           NULL,
                                           &DeviceExt->WriteRequestTotalTimer,
                                           StartWriteRequest );

                     goto WriteDone; // skip unlock
                  } // WriteTxBuffer returned SUCCESS
               } // IRP is eligible for WriteTxBuffer
            } // IRP started
            KeReleaseSpinLockFromDpcLevel( &DeviceExt->ControlAccess );
WriteDone:;
         }
         else // empty(WQ)
         {
            DigiDump( DIGIEVENT, ("---------  WriteQueue was empty\n") );

            if( Event.Flags & FEP_TX_EMPTY )
               EventReason |= SERIAL_EV_TXEMPTY;

            KeReleaseSpinLockFromDpcLevel( &DeviceExt->ControlAccess );
         }
      } // FEP_TX_LOW | FEP_TX_EMPTY

      if( Event.Flags & FEP_RX_PRESENT )
      {
         PLIST_ENTRY ReadQueue;
         PFEP_CHANNEL_STRUCTURE ChInfo;
         USHORT Rin, Rout, Rmax, RxSize;

         DigiDump( DIGIEVENT, ("---------  Rcv Data Present Event: (%s:%d)\n",
                              __FILE__, __LINE__ ) );

GetReceivedData:;

         ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
                                           DeviceExt->ChannelInfo.Offset);

         KeAcquireSpinLockAtDpcLevel( &DeviceExt->ControlAccess );

         EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
         Rout = READ_REGISTER_USHORT( &ChInfo->rout );
         Rin = READ_REGISTER_USHORT( &ChInfo->rin );
         Rmax = READ_REGISTER_USHORT( &ChInfo->rmax );
         DisableWindow( ControllerExt );

         if( (DeviceExt->WaitMask & SERIAL_EV_RXCHAR) &&
             (DeviceExt->PreviousRxChar != (ULONG)Rin) )
         {
            EventReason |= SERIAL_EV_RXCHAR;
         }

         if( (DeviceExt->WaitMask & SERIAL_EV_RXFLAG) &&
             (DeviceExt->UnscannedRXFLAGPosition != (ULONG)Rin) )
         {
            if( ScanReadBufferForSpecialCharacter( DeviceExt,
                                                   DeviceExt->SpecialChars.EventChar ) )
            {
               EventReason |= SERIAL_EV_RXFLAG;
            }
         }

         //
         // Determine if we are waiting to notify a 80% receive buffer
         // full.
         //
         //    NOTE: I assume the controller will continue to notify
         //          us that data is still in the buffer, even if
         //          we don't take the data out of the controller's
         //          buffer.
         //
         if( (DeviceExt->WaitMask & SERIAL_EV_RX80FULL)
         &&  !(DeviceExt->HistoryWait & SERIAL_EV_RX80FULL) ) // notification is already pending
         {
            //
            // Okay, is the receive buffer 80% or more full??
            //
            RxSize = (Rin - Rout) & Rmax;

            if( RxSize )
            {
               if( DeviceExt->SpecialFlags & DIGI_SPECIAL_FLAG_FAST_RAS )
               {
                  if( RxSize >= DeviceExt->ReceiveNotificationLimit )
                  {
                     EventReason |= SERIAL_EV_RX80FULL;
                  }
               }
               else // not RAS
               {
                  // Perform 32-bit math to avoid roundoff errors.
                  if( RxSize >= (USHORT) ( ((ULONG)Rmax + 1UL) * 8UL / 10UL) )
                  {
                     EventReason |= SERIAL_EV_RX80FULL;
                  }
                  else
                  {
                     USHORT RxHighWater;

                     EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
                     RxHighWater = READ_REGISTER_USHORT( &ChInfo->rhigh );
                     DisableWindow( ControllerExt );

                     // If flow control is engaged, trigger the event (we won't get any more data).
                     if( RxSize >= RxHighWater - 1 )
                     {
                        EventReason |= SERIAL_EV_RX80FULL;
                     }
                  }
               } // not RAS
            } // if data
         } // RX80FULL

         ReadQueue = &DeviceExt->ReadQueue;
         if( !IsListEmpty( ReadQueue ) )
         {
            PIRP Irp;

            Irp = CONTAINING_RECORD( ReadQueue->Flink,
                                     IRP,
                                     Tail.Overlay.ListEntry );

            if( DeviceExt->ReadStatus == STATUS_PENDING
            &&  Irp->IoStatus.Information != MAXULONG ) // not started yet
            {
               KIRQL OldIrql = DISPATCH_LEVEL;

               // Hold IRP across lock drop in ReadRxBuffer:ProcessSlowRead:DigiSatisfyWait.
               DIGI_INC_REFERENCE( Irp );

               if( STATUS_SUCCESS == ReadRxBuffer( DeviceExt, &OldIrql ) )
               {
#if DBG
                  if( DigiDebugLevel & DIGIRXTRACE )
                  {
                     PUCHAR Temp;
                     ULONG i;

                     Temp = Irp->AssociatedIrp.SystemBuffer;

                     DigiDump( DIGIRXTRACE, ("Read buffer contains: %s",
                                              DeviceExt->DeviceDbgString) );
                     for( i = 0;
                          i < Irp->IoStatus.Information;
                          i++ )
                     {
                        if( (i & 15) == 0 )
                           DigiDump( DIGIRXTRACE, ( "\n\t") );

                        DigiDump( DIGIRXTRACE, ( "-%02x", Temp[i]) );
                     }
                     DigiDump( DIGIRXTRACE, ("\n") );
                  }
#endif
                  //
                  // We have satisfied this current request, so lets
                  // complete it.
                  //
                  DigiDump( DIGIEVENT, ("---------  Read complete.  Successfully completing Irp.\n") );

                  DigiDump( DIGIEVENT, ("---------  #bytes completing = %d\n",
                                        Irp->IoStatus.Information ) );

                  DeviceExt->ReadStatus = SERIAL_COMPLETE_READ_COMPLETE;

                  DigiTryToCompleteIrp( DeviceExt, &OldIrql,
                                        STATUS_SUCCESS, ReadQueue,
                                        &DeviceExt->ReadRequestIntervalTimer,
                                        &DeviceExt->ReadRequestTotalTimer,
                                        StartReadRequest );

                  goto ReadDone; // skip DEC and unlock
               } // else ReadRxBuffer != SUCCESS
               DIGI_DEC_REFERENCE( Irp );
            } // else ReadStatus != STATUS_PENDING || IRP not started
            KeReleaseSpinLockFromDpcLevel( &DeviceExt->ControlAccess );
ReadDone:;
         }
         else // empty(RQ)
         {
            PSERIAL_XOFF_COUNTER Xc;

            //
            // We don't have an outstanding read request, so make sure
            // we reset the IDATA flag on the controller.
            //
            ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
                                              DeviceExt->ChannelInfo.Offset);

            EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
            WRITE_REGISTER_UCHAR( &ChInfo->idata, TRUE );
            DisableWindow( ControllerExt );

            DigiDump( DIGIEVENT, ("---------  No outstanding read IRP's to place received data.\n") );

            DeviceExt->PreviousRxChar = (ULONG)Rin;

            // The perception of receive data might complete an XOFF_COUNTER on the WriteQueue.
            // Keep track of what we've eaten via XcPreview to avoid counting bytes twice (in ReadRxBuffer).
            Xc = DeviceExt->pXoffCounter;
            if( Xc )
            {
               RxSize = (Rin - Rout) & Rmax;

               if( RxSize < Xc->Counter )
               {
                  DigiDump( (DIGIWRITE|DIGIDIAG1), ("IDATA reduced XOFF_COUNTER\n") );
                  Xc->Counter -= RxSize;
                  DeviceExt->XcPreview += RxSize;
               }
               else
               {
                  // XOFF_COUNTER is complete.

                  KIRQL OldIrql = DISPATCH_LEVEL;
#if DBG
                  Xc->Counter = 0; // Looks a little nicer...
#endif
                  DigiDump( (DIGIWRITE|DIGIDIAG1), ("IDATA on empty(RQ) is completing XOFF_COUNTER\n") );
                  DigiTryToCompleteIrp( DeviceExt, &OldIrql, STATUS_SUCCESS,
                        &DeviceExt->WriteQueue, NULL, &DeviceExt->WriteRequestTotalTimer, StartWriteRequest );
                  goto XcDone; // skip unlock
               }
            }
            KeReleaseSpinLockFromDpcLevel( &DeviceExt->ControlAccess );
XcDone:;
         }
      } // FEP_RX_PRESENT

      if( Event.Flags & FEP_MODEM_CHANGE_SIGNAL )
      {
         ULONG WaitMask;
         UCHAR ChangedModemState;

         ChangedModemState = Event.CurrentModem ^ Event.PreviousModem;

         KeAcquireSpinLockAtDpcLevel( &DeviceExt->ControlAccess );

         WaitMask = DeviceExt->WaitMask;

         DigiDump( (DIGIMODEM|DIGIEVENT|DIGIWAIT),
                     ("---------  Modem Change Event (%s:%d)\t",
                      "   ChangedModemState = 0x%x\n",
                      ChangedModemState,
                      __FILE__, __LINE__ ) );

         if( (WaitMask & SERIAL_EV_CTS) &&
             (ControllerExt->ModemSignalTable[CTS_SIGNAL] & ChangedModemState) )
         {
            EventReason |= SERIAL_EV_CTS;
         }
         if( (WaitMask & SERIAL_EV_DSR) &&
             (ControllerExt->ModemSignalTable[DSR_SIGNAL] & ChangedModemState) )
         {
            EventReason |= SERIAL_EV_DSR;
         }
         if( (WaitMask & SERIAL_EV_RLSD) &&
             (ControllerExt->ModemSignalTable[DCD_SIGNAL] & ChangedModemState) )
         {
            EventReason |= SERIAL_EV_RLSD;
         }
         if( (WaitMask & SERIAL_EV_RING) &&
             (ControllerExt->ModemSignalTable[RI_SIGNAL] & ChangedModemState) )
         {
            EventReason |= SERIAL_EV_RING;
         }

         if( DeviceExt->EscapeChar )
         {
            UCHAR MSRByte, CurrentModemSignals;

            if( DeviceExt->PreviousMSRByte )
               DigiDump( DIGIERRORS, ("   PreviousMSRByte != 0\n") );

            MSRByte = 0;

            if( ControllerExt->ModemSignalTable[CTS_SIGNAL] & ChangedModemState )
            {
               MSRByte |= SERIAL_MSR_DCTS;
            }
            if( ControllerExt->ModemSignalTable[DSR_SIGNAL] & ChangedModemState )
            {
               MSRByte |= SERIAL_MSR_DDSR;
            }
            if( ControllerExt->ModemSignalTable[RI_SIGNAL] & ChangedModemState )
            {
               MSRByte |= SERIAL_MSR_TERI;
            }
            if( ControllerExt->ModemSignalTable[DCD_SIGNAL] & ChangedModemState )
            {
               MSRByte |= SERIAL_MSR_DDCD;
            }

            CurrentModemSignals = DeviceExt->CurrentModemSignals;
            if( ControllerExt->ModemSignalTable[CTS_SIGNAL] & CurrentModemSignals )
            {
               MSRByte |= SERIAL_MSR_CTS;
            }
            if( ControllerExt->ModemSignalTable[DSR_SIGNAL] & CurrentModemSignals )
            {
               MSRByte |= SERIAL_MSR_DSR;
            }
            if( ControllerExt->ModemSignalTable[RI_SIGNAL] & CurrentModemSignals )
            {
               MSRByte |= SERIAL_MSR_RI;
            }
            if( ControllerExt->ModemSignalTable[DCD_SIGNAL] & CurrentModemSignals )
            {
               MSRByte |= SERIAL_MSR_DCD;
            }

            if( !IsListEmpty( &DeviceExt->ReadQueue ) )
            {
               PIRP Irp;
               PIO_STACK_LOCATION IrpSp;

               Irp = CONTAINING_RECORD( DeviceExt->ReadQueue.Flink,
                                        IRP,
                                        Tail.Overlay.ListEntry );

               IrpSp = IoGetCurrentIrpStackLocation( Irp );

               if( (IrpSp->Parameters.Read.Length - Irp->IoStatus.Information) > 3 )
               {
                  PUCHAR ReadBuffer;

                  ReadBuffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer +
                               Irp->IoStatus.Information;

                  ReadBuffer[0] = DeviceExt->EscapeChar;
                  ReadBuffer[1] = SERIAL_LSRMST_MST;
                  ReadBuffer[2] = MSRByte;
                  Irp->IoStatus.Information += 3;

                  DeviceExt->PreviousMSRByte = 0;

                  DigiDump( DIGIMODEM, ("      CurrentModemSignals = 0x%x\n"
                                        "      ChangedModemState = 0x%x\n"
                                        "      MSRByte = 0x%x\n",
                                        DeviceExt->CurrentModemSignals,
                                        ChangedModemState,
                                        MSRByte) );
               }
               else
               {
                  DigiDump( (DIGIMODEM|DIGIERRORS),
                            ("Insufficient read IRP space available to record modem status change!\n") );
                  DeviceExt->PreviousMSRByte = MSRByte;
               }
            }
            else
            {
               DigiDump( (DIGIMODEM|DIGIERRORS),
                         ("No read IRP in which to record modem status change!\n") );
               DeviceExt->PreviousMSRByte = MSRByte;
            }
            KeReleaseSpinLockFromDpcLevel( &DeviceExt->ControlAccess );

            //
            // We need to read any data which may be available.
            //
            Event.Flags &= ~FEP_MODEM_CHANGE_SIGNAL;
            goto GetReceivedData;
         }
         else
         {
            KeReleaseSpinLockFromDpcLevel( &DeviceExt->ControlAccess );
         }
      } // FEP_MODEM_CHANGE_SIGNAL

      if( Event.Flags & FEP_RECEIVE_BUFFER_OVERRUN )
      {
         KeAcquireSpinLockAtDpcLevel( &DeviceExt->ControlAccess );
         DeviceExt->ErrorWord |= SERIAL_ERROR_QUEUEOVERRUN;
         InterlockedIncrement(&DeviceExt->PerfData.BufferOverrunErrorCount);
         KeReleaseSpinLockFromDpcLevel( &DeviceExt->ControlAccess );
      }

      if( Event.Flags & FEP_UART_RECEIVE_OVERRUN )
      {
         KeAcquireSpinLockAtDpcLevel( &DeviceExt->ControlAccess );
         DeviceExt->ErrorWord |= SERIAL_ERROR_OVERRUN;
         InterlockedIncrement(&DeviceExt->PerfData.SerialOverrunErrorCount);
         KeReleaseSpinLockFromDpcLevel( &DeviceExt->ControlAccess );
      }

      if( EventReason )
         DigiSatisfyEvent( ControllerExt, DeviceExt, EventReason );

   }

   //
   // Regardless of whether we processed the event, make sure we forward
   // the event out pointer.
   //
   EnableWindow( ControllerExt, ControllerExt->Global.Window );

   WRITE_REGISTER_USHORT( (PUSHORT)((PUCHAR)ControllerExt->VirtualAddress+FEP_EOUT),
                          Eout );

   DisableWindow( ControllerExt );

   DigiDump( (DIGIFLOW|DIGIEVENT), ("Exiting DigiServiceEvent\n") );

}  // DigiServiceEvent