253 lines
7.4 KiB
C
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);
|
|
}
|