1
0
mirror of https://github.com/prirun/p50em.git synced 2026-03-09 20:01:45 +00:00

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
This commit is contained in:
Jim
2011-07-30 17:06:19 -04:00
parent 6a00d0d937
commit fde9c6e812
4 changed files with 1091 additions and 788 deletions

1011
devpnc.h Normal file

File diff suppressed because it is too large Load Diff

72
em.c
View File

@@ -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; i<gvp->numtraceprocs; 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;

795
emdev.h
View File

@@ -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<FDMAPSIZE; i++)
fdnimap[i] = -1;
for (i=0; i<256; i++) {
ni[i].cfg = 0;
ni[i].fd = -1;
strcpy(ni[i].ip, " ");
ni[i].port = 0;
ni[i].myremid = 0;
}
xmit.state = XR_IDLE;
recv.state = XR_IDLE;
myid = 0; /* set initial node id */
/* read the ring.cfg config file. Each line contains:
localid ip:port [myremoteid]
where:
localid = the remote node's id (1-247) on my ring
ip = the remote emulator's TCP/IP address (or later, name)
port = the remote emulator's TCP/IP PNC port
myremoteid = my node's id (1-247) on the remote ring
The remote id field is optional, and allows the emulator
to be in multiple rings with different administration. If
not specified, myremoteid = my local id.
There cannot be duplicates among the localid field, but there
can be duplicates in the remoteid fields */
linenum = 0;
if ((ringfile=fopen("ring.cfg", "r")) != NULL) {
while (fgets(buf, sizeof(buf), ringfile) != NULL) {
linenum++;
if (strcmp(buf,"") == 0 || buf[0] == ';')
continue;
if ((p=strtok(buf, DELIM)) == NULL) {
fprintf(stderr,"Line %d of ring.cfg: node id missing\n", linenum);
continue;
}
tempid = atoi(p);
if (tempid < 1 || tempid > 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<MAXBOARDS; i++)
for (lx=0; lx<16; lx++)
if (!(dc[devices[i]].dss & BITMASK16(lx+1))) {
newdevice = devices[i];
dc[newdevice].dss |= BITMASK16(lx+1);
dc[newdevice].sockfd[lx] = fd;
//printf("em: AMLC connection, fd=%d, device='%o, line=%d\n", fd, newdevice, lx);
break;
}
if (!newdevice) {
warn("No free AMLC connection");
write(fd, "\rAll AMLC lines are in use!\r\n", 29);
close(fd);
}
xmitdone:
if (xmitstat == 0x0040) { /* complete w/o errors? */
pncstat |= 0x4100; /* set xmit interrupt + token */
xmitstat |= 0x8000; /* yes, set ACK xmit status */
}
#endif
devpoll[device] = PNCPOLL*gvp->instpermsec;
break;
default:
fatal("Bad func in devpcn");
#else
return -1;
#endif
}

View File

@@ -15,7 +15,6 @@ debug:
trace:
cc -arch ppc -DNOREGS -g -O0 -DFAST -DNOMEM -c em.c -fobey-inline -mdynamic-no-pic -I../dongle/mx/ppc/api;g++ -arch ppc -o em em.o ../dongle/mx/ppc/api/libmxmac260.a -framework IOKit -framework CoreFoundation
rm em.o
fixed: