mirror of
https://github.com/PDP-10/klh10.git
synced 2026-02-05 16:05:30 +00:00
1979 lines
55 KiB
C
1979 lines
55 KiB
C
/* KN10DEV.C - KLH10 Generic Device Support
|
||
*/
|
||
/* $Id: kn10dev.c,v 2.4 2001/11/10 21:28:59 klh Exp $
|
||
*/
|
||
/* Copyright © 1992, 1993, 2001 Kenneth L. Harrenstien
|
||
** All Rights Reserved
|
||
**
|
||
** This file is 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: kn10dev.c,v $
|
||
* Revision 2.4 2001/11/10 21:28:59 klh
|
||
* Final 2.0 distribution checkin
|
||
*
|
||
*/
|
||
|
||
/*
|
||
** This file contains generic device support code, including a "null device"
|
||
** definition that is used to help initialize other devices.
|
||
*/
|
||
|
||
#include <stdio.h> /* For stderr */
|
||
#include <ctype.h>
|
||
#include <string.h>
|
||
#include <stdlib.h> /* For malloc/free */
|
||
|
||
#include "klh10.h"
|
||
#include "osdsup.h"
|
||
#include "kn10def.h"
|
||
#include "kn10dev.h"
|
||
#include "kn10ops.h"
|
||
#include "prmstr.h"
|
||
#include "kn10cpu.h"
|
||
|
||
#if KLH10_CPU_KS && KLH10_DEV_TM03
|
||
# include "dvtm03.h" /* For setting up FECOM_BOOTP with magtape params */
|
||
#endif
|
||
|
||
#ifdef RCSID
|
||
RCSID(kn10dev_c,"$Id: kn10dev.c,v 2.4 2001/11/10 21:28:59 klh Exp $")
|
||
#endif
|
||
|
||
/* NULL device routines, for handling non-existent device. */
|
||
|
||
static void dvnull_v(struct device *d) { }
|
||
static int dvnull_i(struct device *d) { return 0; }
|
||
static w10_t dvnull_w(struct device *d)
|
||
{ register w10_t w; op10m_setz(w); return w; }
|
||
static int dvnull_ifs(struct device *d, FILE *f, char *s)
|
||
{ return 0; }
|
||
static int dvnull_ifss(struct device *d, FILE *f, char *s, char *s2)
|
||
{ return 0; }
|
||
static int dvnull_if(struct device *d, FILE *f)
|
||
{ return 1; }
|
||
static dvureg_t dvnull_16(struct device *d)
|
||
{ return 0; }
|
||
static dvureg_t dvnull_rd(struct device *d, dvuadr_t a)
|
||
{ return 0; }
|
||
static void dvnull_wr(struct device *d, dvuadr_t a, dvureg_t u)
|
||
{ }
|
||
|
||
static void dvnull_attn(struct device *d, int i)
|
||
{ }
|
||
static int dvnull_iobeg(struct device *d, int i)
|
||
{ return 0; }
|
||
static int dvnull_iobuf(struct device *d, int wc, w10_t **awp)
|
||
{ return 0; }
|
||
static void dvnull_ioend(struct device *d, int i)
|
||
{ }
|
||
static uint32 dvnull_rrg(struct device *d, int r)
|
||
{ return -1; }
|
||
static int dvnull_wrg(struct device *d, int r, dvureg_t v)
|
||
{ return 0; }
|
||
static int dvnull_evreg(struct device *d,
|
||
void (*rtn)(struct device *, struct dvevent_s *),
|
||
struct dvevent_s *ev)
|
||
{ return 0; }
|
||
static int dvnull_rim(struct device *d, FILE *f,
|
||
w10_t w, w10_t *wp, int cnt)
|
||
{ return 0; }
|
||
|
||
|
||
/* Handle non-existent dev-IO instruction dispatch.
|
||
** CONO and DATAO do nothing.
|
||
** (DATAO memory read reference already made by dispatch code)
|
||
** CONI and DATAI both read 0
|
||
** (Memory write ref will be made by dispatch code)
|
||
*/
|
||
static insdef_cono( dvnull_cono) { }
|
||
static insdef_datao(dvnull_datao) { }
|
||
static insdef_coni( dvnull_coni) { register w10_t w; op10m_setz(w); return w;}
|
||
static insdef_datai(dvnull_datai) { register w10_t w; op10m_setz(w); return w;}
|
||
|
||
/* Set up null (non-ex) device vector.
|
||
** Until vector stabilizes, easier to init it dynamically than statically.
|
||
*/
|
||
void
|
||
iodv_setnull(register struct device *d)
|
||
{
|
||
d->dv_vers = KN10DEV_VERSION; /* Device interface version */
|
||
d->dv_dflags = 0; /* Device flags */
|
||
d->dv_cflags = 0; /* [C] CPU flags */
|
||
d->dv_name = " NULDEV "; /* [C] Device name (unique) */
|
||
d->dv_debug = 0; /* [C] Bit flags for debugging options */
|
||
d->dv_dbf = stderr; /* [C] Debug output stream */
|
||
d->dv_pireq = 0; /* [C/D] NZ = bit for level PI req active on */
|
||
d->dv_pifun = dvnull_attn; /* [C] PI request function */
|
||
d->dv_evreg = dvnull_evreg; /* [C] Event callback reg function */
|
||
d->dv_dpp = NULL; /* Pointer to DP struct, NULL cuz none */
|
||
|
||
d->dv_ctlr = NULL; /* [C] Back-pointer to parent controller */
|
||
d->dv_num = 0; /* [C] Unit number on parent controller */
|
||
d->dv_attn = dvnull_attn; /* [C] Attention request */
|
||
d->dv_drerr = dvnull_v; /* [C] Drive or xfer error */
|
||
d->dv_iobeg = dvnull_iobeg; /* [C] Xfer start */
|
||
d->dv_iobuf = dvnull_iobuf; /* [C] Xfer buffer setup */
|
||
d->dv_ioend = dvnull_ioend; /* [C] Xfer end */
|
||
d->dv_rdreg = dvnull_rrg; /* Drive register read */
|
||
d->dv_wrreg = dvnull_wrg; /* Drive register write */
|
||
|
||
/* KA/KI/KL ("dev" IO )*/
|
||
|
||
d->dv_pifnwd = dvnull_w; /* PI: Get function word */
|
||
d->dv_cono = dvnull_cono; /* CONO 18-bit conds out */
|
||
d->dv_coni = dvnull_coni; /* CONI 36-bit conds in */
|
||
d->dv_datao = dvnull_datao; /* DATAO word out */
|
||
d->dv_datai = dvnull_datai; /* DATAI word in */
|
||
|
||
/* KS ("new" IO) */
|
||
|
||
d->dv_uba = NULL; /* Unibus adapter */
|
||
d->dv_addr = 0; /* 1st valid Unibus address */
|
||
d->dv_aend = 0; /* Last valid Unibus address */
|
||
d->dv_brlev = 0; /* BR setting */
|
||
d->dv_brvec = 0; /* BR interrupt vector setting */
|
||
d->dv_pivec = dvnull_16; /* PI: Get vector */
|
||
d->dv_read = dvnull_rd; /* Read Unibus register */
|
||
d->dv_write = dvnull_wr; /* Write Unibus register */
|
||
|
||
d->dv_bind = NULL; /* Bind device to controller if non-NULL */
|
||
d->dv_init = dvnull_if; /* Final post-bind device init */
|
||
d->dv_exit = dvnull_if; /* Exit, close, terminate */
|
||
d->dv_cmd = dvnull_ifs; /* User command to device */
|
||
d->dv_mount = dvnull_ifss; /* Mount or unmount media */
|
||
d->dv_help = dvnull_if; /* Output help info for user */
|
||
d->dv_status = dvnull_if; /* Output status info for user */
|
||
|
||
d->dv_readin = dvnull_rim; /* Readin mode */
|
||
d->dv_powon = dvnull_v; /* "Power on" */
|
||
d->dv_reset = dvnull_v; /* System reset */
|
||
d->dv_powoff = dvnull_v; /* "Power off" */
|
||
}
|
||
|
||
|
||
/* External routine in case want to know what CPU thinks version is
|
||
*/
|
||
int
|
||
iodv_version(void)
|
||
{
|
||
return KN10DEV_VERSION;
|
||
}
|
||
|
||
/* IODV_NULLINIT - Initialize as null device, check version #
|
||
*/
|
||
int
|
||
iodv_nullinit(register struct device *d,
|
||
int version)
|
||
{
|
||
if (version != KN10DEV_VERSION)
|
||
return FALSE;
|
||
iodv_setnull(d); /* Sets dv_vers to KN10DEV_VERSION */
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
/* Set max # of registered devices.
|
||
** Table can actually be dynamic, but for now let's keep it simple.
|
||
*/
|
||
#ifndef KLH10_DEVMAX
|
||
# define KLH10_DEVMAX 20
|
||
#endif
|
||
|
||
/* Warning - using arrays for the two tables below is good for
|
||
** keyword lookup and general searching. However, it is bad if
|
||
** unloading or undefining is allowed, because the array must then
|
||
** be compacted and things like dev_drv adjusted to point into the
|
||
** right place. Ugh. If dynamic unloading/undefining is desired,
|
||
** a linked list is probably best, with a separate keyword table
|
||
** for parsing purposes.
|
||
**
|
||
** Don't free up static entries.
|
||
*/
|
||
|
||
|
||
/* Device Driver/Emulator Module - information for both static (link-time)
|
||
** and extern (dynamic link, run-time) drivers.
|
||
** drv_path is NULL if driver is static.
|
||
** drv_init is NULL if driver isn't loaded yet (but shouldn't happen)
|
||
*/
|
||
struct dvdrv_s {
|
||
char *drv_name; /* Name (used after "static" keyword) */
|
||
struct device *(*drv_init)(FILE *, /* Addr of dev config rtn */
|
||
char *);
|
||
char *drv_path; /* Path, if dynamically loadable */
|
||
char *drv_isym; /* Symbol, if " ", for device config rtn */
|
||
char *drv_desc; /* Description for help printout */
|
||
osdll_t drv_dllhdl; /* Handle for library, if loaded */
|
||
};
|
||
|
||
|
||
/* Device Definition - binds 10's idea of devices to a specific driver.
|
||
** There is one such entry for every instance of a device.
|
||
*/
|
||
struct dvdef_s {
|
||
char *dev_name; /* Defined name of device */
|
||
int dev_flags; /* Various */
|
||
struct dvdef_s *dev_ctl; /* Pointer to controller dev, if one */
|
||
int dev_num; /* Device # or unibus # or drive unit # */
|
||
struct device *dev_dv; /* Pointer to active device structure */
|
||
struct dvdrv_s *dev_drv; /* Pointer to selected driver code */
|
||
};
|
||
int dvdefcnt = 0;
|
||
struct dvdef_s dvdeftab[KLH10_DEVMAX+1];
|
||
|
||
#define DVDF_EXTERN 01 /* Device is dynamically loaded library */
|
||
#define DVDF_STATIC 02 /* Device is statically linked module */
|
||
#define DVDF_DEVIO 04 /* Device expects <ins> <dev>,E I/O instrs */
|
||
#define DVDF_UBIO 010 /* Device expects KS10 Unibus I/O instrs */
|
||
#define DVDF_CTLIO 020 /* Device invoked as unit of a controller */
|
||
|
||
/* Predefined device number mnemonics
|
||
** These can be provided as the <10dev> spec in a device definition.
|
||
** Combine these with opcdvnam[] later.
|
||
*/
|
||
struct dvnum_s {
|
||
char *dvn_name;
|
||
int dvn_num;
|
||
};
|
||
static struct dvnum_s dvnumtab[] = {
|
||
|
||
{ "apr", IODV_APR<<2 }, /* Processor */
|
||
{ "pi", IODV_PI <<2 }, /* PI system */
|
||
{ "pag", IODV_PAG<<2 }, /* KI+: Pager */
|
||
{ "cca", IODV_CCA<<2 }, /* KL: Cache */
|
||
{ "tim", IODV_TIM<<2 }, /* KL: Timers, KS: Read various */
|
||
{ "mtr", IODV_MTR<<2 }, /* KL: Meters, KS: Write ditto */
|
||
|
||
/* For fun */
|
||
{ "clk", IODV_CLK<<2 }, /* DKN10 clock (KA/KI) */
|
||
{ "ptp", IODV_PTP<<2 }, /* Paper-Tape Punch */
|
||
{ "ptr", IODV_PTR<<2 }, /* Paper-Tape Reader */
|
||
{ "tty", IODV_TTY<<2 }, /* Console TTY */
|
||
{ "dis", IODV_DIS<<2 }, /* Ye 340 display */
|
||
{ NULL, 0 }
|
||
};
|
||
|
||
|
||
/* Without good macros, C forward declarations suck...
|
||
*/
|
||
#if KLH10_DEV_DTE
|
||
extern struct device *dvdte_create(FILE *f, char *s);
|
||
#endif
|
||
#if KLH10_DEV_RH20
|
||
extern struct device *dvrh20_create(FILE *f, char *s);
|
||
#endif
|
||
#if KLH10_DEV_RPXX
|
||
extern struct device *dvrp_create(FILE *f, char *s);
|
||
#endif
|
||
#if KLH10_DEV_TM03
|
||
extern struct device *dvtm03_create(FILE *f, char *s);
|
||
#endif
|
||
#if KLH10_DEV_NI20
|
||
extern struct device *dvni20_create(FILE *f, char *s);
|
||
#endif
|
||
#if KLH10_DEV_HOST
|
||
extern struct device *dvhost_create(FILE *f, char *s);
|
||
#endif
|
||
#if KLH10_DEV_RH11
|
||
extern struct device *dvrh11_create(FILE *f, char *s);
|
||
#endif
|
||
#if KLH10_DEV_LHDH
|
||
extern struct device *dvlhdh_create(FILE *f, char *s);
|
||
#endif
|
||
#if KLH10_DEV_DZ11
|
||
extern struct device *dvdz11_init(FILE *f, char *s);
|
||
#endif
|
||
#if KLH10_DEV_CH11
|
||
extern struct device *dvch11_init(FILE *f, char *s);
|
||
#endif
|
||
|
||
/* Table binding static device driver modules with their names
|
||
*/
|
||
static struct dvdrv_s dvdrvtab[KLH10_DEVMAX+1] = {
|
||
#if KLH10_DEV_DTE
|
||
{ "dte", dvdte_create, NULL, NULL, "DTE 10-11 Interface (dev)" },
|
||
#endif
|
||
#if KLH10_DEV_RH20
|
||
{ "rh20", dvrh20_create, NULL, NULL, "RH20 Massbus Controller (MB dev)" },
|
||
#endif
|
||
#if KLH10_DEV_RPXX
|
||
{ "rp", dvrp_create, NULL, NULL, "RP/RM Disk Drive (unit)" },
|
||
#endif
|
||
#if KLH10_DEV_TM03
|
||
/* Keep "tm02" as a backward-compatible alias for "tm03" */
|
||
{ "tm02", dvtm03_create, NULL, NULL, "TM03 Tape Drive (unit)" },
|
||
{ "tm03", dvtm03_create, NULL, NULL, "TM03 Tape Drive (unit)" },
|
||
#endif
|
||
#if KLH10_DEV_NI20
|
||
{ "ni20", dvni20_create, NULL, NULL, "NIA20 Ethernet Port (MB dev)" },
|
||
#endif
|
||
#if KLH10_DEV_HOST
|
||
{ "host", dvhost_create, NULL, NULL, "HOST native access (dev or Unibus)" },
|
||
#endif
|
||
#if KLH10_DEV_RH11
|
||
{ "rh11", dvrh11_create, NULL, NULL, "RH11 Controller (Unibus)" },
|
||
#endif
|
||
#if KLH10_DEV_LHDH
|
||
{ "lhdh", dvlhdh_create, NULL, NULL, "LHDH IMP Interface (Unibus)" },
|
||
#endif
|
||
#if KLH10_DEV_DZ11
|
||
{ "dz11", dvdz11_init, NULL, NULL, "DZ11 dummy (Unibus)" },
|
||
#endif
|
||
#if KLH10_DEV_CH11
|
||
{ "ch11", dvch11_init, NULL, NULL, "Chaosnet dummy (Unibus)" },
|
||
#endif
|
||
{ NULL, NULL, NULL }
|
||
};
|
||
|
||
#if !KLH10_CPU_KS
|
||
|
||
/* General Dev-IO support - "IOB" for I/O Bus.
|
||
** Eventually move this page elsewhere, either into dviob.c or
|
||
** perhaps inio.c?
|
||
*/
|
||
|
||
struct device dvnull; /* Null device - default vector */
|
||
|
||
/* Actual binding of dev-IO devices!
|
||
*/
|
||
struct device *devtab[128];
|
||
|
||
/* Sub-binding tables. On the KL, all devices ultimately belong to one
|
||
** of the following three groups: RH20, DTE, or DIA.
|
||
*/
|
||
#if KLH10_CPU_KL
|
||
# define NRH20 8
|
||
# define DEVRH20(n) (0540+((n)<<2)) /* Cvt 0-7 into RH20 devs 540-574 */
|
||
struct device *devrh20[8+1]; /* Up to 8 RH20 channels, in order of binding
|
||
** (NOT indexed by device number!)
|
||
** Plus one terminating null pointer.
|
||
** Code assumes all entries null initially!
|
||
*/
|
||
# define NDTE20 4
|
||
# define DEVDTE20(n) (0200+((n)<<2))
|
||
struct device *devdte20[4+1]; /* 4 DTE20s, devs 200-214 */
|
||
/* Same format as devrh20 table */
|
||
|
||
/* Not clear if the DIA20 is a distinct device or merely a transparent
|
||
** hookup to a traditional IO bus to support all other random device #s.
|
||
*/
|
||
struct device *devdia20; /* One DIA20 for random devs */
|
||
#endif /* KL */
|
||
|
||
|
||
static void
|
||
dviob_init(void)
|
||
{
|
||
iodv_setnull(&dvnull); /* Initialize null device vector */
|
||
{
|
||
register int i;
|
||
for (i = 0; i < 128; i++)
|
||
devtab[i] = &dvnull;
|
||
}
|
||
}
|
||
|
||
#if KLH10_CPU_KL
|
||
|
||
static void
|
||
rh20pifun(register struct device *d, int lev)
|
||
{
|
||
if (lev) { /* Requesting PI on level? */
|
||
d->dv_pireq = lev; /* Force new level for device */
|
||
if (cpu.pi.pilev_rhreq & lev) /* Already being requested? */
|
||
return; /* Yes, don't yell at CPU again */
|
||
cpu.pi.pilev_rhreq |= lev;
|
||
} else {
|
||
if (!(lev = d->dv_pireq)) /* Turning off -- dev already off? */
|
||
return; /* Yep, nothing to do */
|
||
d->dv_pireq = 0; /* Force level off for device */
|
||
if (!(cpu.pi.pilev_rhreq & lev)) /* RHs already turned off? */
|
||
return; /* Yes, don't yell at CPU again */
|
||
|
||
/* Ugh, generic RH PI indicator is turned on. Need to re-assess all
|
||
** RHs to see if safe to turn off bit. Do this by simply summing
|
||
** all RH request bits.
|
||
** In a multiproc environment this scan is a critical section.
|
||
*/
|
||
{
|
||
register struct device **adv;
|
||
|
||
cpu.pi.pilev_rhreq = 0;
|
||
for (adv = devrh20; *adv; ++adv)
|
||
cpu.pi.pilev_rhreq |= (*adv)->dv_pireq;
|
||
}
|
||
}
|
||
pi_devupd(); /* Update main PI state */
|
||
}
|
||
|
||
static void
|
||
dte20pifun(register struct device *d, int lev)
|
||
{
|
||
if (lev) { /* Requesting PI on level? */
|
||
d->dv_pireq = lev; /* Force new level for device */
|
||
if (cpu.pi.pilev_dtereq & lev) /* Already being requested? */
|
||
return; /* Yes, don't yell at CPU again */
|
||
cpu.pi.pilev_dtereq |= lev;
|
||
} else {
|
||
if (!(lev = d->dv_pireq)) /* Turning off -- dev already off? */
|
||
return; /* Yep, nothing to do */
|
||
d->dv_pireq = 0; /* Force level off for device */
|
||
if (!(cpu.pi.pilev_dtereq & lev)) /* DTEs already turned off? */
|
||
return; /* Yes, don't yell at CPU again */
|
||
|
||
/* Ugh, generic DTE PI indicator is turned on. Need to re-assess all
|
||
** DTEs to see if safe to turn off bit. Do this by simply summing
|
||
** all DTE request bits.
|
||
** In a multiproc environment this scan is a critical section.
|
||
*/
|
||
{
|
||
register struct device **adv;
|
||
|
||
cpu.pi.pilev_dtereq = 0;
|
||
for (adv = devdte20; *adv; ++adv)
|
||
cpu.pi.pilev_dtereq |= (*adv)->dv_pireq;
|
||
}
|
||
}
|
||
pi_devupd(); /* Update main PI state */
|
||
}
|
||
|
||
#endif /* KL */
|
||
|
||
|
||
static int
|
||
dviob_add(FILE *of, /* Error output */
|
||
register struct device *d, /* Device (post-define, pre-init) */
|
||
int num) /* IO Bus Device # to bind it to */
|
||
{
|
||
register int i;
|
||
|
||
if (devtab[num>>2] != &dvnull) {
|
||
if (of)
|
||
fprintf(of, "Cannot bind to IO device %o: already in use\n", num);
|
||
return FALSE;
|
||
}
|
||
|
||
#if KLH10_CPU_KL
|
||
/* Do special hackery on KL for Massbus and DTE devs */
|
||
if (DEVRH20(0) <= num && num < DEVRH20(8)) {
|
||
for (i = 0; i < NRH20; ++i) {
|
||
if (!devrh20[i] || (devrh20[i] == d))
|
||
break; /* Found entry, stop loop */
|
||
}
|
||
if (i >= NRH20) {
|
||
if (of)
|
||
fprintf(of, "Cannot bind RH20 %o: No slots left?!\n", num);
|
||
return FALSE;
|
||
}
|
||
d->dv_pifun = rh20pifun;
|
||
devrh20[i] = d;
|
||
} else if (DEVDTE20(0) <= num && num < DEVDTE20(4)) {
|
||
for (i = 0; i < NDTE20; ++i) {
|
||
if (!devdte20[i] || (devdte20[i] == d))
|
||
break; /* Found entry, stop loop */
|
||
}
|
||
if (i >= NDTE20) {
|
||
if (of)
|
||
fprintf(of, "Cannot bind DTE20 %o: No slots left?!\n", num);
|
||
return FALSE;
|
||
}
|
||
d->dv_pifun = dte20pifun;
|
||
devdte20[i] = d;
|
||
}
|
||
#endif /* KL */
|
||
|
||
devtab[num>>2] = d; /* Bind it! */
|
||
d->dv_num = num; /* Remember device # bound to */
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
#endif /* !KS */
|
||
|
||
/* DEV_INIT
|
||
** Initializes all device support. Called from main startup code.
|
||
*/
|
||
void
|
||
dev_init(void)
|
||
{
|
||
dvdefcnt = 0;
|
||
memset((char *)dvdeftab, 0, sizeof(dvdeftab));
|
||
|
||
#if KLH10_EVHS_INT
|
||
dev_evinit();
|
||
#endif
|
||
|
||
#if KLH10_CPU_KS
|
||
dvub_init(); /* New-style Unibus devices */
|
||
#else
|
||
dviob_init(); /* Old-style I/O bus devices */
|
||
#endif
|
||
}
|
||
|
||
/* DEV_TERM - Terminate device support.
|
||
** Called from main shutdown code.
|
||
**
|
||
** Doesn't bother cleaning up data structures; merely powers off all
|
||
** devices in case they're using system resources that need to be
|
||
** reclaimed.
|
||
*/
|
||
void
|
||
dev_term(void)
|
||
{
|
||
register struct dvdef_s *df;
|
||
|
||
/* Make two passes through the device definition table; once
|
||
** to turn off all controller subdevices, and then to turn off
|
||
** all others (controllers and otherwise).
|
||
** This ensures that the subdevice being powered off can count on
|
||
** its controller still being present.
|
||
*/
|
||
for (df = dvdeftab; df < &dvdeftab[KLH10_DEVMAX]; ++df) {
|
||
if (df->dev_name && (df->dev_flags & DVDF_CTLIO)) {
|
||
(*df->dev_dv->dv_powoff)(df->dev_dv); /* Turn it off */
|
||
}
|
||
}
|
||
|
||
/* Second pass, turn off everything that's NOT a controller subdev */
|
||
for (df = dvdeftab; df < &dvdeftab[KLH10_DEVMAX]; ++df) {
|
||
if (df->dev_name && !(df->dev_flags & DVDF_CTLIO)) {
|
||
(*df->dev_dv->dv_powoff)(df->dev_dv); /* Turn it off */
|
||
}
|
||
}
|
||
}
|
||
|
||
/* DEV_LOOKUP - Look up device definition
|
||
*/
|
||
static struct dvdef_s *
|
||
dev_lookup(char *name)
|
||
{
|
||
return (struct dvdef_s *)
|
||
s_fkeylookup(name, (void *)dvdeftab, sizeof(struct dvdef_s));
|
||
}
|
||
|
||
/* DEV_DRVLOOKUP - Look up device driver
|
||
*/
|
||
static struct dvdrv_s *
|
||
dev_drvlookup(char *name)
|
||
{
|
||
return (struct dvdrv_s *)
|
||
s_fkeylookup(name, (void *)dvdrvtab, sizeof(struct dvdrv_s));
|
||
}
|
||
|
||
/* DEV_NUMLOOKUP - Look up device number
|
||
*/
|
||
static int
|
||
dev_numlookup(char *name)
|
||
{
|
||
struct dvnum_s *dn;
|
||
|
||
dn = (struct dvnum_s *)
|
||
s_fkeylookup(name, (void *)dvnumtab, sizeof(*dn));
|
||
return (dn ? dn->dvn_num : -1);
|
||
}
|
||
|
||
|
||
/* Auxiliaries for dev_define. */
|
||
|
||
static struct dvdef_s *
|
||
dev_make(void)
|
||
{
|
||
register struct dvdef_s *dp;
|
||
|
||
if (dvdefcnt >= KLH10_DEVMAX-1) {
|
||
/* Print error msg? */
|
||
return NULL;
|
||
}
|
||
for (dp = dvdeftab; dp < &dvdeftab[KLH10_DEVMAX]; ++dp)
|
||
if (! dp->dev_name) {
|
||
memset((char *)dp, 0, sizeof(*dp)); /* Ensure entry cleared */
|
||
++dvdefcnt;
|
||
return dp; /* Won */
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
static void
|
||
dev_unmake(struct dvdef_s *dp)
|
||
{
|
||
if (dp->dev_name) {
|
||
free(dp->dev_name);
|
||
dp->dev_name = NULL;
|
||
}
|
||
--dvdefcnt;
|
||
}
|
||
|
||
|
||
/* Auxiliaries for dev_drvload. */
|
||
|
||
static struct dvdrv_s *
|
||
dev_drvmake(void)
|
||
{
|
||
register struct dvdrv_s *dp;
|
||
|
||
/* Scan entire table to find first free entry */
|
||
for (dp = dvdrvtab; dp < &dvdrvtab[KLH10_DEVMAX]; ++dp)
|
||
if (! dp->drv_name) {
|
||
memset((char *)dp, 0, sizeof(*dp)); /* Ensure entry cleared */
|
||
return dp; /* Won */
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
static void
|
||
dev_drvunmake(struct dvdrv_s *dp)
|
||
{
|
||
if (dp->drv_path || dp->drv_isym) {
|
||
if (dp->drv_name)
|
||
free(dp->drv_name);
|
||
if (dp->drv_path)
|
||
free(dp->drv_path);
|
||
if (dp->drv_isym)
|
||
free(dp->drv_isym);
|
||
if (dp->drv_desc)
|
||
free(dp->drv_desc);
|
||
memset((char *)dp, 0, sizeof(*dp));
|
||
}
|
||
}
|
||
|
||
/* DEV_LOAD - Load a dynamic/shared library as a device driver,
|
||
** and defines its name for later use in configuration.
|
||
** <name> - Arbitrary device driver name.
|
||
** <path> - Pathname for library in native OS.
|
||
** <isym> - "Entry point" symbol (init/config routine)
|
||
*/
|
||
|
||
int
|
||
dev_drvload(FILE *of, char *name, char *path, char *isym, char *args)
|
||
{
|
||
struct dvdrv_s *dp, ddef;
|
||
|
||
if (!(dp = dev_drvmake())) {
|
||
if (of)
|
||
fprintf(of, "Too many drivers, cannot load another\n");
|
||
return FALSE;
|
||
}
|
||
|
||
/* Check out 1st arg (driver name) for pre-existence */
|
||
if (dev_drvlookup(name)) {
|
||
if (of)
|
||
fprintf(of, "Device \"%s\" already exists, cannot reload\n", name);
|
||
dev_drvunmake(dp);
|
||
return FALSE;
|
||
}
|
||
|
||
/* Set up rest of entry. Do this first because that's easier to back
|
||
** out of, if load fails, than the reverse (i.e. unloading the driver!)
|
||
*/
|
||
if ( !(dp->drv_name = s_dup(name))
|
||
|| !(dp->drv_path = s_dup(path))
|
||
|| !(dp->drv_isym = s_dup(isym))
|
||
|| !(dp->drv_desc = s_dup(args))) { /* Rest of line is comment */
|
||
if (of)
|
||
fprintf(of, "Cannot malloc space for dvdrv entry\n");
|
||
dev_drvunmake(dp);
|
||
return FALSE;
|
||
}
|
||
|
||
/* Attempt to load up library. This is very OS dependent.
|
||
** For now, bundle the symbol lookup in there too because OS may not
|
||
** be able to do it separately.
|
||
*/
|
||
if (!os_dlload(of, ddef.drv_path, &(dp->drv_dllhdl),
|
||
ddef.drv_isym, (void **)&(dp->drv_init))) {
|
||
dev_drvunmake(dp); /* If fail, error already reported */
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE; /* Yow! Yow!*/
|
||
}
|
||
|
||
|
||
/* DEV_DEFINE - Do initial device configuration
|
||
** Defines the device name and does its initial configuration setup.
|
||
**
|
||
** <name> - Name to identify device
|
||
** <sdev> - A PDP-10 device number (dev-IO),
|
||
** or predefined keyword (dev-IO) such as CTY,
|
||
** or UB<n> - A KS10 Unibus Adapter (n = 1 or 3)
|
||
** <sdrv> - Name of a driver module, either builtin or dynamic.
|
||
** <args> - Optional args to dev's definition routine (rest of line)
|
||
**
|
||
** If the <sdev> and <sdrv> args check out, the following steps are done:
|
||
** (1) The driver module's "define" routine is called to obtain a
|
||
** device struct for it.
|
||
** (2) The device is bound either to a controller or directly to the 10.
|
||
** This is done by invoking the appropriate bind function.
|
||
** For the 10 this depends on the bus involved.
|
||
** For controllers, this is the dv_bind vector.
|
||
** (3) If bind succeeds, the device init is completed by invoking its
|
||
** dv_init vector.
|
||
*/
|
||
int
|
||
dev_define(FILE *of, char *name, char *sdev, char *sdrv, char *args)
|
||
{
|
||
char *cp;
|
||
register int dnum, flags = 0;
|
||
long lnum;
|
||
register struct dvdef_s *dp;
|
||
struct dvdrv_s *drv;
|
||
struct dvdef_s *ctldef = NULL; /* Possible controller */
|
||
|
||
/* Perhaps check here to make sure there's room for another device def
|
||
** before going farther, but no big deal.
|
||
*/
|
||
|
||
/* Check out device name for pre-existence */
|
||
if (dev_lookup(name)) {
|
||
if (of)
|
||
fprintf(of, "Device \"%s\" already exists, cannot redefine\n",
|
||
name);
|
||
return FALSE;
|
||
}
|
||
|
||
/* Parse 10-device binding specification.
|
||
** Syntax is a bit peculiar, depends on # of components.
|
||
** If 2, 1st must be an existing device name and the 2nd a plain number.
|
||
*/
|
||
if ((cp = strchr(sdev, '.'))) {
|
||
char dnbuf[100];
|
||
int i = cp - sdev;
|
||
|
||
if (i >= (sizeof(dnbuf)-1)) {
|
||
if (of)
|
||
fprintf(of, "Device binding \"%s\" too long.\n", sdev);
|
||
return FALSE;
|
||
}
|
||
memcpy(dnbuf, sdev, i);
|
||
dnbuf[i] = '\0';
|
||
if (!(ctldef = dev_lookup(dnbuf))) {
|
||
if (of)
|
||
fprintf(of, "Unknown device \"%s\" in binding \"%s\".\n",
|
||
dnbuf, sdev);
|
||
return FALSE;
|
||
}
|
||
if (!ctldef->dev_dv->dv_bind) {
|
||
if (of)
|
||
fprintf(of, "Device \"%s\" cannot accept bindings.\n", dnbuf);
|
||
return FALSE;
|
||
}
|
||
/* Have valid controller spec! Now get unit # for it... */
|
||
if (!s_tonum(++cp, &lnum)) { /* Parse rest as unit number */
|
||
if (of)
|
||
fprintf(of, "Unit binding must be a number (\"%s\" invalid).",
|
||
cp);
|
||
return FALSE;
|
||
}
|
||
dnum = (int)lnum;
|
||
flags |= DVDF_CTLIO;
|
||
|
||
} else {
|
||
cp = sdev;
|
||
if (isdigit(*cp)) {
|
||
if (s_tonum(cp, &lnum)) /* Parse as device number */
|
||
flags |= DVDF_DEVIO;
|
||
dnum = (int)lnum;
|
||
} else if (*cp == 'u' && cp[1] == 'b') {
|
||
if (s_tonum(cp+2, &lnum)) /* Parse as unibus controller # */
|
||
flags |= DVDF_UBIO;
|
||
dnum = (int)lnum;
|
||
} else { /* Parse as known-10-device name */
|
||
if (-1 != (dnum = dev_numlookup(cp))) /* -1 is error */
|
||
flags |= DVDF_DEVIO;
|
||
}
|
||
if (!(flags & (DVDF_DEVIO|DVDF_UBIO))) {
|
||
if (of)
|
||
fprintf(of, "Device num spec \"%s\" not recognized.\n", cp);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
/* Do a few sanity checks on the resulting number */
|
||
if (flags & DVDF_UBIO) {
|
||
/* Check out for new-style KS10 unibus IO */
|
||
#if KLH10_CPU_KS
|
||
if (dnum != 1 && dnum != 3) {
|
||
if (of)
|
||
fprintf(of, "Bad unibus #: %#.0o - only 1 or 3 supported\n",
|
||
dnum);
|
||
return FALSE;
|
||
}
|
||
#else
|
||
if (of)
|
||
fprintf(of, "Unibus devices not supported on non-KS10\n");
|
||
return FALSE;
|
||
#endif
|
||
} else if (flags & DVDF_DEVIO) {
|
||
/* Check out for old-style device IO */
|
||
#if !KLH10_CPU_KS
|
||
if ((dnum & 03) || (dnum > 0774)) {
|
||
if (of)
|
||
fprintf(of, "Bad dev number %#.0o, must be in {0,4,10,...,774}\n",
|
||
dnum);
|
||
return FALSE;
|
||
}
|
||
if (dnum <= (IODV_MTR<<2)) {
|
||
if (of)
|
||
fprintf(of, "Device %#.0o is internal, cannot be redefined\n",
|
||
dnum);
|
||
return FALSE;
|
||
}
|
||
#else
|
||
if (of)
|
||
fprintf(of, "Old-style IO devices not supported on KS10\n");
|
||
return FALSE;
|
||
#endif /* KS */
|
||
}
|
||
|
||
/* Parse driver keyword */
|
||
if (!(drv = dev_drvlookup(sdrv))) {
|
||
if (of)
|
||
fprintf(of, "Unknown device driver \"%s\"\n", sdrv);
|
||
return FALSE;
|
||
}
|
||
|
||
/* Have everything set up! Now put together real device definition. */
|
||
if (!(dp = dev_make())) {
|
||
if (of)
|
||
fprintf(of, "Out of room for more device defs\n");
|
||
return FALSE;
|
||
}
|
||
if (!(dp->dev_name = s_dup(name))) { /* Finally make it valid */
|
||
if (of)
|
||
fprintf(of, "Cannot malloc copy of device name\n");
|
||
dev_unmake(dp);
|
||
return FALSE;
|
||
}
|
||
dp->dev_ctl = ctldef;
|
||
dp->dev_num = dnum;
|
||
dp->dev_drv = drv;
|
||
dp->dev_flags = flags;
|
||
|
||
/* Invoke the driver's init-config routine on the rest of the arg line! */
|
||
if ((dp->dev_dv = (*(drv->drv_init))(of, args)) == NULL) {
|
||
if (of)
|
||
fprintf(of, "Device init failed\n");
|
||
/* Maybe need to flush device? */
|
||
dev_unmake(dp); /* Flush entry, free stg */
|
||
return FALSE;
|
||
}
|
||
dp->dev_dv->dv_name = dp->dev_name; /* Won, set name prior to bind */
|
||
#if KLH10_EVHS_INT
|
||
dp->dev_dv->dv_evreg = dev_evreg; /* Allow dev to register for events */
|
||
#endif
|
||
|
||
/* Now bind device to 10-side */
|
||
#if KLH10_CPU_KS
|
||
if (dp->dev_flags & DVDF_UBIO) {
|
||
/* Bind to specified unibus. This call reports its own errors.
|
||
*/
|
||
dp->dev_dv->dv_dflags |= DVFL_UBIO;
|
||
if (!dvub_add(of, dp->dev_dv, dp->dev_num)) {
|
||
dev_unmake(dp);
|
||
return FALSE;
|
||
}
|
||
}
|
||
#endif
|
||
#if !KLH10_CPU_KS
|
||
if (dp->dev_flags & DVDF_DEVIO) {
|
||
/* Bind to specified PDP-10 device #. Reports own errors.
|
||
*/
|
||
dp->dev_dv->dv_dflags |= DVFL_DEVIO;
|
||
if (!dviob_add(of, dp->dev_dv, dp->dev_num)) {
|
||
dev_unmake(dp);
|
||
return FALSE;
|
||
}
|
||
}
|
||
#endif /* !KS */
|
||
|
||
/* Ensure device agrees as to whether it's a controlled unit */
|
||
if (((dp->dev_flags & DVDF_CTLIO)==0)
|
||
!= ((dp->dev_dv->dv_dflags & DVFL_CTLIO)==0)) {
|
||
if (of)
|
||
fprintf(of,
|
||
"Ctlr/unit mismatch? Device %s %s to be controlled.\n",
|
||
dp->dev_name,
|
||
(dp->dev_dv->dv_dflags & DVFL_CTLIO)
|
||
? "wants" : "refuses");
|
||
dev_unmake(dp);
|
||
return FALSE;
|
||
}
|
||
if (dp->dev_flags & DVDF_CTLIO) {
|
||
/* Bind to specified controller. Have already checked for
|
||
** existence of dv_bind vector.
|
||
*/
|
||
dp->dev_dv->dv_ctlr = ctldef->dev_dv; /* Set up backpointer */
|
||
dp->dev_dv->dv_num = dp->dev_num; /* Set up dev unit # */
|
||
if (0 == (*ctldef->dev_dv->dv_bind)(ctldef->dev_dv, of,
|
||
dp->dev_dv, dp->dev_num)) {
|
||
if (of)
|
||
fprintf(of, "Cannot bind to %s unit %o: already in use?\n",
|
||
ctldef->dev_name, dp->dev_num);
|
||
dev_unmake(dp);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
|
||
/* Binding succeeded, now do final init for device */
|
||
if (0 == (*dp->dev_dv->dv_init)(dp->dev_dv, of)) {
|
||
if (of)
|
||
fprintf(of, "Final init of device \"%s\" failed!\n", dp->dev_name);
|
||
dev_unmake(dp);
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Various device command functions
|
||
*/
|
||
|
||
/* Handy auxiliary for most of the commands
|
||
*/
|
||
static struct device *
|
||
devverify(register FILE *of,
|
||
register char *dstr)
|
||
{
|
||
struct dvdef_s *def;
|
||
|
||
if (!dstr) {
|
||
if (of)
|
||
fprintf(of, "No device specified\n");
|
||
return NULL;
|
||
}
|
||
if (!(def = dev_lookup(dstr))) {
|
||
if (of)
|
||
fprintf(of, "Unknown device \"%s\"\n", dstr);
|
||
return NULL;
|
||
}
|
||
|
||
/* Exercise a little paranoia for now */
|
||
if (!def->dev_dv) {
|
||
if (of)
|
||
fprintf(of, "Internal error! Null vector for dev_dv !!\n");
|
||
return NULL;
|
||
}
|
||
return def->dev_dv;
|
||
}
|
||
|
||
/* Similar auxiliary for anything that wants to hack DPs
|
||
** Parse "<dev>" - just like devverify, existence of DP is irrelevant.
|
||
** *adp is set to NULL.
|
||
** also parse "<dev>.dp" - Sets *adp to DP pointer; returns NULL if *adp is
|
||
** NULL (meaning no DP exists, altho user is trying to
|
||
** refer to it).
|
||
*/
|
||
static struct device *
|
||
devdpverify(register FILE *of,
|
||
register char *dstr,
|
||
struct dp_s **adp)
|
||
{
|
||
register int i;
|
||
register char *cp;
|
||
char tmpstr[100];
|
||
register struct device *d;
|
||
|
||
*adp = NULL;
|
||
|
||
/* See if attempting reference to DP of a device */
|
||
if (dstr && (cp = strrchr(dstr, '.'))
|
||
&& (s_match(cp, ".dp") == 2)
|
||
&& ((i = cp-dstr) < sizeof(tmpstr))) {
|
||
|
||
/* Yes, identify device string. Copy so needn't mung arg */
|
||
memcpy(tmpstr, dstr, i);
|
||
tmpstr[i] = '\0';
|
||
if (!(d = devverify(of, tmpstr)))
|
||
return NULL;
|
||
if (!(*adp = d->dv_dpp)) {
|
||
if (of)
|
||
fprintf(of, "Device \"%s\" has no DP\n", d->dv_name);
|
||
return NULL;
|
||
}
|
||
return d; /* DP won! */
|
||
} else
|
||
return devverify(of, dstr); /* No ".dp" spec, do normal stuff */
|
||
}
|
||
|
||
|
||
/* DEV_COMMAND - Generic device command
|
||
** Syntax: dev <name> <commandline>
|
||
**
|
||
** Invokes device's command parsing and execution, which can be anything
|
||
** the device wants to do with the line. Most should follow certain
|
||
** conventions, however:
|
||
** dev <name> help - Show what this device understands
|
||
** dev <name> status - Show status in appropriate form
|
||
** dev <name> show [param] [...] - Show specific device vars
|
||
** dev <name> set [param=val] [...] - Set them
|
||
*/
|
||
int
|
||
dev_command(FILE *of,
|
||
char *dstr,
|
||
char *args)
|
||
{
|
||
register struct device *d;
|
||
|
||
if (!(d = devverify(of, dstr)))
|
||
return FALSE;
|
||
if (!d->dv_cmd) { /* Exercise more paranoia */
|
||
if (of)
|
||
fprintf(of, "Internal error! Null vector for dv_cmd\n");
|
||
return FALSE;
|
||
}
|
||
return (*(d->dv_cmd))(d, of, args); /* Invoke it! */
|
||
}
|
||
|
||
|
||
int
|
||
dev_help(FILE *of,
|
||
char *dstr,
|
||
char *args)
|
||
{
|
||
register struct device *d;
|
||
|
||
if (!dstr || strcmp(dstr, "?")==0) {
|
||
/* Show general help */
|
||
|
||
return TRUE;
|
||
}
|
||
if (!(d = devverify(of, dstr)))
|
||
return FALSE;
|
||
if (!d->dv_help) { /* Exercise more paranoia */
|
||
if (of)
|
||
fprintf(of, "Internal error! Null vector for dv_help !!\n");
|
||
return FALSE;
|
||
}
|
||
|
||
return (*(d->dv_help))(d, of); /* Invoke it! */
|
||
}
|
||
|
||
int
|
||
dev_status(FILE *of,
|
||
char *dstr,
|
||
char *args)
|
||
{
|
||
register struct device *d;
|
||
|
||
if (!dstr || strcmp(dstr, "?")==0) {
|
||
/* Show general status */
|
||
return TRUE;
|
||
}
|
||
if (!(d = devverify(of, dstr)))
|
||
return FALSE;
|
||
if (!d->dv_status) { /* Exercise more paranoia */
|
||
if (of)
|
||
fprintf(of, "Internal error! Null vector for dv_status !!\n");
|
||
return FALSE;
|
||
}
|
||
|
||
return (*(d->dv_status))(d, of); /* Invoke it! */
|
||
}
|
||
|
||
int
|
||
dev_mount(FILE *of,
|
||
char *dstr,
|
||
char *path,
|
||
char *args)
|
||
{
|
||
register struct device *d;
|
||
|
||
if (!(d = devverify(of, dstr)))
|
||
return FALSE;
|
||
if (!d->dv_mount) { /* Exercise more paranoia */
|
||
if (of)
|
||
fprintf(of, "Internal error! Null vector for dv_mount !!\n");
|
||
return FALSE;
|
||
}
|
||
|
||
return (*(d->dv_mount))(d, of, path, args); /* Invoke it! */
|
||
}
|
||
|
||
|
||
int
|
||
dev_debug(FILE *of,
|
||
char *dstr,
|
||
char *sval,
|
||
char *args)
|
||
{
|
||
long dbgval;
|
||
register struct device *d;
|
||
struct dp_s *dp;
|
||
|
||
if (!dstr || !*dstr || (*dstr == '?')) {
|
||
/* Show debug values for all known devices */
|
||
struct dvdef_s *def;
|
||
|
||
fprintf(of, " Device debug values:\n");
|
||
for (def = dvdeftab; def->dev_name; ++def) {
|
||
#if KLH10_DEV_DP
|
||
fprintf(of, " %6s = %d\n", def->dev_name,
|
||
def->dev_dv->dv_debug);
|
||
if ((dp = def->dev_dv->dv_dpp)) {
|
||
fprintf(of, " %6s.dp = ", def->dev_name);
|
||
if (dp->dp_adr)
|
||
fprintf(of, "%d\n", dp->dp_adr->dpc_debug);
|
||
else
|
||
fprintf(of, "??\n");
|
||
}
|
||
#else
|
||
fprintf(of, " %6s = %d\n", def->dev_name, def->dev_dv->dv_debug);
|
||
#endif
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
if (!(d = devdpverify(of, dstr, &dp)))
|
||
return FALSE;
|
||
|
||
/* Parse debug value */
|
||
if (!sval || !*sval) {
|
||
if (of) {
|
||
fprintf(of, " Debug value for %s = ", dstr);
|
||
#if KLH10_DEV_DP
|
||
if (dp) {
|
||
if (dp->dp_adr)
|
||
fprintf(of, "%d\n", dp->dp_adr->dpc_debug);
|
||
else
|
||
fprintf(of, "??\n");
|
||
} else
|
||
#endif
|
||
fprintf(of, "%d\n", d->dv_debug);
|
||
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
if (!s_tonum(sval, &dbgval)) {
|
||
if (of)
|
||
fprintf(of, "Bad syntax - \"%s\" not a number\n", sval);
|
||
return FALSE;
|
||
}
|
||
|
||
/* Won, set it!! */
|
||
#if KLH10_DEV_DP
|
||
if (dp) {
|
||
if (dp->dp_adr) {
|
||
dp->dp_adr->dpc_debug = dbgval;
|
||
return TRUE;
|
||
}
|
||
if (of)
|
||
fprintf(of, "DP not active, cannot set debug value\n");
|
||
return FALSE;
|
||
}
|
||
#endif
|
||
d->dv_debug = dbgval;
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
int
|
||
dev_show(FILE *of,
|
||
char *dstr,
|
||
char *args)
|
||
{
|
||
struct dvdef_s *def;
|
||
struct dvdrv_s *drv;
|
||
|
||
if (!dstr || strcmp(dstr, "?")==0) {
|
||
/* Show general config status, etc */
|
||
if (!of) return TRUE;
|
||
|
||
/* Show all defined drivers */
|
||
fprintf(of, "Known device drivers:\n");
|
||
fprintf(of, " Name Linkage EntryAddr \"Description\"\n");
|
||
for (drv = dvdrvtab; drv->drv_name; ++drv) {
|
||
fprintf(of, " %6s (%s) 0x%lX ", drv->drv_name,
|
||
(drv->drv_path ? "extern-dll" : "static-lib"),
|
||
(long)(drv->drv_init));
|
||
if (drv->drv_path)
|
||
fprintf(of, "{path=%s isym=%s}", drv->drv_path, drv->drv_isym);
|
||
fprintf(of, " \"%s\"\n", drv->drv_desc);
|
||
}
|
||
|
||
/* Show all bound devices */
|
||
fprintf(of, "\nDefined devices:\n");
|
||
fprintf(of, " DevID Dev# Driver StructAddr\n");
|
||
for (def = dvdeftab; def->dev_name; ++def) {
|
||
fprintf(of, " %6s ", def->dev_name);
|
||
if (def->dev_flags & DVDF_UBIO) {
|
||
struct device *dv = def->dev_dv;
|
||
fprintf(of,
|
||
" UB%d %-6s 0x%lX addr=%#.0o:%#.0o br=%d vec=%#.0o\n",
|
||
def->dev_num, def->dev_drv->drv_name,
|
||
(unsigned long)dv,
|
||
dv->dv_addr, dv->dv_aend, dv->dv_brlev,
|
||
dv->dv_brvec);
|
||
} else if (def->dev_flags & DVDF_DEVIO) {
|
||
fprintf(of,
|
||
"%6o %-6s 0x%lX\n",
|
||
def->dev_num, def->dev_drv->drv_name,
|
||
(unsigned long)def->dev_dv);
|
||
} else if (def->dev_flags & DVDF_CTLIO) {
|
||
fprintf(of,
|
||
"%6s.%o %-6s 0x%lX\n",
|
||
def->dev_ctl->dev_name,
|
||
def->dev_num, def->dev_drv->drv_name,
|
||
(unsigned long)def->dev_dv);
|
||
} else
|
||
fprintf(of, "\?\?\?\n");
|
||
}
|
||
|
||
/* List known device number mnemonics? */
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
#if 0
|
||
if (!(def = dev_lookup(dstr))) {
|
||
if (of)
|
||
fprintf(of, "Unknown device driver \"%s\"\n", dstr);
|
||
return FALSE;
|
||
}
|
||
|
||
/* Exercise a little paranoia for now */
|
||
if (!def->dev_dv || !def->dev_dv->dv_help) {
|
||
if (of)
|
||
fprintf(of, "Internal error! Null vector for %s!!\n",
|
||
def->dev_dv ? "dev_dv" : "dev_dv->dv_help");
|
||
return FALSE;
|
||
}
|
||
|
||
/* Found it, now invoke it */
|
||
return (*(def->dev_dv->dv_help))(def->dev_dv, of);
|
||
#endif
|
||
}
|
||
|
||
/* Device boot code */
|
||
|
||
/*
|
||
|
||
The device boot code doesn't go in any really obvious place as it can
|
||
combine CPU instructions, device invocations, and FE knowledge of
|
||
where bootstraps are located, the latter being the most devious.
|
||
|
||
For now, put it here.
|
||
|
||
*/
|
||
|
||
#define FE_BOOTLOC 01000
|
||
#define FE_BOOTLEN 512
|
||
|
||
/* DEV_BOOT - Read bootstrap from device using "read-in mode"
|
||
**
|
||
*/
|
||
int
|
||
dev_boot(FILE *of,
|
||
char *dstr,
|
||
vaddr_t *bootsa)
|
||
{
|
||
register struct device *d, *ctlr;
|
||
register w10_t w;
|
||
register vmptr_t vp;
|
||
int res;
|
||
|
||
/* XXX: Later use first controller if no dev specified? */
|
||
|
||
if (!(d = devverify(of, dstr)))
|
||
return FALSE;
|
||
|
||
/* Determine device type.
|
||
XXX: If it's a controller, use first live device on it.
|
||
*/
|
||
if (d->dv_dflags & DVFL_CTLR) {
|
||
if (of) fprintf(of, "Cannot boot from a controller\n");
|
||
return FALSE;
|
||
} else
|
||
ctlr = d->dv_ctlr;
|
||
|
||
/* Have device, now determine whether dealing with random-access
|
||
block mode device, or tape device.
|
||
*/
|
||
#if KLH10_CPU_KS
|
||
/* Must set up device variables in FECOM area so boot code knows
|
||
where it came from.
|
||
*/
|
||
if (ctlr) {
|
||
W10_XSET(w, ctlr->dv_uba->ubn, ctlr->dv_addr);
|
||
vm_pset(vm_physmap(FECOM_RHBAS), w); /* RH11 base address */
|
||
W10_U32SET(w, d->dv_num);
|
||
vm_pset(vm_physmap(FECOM_QNUM), w); /* RH11 unit number */
|
||
W10_U32SET(w, (TM_D16<<8) | (TM_FCD<<4) | 0);
|
||
vm_pset(vm_physmap(FECOM_BOOTP), w); /* Tape OFTC (dens & slave) */
|
||
}
|
||
|
||
W10_U32SET(w, 1); /* Address: one file or one sector in */
|
||
vp = vm_physmap(FE_BOOTLOC); /* Read into here */
|
||
if (d->dv_dflags & DVFL_TAPE) {
|
||
/* A boot tape is set up so the first file is microcode, and
|
||
the second is a PDP-10 boot image. The first record of that
|
||
boot (normally 512 wds) is read into loc 1000 and the 10
|
||
started there.
|
||
*/
|
||
if (of) fprintf(of, "Reading 1st record of 2nd file ... ");
|
||
res = (*d->dv_readin)(d, of, w, (w10_t *)vp, FE_BOOTLEN);
|
||
if (!res)
|
||
return FALSE;
|
||
if (of) fprintf(of, "OK\nRead in %ld words at %lo\n",
|
||
(long)res, (long)FE_BOOTLOC);
|
||
*bootsa = FE_BOOTLOC;
|
||
|
||
} else if (d->dv_dflags & DVFL_DISK) {
|
||
/* KS disk boot is messy.
|
||
First reads sector #1 (each sector 128 words)
|
||
and verifies first word is 'HOM '.
|
||
Then reads sector #010 and does same verification.
|
||
If OK, takes disk address from loc +103 in that block,
|
||
reads it, gets another disk address from loc +4,
|
||
and finally uses that to read the first 512 words (4
|
||
sectors) of the "real" 10 boot. Jumps to 1000 to start.
|
||
*/
|
||
w10_t w6hom = W10_XINIT(0505755,0); /* sixbit /HOM / */
|
||
|
||
if (of) fprintf(of, "Reading HOM sector 1 ... ");
|
||
if (!(res = (*d->dv_readin)(d, of, w, (w10_t *)vp, 128)))
|
||
return FALSE;
|
||
if (op10m_camn(vm_pget(vp), w6hom)) {
|
||
if (of) fprintf(of, "Bad HOM check, val = %lo,,%lo\n",
|
||
(long)LHGET(w), (long)RHGET(w));
|
||
return FALSE;
|
||
}
|
||
if (of) fprintf(of, "OK\nReading HOM sector 010 ...");
|
||
W10_U32SET(w, 010);
|
||
if (!(res = (*d->dv_readin)(d, of, w, (w10_t *)vp, 128)))
|
||
return FALSE;
|
||
if (op10m_camn(vm_pget(vp), w6hom)) {
|
||
if (of) fprintf(of, "Bad HOM check, val = %lo,,%lo\n",
|
||
(long)LHGET(w), (long)RHGET(w));
|
||
return FALSE;
|
||
}
|
||
w = vm_pget(vm_physmap(FE_BOOTLOC+0103)); /* Get ptrs pag addr */
|
||
if (op10m_skipn(w)) {
|
||
if (of) fprintf(of, "Bad ptr pag addr from HOM blk = %lo,,%lo\n",
|
||
(long)LHGET(w), (long)RHGET(w));
|
||
return FALSE;
|
||
}
|
||
if (of) fprintf(of, "OK\nReading pointers sector at %lo,,%lo ...",
|
||
(long)LHGET(w), (long)RHGET(w));
|
||
if (!(res = (*d->dv_readin)(d, of, w, (w10_t *)vp, 128)))
|
||
return FALSE;
|
||
w = vm_pget(vm_physmap(FE_BOOTLOC+04)); /* Get boot code addr */
|
||
if (op10m_skipn(w)) {
|
||
if (of) fprintf(of, "Bad bootcode addr from ptr blk = %lo,,%lo\n",
|
||
(long)LHGET(w), (long)RHGET(w));
|
||
return FALSE;
|
||
}
|
||
|
||
/* Finally! Note we now read in a full page (4 sectors) */
|
||
if (of) fprintf(of, "OK\nReading 4 boot sectors at %lo,,%lo ...",
|
||
(long)LHGET(w), (long)RHGET(w));
|
||
if (!(res = (*d->dv_readin)(d, of, w, (w10_t *)vp, 512)))
|
||
return FALSE;
|
||
|
||
if (of) fprintf(of, "OK\nRead in %ld words at %lo\n",
|
||
(long)res, (long)FE_BOOTLOC);
|
||
*bootsa = FE_BOOTLOC;
|
||
|
||
}
|
||
|
||
#elif KLH10_CPU_KA || KLH10_CPU_KI
|
||
/* Do one DATAI into loc 0, then treat it as
|
||
a BLKI pointer. When done, KI jumps to last word,
|
||
KA executes it.
|
||
*/
|
||
{
|
||
int32 cnt;
|
||
#if KLH10_CPU_KA
|
||
w10_t tmp = W10XWD(1,1); /* KA adds 1,,1 */
|
||
#endif
|
||
w = (*d->dv_datai)(d);
|
||
|
||
if ((cnt = LHGET(w)) & H10SIGN)
|
||
cnt = -(cnt | ~((int32)MASK18));
|
||
vp = vm_xrwmap(0); /* Will be in AC so map it */
|
||
|
||
if (of) fprintf(of, "Read BLKI=%lo,,%lo (%ld words)\n"
|
||
LHGET(w), RHGET(w), (long)cnt);
|
||
vm_pset(vp, w);
|
||
|
||
/* Now do BLKI loop. Always inputs at least one word */
|
||
for (cnt = 1; ; ++cnt) {
|
||
/* Get word and store it.
|
||
Re-map each time since may cross AC or address bounds
|
||
*/
|
||
vm_pset(vm_xrwmap(RHGET(w)), (*d->dv_datai)(d));
|
||
#if KLH10_CPU_KA
|
||
op10m_add(w, tmp); /* KA adds 1,,1 */
|
||
#else /* KI increments independently */
|
||
LRHSET(w, (LHGET(w)+1)&H10MASK, (RHGET(w)+1)&H10MASK);
|
||
#endif
|
||
vm_pset(vp, w); /* Store back inc'd pointer */
|
||
if (LHGET(w) == 0)
|
||
break;
|
||
}
|
||
if (of) fprintf(of, "Readin complete, %ld words\n", (long)cnt);
|
||
*bootsa = (vaddr_t)RHGET(w);
|
||
}
|
||
|
||
#else
|
||
if (0) ;
|
||
#endif
|
||
else {
|
||
if (of) fprintf(of, "%s not supported for boot\n", d->dv_name);
|
||
return FALSE;
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
/* First stab at support for device subprocs -- cooperates with DPSUP code */
|
||
|
||
#if KLH10_EVHS_INT
|
||
|
||
/* Event callback registration:
|
||
|
||
The mechanism for organizing asynchronous events will rely on
|
||
having handlers register themselves with DEV_EVREG. The various types
|
||
of events understood are defined in kn10dev.h as DVEV_xxx values.
|
||
For now, event handlers are *only* invoked atomically between
|
||
instructions, as part of INSBRK processing. They are never invoked
|
||
at "OS interrupt level", i.e. as part of an OS signal handler. Such
|
||
signal handlers merely set the appropriate flags so that INSBRK will
|
||
know that the respective event happened.
|
||
If handlers generate any PI interrupts, those will take effect
|
||
before any instruction is executed.
|
||
|
||
Some notes on specific events:
|
||
|
||
DVEV_1SIG - Single signal specified by eva_int.
|
||
Event handler wants to monopolize this signal -- registration fails
|
||
if anyone else tries to register for it.
|
||
When signal happens, its handler is always invoked.
|
||
|
||
DVEV_NSIG - Multiplexed signal specified by eva_int.
|
||
Event handler is willing to share this signal.
|
||
When signal happens, *all* registered handlers are invoked, since
|
||
without additional hackery there's no way to know which one was
|
||
intended to receive the signal.
|
||
This is rather inefficient and at the top of the list.
|
||
|
||
DVEV_ASIG - Allocated signal. No arg; signal value is returned.
|
||
This type asks the registrar to find an unused single signal
|
||
if possible, or a multiplexed one if not.
|
||
|
||
DVEV_DPSIG - Device subProcess signal specified by eva_int.
|
||
Pointer to flag check word specified by 2nd arg's eva_ip.
|
||
Like DVEV_NSIG but handler only invoked if flag is non-zero
|
||
(flag is cleared just prior to invoking handler).
|
||
|
||
DVEV_CLOCK - Periodic clock callout. Time in eva_int is # msec.
|
||
The time will be converted to some # of internal clock ticks
|
||
by rounding to the nearest tick (but no less than 1).
|
||
|
||
DVEV_TIMOUT - Clock timeout. Time in eva_int is # msec.
|
||
Same as DVEV_CLOCK except routine is de-registered just before
|
||
the callback is made.
|
||
|
||
*/
|
||
|
||
|
||
#ifndef SIGMAX
|
||
# ifndef NSIG
|
||
# define SIGMAX 32 /* Probable guess */
|
||
# else
|
||
# define SIGMAX NSIG
|
||
# endif
|
||
#endif
|
||
|
||
#ifndef KLH10_EVHS_MAX
|
||
# define KLH10_EVHS_MAX (KLH10_DEVMAX+10)
|
||
#endif
|
||
|
||
struct dvevsig_s *evsiglist;
|
||
struct dvevsig_s evsigtab[SIGMAX]; /* Registered signals */
|
||
|
||
struct dvevreg_s *evregfree;
|
||
struct dvevreg_s evregtab[KLH10_EVHS_MAX]; /* Registered handlers */
|
||
|
||
static void dev_evunreg(struct device *);
|
||
|
||
/* DEV_EVINIT - Initialize event stuff
|
||
*/
|
||
void
|
||
dev_evinit(void)
|
||
{
|
||
register struct dvevreg_s *evr;
|
||
|
||
INTF_INIT(cpu.intf_evsig);
|
||
evsiglist = NULL;
|
||
memset((char *)evsigtab, 0, sizeof(evsigtab));
|
||
memset((char *)evregtab, 0, sizeof(evregtab));
|
||
|
||
/* Set up handler entry freelist */
|
||
evregfree = evregtab;
|
||
for (evr = evregfree; evr < &evregtab[KLH10_EVHS_MAX-1]; ++evr)
|
||
evr->dver_next = evr+1;
|
||
evr->dver_next = NULL;
|
||
}
|
||
|
||
static void
|
||
dev_sighan(int sig)
|
||
{
|
||
if (0 < sig && sig < SIGMAX) { /* Paranoia */
|
||
INTF_SET(evsigtab[sig].dves_intf); /* Say this signal seen */
|
||
INTF_SET(cpu.intf_evsig); /* Say some signal seen */
|
||
INSBRKSET(); /* Interrupt instr loop */
|
||
}
|
||
}
|
||
|
||
/* DEV_EVCHECK - Called at INSBRK to process any valid events
|
||
*/
|
||
|
||
void
|
||
dev_evcheck(void)
|
||
{
|
||
register struct dvevsig_s *evs;
|
||
register struct dvevreg_s *evr;
|
||
|
||
/* Check all signals to see which ones went off */
|
||
for (evs = evsiglist; evs; evs = evs->dves_next) {
|
||
if (INTF_TEST(evs->dves_intf)) {
|
||
INTF_ACTBEG(evs->dves_intf);
|
||
|
||
/* Process all handlers registered for this signal */
|
||
for (evr = evs->dves_reglist; evr; evr = evr->dver_next) {
|
||
if (*(evr->dver_ev.dvev_arg2.eva_ip)) {
|
||
/* If this handler's flag shows it really does want
|
||
** the call, invoke handler after clearing flag!
|
||
*/
|
||
*(evr->dver_ev.dvev_arg2.eva_ip) = 0;
|
||
(*(evr->dver_hdlr))(evr->dver_d, &(evr->dver_ev));
|
||
}
|
||
}
|
||
|
||
INTF_ACTEND(evs->dves_intf);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* DEV_EVREG - Called by devices through device vector.
|
||
** Registers a callback routine for handling a specific event.
|
||
** If any suitable event happens, routine will be invoked atomically
|
||
** between instructions (as part of INSBRK processing). It should
|
||
** be defined as:
|
||
** callback_rtn(struct device *d, int evtyp, int evarg)
|
||
**
|
||
*/
|
||
int
|
||
dev_evreg(register struct device *d,
|
||
void (*rtn)(struct device *, struct dvevent_s *),
|
||
register struct dvevent_s *evp)
|
||
{
|
||
register struct dvevreg_s *evr;
|
||
register struct dvevsig_s *evs;
|
||
int sig;
|
||
ossigset_t oldmask, allmask;
|
||
|
||
/* First ensure we aren't interrupted while messing with table */
|
||
os_sigfillset(&allmask);
|
||
(void) os_sigsetmask(&allmask, &oldmask);
|
||
|
||
/* Check if unregistering device and handle if so */
|
||
if (rtn == NULL) {
|
||
dev_evunreg(d); /* Flush it */
|
||
(void) os_sigsetmask(&oldmask, (ossigset_t *)NULL);
|
||
return TRUE;
|
||
}
|
||
|
||
/* Find new entry in evreg table */
|
||
if ((evr = evregfree)) { /* Pluck from freelist */
|
||
evregfree = evr->dver_next; /* Freelist is one-way, no prev */
|
||
} else {
|
||
fprintf(stderr, "[dev_evreg: out of table entries!]\r\n");
|
||
(void) os_sigsetmask(&oldmask, (ossigset_t *)NULL);
|
||
return 0;
|
||
}
|
||
|
||
evr->dver_hdlr = rtn; /* Remember callback rtn */
|
||
evr->dver_d = d; /* Remember device */
|
||
evr->dver_ev = *evp; /* Remember event & args */
|
||
|
||
switch (evp->dvev_type) {
|
||
|
||
case DVEV_DPSIG:
|
||
/* Ensure 2 args look sensible */
|
||
sig = evp->dvev_arg.eva_int;
|
||
if (sig <= 0 || SIGMAX <= sig) {
|
||
fprintf(stderr, "[dev_evreg: Bad signal: %d]\r\n", sig);
|
||
break; /* Fail... */
|
||
}
|
||
if (! evp->dvev_arg2.eva_ip) {
|
||
fprintf(stderr, "[dev_evreg: Bad DPSIG pointer]\r\n");
|
||
break; /* Fail... */
|
||
}
|
||
*(evp->dvev_arg2.eva_ip) = 0; /* Clear request flag */
|
||
|
||
/* If signal not already registered, add it */
|
||
evs = &evsigtab[sig];
|
||
if (evs->dves_sig == 0) {
|
||
|
||
/* New, see if can install OS signal handler */
|
||
if (osux_signal(sig, dev_sighan) == -1) {
|
||
fprintf(stderr, "[dev_evreg: Can't handle sig %d - %s]\r\n",
|
||
sig, os_strerror(-1));
|
||
break; /* Fail... */
|
||
}
|
||
|
||
/* Yup, proceed with registration! */
|
||
evs->dves_sig = sig;
|
||
INTF_INIT(evs->dves_intf);
|
||
evs->dves_reglist = NULL;
|
||
evs->dves_prev = NULL;
|
||
if ((evs->dves_next = evsiglist)) /* Add to head of list */
|
||
evsiglist->dves_prev = evs;
|
||
evsiglist = evs;
|
||
}
|
||
|
||
/* Now add handler to list for this signal */
|
||
evr->dver_prev = NULL;
|
||
if ((evr->dver_next = evs->dves_reglist))/* Add to head of list */
|
||
evr->dver_next->dver_prev = evr;
|
||
evs->dves_reglist = evr;
|
||
|
||
(void) os_sigsetmask(&oldmask, (ossigset_t *)NULL);
|
||
return TRUE;
|
||
|
||
default:
|
||
/* Can't handle any other event type, break out to fail */
|
||
break;
|
||
}
|
||
|
||
/* Failure if get here */
|
||
|
||
/* Put event entry back on freelist */
|
||
evr->dver_next = evregfree; /* Freelist is one-way, no prev */
|
||
evregfree = evr;
|
||
(void) os_sigsetmask(&oldmask, (ossigset_t *)NULL);
|
||
return 0;
|
||
}
|
||
|
||
/* DEV_EVUNREG - De-registers specified device completely.
|
||
** Currently only used when device is powering off, so no need
|
||
** to distinguish between events/handlers for a device.
|
||
** Note that interrupts are suspended by caller (dev_evreg).
|
||
*/
|
||
static void
|
||
dev_evunreg(register struct device *d)
|
||
{
|
||
register struct dvevreg_s *evr, *nextevr;
|
||
register struct dvevsig_s *evs, *nextevs;
|
||
|
||
/* Scan all signals handled */
|
||
for (evs = evsiglist; evs; evs = nextevs) {
|
||
nextevs = evs->dves_next;
|
||
|
||
/* Scan all handlers registered for this signal */
|
||
for (evr = evs->dves_reglist; evr; evr = nextevr) {
|
||
nextevr = evr->dver_next;
|
||
if (evr->dver_d == d) { /* Matching device? */
|
||
/* Take it off list */
|
||
if (evr->dver_prev)
|
||
evr->dver_prev->dver_next = nextevr;
|
||
else
|
||
evs->dves_reglist = evr->dver_next; /* Fix up head */
|
||
if (nextevr)
|
||
nextevr->dver_prev = evr->dver_prev;
|
||
|
||
/* Put on freelist */
|
||
evr->dver_next = evregfree; /* Flist is one-way, no prev */
|
||
evregfree = evr;
|
||
}
|
||
}
|
||
|
||
/* Handler scan done, now see if anything left for signal to do */
|
||
if (evs->dves_reglist == NULL) {
|
||
/* All gone, so turn off handling of signal.
|
||
** This is probably superfluous, but tidy is as tidy do.
|
||
** Ignore errors since some signals may be un-ignorable.
|
||
*/
|
||
osux_signal(evs->dves_sig, SIG_IGN);
|
||
|
||
/* Take it off list */
|
||
if (evs->dves_prev)
|
||
evs->dves_prev->dves_next = nextevs;
|
||
else
|
||
evsiglist = evs->dves_next; /* Fix up head */
|
||
if (nextevs)
|
||
nextevs->dves_prev = evs->dves_prev;
|
||
|
||
/* Now say it's free (uses indexed table, no freelist) */
|
||
evs->dves_sig = 0; /* No signal in effect */
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/* DEV_EVSHOW - Show status of event registration data
|
||
** Maybe should lock out interrupts while doing this...
|
||
*/
|
||
int
|
||
dev_evshow(FILE *of,
|
||
char *dstr,
|
||
char *args)
|
||
{
|
||
register struct dvevreg_s *evr;
|
||
register struct dvevsig_s *evs;
|
||
register int i;
|
||
struct dvdef_s *def;
|
||
|
||
#if 0 /* For now, ignore any args */
|
||
if (!dstr || strcmp(dstr, "?")==0)
|
||
/* Show general config status, etc */
|
||
|
||
#endif
|
||
if (of) {
|
||
fprintf(of, "Outstanding event flag: %lo\n", (long)cpu.intf_evsig);
|
||
|
||
/* Show all registered event handlers */
|
||
fprintf(of, "Registered event handlers:\n");
|
||
|
||
for (evs = evsiglist; evs; evs = evs->dves_next) {
|
||
|
||
/* Show signal event and stuff registered for it */
|
||
fprintf(of, " Signal: Sig %d, intf=%ld\n",
|
||
evs->dves_sig, (long)evs->dves_intf);
|
||
|
||
/* Show all handlers registered for this signal */
|
||
for (evr = evs->dves_reglist; evr; evr = evr->dver_next) {
|
||
fprintf(of, " Hdlr: 0x%lx (", (long)(evr->dver_hdlr));
|
||
|
||
/* Try to identify device */
|
||
for (def = dvdeftab; def->dev_name; ++def) {
|
||
if (def->dev_dv == evr->dver_d)
|
||
break;
|
||
}
|
||
if (def->dev_name)
|
||
fprintf(of, "dev \"%s\"", def->dev_name);
|
||
else
|
||
fprintf(of, "unknowndev 0x%lx",
|
||
(unsigned long)(evr->dver_d));
|
||
|
||
/* Now show its event args in general */
|
||
fprintf(of, ", ev{%o, 0x%lx, 0x%lx})",
|
||
i = evr->dver_ev.dvev_type,
|
||
evr->dver_ev.dvev_arg.eva_long,
|
||
evr->dver_ev.dvev_arg2.eva_long);
|
||
|
||
/* Try to show event-specific stuff */
|
||
if (i == DVEV_DPSIG) {
|
||
fprintf(of, " DPSIG %d, Flag=%o",
|
||
evr->dver_ev.dvev_arg.eva_int,
|
||
*(evr->dver_ev.dvev_arg2.eva_ip));
|
||
}
|
||
fputc('\n', of);
|
||
}
|
||
}
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
#endif /* KLH10_EVHS_INT */
|
||
|
||
/* Support for asynch device attention during FE command loop
|
||
|
||
The problem arises when using DPs for devices with mountable media.
|
||
When a user does a device (normally tape) mount or dismount at the
|
||
command loop, the command is relayed on to the DP, but the acknowledgement
|
||
of the response is normally handled by the CPU processing loop (see
|
||
apr_check().) Without an explicit call to dev_evcheck these devices
|
||
would appear to remain busy until the CPU is continued again.
|
||
|
||
Solution:
|
||
If any command is executed that might require DP waiting, do a check
|
||
and if waiting for any DPs, turn hackery on if config/switch permits.
|
||
|
||
If hackery enabled, during cmd input wait enable SIGALRM with
|
||
a sigaction sa_flag of SA_RESTART so as not to interfere with
|
||
any pending input. All known systems now support that.
|
||
|
||
On each SIGALRM, call dev_evcheck(), then return quietly.
|
||
|
||
Between each command, attempt to turn hackery off if no more
|
||
DPs are waiting.
|
||
|
||
If calling apr_run, force hackery off.
|
||
|
||
Plus "devwait" added for benefit of command scripts:
|
||
|
||
devwait [dev] [n]
|
||
Wait for device DPs to become ready:
|
||
no args - all, indefinitely
|
||
[dev] - that dev only
|
||
[n] - for N seconds
|
||
|
||
Also implement dev_status call to return actual status when desired,
|
||
eliminating the crock of using a null arg to devmount.
|
||
*/
|
||
|
||
static int dev_dpwaiting(void);
|
||
|
||
/* Signal handler for command-level periodic check
|
||
*/
|
||
#if KLH10_DEV_DP
|
||
static void
|
||
dev_dpchk_sighan(int sig)
|
||
{
|
||
#if KLH10_EVHS_INT
|
||
dev_evcheck();
|
||
#endif
|
||
}
|
||
#endif /* KLH10_DEV_DP */
|
||
|
||
/* DEV_DPCHK_CTL - Turn dev-dp checking on or off.
|
||
**
|
||
** Intended use:
|
||
** After every command that affects devices:
|
||
** chkflag = dev_dpchk_ctl(TRUE); // Start checking if necessary
|
||
** Between entry of each command and its execution:
|
||
** if (chkflag) // If on, try to turn checking off
|
||
** chkflag = dev_dpchk_ctl(TRUE);
|
||
** Before calling apr_run to proceed PDP-10:
|
||
** chkflag = dev_dpchk_ctl(FALSE); // Turn checking off
|
||
*/
|
||
int
|
||
dev_dpchk_ctl(int dochk) /* Arg TRUE to do a check */
|
||
{
|
||
#if KLH10_DEV_DP
|
||
static int ischecking = 0;
|
||
static ostimer_t savestate;
|
||
int ndps;
|
||
|
||
if (dochk) {
|
||
ndps = dev_dpwaiting(); /* Find # devs waiting */
|
||
if (ndps && !ischecking) {
|
||
/* Devs waiting and not checking, so turn checking on */
|
||
os_timer(OS_ITIMER_REAL, dev_dpchk_sighan,
|
||
1000000, /* One sec's worth of usec */
|
||
&savestate);
|
||
ischecking = TRUE;
|
||
}
|
||
} else
|
||
ndps = 0;
|
||
|
||
if (!ndps && ischecking) {
|
||
/* No devs but we're checking, so turn checking off */
|
||
ischecking = FALSE;
|
||
os_timer_restore(&savestate);
|
||
}
|
||
return ischecking;
|
||
#else
|
||
return FALSE;
|
||
#endif /* !KLH10_DEV_DP */
|
||
}
|
||
|
||
|
||
/* DEV_DPWAITING - Return some indication of the number of DPs that are
|
||
being waited for, i.e. which are not ready to accept new commands.
|
||
|
||
Returns:
|
||
>=1 if there are any DPs waiting
|
||
-1 if there are any DPs which required a call to dev_evcheck().
|
||
0 if there are neither - can stop checking!
|
||
|
||
Could either step thru all devs, or go through event reg list.
|
||
Latter probably a little faster.
|
||
*/
|
||
static int
|
||
dev_dpwaiting(void)
|
||
{
|
||
#if KLH10_EVHS_INT
|
||
register struct dvevsig_s *evs;
|
||
register struct dvevreg_s *evr;
|
||
register int cnt = 0;
|
||
|
||
/* Our DP registry starts with a list of signals; check them all,
|
||
and check all handlers registered for each signal.
|
||
*/
|
||
for (evs = evsiglist; evs; evs = evs->dves_next) {
|
||
for (evr = evs->dves_reglist; evr; evr = evr->dver_next) {
|
||
/* If the handler's flag indicates it's waiting to be processed
|
||
by dev_evcheck(), call it immediately and return with
|
||
an indication that this was done.
|
||
No re-check is performed (even though it might indicate
|
||
that everything's clear) in order to avoid infinite loops for
|
||
a misbehaving device.
|
||
*/
|
||
if (*(evr->dver_ev.dvev_arg2.eva_ip)) {
|
||
dev_evcheck();
|
||
return -1;
|
||
}
|
||
|
||
/* Count as "waiting" any registered device for which
|
||
its comm area says "cannot send". Keep going in case
|
||
we later find a device waiting to be processed as above.
|
||
*/
|
||
if (!dp_xstest(dp_dpxto(evr->dver_d->dv_dpp)))
|
||
++cnt;
|
||
}
|
||
}
|
||
return cnt;
|
||
#elif KLH10_DEV_DP
|
||
register struct dvdef_s *df;
|
||
|
||
/* Grovel through entire device definition table.
|
||
** This would be a good place to have a list of just DP devices.
|
||
*/
|
||
for (df = dvdeftab; df < &dvdeftab[KLH10_DEVMAX]; ++df) {
|
||
if (df->dev_name && df->dev_dv->dv_dpp) {
|
||
/* Count as "waiting" any device having a DP for which
|
||
its comm area says "cannot send".
|
||
*/
|
||
if (!dp_xstest(dp_dpxto(df->dev_dv->dv_dpp)))
|
||
return 1;
|
||
}
|
||
}
|
||
return 0;
|
||
#else
|
||
return 0;
|
||
#endif
|
||
}
|
||
|
||
/* DEV_WAITING - returns TRUE if a device is waiting for its DP to respond.
|
||
*/
|
||
int
|
||
dev_waiting(FILE *of, char *dstr)
|
||
{
|
||
#if KLH10_DEV_DP
|
||
register struct device *d;
|
||
struct dp_s *dp;
|
||
|
||
if (!dstr || !*dstr || (*dstr == '*')) {
|
||
/* Check all devices */
|
||
return (dev_dpwaiting() ? TRUE : FALSE);
|
||
}
|
||
if (!(d = devdpverify(of, dstr, &dp)) || !dp)
|
||
return FALSE; /* No such dev, or has no DP */
|
||
|
||
/* Check out specific DP. First do complete event check to remove
|
||
possibility that it might be ready but its event needs handling.
|
||
(and if nothing's waiting, can skip specific check).
|
||
*/
|
||
if (dev_dpwaiting() /* If any DPs still waiting, */
|
||
&& !dp_xstest(dp_dpxto(dp))) /* check this one. */
|
||
return TRUE; /* DP not ready, still waiting */
|
||
#endif /* KLH10_DEV_DP */
|
||
return FALSE;
|
||
}
|