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