* SerialPort.cs:
[mono.git] / mcs / class / System / System.IO.Ports / WinSerialStream.cs
index d333df37ef5d0f7e623d66bdbcb065126e814eb4..23958f562df8f2e11ccdc30a372463b6c6b630d0 100644 (file)
@@ -1,5 +1,13 @@
+//
+// System.IO.Ports.WinSerialStream.cs
+//
+// Authors:
+//     Carlos Alberto Cortez (calberto.cortez@gmail.com)
+//
+// (c) Copyright 2006 Novell, Inc. (http://www.novell.com)
+//
+
 using System;
-using System.Collections.Generic;
 using System.Text;
 using System.IO;
 using System.Runtime.InteropServices;
@@ -8,9 +16,9 @@ using System.ComponentModel;
 
 #if NET_2_0
 
-namespace Mono.System.IO.Ports
+namespace System.IO.Ports
 {
-       public class WinSerialStream : Stream, IDisposable
+       class WinSerialStream : Stream, ISerialStream, IDisposable
        {
                // Windows API Constants
                const uint GenericRead = 0x80000000;
@@ -19,192 +27,416 @@ namespace Mono.System.IO.Ports
                const uint FileFlagOverlapped = 0x40000000;
                const uint PurgeRxClear = 0x0004;
                const uint PurgeTxClear = 0x0008;
+               const uint WinInfiniteTimeout = 0xFFFFFFFF;
+               const uint FileIOPending = 997;
 
                int handle;
+               int read_timeout;
+               int write_timeout;
                bool disposed;
+               IntPtr write_overlapped;
+               IntPtr read_overlapped;
+               ManualResetEvent read_event;
+               ManualResetEvent write_event;
+               Timeouts timeouts;
 
-               [DllImport("kernel32", SetLastError=true)]
+               [DllImport("kernel32", SetLastError = true)]
                static extern int CreateFile(string port_name, uint desired_access,
-                       uint share_mode, uint security_attrs, uint creation, uint flags,
-                       uint template);
+                               uint share_mode, uint security_attrs, uint creation, uint flags,
+                               uint template);
 
-               [DllImport("kernel32", SetLastError=true)]
+               [DllImport("kernel32", SetLastError = true)]
                static extern bool SetupComm(int handle, int read_buffer_size, int write_buffer_size);
 
                [DllImport("kernel32", SetLastError = true)]
                static extern bool PurgeComm(int handle, uint flags);
 
-               public unsafe WinSerialStream(string port_name, int read_buffer_size, int write_buffer_size)
+               [DllImport("kernel32", SetLastError = true)]
+               static extern bool SetCommTimeouts(int handle, Timeouts timeouts);
+
+               public WinSerialStream (string port_name, int baud_rate, int data_bits, Parity parity,
+                               StopBits sb, Handshake hs, int read_timeout, int write_timeout,
+                               int read_buffer_size, int write_buffer_size)
                {
-                       handle = CreateFile(port_name, GenericRead | GenericWrite, 0, 0, OpenExisting,
+                       handle = CreateFile (port_name, GenericRead | GenericWrite, 0, 0, OpenExisting,
                                        FileFlagOverlapped, 0);
 
                        if (handle == -1)
-                               throw new Win32Exception ();
-
-                       // Clean buffers
-                       if (!PurgeComm(handle, PurgeRxClear | PurgeTxClear))
-                               throw new Win32Exception();
-
-                       // Set buffers size
-                       if (!SetupComm(handle, read_buffer_size, write_buffer_size))
-                               throw new Win32Exception();
+                               ReportIOError (port_name);
+
+                       // Set port low level attributes
+                       SetAttributes (baud_rate, parity, data_bits, sb, hs);
+
+                       // Clean buffers and set sizes
+                       if (!PurgeComm (handle, PurgeRxClear | PurgeTxClear) ||
+                                       !SetupComm (handle, read_buffer_size, write_buffer_size))
+                               ReportIOError (null);
+
+                       // Set timeouts
+                       this.read_timeout = read_timeout;
+                       this.write_timeout = write_timeout;
+                       timeouts = new Timeouts (read_timeout, write_timeout);
+                       if (!SetCommTimeouts(handle, timeouts))
+                               ReportIOError (null);
+
+                       // Init overlapped structures
+                       NativeOverlapped wo = new NativeOverlapped ();
+                       write_event = new ManualResetEvent (false);
+                       wo.EventHandle = (int) write_event.Handle;
+                       write_overlapped = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (NativeOverlapped)));
+                       Marshal.StructureToPtr (wo, write_overlapped, true);
+
+                       NativeOverlapped ro = new NativeOverlapped ();
+                       read_event = new ManualResetEvent (false);
+                       ro.EventHandle = (int) read_event.Handle;
+                       read_overlapped = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (NativeOverlapped)));
+                       Marshal.StructureToPtr (ro, read_overlapped, true);
                }
 
