124 lines
3.4 KiB
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);
|
|
}
|