New test.
[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,
78                                 StopBits sb, 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                         // Init overlapped structures
103                         NativeOverlapped wo = new NativeOverlapped ();
104                         write_event = new ManualResetEvent (false);
105                         wo.EventHandle = (int) write_event.Handle;
106                         write_overlapped = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (NativeOverlapped)));
107                         Marshal.StructureToPtr (wo, write_overlapped, true);
108
109                         NativeOverlapped ro = new NativeOverlapped ();
110                         read_event = new ManualResetEvent (false);
111                         ro.EventHandle = (int) read_event.Handle;
112                         read_overlapped = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (NativeOverlapped)));
113                         Marshal.StructureToPtr (ro, read_overlapped, true);
114                 }
115
116                 public override bool CanRead {
117                         get {
118                                 return true;
119                         }
120                 }
121
122                 public override bool CanSeek {
123                         get {
124                                 return false;
125                         }
126                 }
127
128                 public override bool CanTimeout {
129                         get {
130                                 return true;
131                         }
132                 }
133
134                 public override bool CanWrite {
135                         get {
136                                 return true;
137                         }
138                 }
139
140                 public override int ReadTimeout {
141                         get {
142                                 return read_timeout;
143                         }
144                         set {
145                                 if (value < 0 && value != SerialPort.InfiniteTimeout)
146                                         throw new ArgumentOutOfRangeException ("value");
147
148                                 timeouts.SetValues (value, write_timeout);
149                                 if (!SetCommTimeouts (handle, timeouts))
150                                         ReportIOError (null);
151
152                                 read_timeout = value;
153                         }
154                 }
155
156                 public override int WriteTimeout {
157                         get {
158                                 return write_timeout;
159                         }
160                         set
161                         {
162                                 if (value < 0 && value != SerialPort.InfiniteTimeout)
163                                         throw new ArgumentOutOfRangeException ("value");
164
165                                 timeouts.SetValues (read_timeout, value);
166                                 if (!SetCommTimeouts (handle, timeouts))
167                                         ReportIOError (null);
168
169                                 write_timeout = value;
170                         }
171                 }
172
173                 public override long Length {
174                         get {
175                                 throw new NotSupportedException ();
176                         }
177                 }
178
179                 public override long Position {
180                         get {
181                                 throw new NotSupportedException ();
182                         }
183                         set {
184                                 throw new NotSupportedException ();
185                         }
186                 }
187
188                 [DllImport("kernel32", SetLastError = true)]
189                 static extern bool CloseHandle (int handle);
190
191                 protected override void Dispose (bool disposing)
192                 {
193                         if (disposed)
194                                 return;
195
196                         disposed = true;
197                         CloseHandle (handle);
198                         Marshal.FreeHGlobal (write_overlapped);
199                         Marshal.FreeHGlobal (read_overlapped);
200                 }
201
202                 void IDisposable.Dispose ()
203                 {
204                         Dispose (true);
205                         GC.SuppressFinalize (this);
206                 }
207
208                 public override void Close ()
209                 {
210                         ((IDisposable)this).Dispose ();
211                 }
212
213                 ~WinSerialStream ()
214                 {
215                         Dispose (false);
216                 }
217
218                 public override void Flush ()
219                 {
220                         CheckDisposed ();
221                         // No dothing by now
222                 }
223
224                 public override long Seek (long offset, SeekOrigin origin)
225                 {
226                         throw new NotSupportedException();
227                 }
228
229                 public override void SetLength (long value)
230                 {
231                         throw new NotSupportedException();
232                 }
233
234 #if !TARGET_JVM
235                 [DllImport("kernel32", SetLastError = true)]
236                         static extern unsafe bool ReadFile (int handle, byte* buffer, int bytes_to_read,
237                                         out int bytes_read, IntPtr overlapped);
238
239                 [DllImport("kernel32", SetLastError = true)]
240                         static extern unsafe bool GetOverlappedResult (int handle, IntPtr overlapped,
241                                         ref int bytes_transfered, bool wait);
242 #endif
243
244                 public override int Read ([In, Out] byte [] buffer, int offset, int count)
245                 {
246                         CheckDisposed ();
247                         if (buffer == null)
248                                 throw new ArgumentNullException ("buffer");
249                         if (offset < 0 || count < 0)
250                                 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
251
252                         if (buffer.Length - offset < count )
253                                 throw new ArgumentException ("offset+count",
254                                                               "The size of the buffer is less than offset + count.");
255
256                         int bytes_read;
257
258                         unsafe {
259                                 fixed (byte* ptr = buffer) {
260                                         if (ReadFile (handle, ptr + offset, count, out bytes_read, read_overlapped))
261                                                 return bytes_read;
262                                 
263                                         // Test for overlapped behavior
264                                         if (Marshal.GetLastWin32Error () != FileIOPending)
265                                                 ReportIOError (null);
266                                 
267                                         if (!GetOverlappedResult (handle, read_overlapped, ref bytes_read, true))
268                                                 ReportIOError (null);
269                         
270                                 }
271                         }
272
273                         if (bytes_read == 0)
274                                 throw new TimeoutException (); // We didn't get any byte
275
276                         return bytes_read;
277                 }
278
279 #if !TARGET_JVM
280                 [DllImport("kernel32", SetLastError = true)]
281                 static extern unsafe bool WriteFile (int handle, byte* buffer, int bytes_to_write,
282                                 out int bytes_written, IntPtr overlapped);
283 #endif
284
285                 public override void Write (byte [] buffer, int offset, int count)
286                 {
287                         CheckDisposed ();
288                         if (buffer == null)
289                                 throw new ArgumentNullException ("buffer");
290
291                         if (offset < 0 || count < 0)
292                                 throw new ArgumentOutOfRangeException ();
293
294                         if (buffer.Length - offset < count)
295                                 throw new ArgumentException ("offset+count",
296                                                              "The size of the buffer is less than offset + count.");
297
298                         int bytes_written = 0;
299
300                         unsafe {
301                                 fixed (byte* ptr = buffer) {
302                                         if (WriteFile (handle, ptr + offset, count, out bytes_written, write_overlapped))
303                                                 return;
304                                         if (Marshal.GetLastWin32Error() != FileIOPending)
305                                                 ReportIOError (null);
306                                         
307                                         if (!GetOverlappedResult(handle, write_overlapped, ref bytes_written, true))
308                                                 ReportIOError (null);
309                                 }
310                         }
311
312                         // If the operation timed out, then
313                         // we transfered less bytes than the requested ones
314                         if (bytes_written < count)
315                                 throw new TimeoutException ();
316                 }
317
318                 [DllImport("kernel32", SetLastError = true)]
319                 static extern bool GetCommState (int handle, [Out] DCB dcb);
320
321                 [DllImport ("kernel32", SetLastError=true)]
322                 static extern bool SetCommState (int handle, DCB dcb);
323
324                 public void SetAttributes (int baud_rate, Parity parity, int data_bits, StopBits bits, Handshake hs)
325                 {
326                         DCB dcb = new DCB ();
327                         if (!GetCommState (handle, dcb))
328                                 ReportIOError (null);
329
330                         dcb.SetValues (baud_rate, parity, data_bits, bits, hs);
331                         if (!SetCommState (handle, dcb))
332                                 ReportIOError (null);
333                 }
334
335                 void ReportIOError(string optional_arg)
336                 {
337                         int error = Marshal.GetLastWin32Error ();
338                         string message;
339                         switch (error) {
340                                 case 2:
341                                 case 3:
342                                         message = "The port `" + optional_arg + "' does not exist.";
343                                         break;
344                                 case 87:
345                                         message = "Parameter is incorrect.";
346                                         break;
347                                 default:
348                                         // As fallback, we show the win32 error
349                                         message = new Win32Exception ().Message;
350                                         break;
351                         }
352
353                         throw new IOException (message);
354                 }
355
356                 void CheckDisposed ()
357                 {
358                         if (disposed)
359                                 throw new ObjectDisposedException (GetType ().FullName);
360                 }
361
362                 // ISerialStream members
363                 public void DiscardInBuffer ()
364                 {
365                         if (!PurgeComm (handle, PurgeRxClear))
366                                 ReportIOError (null);
367                 }
368
369                 public void DiscardOutBuffer ()
370                 {
371                         if (!PurgeComm (handle, PurgeRxClear))
372                                 ReportIOError (null);
373                 }
374
375                 [DllImport ("kernel32", SetLastError=true)]
376                 static extern bool ClearCommError (int handle, out CommStat stat);
377
378                 public int BytesToRead {
379                         get {
380                                 CommStat stat;
381                                 if (!ClearCommError (handle, out stat))
382                                         ReportIOError (null);
383
384                                 return (int)stat.BytesIn;
385                         }
386                 }
387
388                 public int BytesToWrite {
389                         get {
390                                 CommStat stat;
391                                 if (!ClearCommError (handle, out stat))
392                                         ReportIOError (null);
393
394                                 return (int)stat.BytesOut;
395                         }
396                 }
397
398                 [DllImport ("kernel32", SetLastError=true)]
399                 static extern bool GetCommModemStatus (int handle, out uint flags);
400
401                 public SerialSignal GetSignals ()
402                 {
403                         uint flags;
404                         if (!GetCommModemStatus (handle, out flags))
405                                 ReportIOError (null);
406
407                         SerialSignal signals = SerialSignal.None;
408                         if ((flags & RsldOn) != 0)
409                                 signals |= SerialSignal.Cd;
410                         if ((flags & CtsOn) != 0)
411                                 signals |= SerialSignal.Cts;
412                         if ((flags & DsrOn) != 0)
413                                 signals |= SerialSignal.Dsr;
414
415                         return signals;
416                 }
417                 
418                 [DllImport ("kernel32", SetLastError=true)]
419                 static extern bool EscapeCommFunction (int handle, uint flags);
420
421                 public void SetSignal (SerialSignal signal, bool value)
422                 {
423                         if (signal != SerialSignal.Rts || signal != SerialSignal.Dtr)
424                                 throw new Exception ("Wrong internal value");
425
426                         uint flag;
427                         if (signal == SerialSignal.Rts)
428                                 if (value)
429                                         flag = SetRts;
430                                 else
431                                         flag = ClearRts;
432                         else 
433                                 if (value)
434                                         flag = SetDtr;
435                                 else
436                                         flag = ClearDtr;
437
438                         if (!EscapeCommFunction (handle, flag))
439                                 ReportIOError (null);
440                 }
441
442                 public void SetBreakState (bool value)
443                 {
444                         if (!EscapeCommFunction (handle, value ? SetBreak : ClearBreak))
445                                 ReportIOError (null);
446                 }
447
448         }
449         
450         [StructLayout (LayoutKind.Sequential)]
451         class DCB
452         {
453                 public int dcb_length;
454                 public int baud_rate;
455                 public int flags;
456                 public short w_reserved;
457                 public short xon_lim;
458                 public short xoff_lim;
459                 public byte byte_size;
460                 public byte parity;
461                 public byte stop_bits;
462                 public byte xon_char;
463                 public byte xoff_char;
464                 public byte error_char;
465                 public byte eof_char;
466                 public byte evt_char;
467                 public short w_reserved1;
468
469                 public void SetValues (int baud_rate, Parity parity, int byte_size, StopBits sb, Handshake hs)
470                 {
471                         switch (sb) {
472                                 case StopBits.One:
473                                         stop_bits = 0;
474                                         break;
475                                 case StopBits.OnePointFive:
476                                         stop_bits = 1;
477                                         break;
478                                 case StopBits.Two:
479                                         stop_bits = 2;
480                                         break;
481                                 default: // Shouldn't happen
482                                         break;
483                         }
484
485                         this.baud_rate = baud_rate;
486                         this.parity = (byte) parity;
487                         this.byte_size = (byte) byte_size;
488                 }
489
490         }
491         
492         [StructLayout (LayoutKind.Sequential)]
493         class Timeouts
494         {
495                 public uint ReadIntervalTimeout;
496                 public uint ReadTotalTimeoutMultiplier;
497                 public uint ReadTotalTimeoutConstant;
498                 public uint WriteTotalTimeoutMultiplier;
499                 public uint WriteTotalTimeoutConstant;
500
501                 public const uint MaxDWord = 0xFFFFFFFF;
502
503                 public Timeouts (int read_timeout, int write_timeout)
504                 {
505                         SetValues (read_timeout, write_timeout);
506                 }
507
508                 public void SetValues (int read_timeout, int write_timeout)
509                 {
510                         // FIXME: The windows api docs are not very clear about read timeouts,
511                         // and we have to simulate infinite with a big value (uint.MaxValue - 1)
512                         ReadIntervalTimeout = MaxDWord;
513                         ReadTotalTimeoutMultiplier = MaxDWord;
514                         ReadTotalTimeoutConstant = (read_timeout == -1 ? MaxDWord - 1 : (uint) read_timeout);
515
516                         WriteTotalTimeoutMultiplier = 0;
517                         WriteTotalTimeoutConstant = (write_timeout == -1 ? MaxDWord : (uint) write_timeout);
518                 }
519
520         }
521
522         [StructLayout (LayoutKind.Sequential)]
523         class CommStat
524         {
525                 public uint flags;
526                 public uint BytesIn;
527                 public uint BytesOut;
528         }
529 }
530
531 #endif
532