New test.
[mono.git] / mcs / class / System / System.IO.Ports / SerialPort.cs
1 /* -*- Mode: Csharp; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 //
3 //
4 // This class has several problems:
5 //
6 //   * No buffering, the specification requires that there is buffering, this
7 //     matters because a few methods expose strings and chars and the reading
8 //     is encoding sensitive.   This means that when we do a read of a byte
9 //     sequence that can not be turned into a full string by the current encoding
10 //     we should keep a buffer with this data, and read from it on the next
11 //     iteration.
12 //
13 //   * Calls to read_serial from the unmanaged C do not check for errors,
14 //     like EINTR, that should be retried
15 //
16 //   * Calls to the encoder that do not consume all bytes because of partial
17 //     reads 
18 //
19
20 #if NET_2_0
21
22 using System;
23 using System.Collections.Generic;
24 using System.ComponentModel;
25 using System.Text;
26 using System.Runtime.InteropServices;
27
28 namespace System.IO.Ports
29 {
30         public class SerialPort : Component
31         {
32                 public const int InfiniteTimeout = -1;
33                 const int DefaultReadBufferSize = 4096;
34                 const int DefaultWriteBufferSize = 2048;
35                 const int DefaultBaudRate = 9600;
36                 const int DefaultDataBits = 8;
37                 const Parity DefaultParity = Parity.None;
38                 const StopBits DefaultStopBits = StopBits.One;
39
40                 bool is_open;
41                 int baud_rate;
42                 Parity parity;
43                 StopBits stop_bits;
44                 Handshake handshake;
45                 int data_bits;
46                 bool break_state = false;
47                 bool dtr_enable = false;
48                 bool rts_enable = false;
49                 ISerialStream stream;
50                 Encoding encoding = Encoding.ASCII;
51                 string new_line = Environment.NewLine;
52                 string port_name;
53                 int read_timeout = InfiniteTimeout;
54                 int write_timeout = InfiniteTimeout;
55                 int readBufferSize = DefaultReadBufferSize;
56                 int writeBufferSize = DefaultWriteBufferSize;
57                 object error_received = new object ();
58                 object data_received = new object ();
59                 object pin_changed = new object ();
60                 
61                 static string default_port_name = "ttyS0";
62
63                 public SerialPort () : 
64                         this (GetDefaultPortName (), DefaultBaudRate, DefaultParity, DefaultDataBits, DefaultStopBits)
65                 {
66                 }
67
68                 /*
69                   IContainer is in 2.0?
70                   public SerialPort (IContainer container) {
71                   }
72                 */
73
74                 public SerialPort (string portName) :
75                         this (portName, DefaultBaudRate, DefaultParity, DefaultDataBits, DefaultStopBits)
76                 {
77                 }
78
79                 public SerialPort (string portName, int baudRate) :
80                         this (portName, baudRate, DefaultParity, DefaultDataBits, DefaultStopBits)
81                 {
82                 }
83
84                 public SerialPort (string portName, int baudRate, Parity parity) :
85                         this (portName, baudRate, parity, DefaultDataBits, DefaultStopBits)
86                 {
87                 }
88
89                 public SerialPort (string portName, int baudRate, Parity parity, int dataBits) :
90                         this (portName, baudRate, parity, dataBits, DefaultStopBits)
91                 {
92                 }
93
94                 public SerialPort (string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits) 
95                 {
96                         port_name = portName;
97                         baud_rate = baudRate;
98                         data_bits = dataBits;
99                         stop_bits = stopBits;
100                         this.parity = parity;
101                 }
102
103                 static string GetDefaultPortName ()
104                 {
105                         return default_port_name;
106                 }
107
108                 public Stream BaseStream {
109                         get {
110                                 if (!is_open)
111                                         throw new InvalidOperationException ();
112
113                                 return (Stream) stream;
114                         }
115                 }
116
117                 [DefaultValueAttribute (DefaultBaudRate)]
118                 public int BaudRate {
119                         get {
120                                 return baud_rate;
121                         }
122                         set {
123                                 if (value <= 0)
124                                         throw new ArgumentOutOfRangeException ("value");
125                                 
126                                 if (is_open)
127                                         stream.SetAttributes (value, parity, data_bits, stop_bits, handshake);
128                                 
129                                 baud_rate = value;
130                         }
131                 }
132
133                 public bool BreakState {
134                         get {
135                                 return break_state;
136                         }
137                         set {
138                                 CheckOpen ();
139                                 if (value == break_state)
140                                         return; // Do nothing.
141
142                                 stream.SetBreakState (value);
143                                 break_state = value;
144                         }
145                 }
146
147                 public int BytesToRead {
148                         get {
149                                 CheckOpen ();
150                                 return stream.BytesToRead;
151                         }
152                 }
153
154                 public int BytesToWrite {
155                         get {
156                                 CheckOpen ();
157                                 return stream.BytesToWrite;
158                         }
159                 }
160
161                 public bool CDHolding {
162                         get {
163                                 CheckOpen ();
164                                 return (stream.GetSignals () & SerialSignal.Cd) != 0;
165                         }
166                 }
167
168                 public bool CtsHolding {
169                         get {
170                                 CheckOpen ();
171                                 return (stream.GetSignals () & SerialSignal.Cts) != 0;
172                         }
173                 }
174
175                 [DefaultValueAttribute(DefaultDataBits)]
176                 public int DataBits {
177                         get {
178                                 return data_bits;
179                         }
180                         set {
181                                 if (value < 5 || value > 8)
182                                         throw new ArgumentOutOfRangeException ("value");
183
184                                 if (is_open)
185                                         stream.SetAttributes (baud_rate, parity, value, stop_bits, handshake);
186                                 
187                                 data_bits = value;
188                         }
189                 }
190
191                 [MonoTODO("Not implemented")]
192                 public bool DiscardNull {
193                         get {
194                                 CheckOpen ();
195                                 throw new NotImplementedException ();
196                         }
197                         set {
198                                 CheckOpen ();
199                                 throw new NotImplementedException ();
200                         }
201                 }
202
203                 public bool DsrHolding {
204                         get {
205                                 CheckOpen ();
206                                 return (stream.GetSignals () & SerialSignal.Dsr) != 0;
207                         }
208                 }
209
210                 [DefaultValueAttribute(false)]
211                 public bool DtrEnable {
212                         get {
213                                 return dtr_enable;
214                         }
215                         set {
216                                 if (value == dtr_enable)
217                                         return;
218                                 if (is_open)
219                                         stream.SetSignal (SerialSignal.Dtr, value);
220                                 
221                                 dtr_enable = value;
222                         }
223                 }
224
225                 public Encoding Encoding {
226                         get {
227                                 return encoding;
228                         }
229                         set {
230                                 if (value == null)
231                                         throw new ArgumentNullException ("value");
232
233                                 encoding = value;
234                         }
235                 }
236
237                 [DefaultValueAttribute(Handshake.None)]
238                 public Handshake Handshake {
239                         get {
240                                 return handshake;
241                         }
242                         set {
243                                 if (value < Handshake.None || value > Handshake.RequestToSendXOnXOff)
244                                         throw new ArgumentOutOfRangeException ("value");
245
246                                 if (is_open)
247                                         stream.SetAttributes (baud_rate, parity, data_bits, stop_bits, value);
248                                 
249                                 handshake = value;
250                         }
251                 }
252
253                 public bool IsOpen {
254                         get {
255                                 return is_open;
256                         }
257                 }
258
259                 [DefaultValueAttribute("\n")]
260                 public string NewLine {
261                         get {
262                                 return new_line;
263                         }
264                         set {
265                                 if (value == null)
266                                         throw new ArgumentNullException ("value");
267                                 
268                                 new_line = value;
269                         }
270                 }
271
272                 [DefaultValueAttribute(DefaultParity)]
273                 public Parity Parity {
274                         get {
275                                 return parity;
276                         }
277                         set {
278                                 if (value < Parity.None || value > Parity.Space)
279                                         throw new ArgumentOutOfRangeException ("value");
280
281                                 if (is_open)
282                                         stream.SetAttributes (baud_rate, value, data_bits, stop_bits, handshake);
283                                 
284                                 parity = value;
285                         }
286                 }
287
288                 [MonoTODO("Not implemented")]
289                 public byte ParityReplace {
290                         get {
291                                 throw new NotImplementedException ();
292                         }
293                         set {
294                                 throw new NotImplementedException ();
295                         }
296                 }
297
298                 
299                 public string PortName {
300                         get {
301                                 return port_name;
302                         }
303                         set {
304                                 if (is_open)
305                                         throw new InvalidOperationException ("Port name cannot be set while port is open.");
306                                 if (value == null)
307                                         throw new ArgumentNullException ("value");
308                                 if (value.Length == 0 || value.StartsWith ("\\\\"))
309                                         throw new ArgumentException ("value");
310
311                                 port_name = value;
312                         }
313                 }
314
315                 [DefaultValueAttribute(DefaultReadBufferSize)]
316                 public int ReadBufferSize {
317                         get {
318                                 return readBufferSize;
319                         }
320                         set {
321                                 if (is_open)
322                                         throw new InvalidOperationException ();
323                                 if (value <= 0)
324                                         throw new ArgumentOutOfRangeException ("value");
325                                 if (value <= DefaultReadBufferSize)
326                                         return;
327
328                                 readBufferSize = value;
329                         }
330                 }
331
332                 [DefaultValueAttribute(InfiniteTimeout)]
333                 public int ReadTimeout {
334                         get {
335                                 return read_timeout;
336                         }
337                         set {
338                                 if (value <= 0 && value != InfiniteTimeout)
339                                         throw new ArgumentOutOfRangeException ("value");
340
341                                 if (is_open)
342                                         stream.ReadTimeout = value;
343                                 
344                                 read_timeout = value;
345                         }
346                 }
347
348                 [MonoTODO("Not implemented")]
349                 [DefaultValueAttribute(1)]
350                 public int ReceivedBytesThreshold {
351                         get {
352                                 throw new NotImplementedException ();
353                         }
354                         set {
355                                 if (value <= 0)
356                                         throw new ArgumentOutOfRangeException ("value");
357
358                                 throw new NotImplementedException ();
359                         }
360                 }
361
362                 [DefaultValueAttribute(false)]
363                 public bool RtsEnable {
364                         get {
365                                 return rts_enable;
366                         }
367                         set {
368                                 if (value == rts_enable)
369                                         return;
370                                 if (is_open)
371                                         stream.SetSignal (SerialSignal.Rts, value);
372                                 
373                                 rts_enable = value;
374                         }
375                 }
376
377                 [DefaultValueAttribute(DefaultStopBits)]
378                 public StopBits StopBits {
379                         get {
380                                 return stop_bits;
381                         }
382                         set {
383                                 if (value < StopBits.One || value > StopBits.OnePointFive)
384                                         throw new ArgumentOutOfRangeException ("value");
385                                 
386                                 if (is_open)
387                                         stream.SetAttributes (baud_rate, parity, data_bits, value, handshake);
388                                 
389                                 stop_bits = value;
390                         }
391                 }
392
393                 [DefaultValueAttribute(DefaultWriteBufferSize)]
394                 public int WriteBufferSize {
395                         get {
396                                 return writeBufferSize;
397                         }
398                         set {
399                                 if (is_open)
400                                         throw new InvalidOperationException ();
401                                 if (value <= 0)
402                                         throw new ArgumentOutOfRangeException ("value");
403                                 if (value <= DefaultWriteBufferSize)
404                                         return;
405
406                                 writeBufferSize = value;
407                         }
408                 }
409
410                 [DefaultValueAttribute(InfiniteTimeout)]
411                 public int WriteTimeout {
412                         get {
413                                 return write_timeout;
414                         }
415                         set {
416                                 if (value <= 0 && value != InfiniteTimeout)
417                                         throw new ArgumentOutOfRangeException ("value");
418
419                                 if (is_open)
420                                         stream.WriteTimeout = value;
421                                 
422                                 write_timeout = value;
423                         }
424                 }
425
426                 // methods
427
428                 public void Close ()
429                 {
430                         Dispose (false);
431                 }
432
433                 protected override void Dispose (bool disposing)
434                 {
435                         if (!is_open)
436                                 return;
437                         
438                         is_open = false;
439                         stream.Close ();
440                         stream = null;
441                 }
442
443                 public void DiscardInBuffer ()
444                 {
445                         CheckOpen ();
446                         stream.DiscardInBuffer ();
447                 }
448
449                 public void DiscardOutBuffer ()
450                 {
451                         CheckOpen ();
452                         stream.DiscardOutBuffer ();
453                 }
454
455                 public static string [] GetPortNames ()
456                 {
457                         int p = (int) Environment.OSVersion.Platform;
458                         
459                         // Are we on Unix?
460                         if (p == 4 || p == 128){
461                                 string [] ttys = Directory.GetFiles ("/dev/", "tty*");
462                                 List<string> serial_ports = new List<string> ();
463                                 
464                                 foreach (string dev in ttys){
465                                         if (dev.StartsWith ("/dev/ttyS") || dev.StartsWith ("/dev/ttyUSB"))
466                                                 serial_ports.Add (dev);
467                                                 
468                                 }
469                                 return serial_ports.ToArray ();
470                         }
471                         throw new NotImplementedException ("Detection of ports is not implemented for this platform yet.");
472                 }
473
474                 static bool IsWindows {
475                         get {
476                                 PlatformID id =  Environment.OSVersion.Platform;
477                                 return id == PlatformID.Win32Windows || id == PlatformID.Win32NT; // WinCE not supported
478                         }
479                 }
480
481                 public void Open ()
482                 {
483                         if (is_open)
484                                 throw new InvalidOperationException ("Port is already open");
485                         
486 #if !TARGET_JVM
487                         if (IsWindows) // Use windows kernel32 backend
488                                 stream = new WinSerialStream (port_name, baud_rate, data_bits, parity, stop_bits,
489                                                 handshake, read_timeout, write_timeout, readBufferSize, writeBufferSize);
490                         else // Use standard unix backend
491 #endif
492                                 stream = new SerialPortStream (port_name, baud_rate, data_bits, parity, stop_bits, dtr_enable,
493                                         rts_enable, handshake, read_timeout, write_timeout, readBufferSize, writeBufferSize);
494                         
495                         is_open = true;
496                 }
497
498                 public int Read (byte[] buffer, int offset, int count)
499                 {
500                         CheckOpen ();
501                         if (buffer == null)
502                                 throw new ArgumentNullException ("buffer");
503                         if (offset < 0 || count < 0)
504                                 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
505
506                         if (buffer.Length - offset < count )
507                                 throw new ArgumentException ("offset+count",
508                                                               "The size of the buffer is less than offset + count.");
509                         
510                         return stream.Read (buffer, offset, count);
511                 }
512
513                 [Obsolete("Read of char buffers is currently broken")]
514                 [MonoTODO("This is broken")]
515                 public int Read (char[] buffer, int offset, int count)
516                 {
517                         CheckOpen ();
518                         if (buffer == null)
519                                 throw new ArgumentNullException ("buffer");
520                         if (offset < 0 || count < 0)
521                                 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
522
523                         if (buffer.Length - offset < count )
524                                 throw new ArgumentException ("offset+count",
525                                                               "The size of the buffer is less than offset + count.");
526
527                         // The following code does not work, we nee to reintroduce a buffer stream somewhere
528                         // for this to work;  In addition the code is broken.
529                         byte [] bytes = encoding.GetBytes (buffer, offset, count);
530                         return stream.Read (bytes, 0, bytes.Length);
531                 }
532
533                 internal int read_byte ()
534                 {
535                         byte [] buff = new byte [1];
536                         if (stream.Read (buff, 0, 1) > 0)
537                                 return buff [0];
538
539                         return -1;
540                 }
541                 
542                 public int ReadByte ()
543                 {
544                         CheckOpen ();
545                         return read_byte ();
546                 }
547
548                 public int ReadChar ()
549                 {
550                         CheckOpen ();
551                         
552                         byte [] buffer = new byte [16];
553                         int i = 0;
554
555                         do {
556                                 int b = read_byte ();
557                                 if (b == -1)
558                                         return -1;
559                                 buffer [i++] = (byte) b;
560                                 char [] c = encoding.GetChars (buffer, 0, 1);
561                                 if (c.Length > 0)
562                                         return (int) c [0];
563                         } while (i < buffer.Length);
564
565                         return -1;
566                 }
567
568                 public string ReadExisting ()
569                 {
570                         CheckOpen ();
571                         
572                         int count = BytesToRead;
573                         byte [] bytes = new byte [count];
574                         
575                         int n = stream.Read (bytes, 0, count);
576                         return new String (encoding.GetChars (bytes, 0, n));
577                 }
578
579                 public string ReadLine ()
580                 {
581                         CheckOpen ();
582                         List<byte> bytes_read = new List<byte>();
583                         byte [] buff = new byte [1];
584                         
585                         while (true){
586                                 int n = stream.Read (buff, 0, 1);
587                                 if (n == -1 || buff [0] == '\n')
588                                         break;
589                                 bytes_read.Add (buff [0]);
590                         } 
591                         return new String (encoding.GetChars (bytes_read.ToArray ()));
592                 }
593
594                 public string ReadTo (string value)
595                 {
596                         CheckOpen ();
597                         if (value == null)
598                                 throw new ArgumentNullException ("value");
599                         if (value.Length == 0)
600                                 throw new ArgumentException ("value");
601
602                         // Turn into byte array, so we can compare
603                         byte [] byte_value = encoding.GetBytes (value);
604                         int current = 0;
605                         List<byte> seen = new List<byte> ();
606
607                         while (true){
608                                 int n = read_byte ();
609                                 if (n == -1)
610                                         break;
611                                 seen.Add ((byte)n);
612                                 if (n == byte_value [current]){
613                                         current++;
614                                         if (current == byte_value.Length)
615                                                 return encoding.GetString (seen.ToArray (), 0, seen.Count - byte_value.Length);
616                                 } else {
617                                         current = (byte_value [0] == n) ? 1 : 0;
618                                 }
619                         }
620                         return encoding.GetString (seen.ToArray ());
621                 }
622
623                 public void Write (string str)
624                 {
625                         CheckOpen ();
626                         if (str == null)
627                                 throw new ArgumentNullException ("str");
628                         
629                         byte [] buffer = encoding.GetBytes (str);
630                         Write (buffer, 0, buffer.Length);
631                 }
632
633                 public void Write (byte [] buffer, int offset, int count)
634                 {
635                         CheckOpen ();
636                         if (buffer == null)
637                                 throw new ArgumentNullException ("buffer");
638
639                         if (offset < 0 || count < 0)
640                                 throw new ArgumentOutOfRangeException ();
641
642                         if (buffer.Length - offset < count)
643                                 throw new ArgumentException ("offset+count",
644                                                              "The size of the buffer is less than offset + count.");
645
646                         stream.Write (buffer, offset, count);
647                 }
648
649                 public void Write (char [] buffer, int offset, int count)
650                 {
651                         CheckOpen ();
652                         if (buffer == null)
653                                 throw new ArgumentNullException ("buffer");
654                         if (offset < 0 || offset >= buffer.Length)
655                                 throw new ArgumentOutOfRangeException ("offset");
656                         if (count < 0 || count > buffer.Length)
657                                 throw new ArgumentOutOfRangeException ("count");
658                         if (count > buffer.Length - offset)
659                                 throw new ArgumentException ("count > buffer.Length - offset");
660
661                         byte [] bytes = encoding.GetBytes (buffer, offset, count);
662                         stream.Write (bytes, 0, bytes.Length);
663                 }
664
665                 public void WriteLine (string str)
666                 {
667                         Write (str + new_line);
668                 }
669
670                 void CheckOpen ()
671                 {
672                         if (!is_open)
673                                 throw new InvalidOperationException ("Specified port is not open.");
674                 }
675
676                 internal void OnErrorReceived (SerialErrorReceivedEventArgs args)
677                 {
678                         SerialErrorReceivedEventHandler handler =
679                                 (SerialErrorReceivedEventHandler) Events [error_received];
680
681                         if (handler != null)
682                                 handler (this, args);
683                 }
684
685                 internal void OnDataReceived (SerialDataReceivedEventArgs args)
686                 {
687                         SerialDataReceivedEventHandler handler =
688                                 (SerialDataReceivedEventHandler) Events [data_received];
689
690                         if (handler != null)
691                                 handler (this, args);
692                 }
693                 
694                 internal void OnDataReceived (SerialPinChangedEventArgs args)
695                 {
696                         SerialPinChangedEventHandler handler =
697                                 (SerialPinChangedEventHandler) Events [pin_changed];
698
699                         if (handler != null)
700                                 handler (this, args);
701                 }
702
703                 // events
704                 public event SerialErrorReceivedEventHandler ErrorReceived {
705                         add { Events.AddHandler (error_received, value); }
706                         remove { Events.RemoveHandler (error_received, value); }
707                 }
708                 
709                 public event SerialPinChangedEventHandler PinChanged {
710                         add { Events.AddHandler (pin_changed, value); }
711                         remove { Events.RemoveHandler (pin_changed, value); }
712                 }
713                 
714                 public event SerialDataReceivedEventHandler DataReceived {
715                         add { Events.AddHandler (data_received, value); }
716                         remove { Events.RemoveHandler (data_received, value); }
717                 }
718         }
719
720         public delegate void SerialDataReceivedEventHandler (object sender, SerialDataReceivedEventArgs e);
721         public delegate void SerialPinChangedEventHandler (object sender, SerialPinChangedEventArgs e);
722         public delegate void SerialErrorReceivedEventHandler (object sender, SerialErrorReceivedEventArgs e);
723
724 }
725
726 #endif