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;
26 using System.Runtime.InteropServices;
28 namespace System.IO.Ports
30 public class SerialPort : Component
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;
46 bool break_state = false;
47 bool dtr_enable = false;
48 bool rts_enable = false;
50 Encoding encoding = Encoding.ASCII;
51 string new_line = Environment.NewLine;
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 ();
61 static string default_port_name = "ttyS0";
63 public SerialPort () :
64 this (GetDefaultPortName (), DefaultBaudRate, DefaultParity, DefaultDataBits, DefaultStopBits)
68 public SerialPort (IContainer container) : this ()
70 // TODO: What to do here?
73 public SerialPort (string portName) :
74 this (portName, DefaultBaudRate, DefaultParity, DefaultDataBits, DefaultStopBits)
78 public SerialPort (string portName, int baudRate) :
79 this (portName, baudRate, DefaultParity, DefaultDataBits, DefaultStopBits)
83 public SerialPort (string portName, int baudRate, Parity parity) :
84 this (portName, baudRate, parity, DefaultDataBits, DefaultStopBits)
88 public SerialPort (string portName, int baudRate, Parity parity, int dataBits) :
89 this (portName, baudRate, parity, dataBits, DefaultStopBits)
93 public SerialPort (string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits)
102 static string GetDefaultPortName ()
104 return default_port_name;
107 public Stream BaseStream {
110 throw new InvalidOperationException ();
112 return (Stream) stream;
116 [DefaultValueAttribute (DefaultBaudRate)]
117 public int BaudRate {
123 throw new ArgumentOutOfRangeException ("value");
126 stream.SetAttributes (value, parity, data_bits, stop_bits, handshake);
132 public bool BreakState {
138 if (value == break_state)
139 return; // Do nothing.
141 stream.SetBreakState (value);
146 public int BytesToRead {
149 return stream.BytesToRead;
153 public int BytesToWrite {
156 return stream.BytesToWrite;
160 public bool CDHolding {
163 return (stream.GetSignals () & SerialSignal.Cd) != 0;
167 public bool CtsHolding {
170 return (stream.GetSignals () & SerialSignal.Cts) != 0;
174 [DefaultValueAttribute(DefaultDataBits)]
175 public int DataBits {
180 if (value < 5 || value > 8)
181 throw new ArgumentOutOfRangeException ("value");
184 stream.SetAttributes (baud_rate, parity, value, stop_bits, handshake);
190 [MonoTODO("Not implemented")]
191 public bool DiscardNull {
194 throw new NotImplementedException ();
198 throw new NotImplementedException ();
202 public bool DsrHolding {
205 return (stream.GetSignals () & SerialSignal.Dsr) != 0;
209 [DefaultValueAttribute(false)]
210 public bool DtrEnable {
215 if (value == dtr_enable)
218 stream.SetSignal (SerialSignal.Dtr, value);
224 public Encoding Encoding {
230 throw new ArgumentNullException ("value");
236 [DefaultValueAttribute(Handshake.None)]
237 public Handshake Handshake {
242 if (value < Handshake.None || value > Handshake.RequestToSendXOnXOff)
243 throw new ArgumentOutOfRangeException ("value");
246 stream.SetAttributes (baud_rate, parity, data_bits, stop_bits, value);
258 [DefaultValueAttribute("\n")]
259 public string NewLine {
265 throw new ArgumentNullException ("value");
271 [DefaultValueAttribute(DefaultParity)]
272 public Parity Parity {
277 if (value < Parity.None || value > Parity.Space)
278 throw new ArgumentOutOfRangeException ("value");
281 stream.SetAttributes (baud_rate, value, data_bits, stop_bits, handshake);
287 [MonoTODO("Not implemented")]
288 public byte ParityReplace {
290 throw new NotImplementedException ();
293 throw new NotImplementedException ();
298 public string PortName {
304 throw new InvalidOperationException ("Port name cannot be set while port is open.");
306 throw new ArgumentNullException ("value");
307 if (value.Length == 0 || value.StartsWith ("\\\\"))
308 throw new ArgumentException ("value");
314 [DefaultValueAttribute(DefaultReadBufferSize)]
315 public int ReadBufferSize {
317 return readBufferSize;
321 throw new InvalidOperationException ();
323 throw new ArgumentOutOfRangeException ("value");
324 if (value <= DefaultReadBufferSize)
327 readBufferSize = value;
331 [DefaultValueAttribute(InfiniteTimeout)]
332 public int ReadTimeout {
337 if (value <= 0 && value != InfiniteTimeout)
338 throw new ArgumentOutOfRangeException ("value");
341 stream.ReadTimeout = value;
343 read_timeout = value;
347 [MonoTODO("Not implemented")]
348 [DefaultValueAttribute(1)]
349 public int ReceivedBytesThreshold {
351 throw new NotImplementedException ();
355 throw new ArgumentOutOfRangeException ("value");
357 throw new NotImplementedException ();
361 [DefaultValueAttribute(false)]
362 public bool RtsEnable {
367 if (value == rts_enable)
370 stream.SetSignal (SerialSignal.Rts, value);
376 [DefaultValueAttribute(DefaultStopBits)]
377 public StopBits StopBits {
382 if (value < StopBits.One || value > StopBits.OnePointFive)
383 throw new ArgumentOutOfRangeException ("value");
386 stream.SetAttributes (baud_rate, parity, data_bits, value, handshake);
392 [DefaultValueAttribute(DefaultWriteBufferSize)]
393 public int WriteBufferSize {
395 return writeBufferSize;
399 throw new InvalidOperationException ();
401 throw new ArgumentOutOfRangeException ("value");
402 if (value <= DefaultWriteBufferSize)
405 writeBufferSize = value;
409 [DefaultValueAttribute(InfiniteTimeout)]
410 public int WriteTimeout {
412 return write_timeout;
415 if (value <= 0 && value != InfiniteTimeout)
416 throw new ArgumentOutOfRangeException ("value");
419 stream.WriteTimeout = value;
421 write_timeout = value;
432 protected override void Dispose (bool disposing)
442 public void DiscardInBuffer ()
445 stream.DiscardInBuffer ();
448 public void DiscardOutBuffer ()
451 stream.DiscardOutBuffer ();
454 static Exception GetNotImplemented ()
456 return new NotImplementedException ("Detection of ports is not implemented for this platform yet.");
459 public static string [] GetPortNames ()
461 int p = (int) Environment.OSVersion.Platform;
464 if (p == 4 || p == 128){
465 string [] ttys = Directory.GetFiles ("/dev/", "tty*");
466 List<string> serial_ports = new List<string> ();
468 foreach (string dev in ttys){
469 if (dev.StartsWith ("/dev/ttyS") || dev.StartsWith ("/dev/ttyUSB"))
470 serial_ports.Add (dev);
473 return serial_ports.ToArray ();
475 throw GetNotImplemented ();
478 static bool IsWindows {
480 PlatformID id = Environment.OSVersion.Platform;
481 return id == PlatformID.Win32Windows || id == PlatformID.Win32NT; // WinCE not supported
488 throw new InvalidOperationException ("Port is already open");
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
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);
502 public int Read (byte[] buffer, int offset, int count)
506 throw new ArgumentNullException ("buffer");
507 if (offset < 0 || count < 0)
508 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
510 if (buffer.Length - offset < count )
511 throw new ArgumentException ("offset+count",
512 "The size of the buffer is less than offset + count.");
514 return stream.Read (buffer, offset, count);
517 [Obsolete("Read of char buffers is currently broken")]
518 [MonoTODO("This is broken")]
519 public int Read (char[] buffer, int offset, int count)
523 throw new ArgumentNullException ("buffer");
524 if (offset < 0 || count < 0)
525 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
527 if (buffer.Length - offset < count )
528 throw new ArgumentException ("offset+count",
529 "The size of the buffer is less than offset + count.");
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);
537 internal int read_byte ()
539 byte [] buff = new byte [1];
540 if (stream.Read (buff, 0, 1) > 0)
546 public int ReadByte ()
552 public int ReadChar ()
556 byte [] buffer = new byte [16];
560 int b = read_byte ();
563 buffer [i++] = (byte) b;
564 char [] c = encoding.GetChars (buffer, 0, 1);
567 } while (i < buffer.Length);
572 public string ReadExisting ()
576 int count = BytesToRead;
577 byte [] bytes = new byte [count];
579 int n = stream.Read (bytes, 0, count);
580 return new String (encoding.GetChars (bytes, 0, n));
583 public string ReadLine ()
586 List<byte> bytes_read = new List<byte>();
587 byte [] buff = new byte [1];
590 int n = stream.Read (buff, 0, 1);
591 if (n == -1 || buff [0] == '\n')
593 bytes_read.Add (buff [0]);
595 return new String (encoding.GetChars (bytes_read.ToArray ()));
598 public string ReadTo (string value)
602 throw new ArgumentNullException ("value");
603 if (value.Length == 0)
604 throw new ArgumentException ("value");
606 // Turn into byte array, so we can compare
607 byte [] byte_value = encoding.GetBytes (value);
609 List<byte> seen = new List<byte> ();
612 int n = read_byte ();
616 if (n == byte_value [current]){
618 if (current == byte_value.Length)
619 return encoding.GetString (seen.ToArray (), 0, seen.Count - byte_value.Length);
621 current = (byte_value [0] == n) ? 1 : 0;
624 return encoding.GetString (seen.ToArray ());
627 public void Write (string str)
631 throw new ArgumentNullException ("str");
633 byte [] buffer = encoding.GetBytes (str);
634 Write (buffer, 0, buffer.Length);
637 public void Write (byte [] buffer, int offset, int count)
641 throw new ArgumentNullException ("buffer");
643 if (offset < 0 || count < 0)
644 throw new ArgumentOutOfRangeException ();
646 if (buffer.Length - offset < count)
647 throw new ArgumentException ("offset+count",
648 "The size of the buffer is less than offset + count.");
650 stream.Write (buffer, offset, count);
653 public void Write (char [] buffer, int offset, int count)
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");
665 byte [] bytes = encoding.GetBytes (buffer, offset, count);
666 stream.Write (bytes, 0, bytes.Length);
669 public void WriteLine (string str)
671 Write (str + new_line);
677 throw new InvalidOperationException ("Specified port is not open.");
680 internal void OnErrorReceived (SerialErrorReceivedEventArgs args)
682 SerialErrorReceivedEventHandler handler =
683 (SerialErrorReceivedEventHandler) Events [error_received];
686 handler (this, args);
689 internal void OnDataReceived (SerialDataReceivedEventArgs args)
691 SerialDataReceivedEventHandler handler =
692 (SerialDataReceivedEventHandler) Events [data_received];
695 handler (this, args);
698 internal void OnDataReceived (SerialPinChangedEventArgs args)
700 SerialPinChangedEventHandler handler =
701 (SerialPinChangedEventHandler) Events [pin_changed];
704 handler (this, args);
708 public event SerialErrorReceivedEventHandler ErrorReceived {
709 add { Events.AddHandler (error_received, value); }
710 remove { Events.RemoveHandler (error_received, value); }
713 public event SerialPinChangedEventHandler PinChanged {
714 add { Events.AddHandler (pin_changed, value); }
715 remove { Events.RemoveHandler (pin_changed, value); }
718 public event SerialDataReceivedEventHandler DataReceived {
719 add { Events.AddHandler (data_received, value); }
720 remove { Events.RemoveHandler (data_received, value); }
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);