Simple ebus, Massbus adapter and Massbus disk drivers.

The Massbus disk drivers should be merged with the vax Massbus disk drivers
when the 4.4BSD Massbus code gets free.
This commit is contained in:
ragge 2003-08-19 10:51:57 +00:00
parent ab103e8af7
commit 7a599d2c3e
7 changed files with 1301 additions and 0 deletions

81
sys/arch/pdp10/dev/ebus.c Normal file
View File

@ -0,0 +1,81 @@
/* $NetBSD: ebus.c,v 1.1 2003/08/19 10:51:57 ragge Exp $ */
/*
* Copyright (c) 2003 Anders Magnusson (ragge@ludd.luth.se).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <machine/bus.h>
#include <pdp10/dev/ebus.h>
static int
ebus_print(void *aux, const char *n)
{
struct ebus_attach_args *ea = aux;
printf(" csr %o", ea->ea_ioh);
return UNCONF;
}
static int
ebus_match(struct device *parent, struct cfdata *cf, void *aux)
{
#ifdef notyet
if (cputype != TYPE_KL)
return 0;
#endif
return 1;
}
static int
ebus_search(struct device *parent, struct cfdata *cf, void *aux)
{
struct ebus_attach_args ea;
int rv;
ea.ea_iot = 0;
ea.ea_ioh = cf->cf_loc[0];
rv = config_match(parent, cf, &ea);
if (rv == 0)
return 0;
config_attach(parent, cf, &ea, ebus_print);
return 1;
}
static void
ebus_attach(struct device *parent, struct device *self, void *aux)
{
printf("\n");
config_search(ebus_search, self, NULL);
}
struct cfattach ebus_ca = {
sizeof(struct device), ebus_match, ebus_attach,
};

32
sys/arch/pdp10/dev/ebus.h Normal file
View File

@ -0,0 +1,32 @@
/* $NetBSD: ebus.h,v 1.1 2003/08/19 10:51:57 ragge Exp $ */
/*
* Copyright (c) 2003 Anders Magnusson (ragge@ludd.luth.se).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
struct ebus_attach_args {
bus_space_tag_t ea_iot;
bus_space_handle_t ea_ioh;
};

505
sys/arch/pdp10/dev/hp.c Normal file
View File

@ -0,0 +1,505 @@
/* $NetBSD: hp.c,v 1.1 2003/08/19 10:51:57 ragge Exp $ */
/*
* Copyright (c) 1996 Ludd, University of Lule}, Sweden.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed at Ludd, University of
* Lule}, Sweden and its contributors.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Simple device driver routine for massbuss disks.
* TODO:
* Fix support for Standard DEC BAD144 bad block forwarding.
* Be able to to handle soft/hard transfer errors.
* Handle non-data transfer interrupts.
* Autoconfiguration of disk drives 'on the fly'.
* Handle disk media changes.
* Dual-port operations should be supported.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/disklabel.h>
#include <sys/disk.h>
#include <sys/dkio.h>
#include <sys/buf.h>
#include <sys/stat.h>
#include <sys/ioccom.h>
#include <sys/fcntl.h>
#include <sys/syslog.h>
#include <sys/reboot.h>
#include <sys/conf.h>
#include <sys/event.h>
#include <machine/bus.h>
#include <machine/io.h>
#include <pdp10/dev/rhvar.h>
#include <pdp10/dev/rhreg.h>
#include <pdp10/dev/hpreg.h>
#include "ioconf.h"
#include "locators.h"
struct hp_softc {
struct device sc_dev;
struct disk sc_disk;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
int sc_unit;
struct rh_device sc_md; /* Common struct used by mbaqueue. */
int sc_wlabel; /* Disklabel area is writable */
};
int hpmatch(struct device *, struct cfdata *, void *);
void hpattach(struct device *, struct device *, void *);
void hpstart(struct rh_device *);
int hpattn(struct rh_device *);
int hpfinish(struct rh_device *, int, int *);
CFATTACH_DECL(hp, sizeof(struct hp_softc), hpmatch, hpattach, NULL, NULL);
dev_type_open(hpopen);
dev_type_close(hpclose);
dev_type_read(hpread);
dev_type_write(hpwrite);
dev_type_ioctl(hpioctl);
dev_type_strategy(hpstrategy);
dev_type_size(hpsize);
const struct bdevsw hp_bdevsw = {
hpopen, hpclose, hpstrategy, hpioctl, nulldump, hpsize, D_DISK
};
const struct cdevsw hp_cdevsw = {
hpopen, hpclose, hpread, hpwrite, hpioctl,
nostop, notty, nopoll, nommap, nokqfilter, D_DISK
};
#define HP_WCSR(reg, val) \
DATAO(sc->sc_ioh, ((reg) << 30) | (sc->sc_unit << 18) | \
RH20_DATAO_LR | (val))
#define HP_RCSR(reg, val) \
DATAO(sc->sc_ioh, ((reg) << 30) | (sc->sc_unit << 18)); \
DATAI(sc->sc_ioh, (val))
static struct hpsizes {
int type, sectrk, trkcyl, cylunit;
} hpsizes[] = {
{ RH_DT_RP04, 20, 19, 400, },
{ RH_DT_RP05, 20, 19, 400, },
{ RH_DT_RP06, 20, 19, 800, },
{ RH_DT_RP07, 43, 32, 629, },
{ RH_DT_RM03, 30, 5, 820, },
{ RH_DT_RM05, 30, 19, 400, },
{ 0, },
};
static void
hpfakelabel(int type, char *name, struct disklabel *dl)
{
int i;
for (i = 0; hpsizes[i].type; i++)
if (hpsizes[i].type == type)
break;
if (hpsizes[i].type == 0)
return;
dl->d_magic2 = dl->d_magic = DISKMAGIC;
dl->d_type = DTYPE_SMD;
strcpy(dl->d_typename, name);
dl->d_secsize = DEV_BSIZE;
dl->d_nsectors = hpsizes[i].sectrk;
dl->d_ntracks = hpsizes[i].trkcyl;
dl->d_ncylinders = hpsizes[i].cylunit;
dl->d_secpercyl = hpsizes[i].sectrk * hpsizes[i].trkcyl;
dl->d_secperunit = hpsizes[i].sectrk * hpsizes[i].trkcyl *
hpsizes[i].cylunit;
dl->d_rpm = 3600;
dl->d_interleave = 1;
dl->d_partitions[2].p_offset = 0;
dl->d_partitions[2].p_size = dl->d_secperunit;
dl->d_npartitions = 3;
}
/*
* Check if this is a disk drive; done by checking type from mbaattach.
*/
int
hpmatch(struct device *parent, struct cfdata *cf, void *aux)
{
struct rh_attach_args *ma = aux;
if (cf->cf_loc[RHCF_DRIVE] != RHCF_DRIVE_DEFAULT &&
cf->cf_loc[RHCF_DRIVE] != ma->ma_unit)
return 0;
if (ma->ma_devtyp != MB_RP)
return 0;
return 1;
}
/*
* Disk drive found; fake a disklabel and try to read the real one.
* If the on-disk label can't be read; we lose.
*/
void
hpattach(struct device *parent, struct device *self, void *aux)
{
struct hp_softc *sc = (void *)self;
struct rh_softc *ms = (void *)parent;
struct disklabel *dl;
struct rh_attach_args *ma = aux;
char *msg;
sc->sc_iot = ma->ma_iot;
sc->sc_ioh = ma->ma_ioh;
/*
* Init the common struct for both the adapter and its slaves.
*/
bufq_alloc(&sc->sc_md.md_q, BUFQ_DISKSORT|BUFQ_SORT_CYLINDER);
sc->sc_md.md_softc = (void *)sc; /* Pointer to this softc */
sc->sc_md.md_rh = (void *)parent; /* Pointer to parent softc */
sc->sc_md.md_start = hpstart; /* Disk start routine */
sc->sc_md.md_attn = hpattn; /* Disk attention routine */
sc->sc_md.md_finish = hpfinish; /* Disk xfer finish routine */
sc->sc_unit = ma->ma_unit;
ms->sc_md[ma->ma_unit] = &sc->sc_md; /* Per-unit backpointer */
/*
* Init and attach the disk structure.
*/
sc->sc_disk.dk_name = sc->sc_dev.dv_xname;
disk_attach(&sc->sc_disk);
/*
* Fake a disklabel to be able to read in the real label.
*/
dl = sc->sc_disk.dk_label;
hpfakelabel(ma->ma_type, ma->ma_name, dl);
/*
* Read in label.
*/
if ((msg = readdisklabel(makedev(0, self->dv_unit * 8), hpstrategy,
dl, NULL)) != NULL)
printf(": %s", msg);
printf(": %s, size = %d sectors\n", dl->d_typename, dl->d_secperunit);
}
void
hpstrategy(struct buf *bp)
{
struct hp_softc *sc;
struct buf *gp;
int unit, s, err;
struct disklabel *lp;
unit = DISKUNIT(bp->b_dev);
sc = hp_cd.cd_devs[unit];
lp = sc->sc_disk.dk_label;
err = bounds_check_with_label(&sc->sc_disk, bp, sc->sc_wlabel);
if (err < 0)
goto done;
bp->b_rawblkno =
bp->b_blkno + lp->d_partitions[DISKPART(bp->b_dev)].p_offset;
bp->b_cylinder = bp->b_rawblkno / lp->d_secpercyl;
s = splbio();
gp = BUFQ_PEEK(&sc->sc_md.md_q);
BUFQ_PUT(&sc->sc_md.md_q, bp);
if (gp == 0)
rhqueue(&sc->sc_md);
splx(s);
return;
done:
bp->b_resid = bp->b_bcount;
biodone(bp);
}
/*
* Start transfer on given disk. Called from mbastart().
*/
void
hpstart(struct rh_device *md)
{
struct hp_softc *sc = md->md_softc;
struct disklabel *lp = sc->sc_disk.dk_label;
struct buf *bp = BUFQ_PEEK(&md->md_q);
unsigned bn, cn, sn, tn;
/*
* Collect statistics.
*/
disk_busy(&sc->sc_disk);
sc->sc_disk.dk_seek++;
bn = bp->b_rawblkno;
if (bn) {
cn = bn / lp->d_secpercyl;
sn = bn % lp->d_secpercyl;
tn = sn / lp->d_nsectors;
sn = sn % lp->d_nsectors;
} else
cn = sn = tn = 0;
HP_WCSR(HP_DC, cn);
#ifdef __pdp10__
md->md_da = (tn << 8) | sn | (sc->sc_unit << 18);
md->md_csr = (bp->b_flags & B_READ ? HPCS_READ : HPCS_WRITE) |
(sc->sc_unit << 18);
#else
HP_WCSR(HP_DA, (tn << 8) | sn);
if (bp->b_flags & B_READ)
HP_WCSR(HP_CS1, HPCS_READ);
else
HP_WCSR(HP_CS1, HPCS_WRITE);
#endif
}
int
hpopen(dev_t dev, int flag, int fmt, struct proc *p)
{
struct hp_softc *sc;
int unit, part;
unit = DISKUNIT(dev);
if (unit >= hp_cd.cd_ndevs)
return ENXIO;
sc = hp_cd.cd_devs[unit];
if (sc == 0)
return ENXIO;
part = DISKPART(dev);
if (part >= sc->sc_disk.dk_label->d_npartitions)
return ENXIO;
switch (fmt) {
case S_IFCHR:
sc->sc_disk.dk_copenmask |= (1 << part);
break;
case S_IFBLK:
sc->sc_disk.dk_bopenmask |= (1 << part);
break;
}
sc->sc_disk.dk_openmask =
sc->sc_disk.dk_copenmask | sc->sc_disk.dk_bopenmask;
return 0;
}
int
hpclose(dev_t dev, int flag, int fmt, struct proc *p)
{
struct hp_softc *sc;
int unit, part;
unit = DISKUNIT(dev);
sc = hp_cd.cd_devs[unit];
part = DISKPART(dev);
switch (fmt) {
case S_IFCHR:
sc->sc_disk.dk_copenmask &= ~(1 << part);
break;
case S_IFBLK:
sc->sc_disk.dk_bopenmask &= ~(1 << part);
break;
}
sc->sc_disk.dk_openmask =
sc->sc_disk.dk_copenmask | sc->sc_disk.dk_bopenmask;
return 0;
}
int
hpioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
struct hp_softc *sc = hp_cd.cd_devs[DISKUNIT(dev)];
struct disklabel *lp = sc->sc_disk.dk_label;
int error;
switch (cmd) {
case DIOCGDINFO:
bcopy(lp, addr, sizeof (struct disklabel));
return 0;
case DIOCGPART:
((struct partinfo *)addr)->disklab = lp;
((struct partinfo *)addr)->part =
&lp->d_partitions[DISKPART(dev)];
break;
case DIOCSDINFO:
if ((flag & FWRITE) == 0)
return EBADF;
return setdisklabel(lp, (struct disklabel *)addr, 0, 0);
case DIOCWDINFO:
if ((flag & FWRITE) == 0)
error = EBADF;
else {
sc->sc_wlabel = 1;
error = writedisklabel(dev, hpstrategy, lp, 0);
sc->sc_wlabel = 0;
}
return error;
case DIOCWLABEL:
if ((flag & FWRITE) == 0)
return EBADF;
sc->sc_wlabel = 1;
break;
default:
return ENOTTY;
}
return 0;
}
/*
* Called when a transfer is finished. Check if transfer went OK,
* Return info about what-to-do-now.
*/
int
hpfinish(struct rh_device *md, int mbasr, int *attn)
{
struct hp_softc *sc = md->md_softc;
struct buf *bp = BUFQ_PEEK(&md->md_q);
int er1, er2;
HP_RCSR(HP_ER1, er1);
er1 &= 0177777;
HP_RCSR(HP_ER2, er2);
er2 &= 0177777;
HP_WCSR(HP_ER1, 0);
HP_WCSR(HP_ER2, 0);
hper1:
switch (ffs(er1) - 1) {
case -1:
HP_WCSR(HP_ER1, 0);
goto hper2;
case HPER1_DCK: /* Corrected? data read. Just notice. */
panic("hpfinish");
#if 0
bc = bus_space_read_4(md->md_mba->sc_iot,
md->md_mba->sc_ioh, MBA_BC);
byte = ~(bc >> 16);
diskerr(buf, hp_cd.cd_name, "soft ecc", LOG_PRINTF,
btodb(bp->b_bcount - byte), sc->sc_disk.dk_label);
er1 &= ~(1<<HPER1_DCK);
break;
#endif
default:
printf("drive error :%s er1 %x er2 %x\n",
sc->sc_dev.dv_xname, er1, er2);
HP_WCSR(HP_ER1, 0);
HP_WCSR(HP_ER2, 0);
goto hper2;
}
goto hper1;
hper2:
mbasr &= ~(RH20_CONI_MBH|RH20_CONI_MEN|RH20_CONI_ATE|
RH20_CONI_DON|RH20_CONO_IMSK);
if (mbasr)
printf("massbuss error :%s %x\n",
sc->sc_dev.dv_xname, mbasr);
BUFQ_PEEK(&md->md_q)->b_resid = 0;
disk_unbusy(&sc->sc_disk, BUFQ_PEEK(&md->md_q)->b_bcount,
(bp->b_flags & B_READ));
return XFER_FINISH;
}
/*
* Non-data transfer interrupt; like volume change.
*/
int
hpattn(struct rh_device *md)
{
struct hp_softc *sc = md->md_softc;
int er1, er2;
HP_RCSR(HP_ER1, er1);
HP_RCSR(HP_ER2, er2);
printf("%s: Attention! er1 %x er2 %x\n",
sc->sc_dev.dv_xname, er1, er2);
return 0;
}
int
hpsize(dev_t dev)
{
int size, unit = DISKUNIT(dev);
struct hp_softc *sc;
if (unit >= hp_cd.cd_ndevs || hp_cd.cd_devs[unit] == 0)
return -1;
sc = hp_cd.cd_devs[unit];
size = sc->sc_disk.dk_label->d_partitions[DISKPART(dev)].p_size *
(sc->sc_disk.dk_label->d_secsize / DEV_BSIZE);
return size;
}
int
hpdump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
{
return 0;
}
int
hpread(dev_t dev, struct uio *uio, int ioflag)
{
return (physio(hpstrategy, NULL, dev, B_READ, minphys, uio));
}
int
hpwrite(dev_t dev, struct uio *uio, int ioflag)
{
return (physio(hpstrategy, NULL, dev, B_WRITE, minphys, uio));
}

