|
|
1.1 ! root 1: /* ! 2: * DSA port driver for KDB50 (aka BDA) ! 3: * called by, e.g., ra.c ! 4: */ ! 5: ! 6: #include "sys/param.h" ! 7: #include "sys/buf.h" ! 8: #include "sys/biic.h" ! 9: #include "sys/biaddr.h" ! 10: #include "sys/map.h" ! 11: #include "sys/bda.h" ! 12: #include "sys/mscp.h" ! 13: #include "sys/pte.h" ! 14: ! 15: #define hiword(x) (((long)(x)>>16)&0177777) ! 16: #define loword(x) ((long)(x)&0177777) ! 17: ! 18: extern struct biaddr bdaddr[]; ! 19: extern struct bd bd[]; ! 20: extern int bdcnt; ! 21: struct ctab { ! 22: int (*c_seql)(); ! 23: int (*c_dg)(); ! 24: int c_ctype; ! 25: } bdctab[MSMAXID]; ! 26: ! 27: /* ! 28: * bd_flags ! 29: */ ! 30: ! 31: #define UINIT 01 /* already did trivial init */ ! 32: #define UIDONE 02 /* initialization all done */ ! 33: #define UPWAIT 04 /* waiting for command packet */ ! 34: #define UFIRST 010 /* let first packet go even if no credits */ ! 35: #define UTIMER 020 /* timer will kick on next go */ ! 36: #define UCWAIT 040 /* waiting for credits */ ! 37: #define UPMWAIT 0100 /* waiting for map */ ! 38: ! 39: #define NOBACK (-1) ! 40: ! 41: /* ! 42: * bd_cbusy ! 43: */ ! 44: ! 45: #define FREE 0 ! 46: #define NABBED 01 ! 47: #define SENT 02 ! 48: #define MAPPED 04 ! 49: ! 50: /* ! 51: * bdip is really bigpr0 in struct biic ! 52: */ ! 53: ! 54: struct device { ! 55: short bdxx; /* unused */ ! 56: short bdip; ! 57: short bdsar; ! 58: short bdsaw; ! 59: }; ! 60: ! 61: #define bdaregs(p) ((struct device *)&((p)->bigpr0)) ! 62: ! 63: /* ! 64: * bits in bdsa (variously read and write half) ! 65: */ ! 66: ! 67: #define ERR 0100000 ! 68: #define STEP4 040000 ! 69: #define STEP3 020000 ! 70: #define STEP2 010000 ! 71: #define STEP1 04000 ! 72: #define STEPS (STEP1|STEP2|STEP3|STEP4) ! 73: ! 74: #define IE 0200 /* step1 interrupt enable */ ! 75: #define PI 01 /* step2 purge intr enab */ ! 76: #define GO 01 /* step4 ok */ ! 77: ! 78: /* ! 79: * bda communication area ! 80: * ring sizes are chosen so that, ! 81: * with 4K byte buffers, ! 82: * one bdcomm + CSIZE-sized command packets will ! 83: * fit in one buffer; ! 84: * RSIZE-sized response packets ! 85: * will fit in another ! 86: * ! 87: * ring sizes must be powers of 2 ! 88: * (they are passed as such to the port) ! 89: * ! 90: * a delicacy: ! 91: * command packets should not cross 64Kb boundaries. ! 92: * it is believed that the KDB50 gets it wrong if they do. ! 93: * hence, make CSIZE+4 evenly divide a page, ! 94: * and get it aligned sensibly in bdreset ! 95: */ ! 96: ! 97: #define NCP2 5 ! 98: #define NRP2 5 ! 99: #define NCMD (1<<NCP2) ! 100: #define NRSP (1<<NRP2) ! 101: ! 102: struct bdcomm { ! 103: short bd__r0; /* reserved (ugh) */ ! 104: char bd__r1; ! 105: char bd_bdp; /* path to purge */ ! 106: short bd_cmdint; /* flag for command interrupt */ ! 107: short bd_rspint; /* flag for response interrupt */ ! 108: long bd_rsp[NRSP]; /* response pointer ring */ ! 109: long bd_cmd[NCMD]; /* command pointer ring */ ! 110: }; ! 111: ! 112: /* ! 113: * bits in ring pointers ! 114: */ ! 115: ! 116: #define DPOWN 0x80000000 /* port owns descriptor */ ! 117: #define DIE 0x40000000 /* ring transition intr enab */ ! 118: ! 119: #define BDAVIRT 0x80000000 /* in m_buff: mapped transfer */ ! 120: #define V 0x80000000 /* valid bit in page map */ ! 121: ! 122: #define CSIZE 60 /* max size of command packet */ ! 123: #define RSIZE 60 /* max size of response packet */ ! 124: #define HDRSIZE 4 /* size of the header */ ! 125: ! 126: struct bdcmd { ! 127: short uc_len; /* length of message */ ! 128: char uc_tc; /* type, credits */ ! 129: char uc_cid; /* connection id */ ! 130: char uc_data[CSIZE]; ! 131: }; ! 132: ! 133: struct bdrsp { ! 134: short ur_len; /* length of message */ ! 135: char ur_tc; /* type, credits */ ! 136: char ur_cid; /* connection id */ ! 137: char ur_data[RSIZE]; ! 138: }; ! 139: ! 140: /* ! 141: * message types ! 142: */ ! 143: ! 144: #define MTYPE 0xf0 /* where type lives */ ! 145: #define MTSEQL 0x00 /* sequential message */ ! 146: #define MTDG 0x10 /* datagram */ ! 147: #define MTCR 0x20 /* credit notification */ ! 148: ! 149: #define MTNC 0xf /* credits in tc */ ! 150: ! 151: /* ! 152: * etc ! 153: */ ! 154: ! 155: #define PRIINI (PZERO-3) ! 156: #define PRIPKT (PZERO-2) ! 157: #define PRICRED (PZERO-1) ! 158: ! 159: /* ! 160: * command packet to index ! 161: */ ! 162: #define bdmptoi(up, mp) ((struct bdcmd *)((char *)(mp) - HDRSIZE) - (up)->bd_cpkt) ! 163: ! 164: #define TIMEOUT 10 /* time between checks */ ! 165: ! 166: int bdinit(), bdsend(), bdmap(), bdunmap(); ! 167: struct mscmd *bdgpkt(); ! 168: struct msportsw bdport = { ! 169: bdinit, bdgpkt, bdmap, bdsend, bdunmap ! 170: }; ! 171: ! 172: /* ! 173: * init the port ! 174: * called once only ! 175: * returns nonzero if probably ok ! 176: * allowed to sleep ! 177: */ ! 178: ! 179: bdinit(dev, type, force, cid, seql, dg) ! 180: unsigned int dev; ! 181: unsigned int cid; ! 182: int force; ! 183: int (*seql)(), (*dg)(); ! 184: { ! 185: register struct bd *up; ! 186: struct buf *geteblk(); ! 187: extern bdtimer(); ! 188: ! 189: if (dev >= bdcnt) ! 190: return (0); ! 191: if (cid >= MSMAXID) ! 192: return (0); ! 193: bdctab[cid].c_seql = seql; ! 194: bdctab[cid].c_dg = dg; ! 195: bdctab[cid].c_ctype = type; ! 196: up = &bd[dev]; ! 197: if (up->bd_flags & UINIT && force == 0) ! 198: return (1); ! 199: if ((up->bd_addr = (struct biic *)biaddr(&bdaddr[dev])) == 0) { ! 200: printf("bd%d absent\n", dev); ! 201: return (0); ! 202: } ! 203: bdrundown(dev); ! 204: if ((up->bd_flags & UINIT) == 0) { ! 205: up->bd_cbuf = geteblk(); ! 206: clrbuf(up->bd_cbuf); ! 207: up->bd_rbuf = geteblk(); ! 208: clrbuf(up->bd_rbuf); ! 209: up->bd_mbuf = geteblk(); ! 210: clrbuf(up->bd_mbuf); ! 211: up->bd_comm = (struct bdcomm *)up->bd_cbuf->b_un.b_addr; ! 212: #define BDCOFF (((sizeof(struct bdcomm)/sizeof(struct bdcmd))+1)*sizeof(struct bdcmd)) ! 213: up->bd_cpkt = (struct bdcmd *)(up->bd_cbuf->b_un.b_addr + BDCOFF); ! 214: up->bd_rpkt = (struct bdrsp *)up->bd_rbuf->b_un.b_addr; ! 215: up->bd_pmap = (long *)up->bd_mbuf->b_un.b_addr; ! 216: up->bd_flags |= UINIT; ! 217: timeout(bdtimer, (caddr_t)dev, TIMEOUT * HZ); ! 218: } ! 219: bdreset(dev); ! 220: return (1); ! 221: } ! 222: ! 223: /* ! 224: * reset device ! 225: * initially or after error or power fail ! 226: * ! 227: * just kick the device here; ! 228: * ideally we would get an interrupt when self-test finishes ! 229: * (step 1 starts) but we don't, so let the timer routine catch it ! 230: */ ! 231: ! 232: bdreset(dev) ! 233: register int dev; ! 234: { ! 235: register struct bd *up; ! 236: ! 237: up = &bd[dev]; ! 238: up->bd_flags &=~ UIDONE; ! 239: up->bd_addr->bicsr |= BINRST; /* hard reset */ ! 240: } ! 241: ! 242: /* ! 243: * finish up init, step by step ! 244: * called from interrupt code ! 245: */ ! 246: ! 247: bdinintr(dev) ! 248: register int dev; ! 249: { ! 250: register struct bd *up; ! 251: register struct device *rp; ! 252: register struct biic *bi; ! 253: ! 254: up = &bd[dev]; ! 255: rp = bdaregs(up->bd_addr); ! 256: if (up->bd_flags & UIDONE) ! 257: printf("bd%d: unexpected init: sa %o\n", dev, rp->bdsar); ! 258: switch (rp->bdsar & STEPS) { ! 259: case STEP1: ! 260: up->bd_cnext = 0; ! 261: up->bd_rnext = 0; ! 262: up->bd_credits = 0; ! 263: bi = up->bd_addr; ! 264: biinit(&bdaddr[dev], 0); ! 265: bi->biuir = bdaddr[dev].vec; ! 266: bi->bieir = (bdaddr[dev].vec + sizeof(long))|EIBR5; ! 267: rp->bdsaw = ERR | IE | (NCP2<<11) | (NRP2<<8); ! 268: return; ! 269: ! 270: case STEP2: ! 271: rp->bdsaw = PI | loword(physadr(up->bd_comm->bd_rsp)); ! 272: return; ! 273: ! 274: case STEP3: ! 275: rp->bdsaw = hiword(physadr(up->bd_comm->bd_rsp)); ! 276: return; ! 277: ! 278: case STEP4: ! 279: rp->bdsaw = GO; ! 280: for (dev = 0; dev < NCMD; dev++) { ! 281: up->bd_comm->bd_cmd[dev] = 0; /* unnecessary */ ! 282: up->bd_cpkt[dev].uc_len = CSIZE; ! 283: up->bd_back[dev] = NOBACK; ! 284: } ! 285: for (dev = 0; dev < NRSP; dev++) { ! 286: up->bd_rpkt[dev].ur_len = RSIZE; ! 287: up->bd_comm->bd_rsp[dev] = physadr(up->bd_rpkt[dev].ur_data) | DIE | DPOWN; ! 288: } ! 289: up->bd_flags |= UIDONE | UFIRST; ! 290: rminit(up->bd_map, BDANMAP, (up->bd_mbuf->b_bcount/sizeof(long))-1, 1); ! 291: wakeup((caddr_t)up); ! 292: return; ! 293: ! 294: default: ! 295: printf("bd%d init bad: sar %o\n", dev, rp->bdsar); ! 296: return; ! 297: } ! 298: } ! 299: ! 300: /* ! 301: * tell the class drivers that the controller was reset ! 302: * so they can clean up ! 303: * called after controller is stopped (so it's safe to unmap things) ! 304: */ ! 305: bdrundown(dev) ! 306: int dev; ! 307: { ! 308: static struct msend me; ! 309: register int i; ! 310: ! 311: me.m_sts = STRST; /* magic */ ! 312: for (i = 0; i < MSMAXID; i++) ! 313: if (bdctab[i].c_seql) ! 314: (*bdctab[i].c_seql)(dev, bdctab[i].c_ctype, &me); ! 315: } ! 316: ! 317: /* ! 318: * allocate a packet ! 319: * and sufficient resources to send it ! 320: * eg credits ! 321: * may sleep ! 322: * ! 323: * somewhat silly assumption: ! 324: * class driver will allocate a packet, and send it right away or nearly so ! 325: */ ! 326: ! 327: struct mscmd * ! 328: bdgpkt(dev) ! 329: int dev; ! 330: { ! 331: register struct bd *up; ! 332: register int i; ! 333: int s; ! 334: ! 335: up = &bd[dev]; ! 336: s = spl6(); ! 337: while ((up->bd_flags & UIDONE) == 0) ! 338: sleep((caddr_t)up, PRIINI); ! 339: while (up->bd_credits < 2 && (up->bd_flags & UFIRST) == 0) { ! 340: if (bdpkscan(dev, 1)) ! 341: continue; ! 342: up->bd_flags |= UCWAIT; ! 343: sleep((caddr_t)&up->bd_credits, PRICRED); ! 344: } ! 345: if ((up->bd_flags & UFIRST) == 0) ! 346: up->bd_credits--; ! 347: for (;;) { ! 348: for (i = 0; i < NCMD; i++) ! 349: if (up->bd_cbusy[i] == FREE) ! 350: break; ! 351: if (i < NCMD) ! 352: break; ! 353: if (bdpkscan(dev, 1) == 0 && bdcmdscan(dev) == 0) { /* kludge for hung controller */ ! 354: up->bd_flags |= UPWAIT; ! 355: sleep((caddr_t)up->bd_cbusy, PRIPKT); ! 356: } ! 357: } ! 358: up->bd_cbusy[i] = NABBED; ! 359: splx(s); ! 360: return ((struct mscmd *)up->bd_cpkt[i].uc_data); ! 361: } ! 362: ! 363: /* ! 364: * map a transfer if need be ! 365: * and record in the buffer descriptor of a packet ! 366: * may sleep ! 367: * ! 368: * BDA use of m_buff: ! 369: * first longword is buffer address; ! 370: * if BDAVIRT clear, physical addr, else virtual ! 371: * second longword is phys addr of base of page table if virtual ! 372: * ! 373: * subtlety: pretend we mapped it even if we didn't, ! 374: * so it won't be unmapped until someone calls bdunmap ! 375: * else they may be confused when they call it anyway ! 376: */ ! 377: ! 378: bdmap(dev, mp, bp) ! 379: int dev; ! 380: struct mscmd *mp; ! 381: register struct buf *bp; ! 382: { ! 383: register int i; ! 384: register long *b; ! 385: register struct pte *pt; ! 386: int s; ! 387: int base; ! 388: register int size; ! 389: register struct bd *up; ! 390: ! 391: up = &bd[dev]; ! 392: i = bdmptoi(up, mp); ! 393: up->bd_cbusy[i] |= MAPPED; ! 394: if ((bp->b_flags & (B_PHYS|B_UAREA|B_PAGET|B_DIRTY)) == 0) { ! 395: b = (long *)&mp->m_buff; ! 396: *b++ = (long)bp->b_un.b_addr|BDAVIRT; ! 397: *b++ = physadr(Sysmap); ! 398: *b = 0; ! 399: return; ! 400: } ! 401: size = btoc(bp->b_bcount); ! 402: if ((long)bp->b_un.b_addr & PGOFSET) ! 403: size++; /* unaligned */ ! 404: s = spl6(); ! 405: while ((base = rmalloc(up->bd_map, size)) == 0) { ! 406: up->bd_flags |= UPMWAIT; ! 407: sleep((caddr_t)up->bd_map, PRIPKT); ! 408: } ! 409: splx(s); ! 410: up->bd_mbase[i] = base; ! 411: up->bd_msize[i] = size; ! 412: b = &up->bd_pmap[base]; ! 413: pt = btopte(bp); ! 414: while (--size >= 0) ! 415: *b++ = pt++->pg_pfnum | V; ! 416: b = (long *)&mp->m_buff; ! 417: *b++ = ctob(base)|((long)bp->b_un.b_addr&PGOFSET)|BDAVIRT; ! 418: *b++ = physadr(up->bd_pmap); ! 419: *b = 0; ! 420: } ! 421: ! 422: /* ! 423: * free a mapped packet ! 424: */ ! 425: ! 426: bdunmap(dev, mp) ! 427: int dev; ! 428: struct mscmd *mp; ! 429: { ! 430: register struct bd *up; ! 431: register int i; ! 432: ! 433: up = &bd[dev]; ! 434: i = bdmptoi(up, mp); ! 435: if (up->bd_cbusy[i] == FREE) ! 436: return; /* wasn't mapped, and already freed */ ! 437: if (up->bd_msize[i]) { ! 438: rmfree(up->bd_map, up->bd_msize[i], up->bd_mbase[i]); ! 439: up->bd_msize[i] = 0; ! 440: if (up->bd_flags & UPMWAIT) { ! 441: up->bd_flags &=~ UPMWAIT; ! 442: wakeup((caddr_t)up->bd_map); ! 443: } ! 444: } ! 445: up->bd_cbusy[i] = FREE; ! 446: if (up->bd_flags & UPWAIT) { ! 447: up->bd_flags &=~ UPWAIT; ! 448: wakeup((caddr_t)up->bd_cbusy); ! 449: } ! 450: } ! 451: ! 452: /* ! 453: * send a packet ! 454: * may not sleep ! 455: * call at spl5 ! 456: */ ! 457: ! 458: bdsend(dev, cid, mp) ! 459: int dev; ! 460: int cid; ! 461: struct mscmd *mp; ! 462: { ! 463: register struct bd *up; ! 464: register int i; ! 465: register int j; ! 466: ! 467: up = &bd[dev]; ! 468: up->bd_flags &=~ UFIRST; ! 469: i = bdmptoi(up, mp); ! 470: up->bd_cpkt[i].uc_cid = cid; ! 471: j = up->bd_cnext++; ! 472: if (up->bd_cnext >= NCMD) ! 473: up->bd_cnext = 0; ! 474: if (up->bd_comm->bd_cmd[j] & DPOWN) ! 475: panic("bdsend"); ! 476: if (up->bd_back[j] >= 0) { /* ran for a while with no free */ ! 477: bdcmdscan(dev); ! 478: if (up->bd_back[j] >= 0) ! 479: panic("bdsend"); ! 480: } ! 481: up->bd_back[j] = i; ! 482: up->bd_comm->bd_cmd[j] = DPOWN | DIE | physadr(mp); ! 483: up->bd_cbusy[i] |= SENT; ! 484: up->bd_cbusy[i] &=~ NABBED; ! 485: i = bdaregs(up->bd_addr)->bdip; /* initiate polling */ ! 486: } ! 487: ! 488: /* ! 489: * interrupt routine ! 490: */ ! 491: ! 492: long bd_spur; ! 493: int bdspl; ! 494: ! 495: bd0int(dev) ! 496: int dev; ! 497: { ! 498: register struct bd *up; ! 499: register struct device *rp; ! 500: ! 501: bdspl = mfpr(0x12); ! 502: up = &bd[dev]; ! 503: if (dev >= bdcnt || (up->bd_flags & UINIT) == 0) { ! 504: printf("bd%d: stray intr\n", dev); ! 505: return; ! 506: } ! 507: rp = bdaregs(up->bd_addr); ! 508: if (rp->bdsar & ERR) { ! 509: printf("bd%d: hard error %o\n", dev, rp->bdsar & 0177777); ! 510: bdreset(dev); ! 511: return; ! 512: } ! 513: if (rp->bdsar & STEPS) { ! 514: bdinintr(dev); ! 515: return; ! 516: } ! 517: if (up->bd_comm->bd_cmdint == 0 ! 518: && up->bd_comm->bd_rspint == 0) ! 519: bd_spur++; ! 520: while (up->bd_comm->bd_cmdint) { ! 521: up->bd_comm->bd_cmdint = 0; ! 522: bdcmdscan(dev); ! 523: } ! 524: while (up->bd_comm->bd_rspint) { ! 525: up->bd_comm->bd_rspint = 0; ! 526: if (bdpkscan(dev, 0)) ! 527: up->bd_flags &=~ UTIMER; ! 528: } ! 529: } ! 530: ! 531: bd1int(dev) ! 532: int dev; ! 533: { ! 534: printf("bd%d: error intr\n", dev); ! 535: } ! 536: ! 537: /* ! 538: * free packets which are completely sent ! 539: * (and which don't have associated map) ! 540: */ ! 541: ! 542: bdcmdscan(dev) ! 543: int dev; ! 544: { ! 545: register struct bd *up; ! 546: register int i, j; ! 547: register int freed; ! 548: register struct bdcomm *bdc; ! 549: ! 550: up = &bd[dev]; ! 551: bdc = up->bd_comm; ! 552: freed = 0; ! 553: for (j = 0; j < NCMD; j++) ! 554: if (up->bd_back[j] >= 0 ! 555: && (bdc->bd_cmd[j] & DPOWN) == 0) { ! 556: i = up->bd_back[j]; ! 557: if ((up->bd_cbusy[i] & (SENT|MAPPED)) == SENT) { ! 558: up->bd_cbusy[i] = FREE; ! 559: freed++; ! 560: } ! 561: up->bd_back[j] = NOBACK; ! 562: } ! 563: if (freed && up->bd_flags & UPWAIT) ! 564: wakeup((caddr_t)up->bd_cbusy); ! 565: return (freed); ! 566: } ! 567: ! 568: /* ! 569: * check for responses from the controller ! 570: * and deal with them ! 571: * return a count for debugging purposes ! 572: */ ! 573: ! 574: int ! 575: bdpkscan(dev, doall) ! 576: int dev; ! 577: int doall; ! 578: { ! 579: register struct bd *up; ! 580: register int i; ! 581: int nf; ! 582: register struct bdrsp *pk; ! 583: register struct ctab *cp; ! 584: register struct bdcomm *bdc; /* speed */ ! 585: int s; ! 586: ! 587: up = &bd[dev]; ! 588: bdc = up->bd_comm; ! 589: nf = 0; ! 590: s = spl6(); ! 591: for (i = up->bd_rnext; ; i < NRSP-1 ? i++ : (i = 0)) { ! 592: if (bdc->bd_rsp[i] & DPOWN) { ! 593: up->bd_rnext = i; ! 594: /* don't stop if doall? */ ! 595: break; ! 596: } ! 597: nf++; ! 598: pk = &up->bd_rpkt[i]; ! 599: up->bd_credits += pk->ur_tc & MTNC; ! 600: if (up->bd_flags & UCWAIT) { ! 601: wakeup((caddr_t)&up->bd_credits); ! 602: up->bd_flags &=~ UCWAIT; ! 603: } ! 604: if (pk->ur_cid > MSMAXID) ! 605: printf("bd%d msg id %d\n", dev, pk->ur_cid); ! 606: else { ! 607: cp = &bdctab[pk->ur_cid]; ! 608: switch (pk->ur_tc & MTYPE) { ! 609: case MTSEQL: ! 610: if (cp->c_seql) ! 611: (*cp->c_seql)(dev, cp->c_ctype, (struct msend *)pk->ur_data); ! 612: break; ! 613: ! 614: case MTDG: ! 615: if (cp->c_dg) ! 616: (*cp->c_dg)(dev, cp->c_ctype, pk->ur_data); ! 617: break; ! 618: ! 619: /* default: ignore */ ! 620: } ! 621: } ! 622: pk->ur_len = RSIZE; ! 623: bdc->bd_rsp[i] |= DPOWN | DIE; ! 624: } ! 625: splx(s); ! 626: return (nf); ! 627: } ! 628: ! 629: /* ! 630: * timeout routine ! 631: * return any waiting packets ! 632: * -- callout routines don't necessarily run at high priority. ! 633: * hence the spl6, so bdpkscan won't be reentered ! 634: */ ! 635: ! 636: int bd_kicked; ! 637: ! 638: bdtimer(i) ! 639: int i; ! 640: { ! 641: register struct bd *up; ! 642: register int s; ! 643: ! 644: up = &bd[i]; ! 645: if ((up->bd_flags & UINIT) == 0) ! 646: return; ! 647: if ((up->bd_flags & UIDONE) == 0) { ! 648: if ((bdaregs(up->bd_addr)->bdsar & STEPS) == STEP1) ! 649: bdinintr(i); ! 650: } ! 651: else if ((up->bd_flags & UTIMER) == 0) ! 652: up->bd_flags |= UTIMER; ! 653: else { ! 654: s = spl6(); ! 655: if (bdpkscan(i, 1) && up->bd_flags & UPWAIT) { ! 656: wakeup((caddr_t)up->bd_cbusy); ! 657: bd_kicked++; ! 658: } ! 659: splx(s); ! 660: up->bd_flags &=~ UTIMER; ! 661: } ! 662: timeout(bdtimer, (caddr_t)i, TIMEOUT * HZ); ! 663: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.