diff --git a/sim_serial.c b/sim_serial.c index 146808f7..bb3e91e6 100644 --- a/sim_serial.c +++ b/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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 */ diff --git a/sim_serial.h b/sim_serial.h index 7eee1add..f632e973 100644 --- a/sim_serial.h +++ b/sim_serial.h @@ -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 */