1
0
mirror of https://github.com/PDP-10/klh10.git synced 2026-02-09 01:31:33 +00:00
Files
PDP-10.klh10/src/dvrpxx.c
2017-05-14 13:10:33 +02:00

2201 lines
64 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.
/* DVRPXX.C - Emulates RP/RM disk drives under RH20 for KL10
*/
/* $Id: dvrpxx.c,v 2.4 2002/05/21 09:52:07 klh Exp $
*/
/* Copyright © 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: dvrpxx.c,v $
* Revision 2.4 2002/05/21 09:52:07 klh
* Change protos for vdk_read, vdk_write
*
* Revision 2.3 2001/11/10 21:28:59 klh
* Final 2.0 distribution checkin
*
*/
#include "klh10.h"
#if !KLH10_DEV_RPXX && 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_RPXX /* Moby conditional for entire file */
#include <stddef.h> /* For size_t etc */
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h> /* For malloc */
#include "kn10def.h" /* This includes OSD defs */
#include "kn10dev.h"
#include "dvuba.h"
#include "dvrh20.h"
#include "dvrpxx.h"
#include "prmstr.h" /* For parameter parsing */
#if KLH10_DEV_DPRPXX
# include "dpsup.h" /* Using device subproc! */
# include "dprpxx.h" /* Define stuff shared with subproc */
#endif
#if 1 /* !KLH10_DEV_DPRPXX */ /* For now, include these too */
# include "wfio.h" /* For word-based file i/o */
# include "vdisk.h" /* Virtual Disk facilities */
#endif
#ifdef RCSID
RCSID(dvrpxx_c,"$Id: dvrpxx.c,v 2.4 2002/05/21 09:52:07 klh Exp $")
#endif
#ifndef DVRP_NSUP /* Max # of drives can create */
# define DVRP_NSUP (8*8)
#endif
#ifndef DVRP_MAXPATH /* Length of pathname for diskfile */
# define DVRP_MAXPATH 63
#endif
#if 0
#define DVDEBUG(d) ((d)->rp_dv.dv_debug)
#define DVDBF(d) ((d)->rp_dv.dv_dbf)
#endif
int rp_format = VDK_FMT_RAW; /* VDK_DFxxx value */
/* External needed until becomes config param */
#if KLH10_DEV_DPRPXX /* Max I/O operation in bytes */
# define DPRP_MAXRECSIZ (sizeof(w10_t)*128*DPRP_NSECS_MAX)
#endif
/* Disk type configuration params.
** All of these numbers assume drives using 18-bit formatting.
** (16-bit formatting has more sectors per track)
*/
static struct diskconf {
char dcf_name[8]; /* Drive type name (RP06, etc) */
int dcf_type; /* Type bits for type register */
int dcf_nwds; /* Words/Sector */
int dcf_nsec; /* Sectors/Track */
int dcf_ntrk; /* Tracks/Cylinder */
int dcf_ncyl; /* Cylinders/Drive (includes maint cyls) */
} diskconfs[] = {
/* wrd sec trk cyl Cyl+maint totsec totmaintsec */
{ "RP02", 0, 128, 10, 20, 203 }, /* 200+3 40000 40,600 */
{ "RP03", 0, 128, 10, 20, 403 }, /* 400+3 80000 80,600 */
{ "RP04", RH_DTRP04, 128, 20, 19, 411 }, /* 406+5 154280 156,180 */
{ "RP05", RH_DTRP05, 128, 20, 19, 411 }, /* " " " */
{ "RP06", RH_DTRP06, 128, 20, 19, 815 }, /* 810+5 307800 309,700 */
{ "RP07", RH_DTRP07, 128, 43, 32, 630 }, /* 629+1 865504 866,880 */
{ "RM03", RH_DTRM03, 128, 30, 5, 823 }, /* 821+2 123150 123,450 */
{ "RM02", RH_DTRM02, 128, 30, 5, 823 }, /* " " " */
{ "RM05", RH_DTRM05, 128, 30, 19, 823 }, /* 821+2 467970 469,110 */
{ "RM80", RH_DTRM80, 128, 30, 14, 561 }, /* 559+2 234780 235,620 */
{ "" }
};
/* RP07 doc p.6-23 says addressing params are:
** Mode Cyl Head/Track Sector(18) Sector(16)
** Functional: 630 32 43 50
** Diagnostic: 632 32 43 50
*/
/* In ITS:
RP06 was 812+3
RP07 was 627+3
RM03 was 821+2
RM05 was 820
RM80 was 556+3
*/
#ifndef DVRP_DEFAULT_DISK
# define DVRP_DEFAULT_DISK "RP06"
#endif
struct rpdev {
struct device rp_dv;
/* Drive-specific stuff */
int rp_bit; /* Attention bit */
int rp_scmd; /* Pending command if any */
# define NRH20DRVREGS 040 /* 0-037 */
dvureg_t rp_reg[NRH20DRVREGS];
/* Drive config */
struct diskconf rp_dcf; /* Disk configuration, incl dev type */
long rp_totsecs; /* Total # sectors on disk */
int rp_isdyn; /* TRUE if dynamically sized */
int rp_fmt; /* Data format on real disk, VDK_FMT_xxx */
int rp_iswrite; /* TRUE if writable, else RO */
/* I/O transfer vars, updated to track progress */
int rp_blkcnt; /* # sectors in total transfer */
int rp_xfrcnt; /* # subtransfers so far */
long rp_blkwds; /* # words left to transfer */
long rp_blklim; /* # words actually allowed to xfer */
long rp_cyl; /* Current seek pos - must hold > 16 bits */
int rp_trk, rp_sec; /* Current position in cyl - 8 bits each */
long rp_blkadr; /* Disk loc as a sector number */
int rp_isdirect; /* TRUE if current xfer is direct */
vmptr_t rp_xfrvp; /* If direct, holds ptr to 10-mem */
int rp_bufwds; /* Size of buffer in words */
int rp_bufsec; /* Size of buffer in sectors */
unsigned char *rp_buff; /* Ptr to buffer (may be in shared mem) */
clkval_t rp_iodly; /* Optional usec to delay I/O ops */
clktmr_t rp_iotmr; /* Timer for above */
#if KLH10_DEV_DPRPXX
char *rp_dpname; /* Pathname of executable subproc */
int rp_dpdma; /* TRUE to use DP DMA if possible */
struct dp_s rp_dp; /* Handle on dev subprocess */
struct dprpxx_s *rp_sdprp; /* Ptr to shared memory segment */
int rp_state;
# define RPXX_ST_OFF 0 /* Turned off - no subproc */
# define RPXX_ST_READY 1 /* On and ready for command */
# define RPXX_ST_BUSY 2 /* Executing some command */
int rp_dpdbg; /* Initial DP debug value */
#else
long rp_rescnt; /* I/O result sector count */
long rp_reserr; /* I/O result error (if nonzero) */
struct vdk_unit rp_vdk; /* Virtual Disk unit */
#endif
char rp_spath[DVRP_MAXPATH+1];
};
#define RPREG(d,r) ((d)->rp_reg[r])
static int nrps = 0; /* # of RPs defined */
struct rpdev /* External for easier debug */
*dvrpxx[DVRP_NSUP]; /* Table of pointers, for easier debug */
static struct rpdev dvrp; /* Make #0 be static, for easier debug */
/* Functions provided to device vector */
static int rpxx_init(struct device *d, FILE *of);
static void rpxx_reset(struct device *d);
static uint32 rpxx_rdreg(struct device *d, int reg);
static int rpxx_wrreg(struct device *d, int reg, dvureg_t val);
static void rpxx_powoff(struct device *d);
static int rpxx_mount(struct device *d, FILE *f, char *path, char *argstr);
/* Other exported vectors */
#if KLH10_DEV_DPRPXX
static void rpxx_evhsdon(struct device *d, struct dvevent_s *evp);
static void rpxx_evhrwak(struct device *d, struct dvevent_s *evp);
#endif
static int rpxx_timeout(void *);
/* Completely internal functions */
static int rp_conf(FILE *f, char *s, struct rpdev *rp);
static int rp_xmount(struct rpdev *rp);
static void rp_attn(struct rpdev *);
static void rp_clear(struct rpdev *rp);
static void rp_cmdxct(struct rpdev *, unsigned int);
static void rp_delayop(struct rpdev *, int);
static int rp_ioxfr(struct rpdev *, int);
static void rp_ioend(struct rpdev *);
static int rp_updxfr(struct rpdev *);
static int rp_wrfilbuf(struct rpdev *);
static int rp_rdflsbuf(struct rpdev *);
static void rp_showbuf(struct rpdev *, unsigned char *, vmptr_t, int, int);
#if KLH10_DEV_DPRPXX
static void rp_dpcmd(struct rpdev *rp, int cmd, size_t arg);
static int rp_dpstart(struct rpdev *rp);
static void rp_dpcmddon(struct rpdev *);
#endif
/* Configuration Parameters */
#define DVRPXX_PARAMS \
prmdef(RPP_DBG, "debug"), /* Initial debug value */\
prmdef(RPP_TYP, "type"), /* Drive type (eg RP06) */\
prmdef(RPP_CYL, "cyl"), /* Drive size: # cylinders/drive */\
prmdef(RPP_TRK, "trk"), /* Drive size: # tracks/cyl */\
prmdef(RPP_SEC, "sec"), /* Drive size: # sectors/trk */\
prmdef(RPP_SN, "sn"), /* Drive Serial number */\
prmdef(RPP_PATH, "path"), /* Pack pathname - OS file or raw device */\
prmdef(RPP_FMT, "format"), /* Pack format */\
prmdef(RPP_RO, "ro"), /* Pack is Read-Only */\
prmdef(RPP_RW, "rw"), /* Pack is Read/Write (default) */\
prmdef(RPP_BUF, "bufsiz"), /* Buffer size in words */\
prmdef(RPP_IODLY,"iodly"), /* Usec to delay I/O operations */\
prmdef(RPP_DPDBG,"dpdebug"), /* Initial DP debug value */\
prmdef(RPP_DMA, "dpdma"), /* True to use subproc DMA if possible */\
prmdef(RPP_DP, "dppath") /* Device subproc pathname */
enum {
# define prmdef(i,s) i
DVRPXX_PARAMS
# undef prmdef
};
static char *rpprmtab[] = {
# define prmdef(i,s) s
DVRPXX_PARAMS
# undef prmdef
, NULL
};
static int partyp(struct rpdev *, char *); /* Local parsing routines */
static int parfmt(char *cp, int *afmt);
/* RP_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
rp_conf(FILE *f, char *s, struct rpdev *rp)
{
int i, ret = TRUE;
struct prmstate_s prm;
char buff[200];
long lval;
/* First set defaults for all configurable parameters */
DVDEBUG(rp) = FALSE;
rp->rp_fmt = rp_format; /* For now, use external default */
rp->rp_iswrite = TRUE;
partyp(rp, DVRP_DEFAULT_DISK); /* Default disk config */
RPREG(rp, RHR_SN) = /* Serial Number register (BCD) */
(((1600 / 1000)%10) << 12)
| (((1600 / 100)%10) << 8)
| (((nrps / 10)%10) << 4)
| (((nrps )%10) );
rp->rp_bufsec = 4; /* # sectors in buffer */
rp->rp_bufwds = rp->rp_bufsec /* # words in buffer */
* rp->rp_dcf.dcf_nwds;
rp->rp_iodly = /* I/O delay */
#if KLH10_CPU_KS
CLK_USECS_PER_MSEC; /* on KS, default I/O delay to 1ms */
#else
0; /* else none */
#endif
rp->rp_iotmr = NULL;
rp->rp_spath[0] = '\0'; /* No path (default it later) */
#if KLH10_DEV_DPRPXX
rp->rp_dpdma = TRUE; /* Default is DO use DMA if possible */
rp->rp_dpname = "dprpxx"; /* Subproc executable */
rp->rp_dpdbg = FALSE;
#endif
prm_init(&prm, buff, sizeof(buff),
s, strlen(s),
rpprmtab, sizeof(rpprmtab[0]));
while ((i = prm_next(&prm)) != PRMK_DONE) {
switch (i) {
case PRMK_NONE:
fprintf(f, "Unknown RPXX parameter \"%s\"\n", prm.prm_name);
ret = FALSE;
continue;
case PRMK_AMBI:
fprintf(f, "Ambiguous RPXX parameter \"%s\"\n", prm.prm_name);
ret = FALSE;
continue;
default: /* Handle matches not supported */
fprintf(f, "Unsupported RPXX parameter \"%s\"\n", prm.prm_name);
ret = FALSE;
continue;
case RPP_DBG: /* Parse as true/false boolean or number */
if (!prm.prm_val) /* No arg => default to 1 */
DVDEBUG(rp) = 1;
else if (!s_tobool(prm.prm_val, &DVDEBUG(rp)))
break;
continue;
case RPP_TYP: /* Parse as disk type string */
if (!prm.prm_val)
break;
if (!partyp(rp, prm.prm_val))
break;
continue;
case RPP_CYL: /* Parse as decimal number */
if (!prm.prm_val || !s_todnum(prm.prm_val, &lval))
break;
if (lval <= 0 || lval > MASK16) {
fprintf(f, "RPXX CYL must be 0 < x < %ld\n", (long)MASK16);
ret = FALSE;
}
rp->rp_dcf.dcf_ncyl = lval;
rp->rp_isdyn = TRUE;
continue;
case RPP_TRK: /* Parse as decimal number */
if (!prm.prm_val || !s_todnum(prm.prm_val, &lval))
break;
if (lval <= 0 || lval > 0377) {
fprintf(f, "RPXX TRK must be 0 < x < %d\n", 0377);
ret = FALSE;
}
rp->rp_dcf.dcf_ntrk = lval;
rp->rp_isdyn = TRUE;
continue;
case RPP_SEC: /* Parse as decimal number */
if (!prm.prm_val || !s_todnum(prm.prm_val, &lval))
break;
if (lval <= 0 || lval > 0377) {
fprintf(f, "RPXX SEC must be 0 < x < %d\n", 0377);
ret = FALSE;
}
rp->rp_dcf.dcf_nsec = lval;
rp->rp_isdyn = TRUE;
continue;
case RPP_SN: /* Parse as decimal number */
if (!prm.prm_val || !s_todnum(prm.prm_val, &lval))
break;
if (lval < 0) {
fprintf(f, "RP SN must be >= 0\n");
ret = FALSE;
} else
/* Turn last 4 digits into BCD */
RPREG(rp, RHR_SN) =
(((lval / 1000)%10) << 12)
| (((lval / 100)%10) << 8)
| (((lval / 10)%10) << 4)
| (((lval )%10) );
continue;
case RPP_FMT: /* Parse as disk format string */
if (!prm.prm_val)
break;
if (!parfmt(prm.prm_val, &rp->rp_fmt))
break;
continue;
case RPP_RO:
rp->rp_iswrite = FALSE;
continue;
case RPP_RW:
rp->rp_iswrite = TRUE;
continue;
case RPP_BUF: /* Parse as decimal number */
if (!prm.prm_val || !s_todnum(prm.prm_val, &lval))
break;
if (lval <= 0 || (lval % rp->rp_dcf.dcf_nwds)) {
fprintf(f, "RPXX bufsiz must be multiple of %d\n",
rp->rp_dcf.dcf_nwds);
ret = FALSE;
} else {
rp->rp_bufsec = lval / rp->rp_dcf.dcf_nwds;
rp->rp_bufwds = rp->rp_bufsec * rp->rp_dcf.dcf_nwds;
}
continue;
case RPP_IODLY: /* Parse as decimal number */
if (!prm.prm_val || !s_todnum(prm.prm_val, &lval))
break;
if ((lval < 0) || (lval > CLK_USECS_PER_SEC)) {
fprintf(f, "RPXX I/O delay invalid: %ld\n", lval);
ret = FALSE;
} else {
rp->rp_iodly = lval;
}
continue;
case RPP_PATH: /* Parse as simple string */
if (!prm.prm_val)
break;
if (strlen(prm.prm_val) > DVRP_MAXPATH) {
fprintf(f, "RPXX path too long (max %d)\n", DVRP_MAXPATH);
ret = FALSE;
} else
strcpy(rp->rp_spath, prm.prm_val);
continue;
case RPP_DPDBG: /* Parse as true/false boolean or number */
#if KLH10_DEV_DPRPXX
if (!prm.prm_val) /* No arg => default to 1 */
rp->rp_dpdbg = 1;
else if (!s_tobool(prm.prm_val, &(rp->rp_dpdbg)))
break;
#endif
continue;
case RPP_DMA: /* Parse as true/false boolean */
#if KLH10_DEV_DPRPXX
if (!prm.prm_val)
break;
if (!s_tobool(prm.prm_val, &rp->rp_dpdma))
break;
#endif
continue;
case RPP_DP: /* Parse as simple string */
#if KLH10_DEV_DPRPXX
if (!prm.prm_val)
break;
rp->rp_dpname = s_dup(prm.prm_val);
#endif
continue;
}
ret = FALSE;
fprintf(f, "RPXX 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 KLH10_DEV_DPRPXX
if (!cpu.mm_shared) /* If no shared 10 mem, */
rp->rp_dpdma = FALSE; /* force no DMA. */
#endif
/* Set default path for diskfile if none given */
if (!rp->rp_spath[0]) {
sprintf(rp->rp_spath, "RH20.%s.%d",
rp->rp_dcf.dcf_name, nrps);
}
/* Helpful checks to avoid shooting self in foot. */
/* If using dynamically sized drive, make sure all geometry
parameters were provided!
*/
if (rp->rp_isdyn) {
if (!rp->rp_dcf.dcf_nsec
|| !rp->rp_dcf.dcf_ntrk
|| !rp->rp_dcf.dcf_ncyl) {
fprintf(f, "RPXX incomplete dynamic geometry: DT %o, %dc %dt %ds\n",
rp->rp_dcf.dcf_type, rp->rp_dcf.dcf_ncyl,
rp->rp_dcf.dcf_ntrk, rp->rp_dcf.dcf_nsec);
return FALSE;
}
fprintf(f, "RPXX dynamic geom: DT %o, %dc %dt %ds\n",
rp->rp_dcf.dcf_type, rp->rp_dcf.dcf_ncyl,
rp->rp_dcf.dcf_ntrk, rp->rp_dcf.dcf_nsec);
}
/* Ensure the drive serial # isn't duplicated, otherwise TOPS-20
will think it's a dual-ported drive and get very confused.
Do similar check for diskfile path as well.
*/
for (i = 0; i < nrps; ++i) { /* Step thru all known RP devs */
struct rpdev *ckrp;
if (!(ckrp = dvrpxx[i]) || (ckrp == rp))
continue;
if (RPREG(ckrp, RHR_SN) == RPREG(rp, RHR_SN)) {
fprintf(f, "RPXX serial num duplicated! %d%d%d%d\n",
(RPREG(rp, RHR_SN) >> 12) & 017,
(RPREG(rp, RHR_SN) >> 8) & 017,
(RPREG(rp, RHR_SN) >> 4) & 017,
(RPREG(rp, RHR_SN) ) & 017);
ret = FALSE;
break;
}
if (strcmp(ckrp->rp_spath, rp->rp_spath) == 0) {
fprintf(f, "RPXX path duplicated! \"%s\"\n", rp->rp_spath);
ret = FALSE;
break;
}
}
return ret;
}
static int
partyp(struct rpdev *rp, char *cp)
{
register struct diskconf *dc;
long lval;
for (dc = diskconfs; dc->dcf_name[0]; ++dc) {
if (s_match(cp, dc->dcf_name) == 2) {
rp->rp_dcf = *dc; /* Found match, won */
rp->rp_isdyn = FALSE; /* Not dyn sized */
return TRUE;
}
}
/* Type name not found. See if it's actually a number;
if so, allow using that but make it dynamic.
Parse as octal.
*/
if (!s_tonum(cp, &lval) || (((unsigned long)lval) > RH_DTTYP)) {
return FALSE; /* Unknown disk type */
}
/* Specified arbitrary type number. Assume dynamically sized as
well; will barf later if no sizes were provided.
*/
rp->rp_isdyn = TRUE;
sprintf(rp->rp_dcf.dcf_name, "DT%lo", lval);
rp->rp_dcf.dcf_type = lval;
rp->rp_dcf.dcf_nwds = 128;
rp->rp_dcf.dcf_nsec = 0;
rp->rp_dcf.dcf_ntrk = 0;
rp->rp_dcf.dcf_ncyl = 0;
return TRUE;
}
/* Parse disk format -- something that VDISK understands.
*/
static char *fmttab[] = {
# define vdk_fmt(i,n,c,s,f,t) n
VDK_FORMATS
# undef vdk_fmt
};
static int
parfmt(char *cp, int *afmt)
{
register int i;
for (i = 0; i < VDK_FMT_N; ++i) {
if (s_match(cp, fmttab[i]) == 2) {
*afmt = i; /* Found match, won */
return TRUE;
}
}
return FALSE; /* Unknown disk format */
}
struct device *
dvrp_create(FILE *f, char *s)
{
register struct rpdev *rp;
/* Allocate an RP device structure */
if (nrps >= DVRP_NSUP) {
fprintf(f, "Too many RPs, max: %d\n", DVRP_NSUP);
return NULL;
}
if (nrps == 0) /* Special-case first RP */
rp = &dvrp;
else {
if (!(rp = (struct rpdev *)malloc(sizeof(struct rpdev)))) {
fprintf(f, "Cannot allocate RP device! (out of memory)\n");
return NULL;
}
}
dvrpxx[nrps++] = rp;
/* Various initialization stuff */
memset((char *)rp, 0, sizeof(*rp));
/* Configure drive externals */
iodv_setnull(&rp->rp_dv); /* Init as nulldev */
rp->rp_dv.dv_dflags = DVFL_CTLIO | DVFL_DISK;
rp->rp_dv.dv_init = rpxx_init;
rp->rp_dv.dv_reset = rpxx_reset;
rp->rp_dv.dv_rdreg = rpxx_rdreg;
rp->rp_dv.dv_wrreg = rpxx_wrreg;
rp->rp_dv.dv_powoff = rpxx_powoff;
rp->rp_dv.dv_mount = rpxx_mount;
/* Configure drive internals from parsed string and remember for
** setting up disk during init.
*/
if (!rp_conf(f, s, rp))
return NULL;
return &rp->rp_dv;
}
/* Init RP-specific stuff
** This is called as final stage of binding device to controller.
*/
static int
rpxx_init(struct device *d, FILE *of)
{
register struct rpdev *rp = (struct rpdev *)d;
rp->rp_bit = 1 << rp->rp_dv.dv_num; /* Set attention bit mask */
rp->rp_totsecs = rp->rp_dcf.dcf_ncyl *
(rp->rp_dcf.dcf_ntrk * rp->rp_dcf.dcf_nsec);
RPREG(rp, RHR_OFTC) = 0; /* OFFSET */
RPREG(rp, RHR_DT) = RH_DTMH /* DRIVE TYPE */
| rp->rp_dcf.dcf_type;
if (rp->rp_isdyn) {
RPREG(rp, RHR_DT) |= RH_DTDYNGEOM;
RPREG(rp, RHR_EPOS) = rp->rp_dcf.dcf_ncyl-1;
RPREG(rp, RHR_EPAT) = RH_ADRSET(rp->rp_dcf.dcf_ntrk-1,
rp->rp_dcf.dcf_nsec-1);
}
rp->rp_scmd = -1;
#if KLH10_DEV_DPRPXX
{
register struct dprpxx_s *dprp;
struct dvevent_s ev;
rp->rp_state = RPXX_ST_OFF;
if (!dp_init(&rp->rp_dp, sizeof(struct dprpxx_s),
DP_XT_MSIG, SIGUSR1, 0, /* in fr dp */
DP_XT_MSIG, SIGUSR1, /* out to dp */
(size_t)rp->rp_bufwds*sizeof(w10_t))) {
if (of) fprintf(of, "RPXX subproc init failed!\n");
return FALSE;
}
rp->rp_buff = dp_xsbuff(&(rp->rp_dp.dp_adr->dpc_todp), (size_t *)NULL);
rp->rp_dv.dv_dpp = &(rp->rp_dp); /* Tell CPU where our DP struct is */
/* Set up RPXX-specific part of shared DP memory */
dprp = (struct dprpxx_s *) rp->rp_dp.dp_adr;
rp->rp_sdprp = dprp;
if (rp->rp_dpdma) { /* If have shared mem and want DMA, */
dprp->dprp_shmid = cpu.mm_physegid; /* tell DP where it is */
dprp->dprp_dma = TRUE;
} else {
dprp->dprp_shmid = 0;
dprp->dprp_dma = FALSE;
}
dprp->dprp_dpc.dpc_debug = rp->rp_dv.dv_debug; /* Init debug flag */
if (cpu.mm_locked) /* Lock DP mem if CPU is */
dprp->dprp_dpc.dpc_flags |= DPCF_MEMLOCK;
/* Set up config vars */
dprp->dprp_fmt = rp->rp_fmt;
strncpy(dprp->dprp_devname, rp->rp_dcf.dcf_name,
sizeof(dprp->dprp_devname)-1);
dprp->dprp_ncyl = rp->rp_dcf.dcf_ncyl;
dprp->dprp_ntrk = rp->rp_dcf.dcf_ntrk;
dprp->dprp_nsec = rp->rp_dcf.dcf_nsec;
dprp->dprp_nwds = rp->rp_dcf.dcf_nwds;
/* 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 = &(rp->rp_dp.dp_adr->dpc_todp.dpx_donflg);
if (!(*rp->rp_dv.dv_evreg)((struct device *)rp, rpxx_evhsdon, &ev)) {
if (of) fprintf(of, "RPXX 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 = &(rp->rp_dp.dp_adr->dpc_frdp.dpx_wakflg);
if (!(*rp->rp_dv.dv_evreg)((struct device *)rp, rpxx_evhrwak, &ev)) {
if (of) fprintf(of, "RPXX event reg failed!\n");
return FALSE;
}
}
#else
if (!vdk_init(&(rp->rp_vdk), (void (*)())NULL, (char *)NULL))
return FALSE;
if (!(rp->rp_buff = (unsigned char *)
malloc(rp->rp_bufwds * sizeof(w10_t)))) {
if (of) fprintf(of, "RPXX alloc of %d-word buffer failed!\n",
rp->rp_bufwds);
return FALSE;
}
/* Set up config vars */
rp->rp_vdk.dk_format = rp->rp_fmt;
rp->rp_vdk.dk_filename = rp->rp_spath;
rp->rp_vdk.dk_dtype = rp->rp_dcf.dcf_type;
strcpy(rp->rp_vdk.dk_devname, rp->rp_dcf.dcf_name);
rp->rp_vdk.dk_ncyls = rp->rp_dcf.dcf_ncyl;
rp->rp_vdk.dk_ntrks = rp->rp_dcf.dcf_ntrk;
rp->rp_vdk.dk_nsecs = rp->rp_dcf.dcf_nsec;
rp->rp_vdk.dk_nwds = rp->rp_dcf.dcf_nwds;
#endif
/* Do standard drive clear - sets "Drive Available" */
rp_clear(rp);
/* Mount pack here if specified as init arg? */
if (rp->rp_spath[0]) {
#if KLH10_DEV_DPRPXX
if (!rp_dpstart(rp)) { /* Fire up the subproc! */
if (of) fprintf(of, "RPXX subproc \"%s\" startup failed!\n",
rp->rp_dpname);
return FALSE;
}
#endif
if (!rp_xmount(rp)) { /* Special initial mount */
if (of) fprintf(of, "RPXX initial mount of \"%s\" failed!\n",
rp->rp_spath);
return FALSE;
}
}
/* Set up I/O delay timer if needed */
if (rp->rp_iodly) {
rp->rp_iotmr = clk_tmrget(rpxx_timeout, (void *)rp, rp->rp_iodly);
clk_tmrquiet(rp->rp_iotmr); /* Immediately make it quiescent */
}
return TRUE;
}
/* RPXX_RESET - reset RP, clear stuff
*/
static void
rpxx_reset(struct device *d)
{
register struct rpdev *rp = (struct rpdev *)d;
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rpxx_reset]");
rp_clear(rp);
}
/* RPXX_POWOFF - Handle "power-off" which usually means the KLH10 is
** being shut down. This is important if using a dev subproc!
*/
static void
rpxx_powoff(struct device *d)
{
register struct rpdev *rp = (struct rpdev *)d;
/* Later could add stuff to pretend controller/slave is off, but for
** now it suffices just to clean up.
*/
#if KLH10_DEV_DPRPXX
(*rp->rp_dv.dv_evreg)( /* Flush all event handlers for device */
(struct device *)rp,
NULL, /* No event handler proc */
(struct dvevent_s *)NULL);
dp_term(&(rp->rp_dp), 0); /* Flush all subproc overhead */
rp->rp_state = RPXX_ST_OFF;
rp->rp_sdprp = NULL; /* Clear pointers no longer meaningful */
rp->rp_buff = NULL;
#endif
}
/* RPXX_MOUNT - Mount or dismount a disk pack.
** If path is NULL, wants to dismount; argstr is ignored.
** If path is "", just wants status report.
** Else mounting diskfile; argstr if present has keyword params:
** "ro" - Read-Only
** "rw" - Read/Write (default)
** <fmt> - "raw", "dbh8", etc - indicate disk word format
** Returns:
** 0 - error. Error message already output to stream, if one.
** 1 - action succeeded.
*/
static int
rpxx_mount(struct device *d,
FILE *f, char *path, char *argstr)
{
register struct rpdev *rp = (struct rpdev *)d;
int res;
char *opath;
#if KLH10_DEV_DPRPXX
opath = rp->rp_spath[0] ? rp->rp_spath : NULL;
#else
int prevmount = vdk_ismounted(&(rp->rp_vdk)); /* Get state */
int vstate = 0;
if (prevmount) {
opath = rp->rp_vdk.dk_filename;
vstate = vdk_iswritable(&(rp->rp_vdk)) ? 2 : 1;
}
#endif
if (path && !*path) {
/* Just wants mount status report */
if (!opath) {
fprintf(f, "No pack mounted.\n");
return TRUE;
}
fprintf(f, "Current disk pathname is \"%s\", status ", opath);
#if KLH10_DEV_DPRPXX
switch (rp->rp_state) {
case RPXX_ST_OFF:
fprintf(f, "OFF\n");
return TRUE;
case RPXX_ST_BUSY:
fprintf(f, "BUSY");
break;
case RPXX_ST_READY:
fprintf(f, "READY");
break;
default:
fprintf(f, "<\?\?%d\?\?>", rp->rp_state);
break;
}
if (rp->rp_sdprp->dprp_mol)
fprintf(f, " ONLINE");
if (rp->rp_sdprp->dprp_wrl)
fprintf(f, " WRITELOCKED");
#else
switch (vstate) {
case 1:
fprintf(f, "ONLINE WRITELOCKED");
break;
case 2:
fprintf(f, "ONLINE");
break;
default:
fprintf(f, "<\?\?%d\?\?>", vstate);
break;
}
#endif
fprintf(f, "\n");
return TRUE;
}
/* Unmount any existing tape, and mount new tape if one provided */
#if KLH10_DEV_DPRPXX
/* Should this kill the subproc, or wait its turn to send a command?
** Don't want to hang waiting for rewind to complete!
*/
/* For now, return error if busy (sigh)
*/
if (rp->rp_state == RPXX_ST_BUSY) {
fprintf(f, "Cannot %smount: drive busy\n", (path ? "" : "un"));
return FALSE;
}
if (rp->rp_state == RPXX_ST_OFF) {
/* Subproc not running. If call is just unmounting, that's all,
** else must start it up so it can handle the mount.
*/
if (!path) {
rp->rp_spath[0] = '\0'; /* Make sure no current pack */
fprintf(f, "No pack mounted.\n");
return TRUE; /* OK, no pack mounted */
}
if (!rp_dpstart(rp)) /* Fire up the subproc! */
return FALSE;
}
#endif /* KLH10_DEV_DPRPXX */
/* At this point, state should be READY... */
if (path) {
/* Process argstr to determine optional params for mount */
int roflag = FALSE;
int fmt = rp_format; /* For now, default to external */
int err = FALSE;
size_t plen;
char tokbuf[100];
while (s_eztoken(tokbuf, sizeof(tokbuf), &argstr)) {
if (s_match(tokbuf, "ro")==2) roflag = TRUE;
else if (s_match(tokbuf, "rw")==2) roflag = FALSE;
else if (parfmt(tokbuf, &fmt));
else {
fprintf(f, "Unknown mount option: \"%s\"\n", tokbuf);
err++;
}
}
if (err)
return FALSE;
/* Plug params into RP struct for use by xmount */
rp->rp_fmt = fmt;
rp->rp_iswrite = !roflag;
plen = strlen(path);
if (plen > DVRP_MAXPATH-1)
plen = DVRP_MAXPATH-1;
memcpy(rp->rp_spath, path, plen); /* Remember pathname */
rp->rp_spath[plen] = '\0';
res = rp_xmount(rp); /* Do it! */
#if KLH10_DEV_DPRPXX
fprintf(f, "Mount requested: \"%s\"\n", path);
#else
if (res)
fprintf(f, "Pack mounted: \"%s\"\n", path);
else
fprintf(f, "Mount failed for: \"%s\"\n", path);
#endif
} else {
/* Just unmounting current pack? */
#if KLH10_DEV_DPRPXX
rp->rp_buff[0] = '\0'; /* Tell DP to unmount */
rp->rp_scmd = RH_MNOP; /* Conspire with rp_dpcmddon */
rp_dpcmd(rp, DPRP_MOUNT, (size_t)0);
fprintf(f, "Unmount requested\n");
return TRUE;
#else
res = vdk_unmount(&(rp->rp_vdk));
rp_clear(rp); /* Clear drive status, set regs */
if (res)
fprintf(f, "Pack unmounted\n");
else
fprintf(f, "Unmount failed\n");
#endif /* !KLH10_DEV_DPRPXX */
}
return res;
}
static int
rp_xmount(register struct rpdev *rp)
{
#if KLH10_DEV_DPRPXX
register size_t cnt;
/* Copy new path into DP comm buffer, including a terminating nul.
*/
cnt = strlen(rp->rp_spath);
rp->rp_buff[0] = (rp->rp_iswrite ? 'W' : 'R'); /* Special prefix */
memcpy((char *)(rp->rp_buff+1), rp->rp_spath, cnt);
rp->rp_buff[++cnt] = '\0';
rp->rp_sdprp->dprp_fmt = rp->rp_fmt; /* Set desired format */
/* Do command! And hope for the best... */
rp->rp_scmd = RH_MNOP; /* Conspire with rp_dpcmddon */
rp_dpcmd(rp, DPRP_MOUNT, cnt);
return TRUE;
#else
int res;
rp->rp_vdk.dk_format = rp->rp_fmt;
res = vdk_mount(&(rp->rp_vdk), rp->rp_spath, rp->rp_iswrite);
rp_clear(rp); /* Clear drive status, set regs */
return res;
#endif /* !KLH10_DEV_DPRPXX */
}
#if KLH10_DEV_DPRPXX
/* RP_DPCMD - Carry out an asynchronous command
*/
static void
rp_dpcmd(register struct rpdev *rp, int cmd, size_t arg)
{
register struct dpx_s *dpx = &(rp->rp_dp.dp_adr->dpc_todp);
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rp_dpcmd: DP %d, %ld]\r\n", cmd, (long)arg);
/* First, double-check to be sure it's OK to send a command */
if (rp->rp_state != RPXX_ST_READY) {
/* Says not ready -- check DP to see if true */
if (!(rp->rp_state == RPXX_ST_BUSY) || !dp_xstest(dpx)) {
/* Yep, really can't send command now.
** This shouldn't happen; what to do?
*/
panic("[rp_dpcmd: can't send cmd %d]", cmd);
}
/* Hmmm, state is BUSY but dp_xstest thinks we're OK, so go ahead */
}
rp->rp_state = RPXX_ST_BUSY;
dp_xsend(dpx, cmd, arg); /* Send command! */
}
/* RPXX_EVHSDON - Invoked by INSBRK event handling when
** signal detected from DP saying "done" in response to something
** we sent it.
** Basically this means the DP should be ready to accept another
** command.
*/
static void
rpxx_evhsdon(struct device *d,
register struct dvevent_s *evp)
{
register struct rpdev *rp = (struct rpdev *)d;
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rpxx_evhsdon: %d]",
(int)dp_xstest(&(rp->rp_dp.dp_adr->dpc_todp)));
rp->rp_state = RPXX_ST_READY; /* Say ready for cmd again */
rp_dpcmddon(rp);
}
/* RPXX_EVHRWAK - Invoked by INSBRK event handling when
** signal detected from DP saying "wake up"; the DP is sending
** us something.
** The RPXX will use this to receive notice of unexpected manual events,
** specifically tape being mounted or unmounted.
*/
static void
rpxx_evhrwak(struct device *d,
register struct dvevent_s *evp)
{
register struct rpdev *rp = (struct rpdev *)d;
register struct dpx_s *dpx = &(rp->rp_dp.dp_adr->dpc_frdp);
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rpxx_evhrwak: %d]", (int)dp_xrtest(dpx));
if (dp_xrtest(dpx)) { /* Verify there's a message for us */
switch (dp_xrcmd(dpx)) {
case DPRP_MOUNT:
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rpxx_evhrwak: Disk Online!]\r\n");
#if 0 /* Any equivalent for this? */
RPREG(rp, RHR_STS) |= RP_SSSC; /* Set Slave Status Change */
#endif
rp_attn(rp);
break;
default:
break;
}
dp_xrdone(dpx); /* just ACK it */
}
}
static int
rp_dpstart(register struct rpdev *rp)
{
if (rp->rp_state != RPXX_ST_OFF) {
fprintf(DVDBF(rp), "[rp_dpstart: Already running\?\?]\r\n");
return FALSE;
}
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rp_dpstart: Starting DP \"%s\"...",
rp->rp_dpname);
if (!dp_start(&rp->rp_dp, rp->rp_dpname)) {
if (DVDEBUG(rp))
fprintf(DVDBF(rp), " failed!]\r\n");
else
fprintf(DVDBF(rp), "[rp_dpstart: Start of DP \"%s\" failed!]\r\n",
rp->rp_dpname);
return FALSE;
}
if (DVDEBUG(rp))
fprintf(DVDBF(rp), " started!]\r\n");
rp->rp_state = RPXX_ST_READY;
return TRUE;
}
#endif /* KLH10_DEV_DPRPXX */
/* RP_SSTA - Set Status bits from slave info
*/
static void
rp_ssta(register struct rpdev *rp)
{
register unsigned int sts = RPREG(rp, RHR_STS);
/* Clear bits we'll check */
sts &= ~(RH_SPIP|RH_SMOL|RH_SWRL|RH_SLBT|RH_SPGM
|RH_SDPR|RH_SDRY|RH_SVV);
sts |= RH_SDPR; /* Assume drive always there */
#if KLH10_DEV_DPRPXX
{
register struct dprpxx_s *dprp = rp->rp_sdprp;
if (rp->rp_state != RPXX_ST_OFF && dprp) {
/* Drive present, see if ready for commands */
if (rp->rp_state == RPXX_ST_READY)
sts |= RH_SDRY; /* Drive present & ready for commands */
if (dprp->dprp_mol) sts |= RH_SMOL|RH_SVV; /* Medium online */
if (dprp->dprp_wrl) sts |= RH_SWRL; /* Write-locked */
}
}
#else
/* MOL,DPR,RDY, and VV must all be present for the drive to be
** considered "good" by T20.
*/
sts |= RH_SDPR|RH_SDRY; /* Drive Present & Ready */
if (vdk_ismounted(&(rp->rp_vdk)))
sts |= RH_SMOL|RH_SVV; /* Online and Valid */
if (!vdk_iswritable(&(rp->rp_vdk)))
sts |= RH_SWRL; /* Write-locked */
#endif
RPREG(rp, RHR_STS) = sts; /* Set new value of status register! */
}
/* RP_ATTN - Send special attention interrupt
*/
static void
rp_attn(register struct rpdev *rp)
{
RPREG(rp, RHR_STS) |= RH_SATN;
RPREG(rp, RHR_ATTN) |= rp->rp_bit; /* For our drive # */
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rp_attn: ON]");
(*rp->rp_dv.dv_attn)(&(rp->rp_dv), 1); /* Assert ATTN */
}
/* RP_CLEAR - clear RP drive
*/
static void
rp_clear(register struct rpdev *rp)
{
/* Turn off any attention bit. */
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rp_attn: off]");
(*rp->rp_dv.dv_attn)(&rp->rp_dv, 0);
rp->rp_scmd = -1;
if (rp->rp_iodly && rp->rp_iotmr) /* Ensure any timer is quiescent */
clk_tmrquiet(rp->rp_iotmr);
/* Clear and set Drive bits in CS1. */
RPREG(rp, RHR_CSR) = RH_XDVA; /* Drive Available */
/* Clearing errors may be tricky. Have to ensure that slave
** errors are reset as well -- if this involves a command to
** the DP then how to wait for synchronization to happen?
** May need to have a shared "clear-before-executing-cmd" flag which can
** be set anytime.
*/
#if KLH10_DEV_DPRPXX
rp->rp_sdprp->dprp_err = 0; /* For now, so rp_ssta doesn't spill beans */
#endif
RPREG(rp, RHR_STS) = /* RO FS Formatter Status */
RH_SDPR; /* Drive/formatter Present */
rp_ssta(rp); /* Set status bits for drive */
#if 0
RPREG(rp, RHR_STS) =
RH_SMOL|RH_SDPR|RH_SDRY|RH_SVV; /* Drive Status */
#endif
RPREG(rp, RHR_ER1) = 0; /* ERROR 1. */
switch (rp->rp_dcf.dcf_type) {
case RH_DTRP06: /* RP06 or RP07 */
case RH_DTRP07:
case RH_DTRM03: /* These too? Does ER2 even exist? */
case RH_DTRM80:
RPREG(rp, RHR_ER2) = 0; /* ERROR 2. */
RPREG(rp, RHR_ER3) = 0; /* ERROR 3. */
break;
}
RPREG(rp, RHR_MNT) &= ~(1<<15); /* Clears bit 15 of MNT */
if (!rp->rp_isdyn) { /* These are re-used for dynsizing */
RPREG(rp, RHR_EPOS) = 0; /* ECC POSITION. */
RPREG(rp, RHR_EPAT) = 0; /* ECC PATTERN. */
}
}
static uint32
rpxx_rdreg(struct device *d, int reg)
{
register struct rpdev *rp = (struct rpdev *)d;
switch (reg) {
/* In general, device registers are just read directly */
case RHR_CSR: /* R/W CS1 Control/command */
case RHR_STS: /* RO [I2] STS Status */
case RHR_ER1: /* R/W ER1 Error 1 */
case RHR_MNT: /* R/W [-2] MNT Maintenance */
case RHR_ATTN: /* R/W [I2] ATN? Attention Summary */
case RHR_BAFC: /* R/W [I2] ADR Block Address or Frame Count */
case RHR_DT: /* RO [I2] TYP Drive Type */
case RHR_ER2: /* R/W ER2 Error 2 */
case RHR_OFTC: /* R/W OFS Offset or TapeControl */
case RHR_DCY: /* R/W CYL Desired Cylinder Addr */
case RHR_CCY: /* RO [-2] CCY Current Cylinder Addr */
case RHR_SN: /* RO [-2] SER Serial Number */
case RHR_ER3: /* R/W ER3 Error 3 */
case RHR_EPOS: /* RO POS ECC Position */
case RHR_EPAT: /* RO PAT ECC Pattern */
break;
/* According to ITS, only RP06/7 have CCY,ER2,ER3. */
/* The RM03 and RM80 have ER3, not clear on CCY and ER2. */
/* Like above but with special hack for stupid T20 init,
** which wants to see Look-Ahead changing so it knows that the
** disk really is spinning. Little does it know...
*/
case RHR_LAH: /* RO LAH Current BlkAdr or R/W ChkChr */
RPREG(rp, reg) += 0100; /* Use RP07 sector field & mask */
RPREG(rp, reg) &= 07700; /* Bump field and return it */
break;
default: /* Unknown register */
if (rp->rp_dv.dv_debug)
fprintf(rp->rp_dv.dv_dbf, "[rpxx_rdreg: unknown reg %o]\r\n", reg);
return -1; /* Return error, caller will handle */
}
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rpxx_rdreg: r%o/ %o]\r\n",
reg, RPREG(rp, reg));
return RPREG(rp, reg);
}
static int
rpxx_wrreg(struct device *d,
int reg,
register dvureg_t val)
{
register struct rpdev *rp = (struct rpdev *)d;
val &= MASK16;
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rpxx_wrreg: r%o/ %o = %o]\r\n",
reg, RPREG(rp, reg), val);
/* If GO bit is still set, all reg mods are refused except for ATTN */
if ((RPREG(rp, RHR_CSR) & RH_XGO) && (reg != RHR_ATTN)) {
/* Set RMR error bit (register modif refused); drive is busy.
** No ATTN generated.
*/
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rpxx_wrreg: reg mod refused: %o]\r\n", reg);
RPREG(rp, RHR_ER1) |= RH_1RMR; /* Set RMR error bit */
RPREG(rp, RHR_STS) |= RH_SERR; /* And composite error */
return 1;
}
switch (reg) {
case RHR_CSR: /* R/W CS1 Control/command */
/* Set any permissible drive bits */
RPREG(rp, RHR_CSR) &= ~(RH_XCMD); /* Bits can set */
RPREG(rp, RHR_CSR) |= (val & RH_XCMD); /* Set em */
val &= RH_XCMD;
if (val & RH_XGO)
rp_cmdxct(rp, val); /* Perform drive command */
break;
case RHR_ATTN: /* R/W [I2] ATN? Attention Summary */
/* This register is actually intercepted and handled specially
** by the controller. At this point, only this specific drive
** is being addressed, so only one bit of information
** is meaningful; consider the entire value to be either zero
** or non-zero.
** If non-zero, turns off the ATTN bit for this drive.
*/
/* Ignores state of GO bit */
if (val) { /* Any live bits set? */
RPREG(rp, RHR_ATTN) = 0; /* Yep, turn them off! */
RPREG(rp, RHR_STS) &= ~RH_SATN; /* Clear status bit */
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rp_attn: off]");
(*rp->rp_dv.dv_attn)(&rp->rp_dv, 0); /* Tell controller it's off */
}
break;
/* Regs that can write */
case RHR_BAFC: /* R/W [I2] ADR Block Address or Frame Count */
case RHR_OFTC: /* R/W OFS Offset or TapeControl */
case RHR_DCY: /* R/W CYL Desired Cylinder Addr */
case RHR_MNT: /* R/W [-2] MNT Maintenance (not supported) */
RPREG(rp, reg) = val;
break;
/* Not clear on these regs. RP07 claims RO, but others say R/W */
case RHR_ER1: /* R/W? ER1 Error 1 */
case RHR_ER2: /* R/W? ER2 Error 2 */
case RHR_ER3: /* R/W? ER3 Error 3 */
RPREG(rp, reg) = val;
break;
/* Read-Only registers, write is no-op */
case RHR_STS: /* RO [I2] STS Status */
case RHR_DT: /* RO [I2] TYP Drive Type */
case RHR_LAH: /* RO Current BlockAddr or R/W CheckChar */
case RHR_SN: /* RO [-2] SER Serial Number */
case RHR_CCY: /* RO [-2] CCY Current Cylinder Addr */
case RHR_EPOS: /* RO POS ECC Position */
case RHR_EPAT: /* RO PAT ECC Pattern */
break;
/* According to ITS, only RP06/7 have CCY,ER2,ER3. */
/* The RM03 and RM80 have ER3, not clear on CCY and ER2. */
default: /* Unknown register */
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rpxx_wrreg: unknown reg %o]\r\n", reg);
/* If illegal register was selected, sets ILR bit in error reg, but
** doesn't generate ATTN.
*/
RPREG(rp, RHR_ER1) |= RH_1ILR; /* Set ILR error bit */
RPREG(rp, RHR_STS) |= RH_SERR; /* And composite error */
return 0; /* Return error, caller will handle */
}
return 1;
}
/* RP_CMDXCT - RP drive command execution
** Note that rp_cmdxct cannot be called unless the GO bit is off!
**
** Only one command can be given
*/
static void
rp_cmdxct(register struct rpdev *rp,
unsigned int cmd)
{
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[RP cmd: %o]\r\n", cmd);
/* The RP07 doc (p. 6-20) claims that giving a command with GO
** while an error condition exists will always fail (the operation
** is "inhibited" and GO is not set).
** The only exceptions are a Drive Clear or Microdiagnostic command.
*/
/* Check for always-legal CLR command */
if (cmd == RH_MCLR) {
rp_clear(rp);
return;
}
/* Now check for pre-existing error */
if (RPREG(rp, RHR_STS) & RH_SERR) { /* Check composite bit */
RPREG(rp, RHR_CSR) &= ~RH_XGO; /* Turn off GO */
rp_attn(rp); /* and set ATA to interrupt */
return;
}
/* Check for medium online.
** RP07 doc says MOL must be set prior to initiation of any command
** except in Microdiag mode, but doesn't say what happens if you try
** anyway. The following code is similar to that of the TM02/3.
*/
if (!(RPREG(rp, RHR_STS) & RH_SMOL)) { /* No "Medium Online"? */
/* Must distinguish between I/O commands and everything else.
** I/O xfers must invoke special handler.
*/
if ((061 <= cmd && cmd <= 067) /* Write function? */
|| (071 <= cmd && cmd <= 077)) { /* Read function? */
(*rp->rp_dv.dv_iobeg)(&rp->rp_dv, (cmd < 070)); /* Set up xfer */
(*rp->rp_dv.dv_drerr)(&rp->rp_dv); /* then say error */
}
/* Always add error bit for "Unsafe" -- can't find any
** other plausible bit for offline medium.
*/
RPREG(rp, RHR_CSR) &= ~RH_XGO; /* Turn off GO */
RPREG(rp, RHR_ER1) |= RH_1UNS; /* Unsafe */
RPREG(rp, RHR_STS) |= RH_SERR; /* Error summary */
rp_attn(rp); /* Send attention interrupt */
return;
}
/* OK to execute command!
** Note Drive Clear (RH_MCLR) has already been checked for.
*/
switch (cmd) {
case RH_MNOP: /* No Operation */
break; /* Done, no ATTN */
case RH_MRDP: /* Read-In Preset */
RPREG(rp, RHR_DCY) = 0; /* DC = Desired Cylinder */
RPREG(rp, RHR_BAFC) = 0; /* DA = Desired Sector/Track Address */
RPREG(rp, RHR_OFTC) = 0; /* OF = Offset */
break; /* Done, no ATTN */
case RH_MUNL: /* Unload ("Standby" -- the pack doesn't fly off). */
break; /* Could put drive softwarily offline? */
case RH_MREC: /* Recalibrate */
/* Recalibrate does a head seek to home position (cyl 0) and
** triggers ATTN when done.
** RP07 doc p.6-50 says this takes 500ms (?!)
*/
RPREG(rp, RHR_CCY) = 0; /* CC = Current Cylinder */
rp_attn(rp);
break; /* Done, turn off GO */
case RH_MRLS: /* Drive release (dual port) */
break; /* No-op since we're never hacking dual stuff */
case RH_MSEK: /* Seek to Cylinder */
if (RPREG(rp, RHR_DCY) >= rp->rp_dcf.dcf_ncyl) {
RPREG(rp, RHR_ER1) |= RH_1IAE; /* Illegal Address Error */
RPREG(rp, RHR_STS) |= RH_SERR; /* Error summary */
} else {
RPREG(rp, RHR_CCY) = RPREG(rp, RHR_DCY); /* Do the seek */
}
rp_attn(rp); /* Send attention interrupt */
break; /* Done, turn off GO and return */
case RH_MSRC: /* Search to Cylinder, Track, Sector */
if ((RPREG(rp, RHR_DCY) >= rp->rp_dcf.dcf_ncyl)
|| (RH_ATRKGET(RPREG(rp, RHR_BAFC)) >= rp->rp_dcf.dcf_ntrk)
|| (RH_ASECGET(RPREG(rp, RHR_BAFC)) >= rp->rp_dcf.dcf_nsec)) {
RPREG(rp, RHR_ER1) |= RH_1IAE; /* Illegal Address Error */
RPREG(rp, RHR_STS) |= RH_SERR; /* Error summary */
} else {
RPREG(rp, RHR_CCY) = RPREG(rp, RHR_DCY); /* Do the seek */
}
rp_attn(rp); /* Send attention interrupt */
break; /* Done, turn off GO and return */
case RH_MACK: /* Acknowledge mounting of pack (required before I/O) */
/* RP07 doesn't support this, but so what */
#if KLH10_DEV_DPRPXX
/* Talk to subproc? */
#endif
RPREG(rp, RHR_STS) |= RH_SVV; /* Volume now valid */
break; /* Dunno, but assume no ATTN needed */
case RH_MOFS: /* Offset Heads Slightly */
case RH_MCEN: /* Return Heads To Centerline */
break; /* Dunno, but assume no ATTN needed */
#if 0 /* Commented out so will be intepreted as illegal functions */
case RH_MWCF: /* Write Check Header and Data (?doesn't work) */
case RH_MWHD: /* Write Header And Data (RP07: Format Track) */
case RH_MWTD: /* Write Track Descriptor (RP07 only) */
case RH_MRTD: /* Read Track Descriptor (RP07 only) */
break;
#endif
case RH_MWRT: /* Write Data */
if (rp->rp_iodly) { /* If config setting is for delay, */
rp_delayop(rp, RH_MWRT); /* Do delayed op instead */
}
else if (!rp_ioxfr(rp, 1)) /* Errors handled by rp_io now */
break; /* Failed, drop out and turn off GO */
RPREG(rp, RHR_STS) &= ~RH_SDRY; /* Drive busy, note GO still on! */
return;
#if KLH10_SYS_ITS /* Pretend this works so ITS Salvager will run */
case RH_MRHD: /* Read Header and Data */
#endif
case RH_MWCH: /* Write Check Data (RP07: Same as Read Data) */
case RH_MRED: /* Read Data */
if (rp->rp_iodly) { /* If config setting is for delay, */
rp_delayop(rp, RH_MRED); /* Do delayed op instead */
}
else if (!rp_ioxfr(rp, 0)) /* Errors handled by rp_io now */
break; /* Failed, drop out and turn off GO */
RPREG(rp, RHR_STS) &= ~RH_SDRY; /* Drive busy, note GO still on! */
return;
default:
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rp_cmdxct: unknown cmd %#o]\r\n", cmd);
RPREG(rp, RHR_CSR) &= ~RH_XGO; /* Turn off GO */
RPREG(rp, RHR_ER1) |= RH_1ILF; /* Illegal Function code */
RPREG(rp, RHR_STS) |= RH_SERR; /* Error summary */
rp_attn(rp); /* Send attention interrupt */
return;
}
/* Command done. Default is NOT to set attention bit. */
RPREG(rp, RHR_CSR) &= ~RH_XGO; /* Turn off GO */
}
/* Called to issue a delayed I/O operation.
** Note that delay applies to STARTING the operation, not to
** reporting its completion via interrupt. We cannot quietly start
** the read and then delay the interrupt; that would work for some
** monitor timing problems, but would not avoid situations where,
** for example, the bootstrap initiates a disk read that overlays the
** currently executing code!! This actually happens in an unpatched ITS.
*/
static void
rp_delayop(register struct rpdev *rp, int cmd)
{
if (rp->rp_scmd >= 0)
fprintf(DVDBF(rp), "[rp_delayop: cmd overrun: old %o, new %o]\r\n",
rp->rp_scmd, cmd);
rp->rp_scmd = cmd; /* Save command for rpxx_timeout */
clk_tmractiv(rp->rp_iotmr); /* Start timer */
}
static int
rpxx_timeout(void *arg)
{
register struct rpdev *rp = (struct rpdev *)arg;
switch (rp->rp_scmd) {
case -1: /* No command buffered up?! */
default:
fprintf(DVDBF(rp), "[rpxx_timeout: no cmd?]");
/* Do nothing whatsoever beyond grumbling */
break;
case RH_MWRT: /* Write Data */
if (!rp_ioxfr(rp, 1)) {
RPREG(rp, RHR_CSR) &= ~RH_XGO; /* Error, turn off GO */
}
break;
case RH_MRED: /* Read Data */
if (!rp_ioxfr(rp, 0)) {
RPREG(rp, RHR_CSR) &= ~RH_XGO; /* Error, turn off GO */
}
break;
}
return CLKEVH_RET_QUIET; /* Become quiescent */
}
#if KLH10_DEV_DPRPXX
/* RP_DPCMDDON - Command/operation completed, wrap it up.
** Slave is known to be quiescent at this point.
*/
static void
rp_dpcmddon(register struct rpdev *rp)
{
register int cmd = rp->rp_scmd; /* Find command to complete */
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rp_dpcmddon: %#o]\r\n", cmd);
switch (cmd) {
case RH_MNOP: /* No Operation - actually DPRP_MOUNT! */
/* This should only happen when a DPRP_MOUNT has completed,
** because nothing else puts a NOP in rp_scmd. Hack.
** See also rpxx_evhrwak.
*/
rp_ssta(rp); /* update all status */
#if 0
RPREG(rp, RHR_STS) |= RP_SSSC; /* Set SSC - slave changed state */
#endif
rp_attn(rp);
break;
case RH_MUNL: /* Unload */
rp_ssta(rp); /* Set status to whatever */
break;
case RH_MCLR: /* Formatter clear (reset errors etc.) */
break;
/* The following commands are I/O xfer commands */
case RH_MWRT: /* Write Data */
/* Data written from buffer, maybe fill it up again */
if (rp_wrfilbuf(rp))
return; /* Yep, keep going */
break; /* Nope, all done, turn off GO bit */
case RH_MRED: /* Read Data */
/* Data read into buffer, dispose of it and maybe get more */
if (rp_rdflsbuf(rp))
return; /* Yep, keep going */
break; /* Nope, all done, turn off GO bit */
default:
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[RPXX unknown scmd %#o]\r\n", cmd);
/* Let unknown commands clear GO bit, to avoid wedging */
break;
}
rp->rp_scmd = -1; /* No more pending cmd */
RPREG(rp, RHR_CSR) &= ~RH_XGO; /* Turn off GO bit in CSR */
rp_ssta(rp); /* Update all status */
}
#endif /* KLH10_DEV_DPRPXX */
/*
One complete transfer can involve a number of separate
subtransfers as directed by the controller (data channel), which does
scatter-gather I/O. This makes life harder, because it's not clear
whether to expect a device subproc to be capable of doing transfers of
less than one sector. It's possible, but may be less efficient than
e.g. simply reading in the whole sector into a temporary buffer and then
distributing it from there.
Such transfers DO happen -- eg when booting up the monitor,
the T20 boot skips over the first 16 words of the first page, to skip
over the AC addresses.
Ideal case: transferring whole multiple sectors into sequential
memory addresses.
Minimal direct xfer case: transferring whole sector.
Spiral note: For the RP06/7 at least, T10 expects to be able to do "spiral"
read/write transfers where the drive will automatically seek to the
next cylinder if necessary.
*/
static int
rp_ioxfr(register struct rpdev *rp, int wrtf)
{
/* Find # sectors controller wants us to xfer */
rp->rp_blkcnt = (*rp->rp_dv.dv_iobeg)(&rp->rp_dv, wrtf);
if (!rp->rp_blkcnt) { /* If screwed up somehow, */
(*rp->rp_dv.dv_ioend)(&rp->rp_dv, 0); /* Tell ctlr we're done, */
return 0; /* and take failure return */
}
/* Find resulting total # of words */
rp->rp_blkwds = rp->rp_blkcnt * rp->rp_dcf.dcf_nwds;
/* Determine & verify initial disk address to read from or write to */
rp->rp_cyl = RPREG(rp, RHR_DCY); /* Use "desired" cyl - implied seek! */
rp->rp_trk = RH_ATRKGET(RPREG(rp, RHR_BAFC));
rp->rp_sec = RH_ASECGET(RPREG(rp, RHR_BAFC));
if ( (rp->rp_cyl >= rp->rp_dcf.dcf_ncyl)
|| (rp->rp_trk >= rp->rp_dcf.dcf_ntrk)
|| (rp->rp_sec >= rp->rp_dcf.dcf_nsec)) {
RPREG(rp, RHR_ER1) |= RH_1IAE; /* Invalid Address Error */
RPREG(rp, RHR_STS) |= RH_SERR;
(*rp->rp_dv.dv_drerr)(&rp->rp_dv); /* Complain to ctlr */
rp_attn(rp); /* Trigger ATA */
return 0;
}
/* Starting address OK, get it as a sector address */
rp->rp_blkadr = (((rp->rp_cyl * rp->rp_dcf.dcf_ntrk)
+ rp->rp_trk) * rp->rp_dcf.dcf_nsec
+ rp->rp_sec);
/* Check length of transfer against disk address, to see whether
** it will overrun some boundary, and set limit accordingly.
** Currently we assume spiral xfer capability, so don't check for
** cylinder boundaries other than last one.
*/
if ((rp->rp_blkadr + rp->rp_blkcnt) <= rp->rp_totsecs)
rp->rp_blklim = rp->rp_blkwds; /* OK to xfer all wds */
else /* Oops, truncate */
rp->rp_blklim = (rp->rp_totsecs - rp->rp_blkadr)
* rp->rp_dcf.dcf_nwds;
rp->rp_xfrcnt = 0; /* Init # of subtransfers */
rp->rp_isdirect = FALSE; /* Not direct xfer (yet) */
if (wrtf) {
rp->rp_scmd = RH_MWRT;
#if KLH10_DEV_DPRPXX
return rp_wrfilbuf(rp); /* Writing - Fill up buffer, start I/O */
#else
while (rp_wrfilbuf(rp));
return 0;
#endif
} else {
rp->rp_scmd = RH_MRED;
#if KLH10_DEV_DPRPXX
return rp_rdflsbuf(rp); /* Reading - Start I/O, empty buffer */
#else
while (rp_rdflsbuf(rp));
return 0;
#endif
}
}
/* RP_IOEND - Called when RP xfer done, when either device or channel
** is out of data, or if error detected in middle.
*/
static void
rp_ioend(register struct rpdev *rp)
{
/* Wrap up channel xfer, tell controller we're done */
rp_ssta(rp); /* Set status in case MOL etc changed */
if (RPREG(rp, RHR_STS) & RH_SERR) {
/* Device error - inform controller */
(*rp->rp_dv.dv_drerr)(&rp->rp_dv);
rp_attn(rp);
} else if (rp->rp_blkwds) {
/* Channel problem, ran out of space from channel too early. */
(*rp->rp_dv.dv_ioend)(&rp->rp_dv, /* Say device wanted more */
(int)((rp->rp_blkwds + rp->rp_dcf.dcf_nwds-1)
/ rp->rp_dcf.dcf_nwds));
} else {
/* Normal termination, device done.
** Channel will check itself to see whether it had any words left.
*/
(*rp->rp_dv.dv_ioend)(&rp->rp_dv, 0); /* Win, all blks done */
}
}
/* RP_UPDXFR - Update vars to reflect disk transfer.
** Returns 0 if should stop (error of some kind)
** Assumes no partial sector reads or writes.
*/
static int
rp_updxfr(register struct rpdev *rp)
{
register int i;
#if KLH10_DEV_DPRPXX
i = rp->rp_sdprp->dprp_scnt;
#else
i = rp->rp_rescnt;
#endif
rp->rp_blkadr += i; /* Update sector address */
if ((rp->rp_sec += i) >= rp->rp_dcf.dcf_nsec) {
i = rp->rp_sec / rp->rp_dcf.dcf_nsec;
rp->rp_sec = rp->rp_sec % rp->rp_dcf.dcf_nsec;
if ((rp->rp_trk += i) >= rp->rp_dcf.dcf_ntrk) {
#if 1
/* If spiral read/write supported, bump cylinder # as well.
This is supported by at least the RP05, RP06, RP07; the others
are unknown, but for now assume the same and hope nothing
ever depends on non-spiraling.
*/
i = rp->rp_trk / rp->rp_dcf.dcf_ntrk;
rp->rp_trk = rp->rp_trk % rp->rp_dcf.dcf_ntrk;
if ((rp->rp_cyl += i) >= rp->rp_dcf.dcf_ncyl) {
/* If trk/sec NZ, or plan to do more I/O, say AOE */
if (rp->rp_blkwds || rp->rp_trk || rp->rp_sec) {
RPREG(rp, RHR_ER1) |= RH_1AOE;
RPREG(rp, RHR_STS) |= RH_SERR;
}
}
RPREG(rp, RHR_CCY) = rp->rp_cyl;
RPREG(rp, RHR_DCY) = rp->rp_cyl;
#endif
}
}
RPREG(rp, RHR_BAFC) = RH_ADRSET(rp->rp_trk, rp->rp_sec);
/* Check to see if last write completed successfully */
#if KLH10_DEV_DPRPXX
if ((i = rp->rp_sdprp->dprp_err)) {
#else
if ((i = rp->rp_reserr)) {
#endif
/* Ugh, what error bit to use?? */
if (i < 0) { /* If -1 assume addr ovfl */
RPREG(rp, RHR_ER1) |= RH_1AOE;
} else {
RPREG(rp, RHR_ER1) |= RH_1UNS; /* Use generic "Unsafe" */
}
RPREG(rp, RHR_STS) |= RH_SERR; /* Set composite error */
}
if (RPREG(rp, RHR_STS) & RH_SERR) {
rp_ioend(rp); /* Ugh, stop now */
return 0; /* Do nothing else */
}
return 1;
}
/* RP_WRFILBUF - Set up buffer for writing to disk
*/
static int
rp_wrfilbuf(register struct rpdev *rp)
{
register int wc;
register long bwcnt, totw;
register w10_t *wp;
vmptr_t vp;
if (rp->rp_xfrcnt++) {
/* Invoking after completion of a write; check results, do next
** subtransfer.
** Update registers to reflect any actual I/O done, then
** see if operation complete or need to write another bufferful.
*/
if (rp->rp_isdirect) {
#if KLH10_DEV_DPRPXX
wc = rp->rp_sdprp->dprp_scnt; /* Get # sectors written */
#else
wc = rp->rp_rescnt;
#endif
wc *= rp->rp_dcf.dcf_nwds; /* Find # words */
rp->rp_blkwds -= wc;
rp->rp_blklim -= wc;
(void) (*rp->rp_dv.dv_iobuf)(&rp->rp_dv, wc, &vp);
}
if (!rp_updxfr(rp)) /* Update regs to reflect progress */
return 0; /* Ugh, error of some kind, ATA & DC done */
if (!rp->rp_blkwds) { /* If nothing left to write, */
rp_ioend(rp); /* stop normally. */
return 0;
}
}
/* Get 10's initial data channel info - buffer pointer and count.
** Use this to determine whether a direct xfer makes sense.
*/
wc = (*rp->rp_dv.dv_iobuf)(&rp->rp_dv, 0, &vp);
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rp_wrfilbuf: Write %d (of %ld) words]",
wc, rp->rp_blkwds);
#if KLH10_DEV_DPRPXX
if (rp->rp_dpdma && (wc > rp->rp_dcf.dcf_nwds)) {
#else
if (wc > rp->rp_dcf.dcf_nwds) {
#endif
/* Direct! At least one sector's worth.
*/
totw = (wc < rp->rp_blklim) /* Truncate if needed */
? wc : rp->rp_blklim;
wc = (totw / rp->rp_dcf.dcf_nwds); /* Find # sectors */
rp->rp_isdirect = TRUE;
rp->rp_xfrvp = vp;
if (DVDEBUG(rp)) {
fprintf(DVDBF(rp), "[RP wrdir: %d sec, %ld <- %#lo]\r\n",
wc, (long)rp->rp_blkadr,
(long)(vp - vm_physmap(0)));
if (DVDEBUG(rp) & DVDBF_DATSHO)
rp_showbuf(rp, (unsigned char *)NULL, vp, (int)totw, 0);
}
#if KLH10_DEV_DPRPXX
/* Set up shared vars here */
rp->rp_sdprp->dprp_phyadr = vp - vm_physmap(0);
rp->rp_sdprp->dprp_scnt = wc; /* # sectors */
rp->rp_sdprp->dprp_daddr = rp->rp_blkadr;
rp_dpcmd(rp, DPRP_WRDMA, (size_t)0 /* wc*128*sizeof(w10_t) */);
#else
rp->rp_rescnt = vdk_write(&rp->rp_vdk, vp, (uint32)rp->rp_blkadr, wc);
/* Check for error -- if ran out of space, go offline! */
if ((rp->rp_reserr = rp->rp_vdk.dk_err) == ENOSPC) {
vdk_unmount(&(rp->rp_vdk)); /* Take offline */
RPREG(rp, RHR_ER1) |= RH_1UNS; /* Unsafe */
RPREG(rp, RHR_STS) |= RH_SERR; /* Error summary */
}
#endif
return 1;
}
/* Non-direct xfer, must use shared buffer.
** Xfer is limited by size of shared buffer, but will always
** be some multiple of sector size.
*/
rp->rp_isdirect = FALSE;
bwcnt = (rp->rp_bufwds < rp->rp_blklim)
? rp->rp_bufwds : rp->rp_blklim;
totw = bwcnt;
wp = (w10_t *)(rp->rp_buff);
while (wc && bwcnt) {
if (wc < 0) { /* Channel trying a reverse xfer?? */
/* Yuck, don't attempt to support this. What err to use?
** It's not a device error actually... just halt chan xfer.
*/
(*rp->rp_dv.dv_ioend)(&rp->rp_dv, 1); /* Complain to ctlr */
return 0;
}
/* WC has # words to xfer on this pass.
** VP will always be set cuz we're writing ("channel skip" uses a
** pattern of words, hence VP is never null).
*/
if (wc > bwcnt) /* Limit to # wds in buffer */
wc = bwcnt;
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rp_wrfilbuf: %d %#lo]\r\n",
wc, (long)(vp - vm_physmap(0)));
memcpy((char *)wp, (char *)vp, sizeof(w10_t)*wc);
if (DVDEBUG(rp) & DVDBF_DATSHO)
rp_showbuf(rp, (unsigned char *)(wp - wc), vp, wc, 0);
bwcnt -= wc;
wp += wc;
/* Update controller/datachannel's notion of transfer, and set up
** for next pass. Loop test will fail if WC set 0.
*/
wc = (*rp->rp_dv.dv_iobuf)(&rp->rp_dv, wc, &vp);
}
/* See if buffer has anything and write it out if so */
totw -= bwcnt; /* Find # words put into buffer */
if (totw <= 0) {
rp_ioend(rp); /* Nothing in buffer, stop entire xfer now */
return 0;
}
/* Something in buffer -- ensure rounded up to a sector boundary */
if (bwcnt
&& (wc = bwcnt % rp->rp_dcf.dcf_nwds)) { /* Find # words left */
/* Last sector not completely filled out. Fill it up with
** zero words, which is what real hardware would do.
*/
memset((char *)wp, 0, wc*sizeof(w10_t)); /* Clear rem */
bwcnt = totw + wc; /* Find new rounded-up total */
} else /* Rounded fine, just use totw as is */
bwcnt = totw;
/* Set up for indirect xfer and do it! */
rp->rp_blkwds -= totw; /* Reflect gobble from channel */
rp->rp_blklim -= totw;
wc = (bwcnt / rp->rp_dcf.dcf_nwds); /* Find # sectors */
#if KLH10_DEV_DPRPXX
/* Set up shared mem vars here */
rp->rp_sdprp->dprp_scnt = wc; /* # sectors to write from buffer */
rp->rp_sdprp->dprp_daddr = rp->rp_blkadr;
rp_dpcmd(rp, DPRP_WRITE, (size_t)0 /* wc*128*sizeof(w10_t) */);
#else
/* Set up WC and VP for indirect xfer */
vp = (vmptr_t) rp->rp_buff; /* Pointer to buffer */
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[RP wrbuf: %d sec, %ld <- 0x%lx]\r\n",
wc, (long)rp->rp_blkadr, (long)vp);
rp->rp_rescnt = vdk_write(&rp->rp_vdk, vp, (uint32)rp->rp_blkadr, wc);
/* Check for error -- if ran out of space, go offline! */
if ((rp->rp_reserr = rp->rp_vdk.dk_err) == ENOSPC) {
vdk_unmount(&(rp->rp_vdk)); /* Take offline */
RPREG(rp, RHR_ER1) |= RH_1UNS; /* Unsafe */
RPREG(rp, RHR_STS) |= RH_SERR; /* Error summary */
}
#endif
return 1; /* Say to keep going... */
}
/* RP_RDFLSBUF - Flush record buffer by copying it into 10's memory,
** and ask for another bufferful.
*/
static int
rp_rdflsbuf(register struct rpdev *rp)
{
register int wc;
register long bwcnt, totw;
register w10_t *wp;
vmptr_t vp;
if (rp->rp_xfrcnt++ == 0) {
/* First time - Get initial WC request from channel.
** WC will have # words to xfer (may be 0 if failed or all done, or
** negative if channel is trying to do a reverse transfer.)
** If VP is set, then it points to phys mem to xfer to/from.
*/
wc = (*rp->rp_dv.dv_iobuf)(&rp->rp_dv, 0, &vp);
} else {
/* Invoking after completion of a read.
** Examine result, copy into 10 mem, and set up for next.
*/
#if KLH10_DEV_DPRPXX
totw = rp->rp_sdprp->dprp_scnt; /* Find # sectors read */
#else
totw = rp->rp_rescnt; /* Find # sectors read */
#endif
totw *= rp->rp_dcf.dcf_nwds; /* Find # words read */
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rp_rdflsbuf: Read %ld words]", totw);
if (rp->rp_isdirect) {
/* Just completed a direct transfer, needn't copy from buffer!
** Can simply update channel vars.
*/
if (DVDEBUG(rp) & DVDBF_DATSHO)
rp_showbuf(rp, (unsigned char *)NULL, rp->rp_xfrvp, (int)totw, 0);
wc = (*rp->rp_dv.dv_iobuf)(&rp->rp_dv, (int)totw, &vp);
} else {
/* Indirect transfer, must copy from buffer into 10's memory.
** Get 10's current buffer pointer and count
*/
wc = (*rp->rp_dv.dv_iobuf)(&rp->rp_dv, 0, &vp);
wp = (w10_t *)rp->rp_buff; /* Get ptr to buffer */
bwcnt = totw;
for (; wc && bwcnt; ) {
if (wc < 0) { /* Channel trying a reverse xfer?? */
/* Yuck, don't attempt to support this. What err to use?
** It's not a device error actually... just stop channel.
*/
if (!rp_updxfr(rp)) /* Update xfer so far */
return 0;
(*rp->rp_dv.dv_ioend)(&rp->rp_dv, 1); /* Tell ctlr */
return 0;
}
if (wc > bwcnt) /* Apply block wdcount limit */
wc = bwcnt;
if (vp) { /* VP may be NULL if skipping */
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rp_rdflsbuf: %d %#lo]\r\n",
wc, (long)(vp - vm_physmap(0)));
memcpy((char *)vp, (char *)wp, sizeof(w10_t)*wc);
if (DVDEBUG(rp) & DVDBF_DATSHO)
rp_showbuf(rp, (unsigned char *)wp, vp, wc, 0);
} else
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[rp_rdflsbuf: skip %d]\r\n", wc);
bwcnt -= wc; /* Update block wdcnt */
wp += wc; /* and read pointer from buffer */
/* Update controller/datachannel's notion of transfer, and set
** up for next pass. Loop test will fail if WC set 0.
*/
wc = (*rp->rp_dv.dv_iobuf)(&rp->rp_dv, wc, &vp);
}
totw -= bwcnt; /* Find total # words copied */
}
/* Disk input all copied into 10 mem, or no more space from channel.
** See if need to read more from disk.
*/
rp->rp_blkwds -= totw;
rp->rp_blklim -= totw;
if (!rp_updxfr(rp)) /* Update regs, check for error */
return 0;
if (!wc || !rp->rp_blkwds) { /* Ran out in either chan or dev? */
rp_ioend(rp); /* Yup, stop xfer */
return 0;
}
}
/* First time, or more to do.
** Set up and initiate read, using remaining block count.
** WC has # words channel wants for a direct xfer; VP is set if xfer
** is to memory (else skipping).
*/
if ((bwcnt = rp->rp_blklim) == 0) { /* If no more to read, */
rp_ioend(rp); /* terminate normally */
return 0;
}
/* See if OK to do a direct xfer, and
** see how many words we can xfer on this pass.
*/
if (vp && (wc > rp->rp_dcf.dcf_nwds)
#if KLH10_DEV_DPRPXX
&& rp->rp_dpdma
#endif
) {
/* Yup, at least one sector's worth. */
rp->rp_isdirect = TRUE;
wc = (wc < bwcnt) ? wc : bwcnt; /* Truncate if needed */
wc = (wc / rp->rp_dcf.dcf_nwds); /* Find # sectors */
rp->rp_xfrvp = vp; /* Remember loc reading into */
#if KLH10_DEV_DPRPXX
rp->rp_sdprp->dprp_phyadr = vp - vm_physmap(0);
#endif
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[RP rddir: %d sec, %ld -> %#lo]\r\n",
wc, (long)rp->rp_blkadr, (long)(vp - vm_physmap(0)));
} else {
/* Non-direct xfer, must use shared buffer.
** Xfer is limited by size of shared buffer, but will always
** be some multiple of sector size.
** Set up WC and VP for indirect xfer.
*/
rp->rp_isdirect = FALSE;
bwcnt = (rp->rp_bufwds < bwcnt) ? rp->rp_bufwds : bwcnt;
wc = (bwcnt / rp->rp_dcf.dcf_nwds); /* Find # sectors */
vp = (vmptr_t) rp->rp_buff; /* Pointer to buffer */
if (DVDEBUG(rp))
fprintf(DVDBF(rp), "[RP rdbuf: %d sec, %ld -> 0x%lx]\r\n",
wc, (long)rp->rp_blkadr, (long)vp);
}
#if KLH10_DEV_DPRPXX
rp->rp_sdprp->dprp_scnt = wc;
rp->rp_sdprp->dprp_daddr = rp->rp_blkadr;
rp_dpcmd(rp, (rp->rp_isdirect ? DPRP_RDDMA : DPRP_READ),
(size_t)0 /* wc*128*sizeof(w10_t) */);
# if 0
dp_xswait(&(rp->rp_dp.dp_adr->dpc_todp)); /* Synch hack */
# endif
#else
rp->rp_rescnt = vdk_read(&rp->rp_vdk, vp, (uint32)rp->rp_blkadr, wc);
rp->rp_reserr = rp->rp_vdk.dk_err;
#endif
return 1;
}
static void
rp_showbuf(register struct rpdev *rp,
register unsigned char *ucp,
register vmptr_t vp,
register int wc,
int fmt)
{
register w10_t w;
int fpw;
fpw = fmt; /* for now */
for (; --wc >= 0; ++vp) {
w = vm_pget(vp);
fprintf(DVDBF(rp), "[RPXX: %#lo/ %6lo,,%6lo",
(long)(vp - vm_physmap(0)), (long)LHGET(w), (long)RHGET(w));
if (fpw) {
register int i = fpw;
fprintf(DVDBF(rp), " ");
while (--i >= 0)
fprintf(DVDBF(rp), " %3o", *ucp++);
}
fprintf(DVDBF(rp), "]\r\n");
}
}
#endif /* KLH10_DEV_RPXX */