-               public override bool CanRead
-               {
+               public override bool CanRead {
                        get {
                                return true;
                        }
                }
 
-               public override bool CanSeek
-               {
+               public override bool CanSeek {
                        get {
                                return false;
                        }
                }
 
-               public override bool CanTimeout
-               {
-                       get
-                       {
+               public override bool CanTimeout {
+                       get {
                                return true;
                        }
                }
 
-               public override bool CanWrite
-               {
+               public override bool CanWrite {
                        get {
                                return true;
                        }
                }
 
-               public override long Length
-               {
-                       get
-                       {
-                               throw new NotSupportedException();
+               public override int ReadTimeout {
+                       get {
+                               return read_timeout;
+                       }
+                       set {
+                               if (value < 0 && value != SerialPort.InfiniteTimeout)
+                                       throw new ArgumentOutOfRangeException ("value");
+
+                               timeouts.SetValues (value, write_timeout);
+                               if (!SetCommTimeouts (handle, timeouts))
+                                       ReportIOError (null);
+
+                               read_timeout = value;
                        }
                }
 
-               public override long Position
-               {
-                       get
-                       {
-                               throw new NotSupportedException();
+               public override int WriteTimeout {
+                       get {
+                               return write_timeout;
                        }
                        set
                        {
-                               throw new Exception("The method or operation is not implemented.");
+                               if (value < 0 && value != SerialPort.InfiniteTimeout)
+                                       throw new ArgumentOutOfRangeException ("value");
+
+                               timeouts.SetValues (read_timeout, value);
+                               if (!SetCommTimeouts (handle, timeouts))
+                                       ReportIOError (null);
+
+                               write_timeout = value;
+                       }
+               }
+
+               public override long Length {
+                       get {
+                               throw new NotSupportedException ();
                        }
                }
 
-               [DllImport("kernel32", SetLastError=true)]
-               static extern bool CloseHandle(int handle);
+               public override long Position {
+                       get {
+                               throw new NotSupportedException ();
+                       }
+                       set {
+                               throw new NotSupportedException ();
+                       }
+               }
+
+               [DllImport("kernel32", SetLastError = true)]
+               static extern bool CloseHandle (int handle);
 
-               protected override void Dispose(bool disposing)
+               protected override void Dispose (bool disposing)
                {
                        if (disposed)
-                               return; 
+                               return;
 
                        disposed = true;
-                       CloseHandle(handle);
+                       CloseHandle (handle);
+                       Marshal.FreeHGlobal (write_overlapped);
+                       Marshal.FreeHGlobal (read_overlapped);
                }
 
                void IDisposable.Dispose ()
                {
-                       Dispose(true);
-                       GC.SuppressFinalize(this);
+                       Dispose (true);
+                       GC.SuppressFinalize (this);
                }
 
-               public override void Close()
+               public override void Close ()
                {
-                       ((IDisposable)this).Dispose();
+                       ((IDisposable)this).Dispose ();
                }
 
-               ~WinSerialStream()
+               ~WinSerialStream ()
                {
-                       Dispose(false);
+                       Dispose (false);
                }
 
-               public override void Flush()
+               public override void Flush ()
                {
-                       CheckDisposed();
+                       CheckDisposed ();
                        // No dothing by now
                }
 
-               public override long Seek(long offset, SeekOrigin origin)
+               public override long Seek (long offset, SeekOrigin origin)
                {
                        throw new NotSupportedException();
                }
 
-               public override void SetLength(long value)
+               public override void SetLength (long value)
                {
                        throw new NotSupportedException();
                }
 
-               [DllImport("kernel32", SetLastError=true)]
-               static extern unsafe bool ReadFile(int handle, byte *buffer, int bytes_to_read,
-                       int *read_bytes, int overlapped);
+               [DllImport("kernel32", SetLastError = true)]
+                       static extern unsafe bool ReadFile (int handle, byte* buffer, int bytes_to_read,
+                                       out int bytes_read, IntPtr overlapped);
+
+               [DllImport("kernel32", SetLastError = true)]
+                       static extern unsafe bool GetOverlappedResult (int handle, IntPtr overlapped,
+                                       ref int bytes_transfered, bool wait);
 
-               public override unsafe int Read(byte[] buffer, int offset, int count)
+               public override int Read ([In, Out] byte [] buffer, int offset, int count)
                {
-                       CheckDisposed();
+                       CheckDisposed ();
                        if (buffer == null)
-                               throw new ArgumentNullException("buffer");
+                               throw new ArgumentNullException ("buffer");
                        if (offset < 0 || offset >= buffer.Length)
-                               throw new ArgumentOutOfRangeException("offset");
+                               throw new ArgumentOutOfRangeException ("offset");
                        if (count < 0 || count > buffer.Length)
-                               throw new ArgumentOutOfRangeException("count");
+                               throw new ArgumentOutOfRangeException ("count");
                        if (count > buffer.Length - offset)
-                               throw new ArgumentException();
-
-                       int bytes_read = 0;
-                       bool success;
-
-                       fixed (byte *ptr = buffer)
-                       {
-                               success = ReadFile(handle, ptr + offset, count, &bytes_read, 0);
+                               throw new ArgumentException ();
+
+                       int bytes_read;
+
+                       unsafe {
+                               fixed (byte* ptr = buffer) {
+                                       if (ReadFile (handle, ptr + offset, count, out bytes_read, read_overlapped))
+                                               return bytes_read;
+                               
+                                       // Test for overlapped behavior
+                                       if (Marshal.GetLastWin32Error () != FileIOPending)
+                                               ReportIOError (null);
+                               
+                                       if (!GetOverlappedResult (handle, read_overlapped, ref bytes_read, true))
+                                               ReportIOError (null);
+                       
+                               }
                        }
 
-                       if (!success)
-                               throw new Win32Exception();
+                       if (bytes_read == 0)
+                               throw new TimeoutException (); // We didn't get any byte
 
                        return bytes_read;
                }
 
                [DllImport("kernel32", SetLastError = true)]
-               static extern unsafe bool WriteFile(int handle, byte* buffer, int bytes_to_write,
-                       int* bytes_written, int overlapped);
+               static extern unsafe bool WriteFile (int handle, byte* buffer, int bytes_to_write,
+                               out int bytes_written, IntPtr overlapped);
 
-               public override unsafe void Write(byte[] buffer, int offset, int count)
+               public override void Write (byte [] buffer, int offset, int count)
                {
-                       CheckDisposed();
+                       CheckDisposed ();
                        if (buffer == null)
-                               throw new ArgumentNullException("buffer");
+                               throw new ArgumentNullException ("buffer");
                        if (offset < 0 || offset >= buffer.Length)
-                               throw new ArgumentOutOfRangeException("offset");
+                               throw new ArgumentOutOfRangeException ("offset");
                        if (count < 0 || count > buffer.Length)
-                               throw new ArgumentOutOfRangeException("count");
+                               throw new ArgumentOutOfRangeException ("count");
                        if (count > buffer.Length - offset)
-                               throw new ArgumentException("count > buffer.Length - offset");
+                               throw new ArgumentException ("count > buffer.Length - offset");
 
                        int bytes_written = 0;
-                       bool success;
-                       fixed (byte* ptr = buffer)
-                       {
-                               success = WriteFile(handle, ptr + offset, count, &bytes_written, 0);
+
+                       unsafe {
+                               fixed (byte* ptr = buffer) {
+                                       if (WriteFile (handle, ptr + offset, count, out bytes_written, write_overlapped))
+                                               return;
+                                       if (Marshal.GetLastWin32Error() != FileIOPending)
+                                               ReportIOError (null);
+                                       
+                                       if (!GetOverlappedResult(handle, write_overlapped, ref bytes_written, true))
+                                               ReportIOError (null);
+                               }
+                       }
+
+                       // If the operation timed out, then
+                       // we transfered less bytes than the requested ones
+                       if (bytes_written < count)
+                               throw new TimeoutException ();
+               }
+
+               [DllImport("kernel32", SetLastError = true)]
+               static extern bool GetCommState (int handle, [Out] DCB dcb);
+
+               [DllImport ("kernel32", SetLastError=true)]
+               static extern bool SetCommState (int handle, DCB dcb);
+
+               public void SetAttributes (int baud_rate, Parity parity, int data_bits, StopBits bits, Handshake hs)
+               {
+                       DCB dcb = new DCB ();
+                       if (!GetCommState (handle, dcb))
+                               ReportIOError (null);
+
+                       dcb.SetValues (baud_rate, parity, data_bits, bits, hs);
+                       if (!SetCommState (handle, dcb))
+                               ReportIOError (null);
+               }
+
+               void ReportIOError(string optional_arg)
+               {
+                       int error = Marshal.GetLastWin32Error ();
+                       string message;
+                       switch (error) {
+                               case 2:
+                               case 3:
+                                       message = "The port `" + optional_arg + "' does not exist.";
+                                       break;
+                               case 87:
+                                       message = "Parameter is incorrect.";
+                                       break;
+                               default:
+                                       // As fallback, we show the win32 error
+                                       message = new Win32Exception ().Message;
+                                       break;
                        }
 
-                       if (!success)
-                               throw new Win32Exception();
+                       throw new IOException (message);
                }
 
-               void CheckDisposed()
+               void CheckDisposed ()
                {
                        if (disposed)
-                               throw new ObjectDisposedException(GetType().FullName);
+                               throw new ObjectDisposedException (GetType ().FullName);
+               }
+
+               // ISerialStream members
+               public void DiscardInBuffer ()
+               {
+                       throw new NotImplementedException ();
+               }
+
+               public void DiscardOutBuffer ()
+               {
+                       throw new NotImplementedException ();
+               }
+
+               public int BytesToRead {
+                       get {
+                               throw new NotImplementedException ();
+                       }
+               }
+
+               public int BytesToWrite {
+                       get {
+                               throw new NotImplementedException ();
+                       }
+               }
+
+               public SerialSignal GetSignals ()
+               {
+                       throw new NotImplementedException ();
+               }
+               
+               public void SetSignal (SerialSignal signal, bool value)
+               {
+                       throw new NotImplementedException ();
+               }
+
+       }
+       
+       [StructLayout (LayoutKind.Sequential)]
+       class DCB
+       {
+               public int dcb_length;
+               public int baud_rate;
+               public int flags;
+               public short w_reserved;
+               public short xon_lim;
+               public short xoff_lim;
+               public byte byte_size;
+               public byte parity;
+               public byte stop_bits;
+               public byte xon_char;
+               public byte xoff_char;
+               public byte error_char;
+               public byte eof_char;
+               public byte evt_char;
+               public short w_reserved1;
+
+               public void SetValues (int baud_rate, Parity parity, int byte_size, StopBits sb, Handshake hs)
+               {
+                       switch (sb) {
+                               case StopBits.One:
+                                       stop_bits = 0;
+                                       break;
+                               case StopBits.OnePointFive:
+                                       stop_bits = 1;
+                                       break;
+                               case StopBits.Two:
+                                       stop_bits = 2;
+                                       break;
+                               default: // Shouldn't happen
+                                       break;
+                       }
+
+                       this.baud_rate = baud_rate;
+                       this.parity = (byte) parity;
+                       this.byte_size = (byte) byte_size;
+               }
+
+       }
+       
+       [StructLayout (LayoutKind.Sequential)]
+       class Timeouts
+       {
+               public uint ReadIntervalTimeout;
+               public uint ReadTotalTimeoutMultiplier;
+               public uint ReadTotalTimeoutConstant;
+               public uint WriteTotalTimeoutMultiplier;
+               public uint WriteTotalTimeoutConstant;
+
+               public const uint MaxDWord = 0xFFFFFFFF;
+
+               public Timeouts (int read_timeout, int write_timeout)
+               {
+                       SetValues (read_timeout, write_timeout);
+               }
+
+               public void SetValues (int read_timeout, int write_timeout)
+               {
+                       // FIXME: The windows api docs are not very clear about read timeouts,
+                       // and we have to simulate infinite with a big value (uint.MaxValue - 1)
+                       ReadIntervalTimeout = MaxDWord;
+                       ReadTotalTimeoutMultiplier = MaxDWord;
+                       ReadTotalTimeoutConstant = (read_timeout == -1 ? MaxDWord - 1 : (uint) read_timeout);
+
+                       WriteTotalTimeoutMultiplier = 0;
+                       WriteTotalTimeoutConstant = (write_timeout == -1 ? MaxDWord : (uint) write_timeout);
                }
 
        }