/* DVCH11.C - Emulates CH11 Chaosnet interface for KS10 */ /* $Id: dvch11.c,v 2.3 2001/11/10 21:28:59 klh Exp $ */ /* Copyright © 2005, 2018 Björn Victor and Kenneth L. Harrenstien ** All Rights Reserved ** ** This file may become part of the KLH10 Distribution. Use, modification, and ** re-distribution is permitted subject to the terms in the file ** named "LICENSE", which contains the full text of the legal notices ** and should always accompany this Distribution. ** ** This software is provided "AS IS" with NO WARRANTY OF ANY KIND. ** ** This notice (including the copyright and warranty disclaimer) ** must be included in all copies or derivations of this software. */ /* * $Log: dvch11.c,v $ * Revision 2.3 2001/11/10 21:28:59 klh * Final 2.0 distribution checkin * */ /* NOT just a dummy! ** ** See MIT AI Memo 628, "Chaosnet", by David A. Moon (esp. chapter 7) for documentation. ** Note: chapter 7 ("Hardware Programming Documentation") is not completely correct, ** and should be cross-referenced with the ITS source (SYSTEM;CHAOS). ** E.g. the "timer interrupt enable" bit is "transmit busy", and the ** r/w-only definitions of CSR bits is sometimes wrong (e.g. "transmit ** done"). ** ** Based on dvlhdh.c, with dpchaos.c based on dpimp.c. ** (Some things may still be irrelevant inheritage from dvlhdh, and could be cleaned up...) */ #include "klh10.h" #if !KLH10_DEV_CH11 && CENV_SYS_DECOSF /* Stupid gubbish needed to prevent OSF/1 AXP compiler from ** halting merely because compiled file is empty! */ static int decosfcclossage; #endif #if KLH10_DEV_CH11 /* Moby conditional for entire file */ #include #include #include "kn10def.h" #include "kn10dev.h" #include "prmstr.h" /* For parameter parsing */ #include "dvuba.h" #include "dvch11.h" #include "dpchaos.h" #ifndef KLH10_CH11_USE_GETHOSTBYNAME # define KLH10_CH11_USE_GETHOSTBYNAME 1 #endif #if KLH10_CH11_USE_GETHOSTBYNAME # include #endif #define CHAOSBUFSIZ (DPCHAOS_MAXLEN+500) /* Plenty of slop */ #ifndef CH11_NSUP # define CH11_NSUP 1 /* Only one device supported */ #endif #ifndef CH11_CHIP_MAX # define CH11_CHIP_MAX 10 /* max Chaos/IP mappings - see DPCHAOS_CHIP_MAX */ #endif #if CH11_CHIP_MAX > DPCHAOS_CHIP_MAX # error "DPCHAOS_CHIP_MAX must be at least CH11_CHIP_MAX" #endif #define REG(u) ((u)->ch_reg) /* Chaos/IP mapping entry - see dpchaos_chip */ struct ch_chip { unsigned int ch_chip_chaddr; /* Chaos address */ struct in_addr ch_chip_ipaddr; /* IP address */ in_port_t ch_chip_ipport; /* IP port */ }; struct ch11 { struct device ch_dv; /* Generic 10 device structure */ /* CH11-specific vars */ /* CH11 internal register (only one) */ dvureg_t ch_reg; /* Storage for register (16 bits) */ unsigned short ch_lost; /* # msgs rcved with rcv bfr full */ /* I/O signalling flags */ int ch_ipireq; /* Input side doing PI request */ int ch_opireq; /* Output side doing PI request */ int ch_inactf; /* TRUE if input can be done */ int ch_outactf; /* TRUE if output can be done */ unsigned char *ch_iptr; /* Pointer to input data */ unsigned char *ch_optr; /* Pointer to output data */ /* Misc config info not set elsewhere */ char *ch_ifnam; /* Native platform's interface name */ char *ch_ifmeth; /* Native platform's interface access method */ int ch_dedic; /* TRUE if interface dedicated (else shared) */ int ch_backlog; /* Max # input msgs to queue up in kernel */ unsigned int ch_myaddr; /* My Chaos address */ in_port_t ch_chudp_port; /* CHUDP port to use */ /* DP stuff */ char *ch_dpname; /* Pointer to dev process pathname */ int ch_dpidly; /* # secs to sleep when starting DP */ int ch_dpdbg; /* Initial DP debug flag */ int ch_dpstate; /* TRUE if dev process has finished its init */ struct dp_s ch_dp; /* Handle on dev process */ unsigned char *ch_sbuf; /* Pointers to shared memory buffers */ unsigned char *ch_rbuf; int ch_rcnt; /* # chars in received packet input buffer */ /* Chaos/IP mapping */ int ch_chip_tlen; /* table length */ struct ch_chip ch_chip_tbl[CH11_CHIP_MAX]; }; static int nch11s = 0; struct ch11 dvch11[CH11_NSUP]; /* Can be static, but left external for debugging */ /* Function predecls */ static int ch11_conf(FILE *f, char *s, struct ch11 *ch); static int ch11_init(struct device *d, FILE *of); static int ch11_cmd(register struct device *d, FILE *of, char *cmd); static dvureg_t ch11_pivec(struct device *d); static dvureg_t ch11_read(struct device *d, uint18 addr); static void ch11_write(struct device *d, uint18 addr, dvureg_t val); static void ch11_clear(struct device *d); static void ch11_powoff(struct device *d); static void ch_clear(struct ch11 *ch); static void ch_oint(struct ch11 *ch); static void ch_iint(struct ch11 *ch); static void ch_igo(struct ch11 *ch); static void ch_ogo(struct ch11 *ch); static void ch_idone(struct ch11 *ch); static void ch_odone(struct ch11 *ch); static void showpkt(FILE *f, char *id, unsigned char *buf, int cnt); /* Virtual CHAOS low-level stuff */ /* static */ int chaos_init(struct ch11 *ch, FILE *of); static int chaos_start(struct ch11 *ch); static void chaos_stop(struct ch11 *ch); static void chaos_kill(struct ch11 *ch); static int chaos_incheck(struct ch11 *ch); static void chaos_inxfer(struct ch11 *ch); static int chaos_outxfer(struct ch11 *ch); /* Configuration Parameters */ #define DVCH11_PARAMS \ prmdef(CH11P_DBG, "debug"), /* Initial debug value */\ prmdef(CH11P_BR, "br"), /* BR priority */\ prmdef(CH11P_VEC, "vec"), /* Interrupt vector */\ prmdef(CH11P_ADDR,"addr"), /* Unibus address */\ \ prmdef(CH11P_IFC,"ifc"), /* Ethernet interface name */\ prmdef(CH11P_BKL,"backlog"), /* Max bklog for rcvd pkts (else sys deflt) */\ prmdef(CH11P_DED,"dedic"), /* TRUE= Ifc dedicated (else shared) */\ prmdef(CH11P_DPDLY,"dpdelay"),/* # secs to sleep when starting DP */\ prmdef(CH11P_DPDBG,"dpdebug"), /* Initial DP debug value */\ prmdef(CH11P_DP, "dppath"), /* Device subproc pathname */ \ \ prmdef(CH11P_MYADDR, "myaddr"),/* My Chaosnet address */\ prmdef(CH11P_CHUPORT, "chudpport"), /* CHUDP port to use */\ prmdef(CH11P_CHIP, "chip"), /* Chaos/IP mapping */\ \ prmdef(CH11P_IFMETH, "ifmeth") /* Interface method (chudp, pcap, etc) */ enum { # define prmdef(i,s) i DVCH11_PARAMS # undef prmdef }; static char *ch11prmtab[] = { # define prmdef(i,s) s DVCH11_PARAMS # undef prmdef , NULL }; static int parip(char *cp, unsigned char *adr); /* CH11_CONF - Parse configuration string and set defaults. ** At this point, device has just been created, but not yet bound ** or initialized. ** NOTE that some strings are dynamically allocated! Someday may want ** to clean them up nicely if config fails or device is uncreated. */ static int ch11_conf(FILE *f, char *s, struct ch11 *ch) { int i, ret = TRUE; struct prmstate_s prm; char buff[200]; long lval; int chudp_params_set = 0; /* First set defaults for all configurable parameters Unfortunately there's currently no way to access the UBA # that we're gonna be bound to, otherwise could set up defaults. Later fix this by giving ch11_create() a ptr to an arg structure, etc. */ DVDEBUG(ch) = FALSE; ch->ch_ifnam = NULL; ch->ch_backlog = 0; ch->ch_dedic = FALSE; ch->ch_dpidly = 0; ch->ch_dpdbg = FALSE; ch->ch_dpname = "dpchaos"; /* Pathname of device subproc */ ch->ch_chip_tlen = 0; ch->ch_chudp_port = CHUDP_PORT; prm_init(&prm, buff, sizeof(buff), s, strlen(s), ch11prmtab, sizeof(ch11prmtab[0])); while ((i = prm_next(&prm)) != PRMK_DONE) { switch (i) { case PRMK_NONE: fprintf(f, "Unknown CH11 parameter \"%s\"\n", prm.prm_name); ret = FALSE; continue; case PRMK_AMBI: fprintf(f, "Ambiguous CH11 parameter \"%s\"\n", prm.prm_name); ret = FALSE; continue; default: /* Handle matches not supported */ fprintf(f, "Unsupported CH11 parameter \"%s\"\n", prm.prm_name); ret = FALSE; continue; case CH11P_DBG: /* Parse as true/false boolean or number */ if (!prm.prm_val) /* No arg => default to 1 */ DVDEBUG(ch) = 1; else if (!s_tobool(prm.prm_val, &DVDEBUG(ch))) break; continue; case CH11P_BR: /* Parse as octal number */ if (!prm.prm_val || !s_tonum(prm.prm_val, &lval)) break; if (lval < 4 || lval > 7) { fprintf(f, "CH11 BR must be one of 4,5,6,7\n"); ret = FALSE; } else ch->ch_dv.dv_brlev = lval; continue; case CH11P_VEC: /* Parse as octal number */ if (!prm.prm_val || !s_tonum(prm.prm_val, &lval)) break; if (lval < 4 || lval > 0400 || (lval&03)) { fprintf(f, "CH11 VEC must be valid multiple of 4\n"); ret = FALSE; } else ch->ch_dv.dv_brvec = lval; continue; case CH11P_ADDR: /* Parse as octal number */ if (!prm.prm_val || !s_tonum(prm.prm_val, &lval)) break; if (lval < UB_CH11 || (lval&037)) { fprintf(f, "CH11 ADDR must be valid Unibus address\n"); ret = FALSE; } else ch->ch_dv.dv_addr = lval; continue; case CH11P_MYADDR: /* Parse as octal number */ if (!prm.prm_val || !s_tonum(prm.prm_val, &lval)) break; if (((lval & 0xff00) == 0) || ((lval & 0xff) == 0) || (lval >= 0xffff)) { // subnet part must be nonzero, host part too, also max 16 bit fprintf(f, "CH11 MYADDR must be a valid Chaosnet address\n"); ret = FALSE; } else ch->ch_myaddr = lval; continue; case CH11P_CHUPORT: chudp_params_set = 1; if (!prm.prm_val || !s_todnum(prm.prm_val, &lval)) break; if ((lval < 1) || (lval >= 0xffff)) { fprintf(f, "CH11 CHUDPPORT must be a valid UDP port\n"); break; } else ch->ch_chudp_port = lval; continue; case CH11P_CHIP: chudp_params_set = 1; if (ch->ch_chip_tlen >= CH11_CHIP_MAX) { fprintf(f,"CH11 Chaos/IP table full\n"); break; } if (!prm.prm_val) break; { char *c, *s = index(prm.prm_val, '/'); if (s == NULL) break; else { unsigned long cha; unsigned char ipa[IP_ADRSIZ]; struct hostent *he; in_port_t ipp = CHUDP_PORT; struct ch_chip *chip; int idx; *(s++) = '\0'; /* separate chaos from ip */ if (!s_tonum(prm.prm_val, (long*)&cha)) { *(--s) = '/'; /* failed to parse chaos, put back slash */ break; /* and complain */ } if ((c = index(s,':')) != NULL) { long x; if (s_todnum(c+1,&x) && (x > 0) && (x < 0xffff)) { ipp = x; } else { fprintf(f,"CH11 Chaos/IP mapping port number invalid"); *(s-1) = '/'; break; } *c = '\0'; /* zap for IP parsing */ } #if KLH10_CH11_USE_GETHOSTBYNAME if ((he = gethostbyname(s)) == NULL) #else if (!parip(s, &ipa[0])) #endif { *(--s) = '/'; /* put back slash */ if (c) *c = ':'; /* and colon */ break; /* and complain */ } #if KLH10_CH11_USE_GETHOSTBYNAME if ((he->h_addrtype != AF_INET) || (he->h_length != IP_ADRSIZ)) { fprintf(stderr,"CH11 CHIP spec found non-IPv4 address"); break; } #endif idx = (ch->ch_chip_tlen); /* new index */ ch->ch_chip_tbl[idx].ch_chip_chaddr = cha; ch->ch_chip_tbl[idx].ch_chip_ipport = ipp; #if KLH10_CH11_USE_GETHOSTBYNAME memcpy(&ch->ch_chip_tbl[idx].ch_chip_ipaddr, he->h_addr, IP_ADRSIZ); #else memcpy(&ch->ch_chip_tbl[idx].ch_chip_ipaddr, &ipa[0], IP_ADRSIZ); #endif ch->ch_chip_tlen = idx+1; /* update length */ } continue; } case CH11P_IFC: /* Parse as simple string */ if (!prm.prm_val) break; ch->ch_ifnam = s_dup(prm.prm_val); continue; case CH11P_BKL: /* Parse as decimal number */ if (!prm.prm_val || !s_todnum(prm.prm_val, &lval)) break; ch->ch_backlog = lval; continue; case CH11P_DED: /* Parse as true/false boolean */ if (!prm.prm_val) break; if (!s_tobool(prm.prm_val, &ch->ch_dedic)) break; continue; case CH11P_DPDLY: /* Parse as decimal number */ if (!prm.prm_val || !s_todnum(prm.prm_val, &lval)) break; ch->ch_dpidly = lval; continue; case CH11P_DPDBG: /* Parse as true/false boolean or number */ if (!prm.prm_val) /* No arg => default to 1 */ ch->ch_dpdbg = 1; else if (!s_tobool(prm.prm_val, &(ch->ch_dpdbg))) break; continue; case CH11P_DP: /* Parse as simple string */ if (!prm.prm_val) break; ch->ch_dpname = s_dup(prm.prm_val); continue; case CH11P_IFMETH: /* Parse as simple string */ if (!prm.prm_val) break; ch->ch_ifmeth = s_dup(prm.prm_val); continue; } ret = FALSE; fprintf(f, "CH11 param \"%s\": ", prm.prm_name); if (prm.prm_val) fprintf(f, "bad value syntax: \"%s\"\n", prm.prm_val); else fprintf(f, "missing value\n"); } /* Param string all done, do followup checks or cleanup */ if (!ch->ch_dv.dv_brlev || !ch->ch_dv.dv_brvec || !ch->ch_dv.dv_addr) { fprintf(f, "CH11 missing one of BR, VEC, ADDR params\n"); ret = FALSE; } if (ch->ch_dedic || ch->ch_backlog || ch->ch_dpidly || ch->ch_ifnam) fprintf(f, "CH11 params \"dedic\", \"backlog\", \"dpdelay\", \"ifc\" not supported, ignoring"); /* Set 1st invalid addr */ ch->ch_dv.dv_aend = ch->ch_dv.dv_addr + (UB_CH11END-UB_CH11); /* MYADDR must always be set! */ if (ch->ch_myaddr == 0) { fprintf(f, "CH11 param \"myaddr\" must be set\n"); return FALSE; } if (ch->ch_ifmeth) { if ((strcasecmp(ch->ch_ifmeth,"chudp") != 0) && (strcasecmp(ch->ch_ifmeth,"pcap") != 0)) { fprintf(f,"CH11 param \"ifmeth\" only supports \"chudp\" and \"pcap\" for now\n"); return FALSE; } } else if (chudp_params_set) { // backwards compat fprintf(f, "CH11 assuming \"chudp\" interface method\n"); ch->ch_ifmeth = s_dup("chudp"); } else { fprintf(f, "CH11 does not know which interface method to use, none specified?\n"); return FALSE; } return ret; } static int parip(char *cp, unsigned char *adr) { unsigned int b1, b2, b3, b4; if (4 != sscanf(cp, "%u.%u.%u.%u", &b1, &b2, &b3, &b4)) return FALSE; if (b1 > 255 || b2 > 255 || b3 > 255 || b4 > 255) return FALSE; *adr++ = b1; *adr++ = b2; *adr++ = b3; *adr = b4; return TRUE; } /* CH11 interface routines to KLH10 */ struct device * dvch11_create(FILE *f, char *s) { register struct ch11 *ch; /* Allocate a CH11 device structure */ if (nch11s >= CH11_NSUP) { fprintf(f, "Too many CH11s, max: %d\n", CH11_NSUP); return NULL; } ch = &dvch11[nch11s++]; /* Pick unused CH11 */ /* Various initialization stuff */ memset((char *)ch, 0, sizeof(*ch)); iodv_setnull(&ch->ch_dv); /* Init as null device */ ch->ch_dv.dv_init = ch11_init; /* Set up own post-bind init */ ch->ch_dv.dv_reset = ch11_clear; /* System reset (clear stuff) */ ch->ch_dv.dv_powoff = ch11_powoff; /* Power-off cleanup */ ch->ch_dv.dv_cmd = ch11_cmd; /* General user command to device */ /* Unibus stuff */ ch->ch_dv.dv_pivec = ch11_pivec; /* Return PI vector */ ch->ch_dv.dv_read = ch11_read; /* Read unibus register */ ch->ch_dv.dv_write = ch11_write; /* Write unibus register */ /* Configure from parsed string and remember for init */ if (!ch11_conf(f, s, ch)) return NULL; return &ch->ch_dv; } static void ch11_cmd_dpdebug(struct ch11 *ch, FILE *of, int val) { struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; fprintf(of,"Old value: %d. New value: %d.\n", dpc->dpchaos_dpc.dpc_debug, val); dpc->dpchaos_dpc.dpc_debug = val; } static void ch11_cmd_chiptable(struct ch11 *ch, FILE *of) { int n, i; unsigned char *ip; char ipa[4*4]; struct tm *ltime; char last[128]; struct dpchaos_chip *chip; struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; if (!dpc) { fprintf(of,"Can't find DP!\n"); return; } n = dpc->dpchaos_chip_tlen; fprintf(of,"Currently %d entries in Chaos/IP table\n",n); if (n > 0) { fprintf(of,"Chaos IP Port Last received\n"); for (i = 0; i < n; i++) { chip = &dpc->dpchaos_chip_tbl[i]; ip = (unsigned char *)&chip->dpchaos_chip_ipaddr.s_addr; sprintf(ipa,"%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); if (chip->dpchaos_chip_lastrcvd != 0) { ltime = localtime(&chip->dpchaos_chip_lastrcvd); strftime(last, sizeof(last), "%Y-%m-%d %T", ltime); } else strcpy(last,"[static]"); fprintf(of,"%6o %-15s %d. %s\n", chip->dpchaos_chip_chaddr, ipa, chip->dpchaos_chip_ipport, last); } } } void ch11_cmd_print_arp_table(struct ch11 *ch, FILE *of) { int i; time_t age; struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; if (dpc->charp_len > 0) { fprintf(of,"Chaos ARP table:\r\n" "Chaos\tEther\t\t\tAge (s)\r\n"); for (i = 0; i < dpc->charp_len; i++) { age = (time(NULL) - dpc->charp_list[i].charp_age); fprintf(of,"%#o\t\%02X:%02X:%02X:%02X:%02X:%02X\t%lu\t%s\r\n", dpc->charp_list[i].charp_chaddr, dpc->charp_list[i].charp_eaddr[0], dpc->charp_list[i].charp_eaddr[1], dpc->charp_list[i].charp_eaddr[2], dpc->charp_list[i].charp_eaddr[3], dpc->charp_list[i].charp_eaddr[4], dpc->charp_list[i].charp_eaddr[5], age, age > CHARP_MAX_AGE ? "(old)" : ""); } } else fprintf(of,"Chaos ARP table empty\r\n"); } static void ch11_cmd_status(struct ch11 *ch, FILE *of) { struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; fprintf(of,"My CHAOS address: 0%o.\n", ch->ch_myaddr); if (dpc->dpchaos_ifmeth_chudp) fprintf(of, " CHUDP port: %d.\n", ch->ch_chudp_port); else { if (dpc->dpchaos_ifnam[0]) fprintf(of, " Using ifc=\"%s\"", dpc->dpchaos_ifnam); fprintf(of, " ether addr %02X:%02X:%02X:%02X:%02X:%02X\n", dpc->dpchaos_eth[0],dpc->dpchaos_eth[1],dpc->dpchaos_eth[2], dpc->dpchaos_eth[3],dpc->dpchaos_eth[4],dpc->dpchaos_eth[5]); } fprintf(of,"Status register: 0%o\n", REG(ch)); if (REG(ch) & CH_BSY) fprintf(of, " Transmit busy\n"); if (REG(ch) & CH_LUP) fprintf(of, " Loopback\n"); if (REG(ch) & CH_SPY) fprintf(of, " Spy (promiscuous)\n"); if (REG(ch) & CH_REN) fprintf(of, " Receive enabled\n"); if (REG(ch) & CH_TEN) fprintf(of, " Transmit enabled\n"); if (REG(ch) & CH_TAB) fprintf(of, " Transmit aborted\n"); if (REG(ch) & CH_TDN) fprintf(of, " Transmit done\n"); if (REG(ch) & CH_RDN) fprintf(of, " Receive done\n"); if (REG(ch) & CH_ERR) fprintf(of, " CRC error\n"); fprintf(of, " Lost count: %d\n", ch->ch_lost); fprintf(of,"PI request: input %d, output: %d\n", ch->ch_ipireq, ch->ch_opireq); fprintf(of,"Input possible: %d, Output possible: %d\n", ch->ch_inactf, ch->ch_outactf); fprintf(of, "DP status: %d\n", ch->ch_dpstate); fprintf(of, "DP Rtest: %d, Stest: %d\n", (int)dp_xrtest(dp_dpxfr(&ch->ch_dp)), (int)dp_xstest(dp_dpxto(&ch->ch_dp))); fprintf(of, "DP rcmd: %d, rcnt: %d\n", (int)dp_xrcmd(dp_dpxfr(&ch->ch_dp)), (int)dp_xrcnt(dp_dpxfr(&ch->ch_dp))); fprintf(of,"Input buffer: "); if (ch->ch_iptr) fprintf(of,"%d chars\n", (int)(ch->ch_iptr - ch->ch_rbuf)); else fprintf(of,"none\n"); fprintf(of,"Output buffer: "); if (ch->ch_optr) fprintf(of,"%d chars\n", (int)(ch->ch_optr - ch->ch_sbuf)); else fprintf(of,"none\n"); fprintf(of,"Receive count: %d\n", ch->ch_rcnt); } static int ch11_cmd(register struct device *d, FILE *of, char *cmd) { register struct ch11 *ch = (struct ch11 *)d; struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; if (*cmd) while (*cmd == ' ') cmd++; if (*cmd && (strcmp(cmd,"chiptable") == 0)) ch11_cmd_chiptable(ch, of); else if (*cmd && ((strcmp(cmd,"arptable") == 0) || (strcmp(cmd,"arp") == 0))) ch11_cmd_print_arp_table(ch, of); else if (*cmd && (strcmp(cmd,"status") == 0)) ch11_cmd_status(ch, of); else if (*cmd && strncmp(cmd,"dpdebug",strlen("dpdebug")) == 0) { int val = 0; cmd += strlen("dpdebug"); while (*cmd == ' ') cmd++; if (s_tobool(cmd, &val)) ch11_cmd_dpdebug(ch, of, val); else fprintf(of,"Couldn't grok argument: \"%s\"\n", cmd); } else if (*cmd) { fprintf(of,"Unknown command \"%s\"\n", cmd); fprintf(of,"Commands:\n \"chiptable\" to show the Chaos/IP table\n \"arptable\" to show the Chaos ARP table\n \"status\" to show device status\n \"dpdebug x\" to set dpchaos debug level to x\n"); } else { /* No command, do all */ ch11_cmd_status(ch, of); if (dpc->dpchaos_ifmeth_chudp) ch11_cmd_chiptable(ch, of); else ch11_cmd_print_arp_table(ch, of); } return TRUE; } static dvureg_t ch11_pivec(register struct device *d) { (*d->dv_pifun)(d, 0); /* Turn off interrupt request */ return d->dv_brvec; /* Return vector to use */ } static int ch11_init(struct device *d, FILE *of) { register struct ch11 *ch = (struct ch11 *)d; if (!chaos_init(ch, of)) return FALSE; ch_clear(ch); return TRUE; } /* CH11_POWOFF - Handle "power-off" which usually means the KLH10 is ** being shut down. This is important if using a dev subproc! */ static void ch11_powoff(struct device *d) { chaos_kill((struct ch11 *)d); } static void ch11_clear(register struct device *d) { ch_clear((struct ch11 *)d); } /* CH_CLEAR - clear device */ static void ch_iint_off(register struct ch11 *ch) { if (ch->ch_ipireq) { ch->ch_ipireq = 0; /* Clear any interrupt request */ if (ch->ch_opireq == 0) /* if no output interrupt requested */ (*ch->ch_dv.dv_pifun)(&ch->ch_dv, 0); } } static void ch_oint_off(register struct ch11 *ch) { if (ch->ch_opireq) { ch->ch_opireq = 0; /* Clear any interrupt request */ if (ch->ch_ipireq == 0) /* if no input interrupt requested */ (*ch->ch_dv.dv_pifun)(&ch->ch_dv, 0); } } static void ch_iclear(register struct ch11 *ch) { struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; ch_iint_off(ch); if (ch->ch_rbuf) ch->ch_iptr = ch->ch_rbuf + dpc->dpchaos_inoff; // DPCHUDP_DATAOFFSET; else ch->ch_iptr = NULL; ch->ch_rcnt = -1; ch->ch_lost = 0; /* When receiver is re-enabled (write 1 into %CARDN) the count is then cleared. */ ch->ch_inactf = TRUE; /* OK to receive */ REG(ch) &= ~(CH_ERR|CH_RDN); /* No checksum error, no pkt here */ } static void ch_oclear(register struct ch11 *ch) { struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; ch_oint_off(ch); if (ch->ch_sbuf) ch->ch_optr = ch->ch_sbuf + dpc->dpchaos_outoff; // DPCHUDP_DATAOFFSET; else ch->ch_optr = NULL; REG(ch) &= ~CH_TAB; /* Not aborted */ if (ch->ch_outactf) /* OK to transmit? */ REG(ch) |= CH_TDN; /* Ready to send (again) */ } static void ch_clear(register struct ch11 *ch) { chaos_stop(ch); /* Kill CHAOS process, ready line going down */ ch->ch_outactf = TRUE; /* Initialise to true */ ch_iclear(ch); ch_oclear(ch); } static unsigned int ch_checksum(const unsigned char *addr, int count) { /* RFC1071 */ /* Compute Internet Checksum for "count" bytes * beginning at location "addr". */ register long sum = 0; while( count > 1 ) { /* This is the inner loop */ sum += *(addr)<<8 | *(addr+1); addr += 2; count -= 2; } /* Add left-over byte, if any */ if( count > 0 ) sum += * (unsigned char *) addr; /* Fold 32-bit sum to 16 bits */ while (sum>>16) sum = (sum & 0xffff) + (sum >> 16); return (~sum) & 0xffff; } static dvureg_t ch11_read(struct device *d, register uint18 addr) { register struct ch11 *ch = (struct ch11 *)d; struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; dvureg_t val; if (DVDEBUG(ch) > 4) fprintf(DVDBF(ch), "[CH11 Read %#o]\r\n", addr); if (ch->ch_dp.dp_chpid == 0) /* if DP process is not running, start it */ chaos_start(ch); switch (addr) { /* case UB_CHWBF: / * Write buffer (write only) = CHMYN */ /* break; */ case UB_CHMYN: /* My chaos address (read only) */ val = ch->ch_myaddr; break; case UB_CHCSR: /* Command status reg */ val = ch->ch_reg; val &= ~(CH_LOS); /* clear lost field */ val |= (ch->ch_lost & 017) << 9; /* insert current counter */ break; case UB_CHRBF: /* Read buffer (read only) */ /* The first word read will be filled to the left to make the message recieved a multiple of 16 bits. #### (Assume this is managed by ITS, until found wrong.) */ if (ch->ch_rcnt > 0) { val = *(ch->ch_iptr++) << 8; val |= *(ch->ch_iptr++); if ((ch->ch_iptr - ch->ch_rbuf) >= (ch->ch_rcnt + dpc->dpchaos_inoff)) { // DPCHUDP_DATAOFFSET if (DVDEBUG(ch) > 4) fprintf(DVDBF(ch), "[CH11 reading last word, clearing RDN]\r\n"); ch->ch_rcnt = -1; /* read last word */ REG(ch) &= ~CH_RDN; /* Done receiving? */ ch->ch_inactf = TRUE; /* OK to read another */ dp_xrdone(dp_dpxfr(&ch->ch_dp)); /* Done, can now ACK */ } break; } val = 0; break; case UB_CHRBC: /* Receive bit counter (read only) */ if (ch->ch_rcnt > 0) val = (dvureg_t)ch->ch_rcnt*8-1; /* byte count */ else val = (dvureg_t)07777; break; case UB_CHXMT: /* Initiate transmit (read only) */ /* last 16b in buffer is dest addr; add source addr and checksum, and send */ if (ch->ch_optr) { /* #### range check too */ int cks, len; /* Dest addr already in data, checksum at end */ len = (ch->ch_optr - ch->ch_sbuf)-dpc->dpchaos_outoff+2; // DPCHUDP_DATAOFFSET /* Add source */ *(ch->ch_optr++) = (ch->ch_myaddr>>8); *(ch->ch_optr++) = (ch->ch_myaddr & 0xff); /* Make checksum */ cks = ch_checksum(&ch->ch_sbuf[dpc->dpchaos_outoff],len); // DPCHUDP_DATAOFFSET *(ch->ch_optr++) = cks >> 8; *(ch->ch_optr++) = cks & 0xff; chaos_outxfer(ch); /* Send it to DP */ } else panic("ch11_read: no output pointer available at CHXMT"); val = (dvureg_t) ch->ch_myaddr; break; default: panic("ch11_read: Unknown register %lo", (long)addr); } if (DVDEBUG(ch) > 4) fprintf(DVDBF(ch), "[CH11 Read %#o => %o]\r\n", addr, val); return val; } /* Write CH11 registers. */ static void ch11_write(struct device *d, uint18 addr, register dvureg_t val) { register struct ch11 *ch = (struct ch11 *)d; val &= MASK16; if (DVDEBUG(ch) > 4) fprintf(DVDBF(ch), "[CH11 Write %#o <= %#lo]\r\n", addr, (long)val); if (ch->ch_dp.dp_chpid == 0) /* if DP process is not running, start it */ chaos_start(ch); switch (addr) { /* case UB_CHMYN: / * My chaos address (read only) = CHWBF */ case UB_CHRBF: /* Read buffer (read only) */ case UB_CHRBC: /* Receive bit counter (read only) */ case UB_CHXMT: /* Initiate transmit (read only) */ panic("ch11_write: read-only address %lo", (long)addr); break; case UB_CHWBF: /* Write buffer (write only) */ /* AIM628: TDN cleared when a word is written into the outgoingpacket buffer */ REG(ch) &= ~CH_TDN; if (ch->ch_optr) { /* #### range check too */ *(ch->ch_optr++) = val>>8; /* write two bytes */ *(ch->ch_optr++) = val&0xff; } else { panic("ch11_write: no output pointer available at CHWBF"); } return; case UB_CHCSR: /* Command status reg */ if (val & (CH_BSY | CH_TAB | CH_LOS | CH_ERR)) { panic("ch11_write: writing read-only bits to CSR: %lo", (long)val); break; } if (val & (CH_LUP | CH_SPY)) { if (DVDEBUG(ch)) fprintf(DVDBF(ch), "[CH11: loopback/spy being set - NYI]\r\n"); /* AIM628: LUP: the cable and transmitter are not used and the interface is looped back to itself. This is for maintenance. SPY: the interface will receive all packets regardless of their destination. This is for maintenance and network monitoring. */ /* LUP could possibly be usefully implemented, but SPY not, over UDP. */ val &= ~(CH_LUP | CH_SPY); } if (val & CH_RST) { /* I/O reset */ ch_clear(ch); if (DVDEBUG(ch) > 4) fprintf(DVDBF(ch),"[CH11 new CSR contents %#lo]\r\n", (long)REG(ch)); return; } if (val & CH_RCL) { /* Clear and enable the receiver, it can now gobble another msg */ /* AIM628: clears RDN and enables rcvr to receive another pkt */ /* AIM628: resets the lost count to 0 */ val &= ~(CH_RCL|CH_RDN); /* reset buffer, bit count etc */ ch_iclear(ch); } if (val & CH_TCL) { /* Clear the transmitter, making it ready */ /* AIM628: stops transmitter and sets TDN */ /* #### do we need to "stop transmitter" (dpchaos)? */ val &= ~CH_TCL; val |= CH_TDN; /* Done below */ /* ch_oclear(ch); */ } if (val & CH_RDN) { /* RCV DONE */ val &= ~CH_RDN; ch_iclear(ch); /* same as RCL? */ } REG(ch) |= val; /* save bits */ if (val & CH_TDN) /* Transmit Done. Set when transmitter is done */ ch_oclear(ch); /* same effect as TCL? (cf SYSTEM;CHAOS, CHXBK7+4) */ /* AIM628: when both xDN and xEN are set, the computer is interrupted */ if ((val & CH_TEN) && (val & CH_REN)) { /* try to be clever */ if (dp_xstest(dp_dpxto(&ch->ch_dp))) { ch_ogo(ch); /* sender's turn, process output first */ ch_igo(ch); } else { ch_igo(ch); /* receiver's turn */ ch_ogo(ch); } } else if (val & CH_TEN) { REG(ch) &= ~CH_REN; ch_iint_off(ch); ch_ogo(ch); } else if (val & CH_REN) { REG(ch) &= ~CH_TEN; ch_oint_off(ch); ch_igo(ch); } else { REG(ch) &= ~(CH_REN|CH_TEN); ch_iint_off(ch); ch_oint_off(ch); } if (DVDEBUG(ch) > 4) fprintf(DVDBF(ch),"[CH11 new CSR contents %#lo]\r\n", (long)REG(ch)); return; default: panic("ch11_write: Unknown address %lo", (long)addr); } } /* Generate CH11 output interrupt */ static void ch_oint(register struct ch11 *ch) { if (REG(ch) & CH_TEN) { #if 0 // too much noise if (DVDEBUG(ch)) fprintf(DVDBF(ch), "[CH11: output int]\r\n"); #endif ch->ch_opireq = TRUE; (*ch->ch_dv.dv_pifun)(&ch->ch_dv, /* Put up interrupt */ (int)ch->ch_dv.dv_brlev); } } /* Generate CH11 input interrupt */ static void ch_iint(register struct ch11 *ch) { if (REG(ch) & CH_REN) { if (DVDEBUG(ch)) fprintf(DVDBF(ch), "[CH11: input int]\r\n"); ch->ch_ipireq = TRUE; (*ch->ch_dv.dv_pifun)(&ch->ch_dv, /* Put up interrupt */ (int)ch->ch_dv.dv_brlev); } } /* Activate input side - allow CHAOS input to be received and processed. */ static void ch_igo(register struct ch11 *ch) { ch->ch_inactf = TRUE; /* OK to start reading input! */ if (chaos_incheck(ch)) { /* Do initial check for input */ chaos_inxfer(ch); /* Have input! Go snarf it! */ ch_idone(ch); /* Finish up CH input done */ } } /* CH input done - called to finish up CHAOS input */ static void ch_idone(register struct ch11 *ch) { ch->ch_inactf = FALSE; /* don't read another just yet */ ch_iint(ch); /* Send input interrupt! */ } static int ch_outcheck(register struct ch11 *ch) { struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; /* check if there's some output to send */ return ch->ch_outactf && (((ch->ch_optr - ch->ch_sbuf) - dpc->dpchaos_outoff) > 0); // DPCHUDP_DATAOFFSET } /* Activate output side - send a message to the CHAOS. ** If can't do it because an outbound message is already in progress, ** complain and cause an error. */ static void ch_ogo(register struct ch11 *ch) { if (ch_outcheck(ch)) { if (!chaos_outxfer(ch)) { /* Couldn't output, so abort and done immediately */ REG(ch) |= (CH_TAB|CH_TDN); /* #### */ if (DVDEBUG(ch)) fprintf(DVDBF(ch), "[CH11 out err - overrun]\r\n"); } ch_oint(ch); return; /* DPCHAOS will call ch_evhsdon() when ready for more output. */ } else { ch_oint(ch); /* ITS seems to need this? */ return; } } /* CH output done - called to finish up CHAOS output */ static void ch_odone(register struct ch11 *ch) { struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; // note we consumed the buffer if (ch->ch_sbuf) ch->ch_optr = ch->ch_sbuf + dpc->dpchaos_outoff; // DPCHUDP_DATAOFFSET; else ch->ch_optr = NULL; REG(ch) |= CH_TDN; /* Note it's done */ ch->ch_outactf = TRUE; /* OK to send another */ ch_oint(ch); /* Send output interrupt! */ } /* VIRTUAL CHAOS ROUTINES */ /* Utility routine */ #if 0 static void showpkt(FILE *f, char *id, unsigned char *buf, int cnt) { char linbuf[200]; register int i; int once = 0; register char *cp; while (cnt > 0) { cp = linbuf; if (once++) *cp++ = '\t'; else sprintf(cp, "%6s: ", id), cp += 8; for (i = 16; --i >= 0;) { sprintf(cp, " %3o", *buf++); cp += 4; if (--cnt <= 0) break; } *cp = 0; fprintf(f, "%s\r\n", linbuf); } } #else static char *ch_opc[] = { "NIL", "RFC", "OPN", "CLS", "FWD", "ANS", "SNS", "STS", "RUT", "LOS", "LSN", "MNT", "EOF", "UNC", "BRD" }; static char * ch_opcode(int op) { char buf[7]; if (op < 017 && op > 0) return ch_opc[op]; else if (op == 0200) return "DAT"; else if (op == 0300) return "DWD"; else return "bogus"; } char * ch_char(unsigned char x, char *buf) { if (x < 32) sprintf(buf,"^%c", x+64); else if (x == 127) sprintf(buf,"^?"); else if (x < 127) sprintf(buf,"%2c",x); else sprintf(buf,"%2x",x); return buf; } static void showpkt(FILE *f, char *id, unsigned char *ucp, int cnt) { int i, row; char b1[3],b2[3]; fprintf(stderr,"%s pkt dump, len %d\r\n", id, cnt); fprintf(stderr,"Opcode: %#o (%s), unused: %o\r\nFC: %d., Nbytes %d.\r\n", ucp[0], ch_opcode(ucp[0]), ucp[1], ucp[2]>>4, ((ucp[2]&0xf)<<8) | ucp[3]); fprintf(stderr,"Dest host: %#o, index %#o\r\nSource host: %#o, index %#o\r\n", (ucp[4]<<8)|ucp[5], (ucp[6]<<8)|ucp[7], (ucp[8]<<8)|ucp[9], (ucp[10]<<8)|ucp[11]); fprintf(stderr,"Packet #%o\r\nAck #%o\r\n", (ucp[12]<<8)|ucp[13], (ucp[14]<<8)|ucp[15]); fprintf(stderr,"Data:\r\n"); /* Skip headers */ ucp += CHAOS_HEADERSIZE; /* Show only data portion */ cnt -= CHAOS_HEADERSIZE+CHAOS_HW_TRAILERSIZE; for (row = 0; row*8 < cnt; row++) { for (i = 0; (i < 8) && (i+row*8 < cnt); i++) { fprintf(stderr, " %02x", ucp[i+row*8]); fprintf(stderr, "%02x", ucp[(++i)+row*8]); } fprintf(stderr, " (hex)\r\n"); #if 1 for (i = 0; (i < 8) && (i+row*8 < cnt); i++) { fprintf(stderr, " %2s", ch_char(ucp[i+row*8], (char *)&b1)); fprintf(stderr, "%2s", ch_char(ucp[(++i)+row*8], (char *)&b2)); } fprintf(stderr, " (chars)\r\n"); for (i = 0; (i < 8) && (i+row*8 < cnt); i++) { fprintf(stderr, " %2s", ch_char(ucp[i+1+row*8], (char *)&b1)); fprintf(stderr, "%2s", ch_char(ucp[(i++)+row*8], (char *)&b2)); } fprintf(stderr, " (11-chars)\r\n"); #endif } /* Now show trailer */ fprintf(stderr,"HW trailer:\r\n Dest: %#o\r\n Source: %#o\r\n Checksum: %#x\r\n", (ucp[cnt]<<8)|ucp[cnt+1],(ucp[cnt+2]<<8)|ucp[cnt+3],(ucp[cnt+4]<<8)|ucp[cnt+5]); } #endif static void ch_evhrwak(struct device *d, struct dvevent_s *evp); static void ch_evhsdon(struct device *d, struct dvevent_s *evp); /* static */ int chaos_init(register struct ch11 *ch, FILE *of) { register struct dpchaos_s *dpc; struct dvevent_s ev; size_t junk; ch->ch_dpstate = FALSE; if (!dp_init(&ch->ch_dp, sizeof(struct dpchaos_s), DP_XT_MSIG, SIGUSR1, (size_t)CHAOSBUFSIZ, /* in */ DP_XT_MSIG, SIGUSR1, (size_t)CHAOSBUFSIZ)) { /* out */ if (of) fprintf(of, "CH11 subproc init failed!\n"); return FALSE; } ch->ch_sbuf = dp_xsbuff(&(ch->ch_dp.dp_adr->dpc_todp), &junk); ch->ch_rbuf = dp_xrbuff(&(ch->ch_dp.dp_adr->dpc_frdp), &junk); ch->ch_dv.dv_dpp = &(ch->ch_dp); /* Tell CPU where our DP struct is */ /* Set up DPCHAOS-specific part of shared DP memory */ dpc = (struct dpchaos_s *) ch->ch_dp.dp_adr; dpc->dpchaos_dpc.dpc_debug = ch->ch_dpdbg; /* Init DP debug flag */ if (cpu.mm_locked) /* Lock DP mem if CPU is */ dpc->dpchaos_dpc.dpc_flags |= DPCF_MEMLOCK; dpc->dpchaos_ver = DPCHAOS_VERSION; dpc->dpchaos_attrs = 0; dpc->dpchaos_backlog = ch->ch_backlog; /* Pass on backlog value */ dpc->dpchaos_dedic = ch->ch_dedic; /* Pass on dedicated flag */ if (ch->ch_ifnam) /* Pass on interface name if any */ strncpy(dpc->dpchaos_ifnam, ch->ch_ifnam, sizeof(dpc->dpchaos_ifnam)-1); else dpc->dpchaos_ifnam[0] = '\0'; /* No specific interface */ if (ch->ch_ifmeth) { /* Pass on interface access method */ strncpy(dpc->dpchaos_ifmeth, ch->ch_ifmeth, sizeof(dpc->dpchaos_ifmeth)-1); if (strcasecmp(dpc->dpchaos_ifmeth, "chudp") == 0) dpc->dpchaos_ifmeth_chudp = 1; else if (strcasecmp(dpc->dpchaos_ifmeth, "pcap") == 0) dpc->dpchaos_ifmeth_chudp = 0; else { if (of) fprintf(of,"CH11: unsupported ifmeth '%s' (must be chudp or pcap)\n", dpc->dpchaos_ifmeth); return FALSE; } } else dpc->dpchaos_ifmeth[0] = '\0'; /* No specific access method */ dpc->dpchaos_myaddr = ch->ch_myaddr; /* Set our Chaos address */ dpc->dpchaos_port = ch->ch_chudp_port; /* copy chip table */ for (junk = 0; junk < ch->ch_chip_tlen; junk++) { memset(&dpc->dpchaos_chip_tbl[junk], 0, sizeof(struct dpchaos_chip)); dpc->dpchaos_chip_tbl[junk].dpchaos_chip_chaddr = ch->ch_chip_tbl[junk].ch_chip_chaddr; dpc->dpchaos_chip_tbl[junk].dpchaos_chip_ipport = ch->ch_chip_tbl[junk].ch_chip_ipport; memcpy(&dpc->dpchaos_chip_tbl[junk].dpchaos_chip_ipaddr, &ch->ch_chip_tbl[junk].ch_chip_ipaddr, sizeof(struct in_addr)); } dpc->dpchaos_chip_tlen = ch->ch_chip_tlen; /* Register ourselves with main KLH10 loop for DP events */ ev.dvev_type = DVEV_DPSIG; /* Event = Device Proc signal */ ev.dvev_arg.eva_int = SIGUSR1; ev.dvev_arg2.eva_ip = &(ch->ch_dp.dp_adr->dpc_todp.dpx_donflg); if (!(*ch->ch_dv.dv_evreg)((struct device *)ch, ch_evhsdon, &ev)) { if (of) fprintf(of, "CH11 event reg failed!\n"); return FALSE; } ev.dvev_type = DVEV_DPSIG; /* Event = Device Proc signal */ ev.dvev_arg.eva_int = SIGUSR1; ev.dvev_arg2.eva_ip = &(ch->ch_dp.dp_adr->dpc_frdp.dpx_wakflg); if (!(*ch->ch_dv.dv_evreg)((struct device *)ch, ch_evhrwak, &ev)) { if (of) fprintf(of, "CH11 event reg failed!\n"); return FALSE; } return TRUE; } static int chaos_start(register struct ch11 *ch) { register int res; if (DVDEBUG(ch)) fprintf(DVDBF(ch), "[chaos_start: starting DP \"%s\"...", ch->ch_dpname); /* HORRIBLE UGLY HACK: for AXP OSF/1 and perhaps other systems, ** the virtual-runtime timer of setitimer() remains in effect even ** for the child process of a fork()! To avoid this, we must ** temporarily turn the timer off, then resume it after the fork ** is safely out of the way. ** ** Otherise, the timer would go off and the unexpected signal would ** chop down the DP subproc without any warning! ** ** Later this should be done in DPSUP.C itself, when I can figure a ** good way to tell whether the code is part of the KLH10 or a DP ** subproc. */ clk_suspend(); /* Clear internal clock if one */ res = dp_start(&ch->ch_dp, ch->ch_dpname); clk_resume(); /* Resume internal clock if one */ if (!res) { if (DVDEBUG(ch)) fprintf(DVDBF(ch), " failed!]\r\n"); else fprintf(DVDBF(ch), "[chaos_start: Start of DP \"%s\" failed!]\r\n", ch->ch_dpname); return FALSE; } if (DVDEBUG(ch)) fprintf(DVDBF(ch), " started!]\r\n"); REG(ch) |= CH_REN; ch_iint(ch); /* Kick start */ return TRUE; } /* CHAOS_STOP - Stops CHAOS and drops Host Ready by killing CHAOS subproc, ** but allow restarting. */ static void chaos_stop(register struct ch11 *ch) { if (DVDEBUG(ch)) fprintf(DVDBF(ch), "[CH11: stopping..."); dp_stop(&ch->ch_dp, 1); /* Say to kill and wait 1 sec for synch */ ch->ch_dpstate = FALSE; /* No longer there and ready */ if (DVDEBUG(ch)) fprintf(DVDBF(ch), " stopped]\r\n"); } /* CHAOS_KILL - Kill CHAOS process permanently, no restart. */ static void chaos_kill(register struct ch11 *ch) { if (DVDEBUG(ch)) fprintf(DVDBF(ch), "[CH11 kill]\r\n"); ch->ch_dpstate = FALSE; (*ch->ch_dv.dv_evreg)( /* Flush all event handlers for device */ (struct device *)ch, NULLPROC, (struct dvevent_s *)NULL); dp_term(&(ch->ch_dp), 0); /* Flush all subproc overhead */ ch->ch_sbuf = NULL; /* Clear pointers no longer meaningful */ ch->ch_rbuf = NULL; } /* CH_EVHRWAK - Invoked by INSBRK event handling when ** signal detected from DP saying "wake up"; the DP is sending ** us an input packet. */ static void ch_evhrwak(struct device *d, struct dvevent_s *evp) { register struct ch11 *ch = (struct ch11 *)d; if (DVDEBUG(ch)) fprintf(DVDBF(ch), "[CH11 input wakeup: %d]", (int)dp_xrtest(dp_dpxfr(&ch->ch_dp))); /* Always check CHAOS input in order to process any non-data messages ** regardless of whether CH is actively reading, ** then invoke general CH check to do data transfer if OK. */ if (chaos_incheck(ch)) { if (!ch->ch_inactf) { ch->ch_lost++; /* received, but busy */ dp_xrdone(dp_dpxfr(&ch->ch_dp)); /* ack to DP! */ } else { chaos_inxfer(ch); /* Have input! Go snarf it! */ ch_idone(ch); /* Finish up CH input done */ } } } static void ch_evhsdon(struct device *d, struct dvevent_s *evp) { register struct ch11 *ch = (struct ch11 *)d; register struct dpx_s *dpx = dp_dpxto(&ch->ch_dp); if (DVDEBUG(ch)) fprintf(DVDBF(ch), "[ch_evhsdon: %d]", (int)dp_xstest(dpx)); if (dp_xstest(dpx)) { /* Verify message is done */ ch_odone(ch); /* Say CH output done */ } } /* Start CHAOS output. */ static int chaos_outxfer(register struct ch11 *ch) { register int cnt; register struct dpx_s *dpx = dp_dpxto(&ch->ch_dp); struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; /* Make sure we can output message and fail if not */ if (!dp_xstest(dpx)) { if (DVDEBUG(ch)) fprintf(DVDBF(ch), "[CH11: DP out blocked]\r\n"); return 0; } ch->ch_outactf = FALSE; /* Don't send another just yet */ /* Output xfer requested! */ #if 1 /* #### Debug */ int chlen = ((ch->ch_sbuf[dpc->dpchaos_outoff+2] & 0xf) << 8) | ch->ch_sbuf[dpc->dpchaos_outoff+3]; // DPCHUDP_DATAOFFSET #endif cnt = (ch->ch_optr - ch->ch_sbuf) - dpc->dpchaos_outoff; // DPCHUDP_DATAOFFSET #if 1 /* #### Debug */ if ((cnt % 2) == 1) fprintf(stderr,"\r\n[CH11 sending odd number of bytes (%d. data len %d.)]\r\n", cnt, chlen); if ((chlen + CHAOS_HEADERSIZE + CHAOS_HW_TRAILERSIZE) > cnt) fprintf(stderr,"\r\n[CH11 sending less than packet: sending %d, expected %d (Chaos data len %d)]\r\n", cnt, CHAOS_HEADERSIZE + chlen + CHAOS_HW_TRAILERSIZE, chlen); #endif if (DVDEBUG(ch) & DVDBF_DATSHO) /* Show data? */ showpkt(DVDBF(ch), "PKTOUT", ch->ch_sbuf + dpc->dpchaos_outoff, cnt); // DPCHUDP_DATAOFFSET REG(ch) &= ~(CH_TAB|CH_TDN); /* Not done yet, not aborted */ dp_xsend(dpx, DPCHAOS_SPKT, (size_t)cnt + dpc->dpchaos_outoff); // DPCHUDP_DATAOFFSET if (DVDEBUG(ch)) fprintf(DVDBF(ch), "[CH11: Out %d]\r\n", cnt); return 1; } static int chaos_incheck(register struct ch11 *ch) { register struct dpx_s *dpx = dp_dpxfr(&ch->ch_dp); if (dp_xrtest(dpx)) { /* Verify there's a message for us */ switch (dp_xrcmd(dpx)) { case DPCHAOS_INIT: ch->ch_dpstate = TRUE; dp_xrdone(dpx); /* ACK it */ return 0; /* No actual input */ case DPCHAOS_RPKT: /* Input packet ready! */ if (DVDEBUG(ch)) fprintf(DVDBF(ch), "[CH11: inbuf %ld]\r\n", (long) dp_xrcnt(dpx)); return 1; default: if (DVDEBUG(ch)) fprintf(DVDBF(ch), "[CH11: R %d flushed]", dp_xrcmd(dpx)); dp_xrdone(dpx); /* just ACK it */ return 0; } } return 0; } /* CHAOS_INXFER - CHAOS Input. ** For time being, don't worry about partial transfers (sigh), ** which might have left part of a previous message still lying ** around waiting for the next read request. */ static void chaos_inxfer(register struct ch11 *ch) { register int err, cnt, cks; register struct dpx_s *dpx = dp_dpxfr(&ch->ch_dp); register struct dpchaos_s *dpc = (struct dpchaos_s *) ch->ch_dp.dp_adr; register unsigned char *pp; /* Assume this is ONLY called after verification by chaos_incheck that ** an input message is actually ready. */ cnt = dp_xrcnt(dpx); /* Adjust for possible offset */ /* cnt -= dpc->dpchaos_inoff; */ pp = ch->ch_rbuf + dpc->dpchaos_inoff; if (DVDEBUG(ch)) fprintf(DVDBF(ch), "[CH11: In %d]\r\n", cnt); if (DVDEBUG(ch) & DVDBF_DATSHO) /* Show data? */ showpkt(DVDBF(ch), "PKTIN ", pp, cnt); /* Message already read in! */ if (cnt & 01) { /* If msg len not multiple of 2, pad out */ pp[cnt+1] = '\0'; } ch->ch_rcnt = cnt; ch->ch_iptr = pp; /* check hw trailer: dest, checksum */ // byte order warning... u_short chlen = ((pp[2] & 0xf) << 8) | pp[3]; u_short trdest = ((pp[cnt-6]<<8) | pp[cnt-5]); u_short hddest = (pp[4] << 8) | pp[5]; u_short cksm = (pp[cnt-2]<<8) | pp[cnt-1]; if (DVDEBUG(ch) && (CHAOS_HEADERSIZE + chlen + CHAOS_HW_TRAILERSIZE != cnt)) { fprintf(DVDBF(ch), "[CH11: expected len %d+%d+%d = %d, got %d]\r\n", CHAOS_HEADERSIZE , chlen , CHAOS_HW_TRAILERSIZE, CHAOS_HEADERSIZE + chlen + CHAOS_HW_TRAILERSIZE, cnt); } // be conservative in what you generate, and liberal in what you accept if ((trdest != 0) && (trdest != ch->ch_myaddr) && (hddest != 0) && (hddest != ch->ch_myaddr) && !(REG(ch) & CH_SPY)) { if (DVDEBUG(ch)) fprintf(DVDBF(ch), "[CH11: not for my address: trailer dest %#o, header dest %#o]\r\n", trdest, hddest); } else { if (cksm != 0) { cks = ch_checksum(pp,cnt); if (cks != 0) { if (DVDEBUG(ch)) fprintf(DVDBF(ch), "[CH11: bad checksum 0x%x]\r\n", cks); REG(ch) |= CH_ERR; } } REG(ch) |= CH_RDN; /* Note it's done! */ } } #endif /* KLH10_DEV_CH11 */