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
|
||||
|
||||
/* Non-implemented stubs */
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Add table
Reference in a new issue