1 //
2 // System.IO.Ports.SerialPortStream.cs
3 //
4 // Authors:
5 //	Chris Toshok (toshok@ximian.com)
6 //	Carlos Alberto Cortez (calberto.cortez@gmail.com)
7 //
8 // (c) Copyright 2006 Novell, Inc. (http://www.novell.com)
9 //
10 // Slightly modified by Konrad M. Kruczynski (added baud rate value checking)
11 
12 
13 using System;
14 using System.IO;
15 using System.Runtime.InteropServices;
16 
17 namespace System.IO.Ports
18 {
19 	class SerialPortStream : Stream, ISerialStream, IDisposable
20 	{
21 		int fd;
22 		int read_timeout;
23 		int write_timeout;
24 		bool disposed;
25 
26 		[DllImport ("MonoPosixHelper", SetLastError = true)]
open_serial(string portName)27 		static extern int open_serial (string portName);
28 
SerialPortStream(string portName, int baudRate, int dataBits, Parity parity, StopBits stopBits, bool dtrEnable, bool rtsEnable, Handshake handshake, int readTimeout, int writeTimeout, int readBufferSize, int writeBufferSize)29 		public SerialPortStream (string portName, int baudRate, int dataBits, Parity parity, StopBits stopBits,
30 				bool dtrEnable, bool rtsEnable, Handshake handshake, int readTimeout, int writeTimeout,
31 				int readBufferSize, int writeBufferSize)
32 		{
33 			fd = open_serial (portName);
34 			if (fd == -1)
35 				ThrowIOException ();
36 
37 			TryBaudRate (baudRate);
38 
39 			if (!set_attributes (fd, baudRate, parity, dataBits, stopBits, handshake))
40 				ThrowIOException (); // Probably Win32Exc for compatibility
41 
42 			read_timeout = readTimeout;
43 			write_timeout = writeTimeout;
44 
45 			SetSignal (SerialSignal.Dtr, dtrEnable);
46 
47 			if (handshake != Handshake.RequestToSend &&
48 					handshake != Handshake.RequestToSendXOnXOff)
49 				SetSignal (SerialSignal.Rts, rtsEnable);
50 		}
51 
52 		public override bool CanRead {
53 			get {
54 				return true;
55 			}
56 		}
57 
58 		public override bool CanSeek {
59 			get {
60 				return false;
61 			}
62 		}
63 
64 		public override bool CanWrite {
65 			get {
66 				return true;
67 			}
68 		}
69 
70 		public override bool CanTimeout {
71 			get {
72 				return true;
73 			}
74 		}
75 
76 		public override int ReadTimeout {
77 			get {
78 				return read_timeout;
79 			}
80 			set {
81 				if (value < 0 && value != SerialPort.InfiniteTimeout)
82 					throw new ArgumentOutOfRangeException ("value");
83 
84 				read_timeout = value;
85 			}
86 		}
87 
88 		public override int WriteTimeout {
89 			get {
90 				return write_timeout;
91 			}
92 			set {
93 				if (value < 0 && value != SerialPort.InfiniteTimeout)
94 					throw new ArgumentOutOfRangeException ("value");
95 
96 				write_timeout = value;
97 			}
98 		}
99 
100 		public override long Length {
101 			get {
102 				throw new NotSupportedException ();
103 			}
104 		}
105 
106 		public override long Position {
107 			get {
108 				throw new NotSupportedException ();
109 			}
110 			set {
111 				throw new NotSupportedException ();
112 			}
113 		}
114 
Flush()115 		public override void Flush ()
116 		{
117 			// If used, this _could_ flush the serial port
118 			// buffer (not the SerialPort class buffer)
119 		}
120 
121 		[DllImport ("MonoPosixHelper", SetLastError = true)]
read_serial(int fd, byte [] buffer, int offset, int count)122 		static extern int read_serial (int fd, byte [] buffer, int offset, int count);
123 
124 
125 		[DllImport ("MonoPosixHelper", SetLastError = true)]
poll_serial(int fd, out int error, int timeout)126 		static extern bool poll_serial (int fd, out int error, int timeout);
127 
Read([In,Out] byte[] buffer, int offset, int count)128 		public override int Read ([In,Out] byte[] buffer, int offset, int count)
129 		{
130 			CheckDisposed ();
131 			if (buffer == null)
132 				throw new ArgumentNullException ("buffer");
133 			if (offset < 0 || count < 0)
134 				throw new ArgumentOutOfRangeException ("offset or count less than zero.");
135 
136 			if (buffer.Length - offset < count )
137 				throw new ArgumentException ("offset+count",
138 							      "The size of the buffer is less than offset + count.");
139 
140 			int error;
141 			bool poll_result = poll_serial (fd, out error, read_timeout);
142 			if (error == -1)
143 				ThrowIOException ();
144 
145 			if (!poll_result) {
146 				// see bug 79735   http://bugzilla.ximian.com/show_bug.cgi?id=79735
147 				// should the next line read: return -1;
148 				throw new TimeoutException();
149 			}
150 
151 			int result = read_serial (fd, buffer, offset, count);
152 			if (result == -1)
153 				ThrowIOException ();
154 			return result;
155 		}
156 
Seek(long offset, SeekOrigin origin)157 		public override long Seek (long offset, SeekOrigin origin)
158 		{
159 			throw new NotSupportedException ();
160 		}
161 
SetLength(long value)162 		public override void SetLength (long value)
163 		{
164 			throw new NotSupportedException ();
165 		}
166 
167 		[DllImport ("MonoPosixHelper", SetLastError = true)]
write_serial(int fd, byte [] buffer, int offset, int count, int timeout)168 		static extern int write_serial (int fd, byte [] buffer, int offset, int count, int timeout);
169 
Write(byte[] buffer, int offset, int count)170 		public override void Write (byte[] buffer, int offset, int count)
171 		{
172 			CheckDisposed ();
173 			if (buffer == null)
174 				throw new ArgumentNullException ("buffer");
175 
176 			if (offset < 0 || count < 0)
177 				throw new ArgumentOutOfRangeException ();
178 
179 			if (buffer.Length - offset < count)
180 				throw new ArgumentException ("offset+count",
181 							     "The size of the buffer is less than offset + count.");
182 
183 			// FIXME: this reports every write error as timeout
184 			if (write_serial (fd, buffer, offset, count, write_timeout) < 0)
185 				throw new TimeoutException("The operation has timed-out");
186 		}
187 
Dispose(bool disposing)188 		protected override void Dispose (bool disposing)
189 		{
190 			if (disposed)
191 				return;
192 
193 			disposed = true;
194 			if (close_serial (fd) != 0)
195 				ThrowIOException();
196 		}
197 
198 		[DllImport ("MonoPosixHelper", SetLastError = true)]
close_serial(int fd)199 		static extern int close_serial (int fd);
200 
Close()201 		public override void Close ()
202 		{
203 			((IDisposable) this).Dispose ();
204 		}
205 
IDisposable.Dispose()206 		void IDisposable.Dispose ()
207 		{
208 			Dispose (true);
209 			GC.SuppressFinalize (this);
210 		}
211 
~SerialPortStream()212 		~SerialPortStream ()
213 		{
214 			try {
215 				Dispose (false);
216 			} catch (IOException) {
217 			}
218 		}
219 
CheckDisposed()220 		void CheckDisposed ()
221 		{
222 			if (disposed)
223 				throw new ObjectDisposedException (GetType ().FullName);
224 		}
225 
226 		[DllImport ("MonoPosixHelper", SetLastError = true)]
set_attributes(int fd, int baudRate, Parity parity, int dataBits, StopBits stopBits, Handshake handshake)227 		static extern bool set_attributes (int fd, int baudRate, Parity parity, int dataBits, StopBits stopBits, Handshake handshake);
228 
SetAttributes(int baud_rate, Parity parity, int data_bits, StopBits sb, Handshake hs)229 		public void SetAttributes (int baud_rate, Parity parity, int data_bits, StopBits sb, Handshake hs)
230 		{
231 			if (!set_attributes (fd, baud_rate, parity, data_bits, sb, hs))
232 				ThrowIOException ();
233 		}
234 
235 		[DllImport("MonoPosixHelper", SetLastError = true)]
get_bytes_in_buffer(int fd, int input)236 		static extern int get_bytes_in_buffer (int fd, int input);
237 
238 		public int BytesToRead {
239 			get {
240 				int result = get_bytes_in_buffer (fd, 1);
241 				if (result == -1)
242 					ThrowIOException ();
243 				return result;
244 			}
245 		}
246 
247 		public int BytesToWrite {
248 			get {
249 				int result = get_bytes_in_buffer (fd, 0);
250 				if (result == -1)
251 					ThrowIOException ();
252 				return result;
253 			}
254 		}
255 
256 		[DllImport ("MonoPosixHelper", SetLastError = true)]
discard_buffer(int fd, bool inputBuffer)257 		static extern int discard_buffer (int fd, bool inputBuffer);
258 
DiscardInBuffer()259 		public void DiscardInBuffer ()
260 		{
261 			if (discard_buffer (fd, true) != 0)
262 				ThrowIOException();
263 		}
264 
DiscardOutBuffer()265 		public void DiscardOutBuffer ()
266 		{
267 			if (discard_buffer (fd, false) != 0)
268 				ThrowIOException();
269 		}
270 
271 		[DllImport ("MonoPosixHelper", SetLastError = true)]
get_signals(int fd, out int error)272 		static extern SerialSignal get_signals (int fd, out int error);
273 
GetSignals()274 		public SerialSignal GetSignals ()
275 		{
276 			int error;
277 			SerialSignal signals = get_signals (fd, out error);
278 			if (error == -1)
279 				ThrowIOException ();
280 
281 			return signals;
282 		}
283 
284 		[DllImport ("MonoPosixHelper", SetLastError = true)]
set_signal(int fd, SerialSignal signal, bool value)285 		static extern int set_signal (int fd, SerialSignal signal, bool value);
286 
SetSignal(SerialSignal signal, bool value)287 		public void SetSignal (SerialSignal signal, bool value)
288 		{
289 			if (signal < SerialSignal.Cd || signal > SerialSignal.Rts ||
290 					signal == SerialSignal.Cd ||
291 					signal == SerialSignal.Cts ||
292 					signal == SerialSignal.Dsr)
293 				throw new Exception ("Invalid internal value");
294 
295 			if (set_signal (fd, signal, value) == -1)
296 				ThrowIOException ();
297 		}
298 
299 		[DllImport ("MonoPosixHelper", SetLastError = true)]
breakprop(int fd)300 		static extern int breakprop (int fd);
301 
SetBreakState(bool value)302 		public void SetBreakState (bool value)
303 		{
304 			if (value)
305 				if (breakprop (fd) == -1)
306 					ThrowIOException ();
307 		}
308 
309 		[DllImport ("libc")]
strerror(int errnum)310 		static extern IntPtr strerror (int errnum);
311 
ThrowIOException()312 		static void ThrowIOException ()
313 		{
314 			int errnum = Marshal.GetLastWin32Error ();
315 			string error_message = Marshal.PtrToStringAnsi (strerror (errnum));
316 
317 			throw new IOException (error_message);
318 		}
319 
320 		[DllImport ("MonoPosixHelper")]
is_baud_rate_legal(int baud_rate)321 		static extern bool is_baud_rate_legal (int baud_rate);
322 
TryBaudRate(int baudRate)323 		private void TryBaudRate (int baudRate)
324 		{
325 			if (!is_baud_rate_legal (baudRate))
326 			{
327 				// this kind of exception to be compatible with MSDN API
328 				throw new ArgumentOutOfRangeException ("baudRate",
329 					"Given baud rate is not supported on this platform.");
330 			}
331 		}
332 	}
333 }
334 
335 
336