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:
parent
378e3e03a3
commit
c9f73eac90
6 changed files with 126 additions and 313 deletions
|
@ -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_create (DEVICE *pdtr, char *premote, int32 *plink);
|
||||||
t_stat udp_release (DEVICE *dptr, int32 link);
|
t_stat udp_release (DEVICE *dptr, int32 link);
|
||||||
t_stat udp_send (DEVICE *pdtr, int32 link, uint16 *pdata, uint16 count);
|
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);
|
int32 udp_receive (DEVICE *dptr, int32 link, uint16 *pdata, uint16 maxbufg);
|
||||||
|
|
||||||
#endif // #ifndef _H316_IMP_H_
|
#endif // #ifndef _H316_IMP_H_
|
||||||
|
|
|
@ -319,6 +319,7 @@ MIDB *const mi_midbs [MI_NUM] = {&mi1_db, &mi2_db, &mi3_db, &mi4_db,
|
||||||
void mi_reset_rx (uint16 line)
|
void mi_reset_rx (uint16 line)
|
||||||
{
|
{
|
||||||
PMIDB(line)->iloop = PMIDB(line)->lloop = FALSE;
|
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)->rxerror = PMIDB(line)->rxpending = FALSE;
|
||||||
PMIDB(line)->rxtotal = 0;
|
PMIDB(line)->rxtotal = 0;
|
||||||
CLR_RX_IRQ(line); CLR_RX_IEN(line);
|
CLR_RX_IRQ(line); CLR_RX_IEN(line);
|
||||||
|
@ -328,6 +329,7 @@ void mi_reset_rx (uint16 line)
|
||||||
void mi_reset_tx (uint16 line)
|
void mi_reset_tx (uint16 line)
|
||||||
{
|
{
|
||||||
PMIDB(line)->iloop = PMIDB(line)->lloop = FALSE;
|
PMIDB(line)->iloop = PMIDB(line)->lloop = FALSE;
|
||||||
|
udp_set_link_loopback (PDEVICE(line), PMIDB(line)->link, FALSE);
|
||||||
PMIDB(line)->txtotal = PMIDB(line)->txdelay = 0;
|
PMIDB(line)->txtotal = PMIDB(line)->txdelay = 0;
|
||||||
CLR_TX_IRQ(line); CLR_TX_IEN(line);
|
CLR_TX_IRQ(line); CLR_TX_IEN(line);
|
||||||
}
|
}
|
||||||
|
@ -424,9 +426,6 @@ void mi_start_tx (uint16 line)
|
||||||
if (PMIDB(line)->iloop) {
|
if (PMIDB(line)->iloop) {
|
||||||
mi_rx_local(line, next, count);
|
mi_rx_local(line, next, count);
|
||||||
} else if (PMIDB(line)->link != NOLINK) {
|
} 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);
|
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:
|
case 001:
|
||||||
// MnUNXP - un-cross patch modem ...
|
// MnUNXP - un-cross patch modem ...
|
||||||
sim_debug(IMP_DBG_IOT,PDEVICE(line),"un-cross patch modem (PC=%06o)\n", PC-1);
|
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:
|
case 002:
|
||||||
// MnLXP - enable line cross patch ...
|
// MnLXP - enable line cross patch ...
|
||||||
sim_debug(IMP_DBG_IOT,PDEVICE(line),"enable line cross patch (PC=%06o)\n", PC-1);
|
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:
|
case 003:
|
||||||
// MnIXP - enable interface cross patch ...
|
// MnIXP - enable interface cross patch ...
|
||||||
sim_debug(IMP_DBG_IOT,PDEVICE(line),"enable interface cross patch (PC=%06o)\n", PC-1);
|
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:
|
case 004:
|
||||||
// MnIN - start modem input ...
|
// MnIN - start modem input ...
|
||||||
mi_debug_mio(line, PDIB(line)->rxdmc, "input");
|
mi_debug_mio(line, PDIB(line)->rxdmc, "input");
|
||||||
|
|
350
H316/h316_udp.c
350
H316/h316_udp.c
|
@ -29,7 +29,8 @@
|
||||||
udp socket routines
|
udp socket routines
|
||||||
|
|
||||||
26-Jun-13 RLA Rewritten from TCP version
|
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
|
OVERVIEW
|
||||||
|
|
||||||
|
@ -134,15 +135,7 @@
|
||||||
*/
|
*/
|
||||||
#ifdef VM_IMPTIP
|
#ifdef VM_IMPTIP
|
||||||
#include "sim_defs.h" // simh machine independent definitions
|
#include "sim_defs.h" // simh machine independent definitions
|
||||||
#ifdef _WIN32 // WINSOCK definitions
|
#include "sim_tmxr.h" // The MUX layer exposes packet send and receive semantics
|
||||||
#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 "h316_defs.h" // H316 emulator definitions
|
#include "h316_defs.h" // H316 emulator definitions
|
||||||
#include "h316_imp.h" // ARPAnet IMP/TIP definitions
|
#include "h316_imp.h" // ARPAnet IMP/TIP definitions
|
||||||
|
|
||||||
|
@ -158,30 +151,15 @@
|
||||||
// the fragments arrive intact then the destination should reassemble them.
|
// the fragments arrive intact then the destination should reassemble them.
|
||||||
#define MAXDATA 16384 // longest possible IMP packet (in H316 words)
|
#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 ...
|
// UDP connection data structure ...
|
||||||
// One of these blocks is allocated for every simulated modem link.
|
// One of these blocks is allocated for every simulated modem link.
|
||||||
struct _UDP_LINK {
|
struct _UDP_LINK {
|
||||||
t_bool used; // TRUE if this UDP_LINK is in use
|
t_bool used; // TRUE if this UDP_LINK is in use
|
||||||
uint32 ipremote; // IP address of the remote system
|
char rhostport[64]; // Remote host:port
|
||||||
uint16 rxport; // OUR receiving port number
|
char lport[64]; // Local port
|
||||||
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
|
|
||||||
uint32 rxsequence; // next message sequence number for receive
|
uint32 rxsequence; // next message sequence number for receive
|
||||||
uint32 txsequence; // next message sequence number for transmit
|
uint32 txsequence; // next message sequence number for transmit
|
||||||
|
DEVICE *dptr; // Device associated with link
|
||||||
};
|
};
|
||||||
typedef struct _UDP_LINK UDP_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))
|
#define UDP_HEADER_LEN (2*sizeof(uint32) + sizeof(uint16))
|
||||||
|
|
||||||
// Locals ...
|
// Locals ...
|
||||||
t_bool udp_wsa_started = FALSE; // TRUE if WSAStartup() has been called
|
|
||||||
UDP_LINK udp_links[MAXLINKS] = {0}; // data for every active connection
|
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
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 udp_find_free_link (void)
|
int32 udp_find_free_link (void)
|
||||||
{
|
{
|
||||||
|
@ -253,91 +196,12 @@ int32 udp_find_free_link (void)
|
||||||
for (i = 0; i < MAXLINKS; ++i) {
|
for (i = 0; i < MAXLINKS; ++i) {
|
||||||
if (udp_links[i].used == 0) {
|
if (udp_links[i].used == 0) {
|
||||||
memset(&udp_links[i], 0, sizeof(UDP_LINK));
|
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 i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NOLINK;
|
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)
|
t_stat udp_parse_remote (int32 link, char *premote)
|
||||||
{
|
{
|
||||||
// This routine will parse a remote address string in any of these forms -
|
// 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
|
// 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).
|
// 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".
|
// 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;
|
if (*premote == '\0') return SCPE_2FARG;
|
||||||
// Look for the local port number. If it's not there, set rxport to zero for now.
|
memset (udp_links[link].lport, 0, sizeof(udp_links[link].lport));
|
||||||
port = strtoul(premote, &end, 10); udp_links[link].rxport = 0;
|
memset (udp_links[link].rhostport, 0, sizeof(udp_links[link].rhostport));
|
||||||
if ((*end == ':') && (port > 0)) {
|
// Handle the llll::rrrr case first
|
||||||
udp_links[link].rxport = port; premote = end+1;
|
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...
|
// Look for the local port number and save it away.
|
||||||
if ((colon = strchr(premote, ':')) == NULL) return SCPE_ARG;
|
lport = strtoul(premote, &end, 10);
|
||||||
*colon++ = '\0'; port = strtoul(colon, &end, 10);
|
if ((*end == ':') && (lport > 0)) {
|
||||||
if ((*end != '\0') || (port == 0)) return SCPE_ARG;
|
sprintf (udp_links[link].lport, "%d", lport);
|
||||||
udp_links[link].txport = port;
|
premote = end+1;
|
||||||
if (udp_links[link].rxport == 0) udp_links[link].rxport = port;
|
}
|
||||||
|
|
||||||
// Now try to parse the host as a dotted IP address ...
|
ret = sim_parse_addr (premote, host, sizeof(host), "localhost", port, sizeof(port), NULL, NULL);
|
||||||
if (get_ipaddr(premote, &udp_links[link].ipremote, NULL) == SCPE_OK) return SCPE_OK;
|
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);
|
||||||
|
|
||||||
// Special kludge - allow just ":port" to mean "localhost:port" ...
|
if ((strcmp (udp_links[link].lport, port) == 0) &&
|
||||||
if(*premote == '\0') {
|
(strcmp ("localhost", host) == 0))
|
||||||
if (udp_links[link].rxport == udp_links[link].txport)
|
|
||||||
fprintf(stderr,"WARNING - use different transmit and receive ports!\n");
|
fprintf(stderr,"WARNING - use different transmit and receive ports!\n");
|
||||||
premote = "localhost";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
return SCPE_OK;
|
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.
|
// 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());
|
fprintf(stderr,"UDP%d - %s failed with error %d\n", link, msg, WSAGetLastError());
|
||||||
return SCPE_IOERR;
|
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)
|
t_stat udp_create (DEVICE *dptr, char *premote, int32 *pln)
|
||||||
{
|
{
|
||||||
// Create a logical UDP link to the specified remote system. The "remote"
|
// 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()
|
// which is a handle used to identify this connection to all future udp_xyz()
|
||||||
// calls.
|
// calls.
|
||||||
t_stat ret;
|
t_stat ret;
|
||||||
|
char linkinfo[128];
|
||||||
int32 link = udp_find_free_link();
|
int32 link = udp_find_free_link();
|
||||||
if (link < 0) return SCPE_MEM;
|
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 ...
|
// Parse the remote name and set up the ipaddr and port ...
|
||||||
if ((ret = udp_parse_remote(link, premote)) != SCPE_OK) return ret;
|
if ((ret = udp_parse_remote(link, premote)) != SCPE_OK) return ret;
|
||||||
|
|
||||||
// Create the sockets for the transmitter and receiver ...
|
// Create the socket connection to the destination ...
|
||||||
if ((ret = udp_create_rx_socket(link)) != SCPE_OK) return ret;
|
sprintf(linkinfo, "Line=%d,%s,UDP,Connect=%s", link, udp_links[link].lport, udp_links[link].rhostport);
|
||||||
if ((ret = udp_create_tx_socket(link)) != SCPE_OK) return ret;
|
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.
|
// All done - mark the TCP_LINK data as "used" and return the index.
|
||||||
udp_links[link].used = TRUE; *pln = link;
|
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;
|
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
|
// 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
|
// allocated to it. We always return SCPE_OK unless the link specified is
|
||||||
// already unused.
|
// already unused.
|
||||||
int32 iret, i;
|
|
||||||
if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR;
|
if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR;
|
||||||
if (!udp_links[link].used) 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 ...
|
tmxr_detach_ln (&udp_lines[link]);
|
||||||
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()");
|
|
||||||
udp_links[link].used = FALSE;
|
udp_links[link].used = FALSE;
|
||||||
sim_debug(IMP_DBG_UDP, dptr, "link %d - closed\n", link);
|
sim_debug(IMP_DBG_UDP, dptr, "link %d - closed\n", link);
|
||||||
|
|
||||||
// If we just closed the last link, then call udp_shutdown() ...
|
return SCPE_OK;
|
||||||
for (i = 0; i < MAXLINKS; ++i) {
|
|
||||||
if (udp_links[i].used) return SCPE_OK;
|
|
||||||
}
|
|
||||||
return udp_shutdown(dptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// 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,
|
// 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.
|
// 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
|
// 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.
|
// 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 ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR;
|
||||||
if (!udp_links[link].used) return SCPE_IERR;
|
if (!udp_links[link].used) return SCPE_IERR;
|
||||||
if ((pdata == NULL) || (count == 0) || (count > MAXDATA)) 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
|
// Build the UDP packet, filling in our own header information and copying
|
||||||
// the H316 words from memory. REMEMBER THAT EVERYTHING IS IN NETWORK ORDER!
|
// 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);
|
pktlen = UDP_HEADER_LEN + count*sizeof(uint16);
|
||||||
|
|
||||||
// Send it and we're outta here ...
|
// Send it and we're outta here ...
|
||||||
iret = sendto(udp_links[link].txsock, (const char *) &pkt, pktlen, 0, pdest, sizeof (struct sockaddr_in));
|
iret = tmxr_put_packet_ln (&udp_lines[link], (const uint8 *)&pkt, (size_t)pktlen);
|
||||||
if (iret == SOCKET_ERROR) return udp_socket_error(link, "sendto()");
|
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));
|
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;
|
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
|
if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR;
|
||||||
// reason there's any other options at all is so we can emulate loopback.
|
if (!udp_links[link].used) return SCPE_IERR;
|
||||||
return udp_send_to (dptr, link, pdata, count, (SOCKADDR *) &(udp_links[link].txaddr));
|
if (dptr != udp_links[link].dptr) return SCPE_IERR;
|
||||||
}
|
|
||||||
|
|
||||||
t_stat udp_send_self (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count)
|
return tmxr_set_line_loopback (&udp_lines[link], enable_loopback);
|
||||||
{
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 udp_receive_packet (int32 link, UDP_PACKET *ppkt)
|
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
|
// 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
|
// of the checking for valid packets, unexpected packets, duplicate or out of
|
||||||
// sequence packets. That's strictly the caller's problem!
|
// sequence packets. That's strictly the caller's problem!
|
||||||
int32 pktsiz;
|
size_t pktsiz;
|
||||||
struct sockaddr_in sender;
|
uint8 *pbuf;
|
||||||
#if defined (macintosh) || defined (__linux) || defined (__linux__) || \
|
t_stat ret;
|
||||||
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
|
|
||||||
|
|
||||||
pktsiz = recvfrom(udp_links[link].rxsock, (char *) ppkt, sizeof(UDP_PACKET),
|
udp_lines[link].rcve = TRUE; // Enable receiver
|
||||||
0, (SOCKADDR *) &sender, &sndsiz);
|
tmxr_poll_rx (&udp_tmxr);
|
||||||
if (pktsiz >= 0) return pktsiz;
|
ret = tmxr_get_packet_ln (&udp_lines[link], &pbuf, &pktsiz);
|
||||||
if (WSAGetLastError() == WSAEWOULDBLOCK) return 0;
|
udp_lines[link].rcve = FALSE; // Disable receiver
|
||||||
udp_socket_error(link, "recvfrom()");
|
if (ret != SCPE_OK) {
|
||||||
|
udp_error(link, "tmxr_get_packet_ln()");
|
||||||
return NOLINK;
|
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)
|
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;
|
UDP_PACKET pkt; int32 pktlen, explen, implen, i; uint32 magic, pktseq;
|
||||||
if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR;
|
if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR;
|
||||||
if (!udp_links[link].used) 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) {
|
while ((pktlen = udp_receive_packet(link, &pkt)) > 0) {
|
||||||
// First do some header checks for a valid UDP packet ...
|
// First do some header checks for a valid UDP packet ...
|
||||||
|
|
|
@ -599,7 +599,7 @@ if (hostp != NULL) {
|
||||||
hostp[strlen(hostp)-1] = '\0';
|
hostp[strlen(hostp)-1] = '\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (host) /* host wanted? */
|
if (host) { /* host wanted? */
|
||||||
if (hostp != NULL) {
|
if (hostp != NULL) {
|
||||||
if (strlen(hostp) >= host_len)
|
if (strlen(hostp) >= host_len)
|
||||||
return SCPE_ARG; /* no room */
|
return SCPE_ARG; /* no room */
|
||||||
|
@ -613,12 +613,14 @@ if (host) /* host wanted? */
|
||||||
strcpy (host, default_host);
|
strcpy (host, default_host);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (default_host)
|
if (default_host) {
|
||||||
if (strlen(default_host) >= host_len)
|
if (strlen(default_host) >= host_len)
|
||||||
return SCPE_ARG; /* no room */
|
return SCPE_ARG; /* no room */
|
||||||
else
|
else
|
||||||
strcpy (host, default_host);
|
strcpy (host, default_host);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (validate_addr) {
|
if (validate_addr) {
|
||||||
struct addrinfo *ai_host, *ai_validate, *ai;
|
struct addrinfo *ai_host, *ai_validate, *ai;
|
||||||
t_stat status;
|
t_stat status;
|
||||||
|
|
40
sim_tmxr.c
40
sim_tmxr.c
|
@ -664,6 +664,9 @@ else { /* Telnet connection */
|
||||||
written = sim_write_sock (lp->sock, &(lp->txb[i]), length);
|
written = sim_write_sock (lp->sock, &(lp->txb[i]), length);
|
||||||
|
|
||||||
if (written == SOCKET_ERROR) /* did an error occur? */
|
if (written == SOCKET_ERROR) /* did an error occur? */
|
||||||
|
if (lp->datagram)
|
||||||
|
return written; /* ignore errors on datagram sockets */
|
||||||
|
else
|
||||||
return -1; /* return error indication */
|
return -1; /* return error indication */
|
||||||
else
|
else
|
||||||
return written;
|
return written;
|
||||||
|
@ -1543,19 +1546,19 @@ return val;
|
||||||
NULL, but success (SCPE_OK) is returned
|
NULL, but success (SCPE_OK) is returned
|
||||||
*/
|
*/
|
||||||
|
|
||||||
t_stat tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, size_t *psize)
|
t_stat tmxr_get_packet_ln (TMLN *lp, uint8 **pbuf, size_t *psize)
|
||||||
{
|
{
|
||||||
return tmxr_get_packet_ln_ex (lp, pbuf, psize, 0);
|
return tmxr_get_packet_ln_ex (lp, pbuf, psize, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
t_stat tmxr_get_packet_ln_ex (TMLN *lp, const uint8 **pbuf, size_t *psize, uint8 frame_byte)
|
t_stat tmxr_get_packet_ln_ex (TMLN *lp, uint8 **pbuf, size_t *psize, uint8 frame_byte)
|
||||||
{
|
{
|
||||||
int32 c;
|
int32 c;
|
||||||
size_t pktsize;
|
size_t pktsize;
|
||||||
size_t fc_size = (frame_byte ? 1 : 0);
|
size_t fc_size = (frame_byte ? 1 : 0);
|
||||||
|
|
||||||
while (TMXR_VALID & (c = tmxr_getc_ln (lp))) {
|
while (TMXR_VALID & (c = tmxr_getc_ln (lp))) {
|
||||||
if (lp->rxpboffset + 1 > lp->rxpbsize) {
|
if (lp->rxpboffset + 3 > lp->rxpbsize) {
|
||||||
lp->rxpbsize += 512;
|
lp->rxpbsize += 512;
|
||||||
lp->rxpb = (uint8 *)realloc (lp->rxpb, lp->rxpbsize);
|
lp->rxpb = (uint8 *)realloc (lp->rxpb, lp->rxpbsize);
|
||||||
}
|
}
|
||||||
|
@ -1563,8 +1566,15 @@ while (TMXR_VALID & (c = tmxr_getc_ln (lp))) {
|
||||||
tmxr_debug (TMXR_DBG_PRCV, lp, "Received Unexpected Framing Byte", (char *)&lp->rxpb[lp->rxpboffset], 1);
|
tmxr_debug (TMXR_DBG_PRCV, lp, "Received Unexpected Framing Byte", (char *)&lp->rxpb[lp->rxpboffset], 1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
lp->rxpb[lp->rxpboffset] = c & 0xFF;
|
if ((lp->datagram) && (lp->rxpboffset == fc_size)) {
|
||||||
lp->rxpboffset += 1;
|
/* Datagram packet length is provided as a part of the natural datagram
|
||||||
|
delivery, for TCP lines, we read the packet length from the data stream.
|
||||||
|
So, here we stuff packet size into head of packet buffer so it looks like
|
||||||
|
it was delivered by TCP and the below return logic doesn't have to worry */
|
||||||
|
lp->rxpb[lp->rxpboffset++] = (uint8)(((1 + lp->rxbpi - lp->rxbpr) >> 8) & 0xFF);
|
||||||
|
lp->rxpb[lp->rxpboffset++] = (uint8)((1 + lp->rxbpi - lp->rxbpr) & 0xFF);
|
||||||
|
}
|
||||||
|
lp->rxpb[lp->rxpboffset++] = c & 0xFF;
|
||||||
if (lp->rxpboffset >= (2 + fc_size)) {
|
if (lp->rxpboffset >= (2 + fc_size)) {
|
||||||
pktsize = (lp->rxpb[0+fc_size] << 8) | lp->rxpb[1+fc_size];
|
pktsize = (lp->rxpb[0+fc_size] << 8) | lp->rxpb[1+fc_size];
|
||||||
if (pktsize == (lp->rxpboffset - 2)) {
|
if (pktsize == (lp->rxpboffset - 2)) {
|
||||||
|
@ -1612,10 +1622,12 @@ for (i = 0; i < mp->lines; i++) { /* loop thru lines */
|
||||||
lp->rxbsz - lp->rxbpi);
|
lp->rxbsz - lp->rxbpi);
|
||||||
|
|
||||||
if (nbytes < 0) { /* line error? */
|
if (nbytes < 0) { /* line error? */
|
||||||
|
if (!lp->datagram) { /* ignore errors reading UDP sockets */
|
||||||
if (!lp->txbfd || lp->notelnet)
|
if (!lp->txbfd || lp->notelnet)
|
||||||
lp->txbpi = lp->txbpr = 0; /* Drop the data we already know we can't send */
|
lp->txbpi = lp->txbpr = 0; /* Drop the data we already know we can't send */
|
||||||
tmxr_close_ln (lp); /* disconnect line */
|
tmxr_close_ln (lp); /* disconnect line */
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else if (nbytes > 0) { /* if data rcvd */
|
else if (nbytes > 0) { /* if data rcvd */
|
||||||
|
|
||||||
|
@ -1816,6 +1828,7 @@ t_stat tmxr_put_packet_ln_ex (TMLN *lp, const uint8 *buf, size_t size, uint8 fra
|
||||||
{
|
{
|
||||||
t_stat r;
|
t_stat r;
|
||||||
size_t fc_size = (frame_byte ? 1 : 0);
|
size_t fc_size = (frame_byte ? 1 : 0);
|
||||||
|
size_t pktlen_size = (lp->datagram ? 0 : 2);
|
||||||
|
|
||||||
if (!lp->conn)
|
if (!lp->conn)
|
||||||
return SCPE_LOST;
|
return SCPE_LOST;
|
||||||
|
@ -1823,17 +1836,19 @@ if (lp->txppoffset < lp->txppsize) {
|
||||||
tmxr_debug (TMXR_DBG_PXMT, lp, "Skipped Sending Packet - Transmit Busy", (char *)&lp->txpb[3], size);
|
tmxr_debug (TMXR_DBG_PXMT, lp, "Skipped Sending Packet - Transmit Busy", (char *)&lp->txpb[3], size);
|
||||||
return SCPE_STALL;
|
return SCPE_STALL;
|
||||||
}
|
}
|
||||||
if (lp->txpbsize < size + 2 + fc_size) {
|
if (lp->txpbsize < size + pktlen_size + fc_size) {
|
||||||
lp->txpbsize = size + 2 + fc_size;
|
lp->txpbsize = size + pktlen_size + fc_size;
|
||||||
lp->txpb = (uint8 *)realloc (lp->txpb, lp->txpbsize);
|
lp->txpb = (uint8 *)realloc (lp->txpb, lp->txpbsize);
|
||||||
}
|
}
|
||||||
lp->txpb[0] = frame_byte;
|
lp->txpb[0] = frame_byte;
|
||||||
lp->txpb[0+fc_size] = (size >> 8) & 0xFF;
|
if (!lp->datagram) {
|
||||||
lp->txpb[1+fc_size] = size & 0xFF;
|
lp->txpb[0+fc_size] = (size >> 8) & 0xFF;
|
||||||
memcpy (lp->txpb + 2 + fc_size, buf, size);
|
lp->txpb[1+fc_size] = size & 0xFF;
|
||||||
lp->txppsize = size + 2 + fc_size;
|
}
|
||||||
|
memcpy (lp->txpb + pktlen_size + fc_size, buf, size);
|
||||||
|
lp->txppsize = size + pktlen_size + fc_size;
|
||||||
lp->txppoffset = 0;
|
lp->txppoffset = 0;
|
||||||
tmxr_debug (TMXR_DBG_PXMT, lp, "Sending Packet", (char *)&lp->txpb[2+fc_size], size);
|
tmxr_debug (TMXR_DBG_PXMT, lp, "Sending Packet", (char *)&lp->txpb[pktlen_size+fc_size], size);
|
||||||
++lp->txpcnt;
|
++lp->txpcnt;
|
||||||
while ((lp->txppoffset < lp->txppsize) &&
|
while ((lp->txppoffset < lp->txppsize) &&
|
||||||
(SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset]))))
|
(SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset]))))
|
||||||
|
@ -1896,7 +1911,6 @@ if (nbytes) { /* >0? write */
|
||||||
sbytes = tmxr_write (lp, nbytes); /* write all data */
|
sbytes = tmxr_write (lp, nbytes); /* write all data */
|
||||||
else
|
else
|
||||||
sbytes = tmxr_write (lp, lp->txbsz - lp->txbpr);/* write to end buf */
|
sbytes = tmxr_write (lp, lp->txbsz - lp->txbpr);/* write to end buf */
|
||||||
|
|
||||||
if (sbytes >= 0) { /* ok? */
|
if (sbytes >= 0) { /* ok? */
|
||||||
tmxr_debug (TMXR_DBG_XMT, lp, "Sent", &(lp->txb[lp->txbpr]), sbytes);
|
tmxr_debug (TMXR_DBG_XMT, lp, "Sent", &(lp->txb[lp->txbpr]), sbytes);
|
||||||
lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */
|
lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */
|
||||||
|
|
|
@ -185,8 +185,8 @@ int32 tmxr_poll_conn (TMXR *mp);
|
||||||
t_stat tmxr_reset_ln (TMLN *lp);
|
t_stat tmxr_reset_ln (TMLN *lp);
|
||||||
t_stat tmxr_detach_ln (TMLN *lp);
|
t_stat tmxr_detach_ln (TMLN *lp);
|
||||||
int32 tmxr_getc_ln (TMLN *lp);
|
int32 tmxr_getc_ln (TMLN *lp);
|
||||||
t_stat tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, size_t *psize);
|
t_stat tmxr_get_packet_ln (TMLN *lp, uint8 **pbuf, size_t *psize);
|
||||||
t_stat tmxr_get_packet_ln_ex (TMLN *lp, const uint8 **pbuf, size_t *psize, uint8 frame_byte);
|
t_stat tmxr_get_packet_ln_ex (TMLN *lp, uint8 **pbuf, size_t *psize, uint8 frame_byte);
|
||||||
void tmxr_poll_rx (TMXR *mp);
|
void tmxr_poll_rx (TMXR *mp);
|
||||||
t_stat tmxr_putc_ln (TMLN *lp, int32 chr);
|
t_stat tmxr_putc_ln (TMLN *lp, int32 chr);
|
||||||
t_stat tmxr_put_packet_ln (TMLN *lp, const uint8 *buf, size_t size);
|
t_stat tmxr_put_packet_ln (TMLN *lp, const uint8 *buf, size_t size);
|
||||||
|
|
Loading…
Add table
Reference in a new issue