sim_serial.c Added initial serial support for VMS hosts

This commit is contained in:
Mark Pizzolato 2012-10-22 09:07:45 -07:00
commit 3311f4d581
2 changed files with 412 additions and 0 deletions

View file

@ -1268,6 +1268,410 @@ return;
}
#elif defined (__VMS__)
/* VMS implementation */
#if defined(__VAX)
#define sys$assign SYS$ASSIGN
#define sys$qiow SYS$QIOW
#define sys$dassgn SYS$DASSGN
#define sys$device_scan SYS$DEVICE_SCAN
#define sys$getdviw SYS$GETDVIW
#endif
#include <descrip.h>
#include <ttdef.h>
#include <tt2def.h>
#include <iodef.h>
#include <ssdef.h>
#include <dcdef.h>
#include <dvsdef.h>
#include <dvidef.h>
#include <starlet.h>
#include <unistd.h>
typedef struct {
unsigned short sense_count;
unsigned char sense_first_char;
unsigned char sense_reserved;
unsigned int stat;
unsigned int stat2; } SENSE_BUF;
typedef struct {
unsigned short status;
unsigned short count;
unsigned int dev_status; } IOSB;
/* Enumerate the available serial ports.
The serial port names generated by attempting to open /dev/ttyS0 thru
/dev/ttyS53 and /dev/ttyUSB0 thru /dev/ttyUSB0. Ones we can open and
are ttys (as determined by isatty()) are added to the list. The list
is sorted alphabetically by device name.
*/
static int sim_serial_os_devices (int max, SERIAL_LIST* list)
{
$DESCRIPTOR (wild, "*");
char devstr[sizeof(list[0].name)];
$DESCRIPTOR (device, devstr);
int ports;
uint32 status;
t_bool done = FALSE;
struct _generic_64 context;
uint32 devclass = DC$_TERM; /* Only interested in terminal devices */
ILE3 select_items[] = { {sizeof (devclass), DVS$_DEVCLASS, &devclass, NULL},
{ 0, 0, NULL, NULL}};
memset(&context, 0, sizeof(context));
memset(devstr, 0, sizeof(devstr));
memset(list, 0, max*sizeof(*list));
for (ports=0; (ports < max) && !done; ++ports) {
device.dsc$w_length = sizeof (devstr) - 1;
status = sys$device_scan (&device,
&device.dsc$w_length,
&wild,
select_items,
&context);
switch (status) {
case SS$_NOSUCHDEV:
case SS$_NOMOREDEV:
done = TRUE;
break;
default:
if (0 == (status&1))
done = TRUE;
else {
devstr[device.dsc$w_length] = '\0';
strcpy (list[ports].name, devstr);
}
break;
}
}
return ports;
}
/* Open a serial port.
The serial port designated by "name" is opened, and the handle to the port is
returned. If an error occurs, INVALID_HANDLE is returned instead. After
opening, the port is configured to "raw" mode.
Implementation notes:
1. We use a non-blocking open to allow for polling during reads.
2. There is no way to limit "open" just to serial ports, so we must check
after the port is opened. We do this with sys$getdvi.
*/
SERHANDLE sim_open_os_serial (char *name)
{
uint32 status;
uint32 chan = 0;
IOSB iosb;
$DESCRIPTOR (devnam, name);
uint32 devclass;
ILE3 items[] = { {sizeof (devclass), DVI$_DEVCLASS, &devclass, NULL},
{ 0, 0, NULL, NULL}};
SENSE_BUF start_mode = { 0 };
SENSE_BUF run_mode = { 0 };
devnam.dsc$w_length = strlen (devnam.dsc$a_pointer);
status = sys$assign (&devnam, &chan, 0, 0);
if (status != SS$_NORMAL)
return INVALID_HANDLE;
status = sys$getdviw (0, chan, NULL, items, &iosb, NULL, 0, NULL);
if ((status != SS$_NORMAL) ||
(iosb.status != SS$_NORMAL) ||
(devclass != DC$_TERM)) {
sys$dassgn (chan);
return INVALID_HANDLE;
}
status = sys$qiow (0, chan, IO$_SENSEMODE, &iosb, 0, 0,
&start_mode, sizeof (start_mode), 0, 0, 0, 0);
if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) {
sys$dassgn (chan);
return INVALID_HANDLE;
}
run_mode = cmd_mode;
run_mode.stat = cmd_mode.stat | TT$M_NOECHO & ~(TT$M_HOSTSYNC | TT$M_TTSYNC | TT$M_HALFDUP);
run_mode.stat2 = cmd_mode.stat2 | TT2$M_PASTHRU;
status = sys$qiow (0, chan, IO$_SETMODE, &iosb, 0, 0,
&run_mode, sizeof (run_mode), 0, 0, 0, 0);
if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL))
sys$dassgn (chan);
return INVALID_HANDLE;
}
return chan; /* return channel for success */
}
/* Configure a serial port.
Port parameters are configured as specified in the "config" structure. If
"config" contains an invalid configuration value, or if the host system
rejects the configuration (e.g., by requesting an unsupported combination of
character size and stop bits), SCPE_ARG is returned to the caller. If an
unexpected error occurs, SCPE_IOERR is returned. If the configuration
succeeds, SCPE_OK is returned.
Implementation notes:
1. 1.5 stop bits is not a supported configuration.
*/
t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config)
{
int32 i;
SENSE_BUF sense;
uint32 status, speed, parity, charsize, stopbits;
IOSB iosb;
static const struct {
uint32 rate;
speed_t rate_code;
} baud_map [] =
{ { 50, TT$C_BAUD_50 }, { 75, TT$C_BAUD_75 }, { 110, TT$C_BAUD_110 }, { 134, TT$C_BAUD_134 },
{ 150, TT$C_BAUD_150 }, { 300, TT$C_BAUD_300 }, { 600, TT$C_BAUD_600 }, { 1200, TT$C_BAUD_1200 },
{ 1800, TT$C_BAUD_1800 }, { 2000, TT$C_BAUD_2000 }, { 2400, TT$C_BAUD_2400 }, { 3600, TT$C_BAUD_3600 },
{ 4800, TT$C_BAUD_4800 }, { 7200, TT$C_BAUD_7200 }, { 9600, TT$C_BAUD_9600 }, { 19200, TT$C_BAUD_19200 },
{ 38400, TT$C_BAUD_38400 }, { 57600, TT$C_BAUD_57600 }, { 76800, TT$C_BAUD_76800 }, { 115200, TT$C_BAUD_115200} };
static const int32 baud_count = sizeof (baud_map) / sizeof (baud_map [0]);
status = sys$qiow (0, tty_chan, IO$_SENSEMODE, &iosb, 0, 0, &sense, sizeof(sense), 0, NULL, 0, 0);
if (status == SS$_NORMAL)
status = iosb.status;
if (status != SS$_NORMAL) {
sim_error_serial ("config-SENSEMODE", status); /* report unexpected error */
return SCPE_IOERR;
}
for (i = 0; i < baud_count; i++) /* assign baud rate */
if (config.baudrate == baud_map [i].rate) { /* match mapping value? */
speed = baud_map [i].rate_code << 8 | /* set input rate */
baud_map [i].rate_code; /* set output rate */
break;
}
if (i == baud_count) /* baud rate assigned? */
return SCPE_ARG; /* invalid rate specified */
if (config.charsize >= 5 && config.charsize <= 8) /* character size OK? */
charsize = TT$M_ALTFRAME | config.charsize; /* set character size */
else
return SCPE_ARG; /* not a valid size */
switch (config.parity) { /* assign parity */
case 'E':
parity = TT$M_ALTRPAR | TT$M_PARITY; /* set for even parity */
break;
case 'N':
parity = TT$M_ALTRPAR; /* set for no parity */
break;
case 'O':
parity = TT$M_ALTRPAR | TT$M_PARITY | TT$M_ODD; /* set for odd parity */
break;
default:
return SCPE_ARG; /* not a valid parity specifier */
}
case (config.stopbits) {
case 1: /* one stop bit? */
stopbits = 0;
break;
case 2: /* two stop bits? */
if ((speed & 0xff) <= TT$C_BAUD_150) { /* Only valid for */
stopbits = TT$M_TWOSTOP; /* speeds 150baud or less */
break;
}
default:
return SCPE_ARG; /* not a valid number of stop bits */
}
status = sys$qiow (0, port, IO$_SETMODE, &iosb, 0, 0,
&sense, sizeof (sense), speed, 0, parity | charsize | stopbits, 0);
if (status == SS$_NORMAL)
status = iosb.status;
if (status != SS$_NORMAL) {
sim_error_serial ("config-SETMODE", status); /* report unexpected error */
return SCPE_IOERR;
}
return SCPE_OK; /* configuration set successfully */
}
/* Control a serial port.
The DTR and RTS line of the serial port is set or cleared as indicated in
the respective bits_to_set or bits_to_clear parameters. If the
incoming_bits parameter is not NULL, then the modem status bits DCD, RNG,
DSR and CTS are returned.
If unreasonable or nonsense bits_to_set or bits_to_clear bits are
specified, then the return status is SCPE_ARG;
If an error occurs, SCPE_IOERR is returned.
*/
t_stat sim_control_serial (SERHANDLE port, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits)
{
uint32 status;
IOSB iosb;
uint32 bits[2] = {0, 0};
if ((bits_to_set & ~(TMXR_MDM_OUTGOING)) || /* Assure only settable bits */
(bits_to_clear & ~(TMXR_MDM_OUTGOING)) ||
(bits_to_set & bits_to_clear)) /* and can't set and clear the same bits */
return SCPE_ARG;
if (bits_to_set)
bits[0] |= (((bits_to_set&TMXR_MDM_DTR) ? TT$M_DS_DTR : 0) |
((bits_to_set&TMXR_MDM_RTS) ? TT$M_DS_RTS : 0)) << 16;
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,
bits, 0, 0, 0, 0, 0);
if (status == SS$_NORMAL)
status = iosb.status;
if (status != SS$_NORMAL) {
sim_error_serial ("control-SETMODE", status); /* report unexpected error */
return SCPE_IOERR;
}
}
if (incoming_bits) {
uint32 modem;
status = sys$qiow (0, port, IO$_SENSEMODE|IO$M_RD_MODEM, &iosb, 0, 0,
bits, 0, 0, 0, 0, 0);
if (status == SS$_NORMAL)
status = iosb.status;
if (status != SS$_NORMAL) {
sim_error_serial ("control-SENSEMODE", status); /* report unexpected error */
return SCPE_IOERR;
}
modem = bits[0] >> 16;
*incoming_bits = ((modem&TT$M_DS_CTS) ? TMXR_MDM_CTS : 0) |
((modem&TT$M_DS_DSR) ? TMXR_MDM_DSR : 0) |
((modem&TT$M_DS_RING) ? TMXR_MDM_RNG : 0) |
((modem&TT$M_DS_CARRIER) ? TMXR_MDM_DCD : 0);
}
return SCPE_OK;
}
/* Read from a serial port.
The port is checked for available characters. If any are present, they are
copied to the passed buffer, and the count of characters is returned. If no
characters are available, 0 is returned. If an error occurs, -1 is returned.
If a BREAK is detected on the communications line, the corresponding flag in
the "brk" array is set.
Implementation notes:
1. A character with a framing or parity error is indicated in the input
stream by the three-character sequence \377 \000 \ccc, where "ccc" is the
bad character. A communications line BREAK is indicated by the sequence
\377 \000 \000. A received \377 character is indicated by the
two-character sequence \377 \377. If we find any of these sequences,
they are replaced by the single intended character by sliding the
succeeding characters backward by one or two positions. If a BREAK
sequence was encountered, the corresponding location in the "brk" array
is determined, and the flag is set. Note that there may be multiple
sequences in the buffer.
*/
int32 sim_read_serial (SERHANDLE port, char *buffer, int32 count, char *brk)
{
int read_count = 0;
uint32 status;
static uint32 term[2] = {0, 0};
unsigned char buf[4];
IOSB iosb;
SENSE_BUF sense;
status = sys$qiow (0, port, IO$_SENSEMODE | IO$M_TYPEAHDCNT, &iosb,
0, 0, &sense, 8, 0, term, 0, 0);
if (status == SS$_NORMAL)
status = iosb.status;
if (status != SS$_NORMAL) {
sim_error_serial ("read", status); /* report unexpected error */
return -1;
}
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,
&iosb, 0, 0, buffer, (count < sense.sense_count) ? count : sense.sense_count, 0, term, 0, 0);
if (status == SS$_NORMAL)
status = iosb.status;
if (status != SS$_NORMAL) {
sim_error_serial ("read", status); /* report unexpected error */
return -1;
}
return (int32)iosb.count; /* return the number of characters read */
}
/* Write to a serial port.
"Count" characters are written from "buffer" to the serial port. The actual
number of characters written to the port is returned. If an error occurred
on writing, -1 is returned.
*/
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 */
ILE3 items[] = { {sizeof (devclass), 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$qio (0, port, IO$_WRITELBLK | IO$M_NOFORMAT,
NULL, 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 */
}
/* Close a serial port.
The serial port is closed. Errors are ignored.
*/
void sim_close_os_serial (SERHANDLE port)
{
sys$dassgn (port); /* close the port */
return;
}
#else
/* Non-implemented stubs */

View file

@ -65,6 +65,14 @@ typedef int SERHANDLE;
#define INVALID_HANDLE -1
#elif defined (VMS)
/* VMS definitions */
typedef int SERHANDLE;
#define INVALID_HANDLE (uint32)(-1)
#else
/* Non-implemented definitions */