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