mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2025-12-16 12:08:36 +00:00
518 lines
17 KiB
C#
518 lines
17 KiB
C#
/*
|
|
* Author: Marcus Lorentzon, 2001
|
|
* d98malor@dtek.chalmers.se
|
|
*
|
|
* Freeware: Do not remove this header
|
|
*
|
|
* File: SerialStream.cs
|
|
*
|
|
* Description: Implements a Stream for asynchronous
|
|
* transfers and COMM.
|
|
*
|
|
* Version: 2.0 beta
|
|
*
|
|
*/
|
|
|
|
namespace LoMaN.IO {
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Threading;
|
|
using System.Runtime.InteropServices;
|
|
|
|
public class SerialStream : Stream {
|
|
|
|
public class SerialAsyncResult : IAsyncResult {
|
|
public SerialAsyncResult(object asyncObject) {
|
|
m_AsyncObject = asyncObject;
|
|
m_WaitHandle = new ManualResetEvent(false);
|
|
}
|
|
|
|
internal void Init(object stateObject, AsyncCallback callback, bool bIsRead) {
|
|
m_StateObject = stateObject;
|
|
m_Callback = callback;
|
|
m_bIsRead = bIsRead;
|
|
m_bCompleted = false;
|
|
m_WaitHandle.Reset();
|
|
}
|
|
|
|
internal void Reset() {
|
|
m_StateObject = null;
|
|
m_Callback = null;
|
|
m_bCompleted = true;
|
|
m_WaitHandle.Reset();
|
|
}
|
|
|
|
internal bool m_bIsRead;
|
|
internal bool m_bCompleted = true;
|
|
|
|
public bool IsCompleted { get { return m_bCompleted; } }
|
|
public bool CompletedSynchronously { get { return false; } }
|
|
|
|
private object m_AsyncObject;
|
|
public object AsyncObject { get { return m_AsyncObject; } }
|
|
|
|
private object m_StateObject;
|
|
public object AsyncState { get { return m_StateObject; } }
|
|
|
|
private ManualResetEvent m_WaitHandle;
|
|
public WaitHandle AsyncWaitHandle { get { return m_WaitHandle; } }
|
|
|
|
private AsyncCallback m_Callback;
|
|
public AsyncCallback Callback { get { return m_Callback; } }
|
|
}
|
|
|
|
private unsafe void AsyncFSCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped) {
|
|
SerialAsyncResult sar = (SerialAsyncResult)Overlapped.Unpack(pOverlapped).AsyncResult;
|
|
|
|
if (sar.m_bIsRead)
|
|
m_iReadCount = (int)numBytes;
|
|
else
|
|
m_iWriteCount = (int)numBytes;
|
|
((ManualResetEvent)sar.AsyncWaitHandle).Set();
|
|
|
|
if (errorCode == ERROR_OPERATION_ABORTED)
|
|
sar.m_bCompleted = false;
|
|
else
|
|
sar.m_bCompleted = true;
|
|
|
|
if (sar.Callback != null)
|
|
sar.Callback.Invoke(sar);
|
|
}
|
|
|
|
private IOCompletionCallback m_IOCompletionCallback;
|
|
private int m_hFile = 0;
|
|
|
|
private string m_sPort;
|
|
public string Port {
|
|
get {
|
|
return m_sPort;
|
|
}
|
|
set {
|
|
if (m_sPort != value) {
|
|
Close();
|
|
Open(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
private const uint GENERIC_READ = 0x80000000;
|
|
private const uint GENERIC_WRITE = 0x40000000;
|
|
private const uint ERROR_OPERATION_ABORTED = 995;
|
|
private const uint ERROR_IO_PENDING = 997;
|
|
|
|
public void Open(string port) {
|
|
m_hFile = CreateFile(port, (uint)((m_bRead?GENERIC_READ:0)|(m_bWrite?GENERIC_WRITE:0)), 0, 0, 3, 0x40000000, 0);
|
|
if (m_hFile <= 0) {
|
|
m_hFile = 0;
|
|
throw new FileNotFoundException("Unable to open " + port);
|
|
}
|
|
m_sPort = port;
|
|
|
|
ThreadPool.BindHandle(new IntPtr(m_hFile));
|
|
}
|
|
|
|
public SerialStream(string port, FileAccess access) {
|
|
m_bRead = ((int)access & (int)FileAccess.Read) != 0;
|
|
m_bWrite = ((int)access & (int)FileAccess.Write) != 0;
|
|
Open(port);
|
|
unsafe {
|
|
m_IOCompletionCallback = new IOCompletionCallback(AsyncFSCallback);
|
|
}
|
|
}
|
|
|
|
private bool m_bRead;
|
|
public override bool CanRead { get { return m_bRead; } }
|
|
|
|
private bool m_bWrite;
|
|
public override bool CanWrite { get { return m_bWrite; } }
|
|
|
|
public override bool CanSeek { get { return false; } }
|
|
|
|
public bool Closed { get { return m_hFile <= 0; } }
|
|
|
|
public override long Length { get { return 0; } }
|
|
|
|
public override void SetLength(long nLength) { }
|
|
|
|
public override void Close() {
|
|
CloseHandle(m_hFile);
|
|
m_hFile = 0;
|
|
m_sPort = null;
|
|
}
|
|
|
|
private int m_iReadCount;
|
|
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) {
|
|
SerialAsyncResult ar = new SerialAsyncResult(this);
|
|
ar.Init(state, callback, true);
|
|
Overlapped ov = new Overlapped(0, 0, ar.AsyncWaitHandle.Handle.ToInt32(), ar);
|
|
unsafe { fixed (byte* data = &buffer[0]) {
|
|
int read = 0;
|
|
NativeOverlapped* nov = ov.Pack(m_IOCompletionCallback);
|
|
ReadFile(m_hFile, data, count, &read, nov);
|
|
} }
|
|
|
|
if (GetLastError() == ERROR_IO_PENDING)
|
|
return ar;
|
|
else
|
|
throw new Exception("Unable to initialize read. Errorcode: " + GetLastError().ToString());
|
|
}
|
|
|
|
private int m_iWriteCount;
|
|
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) {
|
|
SerialAsyncResult ar = new SerialAsyncResult(this);
|
|
ar.Init(state, callback, false);
|
|
Overlapped ov = new Overlapped(0, 0, ar.AsyncWaitHandle.Handle.ToInt32(), ar);
|
|
unsafe { fixed (byte* data = &buffer[0]) {
|
|
int write = 0;
|
|
NativeOverlapped* nov = ov.Pack(m_IOCompletionCallback);
|
|
WriteFile(m_hFile, data, count, &write, nov);
|
|
} }
|
|
if (GetLastError() == ERROR_IO_PENDING)
|
|
return ar;
|
|
else
|
|
throw new Exception("Unable to initialize write. Errorcode: " + GetLastError().ToString());
|
|
}
|
|
|
|
public override int EndRead(IAsyncResult asyncResult) {
|
|
SerialAsyncResult sar = (SerialAsyncResult)asyncResult;
|
|
if (!sar.m_bIsRead)
|
|
throw new Exception("Invalid parameter: IAsyncResult is not from a read");
|
|
sar.AsyncWaitHandle.WaitOne();
|
|
if (!sar.m_bCompleted) {
|
|
((ManualResetEvent)sar.AsyncWaitHandle).Reset();
|
|
sar.AsyncWaitHandle.WaitOne();
|
|
}
|
|
sar.Reset();
|
|
|
|
return m_iReadCount;
|
|
}
|
|
|
|
public override void EndWrite(IAsyncResult asyncResult) {
|
|
SerialAsyncResult sar = (SerialAsyncResult)asyncResult;
|
|
if (sar.m_bIsRead)
|
|
throw new Exception("Invalid parameter: IAsyncResult is from a read");
|
|
sar.AsyncWaitHandle.WaitOne();
|
|
if (!sar.m_bCompleted) {
|
|
((ManualResetEvent)sar.AsyncWaitHandle).Reset();
|
|
sar.AsyncWaitHandle.WaitOne();
|
|
}
|
|
sar.Reset();
|
|
}
|
|
|
|
public override int Read(byte[] buffer, int offset, int count) {
|
|
return EndRead(BeginRead(buffer, offset, count, null, null));
|
|
}
|
|
|
|
public override void Write(byte[] buffer, int offset, int count) {
|
|
EndWrite(BeginWrite(buffer, offset, count, null, null));
|
|
}
|
|
|
|
public override void Flush() { FlushFileBuffers(m_hFile); }
|
|
|
|
private const uint PURGE_TXABORT = 0x0001; // Kill the pending/current writes to the comm port.
|
|
private const uint PURGE_RXABORT = 0x0002; // Kill the pending/current reads to the comm port.
|
|
private const uint PURGE_TXCLEAR = 0x0004; // Kill the transmit queue if there.
|
|
private const uint PURGE_RXCLEAR = 0x0008; // Kill the typeahead buffer if there.
|
|
|
|
public bool PurgeRead() { return(PurgeComm(m_hFile, PURGE_RXCLEAR)); }
|
|
public bool PurgeWrite() { return(PurgeComm(m_hFile, PURGE_TXCLEAR)); }
|
|
public bool CancelRead() { return(PurgeComm(m_hFile, PURGE_RXABORT)); }
|
|
public bool CancelWrite() { return(PurgeComm(m_hFile, PURGE_TXABORT)); }
|
|
|
|
public override long Seek(long offset, SeekOrigin origin) { return 0; }
|
|
|
|
public override long Position { get { return 0; } set { } }
|
|
|
|
public void SetTimeouts(int ReadIntervalTimeout, int ReadTotalTimeoutMultiplier, int ReadTotalTimeoutConstant,
|
|
int WriteTotalTimeoutMultiplier, int WriteTotalTimeoutConstant) {
|
|
SerialTimeouts Timeouts = new SerialTimeouts(ReadIntervalTimeout, ReadTotalTimeoutMultiplier, ReadTotalTimeoutConstant,
|
|
WriteTotalTimeoutMultiplier, WriteTotalTimeoutConstant);
|
|
unsafe { SetCommTimeouts(m_hFile, &Timeouts); }
|
|
}
|
|
|
|
public enum Parity {None, Odd, Even, Mark, Space};
|
|
public enum StopBits {One, OneAndHalf, Two};
|
|
public enum FlowControl {None, XOnXOff, Hardware};
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct DCB {
|
|
public int DCBlength;
|
|
public uint BaudRate;
|
|
public uint Flags;
|
|
public uint fBinary { get { return Flags&0x0001; }
|
|
set { Flags = Flags & ~1U | value; } }
|
|
public uint fParity { get { return (Flags>>1)&1; }
|
|
set { Flags = Flags & ~(1U << 1) | (value << 1); } }
|
|
public uint fOutxCtsFlow { get { return (Flags>>2)&1; }
|
|
set { Flags = Flags & ~(1U << 2) | (value << 2); } }
|
|
public uint fOutxDsrFlow { get { return (Flags>>3)&1; }
|
|
set { Flags = Flags & ~(1U << 3) | (value << 3); } }
|
|
public uint fDtrControl { get { return (Flags>>4)&3; }
|
|
set { Flags = Flags & ~(3U << 4) | (value << 4); } }
|
|
public uint fDsrSensitivity { get { return (Flags>>6)&1; }
|
|
set { Flags = Flags & ~(1U << 6) | (value << 6); } }
|
|
public uint fTXContinueOnXoff { get { return (Flags>>7)&1; }
|
|
set { Flags = Flags & ~(1U << 7) | (value << 7); } }
|
|
public uint fOutX { get { return (Flags>>8)&1; }
|
|
set { Flags = Flags & ~(1U << 8) | (value << 8); } }
|
|
public uint fInX { get { return (Flags>>9)&1; }
|
|
set { Flags = Flags & ~(1U << 9) | (value << 9); } }
|
|
public uint fErrorChar { get { return (Flags>>10)&1; }
|
|
set { Flags = Flags & ~(1U << 10) | (value << 10); } }
|
|
public uint fNull { get { return (Flags>>11)&1; }
|
|
set { Flags = Flags & ~(1U << 11) | (value << 11); } }
|
|
public uint fRtsControl { get { return (Flags>>12)&3; }
|
|
set { Flags = Flags & ~(3U << 12) | (value << 12); } }
|
|
public uint fAbortOnError { get { return (Flags>>13)&1; }
|
|
set { Flags = Flags & ~(1U << 13) | (value << 13); } }
|
|
public ushort wReserved;
|
|
public ushort XonLim;
|
|
public ushort XoffLim;
|
|
public byte ByteSize;
|
|
public byte Parity;
|
|
public byte StopBits;
|
|
public sbyte XonChar;
|
|
public sbyte XoffChar;
|
|
public sbyte ErrorChar;
|
|
public sbyte EofChar;
|
|
public sbyte EvtChar;
|
|
public ushort wReserved1;
|
|
|
|
public override string ToString() {
|
|
return "DCBlength: " + DCBlength + "\r\n" +
|
|
"BaudRate: " + BaudRate + "\r\n" +
|
|
"fBinary: " + fBinary + "\r\n" +
|
|
"fParity: " + fParity + "\r\n" +
|
|
"fOutxCtsFlow: " + fOutxCtsFlow + "\r\n" +
|
|
"fOutxDsrFlow: " + fOutxDsrFlow + "\r\n" +
|
|
"fDtrControl: " + fDtrControl + "\r\n" +
|
|
"fDsrSensitivity: " + fDsrSensitivity + "\r\n" +
|
|
"fTXContinueOnXoff: " + fTXContinueOnXoff + "\r\n" +
|
|
"fOutX: " + fOutX + "\r\n" +
|
|
"fInX: " + fInX + "\r\n" +
|
|
"fErrorChar: " + fErrorChar + "\r\n" +
|
|
"fNull: " + fNull + "\r\n" +
|
|
"fRtsControl: " + fRtsControl + "\r\n" +
|
|
"fAbortOnError: " + fAbortOnError + "\r\n" +
|
|
"XonLim: " + XonLim + "\r\n" +
|
|
"XoffLim: " + XoffLim + "\r\n" +
|
|
"ByteSize: " + ByteSize + "\r\n" +
|
|
"Parity: " + Parity + "\r\n" +
|
|
"StopBits: " + StopBits + "\r\n" +
|
|
"XonChar: " + XonChar + "\r\n" +
|
|
"XoffChar: " + XoffChar + "\r\n" +
|
|
"EofChar: " + EofChar + "\r\n" +
|
|
"EvtChar: " + EvtChar + "\r\n";
|
|
}
|
|
}
|
|
|
|
public bool SetPortSettings(uint baudrate, byte databits, StopBits stopbits, Parity parity, FlowControl flowcontrol) {
|
|
unsafe {
|
|
DCB dcb = new DCB();
|
|
dcb.DCBlength = sizeof(DCB);
|
|
dcb.BaudRate = baudrate;
|
|
dcb.ByteSize = databits;
|
|
dcb.StopBits = (byte)stopbits;
|
|
dcb.Parity = (byte)parity;
|
|
dcb.fParity = (parity > 0)? 1U : 0U;
|
|
dcb.fBinary = dcb.fDtrControl = dcb.fTXContinueOnXoff = 1;
|
|
dcb.fOutxCtsFlow = dcb.fAbortOnError = (flowcontrol == FlowControl.Hardware)? 1U : 0U;
|
|
dcb.fOutX = dcb.fInX = (flowcontrol == FlowControl.XOnXOff)? 1U : 0U;
|
|
dcb.fRtsControl = (flowcontrol == FlowControl.Hardware)? 2U : 1U;
|
|
dcb.XonLim = 2048;
|
|
dcb.XoffLim = 512;
|
|
dcb.XonChar = 0x11; // Ctrl-Q
|
|
dcb.XoffChar = 0x13; // Ctrl-S
|
|
return SetCommState(m_hFile, &dcb);
|
|
}
|
|
}
|
|
|
|
public bool GetPortSettings(out DCB dcb) {
|
|
unsafe {
|
|
DCB dcb2 = new DCB();
|
|
dcb2.DCBlength = sizeof(DCB);
|
|
bool ret = GetCommState(m_hFile, &dcb2);
|
|
dcb = dcb2;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
[DllImport("kernel32.dll", EntryPoint="CreateFileW", SetLastError=true,
|
|
CharSet=CharSet.Unicode, ExactSpelling=true)]
|
|
static extern int CreateFile(string filename, uint access, uint sharemode, uint security_attributes, uint creation, uint flags, uint template);
|
|
|
|
[DllImport("kernel32.dll", SetLastError=true)]
|
|
static extern bool CloseHandle(int handle);
|
|
|
|
[DllImport("kernel32.dll", SetLastError=true)]
|
|
static unsafe extern bool ReadFile(int hFile, byte* lpBuffer, int nNumberOfBytesToRead, int* lpNumberOfBytesRead, NativeOverlapped* lpOverlapped);
|
|
|
|
[DllImport("kernel32.dll", SetLastError=true)]
|
|
static unsafe extern bool WriteFile(int hFile, byte* lpBuffer, int nNumberOfBytesToWrite, int* lpNumberOfBytesWritten, NativeOverlapped* lpOverlapped);
|
|
|
|
[DllImport("kernel32.dll", SetLastError=true)]
|
|
static unsafe extern bool SetCommTimeouts(int hFile, SerialTimeouts* lpCommTimeouts);
|
|
|
|
[DllImport("kernel32.dll", SetLastError=true)]
|
|
static unsafe extern bool SetCommState(int hFile, DCB* lpDCB);
|
|
|
|
[DllImport("kernel32.dll", SetLastError=true)]
|
|
static unsafe extern bool GetCommState(int hFile, DCB* lpDCB);
|
|
|
|
[DllImport("kernel32.dll", SetLastError=true)]
|
|
static unsafe extern bool BuildCommDCB(string def, DCB* lpDCB);
|
|
|
|
[DllImport("kernel32.dll", SetLastError=true)]
|
|
static unsafe extern int GetLastError();
|
|
|
|
[DllImport("kernel32.dll", SetLastError=true)]
|
|
static unsafe extern bool FlushFileBuffers(int hFile);
|
|
|
|
[DllImport("kernel32.dll", SetLastError=true)]
|
|
static unsafe extern bool PurgeComm(int hFile, uint dwFlags);
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct SerialTimeouts {
|
|
public int ReadIntervalTimeout;
|
|
public int ReadTotalTimeoutMultiplier;
|
|
public int ReadTotalTimeoutConstant;
|
|
public int WriteTotalTimeoutMultiplier;
|
|
public int WriteTotalTimeoutConstant;
|
|
|
|
public SerialTimeouts(int r1, int r2, int r3, int w1, int w2) {
|
|
ReadIntervalTimeout = r1;
|
|
ReadTotalTimeoutMultiplier = r2;
|
|
ReadTotalTimeoutConstant = r3;
|
|
WriteTotalTimeoutMultiplier = w1;
|
|
WriteTotalTimeoutConstant = w2;
|
|
}
|
|
|
|
public override string ToString() {
|
|
return "ReadIntervalTimeout: " + ReadIntervalTimeout + "\r\n" +
|
|
"ReadTotalTimeoutMultiplier: " + ReadTotalTimeoutMultiplier + "\r\n" +
|
|
"ReadTotalTimeoutConstant: " + ReadTotalTimeoutConstant + "\r\n" +
|
|
"WriteTotalTimeoutMultiplier: " + WriteTotalTimeoutMultiplier + "\r\n" +
|
|
"WriteTotalTimeoutConstant: " + WriteTotalTimeoutConstant + "\r\n";
|
|
}
|
|
}
|
|
|
|
public class SerialSettings {
|
|
private bool m_bDirty = false;
|
|
public bool Dirty { get { return m_bDirty; } }
|
|
|
|
// Timeouts
|
|
private SerialTimeouts Timeouts = new SerialTimeouts(1, 0, 0, 0, 0);
|
|
|
|
public int ReadIntervalTimeout {
|
|
get { return Timeouts.ReadIntervalTimeout; }
|
|
set { if (Timeouts.ReadIntervalTimeout != value) {
|
|
Timeouts.ReadIntervalTimeout = value;
|
|
m_bDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public int ReadTotalTimeoutMultiplier {
|
|
get { return Timeouts.ReadTotalTimeoutMultiplier; }
|
|
set { if (Timeouts.ReadTotalTimeoutMultiplier != value) {
|
|
Timeouts.ReadTotalTimeoutMultiplier = value;
|
|
m_bDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public int ReadTotalTimeoutConstant {
|
|
get { return Timeouts.ReadTotalTimeoutConstant; }
|
|
set { if (Timeouts.ReadTotalTimeoutConstant != value) {
|
|
Timeouts.ReadTotalTimeoutConstant = value;
|
|
m_bDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public int WriteTotalTimeoutMultiplier {
|
|
get { return Timeouts.WriteTotalTimeoutMultiplier; }
|
|
set { if (Timeouts.WriteTotalTimeoutMultiplier != value) {
|
|
Timeouts.WriteTotalTimeoutMultiplier = value;
|
|
m_bDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public int WriteTotalTimeoutConstant {
|
|
get { return Timeouts.WriteTotalTimeoutConstant; }
|
|
set { if (Timeouts.WriteTotalTimeoutConstant != value) {
|
|
Timeouts.WriteTotalTimeoutConstant = value;
|
|
m_bDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Valid stuff
|
|
private static readonly object[] s_iValidBitRates = new object[] {75u, 110u, 134u, 150u, 300u, 600u, 1200u, 1800u, 2400u, 4800u,
|
|
7200u, 9600u, 14400u, 19200u, 38400u, 57600u, 115200u, 128000u};
|
|
public static object[] ValidBitRates {
|
|
get { return s_iValidBitRates; }
|
|
}
|
|
|
|
private static readonly object[] s_iValidDataBits = new object[] {(byte)5, (byte)6, (byte)7, (byte)8};
|
|
public static object[] ValidDataBits {
|
|
get { return s_iValidDataBits; }
|
|
}
|
|
|
|
// Port settings
|
|
private uint m_iBitRate = 57600;
|
|
public uint BitRate {
|
|
get { return m_iBitRate; }
|
|
set { if (m_iBitRate != value) {
|
|
m_iBitRate = value;
|
|
m_bDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private byte m_iDataBits = 8;
|
|
public byte DataBits {
|
|
get { return m_iDataBits; }
|
|
set { if (m_iDataBits != value) {
|
|
m_iDataBits = value;
|
|
m_bDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private SerialStream.Parity m_Parity = SerialStream.Parity.None;
|
|
public SerialStream.Parity Parity {
|
|
get { return m_Parity; }
|
|
set { if (m_Parity != value) {
|
|
m_Parity = value;
|
|
m_bDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private SerialStream.StopBits m_StopBits = SerialStream.StopBits.One;
|
|
public SerialStream.StopBits StopBits {
|
|
get { return m_StopBits; }
|
|
set { if (m_StopBits != value) {
|
|
m_StopBits = value;
|
|
m_bDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private SerialStream.FlowControl m_FlowControl = SerialStream.FlowControl.None;
|
|
public SerialStream.FlowControl FlowControl {
|
|
get { return m_FlowControl; }
|
|
set { if (m_FlowControl != value) {
|
|
m_FlowControl = value;
|
|
m_bDirty = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|