1 /* -*- Mode: Csharp; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
4 // This class has several problems:
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
13 // * Calls to read_serial from the unmanaged C do not check for errors,
14 // like EINTR, that should be retried
16 // * Calls to the encoder that do not consume all bytes because of partial
21 using System.Collections.Generic;
22 using System.ComponentModel;
23 using System.Diagnostics;
25 using System.Runtime.InteropServices;
26 using Microsoft.Win32;
28 namespace System.IO.Ports
30 [MonitoringDescription ("")]
31 public class SerialPort : Component
33 public const int InfiniteTimeout = -1;
34 const int DefaultReadBufferSize = 4096;
35 const int DefaultWriteBufferSize = 2048;
36 const int DefaultBaudRate = 9600;
37 const int DefaultDataBits = 8;
38 const Parity DefaultParity = Parity.None;
39 const StopBits DefaultStopBits = StopBits.One;
47 bool break_state = false;
48 bool dtr_enable = false;
49 bool rts_enable = false;
51 Encoding encoding = Encoding.ASCII;
52 string new_line = Environment.NewLine;
54 int read_timeout = InfiniteTimeout;
55 int write_timeout = InfiniteTimeout;
56 int readBufferSize = DefaultReadBufferSize;
57 int writeBufferSize = DefaultWriteBufferSize;
58 object error_received = new object ();
59 object data_received = new object ();
60 object pin_changed = new object ();
62 public SerialPort () :
63 this (GetDefaultPortName (), DefaultBaudRate, DefaultParity, DefaultDataBits, DefaultStopBits)
67 public SerialPort (IContainer container) : this ()
69 // TODO: What to do here?
72 public SerialPort (string portName) :
73 this (portName, DefaultBaudRate, DefaultParity, DefaultDataBits, DefaultStopBits)
77 public SerialPort (string portName, int baudRate) :
78 this (portName, baudRate, DefaultParity, DefaultDataBits, DefaultStopBits)
82 public SerialPort (string portName, int baudRate, Parity parity) :
83 this (portName, baudRate, parity, DefaultDataBits, DefaultStopBits)
87 public SerialPort (string portName, int baudRate, Parity parity, int dataBits) :
88 this (portName, baudRate, parity, dataBits, DefaultStopBits)
92 public SerialPort (string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits)
101 static string GetDefaultPortName ()
103 string[] ports = GetPortNames();
104 if (ports.Length > 0) {
107 int p = (int)Environment.OSVersion.Platform;
108 if (p == 4 || p == 128 || p == 6)
109 return "ttyS0"; // Default for Unix
111 return "COM1"; // Default for Windows
116 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
117 public Stream BaseStream {
120 return (Stream) stream;
124 [DefaultValueAttribute (DefaultBaudRate)]
126 [MonitoringDescription ("")]
127 public int BaudRate {
133 throw new ArgumentOutOfRangeException ("value");
136 stream.SetAttributes (value, parity, data_bits, stop_bits, handshake);
143 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
144 public bool BreakState {
150 if (value == break_state)
151 return; // Do nothing.
153 stream.SetBreakState (value);
159 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
160 public int BytesToRead {
163 return stream.BytesToRead;
168 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
169 public int BytesToWrite {
172 return stream.BytesToWrite;
177 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
178 public bool CDHolding {
181 return (stream.GetSignals () & SerialSignal.Cd) != 0;
186 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
187 public bool CtsHolding {
190 return (stream.GetSignals () & SerialSignal.Cts) != 0;
194 [DefaultValueAttribute(DefaultDataBits)]
196 [MonitoringDescription ("")]
197 public int DataBits {
202 if (value < 5 || value > 8)
203 throw new ArgumentOutOfRangeException ("value");
206 stream.SetAttributes (baud_rate, parity, value, stop_bits, handshake);
212 [MonoTODO("Not implemented")]
214 [MonitoringDescription ("")]
215 [DefaultValue (false)]
216 public bool DiscardNull {
218 throw new NotImplementedException ();
221 // LAMESPEC: Msdn states that an InvalidOperationException exception
222 // is fired if the port is not open, which is *not* happening.
224 throw new NotImplementedException ();
229 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
230 public bool DsrHolding {
233 return (stream.GetSignals () & SerialSignal.Dsr) != 0;
237 [DefaultValueAttribute(false)]
239 [MonitoringDescription ("")]
240 public bool DtrEnable {
245 if (value == dtr_enable)
248 stream.SetSignal (SerialSignal.Dtr, value);
255 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
256 [MonitoringDescription ("")]
257 public Encoding Encoding {
263 throw new ArgumentNullException ("value");
269 [DefaultValueAttribute(Handshake.None)]
271 [MonitoringDescription ("")]
272 public Handshake Handshake {
277 if (value < Handshake.None || value > Handshake.RequestToSendXOnXOff)
278 throw new ArgumentOutOfRangeException ("value");
281 stream.SetAttributes (baud_rate, parity, data_bits, stop_bits, value);
294 [DefaultValueAttribute("\n")]
296 [MonitoringDescription ("")]
297 public string NewLine {
303 throw new ArgumentNullException ("value");
304 if (value.Length == 0)
305 throw new ArgumentException ("NewLine cannot be null or empty.", "value");
311 [DefaultValueAttribute(DefaultParity)]
313 [MonitoringDescription ("")]
314 public Parity Parity {
319 if (value < Parity.None || value > Parity.Space)
320 throw new ArgumentOutOfRangeException ("value");
323 stream.SetAttributes (baud_rate, value, data_bits, stop_bits, handshake);
329 [MonoTODO("Not implemented")]
331 [MonitoringDescription ("")]
333 public byte ParityReplace {
335 throw new NotImplementedException ();
338 throw new NotImplementedException ();
344 [MonitoringDescription ("")]
345 [DefaultValue ("COM1")] // silly Windows-ism. We should ignore it.
346 public string PortName {
352 throw new InvalidOperationException ("Port name cannot be set while port is open.");
354 throw new ArgumentNullException ("value");
355 if (value.Length == 0 || value.StartsWith ("\\\\"))
356 throw new ArgumentException ("value");
362 [DefaultValueAttribute(DefaultReadBufferSize)]
364 [MonitoringDescription ("")]
365 public int ReadBufferSize {
367 return readBufferSize;
371 throw new InvalidOperationException ();
373 throw new ArgumentOutOfRangeException ("value");
374 if (value <= DefaultReadBufferSize)
377 readBufferSize = value;
381 [DefaultValueAttribute(InfiniteTimeout)]
383 [MonitoringDescription ("")]
384 public int ReadTimeout {
389 if (value < 0 && value != InfiniteTimeout)
390 throw new ArgumentOutOfRangeException ("value");
393 stream.ReadTimeout = value;
395 read_timeout = value;
399 [MonoTODO("Not implemented")]
400 [DefaultValueAttribute(1)]
402 [MonitoringDescription ("")]
403 public int ReceivedBytesThreshold {
405 throw new NotImplementedException ();
409 throw new ArgumentOutOfRangeException ("value");
411 throw new NotImplementedException ();
415 [DefaultValueAttribute(false)]
417 [MonitoringDescription ("")]
418 public bool RtsEnable {
423 if (value == rts_enable)
426 stream.SetSignal (SerialSignal.Rts, value);
432 [DefaultValueAttribute(DefaultStopBits)]
434 [MonitoringDescription ("")]
435 public StopBits StopBits {
440 if (value < StopBits.One || value > StopBits.OnePointFive)
441 throw new ArgumentOutOfRangeException ("value");
444 stream.SetAttributes (baud_rate, parity, data_bits, value, handshake);
450 [DefaultValueAttribute(DefaultWriteBufferSize)]
452 [MonitoringDescription ("")]
453 public int WriteBufferSize {
455 return writeBufferSize;
459 throw new InvalidOperationException ();
461 throw new ArgumentOutOfRangeException ("value");
462 if (value <= DefaultWriteBufferSize)
465 writeBufferSize = value;
469 [DefaultValueAttribute(InfiniteTimeout)]
471 [MonitoringDescription ("")]
472 public int WriteTimeout {
474 return write_timeout;
477 if (value < 0 && value != InfiniteTimeout)
478 throw new ArgumentOutOfRangeException ("value");
481 stream.WriteTimeout = value;
483 write_timeout = value;
494 protected override void Dispose (bool disposing)
500 // Do not close the base stream when the finalizer is run; the managed code can still hold a reference to it.
506 public void DiscardInBuffer ()
509 stream.DiscardInBuffer ();
512 public void DiscardOutBuffer ()
515 stream.DiscardOutBuffer ();
518 public static string [] GetPortNames ()
520 int p = (int) Environment.OSVersion.Platform;
521 List<string> serial_ports = new List<string>();
524 if (p == 4 || p == 128 || p == 6) {
525 string[] ttys = Directory.GetFiles("/dev/", "tty*");
526 bool linux_style = false;
529 // Probe for Linux-styled devices: /dev/ttyS* or /dev/ttyUSB*
531 foreach (string dev in ttys) {
532 if (dev.StartsWith("/dev/ttyS") || dev.StartsWith("/dev/ttyUSB") || dev.StartsWith("/dev/ttyACM")) {
538 foreach (string dev in ttys) {
540 if (dev.StartsWith("/dev/ttyS") || dev.StartsWith("/dev/ttyUSB") || dev.StartsWith("/dev/ttyACM"))
541 serial_ports.Add (dev);
543 if (dev != "/dev/tty" && dev.StartsWith ("/dev/tty") && !dev.StartsWith ("/dev/ttyC"))
544 serial_ports.Add (dev);
548 using (RegistryKey subkey = Registry.LocalMachine.OpenSubKey("HARDWARE\\DEVICEMAP\\SERIALCOMM"))
550 if (subkey != null) {
551 string[] names = subkey.GetValueNames();
552 foreach (string value in names) {
553 string port = subkey.GetValue(value, "").ToString();
555 serial_ports.Add(port);
560 return serial_ports.ToArray();
563 static bool IsWindows {
565 PlatformID id = Environment.OSVersion.Platform;
566 return id == PlatformID.Win32Windows || id == PlatformID.Win32NT; // WinCE not supported
573 throw new InvalidOperationException ("Port is already open");
575 if (IsWindows) // Use windows kernel32 backend
576 stream = new WinSerialStream (port_name, baud_rate, data_bits, parity, stop_bits, dtr_enable,
577 rts_enable, handshake, read_timeout, write_timeout, readBufferSize, writeBufferSize);
578 else // Use standard unix backend
579 stream = new SerialPortStream (port_name, baud_rate, data_bits, parity, stop_bits, dtr_enable,
580 rts_enable, handshake, read_timeout, write_timeout, readBufferSize, writeBufferSize);
585 public int Read (byte[] buffer, int offset, int count)
589 throw new ArgumentNullException ("buffer");
590 if (offset < 0 || count < 0)
591 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
593 if (buffer.Length - offset < count )
594 throw new ArgumentException ("offset+count",
595 "The size of the buffer is less than offset + count.");
597 return stream.Read (buffer, offset, count);
600 public int Read (char[] buffer, int offset, int count)
604 throw new ArgumentNullException ("buffer");
605 if (offset < 0 || count < 0)
606 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
608 if (buffer.Length - offset < count )
609 throw new ArgumentException ("offset+count",
610 "The size of the buffer is less than offset + count.");
613 for (i = 0; i < count && (c = ReadChar ()) != -1; i++)
614 buffer[offset + i] = (char) c;
619 internal int read_byte ()
621 byte [] buff = new byte [1];
622 if (stream.Read (buff, 0, 1) > 0)
628 public int ReadByte ()
634 public int ReadChar ()
638 byte [] buffer = new byte [16];
642 int b = read_byte ();
645 buffer [i++] = (byte) b;
646 char [] c = encoding.GetChars (buffer, 0, 1);
649 } while (i < buffer.Length);
654 public string ReadExisting ()
658 int count = BytesToRead;
659 byte [] bytes = new byte [count];
661 int n = stream.Read (bytes, 0, count);
662 return new String (encoding.GetChars (bytes, 0, n));
665 public string ReadLine ()
667 return ReadTo (new_line);
670 public string ReadTo (string value)
674 throw new ArgumentNullException ("value");
675 if (value.Length == 0)
676 throw new ArgumentException ("value");
678 // Turn into byte array, so we can compare
679 byte [] byte_value = encoding.GetBytes (value);
681 List<byte> seen = new List<byte> ();
684 int n = read_byte ();
688 if (n == byte_value [current]){
690 if (current == byte_value.Length)
691 return encoding.GetString (seen.ToArray (), 0, seen.Count - byte_value.Length);
693 current = (byte_value [0] == n) ? 1 : 0;
696 return encoding.GetString (seen.ToArray ());
699 public void Write (string str)
703 throw new ArgumentNullException ("str");
705 byte [] buffer = encoding.GetBytes (str);
706 Write (buffer, 0, buffer.Length);
709 public void Write (byte [] buffer, int offset, int count)
713 throw new ArgumentNullException ("buffer");
715 if (offset < 0 || count < 0)
716 throw new ArgumentOutOfRangeException ();
718 if (buffer.Length - offset < count)
719 throw new ArgumentException ("offset+count",
720 "The size of the buffer is less than offset + count.");
722 stream.Write (buffer, offset, count);
725 public void Write (char [] buffer, int offset, int count)
729 throw new ArgumentNullException ("buffer");
731 if (offset < 0 || count < 0)
732 throw new ArgumentOutOfRangeException ();
734 if (buffer.Length - offset < count)
735 throw new ArgumentException ("offset+count",
736 "The size of the buffer is less than offset + count.");
738 byte [] bytes = encoding.GetBytes (buffer, offset, count);
739 stream.Write (bytes, 0, bytes.Length);
742 public void WriteLine (string str)
744 Write (str + new_line);
750 throw new InvalidOperationException ("Specified port is not open.");
753 internal void OnErrorReceived (SerialErrorReceivedEventArgs args)
755 SerialErrorReceivedEventHandler handler =
756 (SerialErrorReceivedEventHandler) Events [error_received];
759 handler (this, args);
762 internal void OnDataReceived (SerialDataReceivedEventArgs args)
764 SerialDataReceivedEventHandler handler =
765 (SerialDataReceivedEventHandler) Events [data_received];
768 handler (this, args);
771 internal void OnDataReceived (SerialPinChangedEventArgs args)
773 SerialPinChangedEventHandler handler =
774 (SerialPinChangedEventHandler) Events [pin_changed];
777 handler (this, args);
781 [MonitoringDescription ("")]
782 public event SerialErrorReceivedEventHandler ErrorReceived {
783 add { Events.AddHandler (error_received, value); }
784 remove { Events.RemoveHandler (error_received, value); }
787 [MonitoringDescription ("")]
788 public event SerialPinChangedEventHandler PinChanged {
789 add { Events.AddHandler (pin_changed, value); }
790 remove { Events.RemoveHandler (pin_changed, value); }
793 [MonitoringDescription ("")]
794 public event SerialDataReceivedEventHandler DataReceived {
795 add { Events.AddHandler (data_received, value); }
796 remove { Events.RemoveHandler (data_received, value); }
800 public delegate void SerialDataReceivedEventHandler (object sender, SerialDataReceivedEventArgs e);
801 public delegate void SerialPinChangedEventHandler (object sender, SerialPinChangedEventArgs e);
802 public delegate void SerialErrorReceivedEventHandler (object sender, SerialErrorReceivedEventArgs e);