WinComm/vbx/serial.c
2026-02-23 20:53:02 -06:00

253 lines
7.4 KiB
C

// serial.c - Serial port abstraction layer
//
// Wraps Windows 3.1 comm API for use by the MSComm VBX control.
// All functions operate on a comm ID returned by serialOpen().
#include <windows.h>
#include <string.h>
#include "serial.h"
// -----------------------------------------------------------------------
// Prototypes
// -----------------------------------------------------------------------
int16_t serialClose(int16_t commId);
int16_t serialConfigure(int16_t commId, int16_t port, const char FAR *settings);
int16_t serialEnableNotify(int16_t commId, HWND hwnd, int16_t rxThreshold, int16_t txThreshold);
int16_t serialEscape(int16_t commId, int16_t func);
int16_t serialFlush(int16_t commId, int16_t queue);
UINT serialGetEventMask(int16_t commId, UINT mask);
int16_t serialGetStatus(int16_t commId, COMSTAT FAR *stat);
int16_t serialOpen(int16_t port, int16_t inBufSize, int16_t outBufSize);
int16_t serialRead(int16_t commId, char FAR *buf, int16_t len);
int16_t serialSetHandshaking(int16_t commId, int16_t mode);
int16_t serialSetOptions(int16_t commId, BOOL nullDiscard, const char FAR *parityReplace);
int16_t serialWrite(int16_t commId, const char FAR *buf, int16_t len);
// -----------------------------------------------------------------------
// serialClose - Close an open comm port
// -----------------------------------------------------------------------
int16_t serialClose(int16_t commId)
{
// Drop DTR and RTS before closing
EscapeCommFunction(commId, CLRDTR);
EscapeCommFunction(commId, CLRRTS);
return (int16_t)CloseComm(commId);
}
// -----------------------------------------------------------------------
// serialConfigure - Set baud, parity, data bits, stop bits
// -----------------------------------------------------------------------
int16_t serialConfigure(int16_t commId, int16_t port, const char FAR *settings)
{
DCB dcb;
char buf[64];
int rc;
// Get current state to preserve existing settings
rc = GetCommState(commId, &dcb);
if (rc != 0) {
return -1;
}
// Build DCB from "COMn:baud,parity,data,stop" string
wsprintf(buf, "COM%d:%s", (int)port, (LPSTR)settings);
rc = BuildCommDCB(buf, &dcb);
if (rc != 0) {
return -1;
}
// Ensure binary mode for reliable operation
dcb.fBinary = 1;
// Ensure comm ID matches our open port
dcb.Id = (BYTE)commId;
rc = SetCommState(&dcb);
if (rc != 0) {
return -1;
}
return 0;
}
// -----------------------------------------------------------------------
// serialEnableNotify - Enable WM_COMMNOTIFY messages
// -----------------------------------------------------------------------
BOOL serialEnableNotify(int16_t commId, HWND hwnd, int16_t rxThreshold, int16_t txThreshold)
{
// Enable event mask for modem status line changes, errors, and breaks
SetCommEventMask(commId, EV_CTS | EV_DSR | EV_RLSD | EV_RING | EV_ERR | EV_BREAK | EV_RXCHAR);
return EnableCommNotification(commId, hwnd, rxThreshold, txThreshold);
}
// -----------------------------------------------------------------------
// serialEscape - Execute escape function (DTR, RTS, break control)
// -----------------------------------------------------------------------
int16_t serialEscape(int16_t commId, int16_t func)
{
LONG rc;
rc = EscapeCommFunction(commId, func);
return (rc == 0) ? 0 : -1;
}
// -----------------------------------------------------------------------
// serialFlush - Flush receive or transmit buffer
// -----------------------------------------------------------------------
int16_t serialFlush(int16_t commId, int16_t queue)
{
return (int16_t)FlushComm(commId, queue);
}
// -----------------------------------------------------------------------
// serialGetEventMask - Get and clear event mask bits
// -----------------------------------------------------------------------
UINT serialGetEventMask(int16_t commId, UINT mask)
{
return GetCommEventMask(commId, mask);
}
// -----------------------------------------------------------------------
// serialGetStatus - Get error status and buffer counts
// -----------------------------------------------------------------------
int16_t serialGetStatus(int16_t commId, COMSTAT FAR *stat)
{
return (int16_t)GetCommError(commId, stat);
}
// -----------------------------------------------------------------------
// serialOpen - Open a COM port with specified buffer sizes
// -----------------------------------------------------------------------
int16_t serialOpen(int16_t port, int16_t inBufSize, int16_t outBufSize)
{
char name[8];
int commId;
wsprintf(name, "COM%d", (int)port);
commId = OpenComm(name, (UINT)inBufSize, (UINT)outBufSize);
return (int16_t)commId;
}
// -----------------------------------------------------------------------
// serialRead - Read data from receive buffer
// -----------------------------------------------------------------------
int16_t serialRead(int16_t commId, char FAR *buf, int16_t len)
{
return (int16_t)ReadComm(commId, (LPSTR)buf, len);
}
// -----------------------------------------------------------------------
// serialSetHandshaking - Apply handshaking mode
// -----------------------------------------------------------------------
int16_t serialSetHandshaking(int16_t commId, int16_t mode)
{
DCB dcb;
int rc;
rc = GetCommState(commId, &dcb);
if (rc != 0) {
return -1;
}
// Clear all flow control settings
dcb.fOutxCtsFlow = 0;
dcb.fOutxDsrFlow = 0;
dcb.fOutX = 0;
dcb.fInX = 0;
dcb.fRtsDisable = 0;
dcb.fRtsflow = 0;
switch (mode) {
case HANDSHAKE_XONXOFF:
dcb.fOutX = 1;
dcb.fInX = 1;
dcb.XonChar = 0x11; // Ctrl-Q
dcb.XoffChar = 0x13; // Ctrl-S
dcb.XonLim = 256;
dcb.XoffLim = 256;
break;
case HANDSHAKE_RTSCTS:
dcb.fOutxCtsFlow = 1;
dcb.fRtsflow = 1;
break;
case HANDSHAKE_BOTH:
dcb.fOutxCtsFlow = 1;
dcb.fRtsflow = 1;
dcb.fOutX = 1;
dcb.fInX = 1;
dcb.XonChar = 0x11;
dcb.XoffChar = 0x13;
dcb.XonLim = 256;
dcb.XoffLim = 256;
break;
case HANDSHAKE_NONE:
default:
break;
}
dcb.Id = (BYTE)commId;
rc = SetCommState(&dcb);
if (rc != 0) {
return -1;
}
return 0;
}
// -----------------------------------------------------------------------
// serialSetOptions - Apply null-discard and parity-replace settings
// -----------------------------------------------------------------------
int16_t serialSetOptions(int16_t commId, BOOL nullDiscard, const char FAR *parityReplace)
{
DCB dcb;
int rc;
rc = GetCommState(commId, &dcb);
if (rc != 0) {
return -1;
}
dcb.fNull = nullDiscard ? 1 : 0;
if (parityReplace != NULL && parityReplace[0] != '\0') {
dcb.fParity = 1;
dcb.PeChar = parityReplace[0];
} else {
dcb.fParity = 0;
dcb.PeChar = '\0';
}
dcb.Id = (BYTE)commId;
rc = SetCommState(&dcb);
if (rc != 0) {
return -1;
}
return 0;
}
// -----------------------------------------------------------------------
// serialWrite - Write data to transmit buffer
// -----------------------------------------------------------------------
int16_t serialWrite(int16_t commId, const char FAR *buf, int16_t len)
{
return (int16_t)WriteComm(commId, (LPSTR)buf, len);
}