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 foreach (string dev in ttys) {
529 if (dev.StartsWith("/dev/ttyS") || dev.StartsWith("/dev/ttyUSB"))
530 serial_ports.Add(dev);
533 using (RegistryKey subkey = Registry.LocalMachine.OpenSubKey("HARDWARE\\DEVICEMAP\\SERIALCOMM"))
535 if (subkey != null) {
536 string[] names = subkey.GetValueNames();
537 foreach (string value in names) {
538 string port = subkey.GetValue(value, "").ToString();
540 serial_ports.Add(port);
545 return serial_ports.ToArray();
548 static bool IsWindows {
550 PlatformID id = Environment.OSVersion.Platform;
551 return id == PlatformID.Win32Windows || id == PlatformID.Win32NT; // WinCE not supported
558 throw new InvalidOperationException ("Port is already open");
561 if (IsWindows) // Use windows kernel32 backend
562 stream = new WinSerialStream (port_name, baud_rate, data_bits, parity, stop_bits, dtr_enable,
563 rts_enable, handshake, read_timeout, write_timeout, readBufferSize, writeBufferSize);
564 else // Use standard unix backend
566 stream = new SerialPortStream (port_name, baud_rate, data_bits, parity, stop_bits, dtr_enable,
567 rts_enable, handshake, read_timeout, write_timeout, readBufferSize, writeBufferSize);
572 public int Read (byte[] buffer, int offset, int count)
576 throw new ArgumentNullException ("buffer");
577 if (offset < 0 || count < 0)
578 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
580 if (buffer.Length - offset < count )
581 throw new ArgumentException ("offset+count",
582 "The size of the buffer is less than offset + count.");
584 return stream.Read (buffer, offset, count);
587 [MonoTODO("Read of char buffers is currently broken")]
588 public int Read (char[] buffer, int offset, int count)
592 throw new ArgumentNullException ("buffer");
593 if (offset < 0 || count < 0)
594 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
596 if (buffer.Length - offset < count )
597 throw new ArgumentException ("offset+count",
598 "The size of the buffer is less than offset + count.");
600 // The following code does not work, we nee to reintroduce a buffer stream somewhere
601 // for this to work; In addition the code is broken.
602 byte [] bytes = encoding.GetBytes (buffer, offset, count);
603 return stream.Read (bytes, 0, bytes.Length);
606 internal int read_byte ()
608 byte [] buff = new byte [1];
609 if (stream.Read (buff, 0, 1) > 0)
615 public int ReadByte ()
621 public int ReadChar ()
625 byte [] buffer = new byte [16];
629 int b = read_byte ();
632 buffer [i++] = (byte) b;
633 char [] c = encoding.GetChars (buffer, 0, 1);
636 } while (i < buffer.Length);
641 public string ReadExisting ()
645 int count = BytesToRead;
646 byte [] bytes = new byte [count];
648 int n = stream.Read (bytes, 0, count);
649 return new String (encoding.GetChars (bytes, 0, n));
652 public string ReadLine ()
654 return ReadTo (new_line);
657 public string ReadTo (string value)
661 throw new ArgumentNullException ("value");
662 if (value.Length == 0)
663 throw new ArgumentException ("value");
665 // Turn into byte array, so we can compare
666 byte [] byte_value = encoding.GetBytes (value);
668 List<byte> seen = new List<byte> ();
671 int n = read_byte ();
675 if (n == byte_value [current]){
677 if (current == byte_value.Length)
678 return encoding.GetString (seen.ToArray (), 0, seen.Count - byte_value.Length);
680 current = (byte_value [0] == n) ? 1 : 0;
683 return encoding.GetString (seen.ToArray ());
686 public void Write (string str)
690 throw new ArgumentNullException ("str");
692 byte [] buffer = encoding.GetBytes (str);
693 Write (buffer, 0, buffer.Length);
696 public void Write (byte [] buffer, int offset, int count)
700 throw new ArgumentNullException ("buffer");
702 if (offset < 0 || count < 0)
703 throw new ArgumentOutOfRangeException ();
705 if (buffer.Length - offset < count)
706 throw new ArgumentException ("offset+count",
707 "The size of the buffer is less than offset + count.");
709 stream.Write (buffer, offset, count);
712 public void Write (char [] buffer, int offset, int count)
716 throw new ArgumentNullException ("buffer");
718 if (offset < 0 || count < 0)
719 throw new ArgumentOutOfRangeException ();
721 if (buffer.Length - offset < count)
722 throw new ArgumentException ("offset+count",
723 "The size of the buffer is less than offset + count.");
725 byte [] bytes = encoding.GetBytes (buffer, offset, count);
726 stream.Write (bytes, 0, bytes.Length);
729 public void WriteLine (string str)
731 Write (str + new_line);
737 throw new InvalidOperationException ("Specified port is not open.");
740 internal void OnErrorReceived (SerialErrorReceivedEventArgs args)
742 SerialErrorReceivedEventHandler handler =
743 (SerialErrorReceivedEventHandler) Events [error_received];
746 handler (this, args);
749 internal void OnDataReceived (SerialDataReceivedEventArgs args)
751 SerialDataReceivedEventHandler handler =
752 (SerialDataReceivedEventHandler) Events [data_received];
755 handler (this, args);
758 internal void OnDataReceived (SerialPinChangedEventArgs args)
760 SerialPinChangedEventHandler handler =
761 (SerialPinChangedEventHandler) Events [pin_changed];
764 handler (this, args);
768 [MonitoringDescription ("")]
769 public event SerialErrorReceivedEventHandler ErrorReceived {
770 add { Events.AddHandler (error_received, value); }
771 remove { Events.RemoveHandler (error_received, value); }
774 [MonitoringDescription ("")]
775 public event SerialPinChangedEventHandler PinChanged {
776 add { Events.AddHandler (pin_changed, value); }
777 remove { Events.RemoveHandler (pin_changed, value); }
780 [MonitoringDescription ("")]
781 public event SerialDataReceivedEventHandler DataReceived {
782 add { Events.AddHandler (data_received, value); }
783 remove { Events.RemoveHandler (data_received, value); }
787 public delegate void SerialDataReceivedEventHandler (object sender, SerialDataReceivedEventArgs e);
788 public delegate void SerialPinChangedEventHandler (object sender, SerialPinChangedEventArgs e);
789 public delegate void SerialErrorReceivedEventHandler (object sender, SerialErrorReceivedEventArgs e);