125
sys/arch/pdp10/dev/hpreg.h Normal file
View File

@ -0,0 +1,125 @@
/* $NetBSD: hpreg.h,v 1.1 2003/08/19 10:51:57 ragge Exp $ */
/*
* Copyright (c) 1994 Ludd, University of Lule}, Sweden.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed at Ludd, University of Lule}.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef notdef
struct hp_regs {
int hp_cs1;
int hp_ds;
int hp_er1;
int hp_mr1;
int hp_as;
int hp_da;
int hp_dt;
int hp_la;
int hp_sn;
int hp_of;
int hp_dc;
int hp_hr;
int hp_mr2;
int hp_er2;
int hp_ec1;
int hp_ec2;
int utrymme[16];
};
#endif
#define HP_CS1 0
#define HP_DS 1
#define HP_ER1 2
#define HP_MR1 3
#define HP_AS 4
#define HP_DA 5
#define HP_DT 6
#define HP_LA 7
#define HP_SN 8
#define HP_OF 9
#define HP_DC 10
#define HP_HR 11
#define HP_MR2 12
#define HP_ER2 13
#define HP_EC1 14
#define HP_EC2 15
#define HPCS_DVA 4000 /* Drive avail, in dual-port config */
#define HPCS_WRITE 061 /* Write data */
#define HPCS_WCHECK 051 /* Write check data */
#define HPCS_WHEAD 063 /* Write header and data */
#define HPCS_WCHEAD 053 /* Write check header and data */
#define HPCS_READ 071 /* Read data */
#define HPCS_RHEAD 073 /* Read header and data */
#define HPCS_SEEK 005 /* Just seek */
#define HPCS_RECAL 007 /* Recalibrate */
#define HPCS_RTC 017 /* Return to centerline */
#define HPCS_OFFSET 015 /* Offset */
#define HPCS_SEARCH 031 /* Search */
#define HPCS_UNLOAD 003 /* Unload pack (removable) */
#define HPCS_RELEASE 013 /* Release massbuss port */
#define HPCS_RPS 021 /* Read-in preset */
#define HPCS_PA 023 /* Pack acknowledge */
#define HPCS_DC 011 /* Drive clear */
#define HPDS_VV 0x40 /* Volume valid, not changed */
#define HPDS_DRY 0x80 /* Drive ready to accept commands */
#define HPDS_DPR 0x100 /* Drive present */
#define HPDS_PGM 0x200 /* Programmable in dual-port config */
#define HPDS_LBT 0x400 /* Last block transferred */
#define HPDS_WRL 0x800 /* Write locked media */
#define HPDS_MOL 0x1000 /* Medium on-line */
#define HPDS_PIP 0x2000 /* Positioning in progress */
#define HPDS_ERR 0x4000 /* ORed error bit, something wrong */
#define HPDS_ATA 0x8000 /* Attention drive */
#define HPDT_DRQ 0x800 /* Dual-port disk */
#define HPOF_FMT 0x1000 /* 16/18 bit data */
/*
* Error registers. The defines are the corresponding bit number
* in the error register, instead of a bit mask.
* Use (1<<HPER1_FOO) when touching registers.
*/
#define HPER1_ILF 0 /* Illegal function */
#define HPER1_ILR 1 /* Illegal register */
#define HPER1_RMR 2 /* Register modification refused */
#define HPER1_PAR 3 /* Parity error */
#define HPER1_FER 4 /* Format error */
#define HPER1_WCF 5 /* Write clock failed */
#define HPER1_ECH 6 /* ECC hard error */
#define HPER1_HCE 7 /* Header compare error */
#define HPER1_HCRC 8 /* Header CRC error */
#define HPER1_AOE 9 /* Address overflow error */
#define HPER1_IAE 10 /* Invalid address error */
#define HPER1_WLE 11 /* Write lock error */
#define HPER1_DTE 12 /* Drive timing error */
#define HPER1_OPI 13 /* Operation incomplete */
#define HPER1_UNS 14 /* Unsafe drive */
#define HPER1_DCK 15 /* Data check error */

