1
0
mirror of https://github.com/PDP-10/klh10.git synced 2026-04-08 22:21:19 +00:00
Files
PDP-10.klh10/src/dvch11.c
Björn Victor 3c7e11bda5 Default ifmeth=pcap
If no chudp-specific parameters given, and no ifmeth given, default to pcap.
2021-07-05 19:30:39 +02: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 assuming \"pcap\" interface method since no chudp parameters given\n");
ch->ch_ifmeth = s_dup("pcap");
}
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 */