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