Commit | Line | Data |
---|---|---|
196ba1fc PH |
1 | /* ibm1130_sca.c: IBM 1130 synchronous communications adapter emulation\r |
2 | \r | |
3 | Based on the SIMH simulator package written by Robert M Supnik\r | |
4 | \r | |
5 | Brian Knittel\r | |
6 | Revision History\r | |
7 | \r | |
8 | 2005.03.08 - Started\r | |
9 | \r | |
10 | * (C) Copyright 2005, Brian Knittel.\r | |
11 | * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN\r | |
12 | * RISK basis, there is no warranty of fitness for any purpose, and the rest of the\r | |
13 | * usual yada-yada. Please keep this notice and the copyright in any distributions\r | |
14 | * or modifications.\r | |
15 | *\r | |
16 | * This is not a supported product, but I welcome bug reports and fixes.\r | |
17 | * Mail to simh@ibm1130.org\r | |
18 | */\r | |
19 | \r | |
20 | /******************************************************************************************************************\r | |
21 | * NOTES:\r | |
22 | * This module sends raw bisync data over a standard TCP port. It's meant to be\r | |
23 | * used with the emulated 2703 device in Hercules.\r | |
24 | *\r | |
25 | * Attach command:\r | |
26 | *\r | |
27 | * to establish an outgoing connection:\r | |
28 | *\r | |
29 | * attach sca host connect to named host on default port (initial default port is 2703); or\r | |
30 | * attach sca host:### connect to named host on port ###. ### is also set as new default port number.\r | |
31 | * >> The simulator waits until the connection is established\r | |
32 | *\r | |
33 | * or to set up for incoming connections:\r | |
34 | *\r | |
35 | * attach sca -l dummy listen for a connection on default port (initially 2703); Nonnumeric "dummy" argument is ignored; or\r | |
36 | * attach sca -l ### listen for a connection on the port ###. ### is also set as the new default port\r | |
37 | * >> The simulator proceeds. When another simulator connects, the READY bit is set in the DSW.\r | |
38 | *\r | |
39 | * If the SCA's autoanswer-enable bit has been set, an incoming connection causes an interrupt (untested)\r | |
40 | * Configuration commands:\r | |
41 | *\r | |
42 | * set sca bsc set bisync mode (default)\r | |
43 | * set sca str set synchronous transmit/recieve mode (NOT IMPLEMENTED)\r | |
44 | *\r | |
45 | * set sca ### set simulated baud rate to ###, where ### is 600, 1200, 2000, 2400 or 4800 (4800 is default)\r | |
46 | *\r | |
47 | * set sca half set simulated half-duplex mode\r | |
48 | * set sca full set simulated full-duplex mode (note: 1130's SCA can't actually send and receive at the same time!)\r | |
49 | *\r | |
50 | * deposit sca keepalive ### send SYN packets every ### msec when suppressing SYN's, default is 0 (no keepalives)\r | |
51 | *\r | |
52 | * STR/BSC mode is selected by a toggle switch on the 1130, with the SET SCA BSC or SET SET STR command here.\r | |
53 | * Testable with in_bsc_mode() or in_str_mode() in this module. If necessary, the SET command can be configured\r | |
54 | * to call a routine when the mode is changed; or, we can just required the user to reboot the simulated 1130\r | |
55 | * when switching modes.\r | |
56 | *\r | |
57 | * STR MODE IS NOT IMPLEMENTED!\r | |
58 | *\r | |
59 | * The SCA adapter appears to know nothing of the protocols used by STR and BSC. It does handle the sync/idle\r | |
60 | * character specially, and between BSC and STR mode the timers are used differently. Also in STR mode it\r | |
61 | * can be set to a sychronization mode where it sends SYN's without program intervention.\r | |
62 | *\r | |
63 | * See definition of SCA_STATE for defintion of simulator states.\r | |
64 | *\r | |
65 | * Rather than trying to simulate the actual baud rates, we try to keep the character service interrupts\r | |
66 | * coming at about the same number of instruction intervals -- thus existing 1130 code should work correctly\r | |
67 | * but the effective data transfer rate will be much higher. The "timers" however are written to run on real wall clock\r | |
68 | * time, This may or may not work. If necessary they could be set to time out based on the number of calls to sca_svc\r | |
69 | * which occurs once per character send/receive time; For example, at 4800 baud and an 8 bit frame, we get\r | |
70 | * 600 characters/second, so the 3 second timer would expire in 1800 sca_svc calls. Well, that's something to\r | |
71 | * think about.\r | |
72 | *\r | |
73 | * To void blowing zillions of SYN characters across the network when the system is running but idle, we suppress\r | |
74 | * them. If 100 consecutive SYN's are sent, we flush the output buffer and stop sending SYN's\r | |
75 | * until some other character is sent, OR the line is turned around (e.g. INITR, INITW or an end-operation\r | |
76 | * CONTROL is issued), or the number of msec set by DEPOSIT SCS KEEPALIVE has passed, if a value has\r | |
77 | * been set. By default no keepalives are sent.\r | |
78 | *\r | |
79 | * Timer operations are not debugged. First, do timers automatically reset and re-interrupt if\r | |
80 | * left alone after they timeout the first time? Does XIO_SENSE_DEV really restart all running timers?\r | |
81 | * Does it touch the timer trigger (program timer?) How do 3 and 1.25 second timers really work\r | |
82 | * in BSC mode? Hard to tell from the FC manual.\r | |
83 | ******************************************************************************************************************/\r | |
84 | \r | |
85 | #include "ibm1130_defs.h"\r | |
86 | #include "sim_sock.h" /* include path must include main simh directory */\r | |
87 | #include <ctype.h>\r | |
88 | #ifndef INADDR_NONE\r | |
89 | #define INADDR_NONE ((unsigned long)-1)\r | |
90 | #endif\r | |
91 | \r | |
92 | #define DEBUG_SCA_FLUSH 0x0001 /* debugging options */\r | |
93 | #define DEBUG_SCA_TRANSMIT 0x0002\r | |
94 | #define DEBUG_SCA_CHECK_INDATA 0x0004\r | |
95 | #define DEBUG_SCA_RECEIVE_SYNC 0x0008\r | |
96 | #define DEBUG_SCA_RECEIVE_DATA 0x0010\r | |
97 | #define DEBUG_SCA_XIO_READ 0x0020\r | |
98 | #define DEBUG_SCA_XIO_WRITE 0x0040\r | |
99 | #define DEBUG_SCA_XIO_CONTROL 0x0080\r | |
100 | #define DEBUG_SCA_XIO_INITW 0x0100\r | |
101 | #define DEBUG_SCA_XIO_INITR 0x0200\r | |
102 | #define DEBUG_SCA_XIO_SENSE_DEV 0x0400\r | |
103 | #define DEBUG_SCA_TIMERS 0x0800\r | |
104 | #define DEBUG_SCA_ALL 0xFFFF\r | |
105 | \r | |
106 | /* #define DEBUG_SCA (DEBUG_SCA_TIMERS|DEBUG_SCA_FLUSH|DEBUG_SCA_TRANSMIT|DEBUG_SCA_CHECK_INDATA|DEBUG_SCA_RECEIVE_SYNC|DEBUG_SCA_RECEIVE_DATA|DEBUG_SCA_XIO_INITR|DEBUG_SCA_XIO_INITW) */\r | |
107 | #define DEBUG_SCA (DEBUG_SCA_TIMERS|DEBUG_SCA_FLUSH|DEBUG_SCA_CHECK_INDATA|DEBUG_SCA_XIO_INITR|DEBUG_SCA_XIO_INITW)\r | |
108 | \r | |
109 | #define SCA_DEFAULT_PORT 2703 /* default socket, This is the number of the IBM 360's BSC device */\r | |
110 | \r | |
111 | #define MAX_SYNS 100 /* number of consecutive syn's after which we stop buffering them */\r | |
112 | \r | |
113 | /***************************************************************************************\r | |
114 | * SCA\r | |
115 | ***************************************************************************************/\r | |
116 | \r | |
117 | #define SCA_DSW_READ_RESPONSE 0x8000 /* receive buffer full interrupt */\r | |
118 | #define SCA_DSW_WRITE_RESPONSE 0x4000 /* transmitter buffer empty interrupt */\r | |
119 | #define SCA_DSW_CHECK 0x2000 /* data overrun or character gap error */\r | |
120 | #define SCA_DSW_TIMEOUT 0x1000 /* timer interrupt, mode specific */\r | |
121 | #define SCA_DSW_AUTOANSWER_REQUEST 0x0800 /* dataset is ringing and autoanswer is enabled */\r | |
122 | #define SCA_DSW_BUSY 0x0400 /* adapter is in either receive or transmit mode */\r | |
123 | #define SCA_DSW_AUTOANSWER_ENABLED 0x0200 /* 1 when autoanswer mode has been enabled */\r | |
124 | #define SCA_DSW_READY 0x0100 /* Carrier detect? Connected and ready to rcv, xmit or sync */\r | |
125 | #define SCA_DSW_RECEIVE_RUN 0x0080 /* used in two-wire half-duplex STR mode only. "Slave" mode (?) */\r | |
126 | \r | |
127 | #define IS_ONLINE(u) (((u)->flags & (UNIT_ATT|UNIT_DIS)) == UNIT_ATT)\r | |
128 | \r | |
129 | typedef enum { /* ms m = mode (0 = idle, 1 = send, 2 = receive), s = substate */\r | |
130 | SCA_STATE_IDLE = 0x00, /* nothing happening */\r | |
131 | SCA_STATE_TURN_SEND = 0x10, /* line turning around to the send state */\r | |
132 | SCA_STATE_SEND_SYNC = 0x11, /* sca is sending syncs */\r | |
133 | SCA_STATE_SEND1 = 0x12, /* have issued write response, waiting for write command */\r | |
134 | SCA_STATE_SEND2 = 0x13, /* write command issued, "sending" byte */\r | |
135 | SCA_STATE_TURN_RECEIVE = 0x20, /* line turnaround to the receive state */\r | |
136 | SCA_STATE_RECEIVE_SYNC = 0x21, /* sca is receiving syncs */\r | |
137 | SCA_STATE_RECEIVE_SYNC2 = 0x22, /* bsc mode, waiting for 2nd SYN */\r | |
138 | SCA_STATE_RECEIVE_SYNC3 = 0x23, /* bsc mode, waiting for 1st non-SYN */\r | |
139 | SCA_STATE_RECEIVE1 = 0x24, /* "receiving" a byte */\r | |
140 | SCA_STATE_RECEIVE2 = 0x25, /* read response issued, "receiving" next byte */\r | |
141 | } SCA_STATE;\r | |
142 | \r | |
143 | #define in_send_state() (sca_state & 0x10)\r | |
144 | #define in_receive_state() (sca_state & 0x20)\r | |
145 | \r | |
146 | static t_stat sca_svc (UNIT *uptr); /* prototypes */\r | |
147 | static t_stat sca_reset (DEVICE *dptr);\r | |
148 | static t_stat sca_attach (UNIT *uptr, char *cptr);\r | |
149 | static t_stat sca_detach (UNIT *uptr);\r | |
150 | static void sca_start_timer (int n, int msec_now);\r | |
151 | static void sca_halt_timer (int n);\r | |
152 | static void sca_toggle_timer (int n, int msec_now);\r | |
153 | /* timer states, chosen so any_timer_running can be calculated by oring states of all 3 timers */\r | |
154 | typedef enum {SCA_TIMER_INACTIVE = 0, SCA_TIMER_RUNNING = 1, SCA_TIMER_INHIBITED = 2, SCA_TIMER_TIMEDOUT = 4} SCA_TIMER_STATE;\r | |
155 | \r | |
156 | #define TIMER_3S 0 /* 3 second timer index into sca_timer_xxx arrays */\r | |
157 | #define TIMER_125S 1 /* 1.25 second timer */\r | |
158 | #define TIMER_035S 2 /* 0.35 second timer */\r | |
159 | \r | |
160 | static uint16 sca_dsw = 0; /* device status word */\r | |
161 | static uint32 sca_cwait = 275; /* inter-character wait */\r | |
162 | static uint32 sca_iwait = 2750; /* idle wait */\r | |
163 | static uint32 sca_state = SCA_STATE_IDLE;\r | |
164 | static uint8 sichar = 0; /* sync/idle character */\r | |
165 | static uint8 rcvd_char = 0; /* most recently received character */\r | |
166 | static uint8 sca_frame = 8;\r | |
167 | static uint16 sca_port = SCA_DEFAULT_PORT; /* listening port number */\r | |
168 | static int32 sca_keepalive = 0; /* keepalive SYN packet period in msec, default = 0 (disabled) */\r | |
169 | static SCA_TIMER_STATE sca_timer_state[3]; /* current timer state */\r | |
170 | static int sca_timer_endtime[3]; /* clocktime when timeout is to occur if state is RUNNING */\r | |
171 | static int sca_timer_timeleft[3]; /* time left in msec if state is INHIBITED */\r | |
172 | static t_bool any_timer_running = FALSE; /* TRUE if at least one timer is running */\r | |
173 | static int sca_timer_msec[3] = {3000, 1250, 350}; /* timebase in msec for the three timers: 3 sec, 1.25 sec, 0.35 sec */\r | |
174 | static t_bool sca_timer_trigger; /* if TRUE, the "timer trigger" is set, the 0.35s timer is running and the 3 sec and 1.25 sec timers are inhibited */\r | |
175 | static int sca_nsyns = 0; /* number of consecutively sent SYN's */\r | |
176 | static int idles_since_last_write = 0; /* used to detect when software has ceased sending data */\r | |
177 | static SOCKET sca_lsock = INVALID_SOCKET;\r | |
178 | static SOCKET sca_sock = INVALID_SOCKET;\r | |
179 | \r | |
180 | #define SCA_SENDBUF_SIZE 145 /* maximum number of bytes to buffer for transmission */\r | |
181 | #define SCA_RCVBUF_SIZE 256 /* max number of bytes to read from socket at a time */\r | |
182 | #define SCA_SEND_THRESHHOLD 140 /* number of bytes to buffer before initiating packet send */\r | |
183 | #define SCA_IDLE_THRESHHOLD 3 /* maximum number of unintentional idles to buffer before initiating send */\r | |
184 | \r | |
185 | static uint8 sca_sendbuf[SCA_SENDBUF_SIZE]; /* bytes pending to write to socket */\r | |
186 | static uint8 sca_rcvbuf[SCA_RCVBUF_SIZE]; /* bytes received from socket, to be given to SCA */\r | |
187 | static int sca_n2send = 0; /* number of bytes queued for transmission */\r | |
188 | static int sca_nrcvd = 0; /* number of received bytes in buffer */\r | |
189 | static int sca_rcvptr = 0; /* index of next byte to take from rcvbuf */\r | |
190 | \r | |
191 | #define UNIT_V_BISYNC (UNIT_V_UF + 0) /* BSC (bisync) mode */\r | |
192 | #define UNIT_V_BAUD (UNIT_V_UF + 1) /* 3 bits for baud rate encoding */\r | |
193 | #define UNIT_V_FULLDUPLEX (UNIT_V_UF + 4)\r | |
194 | #define UNIT_V_AUTOANSWER (UNIT_V_UF + 5)\r | |
195 | #define UNIT_V_LISTEN (UNIT_V_UF + 6) /* listen socket mode */\r | |
196 | \r | |
197 | #define UNIT_BISYNC (1u << UNIT_V_BISYNC)\r | |
198 | #define UNIT_BAUDMASK (7u << UNIT_V_BAUD)\r | |
199 | #define UNIT_BAUD600 (0u << UNIT_V_BAUD)\r | |
200 | #define UNIT_BAUD1200 (1u << UNIT_V_BAUD)\r | |
201 | #define UNIT_BAUD2000 (2u << UNIT_V_BAUD)\r | |
202 | #define UNIT_BAUD2400 (3u << UNIT_V_BAUD)\r | |
203 | #define UNIT_BAUD4800 (4u << UNIT_V_BAUD)\r | |
204 | #define UNIT_FULLDUPLEX (1u << UNIT_V_FULLDUPLEX)\r | |
205 | #define UNIT_AUTOANSWER (1u << UNIT_V_AUTOANSWER)\r | |
206 | #define UNIT_LISTEN (1u << UNIT_V_LISTEN)\r | |
207 | \r | |
208 | extern int sim_switches; /* variable that gets bits set for -x switches on command lines */\r | |
209 | \r | |
210 | t_stat sca_set_baud (UNIT *uptr, int32 value, char *cptr, void *desc);\r | |
211 | \r | |
212 | UNIT sca_unit = { /* default settings */\r | |
213 | UDATA (sca_svc, UNIT_ATTABLE|UNIT_BISYNC|UNIT_BAUD4800|UNIT_FULLDUPLEX, 0),\r | |
214 | };\r | |
215 | \r | |
216 | REG sca_reg[] = { /* DEVICE STATE/SETTABLE PARAMETERS: */\r | |
217 | { HRDATA (SCADSW, sca_dsw, 16) }, /* device status word */\r | |
218 | { DRDATA (SICHAR, sichar, 8), PV_LEFT }, /* sync/idle character */\r | |
219 | { DRDATA (RCVDCHAR, rcvd_char, 8), PV_LEFT }, /* most recently received character */\r | |
220 | { DRDATA (FRAME, sca_frame, 8), PV_LEFT }, /* frame bits (6, 7 or 8)\r | |
221 | { DRDATA (SCASTATE, sca_state, 32), PV_LEFT }, /* current state */\r | |
222 | { DRDATA (CTIME, sca_cwait, 32), PV_LEFT }, /* inter-character wait */\r | |
223 | { DRDATA (ITIME, sca_iwait, 32), PV_LEFT }, /* idle wait (polling interval for socket connects) */\r | |
224 | { DRDATA (SCASOCKET, sca_port, 16), PV_LEFT }, /* listening port number */\r | |
225 | { DRDATA (KEEPALIVE, sca_keepalive, 32), PV_LEFT }, /* keepalive packet period in msec */\r | |
226 | { NULL } };\r | |
227 | \r | |
228 | MTAB sca_mod[] = { /* DEVICE OPTIONS */\r | |
229 | { UNIT_BISYNC, 0, "STR", "STR", NULL }, /* mode option */\r | |
230 | { UNIT_BISYNC, UNIT_BISYNC, "BSC", "BSC", NULL },\r | |
231 | { UNIT_BAUDMASK, UNIT_BAUD600, "600", "600", sca_set_baud }, /* data rate option */\r | |
232 | { UNIT_BAUDMASK, UNIT_BAUD1200, "1200", "1200", sca_set_baud },\r | |
233 | { UNIT_BAUDMASK, UNIT_BAUD2000, "2000", "2000", sca_set_baud },\r | |
234 | { UNIT_BAUDMASK, UNIT_BAUD2400, "2400", "2400", sca_set_baud },\r | |
235 | { UNIT_BAUDMASK, UNIT_BAUD4800, "4800", "4800", sca_set_baud },\r | |
236 | { UNIT_FULLDUPLEX, 0, "HALF", "HALF", NULL }, /* duplex option (does this matter?) */\r | |
237 | { UNIT_FULLDUPLEX, UNIT_FULLDUPLEX, "FULL", "FULL", NULL },\r | |
238 | { 0 } };\r | |
239 | \r | |
240 | DEVICE sca_dev = {\r | |
241 | "SCA", &sca_unit, sca_reg, sca_mod,\r | |
242 | 1, 16, 16, 1, 16, 16,\r | |
243 | NULL, NULL, sca_reset,\r | |
244 | NULL, sca_attach, sca_detach\r | |
245 | };\r | |
246 | \r | |
247 | /*********************************************************************************************\r | |
248 | * sca_set_baud - set baud rate handler (SET SCA.BAUD nnn)\r | |
249 | *********************************************************************************************/\r | |
250 | \r | |
251 | t_stat sca_set_baud (UNIT *uptr, int32 value, char *cptr, void *desc)\r | |
252 | {\r | |
253 | uint32 newbits;\r | |
254 | \r | |
255 | switch (value) {\r | |
256 | case 600: newbits = UNIT_BAUD600; break;\r | |
257 | case 1200: newbits = UNIT_BAUD1200; break;\r | |
258 | case 2000: newbits = UNIT_BAUD2000; break;\r | |
259 | case 2400: newbits = UNIT_BAUD2400; break;\r | |
260 | case 4800: newbits = UNIT_BAUD4800; break;\r | |
261 | default: return SCPE_ARG;\r | |
262 | }\r | |
263 | \r | |
264 | CLRBIT(sca_unit.flags, UNIT_BAUDMASK);\r | |
265 | SETBIT(sca_unit.flags, newbits);\r | |
266 | \r | |
267 | sca_cwait = 1320000 / value; /* intercharacter wait time in instructions (roughly) */\r | |
268 | \r | |
269 | return SCPE_OK;\r | |
270 | }\r | |
271 | \r | |
272 | /*********************************************************************************************\r | |
273 | * HANDY MACROS \r | |
274 | *********************************************************************************************/\r | |
275 | \r | |
276 | #define in_bsc_mode() (sca_unit.flags & UNIT_BISYNC) /* TRUE if user selected BSC mode */\r | |
277 | #define in_str_mode() ((sca_unit.flags & UNIT_BISYNC) == 0) /* TRUE if user selected STR mode */\r | |
278 | \r | |
279 | /*********************************************************************************************\r | |
280 | * mstring - allocate a copy of a string\r | |
281 | *********************************************************************************************/\r | |
282 | \r | |
283 | char *mstring (char *str)\r | |
284 | {\r | |
285 | int len;\r | |
286 | char *m;\r | |
287 | \r | |
288 | len = strlen(str)+1;\r | |
289 | if ((m = malloc(len)) == NULL) {\r | |
290 | printf("Out of memory!");\r | |
291 | return "?"; /* this will of course cause trouble if it's subsequently freed */\r | |
292 | }\r | |
293 | strcpy(m, str);\r | |
294 | return m;\r | |
295 | }\r | |
296 | \r | |
297 | /*********************************************************************************************\r | |
298 | * sca_socket_error - call when there is an error reading from or writing to socket\r | |
299 | *********************************************************************************************/\r | |
300 | \r | |
301 | static void sca_socket_error (void)\r | |
302 | {\r | |
303 | char name[100];\r | |
304 | \r | |
305 | /* print diagnostic? */\r | |
306 | printf("SCA socket error, closing connection\n");\r | |
307 | \r | |
308 | /* tell 1130 that connection was lost */\r | |
309 | CLRBIT(sca_dsw, SCA_DSW_READY);\r | |
310 | \r | |
311 | if (sca_sock != INVALID_SOCKET) {\r | |
312 | /* close socket, prepare to listen again if in listen mode. It's a "master" socket if it was an outgoing connection */\r | |
313 | sim_close_sock(sca_sock, (sca_unit.flags & UNIT_LISTEN) == 0);\r | |
314 | sca_sock = INVALID_SOCKET;\r | |
315 | \r | |
316 | if (sca_unit.filename != NULL) /* reset filename string in unit record */\r | |
317 | free(sca_unit.filename);\r | |
318 | \r | |
319 | if (sca_unit.flags & UNIT_LISTEN) {\r | |
320 | sprintf(name, "(Listening on port %d)", sca_port);\r | |
321 | sca_unit.filename = mstring(name);\r | |
322 | printf("%s\n", name);\r | |
323 | }\r | |
324 | else\r | |
325 | sca_unit.filename = mstring("(connection failed)");\r | |
326 | }\r | |
327 | \r | |
328 | /* clear buffers */\r | |
329 | sca_nrcvd = sca_rcvptr = sca_n2send = sca_nsyns = 0;\r | |
330 | }\r | |
331 | \r | |
332 | /*********************************************************************************************\r | |
333 | * sca_transmit_byte, sca_flush - send data buffering mechanism\r | |
334 | *********************************************************************************************/\r | |
335 | \r | |
336 | static void sca_flush (void)\r | |
337 | {\r | |
338 | int nbytes;\r | |
339 | \r | |
340 | if (sca_n2send > 0) {\r | |
341 | #if (DEBUG_SCA & DEBUG_SCA_FLUSH)\r | |
342 | printf("* SCA_FLUSH %d byte%s\n", sca_n2send, (sca_n2send == 1) ? "" : "s");\r | |
343 | #endif\r | |
344 | \r | |
345 | if (sca_sock != INVALID_SOCKET) {\r | |
346 | nbytes = sim_write_sock(sca_sock, sca_sendbuf, sca_n2send);\r | |
347 | \r | |
348 | if (nbytes == SOCKET_ERROR)\r | |
349 | sca_socket_error();\r | |
350 | else if (nbytes != sca_n2send)\r | |
351 | printf("SOCKET BLOCKED -- NEED TO REWRITE IBM1130_SCA.C");\r | |
352 | \r | |
353 | /* I'm going to assume that SCA communications on the 1130 will consist entirely */\r | |
354 | /* of back and forth exchanges of short records, and so we should never stuff the pipe so full that */\r | |
355 | /* it blocks. If it does ever block, we'll have to come up with an asychronous buffering mechanism. */\r | |
356 | }\r | |
357 | \r | |
358 | sca_n2send = 0; /* mark buffer cleared */\r | |
359 | }\r | |
360 | }\r | |
361 | \r | |
362 | /*********************************************************************************************\r | |
363 | * sca_transmit_byte - buffer a byte to be send to the socket\r | |
364 | *********************************************************************************************/\r | |
365 | \r | |
366 | static void sca_transmit_byte (uint8 b)\r | |
367 | {\r | |
368 | uint32 curtime;\r | |
369 | static uint32 last_syn_time, next_syn_time;\r | |
370 | \r | |
371 | #if (DEBUG_SCA & DEBUG_SCA_TRANSMIT)\r | |
372 | printf("* SCA_TRANSMIT: %02x\n", b);\r | |
373 | #endif\r | |
374 | \r | |
375 | /* write a byte to the socket. Let's assume an 8 bit frame in all cases.\r | |
376 | * We buffer them up, then send the packet when (a) it fills up to a certain point\r | |
377 | * and/or (b) some time has passed? We handle (b) by:\r | |
378 | * checking in sva_svc if several sca_svc calls are made w/o any XIO_WRITES, and\r | |
379 | * flushing send buffer on line turnaround, timeouts, or any other significant state change\r | |
380 | */\r | |
381 | \r | |
382 | /* on socket error, call sca_socket_error(); */\r | |
383 | \r | |
384 | if (b == sichar) {\r | |
385 | if (sca_nsyns >= MAX_SYNS) { /* we're suppressing SYN's */\r | |
386 | if (sca_keepalive > 0) { /* we're sending keepalives, though... check to see if it's time */\r | |
387 | curtime = sim_os_msec();\r | |
388 | if (curtime >= next_syn_time || curtime < last_syn_time) { /* check for < last_syn_time because sim_os_msec() can wrap when OS has been running a long time */\r | |
389 | sca_sendbuf[sca_n2send++] = b;\r | |
390 | sca_sendbuf[sca_n2send++] = b; /* send 2 of them */\r | |
391 | sca_flush();\r | |
392 | last_syn_time = curtime;\r | |
393 | next_syn_time = last_syn_time + sca_keepalive;\r | |
394 | }\r | |
395 | }\r | |
396 | return;\r | |
397 | }\r | |
398 | \r | |
399 | if (++sca_nsyns == MAX_SYNS) { /* we've sent a bunch of SYN's, time to stop sending them */\r | |
400 | sca_sendbuf[sca_n2send] = b; /* send last one */\r | |
401 | sca_flush();\r | |
402 | last_syn_time = sim_os_msec(); /* remember time, and note time to send next one */\r | |
403 | next_syn_time = last_syn_time + sca_keepalive;\r | |
404 | return;\r | |
405 | }\r | |
406 | }\r | |
407 | else\r | |
408 | sca_nsyns = 0;\r | |
409 | \r | |
410 | sca_sendbuf[sca_n2send] = b; /* store character */\r | |
411 | \r | |
412 | if (++sca_n2send >= SCA_SEND_THRESHHOLD)\r | |
413 | sca_flush(); /* buffer is full, send it immediately */\r | |
414 | }\r | |
415 | \r | |
416 | /*********************************************************************************************\r | |
417 | * sca_interrupt (utility routine) - set a bit in the device status word and initiate an interrupt\r | |
418 | *********************************************************************************************/\r | |
419 | \r | |
420 | static void sca_interrupt (int bit)\r | |
421 | {\r | |
422 | sca_dsw |= bit; /* set device status word bit(s) */\r | |
423 | SETBIT(ILSW[1], ILSW_1_SCA); /* set interrupt level status word bit */\r | |
424 | \r | |
425 | calc_ints(); /* udpate simulator interrupt status (not really needed if within xio handler, since ibm1130_cpu calls it after xio handler) */\r | |
426 | }\r | |
427 | \r | |
428 | /*********************************************************************************************\r | |
429 | * sca_reset - reset the SCA device\r | |
430 | *********************************************************************************************/\r | |
431 | \r | |
432 | static t_stat sca_reset (DEVICE *dptr)\r | |
433 | {\r | |
434 | /* flush any pending data */\r | |
435 | sca_flush();\r | |
436 | sca_nrcvd = sca_rcvptr = sca_n2send = sca_nsyns = 0;\r | |
437 | \r | |
438 | /* reset sca activity */\r | |
439 | sca_state = SCA_STATE_IDLE;\r | |
440 | CLRBIT(sca_dsw, SCA_DSW_BUSY | SCA_DSW_AUTOANSWER_ENABLED | SCA_DSW_RECEIVE_RUN | SCA_DSW_READ_RESPONSE | SCA_DSW_WRITE_RESPONSE | SCA_DSW_CHECK | SCA_DSW_TIMEOUT | SCA_DSW_AUTOANSWER_REQUEST);\r | |
441 | sca_timer_state[0] = sca_timer_state[1] = sca_timer_state[2] = SCA_TIMER_INACTIVE;\r | |
442 | any_timer_running = FALSE;\r | |
443 | sca_timer_trigger = FALSE;\r | |
444 | \r | |
445 | if (sca_unit.flags & UNIT_ATT) /* if unit is attached (or listening) */\r | |
446 | sim_activate(&sca_unit, sca_iwait); /* poll for service. Must do this here as BOOT clears activity queue before resetting all devices */\r | |
447 | \r | |
448 | return SCPE_OK;\r | |
449 | }\r | |
450 | \r | |
451 | /*********************************************************************************************\r | |
452 | * sca_attach - attach the SCA device\r | |
453 | *********************************************************************************************/\r | |
454 | \r | |
455 | static t_stat sca_attach (UNIT *uptr, char *cptr)\r | |
456 | {\r | |
457 | t_bool do_listen;\r | |
458 | char *colon;\r | |
459 | uint32 ipaddr;\r | |
460 | int32 port;\r | |
461 | struct hostent *he;\r | |
462 | char name[256];\r | |
463 | static SOCKET sdummy = INVALID_SOCKET;\r | |
464 | fd_set wr_set, err_set;\r | |
465 | \r | |
466 | do_listen = sim_switches & SWMASK('L'); /* -l means listen mode */\r | |
467 | \r | |
468 | if (sca_unit.flags & UNIT_ATT) /* if already attached, detach */\r | |
469 | detach_unit(&sca_unit);\r | |
470 | \r | |
471 | if (do_listen) { /* if listen mode, string specifies socket number (only; otherwise it's a dummy argument) */\r | |
472 | if (isdigit(*cptr)) { /* if digits specified, extract port number */\r | |
473 | port = atoi(cptr);\r | |
474 | if (port <= 0 || port > 65535)\r | |
475 | return SCPE_ARG;\r | |
476 | else\r | |
477 | sca_port = port;\r | |
478 | }\r | |
479 | /* else if nondigits specified, ignore... but the command has to have something there otherwise the core scp */\r | |
480 | /* attach_cmd() routine complains "too few arguments". */\r | |
481 | \r | |
482 | if ((sca_lsock = sim_master_sock(sca_port)) == INVALID_SOCKET)\r | |
483 | return SCPE_OPENERR;\r | |
484 | \r | |
485 | SETBIT(sca_unit.flags, UNIT_LISTEN); /* note that we are listening, not yet connected */\r | |
486 | \r | |
487 | sprintf(name, "(Listening on port %d)", sca_port);\r | |
488 | sca_unit.filename = mstring(name);\r | |
489 | printf("%s\n", name);\r | |
490 | \r | |
491 | }\r | |
492 | else {\r | |
493 | while (*cptr && *cptr <= ' ')\r | |
494 | cptr++;\r | |
495 | \r | |
496 | if (! *cptr)\r | |
497 | return SCPE_2FARG;\r | |
498 | \r | |
499 | if ((colon = strchr(cptr, ':')) != NULL) {\r | |
500 | *colon++ = '\0'; /* clip hostname at colon */\r | |
501 | \r | |
502 | port = atoi(colon); /* extract port number that follows it */\r | |
503 | if (port <= 0 || port > 65535)\r | |
504 | return SCPE_ARG;\r | |
505 | else\r | |
506 | sca_port = port;\r | |
507 | }\r | |
508 | \r | |
509 | if (sdummy == INVALID_SOCKET)\r | |
510 | if ((sdummy = sim_create_sock()) == INVALID_SOCKET) /* create and keep a socket, to force initialization */\r | |
511 | return SCPE_IERR; /* of socket library (e.g on Win32 call WSAStartup), else gethostbyname fails */\r | |
512 | \r | |
513 | if (get_ipaddr(cptr, &ipaddr, NULL) != SCPE_OK) { /* try to parse hostname as dotted decimal nnn.nnn.nnn.nnn */\r | |
514 | if ((he = gethostbyname(cptr)) == NULL) /* if not decimal, look up name through DNS */\r | |
515 | return SCPE_OPENERR;\r | |
516 | \r | |
517 | if ((ipaddr = * (unsigned long *) he->h_addr_list[0]) == INADDR_NONE)\r | |
518 | return SCPE_OPENERR;\r | |
519 | \r | |
520 | ipaddr = ntohl(ipaddr); /* convert to host byte order; gethostbyname() gives us network order */\r | |
521 | }\r | |
522 | \r | |
523 | if ((sca_sock = sim_connect_sock(ipaddr, sca_port)) == INVALID_SOCKET)\r | |
524 | return SCPE_OPENERR;\r | |
525 | \r | |
526 | /* sim_connect_sock() sets socket to nonblocking before initiating the connect, so\r | |
527 | * the connect is pending when it returns. For outgoing connections, the attach command should wait\r | |
528 | * until the connection succeeds or fails. We use "accept" to wait and find out which way it goes...\r | |
529 | */\r | |
530 | \r | |
531 | FD_ZERO(&wr_set); /* we are only interested in info for sca_sock */\r | |
532 | FD_ZERO(&err_set);\r | |
533 | FD_SET(sca_sock, &wr_set);\r | |
534 | FD_SET(sca_sock, &err_set);\r | |
535 | \r | |
536 | select(3, NULL, &wr_set, &err_set, NULL); /* wait for connection to complete or fail */\r | |
537 | \r | |
538 | if (FD_ISSET(sca_sock, &wr_set)) { /* sca_sock appears in "writable" set -- connect completed */\r | |
539 | sprintf(name, "%s:%d", cptr, sca_port);\r | |
540 | sca_unit.filename = mstring(name);\r | |
541 | SETBIT(sca_dsw, SCA_DSW_READY);\r | |
542 | }\r | |
543 | else if (FD_ISSET(sca_sock, &err_set)) { /* sca_sock appears in "error" set -- connect failed */\r | |
544 | sim_close_sock(sca_sock, TRUE);\r | |
545 | sca_sock = INVALID_SOCKET;\r | |
546 | return SCPE_OPENERR;\r | |
547 | }\r | |
548 | else { /* if we get here my assumption about how select works is wrong */\r | |
549 | printf("SCA_SOCK NOT FOUND IN WR_SET -OR- ERR_SET, CODING IN IBM1130_SCA IS WRONG\n");\r | |
550 | sim_close_sock(sca_sock, TRUE);\r | |
551 | sca_sock = INVALID_SOCKET;\r | |
552 | return SCPE_OPENERR;\r | |
553 | }\r | |
554 | }\r | |
555 | \r | |
556 | /* set up socket connect or listen. on success, set UNIT_ATT.\r | |
557 | * If listen mode, set UNIT_LISTEN. sca_svc will poll for connection\r | |
558 | * If connect mode, set dsw SCA_DSW_READY to indicate connection is up\r | |
559 | */\r | |
560 | \r | |
561 | SETBIT(sca_unit.flags, UNIT_ATT); /* record successful socket binding */\r | |
562 | \r | |
563 | sca_state = SCA_STATE_IDLE;\r | |
564 | sim_activate(&sca_unit, sca_iwait); /* start polling for service */\r | |
565 | \r | |
566 | sca_n2send = 0; /* clear buffers */\r | |
567 | sca_nrcvd = 0;\r | |
568 | sca_rcvptr = 0;\r | |
569 | sca_nsyns = 0;\r | |
570 | \r | |
571 | return SCPE_OK;\r | |
572 | }\r | |
573 | \r | |
574 | /*********************************************************************************************\r | |
575 | * sca_detach - detach the SCA device\r | |
576 | *********************************************************************************************/\r | |
577 | \r | |
578 | static t_stat sca_detach (UNIT *uptr)\r | |
579 | {\r | |
580 | if ((sca_unit.flags & UNIT_ATT) == 0)\r | |
581 | return SCPE_OK;\r | |
582 | \r | |
583 | sca_flush();\r | |
584 | \r | |
585 | sca_state = SCA_STATE_IDLE; /* stop processing during service calls */\r | |
586 | sim_cancel(&sca_unit); /* stop polling for service */\r | |
587 | \r | |
588 | CLRBIT(sca_dsw, SCA_DSW_READY); /* indicate offline */\r | |
589 | \r | |
590 | if (sca_sock != INVALID_SOCKET) { /* close connected socket */\r | |
591 | sim_close_sock(sca_sock, (sca_unit.flags & UNIT_LISTEN) == 0);\r | |
592 | sca_sock = INVALID_SOCKET;\r | |
593 | }\r | |
594 | if (sca_lsock != INVALID_SOCKET) { /* close listening socket */\r | |
595 | sim_close_sock(sca_lsock, TRUE);\r | |
596 | sca_lsock = INVALID_SOCKET;\r | |
597 | }\r | |
598 | \r | |
599 | free(sca_unit.filename);\r | |
600 | sca_unit.filename = NULL;\r | |
601 | \r | |
602 | CLRBIT(sca_unit.flags, UNIT_ATT|UNIT_LISTEN);\r | |
603 | \r | |
604 | return SCPE_OK;\r | |
605 | }\r | |
606 | \r | |
607 | /*********************************************************************************************\r | |
608 | * sca_check_connect - see if an incoming socket connection has com\r | |
609 | *********************************************************************************************/\r | |
610 | \r | |
611 | static void sca_check_connect (void)\r | |
612 | {\r | |
613 | uint32 ipaddr;\r | |
614 | char name[100];\r | |
615 | \r | |
616 | if ((sca_sock = sim_accept_conn(sca_lsock, &ipaddr)) == INVALID_SOCKET)\r | |
617 | return;\r | |
618 | \r | |
619 | ipaddr = htonl(ipaddr); /* convert to network order so we can print it */\r | |
620 | \r | |
621 | sprintf(name, "%d.%d.%d.%d", ipaddr & 0xFF, (ipaddr >> 8) & 0xFF, (ipaddr >> 16) & 0xFF, (ipaddr >> 24) & 0xFF);\r | |
622 | \r | |
623 | printf("(SCA connection from %s)\n", name);\r | |
624 | \r | |
625 | if (sca_unit.filename != NULL)\r | |
626 | free(sca_unit.filename);\r | |
627 | \r | |
628 | sca_unit.filename = mstring(name);\r | |
629 | \r | |
630 | SETBIT(sca_dsw, SCA_DSW_READY); /* indicate active connection */\r | |
631 | \r | |
632 | if (sca_dsw & SCA_DSW_AUTOANSWER_ENABLED) /* if autoanswer was enabled, I guess we should give them an interrupt. Untested. */\r | |
633 | sca_interrupt(SCA_DSW_AUTOANSWER_REQUEST);\r | |
634 | }\r | |
635 | \r | |
636 | /*********************************************************************************************\r | |
637 | * sca_check_indata - try to fill receive buffer from socket\r | |
638 | *********************************************************************************************/\r | |
639 | \r | |
640 | static void sca_check_indata (void)\r | |
641 | {\r | |
642 | int nbytes;\r | |
643 | \r | |
644 | sca_rcvptr = 0; /* reset pointers and count */\r | |
645 | sca_nrcvd = 0;\r | |
646 | \r | |
647 | #ifdef FAKE_SCA\r | |
648 | \r | |
649 | nbytes = 5; /* FAKE: receive SYN SYN SYN SYN DLE ACK0 */\r | |
650 | sca_rcvbuf[0] = 0x32;\r | |
651 | sca_rcvbuf[1] = 0x32;\r | |
652 | sca_rcvbuf[2] = 0x32;\r | |
653 | sca_rcvbuf[3] = 0x10;\r | |
654 | sca_rcvbuf[4] = 0x70;\r | |
655 | \r | |
656 | #else\r | |
657 | /* read socket; 0 is returned if no data is available */\r | |
658 | nbytes = sim_read_sock(sca_sock, sca_rcvbuf, SCA_RCVBUF_SIZE);\r | |
659 | \r | |
660 | #endif\r | |
661 | \r | |
662 | if (nbytes < 0)\r | |
663 | sca_socket_error();\r | |
664 | else /* zero or more */\r | |
665 | sca_nrcvd = nbytes;\r | |
666 | \r | |
667 | #if (DEBUG_SCA & DEBUG_SCA_CHECK_INDATA)\r | |
668 | if (sca_nrcvd > 0)\r | |
669 | printf("* SCA_CHECK_INDATA %d byte%s\n", sca_nrcvd, (sca_nrcvd == 1) ? "" : "s");\r | |
670 | #endif\r | |
671 | }\r | |
672 | \r | |
673 | /*********************************************************************************************\r | |
674 | * sca_svc - handled scheduled event. This will presumably be scheduled frequently, and can check\r | |
675 | * for incoming data, reasonableness of initiating a write interrupt, timeouts etc.\r | |
676 | *********************************************************************************************/\r | |
677 | \r | |
678 | static t_stat sca_svc (UNIT *uptr)\r | |
679 | {\r | |
680 | t_bool timeout;\r | |
681 | int msec_now;\r | |
682 | int i;\r | |
683 | \r | |
684 | /* if not connected, and if in wait-for-connection mode, check for connection attempt */\r | |
685 | if ((sca_unit.flags & UNIT_LISTEN) && ! (sca_dsw & SCA_DSW_READY))\r | |
686 | sca_check_connect();\r | |
687 | \r | |
688 | if (any_timer_running) {\r | |
689 | msec_now = sim_os_msec();\r | |
690 | \r | |
691 | timeout = FALSE;\r | |
692 | for (i = 0; i < 3; i++) {\r | |
693 | if (sca_timer_state[i] == SCA_TIMER_RUNNING && msec_now >= sca_timer_endtime[i]) {\r | |
694 | timeout = TRUE;\r | |
695 | sca_timer_state[i] = SCA_TIMER_TIMEDOUT;\r | |
696 | #if (DEBUG_SCA & DEBUG_SCA_TIMERS)\r | |
697 | printf("+ SCA_TIMER %d timed out\n", i);\r | |
698 | #endif\r | |
699 | \r | |
700 | if (i == TIMER_035S && sca_timer_trigger) {\r | |
701 | sca_timer_trigger = FALSE; /* uninhibit the other two timers */\r | |
702 | sca_toggle_timer(TIMER_3S, msec_now);\r | |
703 | sca_toggle_timer(TIMER_125S, msec_now);\r | |
704 | }\r | |
705 | }\r | |
706 | }\r | |
707 | \r | |
708 | if (timeout)\r | |
709 | sca_interrupt(SCA_DSW_TIMEOUT);\r | |
710 | \r | |
711 | any_timer_running = (sca_timer_state[0]| sca_timer_state[1] | sca_timer_state[2]) & SCA_TIMER_RUNNING;\r | |
712 | }\r | |
713 | \r | |
714 | if (sca_dsw & SCA_DSW_READY) { /* if connected */\r | |
715 | \r | |
716 | Content-type: text/html