DVX_GUI/proxy/sockShim.c

124 lines
3.4 KiB
C

// Socket shim -- rs232-compatible API over TCP sockets
//
// Maps up to 4 "COM ports" to TCP socket file descriptors. Reads use
// MSG_DONTWAIT (non-blocking) to match rs232Read's non-blocking semantics.
// Writes are blocking (loop until all bytes sent) to match rs232Write's
// guarantee of complete delivery.
//
// The shim does NOT close sockets in rs232Close -- socket lifecycle is
// managed by the proxy's main() function. This avoids double-close bugs
// when secLinkClose calls rs232Close but the proxy still needs the fd.
#include "sockShim.h"
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
// ========================================================================
// Internal state
// ========================================================================
#define MAX_PORTS 4
static int sFds[MAX_PORTS] = { -1, -1, -1, -1 };
static bool sOpen[MAX_PORTS] = { false, false, false, false };
// ========================================================================
// Public functions (alphabetical)
// ========================================================================
int rs232Close(int com) {
if (com < 0 || com >= MAX_PORTS) {
return RS232_ERR_INVALID_PORT;
}
sOpen[com] = false;
// Socket lifecycle is managed by the caller, not the shim
return RS232_SUCCESS;
}
// Serial parameters are ignored -- TCP handles framing, flow control, and
// error correction. We just validate that a socket has been assigned.
int rs232Open(int com, int32_t bps, int dataBits, char parity, int stopBits, int handshake) {
(void)bps;
(void)dataBits;
(void)parity;
(void)stopBits;
(void)handshake;
if (com < 0 || com >= MAX_PORTS) {
return RS232_ERR_INVALID_PORT;
}
if (sFds[com] < 0) {
return RS232_ERR_NOT_OPEN;
}
sOpen[com] = true;
return RS232_SUCCESS;
}
// Non-blocking read matching rs232Read semantics:
// > 0: bytes read
// 0: no data available (EAGAIN)
// -1: error or connection closed (maps to PKT_ERR_DISCONNECTED in pktPoll)
int rs232Read(int com, char *data, int len) {
if (com < 0 || com >= MAX_PORTS || sFds[com] < 0) {
return -1;
}
ssize_t n = recv(sFds[com], data, len, MSG_DONTWAIT);
if (n < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return 0;
}
return -1;
}
if (n == 0) {
// TCP FIN -- peer closed connection
return -1;
}
return (int)n;
}
// Blocking write: loops until all bytes are sent. MSG_NOSIGNAL prevents
// SIGPIPE on a closed connection (returns EPIPE instead). This matches
// rs232Write's blocking-until-complete behavior.
int rs232Write(int com, const char *data, int len) {
if (com < 0 || com >= MAX_PORTS || sFds[com] < 0) {
return RS232_ERR_NOT_OPEN;
}
int sent = 0;
while (sent < len) {
ssize_t n = send(sFds[com], data + sent, len - sent, MSG_NOSIGNAL);
if (n < 0) {
if (errno == EINTR) {
continue;
}
return RS232_ERR_NOT_OPEN;
}
sent += (int)n;
}
return RS232_SUCCESS;
}
void sockShimSetFd(int com, int fd) {
if (com < 0 || com >= MAX_PORTS) {
return;
}
sFds[com] = fd;
// Set non-blocking so rs232Read returns immediately when empty
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}