/// <summary> /// Produce the number of bytes read in the buffer. /// </summary> /// <remarks> /// If the number of bytes read is zero, this function should also be called, as it indicates /// that there are no more bytes pending. The recommendation from MS documentation for reading /// from the serial port indicates to read the buffer data, until a result of zero is given, /// which indicates to wait for the next receiving character. /// </remarks> /// <param name="bytes">Number of bytes read.</param> private void ProcessReadEvent(uint bytes) { m_Trace.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 0, "{0}: SerialThread: ProcessReadEvent: {1} bytes", Name, bytes); if (bytes == 0) { m_ReadByteAvailable = false; } else { lock (m_ReadLock) { if (m_Trace.Switch.ShouldTrace(System.Diagnostics.TraceEventType.Verbose)) { // Converting everything to strings before the call is expensive. m_Trace.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 0, "{0}: SerialThread: ProcessReadEvent: End={1}; Bytes={2}", Name, m_Buffers.ReadBuffer.End, bytes); } m_Buffers.ReadBuffer.Produce((int)bytes); if (m_Buffers.ReadBuffer.Free == 0) { m_ReadBufferNotFullEvent.Reset(); } m_ReadBufferNotEmptyEvent.Set(); m_ReadBufferEvent.Set(); } bool eof = (m_ReadByteEof & EofByte.InBuffer) != 0; OnCommEvent(eof ? NativeMethods.SerialEventMask.EV_RXFLAG : NativeMethods.SerialEventMask.EV_RXCHAR); // The EofByte is so designed, that we can use a bit shift to get to the next state. // We don't lock this, as only the Serial I/O thread reads/writes this flag. m_ReadByteEof = (EofByte)(((int)m_ReadByteEof << 1) & 0x03); } }
/// <summary> /// Do work based on the mask event that has occurred. /// </summary> /// <param name="mask">The mask that was provided.</param> private void ProcessWaitCommEvent(NativeMethods.SerialEventMask mask) { if (mask != (int)0) { m_Trace.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 0, "{0}: SerialThread: ProcessWaitCommEvent: {1}", Name, mask); } // Reading a character if ((mask & NativeMethods.SerialEventMask.EV_RXCHAR) != 0) { m_ReadByteAvailable = true; } if ((mask & NativeMethods.SerialEventMask.EV_RXFLAG) != 0) { m_ReadByteAvailable = true; // An EOF character may already be in our buffer. We don't lock this, as only // the Serial I/O thread reads/writes this flag. m_ReadByteEof |= EofByte.InDriver; } // We don't raise an event for characters immediately, but only after the read operation // is complete. OnCommEvent(mask & ~(NativeMethods.SerialEventMask.EV_RXCHAR | NativeMethods.SerialEventMask.EV_RXFLAG)); if ((mask & NativeMethods.SerialEventMask.EV_TXEMPTY) != 0) { lock (m_WriteLock) { if (m_Buffers.WriteBuffer.Length == 0) { m_Trace.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 0, "{0}: SerialThread: ProcessWaitCommEvent: TX-BUFFER empty", Name); m_TxBufferEmpty.Set(); m_TxEmptyEvent = false; } else { // Because the main event loop handles CommEvents before WriteEvents, it could be // that a write event occurs immediately after, actually emptying the buffer. m_TxEmptyEvent = true; } } } if ((mask & (NativeMethods.SerialEventMask.EV_RXCHAR | NativeMethods.SerialEventMask.EV_ERR)) != 0) { NativeMethods.ComStatErrors comErr; bool result = UnsafeNativeMethods.ClearCommError(m_ComPortHandle, out comErr, IntPtr.Zero); int e = Marshal.GetLastWin32Error(); if (result) { comErr = (NativeMethods.ComStatErrors)((int)comErr & 0x10F); if (comErr != 0) { OnCommErrorEvent(comErr); } } else { m_Trace.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 0, "{0}: SerialThread: ClearCommError: WINERROR {1}", Name, e); } } }