sim_serial.c Added initial serial support for VMS hosts
This commit is contained in:
commit
3311f4d581
2 changed files with 412 additions and 0 deletions
404
sim_serial.c
404
sim_serial.c
|
@ -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
|
#else
|
||||||
|
|
||||||
/* Non-implemented stubs */
|
/* Non-implemented stubs */
|
||||||
|
|
|
@ -65,6 +65,14 @@ typedef int SERHANDLE;
|
||||||
#define INVALID_HANDLE -1
|
#define INVALID_HANDLE -1
|
||||||
|
|
||||||
|
|
||||||
|
#elif defined (VMS)
|
||||||
|
|
||||||
|
/* VMS definitions */
|
||||||
|
|
||||||
|
typedef int SERHANDLE;
|
||||||
|
|
||||||
|
#define INVALID_HANDLE (uint32)(-1)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
/* Non-implemented definitions */
|
/* Non-implemented definitions */
|
||||||
|
|
Loading…
Add table
Reference in a new issue