306
sys/arch/pdp10/dev/rh.c Normal file
View File

@ -0,0 +1,306 @@
/* $NetBSD: rh.c,v 1.1 2003/08/19 10:51:57 ragge Exp $ */
/*
* Copyright (c) 1994, 1996 Ludd, University of Lule}, Sweden.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed at Ludd, University of
* Lule}, Sweden and its contributors.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Simple massbus drive routine.
* TODO:
* Autoconfig new devices 'on the fly'.
* More intelligent way to handle different interrupts.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/queue.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <uvm/uvm_extern.h>
#include <machine/bus.h>
#include <machine/io.h>
#include <machine/pcb.h>
#include <pdp10/dev/ebus.h>
#include <pdp10/dev/rhreg.h>
#include <pdp10/dev/rhvar.h>
#include "ioconf.h"
#include "locators.h"
struct rhunit rhunit[] = {
{RH_DT_RP04, "rp04", MB_RP},
{RH_DT_RP05, "rp05", MB_RP},
{RH_DT_RP06, "rp06", MB_RP},
{RH_DT_RP07, "rp07", MB_RP},
{RH_DT_RM02, "rm02", MB_RP},
{RH_DT_RM03, "rm03", MB_RP},
{RH_DT_RM05, "rm05", MB_RP},
{RH_DT_RM80, "rm80", MB_RP},
{RH_DT_TU45, "tu45", MB_TU},
{0, 0, 0}
};
int rhmatch(struct device *, struct cfdata *, void *);
void rhattach(struct device *, struct device *, void *);
void rhintr(void *);
int rhprint(void *, const char *);
void rhqueue(struct rh_device *);
void rhstart(struct rh_softc *);
void rhmapregs(struct rh_softc *);
struct cfattach rh_ca = {
sizeof(struct rh_softc), rhmatch, rhattach
};
#define MBA_WCSR(reg, val) \
DATAO(sc->sc_ioh | (reg << 30), val)
#define MBA_RCSR(reg) \
({ int rv; DATAI(sc->sc_ioh | (reg << 30), rv); rv; })
#define RH_READREG(disk, reg, val) \
DATAO(sc->sc_ioh, ((reg) << 30) | ((disk) << 18)); \
DATAI(sc->sc_ioh, val)
#define RH_WRITEREG(disk, reg, val) \
DATAO(sc->sc_ioh, ((reg) << 30) | ((disk) << 18) | 04000000000 | (val))
/*
* Look if this is a massbuss adapter.
*/
int
rhmatch(struct device *parent, struct cfdata *cf, void *aux)
{
struct ebus_attach_args *ea = aux;
int rv;
/* Try a reset */
CONO(ea->ea_ioh, RH20_CONO_RST|RH20_CONO_RCL);
/* DELAY(1000); */
/* Check presence by toggling the ATE bit */
CONI(ea->ea_ioh, rv);
if (rv & RH20_CONI_ATE)
return 0;
CONO(ea->ea_ioh, RH20_CONO_ATE);
CONI(ea->ea_ioh, rv);
if (rv & RH20_CONI_ATE)
return 1;
return 0;
}
/*
* Attach the found massbuss adapter. Setup its interrupt vectors,
* reset it and go searching for drives on it.
*/
void
rhattach(struct device *parent, struct device *self, void *aux)
{
struct rh_softc *sc = (void *)self;
struct ebus_attach_args *ea = aux;
struct rh_attach_args ma;
int i, j, rv;
printf(": RH20\n");
sc->sc_iot = ea->ea_iot;
sc->sc_ioh = ea->ea_ioh;
CONO(ea->ea_ioh, RH20_CONO_MEN|RH20_CONO_ATE|IPL_BIO);
sc->sc_first = 0;
sc->sc_last = (void *)&sc->sc_first;
RH_WRITEREG(0, RH_IVIR, MAKEIV(IPL_BIO));
for (i = 0; i < MAXMBADEV; i++) {
sc->sc_state = SC_AUTOCONF;
RH_READREG(i, RH_DS, rv);
if ((rv & RH_DS_DPR) == 0) {
CONO(sc->sc_ioh, RH20_CONO_RAE|RH20_CONO_ATE|IPL_BIO);
continue;
}
/* We have a drive, ok. */
ma.ma_unit = i;
RH_READREG(i, RH_DT, rv);
ma.ma_type = rv & 0777;
j = 0;
while (rhunit[j++].nr)
if (rhunit[j].nr == ma.ma_type)
break;
ma.ma_devtyp = rhunit[j].devtyp;
ma.ma_name = rhunit[j].name;
ma.ma_iot = sc->sc_iot;
ma.ma_ioh = sc->sc_ioh;
config_found(&sc->sc_dev, (void *)&ma, rhprint);
}
}
/*
* We got an interrupt. Check type of interrupt and call the specific
* device interrupt handling routine.
*/
void
rhintr(void *rh)
{
struct rh_softc *sc = rh;
struct rh_device *md;
struct buf *bp;
int itype, attn, anr;
sc = rh_cd.cd_devs[0];
CONI(sc->sc_ioh, itype);
CONO(sc->sc_ioh, itype &
(RH20_CONO_DON|RH20_CONO_ATE|RH20_CONO_MEN));
RH_READREG(0, RH_AS, attn);
attn &= 0377;
RH_WRITEREG(0, RH_AS, attn);
if (sc->sc_state == SC_AUTOCONF)
return; /* During autoconfig */
md = sc->sc_first;
bp = BUFQ_PEEK(&md->md_q);
/*
* A data-transfer interrupt. Current operation is finished,
* call that device's finish routine to see what to do next.
*/
if (sc->sc_state == SC_ACTIVE) {
sc->sc_state = SC_IDLE;
switch ((*md->md_finish)(md, itype, &attn)) {
case XFER_FINISH:
/*
* Transfer is finished. Take buffer of drive
* queue, and take drive of adapter queue.
* If more to transfer, start the adapter again
* by calling rhstart().
*/
(void)BUFQ_GET(&md->md_q);
sc->sc_first = md->md_back;
md->md_back = 0;
if (sc->sc_first == 0)
sc->sc_last = (void *)&sc->sc_first;
if (BUFQ_PEEK(&md->md_q) != NULL) {
sc->sc_last->md_back = md;
sc->sc_last = md;
}
bp->b_resid = 0;
biodone(bp);
if (sc->sc_first)
rhstart(sc);
break;
case XFER_RESTART:
/*
* Something went wrong with the transfer. Try again.
*/
rhstart(sc);
break;
}
}
while (attn) {
anr = ffs(attn) - 1;
attn &= ~(1 << anr);
if (sc->sc_md[anr]->md_attn == 0)
panic("Should check for new MBA device %d", anr);
(*sc->sc_md[anr]->md_attn)(sc->sc_md[anr]);
}
}
int
rhprint(void *aux, const char *rhname)
{
struct rh_attach_args *ma = aux;
if (rhname) {
if (ma->ma_name)
printf("%s", ma->ma_name);
else
printf("device type %o", ma->ma_type);
printf(" at %s", rhname);
}
printf(" drive %d", ma->ma_unit);
return (ma->ma_name ? UNCONF : UNSUPP);
}
/*
* A device calls rhqueue() when it wants to get on the adapter queue.
* Called at splbio(). If the adapter is inactive, start it.
*/
void
rhqueue(struct rh_device *md)
{
struct rh_softc *sc = md->md_rh;
int i = (int)sc->sc_first;
sc->sc_last->md_back = md;
sc->sc_last = md;
if (i == 0)
rhstart(sc);
}
/*
* Start activity on (idling) adapter. Calls rhmapregs() to setup
* for dma transfer, then the unit-specific start routine.
*/
void
rhstart(struct rh_softc *sc)
{
struct rh_device *md = sc->sc_first;
struct buf *bp = BUFQ_PEEK(&md->md_q);
int ncnt = bp->b_bcount/4;
int blkcnt = ncnt/128;
paddr_t pap;
if (pmap_extract(pmap_kernel(), (vaddr_t)bp->b_data, &pap) == FALSE)
panic("rhstart");
if (pap & 03777)
panic("rhstart: bad align");
pap >>= 2;
ept->ept_channel[0][DCH_CCL_OFF] =
DCH_CCW_XFR|DCH_CCW_XHLT| pap | (ncnt << DCH_CCW_CNTSH);
sc->sc_state = SC_ACTIVE;
(*md->md_start)(md); /* machine-dependent start */
RH_WRITEREG(0, RH_SBAR, md->md_da);
RH_WRITEREG(0, RH_STCR,
md->md_csr | RH_TCR_RCLP|RH_TCR_SES |
(((-blkcnt) << RH_TCR_NBCSH) & RH_TCR_NBC));
}

