TMXR: Reworked output to physical Serial ports for VMS and Windows hosts

The goal being to have output to physical serial ports complete without host
OS level buffering so that what is transmitted via the serial port has correct
timing with respect to I/O completion status within a simulator.

Linux, OS X, and various other *nix host platforms still need work here.
This commit is contained in:
Mark Pizzolato 2016-07-14 13:53:41 -07:00
parent 83bf230a47
commit c3496e4aa6
3 changed files with 148 additions and 91 deletions

View file

@ -392,7 +392,7 @@ if ((strlen(devname) <= 5)
if (savname == NULL) { /* didn't translate */
if (stat)
*stat = SCPE_OPENERR;
return port;
return INVALID_HANDLE;
}
}
else {
@ -500,6 +500,14 @@ return r;
*/
struct SERPORT {
HANDLE hPort;
DWORD dwEvtMask;
OVERLAPPED oReadSync;
OVERLAPPED oWriteReady;
OVERLAPPED oWriteSync;
};
static int sim_serial_os_devices (int max, SERIAL_LIST* list)
{
int ports = 0;
@ -560,6 +568,7 @@ return ports;
static SERHANDLE sim_open_os_serial (char *name)
{
HANDLE hPort;
SERHANDLE port;
DCB dcb;
COMMCONFIG commdefault;
@ -576,10 +585,10 @@ if (!GetDefaultCommConfig (name, &commdefault, &commsize)) { /* get default c
return INVALID_HANDLE; /* indicate bad port name */
}
port = CreateFile (name, GENERIC_READ | GENERIC_WRITE, /* open the port */
0, NULL, OPEN_EXISTING, 0, 0);
hPort = CreateFile (name, GENERIC_READ | GENERIC_WRITE, /* open the port */
0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if (port == INVALID_HANDLE_VALUE) { /* open failed? */
if (hPort == INVALID_HANDLE_VALUE) { /* open failed? */
error = GetLastError (); /* get error code */
if ((error != ERROR_FILE_NOT_FOUND) && /* bad filename? */
@ -589,13 +598,16 @@ if (port == INVALID_HANDLE_VALUE) { /* open failed? */
return INVALID_HANDLE; /* indicate bad port name */
}
if (!GetCommState (port, &dcb)) { /* get the current comm parameters */
port = (SERHANDLE)calloc (1, sizeof(*port)); /* instantiate the SERHANDLE */
port->hPort = hPort;
if (!GetCommState (port->hPort, &dcb)) { /* get the current comm parameters */
error = GetLastError (); /* function failed; get error */
if (error != ERROR_INVALID_PARAMETER) /* not a serial port name? */
sim_error_serial ("GetCommState", (int) error); /* no, so report unexpected error */
CloseHandle (port); /* close the port */
sim_close_os_serial (port); /* close port */
return INVALID_HANDLE; /* and indicate bad port name */
}
@ -608,10 +620,10 @@ dcb.fInX = commdefault.dcb.fInX;
dcb.fDtrControl = DTR_CONTROL_DISABLE; /* disable DTR initially until poll connects */
if (!SetCommState (port, &dcb)) { /* configure the port with default parameters */
if (!SetCommState (port->hPort, &dcb)) { /* configure the port with default parameters */
sim_error_serial ("SetCommState", /* function failed; report unexpected error */
(int) GetLastError ());
CloseHandle (port); /* close port */
sim_close_os_serial (port); /* close port */
return INVALID_HANDLE; /* and indicate failure to caller */
}
@ -621,10 +633,52 @@ cto.ReadTotalTimeoutConstant = 0;
cto.WriteTotalTimeoutMultiplier = 0;
cto.WriteTotalTimeoutConstant = 0;
if (!SetCommTimeouts (port, &cto)) { /* configure port timeouts */
if (!SetCommTimeouts (port->hPort, &cto)) { /* configure port timeouts */
sim_error_serial ("SetCommTimeouts", /* function failed; report unexpected error */
(int) GetLastError ());
CloseHandle (port); /* close port */
sim_close_os_serial (port); /* close port */
return INVALID_HANDLE; /* and indicate failure to caller */
}
/* Create an event object for use by WaitCommEvent. */
port->oWriteReady.hEvent = CreateEvent(NULL, /* default security attributes */
TRUE, /* manual-reset event */
TRUE, /* signaled */
NULL); /* no name */
if (port->oWriteReady.hEvent == NULL) {
sim_error_serial ("CreateEvent", /* function failed; report unexpected error */
(int) GetLastError ());
sim_close_os_serial (port); /* close port */
return INVALID_HANDLE; /* and indicate failure to caller */
}
port->oReadSync.hEvent = CreateEvent(NULL, /* default security attributes */
TRUE, /* manual-reset event */
FALSE, /* not signaled */
NULL); /* no name */
if (port->oReadSync.hEvent == NULL) {
sim_error_serial ("CreateEvent", /* function failed; report unexpected error */
(int) GetLastError ());
sim_close_os_serial (port); /* close port */
return INVALID_HANDLE; /* and indicate failure to caller */
}
port->oWriteSync.hEvent = CreateEvent(NULL, /* default security attributes */
TRUE, /* manual-reset event */
FALSE, /* not signaled */
NULL); /* no name */
if (port->oWriteSync.hEvent == NULL) {
sim_error_serial ("CreateEvent", /* function failed; report unexpected error */
(int) GetLastError ());
sim_close_os_serial (port); /* close port */
return INVALID_HANDLE; /* and indicate failure to caller */
}
if (!SetCommMask (port->hPort, EV_TXEMPTY)) {
sim_error_serial ("SetCommMask", /* function failed; report unexpected error */
(int) GetLastError ());
sim_close_os_serial (port); /* close port */
return INVALID_HANDLE; /* and indicate failure to caller */
}
@ -665,7 +719,7 @@ DCB dcb;
DWORD error;
int32 i;
if (!GetCommState (port, &dcb)) { /* get the current comm parameters */
if (!GetCommState (port->hPort, &dcb)) { /* get the current comm parameters */
sim_error_serial ("GetCommState", /* function failed; report unexpected error */
(int) GetLastError ());
return SCPE_IOERR; /* return failure status */
@ -696,7 +750,7 @@ else if (config.stopbits == 0) /* 0 implies 1.5 stop bi
else
return SCPE_ARG; /* not a valid number of stop bits */
if (!SetCommState (port, &dcb)) { /* set the configuration */
if (!SetCommState (port->hPort, &dcb)) { /* set the configuration */
error = GetLastError (); /* check for error */
if (error == ERROR_INVALID_PARAMETER) /* invalid configuration? */
@ -729,28 +783,28 @@ if ((bits_to_set & ~(TMXR_MDM_OUTGOING)) || /* Assure only settable bits
(bits_to_set & bits_to_clear)) /* and can't set and clear the same bits */
return SCPE_ARG;
if (bits_to_set&TMXR_MDM_DTR)
if (!EscapeCommFunction (port, SETDTR)) {
if (!EscapeCommFunction (port->hPort, SETDTR)) {
sim_error_serial ("EscapeCommFunction", (int) GetLastError ());
return SCPE_IOERR;
}
if (bits_to_clear&TMXR_MDM_DTR)
if (!EscapeCommFunction (port, CLRDTR)) {
if (!EscapeCommFunction (port->hPort, CLRDTR)) {
sim_error_serial ("EscapeCommFunction", (int) GetLastError ());
return SCPE_IOERR;
}
if (bits_to_set&TMXR_MDM_RTS)
if (!EscapeCommFunction (port, SETRTS)) {
if (!EscapeCommFunction (port->hPort, SETRTS)) {
sim_error_serial ("EscapeCommFunction", (int) GetLastError ());
return SCPE_IOERR;
}
if (bits_to_clear&TMXR_MDM_RTS)
if (!EscapeCommFunction (port, CLRRTS)) {
if (!EscapeCommFunction (port->hPort, CLRRTS)) {
sim_error_serial ("EscapeCommFunction", (int) GetLastError ());
return SCPE_IOERR;
}
if (incoming_bits) {
DWORD ModemStat;
if (GetCommModemStatus (port, &ModemStat)) {
if (GetCommModemStatus (port->hPort, &ModemStat)) {
sim_error_serial ("GetCommModemStatus", (int) GetLastError ());
return SCPE_IOERR;
}
@ -790,14 +844,14 @@ DWORD commerrors;
COMSTAT cs;
char *bptr;
if (!ClearCommError (port, &commerrors, &cs)) { /* get the comm error flags */
if (!ClearCommError (port->hPort, &commerrors, &cs)) { /* get the comm error flags */
sim_error_serial ("ClearCommError", /* function failed; report unexpected error */
(int) GetLastError ());
return -1; /* return failure to caller */
}
if (!ReadFile (port, (LPVOID) buffer, /* read any available characters */
(DWORD) count, &read, NULL)) {
if (!ReadFile (port->hPort, (LPVOID) buffer, /* read any available characters */
(DWORD) count, &read, &port->oReadSync)) {
sim_error_serial ("ReadFile", /* function failed; report unexpected error */
(int) GetLastError ());
return -1; /* return failure to caller */
@ -825,16 +879,22 @@ return read; /* return the number of
int32 sim_write_serial (SERHANDLE port, char *buffer, int32 count)
{
DWORD written;
if (!WriteFile (port, (LPVOID) buffer, /* write the buffer to the serial port */
(DWORD) count, &written, NULL)) {
sim_error_serial ("WriteFile", /* function failed; report unexpected error */
if (WaitForSingleObject (port->oWriteReady.hEvent, 0) == WAIT_TIMEOUT)
return 0;
if ((!WriteFile (port->hPort, (LPVOID) buffer, /* write the buffer to the serial port */
(DWORD) count, NULL, &port->oWriteSync)) &&
(GetLastError () != ERROR_IO_PENDING)) {
sim_error_serial ("WriteFile", /* function failed; report unexpected error */
(int) GetLastError ());
return -1; /* return failure to caller */
return -1; /* return failure to caller */
}
else
return written; /* return number of characters written */
if ((!WaitCommEvent (port->hPort, &port->dwEvtMask, &port->oWriteReady)) &&
(GetLastError () != ERROR_IO_PENDING)) {
sim_error_serial ("WaitCommEvent", /* function failed; report unexpected error */
(int) GetLastError ());
return -1; /* return failure to caller */
}
return count; /* return number of characters written/queued */
}
@ -845,14 +905,25 @@ else
static void sim_close_os_serial (SERHANDLE port)
{
CloseHandle (port); /* close the port */
return;
if (port->oWriteReady.hEvent)
CloseHandle (port->oWriteReady.hEvent); /* close the event handle */
if (port->oReadSync.hEvent)
CloseHandle (port->oReadSync.hEvent); /* close the event handle */
if (port->oWriteSync.hEvent)
CloseHandle (port->oWriteSync.hEvent); /* close the event handle */
if (port->hPort)
CloseHandle (port->hPort); /* close the port */
free (port);
}
#elif defined (__unix__) || defined(__APPLE__) || defined(__hpux)
struct SERPORT {
int port;
};
#if defined(__linux) || defined(__linux__)
#include <dirent.h>
#include <libgen.h>
@ -1028,9 +1099,8 @@ static const tcflag_t l_clear = ISIG | /* enable signals */
IEXTEN; /* enable extended functions */
static const tcflag_t l_set = 0;
SERHANDLE port;
int port;
SERHANDLE serport;
struct termios tio;
port = open (name, O_RDWR | O_NOCTTY | O_NONBLOCK); /* open the port */
@ -1092,7 +1162,9 @@ if (tcsetattr (port, TCSANOW, &tio)) { /* set the terminal attr
return INVALID_HANDLE; /* and return failure to caller */
}
return port; /* return port fd for success */
serport = (SERHANDLE)calloc (1, sizeof(*serport));
serport->port = port;
return serport; /* return port fd for success */
}
@ -1131,7 +1203,7 @@ static const int32 baud_count = sizeof (baud_map) / sizeof (baud_map [0]);
static const tcflag_t charsize_map [4] = { CS5, CS6, CS7, CS8 };
if (tcgetattr (port, &tio)) { /* get the current configuration */
if (tcgetattr (port->port, &tio)) { /* get the current configuration */
sim_error_serial ("tcgetattr", errno); /* function failed; report unexpected error */
return SCPE_IOERR; /* return failure status */
}
@ -1176,7 +1248,7 @@ else if (config.stopbits == 2) /* two stop bits? */
else /* some other number? */
return SCPE_ARG; /* not a valid number of stop bits */
if (tcsetattr (port, TCSAFLUSH, &tio)) { /* set the new configuration */
if (tcsetattr (port->port, TCSAFLUSH, &tio)) { /* set the new configuration */
sim_error_serial ("tcsetattr", errno); /* function failed; report unexpected error */
return SCPE_IERR; /* return failure status */
}
@ -1208,7 +1280,7 @@ if ((bits_to_set & ~(TMXR_MDM_OUTGOING)) || /* Assure only settable bits
if (bits_to_set) {
bits = ((bits_to_set&TMXR_MDM_DTR) ? TIOCM_DTR : 0) |
((bits_to_set&TMXR_MDM_RTS) ? TIOCM_RTS : 0);
if (ioctl (port, TIOCMBIS, &bits)) { /* set the desired bits */
if (ioctl (port->port, TIOCMBIS, &bits)) { /* set the desired bits */
sim_error_serial ("ioctl", errno); /* report unexpected error */
return SCPE_IOERR; /* return failure status */
}
@ -1216,13 +1288,13 @@ if (bits_to_set) {
if (bits_to_clear) {
bits = ((bits_to_clear&TMXR_MDM_DTR) ? TIOCM_DTR : 0) |
((bits_to_clear&TMXR_MDM_RTS) ? TIOCM_RTS : 0);
if (ioctl (port, TIOCMBIC, &bits)) { /* clear the desired bits */
if (ioctl (port->port, TIOCMBIC, &bits)) { /* clear the desired bits */
sim_error_serial ("ioctl", errno); /* report unexpected error */
return SCPE_IOERR; /* return failure status */
}
}
if (incoming_bits) {
if (ioctl (port, TIOCMGET, &bits)) { /* get the modem bits */
if (ioctl (port->port, TIOCMGET, &bits)) { /* get the modem bits */
sim_error_serial ("ioctl", errno); /* report unexpected error */
return SCPE_IOERR; /* return failure status */
}
@ -1264,7 +1336,7 @@ int read_count;
char *bptr, *cptr;
int32 remaining;
read_count = read (port, (void *) buffer, (size_t) count); /* read from the serial port */
read_count = read (port->port, (void *) buffer, (size_t) count);/* read from the serial port */
if (read_count == -1) /* read error? */
if (errno == EAGAIN) /* no characters available? */
@ -1314,7 +1386,7 @@ int32 sim_write_serial (SERHANDLE port, char *buffer, int32 count)
{
int written;
written = write (port, (void *) buffer, (size_t) count); /* write the buffer to the serial port */
written = write (port->port, (void *) buffer, (size_t) count);/* write the buffer to the serial port */
if (written == -1) {
if (errno == EWOULDBLOCK)
@ -1338,8 +1410,8 @@ return (int32) written; /* return number of
static void sim_close_os_serial (SERHANDLE port)
{
close (port); /* close the port */
return;
close (port->port); /* close the port */
free (port);
}
@ -1385,6 +1457,11 @@ typedef struct {
void *return_length_address;
} ITEM;
struct SERPORT {
uint32 port;
IOSB write_iosb;
};
/* Enumerate the available serial ports.
The serial port names generated by attempting to open /dev/ttyS0 thru
@ -1492,6 +1569,7 @@ ITEM items[] = { {sizeof (devclass), DVI$_DEVCLASS, &devclass, NULL},
{ 0, 0, NULL, NULL}};
SENSE_BUF start_mode = { 0 };
SENSE_BUF run_mode = { 0 };
SERHANDLE port;
devnam.dsc$w_length = strlen (devnam.dsc$a_pointer);
status = sys$assign (&devnam, &chan, 0, 0);
@ -1519,7 +1597,10 @@ if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) {
sys$dassgn (chan);
return INVALID_HANDLE;
}
return chan; /* return channel for success */
port = (SERHANDLE)calloc (1, sizeof(*port));
port->port = chan;
port->write_iosb.status = 1;
return port; /* return channel for success */
}
@ -1556,7 +1637,7 @@ static const struct {
static const int32 baud_count = sizeof (baud_map) / sizeof (baud_map [0]);
status = sys$qiow (0, port, IO$_SENSEMODE, &iosb, 0, 0, &sense, sizeof(sense), 0, NULL, 0, 0);
status = sys$qiow (0, port->port, IO$_SENSEMODE, &iosb, 0, 0, &sense, sizeof(sense), 0, NULL, 0, 0);
if (status == SS$_NORMAL)
status = iosb.status;
if (status != SS$_NORMAL) {
@ -1610,7 +1691,7 @@ switch (config.stopbits) {
return SCPE_ARG; /* not a valid number of stop bits */
}
status = sys$qiow (0, port, IO$_SETMODE, &iosb, 0, 0,
status = sys$qiow (0, port->port, IO$_SETMODE, &iosb, 0, 0,
&sense, sizeof (sense), speed, 0, parity | charsize | stopbits, 0);
if (status == SS$_NORMAL)
status = iosb.status;
@ -1651,7 +1732,7 @@ if (bits_to_clear)
bits[0] |= (((bits_to_clear&TMXR_MDM_DTR) ? TT$M_DS_DTR : 0) |
((bits_to_clear&TMXR_MDM_RTS) ? TT$M_DS_RTS : 0)) << 24;
if (bits_to_set || bits_to_clear) {
status = sys$qiow (0, port, IO$_SETMODE|IO$M_SET_MODEM|IO$M_MAINT, &iosb, 0, 0,
status = sys$qiow (0, port->port, IO$_SETMODE|IO$M_SET_MODEM|IO$M_MAINT, &iosb, 0, 0,
bits, 0, 0, 0, 0, 0);
if (status == SS$_NORMAL)
status = iosb.status;
@ -1663,7 +1744,7 @@ if (bits_to_set || bits_to_clear) {
if (incoming_bits) {
uint32 modem;
status = sys$qiow (0, port, IO$_SENSEMODE|IO$M_RD_MODEM, &iosb, 0, 0,
status = sys$qiow (0, port->port, IO$_SENSEMODE|IO$M_RD_MODEM, &iosb, 0, 0,
bits, 0, 0, 0, 0, 0);
if (status == SS$_NORMAL)
status = iosb.status;
@ -1713,7 +1794,7 @@ unsigned char buf[4];
IOSB iosb;
SENSE_BUF sense;
status = sys$qiow (0, port, IO$_SENSEMODE | IO$M_TYPEAHDCNT, &iosb,
status = sys$qiow (0, port->port, IO$_SENSEMODE | IO$M_TYPEAHDCNT, &iosb,
0, 0, &sense, 8, 0, term, 0, 0);
if (status == SS$_NORMAL)
status = iosb.status;
@ -1723,7 +1804,7 @@ if (status != SS$_NORMAL) {
}
if (sense.sense_count == 0) /* no characters available? */
return 0; /* return 0 to indicate */
status = sys$qiow (0, port, IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TIMED | IO$M_TRMNOECHO,
status = sys$qiow (0, port->port, IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TIMED | IO$M_TRMNOECHO,
&iosb, 0, 0, buffer, (count < sense.sense_count) ? count : sense.sense_count, 0, term, 0, 0);
if (status == SS$_NORMAL)
status = iosb.status;
@ -1745,30 +1826,16 @@ return (int32)iosb.count; /* return the number
int32 sim_write_serial (SERHANDLE port, char *buffer, int32 count)
{
uint32 status;
static uint32 term[2] = {0, 0};
unsigned char buf[4];
IOSB iosb;
uint32 devsts = 0;
#define UCB$M_BSY 0x100 /* Device I/O busy flag */
ITEM items[] = { {sizeof (devsts), DVI$_STS, &devsts, NULL},
{ 0, 0, NULL, NULL}};
status = sys$getdviw (0, port, NULL, items, &iosb, NULL, 0, 0);
if (status == SS$_NORMAL)
status = iosb.status;
if (status != SS$_NORMAL) {
sim_error_serial ("write-GETDVI", status); /* report unexpected error */
return -1;
}
if (devsts & UCB$M_BSY)
return 0; /* Would block */
status = sys$qiow (0, port, IO$_WRITELBLK | IO$M_NOFORMAT,
NULL, 0, 0, buffer, count, 0, 0, 0, 0);
if (port->write_iosb.status == 0) /* Prior write not done yet? */
return 0;
status = sys$qio (0, port->port, IO$_WRITELBLK | IO$M_NOFORMAT,
&port->write_iosb, 0, 0, buffer, count, 0, 0, 0, 0);
if (status != SS$_NORMAL) {
sim_error_serial ("write", status); /* report unexpected error */
return -1;
}
return (int32)iosb.count; /* return number of characters written */
return (int32)count; /* return number of characters written */
}
@ -1779,11 +1846,10 @@ return (int32)iosb.count; /* return number of char
static void sim_close_os_serial (SERHANDLE port)
{
sys$dassgn (port); /* close the port */
return;
sys$dassgn (port->port); /* close the port */
free (port);
}
#else
/* Non-implemented stubs */
@ -1839,7 +1905,6 @@ return -1;
static void sim_close_os_serial (SERHANDLE port)
{
return;
}

View file

@ -34,6 +34,11 @@
extern "C" {
#endif
#ifndef SIMH_SERHANDLE_DEFINED
#define SIMH_SERHANDLE_DEFINED 0
typedef struct SERPORT *SERHANDLE;
#endif /* SERHANDLE_DEFINED */
#if defined (_WIN32) /* Windows definitions */
/* We need the basic Win32 definitions, but including "windows.h" also includes
@ -47,7 +52,7 @@ extern "C" {
#endif
#include <windows.h>
#if !defined(INVALID_HANDLE)
#define INVALID_HANDLE INVALID_HANDLE_VALUE
#define INVALID_HANDLE (SERHANDLE)INVALID_HANDLE_VALUE
#endif /* !defined(INVALID_HANDLE) */
#elif defined (__unix__) || defined (__APPLE__) || defined (__hpux) /* UNIX definitions */
@ -61,31 +66,22 @@ extern "C" {
#include <sys/ioctl.h>
#if !defined(INVALID_HANDLE)
#define INVALID_HANDLE -1
#define INVALID_HANDLE ((SERHANDLE)(void *)-1)
#endif /* !defined(INVALID_HANDLE) */
#elif defined (VMS) /* VMS definitions */
#if !defined(INVALID_HANDLE)
#define INVALID_HANDLE (uint32)(-1)
#define INVALID_HANDLE ((SERHANDLE)(void *)-1)
#endif /* !defined(INVALID_HANDLE) */
#else /* Non-implemented definitions */
#if !defined(INVALID_HANDLE)
#define INVALID_HANDLE -1
#define INVALID_HANDLE ((SERHANDLE)(void *)-1)
#endif /* !defined(INVALID_HANDLE) */
#endif /* OS variants */
#ifndef SIMH_SERHANDLE_DEFINED
#define SIMH_SERHANDLE_DEFINED 0
#if defined (_WIN32) /* Windows definitions */
typedef void *SERHANDLE;
#else /* all other platforms */
typedef int SERHANDLE;
#endif
#endif /* SERHANDLE_DEFINED */
/* Common definitions */

View file

@ -53,11 +53,7 @@ extern "C" {
#ifndef SIMH_SERHANDLE_DEFINED
#define SIMH_SERHANDLE_DEFINED 0
#if defined (_WIN32) /* Windows definitions */
typedef void *SERHANDLE;
#else /* all other platforms */
typedef int SERHANDLE;
#endif
typedef struct SERPORT *SERHANDLE;
#endif
#include "sim_sock.h"