1
0
mirror of https://github.com/open-simh/simh.git synced 2026-05-04 23:25:46 +00:00

H316: Rewrote h316_udp to use TMXR lines for UDP transport of data. Changed h316_mi to leverage built-in loopback mode in TMXR.

This gives UDP transport on all simh host platforms.
This commit is contained in:
Mark Pizzolato
2013-11-26 13:30:11 -08:00
parent 378e3e03a3
commit c9f73eac90
6 changed files with 126 additions and 313 deletions

View File

@@ -192,7 +192,7 @@ extern t_stat mi_tx_service (uint32 quantum);
t_stat udp_create (DEVICE *pdtr, char *premote, int32 *plink);
t_stat udp_release (DEVICE *dptr, int32 link);
t_stat udp_send (DEVICE *pdtr, int32 link, uint16 *pdata, uint16 count);
t_stat udp_send_self (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count);
t_stat udp_set_link_loopback (DEVICE *dptr, int32 link, t_bool enable_loopback);
int32 udp_receive (DEVICE *dptr, int32 link, uint16 *pdata, uint16 maxbufg);
#endif // #ifndef _H316_IMP_H_

View File

@@ -319,6 +319,7 @@ MIDB *const mi_midbs [MI_NUM] = {&mi1_db, &mi2_db, &mi3_db, &mi4_db,
void mi_reset_rx (uint16 line)
{
PMIDB(line)->iloop = PMIDB(line)->lloop = FALSE;
udp_set_link_loopback (PDEVICE(line), PMIDB(line)->link, FALSE);
PMIDB(line)->rxerror = PMIDB(line)->rxpending = FALSE;
PMIDB(line)->rxtotal = 0;
CLR_RX_IRQ(line); CLR_RX_IEN(line);
@@ -328,6 +329,7 @@ void mi_reset_rx (uint16 line)
void mi_reset_tx (uint16 line)
{
PMIDB(line)->iloop = PMIDB(line)->lloop = FALSE;
udp_set_link_loopback (PDEVICE(line), PMIDB(line)->link, FALSE);
PMIDB(line)->txtotal = PMIDB(line)->txdelay = 0;
CLR_TX_IRQ(line); CLR_TX_IEN(line);
}
@@ -424,10 +426,7 @@ void mi_start_tx (uint16 line)
if (PMIDB(line)->iloop) {
mi_rx_local(line, next, count);
} else if (PMIDB(line)->link != NOLINK) {
if (PMIDB(line)->lloop)
ret = udp_send_self(PDEVICE(line), PMIDB(line)->link, &M[next], count);
else
ret = udp_send(PDEVICE(line), PMIDB(line)->link, &M[next], count);
ret = udp_send(PDEVICE(line), PMIDB(line)->link, &M[next], count);
if (ret != SCPE_OK) mi_link_error(line);
}
@@ -581,15 +580,21 @@ int32 mi_io (uint16 line, int32 inst, int32 fnc, int32 dat, int32 dev)
case 001:
// MnUNXP - un-cross patch modem ...
sim_debug(IMP_DBG_IOT,PDEVICE(line),"un-cross patch modem (PC=%06o)\n", PC-1);
PMIDB(line)->iloop = PMIDB(line)->lloop = FALSE; return dat;
PMIDB(line)->iloop = PMIDB(line)->lloop = FALSE;
udp_set_link_loopback (PDEVICE(line), PMIDB(line)->link, FALSE);
return dat;
case 002:
// MnLXP - enable line cross patch ...
sim_debug(IMP_DBG_IOT,PDEVICE(line),"enable line cross patch (PC=%06o)\n", PC-1);
PMIDB(line)->lloop = TRUE; PMIDB(line)->iloop = FALSE; return dat;
PMIDB(line)->lloop = TRUE;
udp_set_link_loopback (PDEVICE(line), PMIDB(line)->link, TRUE);
PMIDB(line)->iloop = FALSE; return dat;
case 003:
// MnIXP - enable interface cross patch ...
sim_debug(IMP_DBG_IOT,PDEVICE(line),"enable interface cross patch (PC=%06o)\n", PC-1);
PMIDB(line)->iloop = TRUE; PMIDB(line)->lloop = FALSE; return dat;
PMIDB(line)->iloop = TRUE; PMIDB(line)->lloop = FALSE;
udp_set_link_loopback (PDEVICE(line), PMIDB(line)->link, FALSE);
return dat;
case 004:
// MnIN - start modem input ...
mi_debug_mio(line, PDIB(line)->rxdmc, "input");

View File

@@ -29,7 +29,8 @@
udp socket routines
26-Jun-13 RLA Rewritten from TCP version
26-Nov-13 MP Rewritten to use TMXR layer packet semantics thus
allowing portability to all simh hosts.
OVERVIEW
@@ -134,15 +135,7 @@
*/
#ifdef VM_IMPTIP
#include "sim_defs.h" // simh machine independent definitions
#ifdef _WIN32 // WINSOCK definitions
#include <winsock2.h> // at least Windows puts it all in one file!
#elif defined(__linux__) // Linux definitions
#include <sys/socket.h> // struct socketaddr_in, et al
#include <netinet/in.h> // INADDR_NONE, et al
#include <netdb.h> // gethostbyname()
#include <fcntl.h> // fcntl() (what else??)
#include <unistd.h> // getpid(), more?
#endif
#include "sim_tmxr.h" // The MUX layer exposes packet send and receive semantics
#include "h316_defs.h" // H316 emulator definitions
#include "h316_imp.h" // ARPAnet IMP/TIP definitions
@@ -158,30 +151,15 @@
// the fragments arrive intact then the destination should reassemble them.
#define MAXDATA 16384 // longest possible IMP packet (in H316 words)
// Compatibility hacks for WINSOCK vs Linux ...
#ifdef __linux__
#define WSAGetLastError() errno
#define closesocket close
#define SOCKET int32
#define SOCKADDR struct sockaddr
#define WSAEWOULDBLOCK EWOULDBLOCK
#define INVALID_SOCKET ((SOCKET) (-1))
#define SOCKET_ERROR (-1)
#endif
// UDP connection data structure ...
// One of these blocks is allocated for every simulated modem link.
struct _UDP_LINK {
t_bool used; // TRUE if this UDP_LINK is in use
uint32 ipremote; // IP address of the remote system
uint16 rxport; // OUR receiving port number
uint16 txport; // HIS receiving port number (on ipremote)
struct sockaddr_in rxaddr; // OUR receiving address (goes with rxsock!)
struct sockaddr_in txaddr; // HIS transmitting address (pairs with txsock!)
SOCKET rxsock; // socket for receiving incoming packets
SOCKET txsock; // socket for sending outgoing packets
char rhostport[64]; // Remote host:port
char lport[64]; // Local port
uint32 rxsequence; // next message sequence number for receive
uint32 txsequence; // next message sequence number for transmit
DEVICE *dptr; // Device associated with link
};
typedef struct _UDP_LINK UDP_LINK;
@@ -206,44 +184,9 @@ typedef struct _UDP_PACKET UDP_PACKET;
#define UDP_HEADER_LEN (2*sizeof(uint32) + sizeof(uint16))
// Locals ...
t_bool udp_wsa_started = FALSE; // TRUE if WSAStartup() has been called
UDP_LINK udp_links[MAXLINKS] = {0}; // data for every active connection
t_stat udp_startup (DEVICE *dptr)
{
// WINSOCK requires that WSAStartup be called exactly once before any other
// network calls are made. That's a bit inconvenient, but this routine deals
// with it by using a static variable to call WSAStartup the first time thru
// and then never again.
#ifdef _WIN32
WORD wVersionRequested = MAKEWORD(2,2);
WSADATA wsaData; int32 ret;
if (!udp_wsa_started) {
ret = WSAStartup (wVersionRequested, &wsaData);
if (ret != 0) {
fprintf(stderr,"UDP - WINSOCK startup error %d\n", ret);
return SCPE_IERR;
} else
sim_debug(IMP_DBG_UDP, dptr, "WSAStartup() called\n");
udp_wsa_started = TRUE;
}
#endif
return SCPE_OK;
}
t_stat udp_shutdown (DEVICE *dptr)
{
// This routine calls WSACleanup() after the last socket has been closed.
// It's essentially the opposite of udp_startup() ...
#ifdef _WIN32
if (udp_wsa_started) {
WSACleanup(); udp_wsa_started = FALSE;
sim_debug(IMP_DBG_UDP, dptr, "WSACleanup() called\n");
}
#endif
return SCPE_OK;
}
UDP_LINK udp_links[MAXLINKS] = {0}; // data for every active connection
TMLN udp_lines[MAXLINKS] = { 0 }; // line descriptors
TMXR udp_tmxr = { MAXLINKS, NULL, 0, udp_lines};// datagram mux
int32 udp_find_free_link (void)
{
@@ -253,91 +196,12 @@ int32 udp_find_free_link (void)
for (i = 0; i < MAXLINKS; ++i) {
if (udp_links[i].used == 0) {
memset(&udp_links[i], 0, sizeof(UDP_LINK));
// Just in case these values aren't zero!
udp_links[i].rxsock = udp_links[i].txsock = INVALID_SOCKET;
return i;
}
}
return NOLINK;
}
char *udp_format_remote (int32 link)
{
// Format the remote address and port in the format "w.x.y.z:pppp" . It's
// a bit ugly (OK, it's a lot ugly!) but it's just for error messages...
static char buf[64];
sprintf(buf, "%d.%d.%d.%d:%d",
(udp_links[link].ipremote >> 24) & 0xFF,
(udp_links[link].ipremote >> 16) & 0xFF,
(udp_links[link].ipremote >> 8) & 0xFF,
udp_links[link].ipremote & 0xFF,
udp_links[link].txport);
return buf;
}
/* get_ipaddr IP address:port
Inputs:
cptr = pointer to input string
Outputs:
ipa = pointer to IP address (may be NULL), 0 = none
ipp = pointer to IP port (may be NULL), 0 = none
result = status
*/
static t_stat get_ipaddr (char *cptr, uint32 *ipa, uint32 *ipp)
{
char gbuf[CBUFSIZE];
char *addrp, *portp, *octetp;
uint32 i, addr, port, octet;
t_stat r;
if ((cptr == NULL) || (*cptr == 0))
return SCPE_ARG;
strncpy (gbuf, cptr, CBUFSIZE);
addrp = gbuf; /* default addr */
if ((portp = strchr (gbuf, ':'))) /* x:y? split */
*portp++ = 0;
else if (strchr (gbuf, '.')) /* x.y...? */
portp = NULL;
else {
portp = gbuf; /* port only */
addrp = NULL; /* no addr */
}
if (portp) { /* port string? */
if (ipp == NULL) /* not wanted? */
return SCPE_ARG;
port = (int32) get_uint (portp, 10, 65535, &r);
if ((r != SCPE_OK) || (port == 0))
return SCPE_ARG;
}
else port = 0;
if (addrp) { /* addr string? */
if (ipa == NULL) /* not wanted? */
return SCPE_ARG;
for (i = addr = 0; i < 4; i++) { /* four octets */
octetp = strchr (addrp, '.'); /* find octet end */
if (octetp != NULL) /* split string */
*octetp++ = 0;
else if (i < 3) /* except last */
return SCPE_ARG;
octet = (int32) get_uint (addrp, 10, 255, &r);
if (r != SCPE_OK)
return SCPE_ARG;
addr = (addr << 8) | octet;
addrp = octetp;
}
if (((addr & 0377) == 0) || ((addr & 0377) == 255))
return SCPE_ARG;
}
else addr = 0;
if (ipp) /* return req values */
*ipp = port;
if (ipa)
*ipa = addr;
return SCPE_OK;
}
t_stat udp_parse_remote (int32 link, char *premote)
{
// This routine will parse a remote address string in any of these forms -
@@ -356,102 +220,46 @@ t_stat udp_parse_remote (int32 link, char *premote)
// yourself!! In both cases, "w.x.y.z" is a dotted IP for the remote machine
// and "name.domain.com" is its name (which will be looked up to get the IP).
// If the host name/IP is omitted then it defaults to "localhost".
char *end, *colon; int32 port; struct hostent *he;
char *end; int32 lport, rport; t_stat ret;
char host[64], port[16];
if (*premote == '\0') return SCPE_2FARG;
// Look for the local port number. If it's not there, set rxport to zero for now.
port = strtoul(premote, &end, 10); udp_links[link].rxport = 0;
if ((*end == ':') && (port > 0)) {
udp_links[link].rxport = port; premote = end+1;
memset (udp_links[link].lport, 0, sizeof(udp_links[link].lport));
memset (udp_links[link].rhostport, 0, sizeof(udp_links[link].rhostport));
// Handle the llll::rrrr case first
if (2 == sscanf (premote, "%d::%d", &lport, &rport)) {
if ((lport < 1) || (lport >65535) || (rport < 1) || (rport >65535)) return SCPE_ARG;
sprintf (udp_links[link].lport, "%d", lport);
sprintf (udp_links[link].rhostport, "localhost:%d", rport);
return SCPE_OK;
}
// Look for "name:port" and extract the remote port...
if ((colon = strchr(premote, ':')) == NULL) return SCPE_ARG;
*colon++ = '\0'; port = strtoul(colon, &end, 10);
if ((*end != '\0') || (port == 0)) return SCPE_ARG;
udp_links[link].txport = port;
if (udp_links[link].rxport == 0) udp_links[link].rxport = port;
// Now try to parse the host as a dotted IP address ...
if (get_ipaddr(premote, &udp_links[link].ipremote, NULL) == SCPE_OK) return SCPE_OK;
// Special kludge - allow just ":port" to mean "localhost:port" ...
if(*premote == '\0') {
if (udp_links[link].rxport == udp_links[link].txport)
fprintf(stderr,"WARNING - use different transmit and receive ports!\n");
premote = "localhost";
// Look for the local port number and save it away.
lport = strtoul(premote, &end, 10);
if ((*end == ':') && (lport > 0)) {
sprintf (udp_links[link].lport, "%d", lport);
premote = end+1;
}
// Not a dotted IP - try to lookup a host name ...
if ((he = gethostbyname(premote)) == NULL) return SCPE_OPENERR;
udp_links[link].ipremote = * (unsigned long *) he->h_addr_list[0];
if (udp_links[link].ipremote == INADDR_NONE) {
fprintf(stderr,"WARNING - unable to resolve \"%s\"\n", premote);
return SCPE_OPENERR;
}
udp_links[link].ipremote = ntohl(udp_links[link].ipremote);
ret = sim_parse_addr (premote, host, sizeof(host), "localhost", port, sizeof(port), NULL, NULL);
if (ret != SCPE_OK) return SCPE_ARG;
sprintf (udp_links[link].rhostport, "%s:%s", host, port);
if (udp_links[link].lport[0] == '\0')
strcpy (udp_links[link].lport, port);
if ((strcmp (udp_links[link].lport, port) == 0) &&
(strcmp ("localhost", host) == 0))
fprintf(stderr,"WARNING - use different transmit and receive ports!\n");
return SCPE_OK;
}
t_stat udp_socket_error (int32 link, const char *msg)
t_stat udp_error (int32 link, const char *msg)
{
// This routine is called whenever a SOCKET_ERROR is returned for any I/O.
fprintf(stderr,"UDP%d - %s failed with error %d\n", link, msg, WSAGetLastError());
return SCPE_IOERR;
}
t_stat udp_create_rx_socket (int32 link)
{
// This routine will create the receiver socket for the virtual modem.
// Sockets are always UDP and, in the case of the receiver, bound to the port
// specified. Receiving sockets are also always set to NON BLOCKING mode.
int32 iret; uint32 flags = 1;
// Creating the socket works on both Windows and Linux ...
udp_links[link].rxsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (udp_links[link].rxsock == INVALID_SOCKET)
return udp_socket_error(link, "RX socket()");
udp_links[link].rxaddr.sin_family = AF_INET;
udp_links[link].rxaddr.sin_port = htons(udp_links[link].rxport);
udp_links[link].rxaddr.sin_addr.s_addr = htonl(INADDR_ANY);
iret = bind(udp_links[link].rxsock, (SOCKADDR *) &udp_links[link].rxaddr, sizeof(struct sockaddr_in));
if (iret != 0)
return udp_socket_error(link, "bind()");
// But making it non-blocking is a problem ...
#ifdef _WIN32
iret = ioctlsocket(udp_links[link].rxsock, FIONBIO, (u_long *) &flags);
if (iret != 0)
return udp_socket_error(link, "ioctlsocket()");
#elif defined(__linux__)
flags = fcntl(udp_links[link].rxsock, F_GETFL, 0);
if (flags == -1) return udp_socket_error(link, "fcntl(F_GETFL)");
iret = fcntl(udp_links[link].rxsock, F_SETFL, flags | O_NONBLOCK);
if (iret == -1) return udp_socket_error(link, "fcntl(F_SETFL)");
iret = fcntl(udp_links[link].rxsock, F_SETOWN, getpid());
if (iret == -1) return udp_socket_error(link, "fcntl(F_SETOWN)");
#endif
return SCPE_OK;
}
t_stat udp_create_tx_socket (int32 link)
{
// This routine will create the transmitter socket for the virtual modem.
// In the case of the transmitter, we don't bind the socket at this time -
// WINSOCK will automatically bind it for us to a free port on the first IO.
// Also note that transmitting sockets are blocking; we don't have code (yet!)
// to allow them to be nonblocking.
udp_links[link].txsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (udp_links[link].txsock == INVALID_SOCKET)
return udp_socket_error(link, "TX socket()");
// Initialize the txaddr structure too - note that this isn't used now; it's
// the sockaddr we will use when we later do a sendto() the remote host!
udp_links[link].txaddr.sin_family = AF_INET;
udp_links[link].txaddr.sin_port = htons(udp_links[link].txport);
udp_links[link].txaddr.sin_addr.s_addr = htonl(udp_links[link].ipremote);
return SCPE_OK;
}
t_stat udp_create (DEVICE *dptr, char *premote, int32 *pln)
{
// Create a logical UDP link to the specified remote system. The "remote"
@@ -467,22 +275,26 @@ t_stat udp_create (DEVICE *dptr, char *premote, int32 *pln)
// which is a handle used to identify this connection to all future udp_xyz()
// calls.
t_stat ret;
char linkinfo[128];
int32 link = udp_find_free_link();
if (link < 0) return SCPE_MEM;
// Make sure WINSOCK is initialized ...
if ((ret = udp_startup(dptr)) != SCPE_OK) return ret;
// Parse the remote name and set up the ipaddr and port ...
if ((ret = udp_parse_remote(link, premote)) != SCPE_OK) return ret;
// Create the sockets for the transmitter and receiver ...
if ((ret = udp_create_rx_socket(link)) != SCPE_OK) return ret;
if ((ret = udp_create_tx_socket(link)) != SCPE_OK) return ret;
// Create the socket connection to the destination ...
sprintf(linkinfo, "Line=%d,%s,UDP,Connect=%s", link, udp_links[link].lport, udp_links[link].rhostport);
ret = tmxr_open_master (&udp_tmxr, linkinfo);
if (ret != SCPE_OK) return ret;
// All done - mark the TCP_LINK data as "used" and return the index.
udp_links[link].used = TRUE; *pln = link;
sim_debug(IMP_DBG_UDP, dptr, "link %d - listening on port %d and sending to %s\n", link, udp_links[link].rxport, udp_format_remote(link));
udp_lines[link].dptr = udp_links[link].dptr = dptr; // save device
udp_tmxr.uptr = dptr->units;
udp_tmxr.last_poll_time = 1; // h316'a use of TMXR doesn't poll periodically for connects
tmxr_poll_conn (&udp_tmxr); // force connection initialization now
udp_tmxr.last_poll_time = 1; // h316'a use of TMXR doesn't poll periodically for connects
sim_debug(IMP_DBG_UDP, dptr, "link %d - listening on port %s and sending to %s\n", link, udp_links[link].lport, udp_links[link].rhostport);
return SCPE_OK;
}
@@ -491,26 +303,18 @@ t_stat udp_release (DEVICE *dptr, int32 link)
// Close a link that was created by udp_create() and release any resources
// allocated to it. We always return SCPE_OK unless the link specified is
// already unused.
int32 iret, i;
if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR;
if (!udp_links[link].used) return SCPE_IERR;
if (dptr != udp_links[link].dptr) return SCPE_IERR;
// Close the sockets associated with this connection - that's easy ...
iret = closesocket(udp_links[link].rxsock);
if (iret != 0) udp_socket_error(link, "closesocket()");
iret = closesocket(udp_links[link].txsock);
if (iret != 0) udp_socket_error(link, "closesocket()");
tmxr_detach_ln (&udp_lines[link]);
udp_links[link].used = FALSE;
sim_debug(IMP_DBG_UDP, dptr, "link %d - closed\n", link);
// If we just closed the last link, then call udp_shutdown() ...
for (i = 0; i < MAXLINKS; ++i) {
if (udp_links[i].used) return SCPE_OK;
}
return udp_shutdown(dptr);
return SCPE_OK;
}
t_stat udp_send_to (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count, SOCKADDR *pdest)
t_stat udp_send (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count)
{
// This routine does all the work of sending an IMP data packet. pdata
// is a pointer (usually into H316 simulated memory) to the IMP packet data,
@@ -520,10 +324,11 @@ t_stat udp_send_to (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count, SOCKA
// doesn't necessarily need to have the same endian-ness as this machine.
// Second, notice that transmitting sockets are NOT set to non blocking so
// this routine might wait, but we assume the wait will never be too long.
UDP_PACKET pkt; int pktlen; uint16 i; int32 iret;
UDP_PACKET pkt; int pktlen; uint16 i; t_stat iret;
if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR;
if (!udp_links[link].used) return SCPE_IERR;
if ((pdata == NULL) || (count == 0) || (count > MAXDATA)) return SCPE_IERR;
if (dptr != udp_links[link].dptr) return SCPE_IERR;
// Build the UDP packet, filling in our own header information and copying
// the H316 words from memory. REMEMBER THAT EVERYTHING IS IN NETWORK ORDER!
@@ -534,28 +339,19 @@ t_stat udp_send_to (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count, SOCKA
pktlen = UDP_HEADER_LEN + count*sizeof(uint16);
// Send it and we're outta here ...
iret = sendto(udp_links[link].txsock, (const char *) &pkt, pktlen, 0, pdest, sizeof (struct sockaddr_in));
if (iret == SOCKET_ERROR) return udp_socket_error(link, "sendto()");
iret = tmxr_put_packet_ln (&udp_lines[link], (const uint8 *)&pkt, (size_t)pktlen);
if (iret != SCPE_OK) return udp_error(link, "tmxr_put_packet_ln()");
sim_debug(IMP_DBG_UDP, dptr, "link %d - packet sent (sequence=%d, length=%d)\n", link, ntohl(pkt.sequence), ntohs(pkt.count));
return SCPE_OK;
}
t_stat udp_send (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count)
t_stat udp_set_link_loopback (DEVICE *dptr, int32 link, t_bool enable_loopback)
{
// Send an IMP packet to the remote simh. This is the usual case - the only
// reason there's any other options at all is so we can emulate loopback.
return udp_send_to (dptr, link, pdata, count, (SOCKADDR *) &(udp_links[link].txaddr));
}
if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR;
if (!udp_links[link].used) return SCPE_IERR;
if (dptr != udp_links[link].dptr) return SCPE_IERR;
t_stat udp_send_self (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count)
{
// Send an IMP packet to our own receiving socket. This might seem silly,
// but it's used to emulate the line loopback function...
struct sockaddr_in self;
self.sin_family = AF_INET;
self.sin_port = htons(udp_links[link].rxport);
self.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
return udp_send_to (dptr, link, pdata, count, (SOCKADDR *) &self);
return tmxr_set_line_loopback (&udp_lines[link], enable_loopback);
}
int32 udp_receive_packet (int32 link, UDP_PACKET *ppkt)
@@ -568,27 +364,22 @@ int32 udp_receive_packet (int32 link, UDP_PACKET *ppkt)
// Note that this routine only receives the packet - it doesn't handle any
// of the checking for valid packets, unexpected packets, duplicate or out of
// sequence packets. That's strictly the caller's problem!
int32 pktsiz;
struct sockaddr_in sender;
#if defined (macintosh) || defined (__linux) || defined (__linux__) || \
defined (__APPLE__) || defined (__OpenBSD__) || \
defined(__NetBSD__) || defined(__FreeBSD__) || \
(defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED))
socklen_t sndsiz = (socklen_t)sizeof(sender);
#elif defined (_WIN32) || defined (__EMX__) || \
(defined (__ALPHA) && defined (__unix__)) || \
defined (__hpux)
int sndsiz = (int)sizeof(sender);
#else
size_t sndsiz = sizeof(sender);
#endif
size_t pktsiz;
uint8 *pbuf;
t_stat ret;
pktsiz = recvfrom(udp_links[link].rxsock, (char *) ppkt, sizeof(UDP_PACKET),
0, (SOCKADDR *) &sender, &sndsiz);
if (pktsiz >= 0) return pktsiz;
if (WSAGetLastError() == WSAEWOULDBLOCK) return 0;
udp_socket_error(link, "recvfrom()");
return NOLINK;
udp_lines[link].rcve = TRUE; // Enable receiver
tmxr_poll_rx (&udp_tmxr);
ret = tmxr_get_packet_ln (&udp_lines[link], &pbuf, &pktsiz);
udp_lines[link].rcve = FALSE; // Disable receiver
if (ret != SCPE_OK) {
udp_error(link, "tmxr_get_packet_ln()");
return NOLINK;
}
if (pbuf == NULL) return 0;
// Got a packet, so copy it to the packet buffer
memcpy (ppkt, pbuf, pktsiz);
return pktsiz;
}
int32 udp_receive (DEVICE *dptr, int32 link, uint16 *pdata, uint16 maxbuf)
@@ -611,6 +402,7 @@ int32 udp_receive (DEVICE *dptr, int32 link, uint16 *pdata, uint16 maxbuf)
UDP_PACKET pkt; int32 pktlen, explen, implen, i; uint32 magic, pktseq;
if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR;
if (!udp_links[link].used) return SCPE_IERR;
if (dptr != udp_links[link].dptr) return SCPE_IERR;
while ((pktlen = udp_receive_packet(link, &pkt)) > 0) {
// First do some header checks for a valid UDP packet ...