Home | History | Annotate | Line # | Download | only in qbus
rl.c revision 1.39
      1 /*	$NetBSD: rl.c,v 1.39 2008/06/11 17:27:59 drochner Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2000 Ludd, University of Lule}, Sweden. All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. All advertising materials mentioning features or use of this software
     15  *    must display the following acknowledgement:
     16  *      This product includes software developed at Ludd, University of
     17  *      Lule}, Sweden and its contributors.
     18  * 4. The name of the author may not be used to endorse or promote products
     19  *    derived from this software without specific prior written permission
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 /*
     34  * RL11/RLV11/RLV12 disk controller driver and
     35  * RL01/RL02 disk device driver.
     36  *
     37  * TODO:
     38  *	Handle disk errors more gracefully
     39  *	Do overlapping seeks on multiple drives
     40  *
     41  * Implementation comments:
     42  *
     43  */
     44 
     45 #include <sys/cdefs.h>
     46 __KERNEL_RCSID(0, "$NetBSD: rl.c,v 1.39 2008/06/11 17:27:59 drochner Exp $");
     47 
     48 #include <sys/param.h>
     49 #include <sys/device.h>
     50 #include <sys/systm.h>
     51 #include <sys/conf.h>
     52 #include <sys/disk.h>
     53 #include <sys/disklabel.h>
     54 #include <sys/buf.h>
     55 #include <sys/bufq.h>
     56 #include <sys/stat.h>
     57 #include <sys/dkio.h>
     58 #include <sys/fcntl.h>
     59 #include <sys/event.h>
     60 
     61 #include <ufs/ufs/dinode.h>
     62 #include <ufs/ffs/fs.h>
     63 
     64 #include <sys/bus.h>
     65 
     66 #include <dev/qbus/ubavar.h>
     67 #include <dev/qbus/rlreg.h>
     68 #include <dev/qbus/rlvar.h>
     69 
     70 #include "ioconf.h"
     71 #include "locators.h"
     72 
     73 static	int rlcmatch(device_t, cfdata_t, void *);
     74 static	void rlcattach(device_t, device_t, void *);
     75 static	int rlcprint(void *, const char *);
     76 static	void rlcintr(void *);
     77 static	int rlmatch(device_t, cfdata_t, void *);
     78 static	void rlattach(device_t, device_t, void *);
     79 static	void rlcstart(struct rlc_softc *, struct buf *);
     80 static	void waitcrdy(struct rlc_softc *);
     81 static	void rlcreset(device_t);
     82 
     83 CFATTACH_DECL_NEW(rlc, sizeof(struct rlc_softc),
     84     rlcmatch, rlcattach, NULL, NULL);
     85 
     86 CFATTACH_DECL_NEW(rl, sizeof(struct rl_softc),
     87     rlmatch, rlattach, NULL, NULL);
     88 
     89 static dev_type_open(rlopen);
     90 static dev_type_close(rlclose);
     91 static dev_type_read(rlread);
     92 static dev_type_write(rlwrite);
     93 static dev_type_ioctl(rlioctl);
     94 static dev_type_strategy(rlstrategy);
     95 static dev_type_dump(rldump);
     96 static dev_type_size(rlpsize);
     97 
     98 const struct bdevsw rl_bdevsw = {
     99 	rlopen, rlclose, rlstrategy, rlioctl, rldump, rlpsize, D_DISK
    100 };
    101 
    102 const struct cdevsw rl_cdevsw = {
    103 	rlopen, rlclose, rlread, rlwrite, rlioctl,
    104 	nostop, notty, nopoll, nommap, nokqfilter, D_DISK
    105 };
    106 
    107 #define	MAXRLXFER (RL_BPS * RL_SPT)
    108 
    109 #define	RL_WREG(reg, val) \
    110 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, (reg), (val))
    111 #define RL_RREG(reg) \
    112 	bus_space_read_2(sc->sc_iot, sc->sc_ioh, (reg))
    113 
    114 static const char * const rlstates[] = {
    115 	"drive not loaded",
    116 	"drive spinning up",
    117 	"drive brushes out",
    118 	"drive loading heads",
    119 	"drive seeking",
    120 	"drive ready",
    121 	"drive unloading heads",
    122 	"drive spun down",
    123 };
    124 
    125 static const struct dkdriver rldkdriver = {
    126 	rlstrategy, minphys
    127 };
    128 
    129 static const char *
    130 rlstate(struct rlc_softc *sc, int unit)
    131 {
    132 	int i = 0;
    133 
    134 	do {
    135 		RL_WREG(RL_DA, RLDA_GS);
    136 		RL_WREG(RL_CS, RLCS_GS|(unit << RLCS_USHFT));
    137 		waitcrdy(sc);
    138 	} while (((RL_RREG(RL_CS) & RLCS_ERR) != 0) && i++ < 10);
    139 	if (i == 10)
    140 		return NULL;
    141 	i = RL_RREG(RL_MP) & RLMP_STATUS;
    142 	return rlstates[i];
    143 }
    144 
    145 void
    146 waitcrdy(struct rlc_softc *sc)
    147 {
    148 	int i;
    149 
    150 	for (i = 0; i < 1000; i++) {
    151 		DELAY(10000);
    152 		if (RL_RREG(RL_CS) & RLCS_CRDY)
    153 			return;
    154 	}
    155 	aprint_error_dev(sc->sc_dev, "never got ready\n"); /* ?panic? */
    156 }
    157 
    158 int
    159 rlcprint(void *aux, const char *name)
    160 {
    161 	struct rlc_attach_args *ra = aux;
    162 
    163 	if (name)
    164 		aprint_normal("RL0%d at %s",
    165 		    ra->type & RLMP_DT ? '2' : '1', name);
    166 	aprint_normal(" drive %d", ra->hwid);
    167 	return UNCONF;
    168 }
    169 
    170 /*
    171  * Force the controller to interrupt.
    172  */
    173 int
    174 rlcmatch(device_t parent, cfdata_t cf, void *aux)
    175 {
    176 	struct uba_attach_args *ua = aux;
    177 	struct rlc_softc ssc, *sc = &ssc;
    178 	int i;
    179 
    180 	sc->sc_iot = ua->ua_iot;
    181 	sc->sc_ioh = ua->ua_ioh;
    182 	/* Force interrupt by issuing a "Get Status" command */
    183 	RL_WREG(RL_DA, RLDA_GS);
    184 	RL_WREG(RL_CS, RLCS_GS|RLCS_IE);
    185 
    186 	for (i = 0; i < 100; i++) {
    187 		DELAY(100000);
    188 		if (RL_RREG(RL_CS) & RLCS_CRDY)
    189 			return 1;
    190 	}
    191 	return 0;
    192 }
    193 
    194 void
    195 rlcattach(device_t parent, device_t self, void *aux)
    196 {
    197 	struct rlc_softc *sc = device_private(self);
    198 	struct uba_attach_args *ua = aux;
    199 	struct rlc_attach_args ra;
    200 	int i, error;
    201 
    202 	sc->sc_dev = self;
    203 	sc->sc_uh = device_private(parent);
    204 	sc->sc_iot = ua->ua_iot;
    205 	sc->sc_ioh = ua->ua_ioh;
    206 	sc->sc_dmat = ua->ua_dmat;
    207 	uba_intr_establish(ua->ua_icookie, ua->ua_cvec,
    208 		rlcintr, sc, &sc->sc_intrcnt);
    209 	evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, ua->ua_evcnt,
    210 		device_xname(sc->sc_dev), "intr");
    211 	uba_reset_establish(rlcreset, self);
    212 
    213 	printf("\n");
    214 
    215 	/*
    216 	 * The RL11 can only have one transfer going at a time,
    217 	 * and max transfer size is one track, so only one dmamap
    218 	 * is needed.
    219 	 */
    220 	error = bus_dmamap_create(sc->sc_dmat, MAXRLXFER, 1, MAXRLXFER, 0,
    221 	    BUS_DMA_ALLOCNOW, &sc->sc_dmam);
    222 	if (error) {
    223 		aprint_error(": Failed to allocate DMA map, error %d\n", error);
    224 		return;
    225 	}
    226 	bufq_alloc(&sc->sc_q, "disksort", BUFQ_SORT_CYLINDER);
    227 	for (i = 0; i < RL_MAXDPC; i++) {
    228 		waitcrdy(sc);
    229 		RL_WREG(RL_DA, RLDA_GS|RLDA_RST);
    230 		RL_WREG(RL_CS, RLCS_GS|(i << RLCS_USHFT));
    231 		waitcrdy(sc);
    232 		ra.type = RL_RREG(RL_MP);
    233 		ra.hwid = i;
    234 		if ((RL_RREG(RL_CS) & RLCS_ERR) == 0)
    235 			config_found(sc->sc_dev, &ra, rlcprint);
    236 	}
    237 }
    238 
    239 int
    240 rlmatch(device_t parent, cfdata_t cf, void *aux)
    241 {
    242 	struct rlc_attach_args *ra = aux;
    243 
    244 	if (cf->cf_loc[RLCCF_DRIVE] != RLCCF_DRIVE_DEFAULT &&
    245 	    cf->cf_loc[RLCCF_DRIVE] != ra->hwid)
    246 		return 0;
    247 	return 1;
    248 }
    249 
    250 void
    251 rlattach(device_t parent, device_t self, void *aux)
    252 {
    253 	struct rl_softc *rc = device_private(self);
    254 	struct rlc_attach_args *ra = aux;
    255 	struct disklabel *dl;
    256 
    257 	rc->rc_dev = self;
    258 	rc->rc_rlc = device_private(parent);
    259 	rc->rc_hwid = ra->hwid;
    260 	disk_init(&rc->rc_disk, device_xname(rc->rc_dev), &rldkdriver);
    261 	disk_attach(&rc->rc_disk);
    262 	dl = rc->rc_disk.dk_label;
    263 	dl->d_npartitions = 3;
    264 	strcpy(dl->d_typename, "RL01");
    265 	if (ra->type & RLMP_DT)
    266 		dl->d_typename[3] = '2';
    267 	dl->d_secsize = DEV_BSIZE; /* XXX - wrong, but OK for now */
    268 	dl->d_nsectors = RL_SPT/2;
    269 	dl->d_ntracks = RL_SPD;
    270 	dl->d_ncylinders = ra->type & RLMP_DT ? RL_TPS02 : RL_TPS01;
    271 	dl->d_secpercyl = dl->d_nsectors * dl->d_ntracks;
    272 	dl->d_secperunit = dl->d_ncylinders * dl->d_secpercyl;
    273 	dl->d_partitions[0].p_size = dl->d_partitions[2].p_size =
    274 	    dl->d_secperunit;
    275 	dl->d_partitions[0].p_offset = dl->d_partitions[2].p_offset = 0;
    276 	dl->d_interleave = dl->d_headswitch = 1;
    277 	dl->d_bbsize = BBSIZE;
    278 	dl->d_sbsize = SBLOCKSIZE;
    279 	dl->d_rpm = 2400;
    280 	dl->d_type = DTYPE_DEC;
    281 	printf(": %s, %s\n", dl->d_typename, rlstate(rc->rc_rlc, ra->hwid));
    282 
    283 	/*
    284 	 * XXX We should try to discovery wedges here, but
    285 	 * XXX that would mean loading up the pack and being
    286 	 * XXX able to do I/O.  Should use config_defer() here.
    287 	 */
    288 }
    289 
    290 int
    291 rlopen(dev_t dev, int flag, int fmt, struct lwp *l)
    292 {
    293 	struct rl_softc * const rc = device_lookup_private(&rl_cd, DISKUNIT(dev));
    294 	struct rlc_softc *sc;
    295 	int error, part, mask;
    296 	struct disklabel *dl;
    297 	const char *msg;
    298 
    299 	/*
    300 	 * Make sure this is a reasonable open request.
    301 	 */
    302 	if (rc == NULL)
    303 		return ENXIO;
    304 
    305 	sc = rc->rc_rlc;
    306 	part = DISKPART(dev);
    307 
    308 	mutex_enter(&rc->rc_disk.dk_openlock);
    309 
    310 	/*
    311 	 * If there are wedges, and this is not RAW_PART, then we
    312 	 * need to fail.
    313 	 */
    314 	if (rc->rc_disk.dk_nwedges != 0 && part != RAW_PART) {
    315 		error = EBUSY;
    316 		goto bad1;
    317 	}
    318 
    319 	/* Check that the disk actually is useable */
    320 	msg = rlstate(sc, rc->rc_hwid);
    321 	if (msg == NULL || msg == rlstates[RLMP_UNLOAD] ||
    322 	    msg == rlstates[RLMP_SPUNDOWN]) {
    323 		error = ENXIO;
    324 		goto bad1;
    325 	}
    326 	/*
    327 	 * If this is the first open; read in where on the disk we are.
    328 	 */
    329 	dl = rc->rc_disk.dk_label;
    330 	if (rc->rc_state == DK_CLOSED) {
    331 		u_int16_t mp;
    332 		int maj;
    333 		RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
    334 		waitcrdy(sc);
    335 		mp = RL_RREG(RL_MP);
    336 		rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
    337 		rc->rc_cyl = (mp >> 7) & 0777;
    338 		rc->rc_state = DK_OPEN;
    339 		/* Get disk label */
    340 		maj = cdevsw_lookup_major(&rl_cdevsw);
    341 		if ((msg = readdisklabel(MAKEDISKDEV(maj,
    342 		    device_unit(rc->rc_dev), RAW_PART), rlstrategy, dl, NULL)))
    343 			aprint_normal_dev(rc->rc_dev, "%s", msg);
    344 		aprint_normal_dev(rc->rc_dev, "size %d sectors\n",
    345 		    dl->d_secperunit);
    346 	}
    347 	if (part >= dl->d_npartitions) {
    348 		error = ENXIO;
    349 		goto bad1;
    350 	}
    351 
    352 	mask = 1 << part;
    353 	switch (fmt) {
    354 	case S_IFCHR:
    355 		rc->rc_disk.dk_copenmask |= mask;
    356 		break;
    357 	case S_IFBLK:
    358 		rc->rc_disk.dk_bopenmask |= mask;
    359 		break;
    360 	}
    361 	rc->rc_disk.dk_openmask |= mask;
    362 	error = 0;
    363  bad1:
    364 	mutex_exit(&rc->rc_disk.dk_openlock);
    365 	return (error);
    366 }
    367 
    368 int
    369 rlclose(dev_t dev, int flag, int fmt, struct lwp *l)
    370 {
    371 	int unit = DISKUNIT(dev);
    372 	struct rl_softc *rc = device_lookup_private(&rl_cd, unit);
    373 	int mask = (1 << DISKPART(dev));
    374 
    375 	mutex_enter(&rc->rc_disk.dk_openlock);
    376 
    377 	switch (fmt) {
    378 	case S_IFCHR:
    379 		rc->rc_disk.dk_copenmask &= ~mask;
    380 		break;
    381 	case S_IFBLK:
    382 		rc->rc_disk.dk_bopenmask &= ~mask;
    383 		break;
    384 	}
    385 	rc->rc_disk.dk_openmask =
    386 	    rc->rc_disk.dk_copenmask | rc->rc_disk.dk_bopenmask;
    387 
    388 	if (rc->rc_disk.dk_openmask == 0)
    389 		rc->rc_state = DK_CLOSED; /* May change pack */
    390 	mutex_exit(&rc->rc_disk.dk_openlock);
    391 	return 0;
    392 }
    393 
    394 void
    395 rlstrategy(struct buf *bp)
    396 {
    397 	struct rl_softc * const rc = device_lookup_private(&rl_cd, DISKUNIT(bp->b_dev));
    398 	struct disklabel *lp;
    399 	int s;
    400 
    401 	if (rc == NULL || rc->rc_state != DK_OPEN) /* How did we end up here at all? */
    402 		panic("rlstrategy: state impossible");
    403 
    404 	lp = rc->rc_disk.dk_label;
    405 	if (bounds_check_with_label(&rc->rc_disk, bp, 1) <= 0)
    406 		goto done;
    407 
    408 	if (bp->b_bcount == 0)
    409 		goto done;
    410 
    411 	bp->b_rawblkno =
    412 	    bp->b_blkno + lp->d_partitions[DISKPART(bp->b_dev)].p_offset;
    413 	bp->b_cylinder = bp->b_rawblkno / lp->d_secpercyl;
    414 
    415 	s = splbio();
    416 	BUFQ_PUT(rc->rc_rlc->sc_q, bp);
    417 	rlcstart(rc->rc_rlc, 0);
    418 	splx(s);
    419 	return;
    420 
    421 done:	biodone(bp);
    422 }
    423 
    424 int
    425 rlioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
    426 {
    427 	struct rl_softc *rc = device_lookup_private(&rl_cd, DISKUNIT(dev));
    428 	struct disklabel *lp = rc->rc_disk.dk_label;
    429 	int err = 0;
    430 #ifdef __HAVE_OLD_DISKLABEL
    431 	struct disklabel newlabel;
    432 #endif
    433 
    434 	switch (cmd) {
    435 	case DIOCGDINFO:
    436 		bcopy(lp, addr, sizeof (struct disklabel));
    437 		break;
    438 
    439 #ifdef __HAVE_OLD_DISKLABEL
    440 	case ODIOCGDINFO:
    441 		newlabel = *lp;
    442 		if (newlabel.d_npartitions > OLDMAXPARTITIONS)
    443 			return ENOTTY;
    444 		bcopy(&newlabel, addr, sizeof (struct olddisklabel));
    445 		break;
    446 #endif
    447 
    448 	case DIOCGPART:
    449 		((struct partinfo *)addr)->disklab = lp;
    450 		((struct partinfo *)addr)->part =
    451 		    &lp->d_partitions[DISKPART(dev)];
    452 		break;
    453 
    454 	case DIOCSDINFO:
    455 	case DIOCWDINFO:
    456 #ifdef __HAVE_OLD_DISKLABEL
    457 	case ODIOCWDINFO:
    458 	case ODIOCSDINFO:
    459 #endif
    460 	{
    461 		struct disklabel *tp;
    462 
    463 #ifdef __HAVE_OLD_DISKLABEL
    464 		if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) {
    465 			memset(&newlabel, 0, sizeof newlabel);
    466 			memcpy(&newlabel, addr, sizeof (struct olddisklabel));
    467 			tp = &newlabel;
    468 		} else
    469 #endif
    470 		tp = (struct disklabel *)addr;
    471 
    472 		if ((flag & FWRITE) == 0)
    473 			err = EBADF;
    474 		else {
    475 			mutex_enter(&rc->rc_disk.dk_openlock);
    476 			err = ((
    477 #ifdef __HAVE_OLD_DISKLABEL
    478 			       cmd == ODIOCSDINFO ||
    479 #endif
    480 			       cmd == DIOCSDINFO) ?
    481 			    setdisklabel(lp, tp, 0, 0) :
    482 			    writedisklabel(dev, rlstrategy, lp, 0));
    483 			mutex_exit(&rc->rc_disk.dk_openlock);
    484 		}
    485 		break;
    486 	}
    487 
    488 	case DIOCWLABEL:
    489 		if ((flag & FWRITE) == 0)
    490 			err = EBADF;
    491 		break;
    492 
    493 	case DIOCAWEDGE: {
    494 		struct dkwedge_info *dkw = (void *) addr;
    495 
    496 		if ((flag & FWRITE) == 0)
    497 			return (EBADF);
    498 
    499 		/* If the ioctl happens here, the parent is us. */
    500 		strcpy(dkw->dkw_parent, device_xname(rc->rc_dev));
    501 		return dkwedge_add(dkw);
    502 	}
    503 
    504 	case DIOCDWEDGE: {
    505 		struct dkwedge_info *dkw = (void *) addr;
    506 
    507 		if ((flag & FWRITE) == 0)
    508 			return (EBADF);
    509 
    510 		/* If the ioctl happens here, the parent is us. */
    511 		strcpy(dkw->dkw_parent, device_xname(rc->rc_dev));
    512 		return dkwedge_del(dkw);
    513 	}
    514 
    515 	case DIOCLWEDGES: {
    516 		struct dkwedge_list *dkwl = (void *) addr;
    517 
    518 		return dkwedge_list(&rc->rc_disk, dkwl, l);
    519 	}
    520 
    521 	default:
    522 		err = ENOTTY;
    523 		break;
    524 	}
    525 	return err;
    526 }
    527 
    528 int
    529 rlpsize(dev_t dev)
    530 {
    531 	struct rl_softc * const rc = device_lookup_private(&rl_cd, DISKUNIT(dev));
    532 	struct disklabel *dl;
    533 	int size;
    534 
    535 	if (rc == NULL)
    536 		return -1;
    537 	dl = rc->rc_disk.dk_label;
    538 	size = dl->d_partitions[DISKPART(dev)].p_size *
    539 	    (dl->d_secsize / DEV_BSIZE);
    540 	return size;
    541 }
    542 
    543 int
    544 rldump(dev_t dev, daddr_t blkno, void *va, size_t size)
    545 {
    546 	/* Not likely... */
    547 	return 0;
    548 }
    549 
    550 int
    551 rlread(dev_t dev, struct uio *uio, int ioflag)
    552 {
    553 	return (physio(rlstrategy, NULL, dev, B_READ, minphys, uio));
    554 }
    555 
    556 int
    557 rlwrite(dev_t dev, struct uio *uio, int ioflag)
    558 {
    559 	return (physio(rlstrategy, NULL, dev, B_WRITE, minphys, uio));
    560 }
    561 
    562 static const char * const rlerr[] = {
    563 	"no",
    564 	"operation incomplete",
    565 	"read data CRC",
    566 	"header CRC",
    567 	"data late",
    568 	"header not found",
    569 	"",
    570 	"",
    571 	"non-existent memory",
    572 	"memory parity error",
    573 	"",
    574 	"",
    575 	"",
    576 	"",
    577 	"",
    578 	"",
    579 };
    580 
    581 void
    582 rlcintr(void *arg)
    583 {
    584 	struct rlc_softc *sc = arg;
    585 	struct buf *bp;
    586 	u_int16_t cs;
    587 
    588 	bp = sc->sc_active;
    589 	if (bp == 0) {
    590 		aprint_error_dev(sc->sc_dev, "strange interrupt\n");
    591 		return;
    592 	}
    593 	bus_dmamap_unload(sc->sc_dmat, sc->sc_dmam);
    594 	sc->sc_active = 0;
    595 	cs = RL_RREG(RL_CS);
    596 	if (cs & RLCS_ERR) {
    597 		int error = (cs & RLCS_ERRMSK) >> 10;
    598 
    599 		aprint_error_dev(sc->sc_dev, "%s\n", rlerr[error]);
    600 		bp->b_error = EIO;
    601 		bp->b_resid = bp->b_bcount;
    602 		sc->sc_bytecnt = 0;
    603 	}
    604 	if (sc->sc_bytecnt == 0) /* Finished transfer */
    605 		biodone(bp);
    606 	rlcstart(sc, sc->sc_bytecnt ? bp : 0);
    607 }
    608 
    609 /*
    610  * Start routine. First position the disk to the given position,
    611  * then start reading/writing. An optimization would be to be able
    612  * to handle overlapping seeks between disks.
    613  */
    614 void
    615 rlcstart(struct rlc_softc *sc, struct buf *ob)
    616 {
    617 	struct disklabel *lp;
    618 	struct rl_softc *rc;
    619 	struct buf *bp;
    620 	int bn, cn, sn, tn, blks, err;
    621 
    622 	if (sc->sc_active)
    623 		return;	/* Already doing something */
    624 
    625 	if (ob == 0) {
    626 		bp = BUFQ_GET(sc->sc_q);
    627 		if (bp == NULL)
    628 			return;	/* Nothing to do */
    629 		sc->sc_bufaddr = bp->b_data;
    630 		sc->sc_diskblk = bp->b_rawblkno;
    631 		sc->sc_bytecnt = bp->b_bcount;
    632 		bp->b_resid = 0;
    633 	} else
    634 		bp = ob;
    635 	sc->sc_active = bp;
    636 
    637 	rc = device_lookup_private(&rl_cd, DISKUNIT(bp->b_dev));
    638 	bn = sc->sc_diskblk;
    639 	lp = rc->rc_disk.dk_label;
    640 	if (bn) {
    641 		cn = bn / lp->d_secpercyl;
    642 		sn = bn % lp->d_secpercyl;
    643 		tn = sn / lp->d_nsectors;
    644 		sn = sn % lp->d_nsectors;
    645 	} else
    646 		cn = sn = tn = 0;
    647 
    648 	/*
    649 	 * Check if we have to position disk first.
    650 	 */
    651 	if (rc->rc_cyl != cn || rc->rc_head != tn) {
    652 		u_int16_t da = RLDA_SEEK;
    653 		if (cn > rc->rc_cyl)
    654 			da |= ((cn - rc->rc_cyl) << RLDA_CYLSHFT) | RLDA_DIR;
    655 		else
    656 			da |= ((rc->rc_cyl - cn) << RLDA_CYLSHFT);
    657 		if (tn)
    658 			da |= RLDA_HSSEEK;
    659 		waitcrdy(sc);
    660 		RL_WREG(RL_DA, da);
    661 		RL_WREG(RL_CS, RLCS_SEEK | (rc->rc_hwid << RLCS_USHFT));
    662 		waitcrdy(sc);
    663 		rc->rc_cyl = cn;
    664 		rc->rc_head = tn;
    665 	}
    666 	RL_WREG(RL_DA, (cn << RLDA_CYLSHFT) | (tn ? RLDA_HSRW : 0) | (sn << 1));
    667 	blks = sc->sc_bytecnt/DEV_BSIZE;
    668 
    669 	if (sn + blks > RL_SPT/2)
    670 		blks = RL_SPT/2 - sn;
    671 	RL_WREG(RL_MP, -(blks*DEV_BSIZE)/2);
    672 	err = bus_dmamap_load(sc->sc_dmat, sc->sc_dmam, sc->sc_bufaddr,
    673 	    (blks*DEV_BSIZE), (bp->b_flags & B_PHYS ? bp->b_proc : 0),
    674 	    BUS_DMA_NOWAIT);
    675 	if (err)
    676 		panic("%s: bus_dmamap_load failed: %d",
    677 		    device_xname(sc->sc_dev), err);
    678 	RL_WREG(RL_BA, (sc->sc_dmam->dm_segs[0].ds_addr & 0xffff));
    679 
    680 	/* Count up vars */
    681 	sc->sc_bufaddr = (char *)sc->sc_bufaddr + (blks*DEV_BSIZE);
    682 	sc->sc_diskblk += blks;
    683 	sc->sc_bytecnt -= (blks*DEV_BSIZE);
    684 
    685 	if (bp->b_flags & B_READ)
    686 		RL_WREG(RL_CS, RLCS_IE|RLCS_RD|(rc->rc_hwid << RLCS_USHFT));
    687 	else
    688 		RL_WREG(RL_CS, RLCS_IE|RLCS_WD|(rc->rc_hwid << RLCS_USHFT));
    689 }
    690 
    691 /*
    692  * Called once per controller when an ubareset occurs.
    693  * Retracts all disks and restarts active transfers.
    694  */
    695 void
    696 rlcreset(device_t dev)
    697 {
    698 	struct rlc_softc *sc = device_private(dev);
    699 	struct rl_softc *rc;
    700 	int i;
    701 	u_int16_t mp;
    702 
    703 	for (i = 0; i < rl_cd.cd_ndevs; i++) {
    704 		if ((rc = device_lookup_private(&rl_cd, i)) == NULL)
    705 			continue;
    706 		if (rc->rc_state != DK_OPEN)
    707 			continue;
    708 		if (rc->rc_rlc != sc)
    709 			continue;
    710 
    711 		RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
    712 		waitcrdy(sc);
    713 		mp = RL_RREG(RL_MP);
    714 		rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
    715 		rc->rc_cyl = (mp >> 7) & 0777;
    716 	}
    717 	if (sc->sc_active == 0)
    718 		return;
    719 
    720 	BUFQ_PUT(sc->sc_q, sc->sc_active);
    721 	sc->sc_active = 0;
    722 	rlcstart(sc, 0);
    723 }
    724