2 // System.IO.Ports.WinSerialStream.cs
5 // Carlos Alberto Cortez (calberto.cortez@gmail.com)
7 // (c) Copyright 2006 Novell, Inc. (http://www.novell.com)
13 using System.Runtime.InteropServices;
14 using System.Threading;
15 using System.ComponentModel;
17 namespace System.IO.Ports
19 class WinSerialStream : Stream, ISerialStream, IDisposable
21 // Windows API Constants
22 const uint GenericRead = 0x80000000;
23 const uint GenericWrite = 0x40000000;
24 const uint OpenExisting = 3;
25 const uint FileFlagOverlapped = 0x40000000;
26 const uint PurgeRxClear = 0x0008;
27 const uint PurgeTxClear = 0x0004;
28 const uint WinInfiniteTimeout = 0xFFFFFFFF;
29 const uint FileIOPending = 997;
32 const uint SetRts = 3;
33 const uint ClearRts = 4;
34 const uint SetDtr = 5;
35 const uint ClearDtr = 6;
36 const uint SetBreak = 8;
37 const uint ClearBreak = 9;
38 const uint CtsOn = 0x0010;
39 const uint DsrOn = 0x0020;
40 const uint RsldOn = 0x0080;
43 const uint EvRxChar = 0x0001;
44 const uint EvCts = 0x0008;
45 const uint EvDsr = 0x0010;
46 const uint EvRlsd = 0x0020;
47 const uint EvBreak = 0x0040;
48 const uint EvErr = 0x0080;
49 const uint EvRing = 0x0100;
55 IntPtr write_overlapped;
56 IntPtr read_overlapped;
57 ManualResetEvent read_event;
58 ManualResetEvent write_event;
61 [DllImport("kernel32", SetLastError = true)]
62 static extern int CreateFile(string port_name, uint desired_access,
63 uint share_mode, uint security_attrs, uint creation, uint flags,
66 [DllImport("kernel32", SetLastError = true)]
67 static extern bool SetupComm(int handle, int read_buffer_size, int write_buffer_size);
69 [DllImport("kernel32", SetLastError = true)]
70 static extern bool PurgeComm(int handle, uint flags);
72 [DllImport("kernel32", SetLastError = true)]
73 static extern bool SetCommTimeouts(int handle, Timeouts timeouts);
75 public WinSerialStream (string port_name, int baud_rate, int data_bits, Parity parity, StopBits sb,
76 bool dtr_enable, bool rts_enable, Handshake hs, int read_timeout, int write_timeout,
77 int read_buffer_size, int write_buffer_size)
79 handle = CreateFile (port_name != null && !port_name.StartsWith(@"\\.\")
80 ? @"\\.\" + port_name : port_name,
81 GenericRead | GenericWrite, 0, 0, OpenExisting,
82 FileFlagOverlapped, 0);
85 ReportIOError (port_name);
87 // Set port low level attributes
88 SetAttributes (baud_rate, parity, data_bits, sb, hs);
90 // Clean buffers and set sizes
91 if (!PurgeComm (handle, PurgeRxClear | PurgeTxClear) ||
92 !SetupComm (handle, read_buffer_size, write_buffer_size))
96 this.read_timeout = read_timeout;
97 this.write_timeout = write_timeout;
98 timeouts = new Timeouts (read_timeout, write_timeout);
99 if (!SetCommTimeouts(handle, timeouts))
100 ReportIOError (null);
103 SetSignal(SerialSignal.Dtr, dtr_enable);
105 if (hs != Handshake.RequestToSend &&
106 hs != Handshake.RequestToSendXOnXOff)
107 SetSignal(SerialSignal.Rts, rts_enable);
109 // Init overlapped structures
110 NativeOverlapped wo = new NativeOverlapped ();
111 write_event = new ManualResetEvent (false);
112 wo.EventHandle = write_event.Handle;
113 write_overlapped = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (NativeOverlapped)));
114 Marshal.StructureToPtr (wo, write_overlapped, true);
116 NativeOverlapped ro = new NativeOverlapped ();
117 read_event = new ManualResetEvent (false);
118 ro.EventHandle = read_event.Handle;
119 read_overlapped = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (NativeOverlapped)));
120 Marshal.StructureToPtr (ro, read_overlapped, true);
123 public override bool CanRead {
129 public override bool CanSeek {
135 public override bool CanTimeout {
141 public override bool CanWrite {
147 public override int ReadTimeout {
152 if (value < 0 && value != SerialPort.InfiniteTimeout)
153 throw new ArgumentOutOfRangeException ("value");
155 timeouts.SetValues (value, write_timeout);
156 if (!SetCommTimeouts (handle, timeouts))
157 ReportIOError (null);
159 read_timeout = value;
163 public override int WriteTimeout {
165 return write_timeout;
169 if (value < 0 && value != SerialPort.InfiniteTimeout)
170 throw new ArgumentOutOfRangeException ("value");
172 timeouts.SetValues (read_timeout, value);
173 if (!SetCommTimeouts (handle, timeouts))
174 ReportIOError (null);
176 write_timeout = value;
180 public override long Length {
182 throw new NotSupportedException ();
186 public override long Position {
188 throw new NotSupportedException ();
191 throw new NotSupportedException ();
195 [DllImport("kernel32", SetLastError = true)]
196 static extern bool CloseHandle (int handle);
198 protected override void Dispose (bool disposing)
204 CloseHandle (handle);
205 Marshal.FreeHGlobal (write_overlapped);
206 Marshal.FreeHGlobal (read_overlapped);
209 void IDisposable.Dispose ()
212 GC.SuppressFinalize (this);
215 public override void Close ()
217 ((IDisposable)this).Dispose ();
225 public override void Flush ()
231 public override long Seek (long offset, SeekOrigin origin)
233 throw new NotSupportedException();
236 public override void SetLength (long value)
238 throw new NotSupportedException();
241 [DllImport("kernel32", SetLastError = true)]
242 static extern unsafe bool ReadFile (int handle, byte* buffer, int bytes_to_read,
243 out int bytes_read, IntPtr overlapped);
245 [DllImport("kernel32", SetLastError = true)]
246 static extern unsafe bool GetOverlappedResult (int handle, IntPtr overlapped,
247 ref int bytes_transfered, bool wait);
249 public override int Read ([In, Out] byte [] buffer, int offset, int count)
253 throw new ArgumentNullException ("buffer");
254 if (offset < 0 || count < 0)
255 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
257 if (buffer.Length - offset < count )
258 throw new ArgumentException ("offset+count",
259 "The size of the buffer is less than offset + count.");
264 fixed (byte* ptr = buffer) {
265 if (ReadFile (handle, ptr + offset, count, out bytes_read, read_overlapped))
268 // Test for overlapped behavior
269 if (Marshal.GetLastWin32Error () != FileIOPending)
270 ReportIOError (null);
272 if (!GetOverlappedResult (handle, read_overlapped, ref bytes_read, true))
273 ReportIOError (null);
279 throw new TimeoutException (); // We didn't get any byte
284 [DllImport("kernel32", SetLastError = true)]
285 static extern unsafe bool WriteFile (int handle, byte* buffer, int bytes_to_write,
286 out int bytes_written, IntPtr overlapped);
288 public override void Write (byte [] buffer, int offset, int count)
292 throw new ArgumentNullException ("buffer");
294 if (offset < 0 || count < 0)
295 throw new ArgumentOutOfRangeException ();
297 if (buffer.Length - offset < count)
298 throw new ArgumentException ("offset+count",
299 "The size of the buffer is less than offset + count.");
301 int bytes_written = 0;
304 fixed (byte* ptr = buffer) {
305 if (WriteFile (handle, ptr + offset, count, out bytes_written, write_overlapped))
307 if (Marshal.GetLastWin32Error() != FileIOPending)
308 ReportIOError (null);
310 if (!GetOverlappedResult(handle, write_overlapped, ref bytes_written, true))
311 ReportIOError (null);
315 // If the operation timed out, then
316 // we transfered less bytes than the requested ones
317 if (bytes_written < count)
318 throw new TimeoutException ();
321 [DllImport("kernel32", SetLastError = true)]
322 static extern bool GetCommState (int handle, [Out] DCB dcb);
324 [DllImport ("kernel32", SetLastError=true)]
325 static extern bool SetCommState (int handle, DCB dcb);
327 public void SetAttributes (int baud_rate, Parity parity, int data_bits, StopBits bits, Handshake hs)
329 DCB dcb = new DCB ();
330 if (!GetCommState (handle, dcb))
331 ReportIOError (null);
333 dcb.SetValues (baud_rate, parity, data_bits, bits, hs);
334 if (!SetCommState (handle, dcb))
335 ReportIOError (null);
338 void ReportIOError(string optional_arg)
340 int error = Marshal.GetLastWin32Error ();
345 message = "The port `" + optional_arg + "' does not exist.";
348 message = "Parameter is incorrect.";
351 // As fallback, we show the win32 error
352 message = new Win32Exception ().Message;
356 throw new IOException (message);
359 void CheckDisposed ()
362 throw new ObjectDisposedException (GetType ().FullName);
365 // ISerialStream members
366 public void DiscardInBuffer ()
368 if (!PurgeComm (handle, PurgeRxClear))
369 ReportIOError (null);
372 public void DiscardOutBuffer ()
374 if (!PurgeComm (handle, PurgeTxClear))
375 ReportIOError (null);
378 [DllImport ("kernel32", SetLastError=true)]
379 static extern bool ClearCommError (int handle, out uint errors, out CommStat stat);
381 public int BytesToRead {
385 if (!ClearCommError (handle, out errors, out stat))
386 ReportIOError (null);
388 return (int)stat.BytesIn;
392 public int BytesToWrite {
396 if (!ClearCommError (handle, out errors, out stat))
397 ReportIOError (null);
399 return (int)stat.BytesOut;
403 [DllImport ("kernel32", SetLastError=true)]
404 static extern bool GetCommModemStatus (int handle, out uint flags);
406 public SerialSignal GetSignals ()
409 if (!GetCommModemStatus (handle, out flags))
410 ReportIOError (null);
412 SerialSignal signals = SerialSignal.None;
413 if ((flags & RsldOn) != 0)
414 signals |= SerialSignal.Cd;
415 if ((flags & CtsOn) != 0)
416 signals |= SerialSignal.Cts;
417 if ((flags & DsrOn) != 0)
418 signals |= SerialSignal.Dsr;
423 [DllImport ("kernel32", SetLastError=true)]
424 static extern bool EscapeCommFunction (int handle, uint flags);
426 public void SetSignal (SerialSignal signal, bool value)
428 if (signal != SerialSignal.Rts && signal != SerialSignal.Dtr)
429 throw new Exception ("Wrong internal value");
432 if (signal == SerialSignal.Rts)
443 if (!EscapeCommFunction (handle, flag))
444 ReportIOError (null);
447 public void SetBreakState (bool value)
449 if (!EscapeCommFunction (handle, value ? SetBreak : ClearBreak))
450 ReportIOError (null);
455 [StructLayout (LayoutKind.Sequential)]
458 public int dcb_length;
459 public int baud_rate;
461 public short w_reserved;
462 public short xon_lim;
463 public short xoff_lim;
464 public byte byte_size;
466 public byte stop_bits;
467 public byte xon_char;
468 public byte xoff_char;
469 public byte error_char;
470 public byte eof_char;
471 public byte evt_char;
472 public short w_reserved1;
475 //const int fBinary = 0x0001;
476 //const int fParity = 0x0002;
477 const int fOutxCtsFlow = 0x0004;
478 //const int fOutxDsrFlow1 = 0x0008;
479 //const int fOutxDsrFlow2 = 0x0010;
480 //const int fDtrControl = 0x00020;
481 //const int fDsrSensitivity = 0x0040;
482 //const int fTXContinueOnXoff = 0x0080;
483 const int fOutX = 0x0100;
484 const int fInX = 0x0200;
485 //const int fErrorChar = 0x0400;
486 //const int fNull = 0x0800;
487 //const int fRtsControl1 = 0x1000;
488 const int fRtsControl2 = 0x2000;
489 //const int fAbortOnError = 0x4000;
491 public void SetValues (int baud_rate, Parity parity, int byte_size, StopBits sb, Handshake hs)
497 case StopBits.OnePointFive:
503 default: // Shouldn't happen
507 this.baud_rate = baud_rate;
508 this.parity = (byte)parity;
509 this.byte_size = (byte)byte_size;
511 // Clear Handshake flags
512 flags &= ~(fOutxCtsFlow | fOutX | fInX | fRtsControl2);
514 // Set Handshake flags
519 case Handshake.XOnXOff:
520 flags |= fOutX | fInX;
522 case Handshake.RequestToSend:
523 flags |= fOutxCtsFlow | fRtsControl2;
525 case Handshake.RequestToSendXOnXOff:
526 flags |= fOutxCtsFlow | fOutX | fInX | fRtsControl2;
528 default: // Shouldn't happen
534 [StructLayout (LayoutKind.Sequential)]
537 public uint ReadIntervalTimeout;
538 public uint ReadTotalTimeoutMultiplier;
539 public uint ReadTotalTimeoutConstant;
540 public uint WriteTotalTimeoutMultiplier;
541 public uint WriteTotalTimeoutConstant;
543 public const uint MaxDWord = 0xFFFFFFFF;
545 public Timeouts (int read_timeout, int write_timeout)
547 SetValues (read_timeout, write_timeout);
550 public void SetValues (int read_timeout, int write_timeout)
552 // FIXME: The windows api docs are not very clear about read timeouts,
553 // and we have to simulate infinite with a big value (uint.MaxValue - 1)
554 ReadIntervalTimeout = MaxDWord;
555 ReadTotalTimeoutMultiplier = MaxDWord;
556 ReadTotalTimeoutConstant = (read_timeout == -1 ? MaxDWord - 1 : (uint) read_timeout);
558 WriteTotalTimeoutMultiplier = 0;
559 WriteTotalTimeoutConstant = (write_timeout == -1 ? MaxDWord : (uint) write_timeout);
564 [StructLayout (LayoutKind.Sequential)]
569 public uint BytesOut;