context: //
  // System.IO.Ports.SerialPortStream.cs
  //
  // Authors:
  //	Chris Toshok (toshok@ximian.com)
  //	Carlos Alberto Cortez (calberto.cortez@gmail.com)
  //
  // (c) Copyright 2006 Novell, Inc. (http://www.novell.com)
  //
  // Slightly modified by Konrad M. Kruczynski (added baud rate value checking)
  
  
  using System;
  using System.IO;
  using System.Runtime.InteropServices;
  
  namespace System.IO.Ports
  {
  	class SerialPortStream : Stream, ISerialStream, IDisposable
  	{
  		int fd;
  		int read_timeout;
  		int write_timeout;
  		bool disposed;
  
  		[DllImport ("MonoPosixHelper", SetLastError = true)]
  		static extern int open_serial (string portName);
  
  		public SerialPortStream (string portName, int baudRate, int dataBits, Parity parity, StopBits stopBits,
  				bool dtrEnable, bool rtsEnable, Handshake handshake, int readTimeout, int writeTimeout,
  				int readBufferSize, int writeBufferSize)
  		{
  			fd = open_serial (portName);
  			if (fd == -1)
  				ThrowIOException ();
  				
  			TryBaudRate (baudRate);
  			
  			if (!set_attributes (fd, baudRate, parity, dataBits, stopBits, handshake))
  				ThrowIOException (); // Probably Win32Exc for compatibility
  
  			read_timeout = readTimeout;
  			write_timeout = writeTimeout;
  			
  			SetSignal (SerialSignal.Dtr, dtrEnable);
  			
  			if (handshake != Handshake.RequestToSend && 
  					handshake != Handshake.RequestToSendXOnXOff)
  				SetSignal (SerialSignal.Rts, rtsEnable);
  		}
  
  		public override bool CanRead {
  			get {
  				return true;
  			}
  		}
  
  		public override bool CanSeek {
  			get {
  				return false;
  			}
  		}
  
  		public override bool CanWrite {
  			get {
  				return true;
  			}
  		}
  
  		public override bool CanTimeout {
  			get {
  				return true;
  			}
  		}
  
  		public override int ReadTimeout {
  			get {
  				return read_timeout;
  			}
  			set {
  				if (value < 0 && value != SerialPort.InfiniteTimeout)
  					throw new ArgumentOutOfRangeException ("value");
  
  				read_timeout = value;
  			}
  		}
  
  		public override int WriteTimeout {
  			get {
  				return write_timeout;
  			}
  			set {
  				if (value < 0 && value != SerialPort.InfiniteTimeout)
  					throw new ArgumentOutOfRangeException ("value");
  
  				write_timeout = value;
  			}
  		}
  
  		public override long Length {
  			get {
  				throw new NotSupportedException ();
  			}
  		}
  
  		public override long Position {
  			get {
  				throw new NotSupportedException ();
  			}
  			set {
  				throw new NotSupportedException ();
  			}
  		}
  
  		public override void Flush ()
  		{
  			// If used, this _could_ flush the serial port
  			// buffer (not the SerialPort class buffer)
  		}
  
  		[DllImport ("MonoPosixHelper", SetLastError = true)]
  		static extern int read_serial (int fd, byte [] buffer, int offset, int count);
  		
  
  		[DllImport ("MonoPosixHelper", SetLastError = true)]
  		static extern bool poll_serial (int fd, out int error, int timeout);
  
  		public override int Read ([In,Out] byte[] buffer, int offset, int count)
  		{
  			CheckDisposed ();
  			if (buffer == null)
  				throw new ArgumentNullException ("buffer");
  			if (offset < 0 || count < 0)
  				throw new ArgumentOutOfRangeException ("offset or count less than zero.");
  
  			if (buffer.Length - offset < count )
  				throw new ArgumentException ("offset+count",
  							      "The size of the buffer is less than offset + count.");
  			
  			int error;
  			bool poll_result = poll_serial (fd, out error, read_timeout);
  			if (error == -1)
  				ThrowIOException ();
  
  			if (!poll_result) {
  				// see bug 79735   http://bugzilla.ximian.com/show_bug.cgi?id=79735
  				// should the next line read: return -1; 
  				throw new TimeoutException();
  			}
  
  			int result = read_serial (fd, buffer, offset, count);
  			if (result == -1)
  				ThrowIOException ();
  			return result;
  		}
  
  		public override long Seek (long offset, SeekOrigin origin)
  		{
  			throw new NotSupportedException ();
  		}
  
  		public override void SetLength (long value)
  		{
  			throw new NotSupportedException ();
  		}
  
  		[DllImport ("MonoPosixHelper", SetLastError = true)]
  		static extern int write_serial (int fd, byte [] buffer, int offset, int count, int timeout);
  
  		public override void Write (byte[] buffer, int offset, int count)
  		{
  			CheckDisposed ();
  			if (buffer == null)
  				throw new ArgumentNullException ("buffer");
  
  			if (offset < 0 || count < 0)
  				throw new ArgumentOutOfRangeException ();
  
  			if (buffer.Length - offset < count)
  				throw new ArgumentException ("offset+count",
  							     "The size of the buffer is less than offset + count.");
  
  			// FIXME: this reports every write error as timeout
  			if (write_serial (fd, buffer, offset, count, write_timeout) < 0)
  				throw new TimeoutException("The operation has timed-out");
  		}
  
  		protected override void Dispose (bool disposing)
  		{
  			if (disposed)
  				return;
  			
  			disposed = true;
  			if (close_serial (fd) != 0)
  				ThrowIOException();
  		}
  
  		[DllImport ("MonoPosixHelper", SetLastError = true)]
  		static extern int close_serial (int fd);
  
  		public override void Close ()
  		{
  			((IDisposable) this).Dispose ();
  		}
  
  		void IDisposable.Dispose ()
  		{
  			Dispose (true);
  			GC.SuppressFinalize (this);
  		}
  
  		~SerialPortStream ()
  		{
  			Dispose (false);
  		}
  
  		void CheckDisposed ()
  		{
  			if (disposed)
  				throw new ObjectDisposedException (GetType ().FullName);
  		}
  
  		[DllImport ("MonoPosixHelper", SetLastError = true)]
  		static extern bool set_attributes (int fd, int baudRate, Parity parity, int dataBits, StopBits stopBits, Handshake handshake);
  
  		public void SetAttributes (int baud_rate, Parity parity, int data_bits, StopBits sb, Handshake hs)
  		{
  			if (!set_attributes (fd, baud_rate, parity, data_bits, sb, hs))
  				ThrowIOException ();
  		}
  
  		[DllImport("MonoPosixHelper", SetLastError = true)]
  		static extern int get_bytes_in_buffer (int fd, int input);
  		
  		public int BytesToRead {
  			get {
  				int result = get_bytes_in_buffer (fd, 1);
  				if (result == -1)
  					ThrowIOException ();
  				return result;
  			}
  		}
  
  		public int BytesToWrite {
  			get {
  				int result = get_bytes_in_buffer (fd, 0);
  				if (result == -1)
  					ThrowIOException ();
  				return result;
  			}
  		}
  
  		[DllImport ("MonoPosixHelper", SetLastError = true)]
  		static extern int discard_buffer (int fd, bool inputBuffer);
  
  		public void DiscardInBuffer ()
  		{
  
gt: 			if (discard_buffer (fd, true) != 0)