mattercontrol/FrostedSerial/FrostedSerialStream.cs
2014-01-29 19:09:30 -08:00

530 lines
14 KiB
C#

using System;
using System.IO;
using System.Runtime.InteropServices;
using MatterHackers.TermiosH;
namespace MatterHackers.MatterControl.FrostedSerial
{
[StructLayout(LayoutKind.Sequential, Pack=1)]
public unsafe struct tDeviceInfo
{
public uint c_iflag;
public uint c_oflag;
public uint c_cflag;
public uint c_lflag;
public fixed byte c_cc[20];
public uint c_ispeed;
public uint c_ospeed;
}
class FrostedSerialPortStream : Stream, IFrostedSerialStream, IDisposable
{
int fd;
int read_timeout;
int write_timeout;
bool disposed;
[DllImport("FrostedSerialHelper", SetLastError = true)]
static extern int open_serial(string portName);
[DllImport("FrostedSerialHelper", SetLastError = true)]
static extern int set_attributes (int fd, int baudRate, Parity parity, int dataBits, StopBits stopBits, Handshake handshake);
public FrostedSerialPortStream(string portName, int baudRate, int dataBits, Parity parity, StopBits stopBits,
bool dtrEnable, bool rtsEnable, Handshake handshake, int readTimeout, int writeTimeout,
int readBufferSize, int writeBufferSize)
{
fd = open_serial(portName);
if (fd == -1) {
ThrowIOException ();
}
TryBaudRate(baudRate);
int canSetAttributes = set_attributes (fd, baudRate, parity, dataBits, stopBits, handshake);
if (canSetAttributes != 0)
{
throw new IOException(canSetAttributes.ToString()); // Probably Win32Exc for compatibility
}
read_timeout = readTimeout;
write_timeout = writeTimeout;
SetSignal(SerialSignal.Dtr, dtrEnable);
if (handshake != Handshake.RequestToSend &&
handshake != Handshake.RequestToSendXOnXOff)
SetSignal(SerialSignal.Rts, rtsEnable);
}
private int SetupBaudRate(int baudRate)
{
throw new NotImplementedException();
}
[DllImport("FrostedSerialHelper", SetLastError = true)]
static extern int tcgetattr(int fd, tDeviceInfo newtio);
private int TCGetAttribute(int fd, tDeviceInfo newtio)
{
int result = tcgetattr (fd, newtio);
return result;
}
[DllImport("FrostedSerialHelper", SetLastError = true)]
static extern int tcsetattr(int fd, uint optional_actions, tDeviceInfo newtio);
private int TCSetAttribute(int fd, uint optional_actions, tDeviceInfo newtio)
{
return tcsetattr (fd, optional_actions, newtio);
}
private int CFSetISpeed(tDeviceInfo newtio, int baudRate)
{
newtio.c_ispeed = (uint)baudRate;
return (int)newtio.c_ispeed;
}
private int CFSetOSpeed(tDeviceInfo newtio, int baudRate)
{
newtio.c_ospeed = (uint)baudRate;
return (int)newtio.c_ospeed;
}
private bool SetFrostedAttributes(int fd, int baudRate, Parity parity, int dataBits, StopBits stopBits, Handshake handshake)
{
tDeviceInfo newtio = new tDeviceInfo ();
if (TCGetAttribute (fd, newtio) == -1) {
return false;
}
newtio.c_cflag |= (uint)(e_c_oflag.CLOCAL | e_c_oflag.CREAD);
// there is no defenition for e_c_lflag.ECHOL that I can find. It was in the list of or'ed flags below
unchecked
{
newtio.c_lflag &= (uint)-(int)(e_c_lflag.ICANON | e_c_lflag.ECHO | e_c_lflag.ECHOE | e_c_lflag.ECHOK | e_c_lflag.ECHONL | e_c_lflag.ISIG | e_c_lflag.IEXTEN);
}
newtio.c_oflag &= (uint)(e_c_oflag.OPOST);
newtio.c_iflag = (uint)e_c_iflag.IGNBRK;
baudRate = SetupBaudRate (baudRate);
unchecked {
newtio.c_cflag &= (uint)-(uint)e_c_oflag.CSIZE;
}
switch (dataBits)
{
case 5:
newtio.c_cflag |= (uint)e_c_oflag.CS5;
break;
case 6:
newtio.c_cflag |= (uint)e_c_oflag.CS6;
break;
case 7:
newtio.c_cflag |= (uint)e_c_oflag.CS6;
break;
case 8:
default:
newtio.c_cflag |= (uint)e_c_oflag.CS8;
break;
}
switch (stopBits)
{
case StopBits.None:
break;
case StopBits.One:
unchecked
{
newtio.c_cflag &= (uint)-(uint)e_c_oflag.CSTOPB;
}
break;
case StopBits.Two:
newtio.c_cflag |= (uint)e_c_oflag.CSTOPB;
break;
case StopBits.OnePointFive:
break;
}
unchecked {
newtio.c_iflag &= (uint)-(uint)(e_c_iflag.INPCK | e_c_iflag.ISTRIP);
}
switch (parity)
{
case Parity.None: /* None */
newtio.c_cflag &= ~(uint)(e_c_oflag.PARENB | e_c_oflag.PARODD);
break;
case Parity.Odd: /* Odd */
newtio.c_cflag |= (uint)(e_c_oflag.PARENB | e_c_oflag.PARODD);
break;
case Parity.Even: /* Even */
newtio.c_cflag &= ~(uint)(e_c_oflag.PARODD);
newtio.c_cflag |= (uint)(e_c_oflag.PARENB);
break;
case Parity.Mark: /* Mark */
/* XXX unhandled */
break;
case Parity.Space: /* Space */
/* XXX unhandled */
break;
}
newtio.c_iflag &= ~(uint)(e_c_iflag.IXOFF | e_c_iflag.IXON);
#if CRTSCTS
newtio.c_cflag &= ~CRTSCTS;
#endif //* def CRTSCTS */
switch (handshake)
{
case Handshake.None: /* None */
/* do nothing */
break;
case Handshake.RequestToSend: /* RequestToSend (RTS) */
#if CRTSCTS
newtio.c_cflag |= CRTSCTS;
#endif //* def CRTSCTS */
break;
case Handshake.RequestToSendXOnXOff: /* RequestToSendXOnXOff (RTS + XON/XOFF) */
#if CRTSCTS
newtio.c_cflag |= CRTSCTS;
#endif //* def CRTSCTS */
/* fall through */
case Handshake.XOnXOff: /* XOnXOff */
newtio.c_iflag |= (uint)(e_c_iflag.IXOFF | e_c_iflag.IXON);
break;
}
if (CFSetOSpeed (newtio, baudRate) < 0 || CFSetISpeed (newtio, baudRate) < 0 ||
TCSetAttribute (fd, (uint)e_tcsetaatr.TCSANOW, newtio) < 0)
{
return false;
}
else
{
return true;
}
//return set_attributes(fd, baudRate, parity, dataBits, sb, hs);
}
public override bool CanRead
{
get
{
return true;
}
}
public override bool CanSeek
{
get
{
return false;
}
}
public override bool CanWrite
{
get
{
return true;
}
}
public override bool CanTimeout
{
get
{
return true;
}
}
public override int ReadTimeout
{
get
{
return read_timeout;
}
set
{
if (value < 0 && value != FrostedSerialPort.InfiniteTimeout)
throw new ArgumentOutOfRangeException("value");
read_timeout = value;
}
}
public override int WriteTimeout
{
get
{
return write_timeout;
}
set
{
if (value < 0 && value != FrostedSerialPort.InfiniteTimeout)
throw new ArgumentOutOfRangeException("value");
write_timeout = value;
}
}
public override long Length
{
get
{
throw new NotSupportedException();
}
}
public override long Position
{
get
{
throw new NotSupportedException();
}
set
{
throw new NotSupportedException();
}
}
public override void Flush()
{
// If used, this _could_ flush the serial port
// buffer (not the SerialPort class buffer)
}
[DllImport("FrostedSerialHelper", SetLastError = true)]
static extern int read_serial(int fd, byte[] buffer, int offset, int count);
[DllImport("FrostedSerialHelper", SetLastError = true)]
static extern bool poll_serial(int fd, out int error, int timeout);
public override int Read([In, Out] byte[] buffer, int offset, int count)
{
CheckDisposed();
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset < 0 || count < 0)
throw new ArgumentOutOfRangeException("offset or count less than zero.");
if (buffer.Length - offset < count)
throw new ArgumentException("offset+count",
"The size of the buffer is less than offset + count.");
int error;
bool poll_result = poll_serial(fd, out error, read_timeout);
if (error == -1)
ThrowIOException();
if (!poll_result)
{
// see bug 79735 http://bugzilla.ximian.com/show_bug.cgi?id=79735
// should the next line read: return -1;
throw new TimeoutException();
}
int result = read_serial(fd, buffer, offset, count);
if (result == -1)
ThrowIOException();
return result;
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
[DllImport("FrostedSerialHelper", SetLastError = true)]
static extern int write_serial(int fd, byte[] buffer, int offset, int count, int timeout);
public override void Write(byte[] buffer, int offset, int count)
{
CheckDisposed();
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset < 0 || count < 0)
throw new ArgumentOutOfRangeException();
if (buffer.Length - offset < count)
throw new ArgumentException("offset+count",
"The size of the buffer is less than offset + count.");
// FIXME: this reports every write error as timeout
if (write_serial(fd, buffer, offset, count, write_timeout) < 0)
throw new TimeoutException("The operation has timed-out");
}
protected override void Dispose(bool disposing)
{
if (disposed)
return;
disposed = true;
if (close_serial (fd) != 0)
{
//Don't do anything
}
}
[DllImport("FrostedSerialHelper", SetLastError = true)]
static extern int close_serial(int fd);
public override void Close()
{
((IDisposable)this).Dispose();
}
void IDisposable.Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~FrostedSerialPortStream()
{
Dispose(false);
}
void CheckDisposed()
{
if (disposed)
throw new ObjectDisposedException(GetType().FullName);
}
public void SetAttributes(int baud_rate, Parity parity, int data_bits, StopBits sb, Handshake hs)
{
if (!SetFrostedAttributes(fd, baud_rate, parity, data_bits, sb, hs))
ThrowIOException();
}
[DllImport("FrostedSerialHelper", SetLastError = true)]
static extern int get_bytes_in_buffer(int fd, int input);
public int BytesToRead
{
get
{
int result = get_bytes_in_buffer(fd, 1);
if (result == -1)
ThrowIOException();
return result;
}
}
public int BytesToWrite
{
get
{
int result = get_bytes_in_buffer(fd, 0);
if (result == -1)
ThrowIOException();
return result;
}
}
[DllImport("FrostedSerialHelper", SetLastError = true)]
static extern int discard_buffer(int fd, bool inputBuffer);
public void DiscardInBuffer()
{
if (discard_buffer(fd, true) != 0)
ThrowIOException();
}
public void DiscardOutBuffer()
{
if (discard_buffer(fd, false) != 0)
ThrowIOException();
}
[DllImport("FrostedSerialHelper", SetLastError = true)]
static extern SerialSignal get_signals(int fd, out int error);
public SerialSignal GetSignals()
{
int error;
SerialSignal signals = get_signals(fd, out error);
if (error == -1)
ThrowIOException();
return signals;
}
[DllImport("FrostedSerialHelper", SetLastError = true)]
static extern int set_signal(int fd, SerialSignal signal, bool value);
public void SetSignal(SerialSignal signal, bool value)
{
if (signal < SerialSignal.Cd || signal > SerialSignal.Rts ||
signal == SerialSignal.Cd ||
signal == SerialSignal.Cts ||
signal == SerialSignal.Dsr)
throw new Exception("Invalid internal value");
if (set_signal(fd, signal, value) == -1)
ThrowIOException();
}
[DllImport("FrostedSerialHelper", SetLastError = true)]
static extern int breakprop(int fd);
public void SetBreakState(bool value)
{
if (value)
if (breakprop(fd) == -1)
ThrowIOException();
}
[DllImport("libc")]
static extern IntPtr strerror(int errnum);
static void ThrowIOException()
{
int errnum = Marshal.GetLastWin32Error();
string error_message = Marshal.PtrToStringAnsi(strerror(errnum));
throw new IOException(error_message);
}
[DllImport("FrostedSerialHelper")]
static extern bool is_baud_rate_legal(int baud_rate);
private void TryBaudRate(int baudRate)
{
if (!is_baud_rate_legal(baudRate))
{
// this kind of exception to be compatible with MSDN API
throw new ArgumentOutOfRangeException("baudRate",
"Given baud rate is not supported on this platform.");
}
}
}
}