530 lines
14 KiB
C#
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.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|