[HttpListenre] Avoid multiple calls to Close()
[mono.git] / mcs / class / System / System.IO.Ports / WinSerialStream.cs
1 //
2 // System.IO.Ports.WinSerialStream.cs
3 //
4 // Authors:
5 //      Carlos Alberto Cortez (calberto.cortez@gmail.com)
6 //
7 // (c) Copyright 2006 Novell, Inc. (http://www.novell.com)
8 //
9
10 using System;
11 using System.Text;
12 using System.IO;
13 using System.Runtime.InteropServices;
14 using System.Threading;
15 using System.ComponentModel;
16
17 #if NET_2_0
18
19 namespace System.IO.Ports
20 {
21         class WinSerialStream : Stream, ISerialStream, IDisposable
22         {
23                 // Windows API Constants
24                 const uint GenericRead = 0x80000000;
25                 const uint GenericWrite = 0x40000000;
26                 const uint OpenExisting = 3;
27                 const uint FileFlagOverlapped = 0x40000000;
28                 const uint PurgeRxClear = 0x0004;
29                 const uint PurgeTxClear = 0x0008;
30                 const uint WinInfiniteTimeout = 0xFFFFFFFF;
31                 const uint FileIOPending = 997;
32
33                 // Signal constants
34                 const uint SetRts = 3;
35                 const uint ClearRts = 4;
36                 const uint SetDtr = 5;
37                 const uint ClearDtr = 6;
38                 const uint SetBreak = 8;
39                 const uint ClearBreak = 9;
40                 const uint CtsOn = 0x0010;
41                 const uint DsrOn = 0x0020;
42                 const uint RsldOn = 0x0080;
43
44                 // Event constants
45                 const uint EvRxChar = 0x0001;
46                 const uint EvCts = 0x0008;
47                 const uint EvDsr = 0x0010;
48                 const uint EvRlsd = 0x0020;
49                 const uint EvBreak = 0x0040;
50                 const uint EvErr = 0x0080;
51                 const uint EvRing = 0x0100;
52
53                 int handle;
54                 int read_timeout;
55                 int write_timeout;
56                 bool disposed;
57                 IntPtr write_overlapped;
58                 IntPtr read_overlapped;
59                 ManualResetEvent read_event;
60                 ManualResetEvent write_event;
61                 Timeouts timeouts;
62
63                 [DllImport("kernel32", SetLastError = true)]
64                 static extern int CreateFile(string port_name, uint desired_access,
65                                 uint share_mode, uint security_attrs, uint creation, uint flags,
66                                 uint template);
67
68                 [DllImport("kernel32", SetLastError = true)]
69                 static extern bool SetupComm(int handle, int read_buffer_size, int write_buffer_size);
70
71                 [DllImport("kernel32", SetLastError = true)]
72                 static extern bool PurgeComm(int handle, uint flags);
73
74                 [DllImport("kernel32", SetLastError = true)]
75                 static extern bool SetCommTimeouts(int handle, Timeouts timeouts);
76
77                 public WinSerialStream (string port_name, int baud_rate, int data_bits, Parity parity, StopBits sb,
78                                 bool dtr_enable, bool rts_enable, Handshake hs, int read_timeout, int write_timeout,
79                                 int read_buffer_size, int write_buffer_size)
80                 {
81                         handle = CreateFile (port_name, GenericRead | GenericWrite, 0, 0, OpenExisting,
82                                         FileFlagOverlapped, 0);
83
84                         if (handle == -1)
85                                 ReportIOError (port_name);
86
87                         // Set port low level attributes
88                         SetAttributes (baud_rate, parity, data_bits, sb, hs);
89
90                         // Clean buffers and set sizes
91                         if (!PurgeComm (handle, PurgeRxClear | PurgeTxClear) ||
92                                         !SetupComm (handle, read_buffer_size, write_buffer_size))
93                                 ReportIOError (null);
94
95                         // Set timeouts
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);
101
102                         /// Set DTR and RTS
103                         SetSignal(SerialSignal.Dtr, dtr_enable);
104
105                         if (hs != Handshake.RequestToSend &&
106                                         hs != Handshake.RequestToSendXOnXOff)
107                                 SetSignal(SerialSignal.Rts, rts_enable);
108
109                         // Init overlapped structures
110                         NativeOverlapped wo = new NativeOverlapped ();
111                         write_event = new ManualResetEvent (false);
112 #if NET_2_0
113                         wo.EventHandle = write_event.Handle;
114 #else
115                         wo.EventHandle = (int) write_event.Handle;
116 #endif
117                         write_overlapped = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (NativeOverlapped)));
118                         Marshal.StructureToPtr (wo, write_overlapped, true);
119
120                         NativeOverlapped ro = new NativeOverlapped ();
121                         read_event = new ManualResetEvent (false);
122 #if NET_2_0
123                         ro.EventHandle = read_event.Handle;
124 #else
125                         ro.EventHandle = (int) read_event.Handle;
126 #endif
127                         read_overlapped = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (NativeOverlapped)));
128                         Marshal.StructureToPtr (ro, read_overlapped, true);
129                 }
130
131                 public override bool CanRead {
132                         get {
133                                 return true;
134                         }
135                 }
136
137                 public override bool CanSeek {
138                         get {
139                                 return false;
140                         }
141                 }
142
143                 public override bool CanTimeout {
144                         get {
145                                 return true;
146                         }
147                 }
148
149                 public override bool CanWrite {
150                         get {
151                                 return true;
152                         }
153                 }
154
155                 public override int ReadTimeout {
156                         get {
157                                 return read_timeout;
158                         }
159                         set {
160                                 if (value < 0 && value != SerialPort.InfiniteTimeout)
161                                         throw new ArgumentOutOfRangeException ("value");
162
163                                 timeouts.SetValues (value, write_timeout);
164                                 if (!SetCommTimeouts (handle, timeouts))
165                                         ReportIOError (null);
166
167                                 read_timeout = value;
168                         }
169                 }
170
171                 public override int WriteTimeout {
172                         get {
173                                 return write_timeout;
174                         }
175                         set
176                         {
177                                 if (value < 0 && value != SerialPort.InfiniteTimeout)
178                                         throw new ArgumentOutOfRangeException ("value");
179
180                                 timeouts.SetValues (read_timeout, value);
181                                 if (!SetCommTimeouts (handle, timeouts))
182                                         ReportIOError (null);
183
184                                 write_timeout = value;
185                         }
186                 }
187
188                 public override long Length {
189                         get {
190                                 throw new NotSupportedException ();
191                         }
192                 }
193
194                 public override long Position {
195                         get {
196                                 throw new NotSupportedException ();
197                         }
198                         set {
199                                 throw new NotSupportedException ();
200                         }
201                 }
202
203                 [DllImport("kernel32", SetLastError = true)]
204                 static extern bool CloseHandle (int handle);
205
206                 protected override void Dispose (bool disposing)
207                 {
208                         if (disposed)
209                                 return;
210
211                         disposed = true;
212                         CloseHandle (handle);
213                         Marshal.FreeHGlobal (write_overlapped);
214                         Marshal.FreeHGlobal (read_overlapped);
215                 }
216
217                 void IDisposable.Dispose ()
218                 {
219                         Dispose (true);
220                         GC.SuppressFinalize (this);
221                 }
222
223                 public override void Close ()
224                 {
225                         ((IDisposable)this).Dispose ();
226                 }
227
228                 ~WinSerialStream ()
229                 {
230                         Dispose (false);
231                 }
232
233                 public override void Flush ()
234                 {
235                         CheckDisposed ();
236                         // No dothing by now
237                 }
238
239                 public override long Seek (long offset, SeekOrigin origin)
240                 {
241                         throw new NotSupportedException();
242                 }
243
244                 public override void SetLength (long value)
245                 {
246                         throw new NotSupportedException();
247                 }
248
249 #if !TARGET_JVM
250                 [DllImport("kernel32", SetLastError = true)]
251                         static extern unsafe bool ReadFile (int handle, byte* buffer, int bytes_to_read,
252                                         out int bytes_read, IntPtr overlapped);
253
254                 [DllImport("kernel32", SetLastError = true)]
255                         static extern unsafe bool GetOverlappedResult (int handle, IntPtr overlapped,
256                                         ref int bytes_transfered, bool wait);
257 #endif
258
259                 public override int Read ([In, Out] byte [] buffer, int offset, int count)
260                 {
261                         CheckDisposed ();
262                         if (buffer == null)
263                                 throw new ArgumentNullException ("buffer");
264                         if (offset < 0 || count < 0)
265                                 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
266
267                         if (buffer.Length - offset < count )
268                                 throw new ArgumentException ("offset+count",
269                                                               "The size of the buffer is less than offset + count.");
270
271                         int bytes_read;
272
273                         unsafe {
274                                 fixed (byte* ptr = buffer) {
275                                         if (ReadFile (handle, ptr + offset, count, out bytes_read, read_overlapped))
276                                                 return bytes_read;
277                                 
278                                         // Test for overlapped behavior
279                                         if (Marshal.GetLastWin32Error () != FileIOPending)
280                                                 ReportIOError (null);
281                                 
282                                         if (!GetOverlappedResult (handle, read_overlapped, ref bytes_read, true))
283                                                 ReportIOError (null);
284                         
285                                 }
286                         }
287
288                         if (bytes_read == 0)
289                                 throw new TimeoutException (); // We didn't get any byte
290
291                         return bytes_read;
292                 }
293
294 #if !TARGET_JVM
295                 [DllImport("kernel32", SetLastError = true)]
296                 static extern unsafe bool WriteFile (int handle, byte* buffer, int bytes_to_write,
297                                 out int bytes_written, IntPtr overlapped);
298 #endif
299
300                 public override void Write (byte [] buffer, int offset, int count)
301                 {
302                         CheckDisposed ();
303                         if (buffer == null)
304                                 throw new ArgumentNullException ("buffer");
305
306                         if (offset < 0 || count < 0)
307                                 throw new ArgumentOutOfRangeException ();
308
309                         if (buffer.Length - offset < count)
310                                 throw new ArgumentException ("offset+count",
311                                                              "The size of the buffer is less than offset + count.");
312
313                         int bytes_written = 0;
314
315                         unsafe {
316                                 fixed (byte* ptr = buffer) {
317                                         if (WriteFile (handle, ptr + offset, count, out bytes_written, write_overlapped))
318                                                 return;
319                                         if (Marshal.GetLastWin32Error() != FileIOPending)
320                                                 ReportIOError (null);
321                                         
322                                         if (!GetOverlappedResult(handle, write_overlapped, ref bytes_written, true))
323                                                 ReportIOError (null);
324                                 }
325                         }
326
327                         // If the operation timed out, then
328                         // we transfered less bytes than the requested ones
329                         if (bytes_written < count)
330                                 throw new TimeoutException ();
331                 }
332
333                 [DllImport("kernel32", SetLastError = true)]
334                 static extern bool GetCommState (int handle, [Out] DCB dcb);
335
336                 [DllImport ("kernel32", SetLastError=true)]
337                 static extern bool SetCommState (int handle, DCB dcb);
338
339                 public void SetAttributes (int baud_rate, Parity parity, int data_bits, StopBits bits, Handshake hs)
340                 {
341                         DCB dcb = new DCB ();
342                         if (!GetCommState (handle, dcb))
343                                 ReportIOError (null);
344
345                         dcb.SetValues (baud_rate, parity, data_bits, bits, hs);
346                         if (!SetCommState (handle, dcb))
347                                 ReportIOError (null);
348                 }
349
350                 void ReportIOError(string optional_arg)
351                 {
352                         int error = Marshal.GetLastWin32Error ();
353                         string message;
354                         switch (error) {
355                                 case 2:
356                                 case 3:
357                                         message = "The port `" + optional_arg + "' does not exist.";
358                                         break;
359                                 case 87:
360                                         message = "Parameter is incorrect.";
361                                         break;
362                                 default:
363                                         // As fallback, we show the win32 error
364                                         message = new Win32Exception ().Message;
365                                         break;
366                         }
367
368                         throw new IOException (message);
369                 }
370
371                 void CheckDisposed ()
372                 {
373                         if (disposed)
374                                 throw new ObjectDisposedException (GetType ().FullName);
375                 }
376
377                 // ISerialStream members
378                 public void DiscardInBuffer ()
379                 {
380                         if (!PurgeComm (handle, PurgeRxClear))
381                                 ReportIOError (null);
382                 }
383
384                 public void DiscardOutBuffer ()
385                 {
386                         if (!PurgeComm (handle, PurgeRxClear))
387                                 ReportIOError (null);
388                 }
389
390                 [DllImport ("kernel32", SetLastError=true)]
391                 static extern bool ClearCommError (int handle, out uint errors, out CommStat stat);
392
393                 public int BytesToRead {
394                         get {
395                                 uint errors;
396                                 CommStat stat;
397                                 if (!ClearCommError (handle, out errors, out stat))
398                                         ReportIOError (null);
399
400                                 return (int)stat.BytesIn;
401                         }
402                 }
403
404                 public int BytesToWrite {
405                         get {
406                                 uint errors;
407                                 CommStat stat;
408                                 if (!ClearCommError (handle, out errors, out stat))
409                                         ReportIOError (null);
410
411                                 return (int)stat.BytesOut;
412                         }
413                 }
414
415                 [DllImport ("kernel32", SetLastError=true)]
416                 static extern bool GetCommModemStatus (int handle, out uint flags);
417
418                 public SerialSignal GetSignals ()
419                 {
420                         uint flags;
421                         if (!GetCommModemStatus (handle, out flags))
422                                 ReportIOError (null);
423
424                         SerialSignal signals = SerialSignal.None;
425                         if ((flags & RsldOn) != 0)
426                                 signals |= SerialSignal.Cd;
427                         if ((flags & CtsOn) != 0)
428                                 signals |= SerialSignal.Cts;
429                         if ((flags & DsrOn) != 0)
430                                 signals |= SerialSignal.Dsr;
431
432                         return signals;
433                 }
434                 
435                 [DllImport ("kernel32", SetLastError=true)]
436                 static extern bool EscapeCommFunction (int handle, uint flags);
437
438                 public void SetSignal (SerialSignal signal, bool value)
439                 {
440                         if (signal != SerialSignal.Rts && signal != SerialSignal.Dtr)
441                                 throw new Exception ("Wrong internal value");
442
443                         uint flag;
444                         if (signal == SerialSignal.Rts)
445                                 if (value)
446                                         flag = SetRts;
447                                 else
448                                         flag = ClearRts;
449                         else
450                                 if (value)
451                                         flag = SetDtr;
452                                 else
453                                         flag = ClearDtr;
454
455                         if (!EscapeCommFunction (handle, flag))
456                                 ReportIOError (null);
457                 }
458
459                 public void SetBreakState (bool value)
460                 {
461                         if (!EscapeCommFunction (handle, value ? SetBreak : ClearBreak))
462                                 ReportIOError (null);
463                 }
464
465         }
466         
467         [StructLayout (LayoutKind.Sequential)]
468         class DCB
469         {
470                 public int dcb_length;
471                 public int baud_rate;
472                 public int flags;
473                 public short w_reserved;
474                 public short xon_lim;
475                 public short xoff_lim;
476                 public byte byte_size;
477                 public byte parity;
478                 public byte stop_bits;
479                 public byte xon_char;
480                 public byte xoff_char;
481                 public byte error_char;
482                 public byte eof_char;
483                 public byte evt_char;
484                 public short w_reserved1;
485
486                 // flags:
487                 //const int fBinary = 0x0001;
488                 //const int fParity = 0x0002;
489                 const int fOutxCtsFlow = 0x0004;
490                 //const int fOutxDsrFlow1 = 0x0008;
491                 //const int fOutxDsrFlow2 = 0x0010;
492                 //const int fDtrControl = 0x00020;
493                 //const int fDsrSensitivity = 0x0040;
494                 //const int fTXContinueOnXoff = 0x0080;
495                 const int fOutX = 0x0100;
496                 const int fInX = 0x0200;
497                 //const int fErrorChar = 0x0400;
498                 //const int fNull = 0x0800;
499                 //const int fRtsControl1 = 0x1000;
500                 const int fRtsControl2 = 0x2000;
501                 //const int fAbortOnError = 0x4000;
502
503                 public void SetValues (int baud_rate, Parity parity, int byte_size, StopBits sb, Handshake hs)
504                 {
505                         switch (sb) {
506                                 case StopBits.One:
507                                         stop_bits = 0;
508                                         break;
509                                 case StopBits.OnePointFive:
510                                         stop_bits = 1;
511                                         break;
512                                 case StopBits.Two:
513                                         stop_bits = 2;
514                                         break;
515                                 default: // Shouldn't happen
516                                         break;
517                         }
518
519                         this.baud_rate = baud_rate;
520                         this.parity = (byte)parity;
521                         this.byte_size = (byte)byte_size;
522
523                         // Clear Handshake flags
524                         flags &= ~(fOutxCtsFlow | fOutX | fInX | fRtsControl2);
525
526                         // Set Handshake flags
527                         switch (hs)
528                         {
529                                 case Handshake.None:
530                                         break;
531                                 case Handshake.XOnXOff:
532                                         flags |= fOutX | fInX;
533                                         break;
534                                 case Handshake.RequestToSend:
535                                         flags |= fOutxCtsFlow | fRtsControl2;
536                                         break;
537                                 case Handshake.RequestToSendXOnXOff:
538                                         flags |= fOutxCtsFlow | fOutX | fInX | fRtsControl2;
539                                         break;
540                                 default: // Shouldn't happen
541                                         break;
542                         }
543                 }
544         }
545         
546         [StructLayout (LayoutKind.Sequential)]
547         class Timeouts
548         {
549                 public uint ReadIntervalTimeout;
550                 public uint ReadTotalTimeoutMultiplier;
551                 public uint ReadTotalTimeoutConstant;
552                 public uint WriteTotalTimeoutMultiplier;
553                 public uint WriteTotalTimeoutConstant;
554
555                 public const uint MaxDWord = 0xFFFFFFFF;
556
557                 public Timeouts (int read_timeout, int write_timeout)
558                 {
559                         SetValues (read_timeout, write_timeout);
560                 }
561
562                 public void SetValues (int read_timeout, int write_timeout)
563                 {
564                         // FIXME: The windows api docs are not very clear about read timeouts,
565                         // and we have to simulate infinite with a big value (uint.MaxValue - 1)
566                         ReadIntervalTimeout = MaxDWord;
567                         ReadTotalTimeoutMultiplier = MaxDWord;
568                         ReadTotalTimeoutConstant = (read_timeout == -1 ? MaxDWord - 1 : (uint) read_timeout);
569
570                         WriteTotalTimeoutMultiplier = 0;
571                         WriteTotalTimeoutConstant = (write_timeout == -1 ? MaxDWord : (uint) write_timeout);
572                 }
573
574         }
575
576         [StructLayout (LayoutKind.Sequential)]
577         struct CommStat
578         {
579                 public uint flags;
580                 public uint BytesIn;
581                 public uint BytesOut;
582         }
583 }
584
585 #endif
586