2007-10-22 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System / System.IO.Ports / WinSerialStream.cs
index 91b8c44151f0a730462efbfc388cb892cfb4eeb9..916d45e37e4cda0c79e004d45f20acf545ae2f89 100644 (file)
@@ -8,7 +8,6 @@
 //
 
 using System;
-using System.Collections.Generic;
 using System.Text;
 using System.IO;
 using System.Runtime.InteropServices;
@@ -19,7 +18,7 @@ using System.ComponentModel;
 
 namespace System.IO.Ports
 {
-       public class WinSerialStream : Stream, IDisposable
+       class WinSerialStream : Stream, ISerialStream, IDisposable
        {
                // Windows API Constants
                const uint GenericRead = 0x80000000;
@@ -31,6 +30,26 @@ namespace System.IO.Ports
                const uint WinInfiniteTimeout = 0xFFFFFFFF;
                const uint FileIOPending = 997;
 
+               // Signal constants
+               const uint SetRts = 3;
+               const uint ClearRts = 4;
+               const uint SetDtr = 5;
+               const uint ClearDtr = 6;
+               const uint SetBreak = 8;
+               const uint ClearBreak = 9;
+               const uint CtsOn = 0x0010;
+               const uint DsrOn = 0x0020;
+               const uint RsldOn = 0x0080;
+
+               // Event constants
+               const uint EvRxChar = 0x0001;
+               const uint EvCts = 0x0008;
+               const uint EvDsr = 0x0010;
+               const uint EvRlsd = 0x0020;
+               const uint EvBreak = 0x0040;
+               const uint EvErr = 0x0080;
+               const uint EvRing = 0x0100;
+
                int handle;
                int read_timeout;
                int write_timeout;
@@ -55,15 +74,15 @@ namespace System.IO.Ports
                [DllImport("kernel32", SetLastError = true)]
                static extern bool SetCommTimeouts(int handle, Timeouts timeouts);
 
-               public unsafe WinSerialStream (string port_name, int baud_rate, int data_bits, Parity parity,
-                               StopBits sb, Handshake hs, int read_timeout, int write_timeout,
+               public WinSerialStream (string port_name, int baud_rate, int data_bits, Parity parity, StopBits sb,
+                               bool dtr_enable, bool rts_enable, 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,
                                        FileFlagOverlapped, 0);
 
                        if (handle == -1)
-                               ReportIOError(port_name);
+                               ReportIOError (port_name);
 
                        // Set port low level attributes
                        SetAttributes (baud_rate, parity, data_bits, sb, hs);
@@ -80,16 +99,31 @@ namespace System.IO.Ports
                        if (!SetCommTimeouts(handle, timeouts))
                                ReportIOError (null);
 
+                       /// Set DTR and RTS
+                       SetSignal(SerialSignal.Dtr, dtr_enable);
+
+                       if (hs != Handshake.RequestToSend &&
+                                       hs != Handshake.RequestToSendXOnXOff)
+                               SetSignal(SerialSignal.Rts, rts_enable);
+
                        // Init overlapped structures
                        NativeOverlapped wo = new NativeOverlapped ();
                        write_event = new ManualResetEvent (false);
+#if NET_2_0
+                       wo.EventHandle = write_event.Handle;
+#else
                        wo.EventHandle = (int) write_event.Handle;
+#endif
                        write_overlapped = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (NativeOverlapped)));
                        Marshal.StructureToPtr (wo, write_overlapped, true);
 
                        NativeOverlapped ro = new NativeOverlapped ();
                        read_event = new ManualResetEvent (false);
+#if NET_2_0
+                       ro.EventHandle = read_event.Handle;
+#else
                        ro.EventHandle = (int) read_event.Handle;
+#endif
                        read_overlapped = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (NativeOverlapped)));
                        Marshal.StructureToPtr (ro, read_overlapped, true);
                }
@@ -212,6 +246,7 @@ namespace System.IO.Ports
                        throw new NotSupportedException();
                }
 
+#if !TARGET_JVM
                [DllImport("kernel32", SetLastError = true)]
                        static extern unsafe bool ReadFile (int handle, byte* buffer, int bytes_to_read,
                                        out int bytes_read, IntPtr overlapped);
@@ -219,31 +254,35 @@ namespace System.IO.Ports
                [DllImport("kernel32", SetLastError = true)]
                        static extern unsafe bool GetOverlappedResult (int handle, IntPtr overlapped,
                                        ref int bytes_transfered, bool wait);
