+//
+// 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;
#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;
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);
}
}