303 lines
8.2 KiB
C
303 lines
8.2 KiB
C
//
|
|
// File: win32_serial.h
|
|
// Author: Peter Slattery
|
|
// Creation Date: 2020-10-01
|
|
//
|
|
#ifndef WIN32_SERIAL_H
|
|
|
|
global u32 Win32SerialHandlesCountMax;
|
|
global HANDLE* Win32SerialHandles;
|
|
global gs_string* Win32SerialPortNames;
|
|
global s32* Win32SerialPortFilled;
|
|
|
|
DCB
|
|
Win32SerialPort_GetState(HANDLE ComPortHandle)
|
|
{
|
|
DEBUG_TRACK_FUNCTION;
|
|
DCB ControlSettings = {0};
|
|
ZeroStruct(&ControlSettings);
|
|
ControlSettings.DCBlength = sizeof(ControlSettings);
|
|
|
|
bool Success = GetCommState(ComPortHandle, &ControlSettings);
|
|
Assert(Success);
|
|
|
|
return ControlSettings;
|
|
}
|
|
|
|
void
|
|
Win32SerialPort_SetState(HANDLE ComPortHandle, u32 BaudRate, u8 ByteSize, u8 Parity, u8 StopBits)
|
|
{
|
|
DEBUG_TRACK_FUNCTION;
|
|
DCB ControlSettings = Win32SerialPort_GetState(ComPortHandle);
|
|
|
|
// TODO(pjs): Validate BaudRate - There's only certain rates that are valid right?
|
|
ControlSettings.BaudRate = BaudRate;
|
|
|
|
if (Parity == NOPARITY)
|
|
{
|
|
ControlSettings.Parity = Parity;
|
|
ControlSettings.fParity = 0;
|
|
}
|
|
if (Parity == EVENPARITY || Parity == ODDPARITY)
|
|
{
|
|
ControlSettings.Parity = Parity;
|
|
ControlSettings.fParity = 1;
|
|
}
|
|
|
|
ControlSettings.StopBits = StopBits;
|
|
ControlSettings.ByteSize = ByteSize;
|
|
|
|
ControlSettings.fBinary = true;
|
|
|
|
ControlSettings.fOutxCtsFlow = false;
|
|
ControlSettings.fOutxDsrFlow = false;
|
|
ControlSettings.fDtrControl = DTR_CONTROL_DISABLE;
|
|
ControlSettings.fDsrSensitivity = 0;
|
|
ControlSettings.fRtsControl = RTS_CONTROL_DISABLE;
|
|
ControlSettings.fOutX = false;
|
|
ControlSettings.fInX = false;
|
|
|
|
ControlSettings.fErrorChar = 0;
|
|
ControlSettings.fNull = false;
|
|
ControlSettings.fAbortOnError = false;
|
|
ControlSettings.wReserved = false;
|
|
ControlSettings.XonLim = 2;
|
|
ControlSettings.XoffLim = 4;
|
|
ControlSettings.XonChar = 0x13;
|
|
ControlSettings.XoffChar = 0x19;
|
|
ControlSettings.EvtChar = 0;
|
|
|
|
bool Success = SetCommState(ComPortHandle, &ControlSettings);
|
|
}
|
|
|
|
HANDLE
|
|
Win32SerialPort_Open(char* PortName)
|
|
{
|
|
DEBUG_TRACK_FUNCTION;
|
|
HANDLE ComPortHandle = CreateFile(PortName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, // Default Security Attr
|
|
OPEN_EXISTING,
|
|
0, // Not overlapped I/O
|
|
NULL);
|
|
|
|
if (ComPortHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
COMMTIMEOUTS Timeouts = { 0 };
|
|
Timeouts.ReadIntervalTimeout = 0; // in milliseconds
|
|
Timeouts.ReadTotalTimeoutConstant = 0; // in milliseconds
|
|
Timeouts.ReadTotalTimeoutMultiplier = 0; // in milliseconds
|
|
Timeouts.WriteTotalTimeoutConstant = 0; // in milliseconds
|
|
Timeouts.WriteTotalTimeoutMultiplier = 0; // in milliseconds
|
|
|
|
if (SetCommTimeouts(ComPortHandle, &Timeouts))
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
s32 Error = GetLastError();
|
|
// TODO(pjs): Error logging
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Error
|
|
s32 Error = GetLastError();
|
|
// TODO(pjs): Error logging
|
|
}
|
|
|
|
return ComPortHandle;
|
|
}
|
|
|
|
void
|
|
Win32SerialPort_Close(HANDLE PortHandle)
|
|
{
|
|
CloseHandle(PortHandle);
|
|
}
|
|
|
|
bool
|
|
Win32SerialPort_Write(HANDLE PortHandle, gs_data Buffer)
|
|
{
|
|
DEBUG_TRACK_FUNCTION;
|
|
Assert(PortHandle != INVALID_HANDLE_VALUE);
|
|
bool Success = false;
|
|
|
|
DWORD BytesWritten = 0;
|
|
if (WriteFile(PortHandle, Buffer.Memory, Buffer.Size, &BytesWritten, NULL))
|
|
{
|
|
Success = (BytesWritten == Buffer.Size);
|
|
if (!Success)
|
|
{
|
|
OutputDebugString("Error: Entire buffer not written.\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OutputDebugStringA("Error: Unable to write to port\n");
|
|
s32 Error = GetLastError();
|
|
switch (Error)
|
|
{
|
|
case ERROR_OPERATION_ABORTED:
|
|
case ERROR_GEN_FAILURE:
|
|
{
|
|
// NOTE(pjs): Probably means that the serial port became invalid
|
|
// ie. the usb stick was removed
|
|
}break;
|
|
|
|
case ERROR_ACCESS_DENIED:
|
|
{
|
|
// ??
|
|
}break;
|
|
|
|
case ERROR_NO_SUCH_DEVICE:
|
|
{
|
|
}break;
|
|
|
|
case ERROR_INVALID_HANDLE:
|
|
InvalidDefaultCase;
|
|
}
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
bool
|
|
Win32SerialPort_SetRead(HANDLE PortHandle)
|
|
{
|
|
bool Status = SetCommMask(PortHandle, EV_RXCHAR);
|
|
return Status;
|
|
}
|
|
|
|
u32
|
|
Win32SerialPort_ReadMessageWhenReady(HANDLE PortHandle, gs_data Data)
|
|
{
|
|
u32 ReadSize = 0;
|
|
|
|
DWORD EventMask = 0;
|
|
bool Status = WaitCommEvent(PortHandle, &EventMask, NULL);
|
|
if (Status)
|
|
{
|
|
DWORD NoBytesRead = 0;
|
|
do
|
|
{
|
|
u8 Byte = 0;
|
|
Status = ReadFile(PortHandle, &Byte, sizeof(char), &NoBytesRead, NULL);
|
|
Data.Memory[ReadSize] = Byte;
|
|
ReadSize++;
|
|
}
|
|
while (NoBytesRead > 0 && ReadSize < Data.Size);
|
|
}
|
|
//Read data and store in a buffer
|
|
|
|
return ReadSize;
|
|
}
|
|
|
|
/////////////////////////
|
|
// Win32SerialArray
|
|
|
|
void
|
|
Win32SerialArray_Create(gs_thread_context Context)
|
|
{
|
|
DEBUG_TRACK_FUNCTION;
|
|
|
|
Win32SerialHandlesCountMax = 32;
|
|
|
|
Win32SerialHandles = AllocatorAllocArray(Context.Allocator, HANDLE, Win32SerialHandlesCountMax);
|
|
Win32SerialPortNames = AllocatorAllocArray(Context.Allocator, gs_string, Win32SerialHandlesCountMax);
|
|
Win32SerialPortFilled = AllocatorAllocArray(Context.Allocator, s32, Win32SerialHandlesCountMax);
|
|
|
|
u64 PortNameSize = 256;
|
|
u64 PortNameBufferSize = PortNameSize * Win32SerialHandlesCountMax;
|
|
char* PortNameBuffer = AllocatorAllocArray(Context.Allocator, char, PortNameBufferSize);
|
|
for (u32 i = 0; i < Win32SerialHandlesCountMax; i++)
|
|
{
|
|
char* NameBase = PortNameBuffer + (PortNameSize * i);
|
|
Win32SerialPortNames[i] = MakeString(NameBase, 0, PortNameSize);
|
|
Win32SerialPortFilled[i] = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
Win32SerialArray_Push(HANDLE SerialHandle, gs_const_string PortName)
|
|
{
|
|
DEBUG_TRACK_FUNCTION;
|
|
|
|
bool Found = false;
|
|
for (u32 i = 0; i < Win32SerialHandlesCountMax; i++)
|
|
{
|
|
bool WasFilled = InterlockedCompareExchange((LONG volatile*)Win32SerialPortFilled + i, 1, 0);
|
|
if (!WasFilled)
|
|
{
|
|
Win32SerialHandles[i] = SerialHandle;
|
|
PrintF(&Win32SerialPortNames[i], "%S", PortName);
|
|
Found = true;
|
|
break;
|
|
}
|
|
}
|
|
Assert(Found);
|
|
}
|
|
|
|
void
|
|
Win32SerialArray_Pop(u32 Index)
|
|
{
|
|
bool WasFilled = InterlockedCompareExchange((LONG volatile*)Win32SerialPortFilled + Index, 0, 1);
|
|
Assert(WasFilled);
|
|
Win32SerialPortFilled[Index] = false;
|
|
Win32SerialHandles[Index] = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
HANDLE
|
|
Win32SerialArray_Get(gs_const_string PortName)
|
|
{
|
|
DEBUG_TRACK_FUNCTION;
|
|
|
|
HANDLE PortHandle = INVALID_HANDLE_VALUE;
|
|
for (u32 i = 0; i < Win32SerialHandlesCountMax; i++)
|
|
{
|
|
if (Win32SerialPortFilled[i] &&
|
|
StringsEqual(Win32SerialPortNames[i].ConstString, PortName))
|
|
{
|
|
PortHandle = Win32SerialHandles[i];
|
|
break;
|
|
}
|
|
}
|
|
return PortHandle;
|
|
}
|
|
|
|
HANDLE
|
|
Win32SerialArray_GetOrOpen(gs_const_string PortName, u32 BaudRate, u8 ByteSize, u8 Parity, u8 StopBits)
|
|
{
|
|
DEBUG_TRACK_FUNCTION;
|
|
|
|
HANDLE PortHandle = Win32SerialArray_Get(PortName);
|
|
if (PortHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
Assert(IsNullTerminated(PortName));
|
|
PortHandle = Win32SerialPort_Open(PortName.Str);
|
|
if (PortHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
Win32SerialPort_SetState(PortHandle, BaudRate, ByteSize, Parity, StopBits);
|
|
Win32SerialArray_Push(PortHandle, PortName);
|
|
}
|
|
}
|
|
return PortHandle;
|
|
}
|
|
|
|
void
|
|
Win32SerialArray_Close(gs_const_string PortName)
|
|
{
|
|
for (u32 i = 0; i < Win32SerialHandlesCountMax; i++)
|
|
{
|
|
if (Win32SerialPortFilled[i] && StringsEqual(Win32SerialPortNames[i].ConstString, PortName))
|
|
{
|
|
Win32SerialPort_Close(Win32SerialHandles[i]);
|
|
Win32SerialArray_Pop(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define WIN32_SERIAL_H
|
|
#endif // WIN32_SERIAL_H
|