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
23 using System.Collections.Generic;
24 using System.ComponentModel;
25 using System.Diagnostics;
27 using System.Runtime.InteropServices;
28 using Microsoft.Win32;
30 namespace System.IO.Ports
32 [MonitoringDescription ("")]
33 public class SerialPort : Component
35 public const int InfiniteTimeout = -1;
36 const int DefaultReadBufferSize = 4096;
37 const int DefaultWriteBufferSize = 2048;
38 const int DefaultBaudRate = 9600;
39 const int DefaultDataBits = 8;
40 const Parity DefaultParity = Parity.None;
41 const StopBits DefaultStopBits = StopBits.One;
49 bool break_state = false;
50 bool dtr_enable = false;
51 bool rts_enable = false;
53 Encoding encoding = Encoding.ASCII;
54 string new_line = Environment.NewLine;
56 int read_timeout = InfiniteTimeout;
57 int write_timeout = InfiniteTimeout;
58 int readBufferSize = DefaultReadBufferSize;
59 int writeBufferSize = DefaultWriteBufferSize;
60 object error_received = new object ();
61 object data_received = new object ();
62 object pin_changed = new object ();
64 public SerialPort () :
65 this (GetDefaultPortName (), DefaultBaudRate, DefaultParity, DefaultDataBits, DefaultStopBits)
69 public SerialPort (IContainer container) : this ()
71 // TODO: What to do here?
74 public SerialPort (string portName) :
75 this (portName, DefaultBaudRate, DefaultParity, DefaultDataBits, DefaultStopBits)
79 public SerialPort (string portName, int baudRate) :
80 this (portName, baudRate, DefaultParity, DefaultDataBits, DefaultStopBits)
84 public SerialPort (string portName, int baudRate, Parity parity) :
85 this (portName, baudRate, parity, DefaultDataBits, DefaultStopBits)
89 public SerialPort (string portName, int baudRate, Parity parity, int dataBits) :
90 this (portName, baudRate, parity, dataBits, DefaultStopBits)
94 public SerialPort (string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits)
100 this.parity = parity;
103 static string GetDefaultPortName ()
105 string[] ports = GetPortNames();
106 if (ports.Length > 0) {
109 int p = (int)Environment.OSVersion.Platform;
110 if (p == 4 || p == 128 || p == 6)
111 return "ttyS0"; // Default for Unix
113 return "COM1"; // Default for Windows
118 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
119 public Stream BaseStream {
122 return (Stream) stream;
126 [DefaultValueAttribute (DefaultBaudRate)]
128 [MonitoringDescription ("")]
129 public int BaudRate {
135 throw new ArgumentOutOfRangeException ("value");
138 stream.SetAttributes (value, parity, data_bits, stop_bits, handshake);
145 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
146 public bool BreakState {
152 if (value == break_state)
153 return; // Do nothing.
155 stream.SetBreakState (value);
161 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
162 public int BytesToRead {
165 return stream.BytesToRead;
170 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
171 public int BytesToWrite {
174 return stream.BytesToWrite;
179 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
180 public bool CDHolding {
183 return (stream.GetSignals () & SerialSignal.Cd) != 0;
188 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
189 public bool CtsHolding {
192 return (stream.GetSignals () & SerialSignal.Cts) != 0;
196 [DefaultValueAttribute(DefaultDataBits)]
198 [MonitoringDescription ("")]
199 public int DataBits {
204 if (value < 5 || value > 8)
205 throw new ArgumentOutOfRangeException ("value");
208 stream.SetAttributes (baud_rate, parity, value, stop_bits, handshake);
214 [MonoTODO("Not implemented")]
216 [MonitoringDescription ("")]
217 [DefaultValue (false)]
218 public bool DiscardNull {
220 throw new NotImplementedException ();
223 // LAMESPEC: Msdn states that an InvalidOperationException exception
224 // is fired if the port is not open, which is *not* happening.
226 throw new NotImplementedException ();
231 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
232 public bool DsrHolding {
235 return (stream.GetSignals () & SerialSignal.Dsr) != 0;
239 [DefaultValueAttribute(false)]
241 [MonitoringDescription ("")]
242 public bool DtrEnable {
247 if (value == dtr_enable)
250 stream.SetSignal (SerialSignal.Dtr, value);
257 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
258 [MonitoringDescription ("")]
259 public Encoding Encoding {
265 throw new ArgumentNullException ("value");
271 [DefaultValueAttribute(Handshake.None)]
273 [MonitoringDescription ("")]
274 public Handshake Handshake {
279 if (value < Handshake.None || value > Handshake.RequestToSendXOnXOff)
280 throw new ArgumentOutOfRangeException ("value");
283 stream.SetAttributes (baud_rate, parity, data_bits, stop_bits, value);
296 [DefaultValueAttribute("\n")]
298 [MonitoringDescription ("")]
299 public string NewLine {
305 throw new ArgumentNullException ("value");
306 if (value.Length == 0)
307 throw new ArgumentException ("NewLine cannot be null or empty.", "value");
313 [DefaultValueAttribute(DefaultParity)]
315 [MonitoringDescription ("")]
316 public Parity Parity {
321 if (value < Parity.None || value > Parity.Space)
322 throw new ArgumentOutOfRangeException ("value");
325 stream.SetAttributes (baud_rate, value, data_bits, stop_bits, handshake);
331 [MonoTODO("Not implemented")]
333 [MonitoringDescription ("")]
335 public byte ParityReplace {
337 throw new NotImplementedException ();
340 throw new NotImplementedException ();
346 [MonitoringDescription ("")]
347 [DefaultValue ("COM1")] // silly Windows-ism. We should ignore it.
348 public string PortName {
354 throw new InvalidOperationException ("Port name cannot be set while port is open.");
356 throw new ArgumentNullException ("value");
357 if (value.Length == 0 || value.StartsWith ("\\\\"))
358 throw new ArgumentException ("value");
364 [DefaultValueAttribute(DefaultReadBufferSize)]
366 [MonitoringDescription ("")]
367 public int ReadBufferSize {
369 return readBufferSize;
373 throw new InvalidOperationException ();
375 throw new ArgumentOutOfRangeException ("value");
376 if (value <= DefaultReadBufferSize)
379 readBufferSize = value;
383 [DefaultValueAttribute(InfiniteTimeout)]
385 [MonitoringDescription ("")]
386 public int ReadTimeout {
391 if (value <= 0 && value != InfiniteTimeout)
392 throw new ArgumentOutOfRangeException ("value");
395 stream.ReadTimeout = value;
397 read_timeout = value;
401 [MonoTODO("Not implemented")]
402 [DefaultValueAttribute(1)]
404 [MonitoringDescription ("")]
405 public int ReceivedBytesThreshold {
407 throw new NotImplementedException ();
411 throw new ArgumentOutOfRangeException ("value");
413 throw new NotImplementedException ();
417 [DefaultValueAttribute(false)]
419 [MonitoringDescription ("")]
420 public bool RtsEnable {
425 if (value == rts_enable)
428 stream.SetSignal (SerialSignal.Rts, value);
434 [DefaultValueAttribute(DefaultStopBits)]
436 [MonitoringDescription ("")]
437 public StopBits StopBits {
442 if (value < StopBits.One || value > StopBits.OnePointFive)
443 throw new ArgumentOutOfRangeException ("value");
446 stream.SetAttributes (baud_rate, parity, data_bits, value, handshake);
452 [DefaultValueAttribute(DefaultWriteBufferSize)]
454 [MonitoringDescription ("")]
455 public int WriteBufferSize {
457 return writeBufferSize;
461 throw new InvalidOperationException ();
463 throw new ArgumentOutOfRangeException ("value");
464 if (value <= DefaultWriteBufferSize)
467 writeBufferSize = value;
471 [DefaultValueAttribute(InfiniteTimeout)]
473 [MonitoringDescription ("")]
474 public int WriteTimeout {
476 return write_timeout;
479 if (value <= 0 && value != InfiniteTimeout)
480 throw new ArgumentOutOfRangeException ("value");
483 stream.WriteTimeout = value;
485 write_timeout = value;
496 protected override void Dispose (bool disposing)
502 // Do not close the base stream when the finalizer is run; the managed code can still hold a reference to it.
508 public void DiscardInBuffer ()
511 stream.DiscardInBuffer ();
514 public void DiscardOutBuffer ()
517 stream.DiscardOutBuffer ();
520 public static string [] GetPortNames ()
522 int p = (int) Environment.OSVersion.Platform;
523 List<string> serial_ports = new List<string>();
526 if (p == 4 || p == 128 || p == 6) {
527 string[] ttys = Directory.GetFiles("/dev/", "tty*");
528 bool linux_style = false;
531 // Probe for Linux-styled devices: /dev/ttyS* or /dev/ttyUSB*
533 foreach (string dev in ttys) {
534 if (dev.StartsWith("/dev/ttyS") || dev.StartsWith("/dev/ttyUSB")){
540 foreach (string dev in ttys) {
542 if (dev.StartsWith("/dev/ttyS") || dev.StartsWith("/dev/ttyUSB"))
543 serial_ports.Add (dev);
545 if (dev != "/dev/tty" && dev.StartsWith ("/dev/tty") && !dev.StartsWith ("/dev/ttyC"))
546 serial_ports.Add (dev);
550 using (RegistryKey subkey = Registry.LocalMachine.OpenSubKey("HARDWARE\\DEVICEMAP\\SERIALCOMM"))
552 if (subkey != null) {
553 string[] names = subkey.GetValueNames();
554 foreach (string value in names) {
555 string port = subkey.GetValue(value, "").ToString();
557 serial_ports.Add(port);
562 return serial_ports.ToArray();
565 static bool IsWindows {
567 PlatformID id = Environment.OSVersion.Platform;
568 return id == PlatformID.Win32Windows || id == PlatformID.Win32NT; // WinCE not supported
575 throw new InvalidOperationException ("Port is already open");
578 if (IsWindows) // Use windows kernel32 backend
579 stream = new WinSerialStream (port_name, baud_rate, data_bits, parity, stop_bits, dtr_enable,
580 rts_enable, handshake, read_timeout, write_timeout, readBufferSize, writeBufferSize);
581 else // Use standard unix backend
583 stream = new SerialPortStream (port_name, baud_rate, data_bits, parity, stop_bits, dtr_enable,
584 rts_enable, handshake, read_timeout, write_timeout, readBufferSize, writeBufferSize);
589 public int Read (byte[] buffer, int offset, int count)
593 throw new ArgumentNullException ("buffer");
594 if (offset < 0 || count < 0)
595 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
597 if (buffer.Length - offset < count )
598 throw new ArgumentException ("offset+count",
599 "The size of the buffer is less than offset + count.");
601 return stream.Read (buffer, offset, count);
604 [MonoTODO("Read of char buffers is currently broken")]
605 public int Read (char[] buffer, int offset, int count)
609 throw new ArgumentNullException ("buffer");
610 if (offset < 0 || count < 0)
611 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
613 if (buffer.Length - offset < count )
614 throw new ArgumentException ("offset+count",
615 "The size of the buffer is less than offset + count.");
617 // The following code does not work, we nee to reintroduce a buffer stream somewhere
618 // for this to work; In addition the code is broken.
619 byte [] bytes = encoding.GetBytes (buffer, offset, count);
620 return stream.Read (bytes, 0, bytes.Length);
623 internal int read_byte ()
625 byte [] buff = new byte [1];
626 if (stream.Read (buff, 0, 1) > 0)
632 public int ReadByte ()
638 public int ReadChar ()
642 byte [] buffer = new byte [16];
646 int b = read_byte ();
649 buffer [i++] = (byte) b;
650 char [] c = encoding.GetChars (buffer, 0, 1);
653 } while (i < buffer.Length);
658 public string ReadExisting ()
662 int count = BytesToRead;
663 byte [] bytes = new byte [count];
665 int n = stream.Read (bytes, 0, count);
666 return new String (encoding.GetChars (bytes, 0, n));
669 public string ReadLine ()
671 return ReadTo (new_line);
674 public string ReadTo (string value)
678 throw new ArgumentNullException ("value");
679 if (value.Length == 0)
680 throw new ArgumentException ("value");
682 // Turn into byte array, so we can compare
683 byte [] byte_value = encoding.GetBytes (value);
685 List<byte> seen = new List<byte> ();
688 int n = read_byte ();
692 if (n == byte_value [current]){
694 if (current == byte_value.Length)
695 return encoding.GetString (seen.ToArray (), 0, seen.Count - byte_value.Length);
697 current = (byte_value [0] == n) ? 1 : 0;
700 return encoding.GetString (seen.ToArray ());
703 public void Write (string str)
707 throw new ArgumentNullException ("str");
709 byte [] buffer = encoding.GetBytes (str);
710 Write (buffer, 0, buffer.Length);
713 public void Write (byte [] buffer, int offset, int count)
717 throw new ArgumentNullException ("buffer");
719 if (offset < 0 || count < 0)
720 throw new ArgumentOutOfRangeException ();
722 if (buffer.Length - offset < count)
723 throw new ArgumentException ("offset+count",
724 "The size of the buffer is less than offset + count.");
726 stream.Write (buffer, offset, count);
729 public void Write (char [] buffer, int offset, int count)
733 throw new ArgumentNullException ("buffer");
735 if (offset < 0 || count < 0)
736 throw new ArgumentOutOfRangeException ();
738 if (buffer.Length - offset < count)
739 throw new ArgumentException ("offset+count",
740 "The size of the buffer is less than offset + count.");
742 byte [] bytes = encoding.GetBytes (buffer, offset, count);
743 stream.Write (bytes, 0, bytes.Length);
746 public void WriteLine (string str)
748 Write (str + new_line);
754 throw new InvalidOperationException ("Specified port is not open.");
757 internal void OnErrorReceived (SerialErrorReceivedEventArgs args)
759 SerialErrorReceivedEventHandler handler =
760 (SerialErrorReceivedEventHandler) Events [error_received];
763 handler (this, args);
766 internal void OnDataReceived (SerialDataReceivedEventArgs args)
768 SerialDataReceivedEventHandler handler =
769 (SerialDataReceivedEventHandler) Events [data_received];
772 handler (this, args);
775 internal void OnDataReceived (SerialPinChangedEventArgs args)
777 SerialPinChangedEventHandler handler =
778 (SerialPinChangedEventHandler) Events [pin_changed];
781 handler (this, args);
785 [MonitoringDescription ("")]
786 public event SerialErrorReceivedEventHandler ErrorReceived {
787 add { Events.AddHandler (error_received, value); }
788 remove { Events.RemoveHandler (error_received, value); }
791 [MonitoringDescription ("")]
792 public event SerialPinChangedEventHandler PinChanged {
793 add { Events.AddHandler (pin_changed, value); }
794 remove { Events.RemoveHandler (pin_changed, value); }
797 [MonitoringDescription ("")]
798 public event SerialDataReceivedEventHandler DataReceived {
799 add { Events.AddHandler (data_received, value); }
800 remove { Events.RemoveHandler (data_received, value); }
804 public delegate void SerialDataReceivedEventHandler (object sender, SerialDataReceivedEventArgs e);
805 public delegate void SerialPinChangedEventHandler (object sender, SerialPinChangedEventArgs e);
806 public delegate void SerialErrorReceivedEventHandler (object sender, SerialErrorReceivedEventArgs e);