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)
70 public SerialPort (IContainer container) {
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 return default_port_name;
108 public Stream BaseStream {
111 throw new InvalidOperationException ();
113 return (Stream) stream;
117 [DefaultValueAttribute (DefaultBaudRate)]
118 public int BaudRate {
124 throw new ArgumentOutOfRangeException ("value");
127 stream.SetAttributes (value, parity, data_bits, stop_bits, handshake);
133 public bool BreakState {
139 if (value == break_state)
140 return; // Do nothing.
142 stream.SetBreakState (value);
147 public int BytesToRead {
150 return stream.BytesToRead;
154 public int BytesToWrite {
157 return stream.BytesToWrite;
161 public bool CDHolding {
164 return (stream.GetSignals () & SerialSignal.Cd) != 0;
168 public bool CtsHolding {
171 return (stream.GetSignals () & SerialSignal.Cts) != 0;
175 [DefaultValueAttribute(DefaultDataBits)]
176 public int DataBits {
181 if (value < 5 || value > 8)
182 throw new ArgumentOutOfRangeException ("value");
185 stream.SetAttributes (baud_rate, parity, value, stop_bits, handshake);
191 [MonoTODO("Not implemented")]
192 public bool DiscardNull {
195 throw new NotImplementedException ();
199 throw new NotImplementedException ();
203 public bool DsrHolding {
206 return (stream.GetSignals () & SerialSignal.Dsr) != 0;
210 [DefaultValueAttribute(false)]
211 public bool DtrEnable {
216 if (value == dtr_enable)
219 stream.SetSignal (SerialSignal.Dtr, value);
225 public Encoding Encoding {
231 throw new ArgumentNullException ("value");
237 [DefaultValueAttribute(Handshake.None)]
238 public Handshake Handshake {
243 if (value < Handshake.None || value > Handshake.RequestToSendXOnXOff)
244 throw new ArgumentOutOfRangeException ("value");
247 stream.SetAttributes (baud_rate, parity, data_bits, stop_bits, value);
259 [DefaultValueAttribute("\n")]
260 public string NewLine {
266 throw new ArgumentNullException ("value");
272 [DefaultValueAttribute(DefaultParity)]
273 public Parity Parity {
278 if (value < Parity.None || value > Parity.Space)
279 throw new ArgumentOutOfRangeException ("value");
282 stream.SetAttributes (baud_rate, value, data_bits, stop_bits, handshake);
288 [MonoTODO("Not implemented")]
289 public byte ParityReplace {
291 throw new NotImplementedException ();
294 throw new NotImplementedException ();
299 public string PortName {
305 throw new InvalidOperationException ("Port name cannot be set while port is open.");
307 throw new ArgumentNullException ("value");
308 if (value.Length == 0 || value.StartsWith ("\\\\"))
309 throw new ArgumentException ("value");
315 [DefaultValueAttribute(DefaultReadBufferSize)]
316 public int ReadBufferSize {
318 return readBufferSize;
322 throw new InvalidOperationException ();
324 throw new ArgumentOutOfRangeException ("value");
325 if (value <= DefaultReadBufferSize)
328 readBufferSize = value;
332 [DefaultValueAttribute(InfiniteTimeout)]
333 public int ReadTimeout {
338 if (value <= 0 && value != InfiniteTimeout)
339 throw new ArgumentOutOfRangeException ("value");
342 stream.ReadTimeout = value;
344 read_timeout = value;
348 [MonoTODO("Not implemented")]
349 [DefaultValueAttribute(1)]
350 public int ReceivedBytesThreshold {
352 throw new NotImplementedException ();
356 throw new ArgumentOutOfRangeException ("value");
358 throw new NotImplementedException ();
362 [DefaultValueAttribute(false)]
363 public bool RtsEnable {
368 if (value == rts_enable)
371 stream.SetSignal (SerialSignal.Rts, value);
377 [DefaultValueAttribute(DefaultStopBits)]
378 public StopBits StopBits {
383 if (value < StopBits.One || value > StopBits.OnePointFive)
384 throw new ArgumentOutOfRangeException ("value");
387 stream.SetAttributes (baud_rate, parity, data_bits, value, handshake);
393 [DefaultValueAttribute(DefaultWriteBufferSize)]
394 public int WriteBufferSize {
396 return writeBufferSize;
400 throw new InvalidOperationException ();
402 throw new ArgumentOutOfRangeException ("value");
403 if (value <= DefaultWriteBufferSize)
406 writeBufferSize = value;
410 [DefaultValueAttribute(InfiniteTimeout)]
411 public int WriteTimeout {
413 return write_timeout;
416 if (value <= 0 && value != InfiniteTimeout)
417 throw new ArgumentOutOfRangeException ("value");
420 stream.WriteTimeout = value;
422 write_timeout = value;
433 protected override void Dispose (bool disposing)
443 public void DiscardInBuffer ()
446 stream.DiscardInBuffer ();
449 public void DiscardOutBuffer ()
452 stream.DiscardOutBuffer ();
455 public static string [] GetPortNames ()
457 int p = (int) Environment.OSVersion.Platform;
460 if (p == 4 || p == 128){
461 string [] ttys = Directory.GetFiles ("/dev/", "tty*");
462 List<string> serial_ports = new List<string> ();
464 foreach (string dev in ttys){
465 if (dev.StartsWith ("/dev/ttyS") || dev.StartsWith ("/dev/ttyUSB"))
466 serial_ports.Add (dev);
469 return serial_ports.ToArray ();
471 throw new NotImplementedException ("Detection of ports is not implemented for this platform yet.");
474 static bool IsWindows {
476 PlatformID id = Environment.OSVersion.Platform;
477 return id == PlatformID.Win32Windows || id == PlatformID.Win32NT; // WinCE not supported
484 throw new InvalidOperationException ("Port is already open");
487 if (IsWindows) // Use windows kernel32 backend
488 stream = new WinSerialStream (port_name, baud_rate, data_bits, parity, stop_bits,
489 handshake, read_timeout, write_timeout, readBufferSize, writeBufferSize);
490 else // Use standard unix backend
492 stream = new SerialPortStream (port_name, baud_rate, data_bits, parity, stop_bits, dtr_enable,
493 rts_enable, handshake, read_timeout, write_timeout, readBufferSize, writeBufferSize);
498 public int Read (byte[] buffer, int offset, int count)
502 throw new ArgumentNullException ("buffer");
503 if (offset < 0 || count < 0)
504 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
506 if (buffer.Length - offset < count )
507 throw new ArgumentException ("offset+count",
508 "The size of the buffer is less than offset + count.");
510 return stream.Read (buffer, offset, count);
513 [Obsolete("Read of char buffers is currently broken")]
514 [MonoTODO("This is broken")]
515 public int Read (char[] buffer, int offset, int count)
519 throw new ArgumentNullException ("buffer");
520 if (offset < 0 || count < 0)
521 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
523 if (buffer.Length - offset < count )
524 throw new ArgumentException ("offset+count",
525 "The size of the buffer is less than offset + count.");
527 // The following code does not work, we nee to reintroduce a buffer stream somewhere
528 // for this to work; In addition the code is broken.
529 byte [] bytes = encoding.GetBytes (buffer, offset, count);
530 return stream.Read (bytes, 0, bytes.Length);
533 internal int read_byte ()
535 byte [] buff = new byte [1];
536 if (stream.Read (buff, 0, 1) > 0)
542 public int ReadByte ()
548 public int ReadChar ()
552 byte [] buffer = new byte [16];
556 int b = read_byte ();
559 buffer [i++] = (byte) b;
560 char [] c = encoding.GetChars (buffer, 0, 1);
563 } while (i < buffer.Length);
568 public string ReadExisting ()
572 int count = BytesToRead;
573 byte [] bytes = new byte [count];
575 int n = stream.Read (bytes, 0, count);
576 return new String (encoding.GetChars (bytes, 0, n));
579 public string ReadLine ()
582 List<byte> bytes_read = new List<byte>();
583 byte [] buff = new byte [1];
586 int n = stream.Read (buff, 0, 1);
587 if (n == -1 || buff [0] == '\n')
589 bytes_read.Add (buff [0]);
591 return new String (encoding.GetChars (bytes_read.ToArray ()));
594 public string ReadTo (string value)
598 throw new ArgumentNullException ("value");
599 if (value.Length == 0)
600 throw new ArgumentException ("value");
602 // Turn into byte array, so we can compare
603 byte [] byte_value = encoding.GetBytes (value);
605 List<byte> seen = new List<byte> ();
608 int n = read_byte ();
612 if (n == byte_value [current]){
614 if (current == byte_value.Length)
615 return encoding.GetString (seen.ToArray (), 0, seen.Count - byte_value.Length);
617 current = (byte_value [0] == n) ? 1 : 0;
620 return encoding.GetString (seen.ToArray ());
623 public void Write (string str)
627 throw new ArgumentNullException ("str");
629 byte [] buffer = encoding.GetBytes (str);
630 Write (buffer, 0, buffer.Length);
633 public void Write (byte [] buffer, int offset, int count)
637 throw new ArgumentNullException ("buffer");
639 if (offset < 0 || count < 0)
640 throw new ArgumentOutOfRangeException ();
642 if (buffer.Length - offset < count)
643 throw new ArgumentException ("offset+count",
644 "The size of the buffer is less than offset + count.");
646 stream.Write (buffer, offset, count);
649 public void Write (char [] buffer, int offset, int count)
653 throw new ArgumentNullException ("buffer");
654 if (offset < 0 || offset >= buffer.Length)
655 throw new ArgumentOutOfRangeException ("offset");
656 if (count < 0 || count > buffer.Length)
657 throw new ArgumentOutOfRangeException ("count");
658 if (count > buffer.Length - offset)
659 throw new ArgumentException ("count > buffer.Length - offset");
661 byte [] bytes = encoding.GetBytes (buffer, offset, count);
662 stream.Write (bytes, 0, bytes.Length);
665 public void WriteLine (string str)
667 Write (str + new_line);
673 throw new InvalidOperationException ("Specified port is not open.");
676 internal void OnErrorReceived (SerialErrorReceivedEventArgs args)
678 SerialErrorReceivedEventHandler handler =
679 (SerialErrorReceivedEventHandler) Events [error_received];
682 handler (this, args);
685 internal void OnDataReceived (SerialDataReceivedEventArgs args)
687 SerialDataReceivedEventHandler handler =
688 (SerialDataReceivedEventHandler) Events [data_received];
691 handler (this, args);
694 internal void OnDataReceived (SerialPinChangedEventArgs args)
696 SerialPinChangedEventHandler handler =
697 (SerialPinChangedEventHandler) Events [pin_changed];
700 handler (this, args);
704 public event SerialErrorReceivedEventHandler ErrorReceived {
705 add { Events.AddHandler (error_received, value); }
706 remove { Events.RemoveHandler (error_received, value); }
709 public event SerialPinChangedEventHandler PinChanged {
710 add { Events.AddHandler (pin_changed, value); }
711 remove { Events.RemoveHandler (pin_changed, value); }
714 public event SerialDataReceivedEventHandler DataReceived {
715 add { Events.AddHandler (data_received, value); }
716 remove { Events.RemoveHandler (data_received, value); }
720 public delegate void SerialDataReceivedEventHandler (object sender, SerialDataReceivedEventArgs e);
721 public delegate void SerialPinChangedEventHandler (object sender, SerialPinChangedEventArgs e);
722 public delegate void SerialErrorReceivedEventHandler (object sender, SerialErrorReceivedEventArgs e);