[sre] Do we ever try to instantiate a generic instance? Assert that we don't.
[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 namespace System.IO.Ports
18 {
19         class WinSerialStream : Stream, ISerialStream, IDisposable
20         {
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;
30
31                 // Signal constants
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;
41
42                 // Event constants
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;
50
51                 int handle;
52                 int read_timeout;
53                 int write_timeout;
54                 bool disposed;
55                 IntPtr write_overlapped;
56                 IntPtr read_overlapped;
57                 ManualResetEvent read_event;
58                 ManualResetEvent write_event;
59                 Timeouts timeouts;
60
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,
64                                 uint template);
65
66                 [DllImport("kernel32", SetLastError = true)]
67                 static extern bool SetupComm(int handle, int read_buffer_size, int write_buffer_size);
68
69                 [DllImport("kernel32", SetLastError = true)]
70                 static extern bool PurgeComm(int handle, uint flags);
71
72                 [DllImport("kernel32", SetLastError = true)]
73                 static extern bool SetCommTimeouts(int handle, Timeouts timeouts);
74
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)
78                 {
79                         handle = CreateFile (port_name != null && !port_name.StartsWith(@"\\.\")
80                                         ? @"\\.\" + port_name : port_name,
81                                         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                         wo.EventHandle = write_event.Handle;
113                         write_overlapped = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (NativeOverlapped)));
114                         Marshal.StructureToPtr (wo, write_overlapped, true);
115
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);
121                 }
122
123                 public override bool CanRead {
124                         get {
125                                 return true;
126                         }
127                 }
128
129                 public override bool CanSeek {
130                         get {
131                                 return false;
132                         }
133                 }
134
135                 public override bool CanTimeout {
136                         get {
137                                 return true;
138                         }
139                 }
140
141                 public override bool CanWrite {
142                         get {
143                                 return true;
144                         }
145                 }
146
147                 public override int ReadTimeout {
148                         get {
149                                 return read_timeout;
150                         }
151                         set {
152                                 if (value < 0 && value != SerialPort.InfiniteTimeout)
153                                         throw new ArgumentOutOfRangeException ("value");
154
155                                 timeouts.SetValues (value, write_timeout);
156                                 if (!SetCommTimeouts (handle, timeouts))
157                                         ReportIOError (null);
158
159                                 read_timeout = value;
160                         }
161                 }
162
163                 public override int WriteTimeout {
164                         get {
165                                 return write_timeout;
166                         }
167                         set
168                         {
169                                 if (value < 0 && value != SerialPort.InfiniteTimeout)
170                                         throw new ArgumentOutOfRangeException ("value");
171
172                                 timeouts.SetValues (read_timeout, value);
173                                 if (!SetCommTimeouts (handle, timeouts))
174                                         ReportIOError (null);
175
176                                 write_timeout = value;
177                         }
178                 }
179
180                 public override long Length {
181                         get {
182                                 throw new NotSupportedException ();
183                         }
184                 }
185
186                 public override long Position {
187                         get {
188                                 throw new NotSupportedException ();
189                         }
190                         set {
191                                 throw new NotSupportedException ();
192                         }
193                 }
194
195                 [DllImport("kernel32", SetLastError = true)]
196                 static extern bool CloseHandle (int handle);
197
198                 protected override void Dispose (bool disposing)
199                 {
200                         if (disposed)
201                                 return;
202
203                         disposed = true;
204                         CloseHandle (handle);
205                         Marshal.FreeHGlobal (write_overlapped);
206                         Marshal.FreeHGlobal (read_overlapped);
207                 }
208
209                 void IDisposable.Dispose ()
210                 {
211                         Dispose (true);
212                         GC.SuppressFinalize (this);
213                 }
214
215                 public override void Close ()
216                 {
217                         ((IDisposable)this).Dispose ();
218                 }
219
220                 ~WinSerialStream ()
221                 {
222                         Dispose (false);
223                 }
224
225                 public override void Flush ()
226                 {
227                         CheckDisposed ();
228                         // No dothing by now
229                 }
230
231                 public override long Seek (long offset, SeekOrigin origin)
232                 {
233                         throw new NotSupportedException();
234                 }
235
236                 public override void SetLength (long value)
237                 {
238                         throw new NotSupportedException();
239                 }
240
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);
244
245                 [DllImport("kernel32", SetLastError = true)]
246                         static extern unsafe bool GetOverlappedResult (int handle, IntPtr overlapped,
247                                         ref int bytes_transfered, bool wait);
248
249                 public override int Read ([In, Out] byte [] buffer, int offset, int count)
250                 {
251                         CheckDisposed ();
252                         if (buffer == null)
253                                 throw new ArgumentNullException ("buffer");
254                         if (offset < 0 || count < 0)
255                                 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
256
257                         if (buffer.Length - offset < count )
258                                 throw new ArgumentException ("offset+count",
259                                                               "The size of the buffer is less than offset + count.");
260
261                         int bytes_read;
262
263                         unsafe {
264                                 fixed (byte* ptr = buffer) {
265                                         if (ReadFile (handle, ptr + offset, count, out bytes_read, read_overlapped))
266                                                 return bytes_read;
267                                 
268                                         // Test for overlapped behavior
269                                         if (Marshal.GetLastWin32Error () != FileIOPending)
270                                                 ReportIOError (null);
271                                 
272                                         if (!GetOverlappedResult (handle, read_overlapped, ref bytes_read, true))
273                                                 ReportIOError (null);
274                         
275                                 }
276                         }
277
278                         if (bytes_read == 0)
279                                 throw new TimeoutException (); // We didn't get any byte
280
281                         return bytes_read;
282                 }
283
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);
287
288                 public override void Write (byte [] buffer, int offset, int count)
289                 {
290                         CheckDisposed ();
291                         if (buffer == null)
292                                 throw new ArgumentNullException ("buffer");
293
294                         if (offset < 0 || count < 0)
295                                 throw new ArgumentOutOfRangeException ();
296
297                         if (buffer.Length - offset < count)
298                                 throw new ArgumentException ("offset+count",
299                                                              "The size of the buffer is less than offset + count.");
300
301                         int bytes_written = 0;
302
303                         unsafe {
304                                 fixed (byte* ptr = buffer) {
305                                         if (WriteFile (handle, ptr + offset, count, out bytes_written, write_overlapped))
306                                                 return;
307                                         if (Marshal.GetLastWin32Error() != FileIOPending)
308                                                 ReportIOError (null);
309                                         
310                                         if (!GetOverlappedResult(handle, write_overlapped, ref bytes_written, true))
311                                                 ReportIOError (null);
312                                 }
313                         }
314
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 ();
319                 }
320
321                 [DllImport("kernel32", SetLastError = true)]
322                 static extern bool GetCommState (int handle, [Out] DCB dcb);
323
324                 [DllImport ("kernel32", SetLastError=true)]
325                 static extern bool SetCommState (int handle, DCB dcb);
326
327                 public void SetAttributes (int baud_rate, Parity parity, int data_bits, StopBits bits, Handshake hs)
328                 {
329                         DCB dcb = new DCB ();
330                         if (!GetCommState (handle, dcb))
331                                 ReportIOError (null);
332
333                         dcb.SetValues (baud_rate, parity, data_bits, bits, hs);
334                         if (!SetCommState (handle, dcb))
335                                 ReportIOError (null);
336                 }
337
338                 void ReportIOError(string optional_arg)
339                 {
340                         int error = Marshal.GetLastWin32Error ();
341                         string message;
342                         switch (error) {
343                                 case 2:
344                                 case 3:
345                                         message = "The port `" + optional_arg + "' does not exist.";
346                                         break;
347                                 case 87:
348                                         message = "Parameter is incorrect.";
349                                         break;
350                                 default:
351                                         // As fallback, we show the win32 error
352                                         message = new Win32Exception ().Message;
353                                         break;
354                         }
355
356                         throw new IOException (message);
357                 }
358
359                 void CheckDisposed ()
360                 {
361                         if (disposed)
362                                 throw new ObjectDisposedException (GetType ().FullName);
363                 }
364
365                 // ISerialStream members
366                 public void DiscardInBuffer ()
367                 {
368                         if (!PurgeComm (handle, PurgeRxClear))
369                                 ReportIOError (null);
370                 }
371
372                 public void DiscardOutBuffer ()
373                 {
374                         if (!PurgeComm (handle, PurgeTxClear))
375                                 ReportIOError (null);
376                 }
377
378                 [DllImport ("kernel32", SetLastError=true)]
379                 static extern bool ClearCommError (int handle, out uint errors, out CommStat stat);
380
381                 public int BytesToRead {
382                         get {
383                                 uint errors;
384                                 CommStat stat;
385                                 if (!ClearCommError (handle, out errors, out stat))
386                                         ReportIOError (null);
387
388                                 return (int)stat.BytesIn;
389                         }
390                 }
391
392                 public int BytesToWrite {
393                         get {
394                                 uint errors;
395                                 CommStat stat;
396                                 if (!ClearCommError (handle, out errors, out stat))
397                                         ReportIOError (null);
398
399                                 return (int)stat.BytesOut;
400                         }
401                 }
402
403                 [DllImport ("kernel32", SetLastError=true)]
404                 static extern bool GetCommModemStatus (int handle, out uint flags);
405
406                 public SerialSignal GetSignals ()
407                 {
408                         uint flags;
409                         if (!GetCommModemStatus (handle, out flags))
410                                 ReportIOError (null);
411
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;
419
420                         return signals;
421                 }
422                 
423                 [DllImport ("kernel32", SetLastError=true)]
424                 static extern bool EscapeCommFunction (int handle, uint flags);
425
426                 public void SetSignal (SerialSignal signal, bool value)
427                 {
428                         if (signal != SerialSignal.Rts && signal != SerialSignal.Dtr)
429                                 throw new Exception ("Wrong internal value");
430
431                         uint flag;
432                         if (signal == SerialSignal.Rts)
433                                 if (value)
434                                         flag = SetRts;
435                                 else
436                                         flag = ClearRts;
437                         else
438                                 if (value)
439                                         flag = SetDtr;
440                                 else
441                                         flag = ClearDtr;
442
443                         if (!EscapeCommFunction (handle, flag))
444                                 ReportIOError (null);
445                 }
446
447                 public void SetBreakState (bool value)
448                 {
449                         if (!EscapeCommFunction (handle, value ? SetBreak : ClearBreak))
450                                 ReportIOError (null);
451                 }
452
453         }
454         
455         [StructLayout (LayoutKind.Sequential)]
456         class DCB
457         {
458                 public int dcb_length;
459                 public int baud_rate;
460                 public int flags;
461                 public short w_reserved;
462                 public short xon_lim;
463                 public short xoff_lim;
464                 public byte byte_size;
465                 public byte parity;
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;
473
474                 // flags:
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;
490
491                 public void SetValues (int baud_rate, Parity parity, int byte_size, StopBits sb, Handshake hs)
492                 {
493                         switch (sb) {
494                                 case StopBits.One:
495                                         stop_bits = 0;
496                                         break;
497                                 case StopBits.OnePointFive:
498                                         stop_bits = 1;
499                                         break;
500                                 case StopBits.Two:
501                                         stop_bits = 2;
502                                         break;
503                                 default: // Shouldn't happen
504                                         break;
505                         }
506
507                         this.baud_rate = baud_rate;
508                         this.parity = (byte)parity;
509                         this.byte_size = (byte)byte_size;
510
511                         // Clear Handshake flags
512                         flags &= ~(fOutxCtsFlow | fOutX | fInX | fRtsControl2);
513
514                         // Set Handshake flags
515                         switch (hs)
516                         {
517                                 case Handshake.None:
518                                         break;
519                                 case Handshake.XOnXOff:
520                                         flags |= fOutX | fInX;
521                                         break;
522                                 case Handshake.RequestToSend:
523                                         flags |= fOutxCtsFlow | fRtsControl2;
524                                         break;
525                                 case Handshake.RequestToSendXOnXOff:
526                                         flags |= fOutxCtsFlow | fOutX | fInX | fRtsControl2;
527                                         break;
528                                 default: // Shouldn't happen
529                                         break;
530                         }
531                 }
532         }
533         
534         [StructLayout (LayoutKind.Sequential)]
535         class Timeouts
536         {
537                 public uint ReadIntervalTimeout;
538                 public uint ReadTotalTimeoutMultiplier;
539                 public uint ReadTotalTimeoutConstant;
540                 public uint WriteTotalTimeoutMultiplier;
541                 public uint WriteTotalTimeoutConstant;
542
543                 public const uint MaxDWord = 0xFFFFFFFF;
544
545                 public Timeouts (int read_timeout, int write_timeout)
546                 {
547                         SetValues (read_timeout, write_timeout);
548                 }
549
550                 public void SetValues (int read_timeout, int write_timeout)
551                 {
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);
557
558                         WriteTotalTimeoutMultiplier = 0;
559                         WriteTotalTimeoutConstant = (write_timeout == -1 ? MaxDWord : (uint) write_timeout);
560                 }
561
562         }
563
564         [StructLayout (LayoutKind.Sequential)]
565         struct CommStat
566         {
567                 public uint flags;
568                 public uint BytesIn;
569                 public uint BytesOut;
570         }
571 }
572
573