e0cc03a09b
kqueue provides a stateful and efficient event notification framework currently supported events include socket, file, directory, fifo, pipe, tty and device changes, and monitoring of processes and signals kqueue is supported by all writable filesystems in NetBSD tree (with exception of Coda) and all device drivers supporting poll(2) based on work done by Jonathan Lemon for FreeBSD initial NetBSD port done by Luke Mewburn and Jason Thorpe
669 lines
16 KiB
C
669 lines
16 KiB
C
/* $NetBSD: rl.c,v 1.17 2002/10/23 09:13:37 jdolecek Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2000 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.
|
|
*/
|
|
|
|
/*
|
|
* RL11/RLV11/RLV12 disk controller driver and
|
|
* RL01/RL02 disk device driver.
|
|
*
|
|
* TODO:
|
|
* Handle disk errors more gracefully
|
|
* Do overlapping seeks on multiple drives
|
|
*
|
|
* Implementation comments:
|
|
*
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: rl.c,v 1.17 2002/10/23 09:13:37 jdolecek Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/device.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/disk.h>
|
|
#include <sys/disklabel.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/dkio.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/event.h>
|
|
|
|
#include <ufs/ufs/dinode.h>
|
|
#include <ufs/ffs/fs.h>
|
|
|
|
#include <machine/bus.h>
|
|
|
|
#include <dev/qbus/ubavar.h>
|
|
#include <dev/qbus/rlreg.h>
|
|
#include <dev/qbus/rlvar.h>
|
|
|
|
#include "ioconf.h"
|
|
#include "locators.h"
|
|
|
|
static int rlcmatch(struct device *, struct cfdata *, void *);
|
|
static void rlcattach(struct device *, struct device *, void *);
|
|
static int rlcprint(void *, const char *);
|
|
static void rlcintr(void *);
|
|
static int rlmatch(struct device *, struct cfdata *, void *);
|
|
static void rlattach(struct device *, struct device *, void *);
|
|
static void rlcstart(struct rlc_softc *, struct buf *);
|
|
static void waitcrdy(struct rlc_softc *);
|
|
static void rlcreset(struct device *);
|
|
|
|
CFATTACH_DECL(rlc, sizeof(struct rlc_softc),
|
|
rlcmatch, rlcattach, NULL, NULL);
|
|
|
|
CFATTACH_DECL(rl, sizeof(struct rl_softc),
|
|
rlmatch, rlattach, NULL, NULL);
|
|
|
|
dev_type_open(rlopen);
|
|
dev_type_close(rlclose);
|
|
dev_type_read(rlread);
|
|
dev_type_write(rlwrite);
|
|
dev_type_ioctl(rlioctl);
|
|
dev_type_strategy(rlstrategy);
|
|
dev_type_dump(rldump);
|
|
dev_type_size(rlsize);
|
|
|
|
const struct bdevsw rl_bdevsw = {
|
|
rlopen, rlclose, rlstrategy, rlioctl, rldump, rlsize, D_DISK
|
|
};
|
|
|
|
const struct cdevsw rl_cdevsw = {
|
|
rlopen, rlclose, rlread, rlwrite, rlioctl,
|
|
nostop, notty, nopoll, nommap, nokqfilter, D_DISK
|
|
};
|
|
|
|
#define MAXRLXFER (RL_BPS * RL_SPT)
|
|
|
|
#define RL_WREG(reg, val) \
|
|
bus_space_write_2(sc->sc_iot, sc->sc_ioh, (reg), (val))
|
|
#define RL_RREG(reg) \
|
|
bus_space_read_2(sc->sc_iot, sc->sc_ioh, (reg))
|
|
|
|
static char *rlstates[] = {
|
|
"drive not loaded",
|
|
"drive spinning up",
|
|
"drive brushes out",
|
|
"drive loading heads",
|
|
"drive seeking",
|
|
"drive ready",
|
|
"drive unloading heads",
|
|
"drive spun down",
|
|
};
|
|
|
|
static char *
|
|
rlstate(struct rlc_softc *sc, int unit)
|
|
{
|
|
int i = 0;
|
|
|
|
do {
|
|
RL_WREG(RL_DA, RLDA_GS);
|
|
RL_WREG(RL_CS, RLCS_GS|(unit << RLCS_USHFT));
|
|
waitcrdy(sc);
|
|
} while (((RL_RREG(RL_CS) & RLCS_ERR) != 0) && i++ < 10);
|
|
if (i == 10)
|
|
return NULL;
|
|
i = RL_RREG(RL_MP) & RLMP_STATUS;
|
|
return rlstates[i];
|
|
}
|
|
|
|
void
|
|
waitcrdy(struct rlc_softc *sc)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 1000; i++) {
|
|
DELAY(10000);
|
|
if (RL_RREG(RL_CS) & RLCS_CRDY)
|
|
return;
|
|
}
|
|
printf("%s: never got ready\n", sc->sc_dev.dv_xname); /* ?panic? */
|
|
}
|
|
|
|
int
|
|
rlcprint(void *aux, const char *name)
|
|
{
|
|
struct rlc_attach_args *ra = aux;
|
|
|
|
if (name)
|
|
printf("RL0%d at %s", ra->type & RLMP_DT ? '2' : '1', name);
|
|
printf(" drive %d", ra->hwid);
|
|
return UNCONF;
|
|
}
|
|
|
|
/*
|
|
* Force the controller to interrupt.
|
|
*/
|
|
int
|
|
rlcmatch(struct device *parent, struct cfdata *cf, void *aux)
|
|
{
|
|
struct uba_attach_args *ua = aux;
|
|
struct rlc_softc ssc, *sc = &ssc;
|
|
int i;
|
|
|
|
sc->sc_iot = ua->ua_iot;
|
|
sc->sc_ioh = ua->ua_ioh;
|
|
/* Force interrupt by issuing a "Get Status" command */
|
|
RL_WREG(RL_DA, RLDA_GS);
|
|
RL_WREG(RL_CS, RLCS_GS|RLCS_IE);
|
|
|
|
for (i = 0; i < 100; i++) {
|
|
DELAY(100000);
|
|
if (RL_RREG(RL_CS) & RLCS_CRDY)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
rlcattach(struct device *parent, struct device *self, void *aux)
|
|
{
|
|
struct rlc_softc *sc = (struct rlc_softc *)self;
|
|
struct uba_attach_args *ua = aux;
|
|
struct rlc_attach_args ra;
|
|
int i, error;
|
|
|
|
sc->sc_iot = ua->ua_iot;
|
|
sc->sc_ioh = ua->ua_ioh;
|
|
sc->sc_dmat = ua->ua_dmat;
|
|
uba_intr_establish(ua->ua_icookie, ua->ua_cvec,
|
|
rlcintr, sc, &sc->sc_intrcnt);
|
|
evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, ua->ua_evcnt,
|
|
sc->sc_dev.dv_xname, "intr");
|
|
uba_reset_establish(rlcreset, self);
|
|
|
|
printf("\n");
|
|
|
|
/*
|
|
* The RL11 can only have one transfer going at a time,
|
|
* and max transfer size is one track, so only one dmamap
|
|
* is needed.
|
|
*/
|
|
error = bus_dmamap_create(sc->sc_dmat, MAXRLXFER, 1, MAXRLXFER, 0,
|
|
BUS_DMA_ALLOCNOW, &sc->sc_dmam);
|
|
if (error) {
|
|
printf(": Failed to allocate DMA map, error %d\n", error);
|
|
return;
|
|
}
|
|
bufq_alloc(&sc->sc_q, BUFQ_DISKSORT|BUFQ_SORT_CYLINDER);
|
|
for (i = 0; i < RL_MAXDPC; i++) {
|
|
waitcrdy(sc);
|
|
RL_WREG(RL_DA, RLDA_GS|RLDA_RST);
|
|
RL_WREG(RL_CS, RLCS_GS|(i << RLCS_USHFT));
|
|
waitcrdy(sc);
|
|
ra.type = RL_RREG(RL_MP);
|
|
ra.hwid = i;
|
|
if ((RL_RREG(RL_CS) & RLCS_ERR) == 0)
|
|
config_found(&sc->sc_dev, &ra, rlcprint);
|
|
}
|
|
}
|
|
|
|
int
|
|
rlmatch(struct device *parent, struct cfdata *cf, void *aux)
|
|
{
|
|
struct rlc_attach_args *ra = aux;
|
|
|
|
if (cf->cf_loc[RLCCF_DRIVE] != RLCCF_DRIVE_DEFAULT &&
|
|
cf->cf_loc[RLCCF_DRIVE] != ra->hwid)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
rlattach(struct device *parent, struct device *self, void *aux)
|
|
{
|
|
struct rl_softc *rc = (struct rl_softc *)self;
|
|
struct rlc_attach_args *ra = aux;
|
|
struct disklabel *dl;
|
|
|
|
rc->rc_hwid = ra->hwid;
|
|
rc->rc_disk.dk_name = rc->rc_dev.dv_xname;
|
|
disk_attach(&rc->rc_disk);
|
|
dl = rc->rc_disk.dk_label;
|
|
dl->d_npartitions = 3;
|
|
strcpy(dl->d_typename, "RL01");
|
|
if (ra->type & RLMP_DT)
|
|
dl->d_typename[3] = '2';
|
|
dl->d_secsize = DEV_BSIZE; /* XXX - wrong, but OK for now */
|
|
dl->d_nsectors = RL_SPT/2;
|
|
dl->d_ntracks = RL_SPD;
|
|
dl->d_ncylinders = ra->type & RLMP_DT ? RL_TPS02 : RL_TPS01;
|
|
dl->d_secpercyl = dl->d_nsectors * dl->d_ntracks;
|
|
dl->d_secperunit = dl->d_ncylinders * dl->d_secpercyl;
|
|
dl->d_partitions[0].p_size = dl->d_partitions[2].p_size =
|
|
dl->d_secperunit;
|
|
dl->d_partitions[0].p_offset = dl->d_partitions[2].p_offset = 0;
|
|
dl->d_interleave = dl->d_headswitch = 1;
|
|
dl->d_bbsize = BBSIZE;
|
|
dl->d_sbsize = SBSIZE;
|
|
dl->d_rpm = 2400;
|
|
dl->d_type = DTYPE_DEC;
|
|
printf(": %s, %s\n", dl->d_typename,
|
|
rlstate((struct rlc_softc *)parent, ra->hwid));
|
|
}
|
|
|
|
int
|
|
rlopen(dev_t dev, int flag, int fmt, struct proc *p)
|
|
{
|
|
int part, unit, mask;
|
|
struct disklabel *dl;
|
|
struct rlc_softc *sc;
|
|
struct rl_softc *rc;
|
|
char *msg;
|
|
|
|
/*
|
|
* Make sure this is a reasonable open request.
|
|
*/
|
|
unit = DISKUNIT(dev);
|
|
if (unit >= rl_cd.cd_ndevs)
|
|
return ENXIO;
|
|
rc = rl_cd.cd_devs[unit];
|
|
if (rc == 0)
|
|
return ENXIO;
|
|
|
|
sc = (struct rlc_softc *)rc->rc_dev.dv_parent;
|
|
/* Check that the disk actually is useable */
|
|
msg = rlstate(sc, rc->rc_hwid);
|
|
if (msg == NULL || msg == rlstates[RLMP_UNLOAD] ||
|
|
msg == rlstates[RLMP_SPUNDOWN])
|
|
return ENXIO;
|
|
/*
|
|
* If this is the first open; read in where on the disk we are.
|
|
*/
|
|
dl = rc->rc_disk.dk_label;
|
|
if (rc->rc_state == DK_CLOSED) {
|
|
u_int16_t mp;
|
|
int maj;
|
|
RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
|
|
waitcrdy(sc);
|
|
mp = RL_RREG(RL_MP);
|
|
rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
|
|
rc->rc_cyl = (mp >> 7) & 0777;
|
|
rc->rc_state = DK_OPEN;
|
|
/* Get disk label */
|
|
printf("%s: ", rc->rc_dev.dv_xname);
|
|
maj = cdevsw_lookup_major(&rl_cdevsw);
|
|
if ((msg = readdisklabel(MAKEDISKDEV(maj,
|
|
rc->rc_dev.dv_unit, RAW_PART), rlstrategy, dl, NULL)))
|
|
printf("%s: ", msg);
|
|
printf("size %d sectors\n", dl->d_secperunit);
|
|
}
|
|
part = DISKPART(dev);
|
|
if (part >= dl->d_npartitions)
|
|
return ENXIO;
|
|
|
|
mask = 1 << part;
|
|
switch (fmt) {
|
|
case S_IFCHR:
|
|
rc->rc_disk.dk_copenmask |= mask;
|
|
break;
|
|
case S_IFBLK:
|
|
rc->rc_disk.dk_bopenmask |= mask;
|
|
break;
|
|
}
|
|
rc->rc_disk.dk_openmask |= mask;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rlclose(dev_t dev, int flag, int fmt, struct proc *p)
|
|
{
|
|
int unit = DISKUNIT(dev);
|
|
struct rl_softc *rc = rl_cd.cd_devs[unit];
|
|
int mask = (1 << DISKPART(dev));
|
|
|
|
switch (fmt) {
|
|
case S_IFCHR:
|
|
rc->rc_disk.dk_copenmask &= ~mask;
|
|
break;
|
|
case S_IFBLK:
|
|
rc->rc_disk.dk_bopenmask &= ~mask;
|
|
break;
|
|
}
|
|
rc->rc_disk.dk_openmask =
|
|
rc->rc_disk.dk_copenmask | rc->rc_disk.dk_bopenmask;
|
|
|
|
if (rc->rc_disk.dk_openmask == 0)
|
|
rc->rc_state = DK_CLOSED; /* May change pack */
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
rlstrategy(struct buf *bp)
|
|
{
|
|
struct disklabel *lp;
|
|
struct rlc_softc *sc;
|
|
struct rl_softc *rc;
|
|
int unit, s, err;
|
|
/*
|
|
* Make sure this is a reasonable drive to use.
|
|
*/
|
|
unit = DISKUNIT(bp->b_dev);
|
|
if (unit > rl_cd.cd_ndevs || (rc = rl_cd.cd_devs[unit]) == NULL) {
|
|
bp->b_error = ENXIO;
|
|
bp->b_flags |= B_ERROR;
|
|
goto done;
|
|
}
|
|
if (rc->rc_state != DK_OPEN) /* How did we end up here at all? */
|
|
panic("rlstrategy: state impossible");
|
|
|
|
lp = rc->rc_disk.dk_label;
|
|
if ((err = bounds_check_with_label(bp, lp, 1)) <= 0)
|
|
goto done;
|
|
|
|
if (bp->b_bcount == 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;
|
|
sc = (struct rlc_softc *)rc->rc_dev.dv_parent;
|
|
|
|
s = splbio();
|
|
BUFQ_PUT(&sc->sc_q, bp);
|
|
rlcstart(sc, 0);
|
|
splx(s);
|
|
return;
|
|
|
|
done: biodone(bp);
|
|
}
|
|
|
|
int
|
|
rlioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
|
|
{
|
|
struct rl_softc *rc = rl_cd.cd_devs[DISKUNIT(dev)];
|
|
struct disklabel *lp = rc->rc_disk.dk_label;
|
|
int err = 0;
|
|
#ifdef __HAVE_OLD_DISKLABEL
|
|
struct disklabel newlabel;
|
|
#endif
|
|
|
|
switch (cmd) {
|
|
case DIOCGDINFO:
|
|
bcopy(lp, addr, sizeof (struct disklabel));
|
|
break;
|
|
|
|
#ifdef __HAVE_OLD_DISKLABEL
|
|
case ODIOCGDINFO:
|
|
newlabel = *lp;
|
|
if (newlabel.d_npartitions > OLDMAXPARTITIONS)
|
|
return ENOTTY;
|
|
bcopy(&newlabel, addr, sizeof (struct olddisklabel));
|
|
break;
|
|
#endif
|
|
|
|
case DIOCGPART:
|
|
((struct partinfo *)addr)->disklab = lp;
|
|
((struct partinfo *)addr)->part =
|
|
&lp->d_partitions[DISKPART(dev)];
|
|
break;
|
|
|
|
case DIOCSDINFO:
|
|
case DIOCWDINFO:
|
|
#ifdef __HAVE_OLD_DISKLABEL
|
|
case ODIOCWDINFO:
|
|
case ODIOCSDINFO:
|
|
#endif
|
|
{
|
|
struct disklabel *tp;
|
|
|
|
#ifdef __HAVE_OLD_DISKLABEL
|
|
if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) {
|
|
memset(&newlabel, 0, sizeof newlabel);
|
|
memcpy(&newlabel, addr, sizeof (struct olddisklabel));
|
|
tp = &newlabel;
|
|
} else
|
|
#endif
|
|
tp = (struct disklabel *)addr;
|
|
|
|
if ((flag & FWRITE) == 0)
|
|
err = EBADF;
|
|
else
|
|
err = ((
|
|
#ifdef __HAVE_OLD_DISKLABEL
|
|
cmd == ODIOCSDINFO ||
|
|
#endif
|
|
cmd == DIOCSDINFO) ?
|
|
setdisklabel(lp, tp, 0, 0) :
|
|
writedisklabel(dev, rlstrategy, lp, 0));
|
|
break;
|
|
}
|
|
|
|
case DIOCWLABEL:
|
|
if ((flag & FWRITE) == 0)
|
|
err = EBADF;
|
|
break;
|
|
|
|
default:
|
|
err = ENOTTY;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int
|
|
rlsize(dev_t dev)
|
|
{
|
|
struct disklabel *dl;
|
|
struct rl_softc *rc;
|
|
int size, unit = DISKUNIT(dev);
|
|
|
|
if ((unit >= rl_cd.cd_ndevs) || ((rc = rl_cd.cd_devs[unit]) == 0))
|
|
return -1;
|
|
dl = rc->rc_disk.dk_label;
|
|
size = dl->d_partitions[DISKPART(dev)].p_size *
|
|
(dl->d_secsize / DEV_BSIZE);
|
|
return size;
|
|
}
|
|
|
|
int
|
|
rldump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
|
|
{
|
|
/* Not likely... */
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rlread(dev_t dev, struct uio *uio, int ioflag)
|
|
{
|
|
return (physio(rlstrategy, NULL, dev, B_READ, minphys, uio));
|
|
}
|
|
|
|
int
|
|
rlwrite(dev_t dev, struct uio *uio, int ioflag)
|
|
{
|
|
return (physio(rlstrategy, NULL, dev, B_WRITE, minphys, uio));
|
|
}
|
|
|
|
static char *rlerr[] = {
|
|
"no",
|
|
"operation incomplete",
|
|
"read data CRC",
|
|
"header CRC",
|
|
"data late",
|
|
"header not found",
|
|
"",
|
|
"",
|
|
"non-existent memory",
|
|
"memory parity error",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
};
|
|
|
|
void
|
|
rlcintr(void *arg)
|
|
{
|
|
struct rlc_softc *sc = arg;
|
|
struct buf *bp;
|
|
u_int16_t cs;
|
|
|
|
bp = sc->sc_active;
|
|
if (bp == 0) {
|
|
printf("%s: strange interrupt\n", sc->sc_dev.dv_xname);
|
|
return;
|
|
}
|
|
bus_dmamap_unload(sc->sc_dmat, sc->sc_dmam);
|
|
sc->sc_active = 0;
|
|
cs = RL_RREG(RL_CS);
|
|
if (cs & RLCS_ERR) {
|
|
int error = (cs & RLCS_ERRMSK) >> 10;
|
|
|
|
printf("%s: %s\n", sc->sc_dev.dv_xname, rlerr[error]);
|
|
bp->b_flags |= B_ERROR;
|
|
bp->b_error = EIO;
|
|
bp->b_resid = bp->b_bcount;
|
|
sc->sc_bytecnt = 0;
|
|
}
|
|
if (sc->sc_bytecnt == 0) /* Finished transfer */
|
|
biodone(bp);
|
|
rlcstart(sc, sc->sc_bytecnt ? bp : 0);
|
|
}
|
|
|
|
/*
|
|
* Start routine. First position the disk to the given position,
|
|
* then start reading/writing. An optimization would be to be able
|
|
* to handle overlapping seeks between disks.
|
|
*/
|
|
void
|
|
rlcstart(struct rlc_softc *sc, struct buf *ob)
|
|
{
|
|
struct disklabel *lp;
|
|
struct rl_softc *rc;
|
|
struct buf *bp;
|
|
int bn, cn, sn, tn, blks, err;
|
|
|
|
if (sc->sc_active)
|
|
return; /* Already doing something */
|
|
|
|
if (ob == 0) {
|
|
bp = BUFQ_GET(&sc->sc_q);
|
|
if (bp == NULL)
|
|
return; /* Nothing to do */
|
|
sc->sc_bufaddr = bp->b_data;
|
|
sc->sc_diskblk = bp->b_rawblkno;
|
|
sc->sc_bytecnt = bp->b_bcount;
|
|
bp->b_resid = 0;
|
|
} else
|
|
bp = ob;
|
|
sc->sc_active = bp;
|
|
|
|
rc = rl_cd.cd_devs[DISKUNIT(bp->b_dev)];
|
|
bn = sc->sc_diskblk;
|
|
lp = rc->rc_disk.dk_label;
|
|
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;
|
|
|
|
/*
|
|
* Check if we have to position disk first.
|
|
*/
|
|
if (rc->rc_cyl != cn || rc->rc_head != tn) {
|
|
u_int16_t da = RLDA_SEEK;
|
|
if (cn > rc->rc_cyl)
|
|
da |= ((cn - rc->rc_cyl) << RLDA_CYLSHFT) | RLDA_DIR;
|
|
else
|
|
da |= ((rc->rc_cyl - cn) << RLDA_CYLSHFT);
|
|
if (tn)
|
|
da |= RLDA_HSSEEK;
|
|
waitcrdy(sc);
|
|
RL_WREG(RL_DA, da);
|
|
RL_WREG(RL_CS, RLCS_SEEK | (rc->rc_hwid << RLCS_USHFT));
|
|
waitcrdy(sc);
|
|
rc->rc_cyl = cn;
|
|
rc->rc_head = tn;
|
|
}
|
|
RL_WREG(RL_DA, (cn << RLDA_CYLSHFT) | (tn ? RLDA_HSRW : 0) | (sn << 1));
|
|
blks = sc->sc_bytecnt/DEV_BSIZE;
|
|
|
|
if (sn + blks > RL_SPT/2)
|
|
blks = RL_SPT/2 - sn;
|
|
RL_WREG(RL_MP, -(blks*DEV_BSIZE)/2);
|
|
err = bus_dmamap_load(sc->sc_dmat, sc->sc_dmam, sc->sc_bufaddr,
|
|
(blks*DEV_BSIZE), (bp->b_flags & B_PHYS ? bp->b_proc : 0),
|
|
BUS_DMA_NOWAIT);
|
|
if (err)
|
|
panic("%s: bus_dmamap_load failed: %d",
|
|
sc->sc_dev.dv_xname, err);
|
|
RL_WREG(RL_BA, (sc->sc_dmam->dm_segs[0].ds_addr & 0xffff));
|
|
|
|
/* Count up vars */
|
|
sc->sc_bufaddr += (blks*DEV_BSIZE);
|
|
sc->sc_diskblk += blks;
|
|
sc->sc_bytecnt -= (blks*DEV_BSIZE);
|
|
|
|
if (bp->b_flags & B_READ)
|
|
RL_WREG(RL_CS, RLCS_IE|RLCS_RD|(rc->rc_hwid << RLCS_USHFT));
|
|
else
|
|
RL_WREG(RL_CS, RLCS_IE|RLCS_WD|(rc->rc_hwid << RLCS_USHFT));
|
|
}
|
|
|
|
/*
|
|
* Called once per controller when an ubareset occurs.
|
|
* Retracts all disks and restarts active transfers.
|
|
*/
|
|
void
|
|
rlcreset(struct device *dev)
|
|
{
|
|
struct rlc_softc *sc = (struct rlc_softc *)dev;
|
|
struct rl_softc *rc;
|
|
int i;
|
|
u_int16_t mp;
|
|
|
|
for (i = 0; i < rl_cd.cd_ndevs; i++) {
|
|
if ((rc = rl_cd.cd_devs[i]) == NULL)
|
|
continue;
|
|
if (rc->rc_state != DK_OPEN)
|
|
continue;
|
|
|
|
printf(" %s", rc->rc_dev.dv_xname);
|
|
RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
|
|
waitcrdy(sc);
|
|
mp = RL_RREG(RL_MP);
|
|
rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
|
|
rc->rc_cyl = (mp >> 7) & 0777;
|
|
}
|
|
if (sc->sc_active == 0)
|
|
return;
|
|
|
|
BUFQ_PUT(&sc->sc_q, sc->sc_active);
|
|
sc->sc_active = 0;
|
|
rlcstart(sc, 0);
|
|
}
|