1
0
mirror of https://github.com/PDP-10/klh10.git synced 2026-02-12 10:58:20 +00:00
Files
PDP-10.klh10/src/dvch11.c
2017-02-28 20:50:02 +01:00

1343 lines
38 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 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 dpchudp.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 "dpchudp.h"
#define CHUDPBUFSIZ (DPCHUDP_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 20 /* max Chaos/IP mappings - see DPCHUDP_CHIP_MAX */
#endif
#if CH11_CHIP_MAX > DPCHUDP_CHIP_MAX
# error "DPCHUDP_CHIP_MAX must be at least CH11_CHIP_MAX"
#endif
#define REG(u) ((u)->ch_reg)
/* Chaos/IP mapping entry - see dpchudp_chip */
struct ch_chip {
unsigned int ch_chip_chaddr; /* Chaos address */
in_port_t ch_chip_ipport; /* IP port */
const char *ch_chip_hostname;
};
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 */
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 CHUDP low-level stuff */
/* static */
int chudp_init(struct ch11 *ch, FILE *of);
static int chudp_start(struct ch11 *ch);
static void chudp_stop(struct ch11 *ch);
static void chudp_kill(struct ch11 *ch);
static int chudp_incheck(struct ch11 *ch);
static void chudp_inxfer(struct ch11 *ch);
static int chudp_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 */
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;
/* 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 = "dpchudp"; /* 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 < 1) || (lval >= 0xffff)) {
fprintf(f, "CH11 MYADDR must be a valid Chaosnet address\n");
ret = FALSE;
} else
ch->ch_myaddr = lval;
continue;
case CH11P_CHUPORT:
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:
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;
const char *host;
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 */
}
host = strdup(s);
if (strlen(s) > DPCHUDP_CHIP_HOSTNAME_MAX)
{
*(--s) = '/'; /* put back slash */
if (c)
*c = ':'; /* and colon */
break; /* and complain */
}
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;
ch->ch_chip_tbl[idx].ch_chip_hostname = host;
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;
}
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;
}
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 dpchudp_s *dpc = (struct dpchudp_s *)ch->ch_dp.dp_adr;
fprintf(of,"Old value: %d. New value: %d.\n", dpc->dpchudp_dpc.dpc_debug, val);
dpc->dpchudp_dpc.dpc_debug = val;
}
static void
ch11_cmd_chiptable(struct ch11 *ch, FILE *of)
{
int n, i;
struct tm *ltime;
char last[128];
struct dpchudp_chip *chip;
struct dpchudp_s *dpc = (struct dpchudp_s *)ch->ch_dp.dp_adr;
if (!dpc) {
fprintf(of,"Can't find DP!\n");
return;
}
n = dpc->dpchudp_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->dpchudp_chip_tbl[i];
if (chip->dpchudp_chip_lastrcvd != 0) {
ltime = localtime(&chip->dpchudp_chip_lastrcvd);
strftime(last, sizeof(last), "%Y-%m-%d %T", ltime);
} else
strcpy(last,"[static]");
fprintf(of,"%6o %-40s %d. %s\n",
chip->dpchudp_chip_chaddr,
chip->dpchudp_chip_hostname,
chip->dpchudp_chip_ipport,
last);
}
}
}
static void
ch11_cmd_status(struct ch11 *ch, FILE *of)
{
fprintf(of,"My CHAOS address: 0%o, CHUDP port: %d.\n", ch->ch_myaddr, ch->ch_chudp_port);
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_dpxfr(&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",ch->ch_iptr - ch->ch_rbuf);
else
fprintf(of,"none\n");
fprintf(of,"Output buffer: ");
if (ch->ch_optr)
fprintf(of,"%d chars\n",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;
if (*cmd)
while (*cmd == ' ')
cmd++;
if (*cmd && (strcmp(cmd,"chiptable") == 0))
ch11_cmd_chiptable(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 \"status\" to show device status\n \"dpdebug x\" to set dpchudp debug level to x\n");
} else
/* No command, do both */
ch11_cmd_status(ch, of);
ch11_cmd_chiptable(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 (!chudp_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)
{
chudp_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)
{
ch_iint_off(ch);
if (ch->ch_rbuf)
ch->ch_iptr = ch->ch_rbuf + 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)
{
ch_oint_off(ch);
if (ch->ch_sbuf)
ch->ch_optr = ch->ch_sbuf + 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)
{
chudp_stop(ch); /* Kill CHUDP 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;
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 */
chudp_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 + 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? */
}
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)-DPCHUDP_DATAOFFSET+2;
/* Add source */
*(ch->ch_optr++) = (ch->ch_myaddr>>8);
*(ch->ch_optr++) = (ch->ch_myaddr & 0xff);
/* Make checksum */
cks = ch_checksum(&ch->ch_sbuf[DPCHUDP_DATAOFFSET],len);
*(ch->ch_optr++) = cks >> 8;
*(ch->ch_optr++) = cks & 0xff;
chudp_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 */
chudp_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" (dpchudp)? */
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_dpxfr(&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 (DVDEBUG(ch))
fprintf(DVDBF(ch), "[CH11: output int]\r\n");
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 CHUDP input to be received and processed.
*/
static void
ch_igo(register struct ch11 *ch)
{
ch->ch_inactf = TRUE; /* OK to start reading input! */
if (chudp_incheck(ch)) { /* Do initial check for input */
chudp_inxfer(ch); /* Have input! Go snarf it! */
ch_idone(ch); /* Finish up CH input done */
}
}
/* CH input done - called to finish up CHUDP 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)
{
/* check if there's some output to send */
return ch->ch_outactf && (((ch->ch_optr - ch->ch_sbuf) - DPCHUDP_DATAOFFSET) > 0);
}
/* Activate output side - send a message to the CHUDP.
** 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 (!chudp_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;
/* DPCHUDP 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 CHUDP output
*/
static void
ch_odone(register struct ch11 *ch)
{
REG(ch) |= CH_TDN; /* Note it's done */
ch->ch_outactf = TRUE; /* OK to send another */
ch_oint(ch); /* Send output interrupt! */
}
/* VIRTUAL CHUDP ROUTINES
*/
/* Utility routine */
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);
}
}
static void ch_evhrwak(struct device *d, struct dvevent_s *evp);
static void ch_evhsdon(struct device *d, struct dvevent_s *evp);
/* static */
int
chudp_init(register struct ch11 *ch, FILE *of)
{
register struct dpchudp_s *dpc;
struct dvevent_s ev;
size_t junk;
ch->ch_dpstate = FALSE;
if (!dp_init(&ch->ch_dp, sizeof(struct dpchudp_s),
DP_XT_MSIG, SIGUSR1, (size_t)CHUDPBUFSIZ, /* in */
DP_XT_MSIG, SIGUSR1, (size_t)CHUDPBUFSIZ)) { /* out */
if (of) fprintf(of, "CHUDP 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 DPCHUDP-specific part of shared DP memory */
dpc = (struct dpchudp_s *) ch->ch_dp.dp_adr;
dpc->dpchudp_dpc.dpc_debug = ch->ch_dpdbg; /* Init DP debug flag */
if (cpu.mm_locked) /* Lock DP mem if CPU is */
dpc->dpchudp_dpc.dpc_flags |= DPCF_MEMLOCK;
dpc->dpchudp_ver = DPCHUDP_VERSION;
dpc->dpchudp_attrs = 0;
dpc->dpchudp_backlog = ch->ch_backlog; /* Pass on backlog value */
dpc->dpchudp_dedic = ch->ch_dedic; /* Pass on dedicated flag */
if (ch->ch_ifnam) /* Pass on interface name if any */
strncpy(dpc->dpchudp_ifnam, ch->ch_ifnam, sizeof(dpc->dpchudp_ifnam)-1);
else
dpc->dpchudp_ifnam[0] = '\0'; /* No specific interface */
dpc->dpchudp_myaddr = ch->ch_myaddr; /* Set our Chaos address */
dpc->dpchudp_port = ch->ch_chudp_port;
/* copy chip table */
for (junk = 0; junk < ch->ch_chip_tlen; junk++) {
memset(&dpc->dpchudp_chip_tbl[junk], 0, sizeof(struct dpchudp_chip));
dpc->dpchudp_chip_tbl[junk].dpchudp_chip_chaddr = ch->ch_chip_tbl[junk].ch_chip_chaddr;
dpc->dpchudp_chip_tbl[junk].dpchudp_chip_ipport = ch->ch_chip_tbl[junk].ch_chip_ipport;
memset(&dpc->dpchudp_chip_tbl[junk].dpchudp_chip_ipaddr,
0, sizeof(struct in_addr));
strcpy(dpc->dpchudp_chip_tbl[junk].dpchudp_chip_hostname,
ch->ch_chip_tbl[junk].ch_chip_hostname);
}
dpc->dpchudp_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, "CHUDP 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, "CHUDP event reg failed!\n");
return FALSE;
}
return TRUE;
}
static int
chudp_start(register struct ch11 *ch)
{
register int res;
if (DVDEBUG(ch))
fprintf(DVDBF(ch), "[chudp_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), "[chudp_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;
}
/* CHUDP_STOP - Stops CHUDP and drops Host Ready by killing CHUDP subproc,
** but allow restarting.
*/
static void
chudp_stop(register struct ch11 *ch)
{
if (DVDEBUG(ch))
fprintf(DVDBF(ch), "[CHUDP: 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");
}
/* CHUDP_KILL - Kill CHUDP process permanently, no restart.
*/
static void
chudp_kill(register struct ch11 *ch)
{
if (DVDEBUG(ch))
fprintf(DVDBF(ch), "[CHUDP 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 CHUDP 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 (chudp_incheck(ch)) {
if (!ch->ch_inactf) {
ch->ch_lost++; /* received, but busy */
dp_xrdone(dp_dpxfr(&ch->ch_dp)); /* ack to DP! */
} else {
chudp_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 CHUDP output.
*/
static int
chudp_outxfer(register struct ch11 *ch)
{
register int cnt;
register struct dpx_s *dpx = dp_dpxto(&ch->ch_dp);
/* Make sure we can output message and fail if not */
if (!dp_xstest(dpx)) {
if (DVDEBUG(ch))
fprintf(DVDBF(ch), "[CHUDP: DP out blocked]\r\n");
return 0;
}
ch->ch_outactf = FALSE; /* Don't send another just yet */
/* Output xfer requested! */
register struct dpchudp_s *dpc = (struct dpchudp_s *) ch->ch_dp.dp_adr;
#if 1 /* #### Debug */
int chlen = ((ch->ch_sbuf[DPCHUDP_DATAOFFSET+2] & 0xf) << 4) | ch->ch_sbuf[DPCHUDP_DATAOFFSET+3];
#endif
cnt = (ch->ch_optr - ch->ch_sbuf) - 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 + DPCHUDP_DATAOFFSET, cnt);
dpc->dpchudp_outoff = DPCHUDP_DATAOFFSET;
REG(ch) &= ~(CH_TAB|CH_TDN); /* Not done yet, not aborted */
dp_xsend(dpx, DPCHUDP_SPKT, (size_t)cnt + DPCHUDP_DATAOFFSET);
if (DVDEBUG(ch))
fprintf(DVDBF(ch), "[CHUDP: Out %d]\r\n", cnt);
return 1;
}
static int
chudp_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 DPCHUDP_INIT:
ch->ch_dpstate = TRUE;
dp_xrdone(dpx); /* ACK it */
return 0; /* No actual input */
case DPCHUDP_RPKT: /* Input packet ready! */
if (DVDEBUG(ch))
fprintf(DVDBF(ch), "[CHUDP: inbuf %ld]\r\n",
(long) dp_xrcnt(dpx));
return 1;
default:
if (DVDEBUG(ch))
fprintf(DVDBF(ch), "[CHUDP: R %d flushed]", dp_xrcmd(dpx));
dp_xrdone(dpx); /* just ACK it */
return 0;
}
}
return 0;
}
/* CHUDP_INXFER - CHUDP 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
chudp_inxfer(register struct ch11 *ch)
{
register int err, cnt, cks;
register struct dpx_s *dpx = dp_dpxfr(&ch->ch_dp);
register struct dpchudp_s *dpc = (struct dpchudp_s *) ch->ch_dp.dp_adr;
register unsigned char *pp;
/* Assume this is ONLY called after verification by chudp_incheck that
** an input message is actually ready.
*/
cnt = dp_xrcnt(dpx);
/* Adjust for possible offset */
/* cnt -= dpc->dpchudp_inoff; */
pp = ch->ch_rbuf + dpc->dpchudp_inoff;
if (DVDEBUG(ch))
fprintf(DVDBF(ch), "[CHUDP: 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 */
if ((((pp[cnt-6]<<8) | pp[cnt-5]) != ch->ch_myaddr)) {
if (DVDEBUG(ch))
fprintf(DVDBF(ch), "[CHUDP: not for my address: destination %o]\r\n",
((pp[cnt-6]<<8) | pp[cnt-5]));
} else {
cks = ch_checksum(pp,cnt);
if (cks != 0) {
if (DVDEBUG(ch))
fprintf(DVDBF(ch), "[CHUDP: bad checksum 0x%x]\r\n", cks);
#if 1 /* 0 for testing */
REG(ch) |= CH_ERR;
#endif
}
REG(ch) |= CH_RDN; /* Note it's done! */
}
ch->ch_inactf = TRUE; /* OK to read another */
dp_xrdone(dpx); /* Done, can now ACK */
}
#endif /* KLH10_DEV_CH11 */