+#endif
 
-               public override unsafe int Read ([In, Out] byte [] buffer, int offset, int count)
+               public override int Read ([In, Out] byte [] buffer, int offset, int count)
                {
                        CheckDisposed ();
                        if (buffer == null)
                                throw new ArgumentNullException ("buffer");
-                       if (offset < 0 || offset >= buffer.Length)
-                               throw new ArgumentOutOfRangeException ("offset");
-                       if (count < 0 || count > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("count");
-                       if (count > buffer.Length - offset)
-                               throw new ArgumentException ();
-
-                       int bytes_read;
+                       if (offset < 0 || count < 0)
+                               throw new ArgumentOutOfRangeException ("offset or count less than zero.");
 
-                       fixed (byte* ptr = buffer) {
-                               if (ReadFile (handle, ptr + offset, count, out bytes_read, read_overlapped))
-                                       return bytes_read;
+                       if (buffer.Length - offset < count )
+                               throw new ArgumentException ("offset+count",
+                                                             "The size of the buffer is less than offset + count.");
 
-                               // Test for overlapped behavior
-                               if (Marshal.GetLastWin32Error () != FileIOPending)
-                                       ReportIOError (null);
+                       int bytes_read;
 
-                               if (!GetOverlappedResult (handle, read_overlapped, ref bytes_read, true))
-                                       ReportIOError (null);
+                       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 (bytes_read == 0)
@@ -252,33 +291,37 @@ namespace System.IO.Ports
                        return bytes_read;
                }
 
+#if !TARGET_JVM
                [DllImport("kernel32", SetLastError = true)]
                static extern unsafe bool WriteFile (int handle, byte* buffer, int bytes_to_write,
                                out int bytes_written, IntPtr overlapped);
+#endif
 
-               public override unsafe void Write (byte [] buffer, int offset, int count)
+               public override void Write (byte [] buffer, int offset, int count)
                {
                        CheckDisposed ();
                        if (buffer == null)
                                throw new ArgumentNullException ("buffer");
-                       if (offset < 0 || offset >= buffer.Length)
-                               throw new ArgumentOutOfRangeException ("offset");
-                       if (count < 0 || count > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("count");
-                       if (count > buffer.Length - offset)
-                               throw new ArgumentException ("count > buffer.Length - offset");
 
-                       int bytes_written = 0;
+                       if (offset < 0 || count < 0)
+                               throw new ArgumentOutOfRangeException ();
 
-                       fixed (byte* ptr = buffer) {
-                               if (WriteFile (handle, ptr + offset, count, out bytes_written, write_overlapped))
-                                       return;
+                       if (buffer.Length - offset < count)
+                               throw new ArgumentException ("offset+count",
+                                                            "The size of the buffer is less than offset + count.");
 
-                               if (Marshal.GetLastWin32Error() != FileIOPending)
-                                       ReportIOError (null);
+                       int bytes_written = 0;
 
-                               if (!GetOverlappedResult(handle, write_overlapped, ref bytes_written, true))
-                                       ReportIOError (null);
+                       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
@@ -293,7 +336,7 @@ namespace System.IO.Ports
                [DllImport ("kernel32", SetLastError=true)]
                static extern bool SetCommState (int handle, DCB dcb);
 
-               void SetAttributes (int baud_rate, Parity parity, int data_bits, StopBits bits, Handshake hs)
+               public void SetAttributes (int baud_rate, Parity parity, int data_bits, StopBits bits, Handshake hs)
                {
                        DCB dcb = new DCB ();
                        if (!GetCommState (handle, dcb))
@@ -331,6 +374,92 @@ namespace System.IO.Ports
                                throw new ObjectDisposedException (GetType ().FullName);
                }
 
+               // ISerialStream members
+               public void DiscardInBuffer ()
+               {
+                       if (!PurgeComm (handle, PurgeRxClear))
+                               ReportIOError (null);
+               }
+
+               public void DiscardOutBuffer ()
+               {
+                       if (!PurgeComm (handle, PurgeRxClear))
+                               ReportIOError (null);
+               }
+
+               [DllImport ("kernel32", SetLastError=true)]
+               static extern bool ClearCommError (int handle, out CommStat stat);
+
+               public int BytesToRead {
+                       get {
+                               CommStat stat;
+                               if (!ClearCommError (handle, out stat))
+                                       ReportIOError (null);
+
+                               return (int)stat.BytesIn;
+                       }
+               }
+
+               public int BytesToWrite {
+                       get {
+                               CommStat stat;
+                               if (!ClearCommError (handle, out stat))
+                                       ReportIOError (null);
+
+                               return (int)stat.BytesOut;
+                       }
+               }
+
+               [DllImport ("kernel32", SetLastError=true)]
+               static extern bool GetCommModemStatus (int handle, out uint flags);
+
+               public SerialSignal GetSignals ()
+               {
+                       uint flags;
+                       if (!GetCommModemStatus (handle, out flags))
+                               ReportIOError (null);
+
+                       SerialSignal signals = SerialSignal.None;
+                       if ((flags & RsldOn) != 0)
+                               signals |= SerialSignal.Cd;
+                       if ((flags & CtsOn) != 0)
+                               signals |= SerialSignal.Cts;
+                       if ((flags & DsrOn) != 0)
+                               signals |= SerialSignal.Dsr;
+
+                       return signals;
+               }
+               
+               [DllImport ("kernel32", SetLastError=true)]
+               static extern bool EscapeCommFunction (int handle, uint flags);
+
+               public void SetSignal (SerialSignal signal, bool value)
+               {
+                       if (signal != SerialSignal.Rts && signal != SerialSignal.Dtr)
+                               throw new Exception ("Wrong internal value");
+
+                       uint flag;
+                       if (signal == SerialSignal.Rts)
+                               if (value)
+                                       flag = SetRts;
+                               else
+                                       flag = ClearRts;
+                       else
+                               if (value)
+                                       flag = SetDtr;
+                               else
+                                       flag = ClearDtr;
+
+                       if (!EscapeCommFunction (handle, flag))
+                               ReportIOError (null);
+               }
+
+               public void SetBreakState (bool value)
+               {
+                       if (!EscapeCommFunction (handle, value ? SetBreak : ClearBreak))
+                               ReportIOError (null);
+               }
+
        }
        
        [StructLayout (LayoutKind.Sequential)]
@@ -352,6 +481,23 @@ namespace System.IO.Ports
                public byte evt_char;
                public short w_reserved1;
 
+               // flags:
+               //const int fBinary = 0x0001;
+               //const int fParity = 0x0002;
+               const int fOutxCtsFlow = 0x0004;
+               //const int fOutxDsrFlow1 = 0x0008;
+               //const int fOutxDsrFlow2 = 0x0010;
+               //const int fDtrControl = 0x00020;
+               //const int fDsrSensitivity = 0x0040;
+               //const int fTXContinueOnXoff = 0x0080;
+               const int fOutX = 0x0100;
+               const int fInX = 0x0200;
+               //const int fErrorChar = 0x0400;
+               //const int fNull = 0x0800;
+               //const int fRtsControl1 = 0x1000;
+               const int fRtsControl2 = 0x2000;
+               //const int fAbortOnError = 0x4000;
+
                public void SetValues (int baud_rate, Parity parity, int byte_size, StopBits sb, Handshake hs)
                {
                        switch (sb) {
@@ -369,10 +515,30 @@ namespace System.IO.Ports
                        }
 
                        this.baud_rate = baud_rate;
-                       this.parity = (byte) parity;
-                       this.byte_size = (byte) byte_size;
-               }
+                       this.parity = (byte)parity;
+                       this.byte_size = (byte)byte_size;
+
+                       // Clear Handshake flags
+                       flags &= ~(fOutxCtsFlow | fOutX | fInX | fRtsControl2);
 
+                       // Set Handshake flags
+                       switch (hs)
+                       {
+                               case Handshake.None:
+                                       break;
+                               case Handshake.XOnXOff:
+                                       flags |= fOutX | fInX;
+                                       break;
+                               case Handshake.RequestToSend:
+                                       flags |= fOutxCtsFlow | fRtsControl2;
+                                       break;
+                               case Handshake.RequestToSendXOnXOff:
+                                       flags |= fOutxCtsFlow | fOutX | fInX | fRtsControl2;
+                                       break;
+                               default: // Shouldn't happen
+                                       break;
+                       }
+               }
        }
        
        [StructLayout (LayoutKind.Sequential)]
@@ -404,6 +570,14 @@ namespace System.IO.Ports
                }
 
        }
+
+       [StructLayout (LayoutKind.Sequential)]
+       class CommStat
+       {
+               public uint flags;
+               public uint BytesIn;
+               public uint BytesOut;
+       }
 }
 
 #endif