135
sys/arch/pdp10/dev/rhreg.h Normal file
View File

@ -0,0 +1,135 @@
/* $NetBSD: rhreg.h,v 1.1 2003/08/19 10:51:57 ragge Exp $ */
/*
* Copyright (c) 2003 Anders Magnusson (ragge@ludd.luth.se).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* RH20 register definitions.
*/
/*
* Different device codes.
*/
#define RH_DT_RP04 0x10
#define RH_DT_RP05 0x11
#define RH_DT_RP06 0x12
#define RH_DT_RP07 0x22
#define RH_DT_RM02 0x15
#define RH_DT_RM03 0x14
#define RH_DT_RM05 0x17
#define RH_DT_RM80 0x16
#define RH_DT_TU45 052
#define RH_DT_DRQ 0x800 /* Dual ported */
#define RH_DT_MOH 0x2000 /* Moving head device */
/*
* Some common registers used in the rh device driver.
*/
#define RH_DS 1 /* unit status */
#define RH_AS 4 /* attention summary */
#define RH_DT 6 /* drive type */
#define RH_SBAR 070 /* Bus address register */
#define RH_STCR 071 /* Transfer control register */
#define RH_IVIR 074 /* Interrupt vector register */
#define RH_DS_DPR 0x100 /* Unit present */
/*
* Internal register bits.
*/
#define RH_BAR_ADRMSK 0177777 /* Disk/tape address mask */
#define RH_TCR_RCLP 0002000000000 /* Reset channel when starting */
#define RH_TCR_SES 0000200000000 /* Store status after transfer */
#define RH_TCR_DTES 0000000200000 /* Don't stop if errors */
#define RH_TCR_NBC 0000000177700 /* Negative block count */
#define RH_TCR_NBCSH 6 /* Block count shift */
/*
* RH20 register definitions.
*/
#define RH20_CONI_BPE 0400000 /* Data bus parity error */
#define RH20_CONI_EXC 0200000 /* Exception */
#define RH20_CONI_LWE 0100000 /* Long word count error */
#define RH20_CONI_SWE 0040000 /* Short word count error */
#define RH20_CONI_MBE 0020000 /* MBOX error */
#define RH20_CONI_DRE 0010000 /* Drive response error */
#define RH20_CONI_RAE 0004000 /* Register access error */
#define RH20_CONI_MBH 0002000 /* MBOX halted */
#define RH20_CONI_OVR 0001000 /* Data overrun */
#define RH20_CONI_MEN 0000400 /* Massbus enable */
#define RH20_CONI_ATN 0000200 /* Drive attention */
#define RH20_CONI_2RF 0000100 /* Secondary command register full */
#define RH20_CONI_ATE 0000040 /* Attention enable */
#define RH20_CONI_1RF 0000020 /* Primary command register full */
#define RH20_CONI_DON 0000010 /* Channel done */
#define RH20_CONO_RAE 0004000 /* Clear RAE */
#define RH20_CONO_RST 0002000 /* Controller reset */
#define RH20_CONO_XFR 0001000 /* Clear transfer error */
#define RH20_CONO_MEN 0000400 /* Massbus enable */
#define RH20_CONO_RCL 0000200 /* Reset MBOX command list pointer */
#define RH20_CONO_DEL 0000100 /* Delete secondary command */
#define RH20_CONO_ATE 0000040 /* Enable attention interrupts */
#define RH20_CONO_STP 0000020 /* Stop current command */
#define RH20_CONO_DON 0000010 /* Clear done */
#define RH20_CONO_IMSK 0000007 /* Mask for interrupt level */
#define RH20_DATAI_CBPE 0001000000000 /* Control bus parity error */
#define RH20_DATAI_TRA 0000200000000 /* Transfer received */
#define RH20_DATAI_CPA 0000000200000 /* Massbus parity bit */
#define RH20_DATAO_RS 0770000000000 /* Register select */
#define RH20_DATAO_LR 0004000000000 /* Load register */
#define RH20_DATAO_RAES 0000400000000 /* RAE Suppress */
/*
* Data channel defines.
*/
#define DCH_CCL_OFF 0 /* Channel Command List opcode */
#define DCH_CST_OFF 1 /* Channel Status Word */
#define DCH_CCW_OFF 2 /* Current Channel Command Word */
#define DCH_CIV_OFF 3 /* Unused, free for software to use */
#define DCH_CCW_HLT 0000000000000 /* HALT opcode */
#define DCH_CCW_JMP 0200000000000 /* JUMP opcode */
#define DCH_CCW_XFR 0400000000000 /* XFER opcode, plus next 2 bits: */
#define DCH_CCW_XHLT 0200000000000 /* Xfer cmd: "Halt after xfer" */
#define DCH_CCW_XREV 0100000000000 /* Xfer cmd: "Reverse xfer" */
#define DCH_CCW_CNTMSK 0077760000000 /* Word Count (positive) */
#define DCH_CCW_CNTSH 22 /* Word Count shift */
#define DCH_CCW_ADRMSK 0000017777777 /* 22-bit address */
/* Channel Status Word
*/
#define DCH_CSW_SET 0400000000000 /* Always set 1 */
#define DCH_CSW_MPAR 0200000000000 /* Mem Par Err during CCW fetch */
#define DCH_CSW_NOAPE 0100000000000 /* Set if NO mem addr par err */
#define DCH_CSW_NOWC0 0040000000000 /* CCW count NOT 0 when CSW set */
#define DCH_CSW_NXM 0020000000000 /* Chan referenced NXM */
#define DCH_CSW_LXFE 0000400000000 /* Last Transfer Error */
#define DCH_CSW_RH20E 0000200000000 /* RH20 tried to start not-ready chn */
#define DCH_CSW_LWCE 0000100000000 /* Long Word Count Error */
#define DCH_CSW_SWCE 0000040000000 /* Short Word Count Error */
#define DCH_CSW_OVN 0000020000000 /* Overrun */
#define DCH_CSW_ADRMSK 0000017777777 /* 22-bit CCW addr+1 */

