rl.c revision 1.16 1 /* $NetBSD: rl.c,v 1.16 2002/10/02 16:52:30 thorpej 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.16 2002/10/02 16:52:30 thorpej 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/stat.h>
56 #include <sys/dkio.h>
57 #include <sys/fcntl.h>
58
59 #include <ufs/ufs/dinode.h>
60 #include <ufs/ffs/fs.h>
61
62 #include <machine/bus.h>
63
64 #include <dev/qbus/ubavar.h>
65 #include <dev/qbus/rlreg.h>
66 #include <dev/qbus/rlvar.h>
67
68 #include "ioconf.h"
69 #include "locators.h"
70
71 static int rlcmatch(struct device *, struct cfdata *, void *);
72 static void rlcattach(struct device *, struct device *, void *);
73 static int rlcprint(void *, const char *);
74 static void rlcintr(void *);
75 static int rlmatch(struct device *, struct cfdata *, void *);
76 static void rlattach(struct device *, struct device *, void *);
77 static void rlcstart(struct rlc_softc *, struct buf *);
78 static void waitcrdy(struct rlc_softc *);
79 static void rlcreset(struct device *);
80
81 CFATTACH_DECL(rlc, sizeof(struct rlc_softc),
82 rlcmatch, rlcattach, NULL, NULL);
83
84 CFATTACH_DECL(rl, sizeof(struct rl_softc),
85 rlmatch, rlattach, NULL, NULL);
86
87 dev_type_open(rlopen);
88 dev_type_close(rlclose);
89 dev_type_read(rlread);
90 dev_type_write(rlwrite);
91 dev_type_ioctl(rlioctl);
92 dev_type_strategy(rlstrategy);
93 dev_type_dump(rldump);
94 dev_type_size(rlsize);
95
96 const struct bdevsw rl_bdevsw = {
97 rlopen, rlclose, rlstrategy, rlioctl, rldump, rlsize, D_DISK
98 };
99
100 const struct cdevsw rl_cdevsw = {
101 rlopen, rlclose, rlread, rlwrite, rlioctl,
102 nostop, notty, nopoll, nommap, D_DISK
103 };
104
105 #define MAXRLXFER (RL_BPS * RL_SPT)
106
107 #define RL_WREG(reg, val) \
108 bus_space_write_2(sc->sc_iot, sc->sc_ioh, (reg), (val))
109 #define RL_RREG(reg) \
110 bus_space_read_2(sc->sc_iot, sc->sc_ioh, (reg))
111
112 static char *rlstates[] = {
113 "drive not loaded",
114 "drive spinning up",
115 "drive brushes out",
116 "drive loading heads",
117 "drive seeking",
118 "drive ready",
119 "drive unloading heads",
120 "drive spun down",
121 };
122
123 static char *
124 rlstate(struct rlc_softc *sc, int unit)
125 {
126 int i = 0;
127
128 do {
129 RL_WREG(RL_DA, RLDA_GS);
130 RL_WREG(RL_CS, RLCS_GS|(unit << RLCS_USHFT));
131 waitcrdy(sc);
132 } while (((RL_RREG(RL_CS) & RLCS_ERR) != 0) && i++ < 10);
133 if (i == 10)
134 return NULL;
135 i = RL_RREG(RL_MP) & RLMP_STATUS;
136 return rlstates[i];
137 }
138
139 void
140 waitcrdy(struct rlc_softc *sc)
141 {
142 int i;
143
144 for (i = 0; i < 1000; i++) {
145 DELAY(10000);
146 if (RL_RREG(RL_CS) & RLCS_CRDY)
147 return;
148 }
149 printf("%s: never got ready\n", sc->sc_dev.dv_xname); /* ?panic? */
150 }
151
152 int
153 rlcprint(void *aux, const char *name)
154 {
155 struct rlc_attach_args *ra = aux;
156
157 if (name)
158 printf("RL0%d at %s", ra->type & RLMP_DT ? '2' : '1', name);
159 printf(" drive %d", ra->hwid);
160 return UNCONF;
161 }
162
163 /*
164 * Force the controller to interrupt.
165 */
166 int
167 rlcmatch(struct device *parent, struct cfdata *cf, void *aux)
168 {
169 struct uba_attach_args *ua = aux;
170 struct rlc_softc ssc, *sc = &ssc;
171 int i;
172
173 sc->sc_iot = ua->ua_iot;
174 sc->sc_ioh = ua->ua_ioh;
175 /* Force interrupt by issuing a "Get Status" command */
176 RL_WREG(RL_DA, RLDA_GS);
177 RL_WREG(RL_CS, RLCS_GS|RLCS_IE);
178
179 for (i = 0; i < 100; i++) {
180 DELAY(100000);
181 if (RL_RREG(RL_CS) & RLCS_CRDY)
182 return 1;
183 }
184 return 0;
185 }
186
187 void
188 rlcattach(struct device *parent, struct device *self, void *aux)
189 {
190 struct rlc_softc *sc = (struct rlc_softc *)self;
191 struct uba_attach_args *ua = aux;
192 struct rlc_attach_args ra;
193 int i, error;
194
195 sc->sc_iot = ua->ua_iot;
196 sc->sc_ioh = ua->ua_ioh;
197 sc->sc_dmat = ua->ua_dmat;
198 uba_intr_establish(ua->ua_icookie, ua->ua_cvec,
199 rlcintr, sc, &sc->sc_intrcnt);
200 evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, ua->ua_evcnt,
201 sc->sc_dev.dv_xname, "intr");
202 uba_reset_establish(rlcreset, self);
203
204 printf("\n");
205
206 /*
207 * The RL11 can only have one transfer going at a time,
208 * and max transfer size is one track, so only one dmamap
209 * is needed.
210 */
211 error = bus_dmamap_create(sc->sc_dmat, MAXRLXFER, 1, MAXRLXFER, 0,
212 BUS_DMA_ALLOCNOW, &sc->sc_dmam);
213 if (error) {
214 printf(": Failed to allocate DMA map, error %d\n", error);
215 return;
216 }
217 bufq_alloc(&sc->sc_q, BUFQ_DISKSORT|BUFQ_SORT_CYLINDER);
218 for (i = 0; i < RL_MAXDPC; i++) {
219 waitcrdy(sc);
220 RL_WREG(RL_DA, RLDA_GS|RLDA_RST);
221 RL_WREG(RL_CS, RLCS_GS|(i << RLCS_USHFT));
222 waitcrdy(sc);
223 ra.type = RL_RREG(RL_MP);
224 ra.hwid = i;
225 if ((RL_RREG(RL_CS) & RLCS_ERR) == 0)
226 config_found(&sc->sc_dev, &ra, rlcprint);
227 }
228 }
229
230 int
231 rlmatch(struct device *parent, struct cfdata *cf, void *aux)
232 {
233 struct rlc_attach_args *ra = aux;
234
235 if (cf->cf_loc[RLCCF_DRIVE] != RLCCF_DRIVE_DEFAULT &&
236 cf->cf_loc[RLCCF_DRIVE] != ra->hwid)
237 return 0;
238 return 1;
239 }
240
241 void
242 rlattach(struct device *parent, struct device *self, void *aux)
243 {
244 struct rl_softc *rc = (struct rl_softc *)self;
245 struct rlc_attach_args *ra = aux;
246 struct disklabel *dl;
247
248 rc->rc_hwid = ra->hwid;
249 rc->rc_disk.dk_name = rc->rc_dev.dv_xname;
250 disk_attach(&rc->rc_disk);
251 dl = rc->rc_disk.dk_label;
252 dl->d_npartitions = 3;
253 strcpy(dl->d_typename, "RL01");
254 if (ra->type & RLMP_DT)
255 dl->d_typename[3] = '2';
256 dl->d_secsize = DEV_BSIZE; /* XXX - wrong, but OK for now */
257 dl->d_nsectors = RL_SPT/2;
258 dl->d_ntracks = RL_SPD;
259 dl->d_ncylinders = ra->type & RLMP_DT ? RL_TPS02 : RL_TPS01;
260 dl->d_secpercyl = dl->d_nsectors * dl->d_ntracks;
261 dl->d_secperunit = dl->d_ncylinders * dl->d_secpercyl;
262 dl->d_partitions[0].p_size = dl->d_partitions[2].p_size =
263 dl->d_secperunit;
264 dl->d_partitions[0].p_offset = dl->d_partitions[2].p_offset = 0;
265 dl->d_interleave = dl->d_headswitch = 1;
266 dl->d_bbsize = BBSIZE;
267 dl->d_sbsize = SBSIZE;
268 dl->d_rpm = 2400;
269 dl->d_type = DTYPE_DEC;
270 printf(": %s, %s\n", dl->d_typename,
271 rlstate((struct rlc_softc *)parent, ra->hwid));
272 }
273
274 int
275 rlopen(dev_t dev, int flag, int fmt, struct proc *p)
276 {
277 int part, unit, mask;
278 struct disklabel *dl;
279 struct rlc_softc *sc;
280 struct rl_softc *rc;
281 char *msg;
282
283 /*
284 * Make sure this is a reasonable open request.
285 */
286 unit = DISKUNIT(dev);
287 if (unit >= rl_cd.cd_ndevs)
288 return ENXIO;
289 rc = rl_cd.cd_devs[unit];
290 if (rc == 0)
291 return ENXIO;
292
293 sc = (struct rlc_softc *)rc->rc_dev.dv_parent;
294 /* Check that the disk actually is useable */
295 msg = rlstate(sc, rc->rc_hwid);
296 if (msg == NULL || msg == rlstates[RLMP_UNLOAD] ||
297 msg == rlstates[RLMP_SPUNDOWN])
298 return ENXIO;
299 /*
300 * If this is the first open; read in where on the disk we are.
301 */
302 dl = rc->rc_disk.dk_label;
303 if (rc->rc_state == DK_CLOSED) {
304 u_int16_t mp;
305 int maj;
306 RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
307 waitcrdy(sc);
308 mp = RL_RREG(RL_MP);
309 rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
310 rc->rc_cyl = (mp >> 7) & 0777;
311 rc->rc_state = DK_OPEN;
312 /* Get disk label */
313 printf("%s: ", rc->rc_dev.dv_xname);
314 maj = cdevsw_lookup_major(&rl_cdevsw);
315 if ((msg = readdisklabel(MAKEDISKDEV(maj,
316 rc->rc_dev.dv_unit, RAW_PART), rlstrategy, dl, NULL)))
317 printf("%s: ", msg);
318 printf("size %d sectors\n", dl->d_secperunit);
319 }
320 part = DISKPART(dev);
321 if (part >= dl->d_npartitions)
322 return ENXIO;
323
324 mask = 1 << part;
325 switch (fmt) {
326 case S_IFCHR:
327 rc->rc_disk.dk_copenmask |= mask;
328 break;
329 case S_IFBLK:
330 rc->rc_disk.dk_bopenmask |= mask;
331 break;
332 }
333 rc->rc_disk.dk_openmask |= mask;
334 return 0;
335 }
336
337 int
338 rlclose(dev_t dev, int flag, int fmt, struct proc *p)
339 {
340 int unit = DISKUNIT(dev);
341 struct rl_softc *rc = rl_cd.cd_devs[unit];
342 int mask = (1 << DISKPART(dev));
343
344 switch (fmt) {
345 case S_IFCHR:
346 rc->rc_disk.dk_copenmask &= ~mask;
347 break;
348 case S_IFBLK:
349 rc->rc_disk.dk_bopenmask &= ~mask;
350 break;
351 }
352 rc->rc_disk.dk_openmask =
353 rc->rc_disk.dk_copenmask | rc->rc_disk.dk_bopenmask;
354
355 if (rc->rc_disk.dk_openmask == 0)
356 rc->rc_state = DK_CLOSED; /* May change pack */
357 return 0;
358 }
359
360 void
361 rlstrategy(struct buf *bp)
362 {
363 struct disklabel *lp;
364 struct rlc_softc *sc;
365 struct rl_softc *rc;
366 int unit, s, err;
367 /*
368 * Make sure this is a reasonable drive to use.
369 */
370 unit = DISKUNIT(bp->b_dev);
371 if (unit > rl_cd.cd_ndevs || (rc = rl_cd.cd_devs[unit]) == NULL) {
372 bp->b_error = ENXIO;
373 bp->b_flags |= B_ERROR;
374 goto done;
375 }
376 if (rc->rc_state != DK_OPEN) /* How did we end up here at all? */
377 panic("rlstrategy: state impossible");
378
379 lp = rc->rc_disk.dk_label;
380 if ((err = bounds_check_with_label(bp, lp, 1)) <= 0)
381 goto done;
382
383 if (bp->b_bcount == 0)
384 goto done;
385
386 bp->b_rawblkno =
387 bp->b_blkno + lp->d_partitions[DISKPART(bp->b_dev)].p_offset;
388 bp->b_cylinder = bp->b_rawblkno / lp->d_secpercyl;
389 sc = (struct rlc_softc *)rc->rc_dev.dv_parent;
390
391 s = splbio();
392 BUFQ_PUT(&sc->sc_q, bp);
393 rlcstart(sc, 0);
394 splx(s);
395 return;
396
397 done: biodone(bp);
398 }
399
400 int
401 rlioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
402 {
403 struct rl_softc *rc = rl_cd.cd_devs[DISKUNIT(dev)];
404 struct disklabel *lp = rc->rc_disk.dk_label;
405 int err = 0;
406 #ifdef __HAVE_OLD_DISKLABEL
407 struct disklabel newlabel;
408 #endif
409
410 switch (cmd) {
411 case DIOCGDINFO:
412 bcopy(lp, addr, sizeof (struct disklabel));
413 break;
414
415 #ifdef __HAVE_OLD_DISKLABEL
416 case ODIOCGDINFO:
417 newlabel = *lp;
418 if (newlabel.d_npartitions > OLDMAXPARTITIONS)
419 return ENOTTY;
420 bcopy(&newlabel, addr, sizeof (struct olddisklabel));
421 break;
422 #endif
423
424 case DIOCGPART:
425 ((struct partinfo *)addr)->disklab = lp;
426 ((struct partinfo *)addr)->part =
427 &lp->d_partitions[DISKPART(dev)];
428 break;
429
430 case DIOCSDINFO:
431 case DIOCWDINFO:
432 #ifdef __HAVE_OLD_DISKLABEL
433 case ODIOCWDINFO:
434 case ODIOCSDINFO:
435 #endif
436 {
437 struct disklabel *tp;
438
439 #ifdef __HAVE_OLD_DISKLABEL
440 if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) {
441 memset(&newlabel, 0, sizeof newlabel);
442 memcpy(&newlabel, addr, sizeof (struct olddisklabel));
443 tp = &newlabel;
444 } else
445 #endif
446 tp = (struct disklabel *)addr;
447
448 if ((flag & FWRITE) == 0)
449 err = EBADF;
450 else
451 err = ((
452 #ifdef __HAVE_OLD_DISKLABEL
453 cmd == ODIOCSDINFO ||
454 #endif
455 cmd == DIOCSDINFO) ?
456 setdisklabel(lp, tp, 0, 0) :
457 writedisklabel(dev, rlstrategy, lp, 0));
458 break;
459 }
460
461 case DIOCWLABEL:
462 if ((flag & FWRITE) == 0)
463 err = EBADF;
464 break;
465
466 default:
467 err = ENOTTY;
468 }
469 return err;
470 }
471
472 int
473 rlsize(dev_t dev)
474 {
475 struct disklabel *dl;
476 struct rl_softc *rc;
477 int size, unit = DISKUNIT(dev);
478
479 if ((unit >= rl_cd.cd_ndevs) || ((rc = rl_cd.cd_devs[unit]) == 0))
480 return -1;
481 dl = rc->rc_disk.dk_label;
482 size = dl->d_partitions[DISKPART(dev)].p_size *
483 (dl->d_secsize / DEV_BSIZE);
484 return size;
485 }
486
487 int
488 rldump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
489 {
490 /* Not likely... */
491 return 0;
492 }
493
494 int
495 rlread(dev_t dev, struct uio *uio, int ioflag)
496 {
497 return (physio(rlstrategy, NULL, dev, B_READ, minphys, uio));
498 }
499
500 int
501 rlwrite(dev_t dev, struct uio *uio, int ioflag)
502 {
503 return (physio(rlstrategy, NULL, dev, B_WRITE, minphys, uio));
504 }
505
506 static char *rlerr[] = {
507 "no",
508 "operation incomplete",
509 "read data CRC",
510 "header CRC",
511 "data late",
512 "header not found",
513 "",
514 "",
515 "non-existent memory",
516 "memory parity error",
517 "",
518 "",
519 "",
520 "",
521 "",
522 "",
523 };
524
525 void
526 rlcintr(void *arg)
527 {
528 struct rlc_softc *sc = arg;
529 struct buf *bp;
530 u_int16_t cs;
531
532 bp = sc->sc_active;
533 if (bp == 0) {
534 printf("%s: strange interrupt\n", sc->sc_dev.dv_xname);
535 return;
536 }
537 bus_dmamap_unload(sc->sc_dmat, sc->sc_dmam);
538 sc->sc_active = 0;
539 cs = RL_RREG(RL_CS);
540 if (cs & RLCS_ERR) {
541 int error = (cs & RLCS_ERRMSK) >> 10;
542
543 printf("%s: %s\n", sc->sc_dev.dv_xname, rlerr[error]);
544 bp->b_flags |= B_ERROR;
545 bp->b_error = EIO;
546 bp->b_resid = bp->b_bcount;
547 sc->sc_bytecnt = 0;
548 }
549 if (sc->sc_bytecnt == 0) /* Finished transfer */
550 biodone(bp);
551 rlcstart(sc, sc->sc_bytecnt ? bp : 0);
552 }
553
554 /*
555 * Start routine. First position the disk to the given position,
556 * then start reading/writing. An optimization would be to be able
557 * to handle overlapping seeks between disks.
558 */
559 void
560 rlcstart(struct rlc_softc *sc, struct buf *ob)
561 {
562 struct disklabel *lp;
563 struct rl_softc *rc;
564 struct buf *bp;
565 int bn, cn, sn, tn, blks, err;
566
567 if (sc->sc_active)
568 return; /* Already doing something */
569
570 if (ob == 0) {
571 bp = BUFQ_GET(&sc->sc_q);
572 if (bp == NULL)
573 return; /* Nothing to do */
574 sc->sc_bufaddr = bp->b_data;
575 sc->sc_diskblk = bp->b_rawblkno;
576 sc->sc_bytecnt = bp->b_bcount;
577 bp->b_resid = 0;
578 } else
579 bp = ob;
580 sc->sc_active = bp;
581
582 rc = rl_cd.cd_devs[DISKUNIT(bp->b_dev)];
583 bn = sc->sc_diskblk;
584 lp = rc->rc_disk.dk_label;
585 if (bn) {
586 cn = bn / lp->d_secpercyl;
587 sn = bn % lp->d_secpercyl;
588 tn = sn / lp->d_nsectors;
589 sn = sn % lp->d_nsectors;
590 } else
591 cn = sn = tn = 0;
592
593 /*
594 * Check if we have to position disk first.
595 */
596 if (rc->rc_cyl != cn || rc->rc_head != tn) {
597 u_int16_t da = RLDA_SEEK;
598 if (cn > rc->rc_cyl)
599 da |= ((cn - rc->rc_cyl) << RLDA_CYLSHFT) | RLDA_DIR;
600 else
601 da |= ((rc->rc_cyl - cn) << RLDA_CYLSHFT);
602 if (tn)
603 da |= RLDA_HSSEEK;
604 waitcrdy(sc);
605 RL_WREG(RL_DA, da);
606 RL_WREG(RL_CS, RLCS_SEEK | (rc->rc_hwid << RLCS_USHFT));
607 waitcrdy(sc);
608 rc->rc_cyl = cn;
609 rc->rc_head = tn;
610 }
611 RL_WREG(RL_DA, (cn << RLDA_CYLSHFT) | (tn ? RLDA_HSRW : 0) | (sn << 1));
612 blks = sc->sc_bytecnt/DEV_BSIZE;
613
614 if (sn + blks > RL_SPT/2)
615 blks = RL_SPT/2 - sn;
616 RL_WREG(RL_MP, -(blks*DEV_BSIZE)/2);
617 err = bus_dmamap_load(sc->sc_dmat, sc->sc_dmam, sc->sc_bufaddr,
618 (blks*DEV_BSIZE), (bp->b_flags & B_PHYS ? bp->b_proc : 0),
619 BUS_DMA_NOWAIT);
620 if (err)
621 panic("%s: bus_dmamap_load failed: %d",
622 sc->sc_dev.dv_xname, err);
623 RL_WREG(RL_BA, (sc->sc_dmam->dm_segs[0].ds_addr & 0xffff));
624
625 /* Count up vars */
626 sc->sc_bufaddr += (blks*DEV_BSIZE);
627 sc->sc_diskblk += blks;
628 sc->sc_bytecnt -= (blks*DEV_BSIZE);
629
630 if (bp->b_flags & B_READ)
631 RL_WREG(RL_CS, RLCS_IE|RLCS_RD|(rc->rc_hwid << RLCS_USHFT));
632 else
633 RL_WREG(RL_CS, RLCS_IE|RLCS_WD|(rc->rc_hwid << RLCS_USHFT));
634 }
635
636 /*
637 * Called once per controller when an ubareset occurs.
638 * Retracts all disks and restarts active transfers.
639 */
640 void
641 rlcreset(struct device *dev)
642 {
643 struct rlc_softc *sc = (struct rlc_softc *)dev;
644 struct rl_softc *rc;
645 int i;
646 u_int16_t mp;
647
648 for (i = 0; i < rl_cd.cd_ndevs; i++) {
649 if ((rc = rl_cd.cd_devs[i]) == NULL)
650 continue;
651 if (rc->rc_state != DK_OPEN)
652 continue;
653
654 printf(" %s", rc->rc_dev.dv_xname);
655 RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
656 waitcrdy(sc);
657 mp = RL_RREG(RL_MP);
658 rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
659 rc->rc_cyl = (mp >> 7) & 0777;
660 }
661 if (sc->sc_active == 0)
662 return;
663
664 BUFQ_PUT(&sc->sc_q, sc->sc_active);
665 sc->sc_active = 0;
666 rlcstart(sc, 0);
667 }
668