1
0
mirror of https://github.com/PDP-10/klh10.git synced 2026-02-09 09:42:30 +00:00
Files
PDP-10.klh10/src/dvch11.c
Björn Victor efb657a55b Don't read another until this one is read
Instead of accepting new input pkts after checking that the input pkt is shaped OK, do it after actually reading the pkt to ITS. Otherwise read pkts can be overwritten while only partially read to ITS, leading to corruption of data.
2021-02-22 07:34:37 +01:00

1557 lines
46 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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 <stdio.h>
#include <string.h>
#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 <netdb.h>
#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 */