117
sys/arch/pdp10/dev/rhvar.h Normal file
View File

@ -0,0 +1,117 @@
/* $NetBSD: rhvar.h,v 1.1 2003/08/19 10:51:58 ragge Exp $ */
/*
* Copyright (c) 1994 Ludd, University of Lule}, Sweden
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed at Ludd, University of Lule}.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define MBCR_INIT 1
#define MBCR_IE (1<<2)
#define MBDS_DPR (1<<8)
#define MBSR_NED (1<<18)
#define MBDT_MOH (1<<13)
#define MBDT_TYPE 511
#define MBDT_TAP (1<<14)
#define CLOSED 0
#define WANTOPEN 1
#define RDLABEL 2
#define OPEN 3
#define OPENRAW 4
#define MAXMBADEV 8 /* Max units per MBA */
/*
* Devices that have different device drivers.
*/
#define MB_RP 1
#define MB_TU 2
#define MB_MT 3
/*
* Current state of the adapter.
*/
#define SC_AUTOCONF 1
#define SC_ACTIVE 2
#define SC_IDLE 3
/*
* Return value after a finished data transfer, from device driver.
*/
#define XFER_RESTART 1
#define XFER_FINISH 2
/*
* Info passed do unit device driver during autoconfig.
*/
struct rh_attach_args {
int ma_unit;
int ma_type;
int ma_devtyp;
bus_space_tag_t ma_iot;
bus_space_handle_t ma_ioh;
char *ma_name;
};
/*
* Common struct used to communicate between the rh device driver
* and the unit device driver.
*/
struct rh_device {
struct rh_device *md_back; /* linked list of runnable devices */
/* Start routine to be called by rhstart. */
void (*md_start)(struct rh_device *);
/* Routine to be called after attn intr */
int (*md_attn)(struct rh_device *);
/* Call after xfer finish */
int (*md_finish)(struct rh_device *, int, int *);
void *md_softc; /* Backpointer to this units softc. */
struct rh_softc *md_rh;
struct bufq_state md_q; /* queue of I/O requests */
int md_csr; /* Drive command given to RH20 */
int md_da; /* Disk address given to RH20 */
};
struct rh_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
struct evcnt sc_intrcnt;
struct rh_device *sc_first, *sc_last;
int sc_state;
struct rh_device *sc_md[MAXMBADEV];
};
struct rhunit {
int nr;
char *name;
int devtyp;
};
/* Common prototypes */
void rhqueue(struct rh_device *);