private static SerialPinChange SignalsToPinChanges(Signals signals)
        {
            SerialPinChange pinChanges = default;

            if (signals.HasFlag(Signals.SignalCts))
            {
                pinChanges |= SerialPinChange.CtsChanged;
            }

            if (signals.HasFlag(Signals.SignalDsr))
            {
                pinChanges |= SerialPinChange.DsrChanged;
            }

            if (signals.HasFlag(Signals.SignalDcd))
            {
                pinChanges |= SerialPinChange.CDChanged;
            }

            if (signals.HasFlag(Signals.SignalRng))
            {
                pinChanges |= SerialPinChange.Ring;
            }

            return(pinChanges);
        }
Exemple #2
0
        private void NotifyPinChanges(Signals signals)
        {
            if (signals.HasFlag(Signals.SignalCts))
            {
                RaisePinChanged(SerialPinChange.CtsChanged);
            }

            if (signals.HasFlag(Signals.SignalDsr))
            {
                RaisePinChanged(SerialPinChange.DsrChanged);
            }

            if (signals.HasFlag(Signals.SignalDcd))
            {
                RaisePinChanged(SerialPinChange.CDChanged);
            }

            if (signals.HasFlag(Signals.SignalRng))
            {
                RaisePinChanged(SerialPinChange.Ring);
            }
        }
        private void IOLoop()
        {
            bool eofReceived = false;
            // we do not care about bytes we got before - only about changes
            // loop just got started which means we just got request
            bool lastIsIdle           = false;
            int  ticksWhenIdleStarted = 0;

            Signals lastSignals = _pinChanged != null?Interop.Termios.TermiosGetAllSignals(_handle) : Signals.Error;

            bool IsNoEventRegistered() => _dataReceived == null && _pinChanged == null;

            while (IsOpen && !eofReceived && !_ioLoopFinished)
            {
                bool hasPendingReads  = !_readQueue.IsEmpty;
                bool hasPendingWrites = !_writeQueue.IsEmpty;

                bool hasPendingIO = hasPendingReads || hasPendingWrites;
                bool isIdle       = IsNoEventRegistered() && !hasPendingIO;

                if (!hasPendingIO)
                {
                    if (isIdle)
                    {
                        if (!lastIsIdle)
                        {
                            // we've just started idling
                            ticksWhenIdleStarted = Environment.TickCount;
                        }
                        else if (Environment.TickCount - ticksWhenIdleStarted > IOLoopIdleTimeout)
                        {
                            // we are already idling for a while
                            // let's stop the loop until there is some work to do

                            lock (_ioLoopLock)
                            {
                                // double check we are done under lock
                                if (IsNoEventRegistered() && _readQueue.IsEmpty && _writeQueue.IsEmpty)
                                {
                                    _ioLoop = null;
                                    break;
                                }
                                else
                                {
                                    // to make sure timer restarts
                                    lastIsIdle = false;
                                    continue;
                                }
                            }
                        }
                    }

                    Thread.Sleep(1);
                }
                else
                {
                    Interop.PollEvents events = PollEvents(1,
                                                           pollReadEvents: hasPendingReads,
                                                           pollWriteEvents: hasPendingWrites,
                                                           out Interop.ErrorInfo? error);

                    if (error.HasValue)
                    {
                        FinishPendingIORequests(error);
                        break;
                    }

                    if (events.HasFlag(Interop.PollEvents.POLLNVAL) ||
                        events.HasFlag(Interop.PollEvents.POLLERR))
                    {
                        // bad descriptor or some other error we can't handle
                        FinishPendingIORequests();
                        break;
                    }

                    if (events.HasFlag(Interop.PollEvents.POLLIN))
                    {
                        int bytesRead = DoIORequest(_readQueue, _processReadDelegate);
                        _totalBytesRead += bytesRead;
                    }

                    if (events.HasFlag(Interop.PollEvents.POLLOUT))
                    {
                        DoIORequest(_writeQueue, _processWriteDelegate);
                    }
                }

                // check if there is any new data (either already read or in the driver input)
                // this event is private and handled inside of SerialPort
                // which then throttles it with the threshold
                long totalBytesAvailable = TotalBytesAvailable;
                if (totalBytesAvailable > _lastTotalBytesAvailable)
                {
                    _lastTotalBytesAvailable = totalBytesAvailable;
                    RaiseDataReceivedChars();
                }

                if (_pinChanged != null)
                {
                    // Checking for changes could technically speaking be done by waiting with ioctl+TIOCMIWAIT
                    // This would require spinning new thread and also would potentially trigger events when
                    // user didn't have time to respond.
                    // Diffing seems like a better solution.
                    Signals current = Interop.Termios.TermiosGetAllSignals(_handle);

                    // There is no really good action we can take when this errors so just ignore
                    // a sinle event.
                    if (current != Signals.Error && lastSignals != Signals.Error)
                    {
                        Signals changed = current ^ lastSignals;
                        if (changed != Signals.None)
                        {
                            SerialPinChange pinChanged = SignalsToPinChanges(changed);
                            RaisePinChanged(pinChanged);
                        }
                    }

                    lastSignals = current;
                }

                lastIsIdle = isIdle;
            }
        }