From fde9c6e812acdca44bf93e456a150a2fc8899aad Mon Sep 17 00:00:00 2001 From: Jim Date: Sat, 30 Jul 2011 17:06:19 -0400 Subject: [PATCH] Initial PNC driver (no receive yet), added #ifdefs for trace, allow continuing after real HLT in S and R modes for old T&M diagnostics, expanded list of Prime device drivers --- devpnc.h | 1011 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ em.c | 72 ++-- emdev.h | 795 ++---------------------------------------- makefile | 1 - 4 files changed, 1091 insertions(+), 788 deletions(-) create mode 100644 devpnc.h diff --git a/devpnc.h b/devpnc.h new file mode 100644 index 0000000..cb37646 --- /dev/null +++ b/devpnc.h @@ -0,0 +1,1011 @@ +/* PNC (ring net) device handler + + On a real Prime ring network, each node has a unique node id from 1 + to 254. The node id is configured with software and stored into the + PNC during network initialization. The node id of a master-cleared + PNC is zero, and Primos may tell a PNC to connect to the ring with + node id 0 so that the PNC will regenerate packets ("transceive") as + they pass by. The reason is that when a PNC disconnects from the + ring, the segment between its two neighbors becomes longer, and + there is a limit to the distance between active nodes. Since no + node ever has a real node id of zero, this PNC will not ack or + receive any packets. The broadcast node id is 255 (all PNCs accept + packets with this "to" id). In practice, CONFIG_NET limits the node + id to 247. When a node starts, it sends a message to itself. If + any system acks this packet, it means the node id is already in use + and the new node will disconnect from the ring. Since all systems + in a ring have to be physically cabled together, there was usually a + common network administration to ensure that no two nodes had the + same node id. Prime rings were often logically segmented: nodes A, + B, C, D in a physical ring might be configured as two logical + networks: AB and CD. But all 4 systems must still have unique node + ids since they are in the same physical ring. + + The emulation of the PNC controller uses TCP/IP and sockets to + communicate with other emulators. The main differences are unique + point-to-point connections between individual systems, and a + byte-stream interface vs the message-oriented interface of the real + PNC. + + Because the emulated PNC has unique connections to each node, the + unique node id concept isn't required and doesn't make a lot of + sense: if 2 guys running the emulator have a 2-node Ringnet with + their nodes numbered 1 and 2 (network A), and 2 other guys have a + 2-node network with their nodes numbered 1 and 2 (network B), then + it wouldn't be possible for a node in network A to add a node in + network B without one/both of them changing their node ids to ensure + uniqueness. To get around this, the emulator has a config file, + ring.cfg, that lets you say "Here are the nodes in *my* ring (with + all unique node ids), here is each node's IP address and port (to + connect), and a 16-byte unique id used as a password and unique node + identifier. This allows the Prime node id to be a local number that + doesn't need to be coordinated with remote emulators, and allows one + emulator to be in multiple rings simultaneously. The only items two + people need to share are their IP/port addresses, the 16-byte unique + id, and the node-to-node password in the Primenet config. Actually, + the Primenet config node-to-node password becomes redundant and + should probably be left blank for ease in configuration. Cool, eh? + + Prior to rev 19.3, each node sends periodic "timer" messages to all + nodes it knows about. If the message returns ACK'd, the remote node + is up. Otherwise, the remote node must be down. This is not very + efficient, so beginning with 19.3, a broadcast timer message is sent + every 10 seconds to let all nodes know that a machine is up. On a + 100-node network, instead of sending 100*99=9900 timer messages, + only 100 are required. Unfortunately, the emulated PNC does not + have the efficient broadcast mechanism of a physical token ring, so + broadcasts must be simulated by sending packets to each node. It + would be possible to store the first broadcast timer message + received from each node, and simply loop them all back within each + emulator every 10 seconds as long as the TCP/IP connection is up. + Not sure it's worth the trouble though... + + In early version of Primos, PNC ring buffers (physical packets) are + 256 words, with later versions of Primos also supporting 512, and + 1024 word packets. "nbkini" (rev 18) allocates 12 ring buffers + 1 + for every 2 nodes in the ring. Both the 1-to-2 queue for received + packets, and 2-to-1 queue for xmit packets have 63 entries. PNC + ring buffers are wired and never cross page boundaries. + + The PNC controller data buffer has a 2-word header, followed by the + data. I believe the PNC hardware only looks at the first word. + + 0: To (left) and From (right) bytes containing node-ids + 1: "Type" word. + Bit 1 set = odd number of bytes (if set, last word is only 1 byte) + Bit 7 set = normal data messages (otherwise, a timer message) + Bit 16 set = broadcast timer message + + NOTE: at the hardware level, there are many other fields associated + with a ring packet on the wire, for example, a CRC, ack byte, etc. + These are generated and used only by the PNC hardware; the Prime + software, even PNCDIM, never sees these fields. + + The PNC emulation expands the packet format during transmission by + inserting a 16-bit byte count before the first word. This is + necessary since we're emulating a message-based protocol over a + stream-based interface. As a precaution, the emulator also adds the + 16-bit byte count at the end of the packet. If these two counters + don't match, there has been some kind of mishap in the PNC + emulation. These fields are similar to the CRC and ack byte of a + real PNC: the Prime I/O bus never sees them. + + Because of TCP/IP streaming, it is important that a ring packet is + sent in its entirety or not at all. If there is a situation where a + packet has been partially sent or received and can't be finished, + the socket connection must be terminated and restarted. An example + of this is sending to a node with a suspended emulator. The sending + emulator's socket buffer is filled, a packet may be partially + transmitted (to the socket buffer), then Primos eventually decides + the transmit is taking too long and cancels the transmit. There is + no way to cancel the partial packet in the socket buffer. The + emulator uses SO_SNDLOWAT with 2052 to reserve 2052 bytes in the + transmit socket buffer. This should prevent partial packet + transmissions. + + At a higher level, each ring buffer has an associated "block + header", stored in a different location in system memory, that is 8 + words. The block header is used by Prime software, not by the PNC + hardware. The BH fields are (16-bit words): + 0: type field (1) + 1: free pool id (3 for ring buffers) + 2-3: pointer to data block + + 4-7 are available for use by drivers. For the PNC: + 4: number of words received (calculated by pncdim based on DMA regs) + 5: receive status word + 6: data 1 + 7: data 2 + + Primos PNC usage: + + OCP '0007 + - disconnect from ring + + OCP '0207 + - inject a token into the ring + + OCP '0507 + - set PNC into "delay" mode + + OCP '1007 + - stop any xmit in progress + + INA '1707 + - read network status word + - does this in a loop until "connected" bit is clear after disconnect above + + INA '1207 + - read receive status word + + INA '1307 + - read xmit status word + + OCP '1707 + - initialize + + OTA '1707 + - set my node ID (in A register) + - if this fails, no PNC present + + OTA '1607 + - set interrupt vector (in A reg) + + OTA '1407 + - initiate receive, dma channel in A + + OTA '1507 + - initiate xmit, dma channel in A + + OCP '0107 + - connect to the ring + - (Primos follows this with INA '1707 loop until "connected" bit is set) + + OCP '1407 + - ack receive (clears recv interrupt request) + + OCP '0407 + - ack xmit (clears xmit interrupt request) + + OCP '1507 + - set interrupt mask (enable interrupts) + + OCP '1107 + - stop receive in progress + +*/ + +#ifndef HOBBY + +/* PNC poll rate is 1/10th of a second */ + +#define PNCPOLL 100 + +/* PNC network status bits + ACK = the node the packet was addressed to saw the packet correctly + WACK = same as ACK, but the node couldn't accept the packet (no recv active) + NAK = the packet was corrupt when some (any) node saw it + + NOTE: many fields are commented out since they can't occur in the + emulator implementation; they're specified as documentation */ + +#define PNCNSRCVINT 0x8000 /* bit 1 rcv interrupt (rcv complete) */ +#define PNCNSXMITINT 0x4000 /* bit 2 xmit interrupt (xmit complete) */ + //#define PNCNSPNC2 0x2000 /* bit 3 PNC II = 1 (aka pncbooster) */ + //#define PNCNSNEWMODE 0x1000 /* bit 4 PNC in "new"/II mode */ + //#define PNCNSERROR 0x0800 /* bit 5 u-verify failure or bad command (II) */ +#define PNCNSCONNECTED 0x0400 /* bit 6 connected to ring */ + //#define PNCNSMULTOKEN 0x0200 /* bit 7 multiple tokens (only after xmit EOR) */ +#define PNCNSTOKEN 0x0100 /* bit 8, token (only after xmit EOR) */ +#define PNCNSNODEID 0x00FF /* bits 9-16 node-id mask */ + +/* receive status word: first 8 bits are the "ACK byte" */ + +#define PNCRSACK 0x8000 /* ACK'd */ + //#define PNCRSMACK 0x4000 /* multiple ACK */ +#define PNCRSWACK 0x2000 /* WACK'd */ + //#define PNCRSNAK 0x1000 /* NAK'd */ + //#define PNCRSABPE 0x0200 /* ack byte parity error */ + //#define PNCRSABCE 0x0100 /* ack byte check error */ + //#define PNCRSRBPE 0x0080 /* receive buffer parity error */ +#define PNCRSBUSY 0x0040 /* receive busy */ +#define PNCRSEOR 0x0020 /* EOR before end of message */ + +/* xmit status word: first 8 bits are the "ACK byte" */ + +#define PNCXSACK 0x8000 /* ACK'd */ + //#define PNCXSMACK 0x4000 /* multiple ACK */ +#define PNCXSWACK 0x2000 /* WACK'd */ + //#define PNCXSNAK 0x1000 /* NAK'd */ + //#define PNCXSABPE 0x0200 /* ack byte parity error */ + //#define PNCXSABCE 0x0100 /* ack byte check error */ + //#define PNCXSXBPE 0x0080 /* transmit buffer parity error */ +#define PNCXSBUSY 0x0040 /* xmit busy */ + //#define PNCXSNORET 0x0020 /* packet didn't return */ + //#define PNCXSRETBAD 0x0010 /* returned w/Ack byte or CRC error */ + //#define PNCXSRETRYFAIL 0x0008 /* retry not successful */ + //#define PNCXSRETRIES 0x0007 /* retry count */ + +#define MINACCEPTTIME 1 /* wait 1 second before accepting new connections */ +#define MAXACCEPTTIME 5 /* only wait 5 seconds to receive uid */ +#define MINCONNTIME 30 /* wait 30 seconds between connect attempts */ +#define MAXPNCBYTES 2048 /* max of 2048 byte packets */ +#define MAXPKTBYTES 2052 /* adds 16-bit length word to each end */ + +#define MAXHOSTLEN 64 /* max length of remote host name */ +#define MAXUIDLEN 16 /* max length of unique id/password */ + +static short configured = 0; /* true if PNC configured */ +static unsigned short pncstat; /* controller status word */ +static unsigned short rcvstat; /* receive status word */ +static unsigned short xmitstat; /* xmit status word */ +static unsigned short pncvec; /* PNC interrupt vector */ +static unsigned short myid; /* my PNC node id */ +static unsigned short enabled; /* interrupts enabled flag */ +static int pncfd; /* socket fd for all PNC network connections */ + +/* the ni structure contains the important information for each node + in the network and is indexed by the node id */ + +static struct { /* node info for each node in my network */ + short cstate; /* connection state (see below) */ + short fd; /* socket fd for this node, -1 if unconnected */ + char host[MAXHOSTLEN+1]; /* host name/address of the remote node */ + short port; /* emulator network port on the remote node */ + char uid[MAXUIDLEN+1]; /* unique ID/password for this node (16 + null) */ + char rcvpkt[MAXPKTBYTES];/* receive packet w/leading + trailing length */ + short rcvoffset; /* next byte offset for receive */ + time_t conntime; /* time of last connect attempt */ +} ni[255]; /* ring id 0-254 (255 is broadcast) */ + +/* PNC connection states. This is a bit complex because the socket + operations are all non-blocking and the unique ID has to be sent + after connecting */ + +#define PNCCSNONE 0 /* not configured */ +#define PNCCSDISC 1 /* not connected */ +#define PNCCSCONN 2 /* connected */ +#define PNCCSAUTH 3 /* unique ID / password sent */ + +/* array to map socket fd's to node id's for accessing the ni + structure. Not great programming, because the host OS could + choose to use large fd's, which will cause a runtime error */ + +#define MAXFD 1023 +#define FDMAPSIZE MAXFD+1 + +static short fdnimap[FDMAPSIZE]; + +/* xmit/recv buffer states and buffers. The recv buffer must be + static since data may be received over multiple devpnc calls. The + xmit buffer is on the stack: we copy whole packets into the socket + buffer in 1 devpnc call */ + +#define PNCBSIDLE 0 /* initial state: no xmit or recv */ +#define PNCBSRDY 1 /* ready to recv or xmit */ +#define PNCBSXFER 2 /* transferring data over socket */ + +typedef struct { + short toid, fromid; + short state; + short pktsize; /* size of packet in bytes */ + short offset; + unsigned short dmachan, dmareg, dmaaddr; + short dmanw, dmabytesleft; + unsigned short *memp; /* ptr to Prime memory */ + unsigned char iobuf[MAXPKTBYTES]; +} t_dma; +static t_dma rcv; +t_dma xmit; + +/* return pointer to a hex-formatted uid */ + +char * pnchexuid(char * uid) { + static char hexuid[MAXUIDLEN*2]; + int i; + char ch; + + for (i=0; i> 4; + if (0 <= ch && ch <= 9) + hexuid[i*2] = ch + '0'; + else + hexuid[i*2] = ch - 10 + 'a'; + ch = uid[i] & 0xF; + if (0 <= ch && ch <= 9) + hexuid[i*2+1] = ch + '0'; + else + hexuid[i*2+1] = ch - 10 + 'a'; + } + return hexuid; +} + +/* disconnect from a node */ + +unsigned short pncdisc(nodeid) { + if (ni[nodeid].cstate > PNCCSDISC) { + TRACE(T_RIO, " pncdisc: disconnect from node %d\n", nodeid); + close(ni[nodeid].fd); + fdnimap[ni[nodeid].fd] = -1; + ni[nodeid].cstate = PNCCSDISC; + ni[nodeid].fd = -1; + } +} + +/* initialize a socket fd for PNC emulation */ + +pncinitfd(int fd) { + int optval, fdflags; + + if ((fdflags = fcntl(fd, F_GETFL, 0)) == -1) { + perror("unable to get ts flags for PNC"); + fatal(NULL); + } + fdflags |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, fdflags) == -1) { + perror("unable to set fdflags for PNC"); + fatal(NULL); + } + optval = MAXPKTBYTES; + if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &optval, sizeof(optval))) { + perror("setsockopt failed for PNC"); + fatal(NULL); + } +} + +/* process a new incoming connection */ + +pncaccept(time_t timenow) { + static time_t accepttime = 0; + static int fd = -1; + static struct sockaddr_in addr; + static unsigned int addrlen; + char uid[MAXUIDLEN+1]; + int i,n; + + if (!(pncstat & PNCNSCONNECTED)) + return; + if (fd == -1) { + if (timenow-accepttime < MINACCEPTTIME) + return; + accepttime = timenow; + fd = accept(pncfd, (struct sockaddr *)&addr, &addrlen); + if (fd == -1) { + if (errno != EWOULDBLOCK && errno != EINTR && errno != EAGAIN) + perror("accept error for PNC"); + return; + } + if (fd >= MAXFD) + fatal("New connection fd is too big"); + TRACE(T_RIO, " new PNC connection, fd %d\n", fd); + } + + /* PNC connect request seen: + - read unique id / password + - scan host table to find node id + - if no matching unique id / password, display warning and disconnect + - if already connected, display warning error and disconnect + */ + + if (timenow-accepttime > MAXACCEPTTIME) { + fprintf(stderr, "devpnc: too long to receive uid\n"); + goto disc; + } + n = read(fd, uid, MAXUIDLEN); + if (n == -1) { + if (errno == EWOULDBLOCK || errno == EINTR || errno == EAGAIN) + return; + perror("error reading PNC uid"); + goto disc; + } + if (n != MAXUIDLEN) { + fprintf(stderr, "devpnc: error reading uid, expected %d bytes, got %d\n", MAXUIDLEN, n); + goto disc; + } + TRACE(T_RIO, " uid is %s for new PNC fd %d\n", pnchexuid(uid), fd); + + /* look up the uid in our ring.cfg array to determine the node id + we'll use for this remote emulator */ + + for (i=1; i<255; i++) + if (memcmp(uid, ni[i].uid, MAXUIDLEN) == 0) + break; + if (i == 255) { + fprintf(stderr, "devpnc: couldn't find uid %s\n", uid); + goto disc; + } + if (ni[i].cstate > PNCCSDISC) { + TRACE(T_RIO, " devpnc: already connected to node %d, uid %s\n", i, uid); + if (memcmp(uid, ni[myid].uid, MAXUIDLEN) < 0) + goto disc; + else + pncdisc(i); + } + ni[i].cstate = PNCCSAUTH; + ni[i].fd = fd; + fdnimap[fd] = i; + fd = -1; + return; + +disc: + close(fd); + fd = -1; +} + +/* connect to a node, without blocking */ + +unsigned short pncconn1(nodeid, timenow) { + struct hostent* server; + struct sockaddr_in addr; + unsigned int addrlen; + int fd; + + TRACE(T_RIO, "try connect to node %d\n", nodeid); + ni[nodeid].conntime = timenow; + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + perror ("Unable to create socket"); + exit(1); + } + pncinitfd(fd); + server = gethostbyname(ni[nodeid].host); + if (server == NULL) { + fprintf(stderr,"devpnc: cannot resolve %s\n", ni[nodeid].host); + close(fd); + return; + } + bzero((char *) &addr, sizeof(addr)); + addr.sin_family = AF_INET; + bcopy((char *)server->h_addr, (char *)&addr.sin_addr.s_addr, server->h_length); + addr.sin_port = htons(ni[nodeid].port); + if (connect(fd, (void *) &addr,(socklen_t) sizeof(addr)) < 0 && errno != EINPROGRESS) { + perror("devpnc: error connecting to node\n"); + close(fd); + return; + } + ni[nodeid].fd = fd; + ni[nodeid].cstate = PNCCSCONN; +} + +/* send authorization uid / password after a connect */ + +pncauth1(int nodeid, time_t timenow) { + int n; + + n = write(ni[nodeid].fd, ni[nodeid].uid, MAXUIDLEN); + TRACE(T_RIO, "sent uid %s, hex %s, for node %d, fd %d, %d bytes\n", ni[nodeid].uid, pnchexuid(ni[nodeid].uid), nodeid, ni[nodeid].fd, n); + if (n == MAXUIDLEN) { + ni[nodeid].cstate = PNCCSAUTH; + return; + } + if (n == -1) { + if (errno == EWOULDBLOCK || errno == EINTR || errno == EAGAIN || errno == EINPROGRESS) + return; + if (errno != EPIPE) + perror("error sending PNC uid"); + goto disc; + } + fprintf(stderr, "devpnc: expected %d bytes, only wrote %d bytes for uid\n", MAXUIDLEN, n); + +disc: + close(ni[nodeid].fd); + ni[nodeid].fd = -1; + ni[nodeid].cstate = PNCCSDISC; +} + +/* connect to the next unconnected node. We only try to connect to a + remote system once: if they aren't up, we assume they will try to + connect to us when they come up */ + +pncconnect(time_t timenow) { + static int prevnode=0; + int i, nodeid; + + if (myid == 0) + return; + i = prevnode; + while (1) { + i = (i % 254) + 1; + if (i == prevnode) /* went round once? */ + break; + if (i == myid) /* don't connect to myself */ + continue; + if (ni[i].cstate == PNCCSCONN) { + pncauth1(i, timenow); + break; + } + //TRACE(T_RIO, "pncconnect: node %d, state %d, timenow %d, conntime %d\n", i, ni[i].cstate, timenow, ni[i].conntime); + if (ni[i].cstate == PNCCSDISC && timenow - ni[i].conntime > MINCONNTIME) { + pncconn1(i, timenow); + break; + } + } + prevnode = i; +} + +/* initialize a dma transfer */ + +pncinitdma(t_dma *iob, char *iotype) { + (*iob).dmachan = crs[A]; + (*iob).dmareg = (*iob).dmachan << 1; + (*iob).dmanw = regs.sym.regdmx[(*iob).dmareg]; + if ((*iob).dmanw <= 0) + (*iob).dmanw = -((*iob).dmanw>>4); + else + (*iob).dmanw = -(((*iob).dmanw>>4) ^ 0xF000); + (*iob).dmaaddr = ((regs.sym.regdmx[(*iob).dmareg] & 3)<<16) | regs.sym.regdmx[(*iob).dmareg+1]; + (*iob).memp = MEM + mapio((*iob).dmaaddr); + (*iob).state = PNCBSRDY; + (*iob).offset = 0; + TRACE(T_RIO, " pncinitdma: %s dmachan=%o, dmareg=%o, dmaaddr=%o, dmanw=%d\n", iotype, (*iob).dmachan, (*iob).dmareg, (*iob).dmaaddr, (*iob).dmanw); +} + + +unsigned short pncxmit1(short nodeid, t_dma *iob) { + int nwritten, ntowrite; + + if (ni[nodeid].fd == -1) { /* not connected yet */ + if (ni[nodeid].cstate != PNCCSNONE) + TRACE(T_RIO, " can't transmit: not connected to node %d\n", nodeid); + return 0; + } + if (ni[nodeid].cstate != PNCCSAUTH) { /* not authorized yet */ + TRACE(T_RIO, " can't transmit: not authorized to node %d\n", nodeid); + return 0; + } + + /* NOTE: setsockopt SO_SNDLOWAT ensures that a partial packet write + doesn't occur here */ + + ntowrite = (*iob).dmanw*2 + 4; + if ((nwritten=write(ni[nodeid].fd, (*iob).iobuf, ntowrite)) < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + TRACE(T_RIO, " wack packet to node %d\n", nodeid); + return PNCXSWACK; + } else { + TRACE(T_RIO, " error writing packet to node %d: %s\n", nodeid, strerror(errno)); + pncdisc(nodeid); + perror("devpnc: write error"); + return 0; + } + } else if (nwritten != ntowrite) { + fprintf(stderr, "devpnc: pncxmit1 tried to write %d, wrote %d\n", ntowrite, nwritten); + pncdisc(nodeid); + return 0; + } + return PNCXSACK; +} + +/* transmit a packet to its destination, either a single nodeid or + 255, the broadcast nodeid */ + +unsigned short pncxmit(t_dma *iob) { + short i; + + if ((*iob).toid == 255) { + for (i=1; i<255; i++) + xmitstat |= pncxmit1(i, iob); + } else { + xmitstat |= pncxmit1((*iob).toid, iob); + } + return xmitstat; +} + +/* process receives on all connections */ + +pncrecv(time_t timenow) { +} + +int devpnc (int class, int func, int device) { + + short i; + short len; + unsigned short dmaword; + time_t timenow; + struct sockaddr_in addr; + unsigned int addrlen; + int fd, optval, fdflags; + +#define DELIM " \t\n" +#define PDELIM ":" + + FILE *ringfile; + char *tok, buf[128], *p; + int n, linenum; + int tempid, tempport, cfgerrs; + char temphost[MAXHOSTLEN+1]; + + //gvp->traceflags = ~T_MAP; + + if (class >= 0 && !configured) { + TRACE(T_INST|T_RIO, "PIO to PNC ignored, class=%d, func='%02o, device=%02o\n", class, func, device); + return -1; + } + + switch (class) { + + case -1: + if (nport <= 0) { + fprintf(stderr, "-nport is zero, PNC not started\n"); + return -1; + } + + pncstat = 0; + rcvstat = 0; + xmitstat = 0; + pncvec = 0; + myid = 0; /* set initial node id */ + enabled = 0; + pncfd = -1; + for (i=0; i 247) { + fprintf(stderr,"Line %d of ring.cfg: node id is out of range 1-247\n", linenum); + continue; + } + if (ni[tempid].cstate != PNCCSNONE) { + fprintf(stderr,"Line %d of ring.cfg: node id occurs more than once\n", linenum); + continue; + } + + if ((p=strtok(NULL, DELIM)) == NULL) { + fprintf(stderr,"Line %d of ring.cfg: host address missing\n", linenum); + continue; + } + if (strlen(p) > MAXHOSTLEN) { + fprintf(stderr,"Line %d of ring.cfg: IP address too long\n", linenum); + continue; + } + strcpy(temphost, p); + + if ((p=strtok(NULL, DELIM)) == NULL) { + fprintf(stderr,"Line %d of ring.cfg: unique id/password missing\n", linenum); + continue; + } + if (strlen(p) > MAXUIDLEN) { + fprintf(stderr,"Line %d of ring.cfg: unique id/password too long\n", linenum); + continue; + } + bzero(ni[tempid].uid, sizeof(ni[tempid].uid)); + strcpy(ni[tempid].uid, p); + + /* parse the port number from the IP address */ + + tempport = -1; + if ((p=strtok(temphost, PDELIM)) != NULL) { + strcpy(ni[tempid].host, p); + if ((p=strtok(NULL, PDELIM)) != NULL) { + tempport = atoi(p); + if (tempport < 1 || tempport > 65000) + fprintf(stderr,"Line %d of ring.cfg: port number out of range 1-65000\n", linenum); + } + } + if (tempport <= 0) { + fprintf(stderr, "Line %d of ring.cfg: can't parse port number from %s\n", linenum, temphost); + continue; + } + ni[tempid].cstate = PNCCSDISC; + ni[tempid].port = tempport; + TRACE(T_RIO, "Line %d: id=%d, host=\"%s\", port=%d, uid=%s, hex=%s\n", linenum, tempid, temphost, tempport, ni[tempid].uid, pnchexuid(ni[tempid].uid)); + configured = 1; + } + if (!feof(ringfile)) { + perror(" error reading ring.cfg"); + fatal(NULL); + } + fclose(ringfile); + } else + perror("error opening ring.cfg"); + if (!configured) { + fprintf(stderr, "PNC not configured.\n"); + return -1; + } + + /* start listening on the network port */ + + pncfd = socket(AF_INET, SOCK_STREAM, 0); + if (pncfd == -1) { + perror("socket failed for PNC"); + fatal(NULL); + } + pncinitfd(pncfd); + optval = 1; + if (setsockopt(pncfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))) { + perror("setsockopt failed for PNC listen"); + fatal(NULL); + } + addr.sin_family = AF_INET; + addr.sin_port = htons(nport); + addr.sin_addr.s_addr = INADDR_ANY; + if(bind(pncfd, (struct sockaddr *)&addr, sizeof(addr))) { + perror("bind: unable to bind for PNC"); + fatal(NULL); + } + if(listen(pncfd, 10)) { + perror("listen failed for PNC"); + fatal(NULL); + } + TRACE(T_RIO, "PNC configured\n"); + return 0; + + case 0: + + if (func == 00) { /* OCP '0700 - disconnect */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - disconnect\n", func, device); + for (i=0; i<256; i++) { + fd = ni[i].fd; + if (fd >= 0) { + fdnimap[fd] = -1; + close(fd); + ni[i].fd = -1; + } + } + rcv.state = PNCBSIDLE; + rcvstat = 0; + xmitstat = 0; + pncstat &= PNCNSNODEID; + + } else if (func == 01) { /* OCP '0701 connect to the ring */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - connect\n", func, device); + pncstat |= PNCNSCONNECTED; + + } else if (func == 02) { /* OCP '0702 inject a token */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - inject token\n", func, device); + + } else if (func == 04) { /* OCP '0704 ack xmit (clear xmit int) */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - ack xmit int\n", func, device); + pncstat &= ~PNCNSXMITINT; /* clear "xmit interrupting" */ + pncstat &= ~PNCNSTOKEN; /* clear "token detected" */ + xmitstat = 0; + xmit.state = PNCBSIDLE; + + } else if (func == 05) { /* OCP '0705 set PNC into "delay" mode */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - set delay mode\n", func, device); + + } else if (func == 010) { /* OCP '0710 stop xmit in progress */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - stop xmit\n", func, device); + if (xmit.offset == 0) { + xmitstat = 0; + xmit.state = PNCBSIDLE; + } + + } else if (func == 011) { /* OCP '0711 stop recv in progress */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - stop recv\n", func, device); + + } else if (func == 012) { /* OCP '0712 set normal mode */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - set normal mode\n", func, device); + + } else if (func == 013) { /* OCP '0713 set diagnostic mode */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - set diag mode\n", func, device); + + } else if (func == 014) { /* OCP '0714 ack receive (clear rcv int) */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - ack recv int\n", func, device); + rcvstat = 0; + pncstat &= ~PNCNSRCVINT; + + } else if (func == 015) { /* OCP '0715 set interrupt mask (enable int) */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - enable int\n", func, device); + enabled = 1; + + } else if (func == 016) { /* OCP '0716 clear interrupt mask (disable int) */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - disable int\n", func, device); + enabled = 0; + + } else if (func == 017) { /* OCP '0717 initialize */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - initialize\n", func, device); + devpnc(0, 0, 7); /* disconnect */ + pncvec = 0; + enabled = 0; + + } else { + printf("Unimplemented OCP device '%02o function '%02o\n", device, func); + fatal(NULL); + } + break; + + case 1: + TRACE(T_INST|T_RIO, " SKS '%02o%02o\n", func, device); + if (func == 99) + IOSKIP; /* assume it's always ready */ + else { + printf("Unimplemented SKS device '%02o function '%02o\n", device, func); + fatal(NULL); + } + break; + + case 2: + if (func == 011) { /* input ID */ + TRACE(T_INST|T_RIO, " INA '%02o%02o - input ID\n", func, device); + crs[A] = 07; + IOSKIP; + + } else if (func == 012) { /* read receive status word */ + TRACE(T_INST|T_RIO, " INA '%02o%02o - get recv status '%o\n", func, device, rcvstat); + crs[A] = rcvstat; + IOSKIP; + + } else if (func == 013) { /* DIAG - read static register; not impl. */ + crs[A] = 0; + IOSKIP; + + } else if (func == 014) { /* read xmit status word */ + TRACE(T_INST|T_RIO, " INA '%02o%02o - get xmit status '%o\n", func, device, xmitstat); + crs[A] = xmitstat; + IOSKIP; + + } else if (func == 017) { /* read network status word */ + TRACE(T_INST|T_RIO, " INA '%02o%02o - get ctrl status '%o\n", func, device, pncstat); + crs[A] = pncstat; + IOSKIP; + + } else { + printf("Unimplemented INA device '%02o function '%02o\n", device, func); + fatal(NULL); + } + break; + + case 3: + TRACE(T_INST|T_RIO, " OTA '%02o%02o\n", func, device); + if (func == 011) { /* DIAG - single step; not impl.*/ + IOSKIP; + + } else if (func == 014) { /* initiate recv, dma chan in A */ + if (rcvstat & PNCRSBUSY) { /* already busy? */ + warn("pnc: recv when already busy!"); + return; /* yes, return and don't skip */ + } + IOSKIP; + rcvstat = PNCRSBUSY; /* set receive busy */ + pncinitdma(&rcv, "rcv"); + devpoll[device] = 10; + + } else if (func == 015) { /* initiate xmit, dma chan in A */ + if (xmitstat & PNCXSBUSY) { /* already busy? */ + warn("pnc: xmit when already busy!"); + return; /* yes, return and don't skip */ + } + IOSKIP; + xmitstat = PNCXSBUSY; + + /* read the first word, the to and from node id's */ + + pncinitdma(&xmit, "xmit"); + dmaword = *xmit.memp; + xmit.toid = dmaword >> 8; + xmit.fromid = dmaword & 0xFF; + TRACE(T_INST|T_RIO, " xmit: toid=%d, fromid=%d\n", xmit.toid, xmit.fromid); + + /* check for unreasonable situations */ + + if (xmit.fromid != myid) { + printf("PNC: xmit fromid=0x%02x != myid=0x%02x\n", xmit.fromid, myid); + fatal(NULL); + } + + /* bump the DMA registers to show the transfer */ + + regs.sym.regdmx[xmit.dmareg] += xmit.dmanw; /* bump xmit count */ + regs.sym.regdmx[xmit.dmareg+1] += xmit.dmanw; /* and address */ + + /* the Primenet startup code sends a single ring packet from me + to me, to verify that my node id is unique. This packet must + be received with the ACK bit clear, meaning no (other) node + ack'd the packet. All other cases of loopback are handled at + higher levels within Primos. + + So, if this xmit is to me and there is a receive pending and + room in the receive buffer, put the packet directly in my + receive buffer. If we can't receive it now, set WACK xmit + status. */ + + if (xmit.toid == myid) { + if (rcv.state == PNCBSRDY && rcv.dmanw >= xmit.dmanw) { + memcpy(rcv.memp, xmit.memp, xmit.dmanw*2); + regs.sym.regdmx[rcv.dmareg] += xmit.dmanw; /* bump recv count */ + regs.sym.regdmx[rcv.dmareg+1] += xmit.dmanw; /* and address */ + pncstat |= PNCNSRCVINT; /* set recv interrupt bit */ + rcvstat = 0; /* set receive status (no ack!) */ + xmitstat = 0; /* same for transmit status */ + rcv.state = PNCBSIDLE; /* no longer ready to recv */ + } else { + xmitstat = PNCXSWACK; /* no receive, wack it */ + } + } else { /* regular transmit */ + + /* add leading and trailing byte counts to the packet */ + + len = xmit.dmanw*2 + 4; + *(short *)(xmit.iobuf+0) = len; + memcpy(xmit.iobuf+2, xmit.memp, xmit.dmanw*2); + *(short *)(xmit.iobuf+2+xmit.dmanw*2) = len; + + /* send packet, set transmit interrupt, return */ + + xmitstat = pncxmit(&xmit); + } + pncstat |= PNCNSXMITINT; /* set xmit int */ + pncstat |= PNCNSTOKEN; /* and token seen */ + goto intrexit; + + } else if (func == 016) { /* set interrupt vector */ + pncvec = crs[A]; + TRACE(T_INST|T_RIO, " interrupt vector = '%o\n", pncvec); + IOSKIP; + + } else if (func == 017) { /* set my node ID */ + myid = crs[A] & 0xFF; + pncstat = (pncstat & 0xFF00) | myid; + TRACE(T_INST|T_RIO, " my node id is %d\n", myid); + IOSKIP; + + } else { + printf("Unimplemented OTA device '%02o function '%02o, A='%o\n", device, func, crs[A]); + fatal(NULL); + } + break; + + case 4: + TRACE(T_RIO, " POLL '%02o%02o\n", func, device); + + time(&timenow); + pncaccept(timenow); /* accept 1 new connection each poll */ + pncconnect(timenow); /* try to connect to a disconnected node */ + + /* set default repoll and take any pending interrupt */ + + devpoll[device] = PNCPOLL*gvp->instpermsec; + +intrexit: + if (enabled && (pncstat & 0xC000)) { + if (gvp->intvec == -1) + gvp->intvec = pncvec; + else + devpoll[device] = 100; + } + break; + + default: + fatal("Bad func in devpcn"); + } + +#else +int devpnc (int class, int func, int device) { + return -1; +#endif diff --git a/em.c b/em.c index 7c318a8..805f348 100755 --- a/em.c +++ b/em.c @@ -519,6 +519,8 @@ typedef struct { void *disp_rmr[128]; /* R-mode memory ref dispatch table */ void *disp_vmr[128]; /* V-mode memory ref dispatch table */ +#ifndef NOTRACE + /* traceflags is the variable used to test tracing of each instruction traceuser is the user number to trace, 0 meaning any user traceseg is the procedure segment number to trace, 0 meaning any @@ -534,6 +536,7 @@ typedef struct { int traceuser; /* OWNERL to trace */ int traceseg; /* RPH segment # to trace */ int numtraceprocs; /* # of procedures we're tracing */ +#endif } gv_t; @@ -1757,7 +1760,28 @@ static int (*devmap[64])(int, int, int) = { #else #if 0 -/* this is the "full system" controller configuration */ +/* this is the "full system" configuration supported by the emulator */ + + '04 = devasr: system console + '07 = devpnc: Primenet Node Controller aka PNC (Ringnet) + '14 = devmt: mag tape controller (4 drives) + '15 = devamlc: 5th AMLC (16 lines) + '16 = devamlc: 6th AMLC (16 lines) + '17 = devamlc: 7th AMLC (16 lines) + '20 = devcp: clock / VCP / SOC + '22 = devdisk: 3rd disk controller (8 drives) + '23 = devdisk: 4th disk controller (8 drives) + '24 = devdisk: 5th disk controller (8 drives) + '25 = devdisk: 6th disk controller (8 drives) + '26 = devdisk: 1st disk controller (8 drives) + '27 = devdisk: 2nd disk controller (8 drives) + '32 = devamlc: 8th AMLC (16 lines) + '35 = devamlc: 4th AMLC (16 lines) + '45 = devdisk: 7th disk controller (8 drives) + '46 = devdisk: 8th disk controller (8 drives) + '52 = devamlc: 3rd AMLC (16 lines) + '53 = devamlc: 2nd AMLC (16 lines) + '54 = devamlc: 1st AMLC (16 lines) static int (*devmap[64])(int, int, int) = { /* '0x */ devnone,devnone,devnone,devnone,devasr,devnone,devnone,devpnc, @@ -1771,19 +1795,19 @@ static int (*devmap[64])(int, int, int) = { #else -/* this is the "typical system" controller configuration: +/* this is a "typical system" controller configuration: '04 = devasr: system console + '07 = devpnc: Primenet Node Controller aka PNC (Ringnet) '14 = devmt: mag tape controller (4 drives) '20 = devcp: clock / VCP / SOC - '26 = devdisk: 1st disk controller (4/8 drives) - '27 = devdisk: 2nd disk controller (4/8 drives) + '26 = devdisk: 1st disk controller (8 drives) + '27 = devdisk: 2nd disk controller (8 drives) '54 = 1st amlc (terminal) controller (16 lines) - '53 = 2nd amlc (terminal) controller (16 lines) */ static int (*devmap[64])(int, int, int) = { - /* '0x */ devnone,devnone,devnone,devnone,devasr,devnone,devnone,devnone, + /* '0x */ devnone,devnone,devnone,devnone,devasr,devnone,devnone,devpnc, /* '1x */ devnone,devnone,devnone,devnone,devmt,devnone, devnone, devnone, /* '2x */ devcp,devnone,devnone,devnone,devnone,devnone,devdisk,devdisk, /* '3x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone, @@ -1864,7 +1888,9 @@ static void fatal(char *msg) { for (i=0; i<64; i++) devmap[i](-2, 0, i); +#ifndef NOTRACE fclose(gvp->tracefile); +#endif if (lseek(2, 0, SEEK_END) > 0) printf("Check error.log for more information\n"); exit(1); @@ -3227,10 +3253,6 @@ static ors(unsigned short pcbw) { crsl = regs.sym.userregs[rs]; crs[MODALS] = modals; TRACE(T_PX, "ors: rs = %d, reg set in modals = %d, modals = %o\n", rs, (crs[MODALS] & 0340)>>5, crs[MODALS]); -#if 0 - if (rs > 1) - gvp->savetraceflags = ~0; -#endif /* invalidate the mapva translation cache */ @@ -3714,9 +3736,6 @@ static lpsw() { gvp->livereglim = 010; } else gvp->livereglim = 040; -#if 0 - gvp->savetraceflags |= T_FLOW; /****/ -#endif if (crs[MODALS] & 010) { TRACE(T_PX, "Process exchange enabled:\n"); TRACE(T_PX, "LPSW: PLA=%o, PCBA=%o, PLB=%o, PCBB=%o\n", regs.sym.pla, regs.sym.pcba, regs.sym.plb, regs.sym.pcbb); @@ -3750,8 +3769,6 @@ static sssn() { ea_t ea; int i; - //printf("SSSN @ %o/%o\n", RPH, RPL); - //gvp->savetraceflags = gvp->traceflags = ~T_MAP; /*****/ TRACE(T_FLOW, " SSSN\n"); ea = *(unsigned int *)(crs+XB); for (i=0; i<8; i++) @@ -4344,11 +4361,13 @@ main (int argc, char **argv) { gvp->mapvamisses = 0; gvp->supercalls = 0; gvp->supermisses = 0; +#ifndef NOTRACE gvp->traceflags = 0; gvp->savetraceflags = 0; gvp->traceuser = 0; gvp->traceseg = 0; gvp->numtraceprocs = 0; +#endif invalidate_brp(); eap = &gvp->brp[0]; @@ -4360,12 +4379,16 @@ main (int argc, char **argv) { signal (SIGTERM, sensorcheck); +#ifndef NOTRACE + /* open trace log */ if ((gvp->tracefile=fopen("trace.log", "w")) == NULL) { perror("Unable to open trace.log"); exit(1); } + setlinebuf(gvp->tracefile); +#endif /* initialize dispatch tables */ @@ -4504,7 +4527,9 @@ main (int argc, char **argv) { tport = templ; } else fatal("-tport needs an argument\n"); +#endif +#ifndef NOTRACE } else if (strcmp(argv[i],"-trace") == 0) { while (i+1 < argc && argv[i+1][0] != '-') { i++; @@ -4593,6 +4618,7 @@ main (int argc, char **argv) { /* finish setting up tracing after all options are read, ie, maps */ +#ifndef NOTRACE if (gvp->traceuser != 0) TRACEA("Tracing enabled for OWNERL %o\n", gvp->traceuser); else @@ -4614,7 +4640,8 @@ main (int argc, char **argv) { printf("Can't find procedure %s in load maps for tracing.\n", traceprocs[i].name); } } - +#endif + /* set some vars after the options have been read */ if (cpuid == 15 || cpuid == 18 || cpuid == 19 || cpuid == 24 || cpuid >= 26) { @@ -5462,7 +5489,6 @@ d_calf: /* 000705 */ zlen-- d_zmv: /* 001114 */ - //gvp->traceflags = -1; /***/ stopwatch_push(&sw_zmv); TRACE(T_FLOW, " ZMV\n"); zspace = 0240; @@ -5489,13 +5515,11 @@ d_zmv: /* 001114 */ TRACE(T_FLOW, " zch1=%o (%c)\n", zch1, zch1&0x7f); ZPUTC(zea2, zlen2, zcp2, zclen2, zch1); } - gvp->traceflags = 0; stopwatch_pop(&sw_zmv); goto fetch; d_zmvd: /* 001115 */ stopwatch_push(&sw_zmvd); - //gvp->traceflags = -1; /***/ TRACE(T_FLOW, " ZMVD\n"); zlen1 = GETFLR(1); zlen2 = zlen1; @@ -5544,7 +5568,6 @@ d_zmvd: /* 001115 */ #endif } stopwatch_pop(&sw_zmvd); - gvp->traceflags = 0; goto fetch; /* NOTE: ZFIL is used early after PX enabled, and can be used to cause @@ -6172,6 +6195,12 @@ d_hlt: /* 000000 */ TRACE(T_FLOW, " HLT\n"); RESTRICT(); memdump(0,0xFFFF); + if (((crs[KEYS] & 016000) >> 10) <= 3) { + printf("\nCPU halt, instruction #%lu at %o/%o %s: %o %o\nA='%o/%d B='%o/%d L='%o/%d X=%o/%d", gvp->instcount, RPH, RPL, searchloadmap(gvp->prevpc,' '), get16t(gvp->prevpc), get16t(gvp->prevpc+1), crs[A], *(short *)(crs+A), crs[B], *(short *)(crs+B), *(unsigned int *)(crs+A), *(int *)(crs+A), crs[X], *(short *)(crs+X)); + printf("\nPress Enter to continue... "); + getchar(); + goto fetch; + } fatal("CPU halt"); d_pim: /* 000205 (R-mode) */ @@ -7391,7 +7420,6 @@ d_smcs: /* 0101200 */ d_badgen: TRACEA(" unrecognized generic instruction!\n"); printf("em: #%lu %o/%o: Unrecognized generic instruction '%o!\n", gvp->instcount, RPH, RPL, inst); - //gvp->traceflags = ~T_MAP; fault(UIIFAULT, RPL, RP); fatal(NULL); @@ -8903,6 +8931,7 @@ imode: imodepcl: stopwatch_push(&sw_pcl); TRACE(T_FLOW|T_PCL, "#%lu %o/%0o: PCL %o/%o %s\n", gvp->instcount, RPH, RPL-2, ea>>16, ea&0xFFFF, searchloadmap(ea, 'e')); +#ifndef NOTRACE if (gvp->numtraceprocs > 0 && TRACEUSER) for (i=0; inumtraceprocs; i++) if (traceprocs[i].ecb == (ea & 0xFFFFFFF) && traceprocs[i].sb == -1) { @@ -8912,6 +8941,7 @@ imodepcl: printf("Enabled trace for %s at sb '%o/%o\n", traceprocs[i].name, crs[SBH], crs[SBL]); break; } +#endif pcl(ea); stopwatch_pop(&sw_pcl); break; diff --git a/emdev.h b/emdev.h index 91880e7..1bfca31 100644 --- a/emdev.h +++ b/emdev.h @@ -13,51 +13,51 @@ '01 = paper tape reader '02 = paper tape punch '03 = #1 MPC/URC (line printer/card reader/card punch) - '04 = SOC/Option A/VCP board (system console/user terminal) +* '04 = SOC/Option A/VCP board (system console/user terminal) '05 = #2 MPC/URC (line printer/card reader/card punch) '06 = card punch? (RTOS User Guide, A-1) / IPC (Engr Handbook p.101) '06 = Interproc. Channel (IPC) (R20 Hacker's Guide) - '07 = #1 PNC +* '07 = #1 PNC '10 = ICS2 #1 or ICS1 '11 = ICS2 #2 or ICS1 '12 = floppy disk/diskette (magtape controller #3 at rev 22) '13 = #2 magtape controller - '14 = #1 magtape controller - '15 = #5 AMLC or ICS1 - '16 = #6 AMLC or ICS1 - '17 = #7 AMLC or ICS1 - '20 = control panel / real-time clock +* '14 = #1 magtape controller (emulator only supports this one) +* '15 = #5 AMLC or ICS1 +* '16 = #6 AMLC or ICS1 +* '17 = #7 AMLC or ICS1 +* '20 = control panel / real-time clock '21 = 1st 4002 (Option B') disk controller - '22 = disk #3 (0-7) - '23 = disk #4 - '24 = disk #0 (0-7; was Writable Control Store) - '25 = disk #2 (0-7; was 4000 disk controller) - '26 = disk #1 (0-7) - '27 = #7 disk (0-7) +* '22 = disk #3 (0-7) +* '23 = disk #4 +* '24 = disk #0 (0-7; was Writable Control Store) +* '25 = disk #2 (0-7; was 4000 disk controller) +* '26 = disk #1 (0-7) +* '27 = #7 disk (0-7) '30-32 = BPIOC #1-3 (RTOS User Guide, A-1) - '32 = AMLC #8 or ICS1 +* '32 = AMLC #8 or ICS1 '33 = #1 Versatec '34 = #2 Versatec - '35 = #4 AMLC or ICS1 +* '35 = #4 AMLC or ICS1 '36-37 = ELFBUS #1 & 2 (ICS1 #1, ICS1 #2) '40 = A/D converter type 6000 '41 = digital input type 6020 '42 = digital input #2 '43 = digital output type 6040 '44 = digital output #2 - '45 = disk #4 (0-7; was D/A converter type 6060 (analog output) - obsolete) - '46 = disk #6 (0-7) +* '45 = disk #4 (0-7; was D/A converter type 6060 (analog output) - obsolete) +* '46 = disk #6 (0-7) '47 = #2 PNC '50 = #1 HSSMLC/MDLC (synchronous comm) '51 = #2 HSSMLC/MDLC - '52 = #3 AMLC or ICS1 - '53 = #2 AMLC - '54 = #1 AMLC +* '52 = #3 AMLC or ICS1 +* '53 = #2 AMLC +* '54 = #1 AMLC '55 = MACI autocall unit '56 = old SMLC (RTOS User Guide, A-1 & Hacker's Guide) '60-67 = reserved for user devices (GPIB) '70-'73 = Megatek graphics terminals - '75-'76 = T$GPPI + '75-'76 = MPC4 / T$GPPI programmable controller Devices emulated by Primos in ks/ptrap.ftn for I/O instructions in Ring 3: '01 = paper tape reader @@ -414,7 +414,9 @@ int devasr (int class, int func, int device) { timeout.tv_sec = 0; /* yes, can't delay */ else { timeout.tv_sec = 1; /* single user: okay to delay */ +#ifndef NOTRACE fflush(gvp->tracefile); /* flush for DIAG testing */ +#endif } timeout.tv_usec = 0; FD_SET(ttydev, &fds); @@ -449,7 +451,9 @@ int devasr (int class, int func, int device) { } ttyflags = newflags; if (doblock) { /* doblock = no PX = running diags */ +#ifndef NOTRACE fflush(gvp->tracefile); /* flush trace buffer when testing */ +#endif if (needflush) { if (fflush(stdout) == 0) { needflush = 0; @@ -516,7 +520,9 @@ readasr: if (!(terminfo.c_lflag & ECHO) && ch != 015) /* log all except CR */ fputc(ch, conslog); fflush(conslog); /* immediately flush when typing */ +#ifndef NOTRACE fflush(gvp->tracefile); +#endif IOSKIP; } else { printf("Unexpected error reading from tty, n=%d\n", n); @@ -2541,7 +2547,7 @@ int devamlc (int class, int func, int device) { fatal(NULL); } } else - fprintf(stderr, "-tport is zero, can't start AMLC devices\n"); + fprintf(stderr, "-tport is zero, AMLC devices not started\n"); inited = 1; } @@ -3275,749 +3281,6 @@ endconnect: } #endif +#include "devpnc.h" -/* PNC (ring net) device handler - - On a real Prime ring network, each node has a unique node id from 1 - to 254. The node id is configured with software and stored into the - PNC during network initialization. Before being set, the node id of - a master-cleared PNC is zero. The broadcast node id is 255 (all - PNC's accept packets with this "to" id). In practice, CONFIG_NET - limits the node id to 247. When a node starts, it sends a message - to itself. If any system acks this packet, it means the node id is - already in use, and the new node will disconnect from the ring. - Since all systems in a ring have to be physically cabled together, - there was usually a common network administration to ensure that no - two nodes had the same node id. - - Prior to rev 19.3, each node sends periodic "timer" messages to all - nodes it knows about. This is obviously very inefficient. - Beginning with 19.3, An "I'm alive" broadcast message is sent every - 10 seconds to let all nodes know that a machine is still up. - - For use with the emulator, the unique node id concept doesn't make a - lot of sense. If a couple of guys running the emulator wanted to - have a 2-node network with nodes 1 and 2 (network A), and another - couple of guys had a 2-node network with nodes 1 and 2 (network B), - then it wouldn't be possible for a node in one network to add a node - in the other network without other people redoing their node ids to - ensure uniqueness. To get around this, the emulator has a config - file RING.CFG that lets you say "Here are the guys in *my* ring - (with all unique node ids), here is each one's IP address and port, - my node id on their ring is *blah* (optional). This allows the node - id to be a per-host number that only needs to be coordinated with - two people that want to talk to each other, and effectively allows - one emulator to be in multiple rings simulataneously. Cool, eh? - - PNC ring buffers are 256 words, with later versions of Primos also - supporting 512, and 1024 word packets. "nbkini" (rev 18) allocates - 12 ring buffers + 1 for every 2 nodes in the ring. Both the 1-to-2 - queue for received packets, and 2-to-1 queue for xmit packets have - 63 entries. PNC ring buffers are wired and never cross page - boundaries. - - The actual xmit/recv buffers for PNC are 256-1024 words, and each - buffer has an associated "block header", stored in a different - location in system memory, that is 8 words. - - The BH fields are (16-bit words): - 0: type field (1) - 1: free pool id (3 for ring buffers) - 2-3: pointer to data block - - 4-7 are available for use by the drivers. For PNC: - 4: number of words received (calculated by pncdim based on DMA regs) - 5: receive status word - 6: data 1 - 7: data 2 - - The PNC data buffer has a 2-word header, followed by the data: - 0: To (left) and From (right) bytes containing node-ids - 1: "Type" word. - Bit 1 set = odd number of bytes (if set, last word is only 1 byte) - Bit 7 set = normal data messages (otherwise, a timer message) - Bit 16 set = broadcast timer message - PNC data buffers never cross page boundaries. - - Primos PNC usage: - - OCP '0007 - - disconnect from ring - - OCP '0207 - - inject a token into the ring - - OCP '0507 - - set PNC into "delay" mode (token recovery BS) - - OCP '1007 - - stop any xmit in progress - - - INA '1707 - - read network status word - - does this in a loop until "connected" bit is clear after disconnect above - - PNC status word: - bit 1 set if receive interrupt (rcv complete) - bit 2 set if xmit interrupt (xmit complete) - bit 3 set if "PNC booster" (PNC II) - bit 4 set if PNC II is in PNC II mode - bit 5 set if u-verify failed or bad command issued - bit 6 set if connected to ring - bit 7 set if multiple tokens detected (only after xmit EOR) (*) - bit 8 set if token detected (only after xmit EOR) - bits 9-16 controller node ID - - INA '1207 - - read receive status word - bit 1 set for previous ACK (*) - bit 2 set for multiple previous ACK (*) - bit 3 set for previous WACK (receiving node not ready to receive) - bit 4 set for previous NACK (packet received incorrectly) - bits 5-6 unused - bit 7 ACK byte parity error (*) - bit 8 ACK byte check error (parity on bits 1-6) (*) - bit 9 recv buffer parity error (*) - bit 10 recv busy - bit 11 end of range before end of message (received msg was too big - for the receive buffer) - bits 12-16 unused - - INA '1307 - - read xmit status word - bit 1 set for ACK - bit 2 set for multiple ACK (more than 1 node accepted packet) (*) - bit 3 set for WACK - bit 4 set for NACK (bad CRC) (*) - bit 5 unused - bit 6 parity bit of ACK (PNC only) (*) - bit 7 ACK byte parity error (*) - bit 8 ACK byte check error (parity on bits 1-6) (*) - bit 9 xmit buffer parity error (*) - bit 10 xmit busy - bit 11 packet did not return - bit 12 packet returned with bits 6-8 nonzero (*) - bit 13 retry not successful (PNC II) - bits 14-16 retry count (PNC II; zero on PNC) - - (*) = unused in the emulator - - OCP '1707 - - initialize - - OTA '1707 - - set my node ID (in A register) - - if this fails, no PNC present - - OTA '1607 - - set interrupt vector (in A reg) - - OTA '1407 - - initiate receive, dma channel in A - - OTA '1507 - - initiate xmit, dma channel in A - - OCP '0107 - - connect to the ring - - (Primos follows this with INA '1707 loop until "connected" bit is set) - - OCP '1407 - - ack receive (clears recv interrupt request) - - OCP '0407 - - ack xmit (clears xmit interrupt request) - - OCP '1507 - - set interrupt mask (enable interrupts) - - OCP '1107 - - stop receive in progress - -*/ - -int devpnc (int class, int func, int device) { - -#if 0 - -#define PNCPOLL 100 - - /* PNC controller status bits */ -#define PNCRCVINT 0x8000 /* bit 1 rcv interrupt (rcv complete) */ -#define PNCXMITINT 0x4000 /* bit 2 xmit interrupt (xmit complete) */ -#define PNCBOOSTER 0x2000 /* bit 3 PNC booster = 1 */ - -#define PNCCONNECTED 0x400 /* bit 6 */ -#define PNCTWOTOKENS 0x200 /* bit 7, only set after xmit EOR */ -#define PNCTOKDETECT 0x100 /* bit 8, only set after xmit EOR */ - - /* xmit/recv states */ - -#define XR_IDLE 0 /* initial state: no xmit or recv */ -#define XR_READY 1 /* ready to recv or xmit */ -#define XR_XFER 3 /* transferring data over socket */ - -#define MINCONNTIME 30 /* wait 30 seconds between connect attempts */ -#define MAXPKTBYTES 2048 /* max of 2048 byte packets */ - - static short configured = 0; /* true if PNC configured */ - static unsigned short pncstat; /* controller status word */ - static unsigned short recvstat; /* receive status word */ - static unsigned short xmitstat; /* xmit status word */ - static unsigned short pncvec; /* PNC interrupt vector */ - static unsigned short myid; /* my PNC node id */ - static unsigned short enabled; /* interrupts enabled flag */ - static int pncfd; /* socket fd for all PNC network connections */ - - /* the ni structure contains the important information for each node - in the network and is indexed by the local node id */ - - static struct { /* node info for each node in my network */ - short cfg; /* true if seen in ring.cfg */ - short fd; /* socket fd for this node, -1 if unconnected */ - char ip[16]; /* IP address of the remote node */ - short port; /* emulator network port on the remote node */ - short myremid; /* my node ID on the remote node's ring network */ - time_t conntime; /* time of last connect request */ - } ni[256]; - - /* array to map socket fd's to local node id's for accessing the ni - structure. Not great programming, because the host OS could - choose to use large fd's, which will cause a runtime error */ - -#define FDMAPSIZE 1024 - static short fdnimap[FDMAPSIZE]; - - typedef struct { - short state, offset; - unsigned short dmachan, dmareg, dmaaddr; - short dmanw, dmabytesleft, toid, fromid, remtoid, remfromid; - unsigned char iobuf[MAXPKTBYTES+2]; - } t_dma; - static t_dma recv, xmit; - - short i; - unsigned short access, dmaword; - unsigned short *iobufp; - time_t timenow; - struct hostent* server; - - struct sockaddr_in addr; - int fd, optval, fdflags; - unsigned int addrlen; - - FILE *ringfile; - char *tok, buf[128], *p; - int n, linenum; - int tempid, tempmyremid, tempyourremid, tempport, cfgerrs; - char tempip[22]; /* xxx.xxx.xxx.xxx:ppppp */ -#define DELIM " \t\n" -#define PDELIM ":" - - //gvp->traceflags = ~T_MAP; - - if (nport <= 0) { - if (class == -1) - fprintf(stderr, "-nport is zero, PNC not started\n"); - else - TRACE(T_INST|T_RIO, "nport <= 0, PIO to PNC ignored, class=%d, func='%02o, device=%02o\n", class, func, device); - return -1; - } - - switch (class) { - - case -1: - for (i=0; i 247) { - fprintf(stderr,"Line %d of ring.cfg: node id is out of range 1-247\n", linenum); - continue; - } - if (ni[tempid].cfg) { - fprintf(stderr,"Line %d of ring.cfg: node id occurs twice\n", linenum); - continue; - } - if ((p=strtok(NULL, DELIM)) == NULL) { - fprintf(stderr,"Line %d of ring.cfg: IP address missing\n", linenum); - continue; - } - if (strlen(p) > 21) { - fprintf(stderr,"Line %d of ring.cfg: IP address is too long\n", linenum); - continue; - } - strcpy(tempip, p); - if ((p=strtok(NULL, DELIM)) != NULL) { - tempmyremid = atoi(p); - if (tempmyremid < 1 || tempmyremid > 247) { - fprintf(stderr,"Line %d of ring.cfg: my remote node id out of range 1-247\n", linenum); - continue; - } - } else - tempmyremid = -1; - - /* parse the port number from the IP address */ - - tempport = -1; - if ((p=strtok(tempip, PDELIM)) != NULL) { - strcpy(ni[tempid].ip, p); - if ((p=strtok(NULL, PDELIM)) != NULL) { - tempport = atoi(p); - if (tempport < 1 || tempport > 32000) - fprintf(stderr,"Line %d of ring.cfg: port number is out of range 1-32000\n", linenum); - } - } - if (tempport == -1) { - fprintf(stderr, "Line %d of ring.cfg: IP should be xxx.xxx.xxx.xxx:pppp\n", linenum); - continue; - } - ni[tempid].cfg = 1; - ni[tempid].myremid = tempmyremid; - ni[tempid].port = tempport; - TRACE(T_RIO, "Line %d: id=%d, ip=\"%s\", port=%d, myremid=%d\n", linenum, tempid, tempip, tempport, tempmyremid); - configured = 1; - } - if (!feof(ringfile)) { - perror(" error reading ring.cfg"); - fatal(NULL); - } - fclose(ringfile); - } - if (!configured) { - fprintf(stderr, "PNC not configured because ring.cfg missing or errors occurred.\n"); - return -1; - } - return 0; - - case 0: - - /* OCP '0700 - disconnect */ - - if (func == 00) { - TRACE(T_INST|T_RIO, " OCP '%02o%02o - disconnect\n", func, device); - if (pncfd >= 0) { - close(pncfd); - pncfd = -1; - } - for (i=0; i<256; i++) { - if (ni[i].fd >= 0) { - fdnimap[ni[i].fd] = -1; - close(ni[i].fd); - ni[i].fd = -1; - } - } - xmit.state = XR_IDLE; - recv.state = XR_IDLE; - pncstat &= ~PNCCONNECTED; - - /* OCP '0701 - connect - If any errors occur while trying to setup the listening socket, - it seems reasonable to leave the PNC unconnected and continue, - but this will cause Primos (rev 19) to hang in a spin loop. So - for now, bomb. Later, we could put the PNC in a disabled state, - where INA/OTA don't skip, since Primos handles this better. - */ - - } else if (func == 01) { /* connect to the ring */ - TRACE(T_INST|T_RIO, " OCP '%02o%02o - connect\n", func, device); - - /* start listening on the network port */ - - pncfd = socket(AF_INET, SOCK_STREAM, 0); - if (pncfd == -1) { - perror("socket failed for PNC"); - fatal(NULL); - } - if (fcntl(pncfd, F_GETFL, fdflags) == -1) { - perror("unable to get ts flags for PNC"); - fatal(NULL); - } - fdflags |= O_NONBLOCK; - if (fcntl(pncfd, F_SETFL, fdflags) == -1) { - perror("unable to set ts flags for PNC"); - fatal(NULL); - } - optval = 1; - if (setsockopt(pncfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))) { - perror("setsockopt failed for PNC"); - fatal(NULL); - } - addr.sin_family = AF_INET; - addr.sin_port = htons(nport); - addr.sin_addr.s_addr = INADDR_ANY; - if(bind(pncfd, (struct sockaddr *)&addr, sizeof(addr))) { - perror("bind: unable to bind for PNC"); - fatal(NULL); - } - if(listen(pncfd, 10)) { - perror("listen failed for PNC"); - fatal(NULL); - } - pncstat |= PNCCONNECTED; - - } else if (func == 02) { /* inject a token */ - TRACE(T_INST|T_RIO, " OCP '%02o%02o - inject token\n", func, device); - - } else if (func == 04) { /* ack xmit (clear xmit int) */ - TRACE(T_INST|T_RIO, " OCP '%02o%02o - ack xmit int\n", func, device); - pncstat &= ~PNCXMITINT; /* clear "xmit interrupting" */ - pncstat &= ~PNCTOKDETECT; /* clear "token detected" */ - xmitstat = 0; - xmit.state = XR_IDLE; - - } else if (func == 05) { /* set PNC into "delay" mode */ - TRACE(T_INST|T_RIO, " OCP '%02o%02o - set delay mode\n", func, device); - - } else if (func == 010) { /* stop xmit in progress */ - TRACE(T_INST|T_RIO, " OCP '%02o%02o - stop xmit\n", func, device); - xmitstat = 0; - xmit.state = XR_IDLE; - - } else if (func == 011) { /* stop recv in progress */ - TRACE(T_INST|T_RIO, " OCP '%02o%02o - stop recv\n", func, device); - - } else if (func == 012) { /* set normal mode */ - TRACE(T_INST|T_RIO, " OCP '%02o%02o - set normal mode\n", func, device); - - } else if (func == 013) { /* set diagnostic mode */ - TRACE(T_INST|T_RIO, " OCP '%02o%02o - set diag mode\n", func, device); - - } else if (func == 014) { /* ack receive (clear rcv int) */ - TRACE(T_INST|T_RIO, " OCP '%02o%02o - ack recv int\n", func, device); - pncstat &= ~PNCRCVINT; - recvstat = 0; - recv.active = 0; - - } else if (func == 015) { /* set interrupt mask (enable int) */ - TRACE(T_INST|T_RIO, " OCP '%02o%02o - enable int\n", func, device); - enabled = 1; - - } else if (func == 016) { /* clear interrupt mask (disable int) */ - TRACE(T_INST|T_RIO, " OCP '%02o%02o - disable int\n", func, device); - enabled = 0; - - } else if (func == 017) { /* initialize */ - TRACE(T_INST|T_RIO, " OCP '%02o%02o - initialize\n", func, device); - /* XXX: this needs to disconnect from ring! */ - pncstat = pncstat & 0xff; /* keep node id across inits */ - recvstat = 0; - xmitstat = 0; - pncvec = 0; - enabled = 0; - xmit.state = XR_IDLE; - recv.state = XR_IDLE; - - } else { - printf("Unimplemented OCP device '%02o function '%02o\n", device, func); - fatal(NULL); - } - break; - - case 1: - TRACE(T_INST|T_RIO, " SKS '%02o%02o\n", func, device); - if (func == 99) - IOSKIP; /* assume it's always ready */ - else { - printf("Unimplemented SKS device '%02o function '%02o\n", device, func); - fatal(NULL); - } - break; - - case 2: - if (func == 011) { /* input ID */ - TRACE(T_INST|T_RIO, " INA '%02o%02o - input ID\n", func, device); - crs[A] = 07; - IOSKIP; - - } else if (func == 012) { /* read receive status word */ - TRACE(T_INST|T_RIO, " INA '%02o%02o - get recv status '%o\n", func, device, recvstat); - crs[A] = recvstat; - IOSKIP; - - } else if (func == 013) { /* DIAG - read static register; not impl. */ - crs[A] = 0; - IOSKIP; - - } else if (func == 014) { /* read xmit status word */ - TRACE(T_INST|T_RIO, " INA '%02o%02o - get xmit status '%o\n", func, device, xmitstat); - crs[A] = xmitstat; - IOSKIP; - - } else if (func == 017) { /* read controller status word */ - TRACE(T_INST|T_RIO, " INA '%02o%02o - get ctrl status '%o\n", func, device, pncstat); - crs[A] = pncstat; - IOSKIP; - - } else { - printf("Unimplemented INA device '%02o function '%02o\n", device, func); - fatal(NULL); - } - break; - - case 3: - TRACE(T_INST|T_RIO, " OTA '%02o%02o\n", func, device); - if (func == 011) { /* DIAG - single step; not impl.*/ - IOSKIP; - - } else if (func == 014) { /* initiate recv, dma chan in A */ - recvstat = 0x0040; /* set receive busy */ - recv.dmachan = crs[A]; - recv.dmareg = recv.dmachan << 1; - recv.dmanw = regs.sym.regdmx[recv.dmareg]; - if (recv.dmanw <= 0) - recv.dmanw = -(recv.dmanw>>4); - else - recv.dmanw = -((recv.dmanw>>4) ^ 0xF000); - recv.dmaaddr = regs.sym.regdmx[recv.dmareg+1]; - recv.iobufp = mem + mapio(recv.dmaaddr); - recv.state = XR_READY; - recv.offset = -1; /* initialize for new packet */ - TRACE(T_INST|T_RIO, " recv: dmachan=%o, dmareg=%o, dmaaddr=%o, dmanw=%d\n", recv.dmachan, recv.dmareg, recv.dmaaddr, recv.dmanw); - devpoll[device] = 10; - IOSKIP; - - } else if (func == 015) { /* initiate xmit, dma chan in A */ - if (xmitstat & 0x0040) { /* already busy? */ - warn("pnc: xmit when already busy!"); - return; /* yes, return and don't skip */ - } - xmitstat = 0x0040; /* set xmit busy */ - xmit.dmachan = crs[A]; - xmit.dmareg = xmit.dmachan<<1; - xmit.dmanw = regs.sym.regdmx[xmit.dmareg]; - if (xmit.dmanw <= 0) - xmit.dmanw = -(xmit.dmanw>>4); - else - xmit.dmanw = -((xmit.dmanw>>4) ^ 0xF000); - xmit.dmaaddr = regs.sym.regdmx[xmit.dmareg+1]; - TRACE(T_INST|T_RIO, " xmit: dmachan=%o, dmareg=%o, dmaaddr=%o, dmanw=%d\n", xmit.dmachan, xmit.dmareg, xmit.dmaaddr, xmit.dmanw); - - /* read the first word, the to and from node id's, and map them - to the remote hosts' expected to and from node id's */ - - xmit.iobufp = mem + mapva(xmit.dmaaddr, 0, RACC, &access); - dmaword = *xmit.iobufp++; - xmit.toid = dmaword >> 8; - xmit.fromid = dmaword & 0xFF; - TRACE(T_INST|T_RIO, " xmit: toid=%d, fromid=%d\n", xmit.toid, xmit.fromid); - - /* broadcast packets are "I am up" timer msgs and are simply - discarded here, with a succesful transmit status. Node - up/down is handled in the devpnc poll code. - - XXX: should check that this really is the "I am up" msg */ - - if (xmit.toid == 255) { - goto xmitdone1; - } - - /* if this xmit is to me and there is a new receive pending and - there is room left in the receive buffer, put the packet - directly in my receive buffer. If we can't receive it now, - set NACK xmit and receive status. If the packet is not to - me, copy it to the xmit.iobuf and add the header */ - - if (xmit.toid == myid) { - if (recv.state == XR_READY && recv.dmanw >= xmit.dmanw) { - memcpy(recv.iobufp, xmit.iobufp, xmit.dmanw*2); - regs.sym.regdmx[recv.dmareg] += xmit.dmanw; /* bump recv count */ - regs.sym.regdmx[recv.dmareg+1] += xmit.dmanw; /* and address */ - pncstat |= 0x8000; /* set recv interrupt too */ - recv.state = XR_IDLE; /* no longer ready to recv */ - -xmitdone1: - regs.sym.regdmx[xmit.dmareg] += xmit.dmanw; /* and xmit count */ - regs.sym.regdmx[xmit.dmareg+1] += xmit.dmanw; /* and address */ - pncstat |= 0x4100; /* set xmit interrupt + token */ - xmitstat |= 0x8000; /* set ACK xmit status */ - goto xmitdone; - - } else { - xmitstat |= 0x1000; /* set xmit NACK status */ - recvstat |= 0x20; /* set recv premature EOR */ - } - } - - /* check for unreasonable situations */ - - if (xmit.toid == 0) - fatal("PNC: xmit.toid is zero"); - if (xmit.fromid == 0) - fatal("PNC: xmit.fromid is zero"); - if (xmit.fromid != myid) { - printf("PNC: xmit fromid=0x%02x != myid=0x%02x\n", xmit.fromid, myid); - fatal(NULL); - } - - /* map local to and from node id to remote to and from node id */ - - if (ni[xmit.fromid].myremid < 0) - xmit.remfromid = myid; - else - xmit.remfromid = ni[xmit.fromid].myremid; - xmit.state = XR_READY; /* xmit pending */ - xmit.offset = -1; /* initialize for new xmit */ - devpoll[device] = 10; - -xmitdone: - if (enabled && (pncstat & 0xC000)) - if (gvp->intvec == -1) - gvp->intvec = pncvec; - else - devpoll[device] = 100; - IOSKIP; - - } else if (func == 016) { /* set interrupt vector */ - pncvec = crs[A]; - TRACE(T_INST|T_RIO, " interrupt vector = '%o\n", pncvec); - IOSKIP; - - } else if (func == 017) { /* set my node ID */ - myid = crs[A] & 0xFF; - pncstat = (pncstat & 0xFF00) | myid; - TRACE(T_INST|T_RIO, " my node id is %d\n", myid); - if (ni[myid].cfg) - fprintf(stderr, "Warning: my node id of %d is in ring.cfg\n", myid); - strcpy(ni[myid].ip, "127.0.0.1"); - ni[myid].port = nport; - ni[myid].myremid = myid; - IOSKIP; - - } else { - printf("Unimplemented OTA device '%02o function '%02o, A='%o\n", device, func, crs[A]); - fatal(NULL); - } - break; - - case 4: - TRACE(T_INST|T_RIO, " POLL '%02o%02o\n", func, device); - - /* if a transmit is pending, start/continue it until completes */ - - if (xmit.state > XR_IDLE) { - if (ni[xmit.toid].fd == -1) { /* not connected yet */ - if (time(&timenow) - ni[xmit.toid].conntime < MINCONNTIME) { - printf("em: waiting for connection timeout to node %d\n", xmit.toid); - goto xmiterr; - } - if ((ni[xmit.toid].fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - perror ("Unable to create socket"); - exit(1); - } - server = gethostbyname(ni[xmit.toid].ip); - if (server == NULL) { - fprintf(stderr,"pnc: cannot resolve %s\n", ni[xmit.toid].ip); - close(ni[xmit.toid].fd); - ni[xmit.toid].fd = -1; - goto xmiterr; - } - ni[xmit.toid].conntime = timenow; - bzero((char *) &addr, sizeof(addr)); - addr.sin_family = AF_INET; - bcopy((char *)server->h_addr, (char *)&addr.sin_addr.s_addr, server->h_length); - addr.sin_port = htons(ni[xmit.toid].port); - if (connect(ni[xmit.toid].fd, (void *) &addr,(socklen_t) sizeof(addr)) < 0) { - perror("pnc: error connecting to server\n"); - close(ni[xmit.toid].fd); - ni[xmit.toid].fd = -1; - goto xmiterr; - } - xmit.state = XR_XFER; - } - - /* start/continue the transfer */ - - if (xmit.state == XR_XFER) { - if ((n=write(ni[xmit.toid].fd, xmit.iobuf[offset], xmit.dmabytes-xmit.offset)) < 0) { - perror("pnc: write error"); - } else { - xmit.offset += n; - xmit.dmabytesleft -= n; - if (xmit.dmabytesleft == 0) - xmit.state = XR_IDLE; - } - } - -#if 0 - while ((fd = accept(pncfd, (struct sockaddr *)&addr, &addrlen)) == -1 && errno == EINTR) - ; - if (fd == -1) { - if (errno != EWOULDBLOCK) { - perror("accept error for PNC"); - } - } else { - if (fd >= MAXFD) - fatal("New connection fd is too big"); - printf("New PNC connection:\n"); - /* -- new connect request came in -- scan host table to find matching IP -- if already connected, display warning error and ignore - - newdevice = 0; - for (i=0; devices[i] && !newdevice && i