|
|
1.1 ! root 1: /* ! 2: * UNFINISHED! UNFINISHED! UNFINISHED! UNFINISHED! UNFINISHED! UNFINISHED! ! 3: * ! 4: * [email protected] 93/04/02 ! 5: * ! 6: * I was writing this driver for a wd7000-ASC. Yeah, the "-ASC" not the ! 7: * "-FASST2". The difference is that the "-ASC" is missing scatter gather ! 8: * support. ! 9: * ! 10: * In any case, the real reason why I never finished it is because the ! 11: * motherboard I have has broken DMA. This card wants 8MHz 1 wait state ! 12: * operation, and my board munges about 30% of the words transferred. ! 13: * ! 14: * Hopefully someone can finish this for the wd7000-FASST2. It should be ! 15: * quite easy to do. Look at the Linux wd7000 device driver to see how ! 16: * scatter gather is done by the board, then look at one of the Adaptec ! 17: * drivers to finish off the job.. ! 18: */ ! 19: #include "wds.h" ! 20: #if NWDS > 0 ! 21: ! 22: #include <sys/types.h> ! 23: #include <sys/param.h> ! 24: #include <sys/systm.h> ! 25: #include <sys/errno.h> ! 26: #include <sys/ioctl.h> ! 27: #include <sys/buf.h> ! 28: #include <sys/proc.h> ! 29: #include <sys/user.h> ! 30: ! 31: #include <i386/isa/isa_device.h> ! 32: #include <scsi/scsi_all.h> ! 33: #include <scsi/scsiconf.h> ! 34: ! 35: extern int delaycount; /* from clock setup code */ ! 36: ! 37: #define PHYSTOKV(x) ( (u_long)(x) | 0xFE000000) ! 38: #define KVTOPHYS(x) vtophys(x) ! 39: #define PAGESIZ 4096 ! 40: #define INVALIDATE_CACHE {asm volatile( ".byte 0x0F ;.byte 0x08" ); } ! 41: ! 42: ! 43: /* WD7000 registers */ ! 44: #define WDS_STAT 0 /* read */ ! 45: #define WDS_IRQSTAT 1 /* read */ ! 46: ! 47: #define WDS_CMD 0 /* write */ ! 48: #define WDS_IRQACK 1 /* write */ ! 49: #define WDS_HCR 2 /* write */ ! 50: ! 51: /* WDS_STAT (read) defs */ ! 52: #define WDS_IRQ 0x80 ! 53: #define WDS_RDY 0x40 ! 54: #define WDS_REJ 0x20 ! 55: #define WDS_INIT 0x10 ! 56: ! 57: /* WDS_IRQSTAT (read) defs */ ! 58: #define WDSI_MASK 0xc0 ! 59: #define WDSI_ERR 0x00 ! 60: #define WDSI_MFREE 0x80 ! 61: #define WDSI_MSVC 0xc0 ! 62: ! 63: /* WDS_CMD (write) defs */ ! 64: #define WDSC_NOOP 0x00 ! 65: #define WDSC_INIT 0x01 ! 66: #define WDSC_DISUNSOL 0x02 ! 67: #define WDSC_ENAUNSOL 0x03 ! 68: #define WDSC_IRQMFREE 0x04 ! 69: #define WDSC_SCSIRESETSOFT 0x05 ! 70: #define WDSC_SCSIRESETHARD 0x06 ! 71: #define WDSC_MSTART(m) (0x80 + (m)) ! 72: #define WDSC_MMSTART(m) (0xc0 + (m)) ! 73: ! 74: /* WDS_HCR (write) defs */ ! 75: #define WDSH_IRQEN 0x08 ! 76: #define WDSH_DRQEN 0x04 ! 77: #define WDSH_SCSIRESET 0x02 ! 78: #define WDSH_ASCRESET 0x01 ! 79: ! 80: struct wds_cmd { ! 81: u_char cmd; ! 82: u_char targ; ! 83: struct scsi_generic scb; /*u_char scb[12];*/ ! 84: u_char stat; ! 85: u_char venderr; ! 86: u_char len[3]; ! 87: u_char data[3]; ! 88: u_char next[3]; ! 89: u_char write; ! 90: u_char xx[6]; ! 91: }; ! 92: ! 93: struct wds_req { ! 94: struct wds_cmd cmd; ! 95: struct wds_cmd sense; ! 96: struct scsi_xfer *sxp; ! 97: int busy, polled; ! 98: int done, ret, ombn; ! 99: }; ! 100: ! 101: #define WDSX_SCSICMD 0x00 ! 102: #define WDSX_OPEN_RCVBUF 0x80 ! 103: #define WDSX_RCV_CMD 0x81 ! 104: #define WDSX_RCV_DATA 0x82 ! 105: #define WDSX_RCV_DATASTAT 0x83 ! 106: #define WDSX_SND_DATA 0x84 ! 107: #define WDSX_SND_DATASTAT 0x85 ! 108: #define WDSX_SND_CMDSTAT 0x86 ! 109: #define WDSX_READINIT 0x88 ! 110: #define WDSX_READSCSIID 0x89 ! 111: #define WDSX_SETUNSOLIRQMASK 0x8a ! 112: #define WDSX_GETUNSOLIRQMASK 0x8b ! 113: #define WDSX_GETFIRMREV 0x8c ! 114: #define WDSX_EXECDIAG 0x8d ! 115: #define WDSX_SETEXECPARM 0x8e ! 116: #define WDSX_SETEXECPARM 0x8e ! 117: #define WDSX_GETEXECPARM 0x8f ! 118: ! 119: struct wds_mb { ! 120: u_char stat; ! 121: u_char addr[3]; ! 122: }; ! 123: /* ICMB status value */ ! 124: #define ICMB_OK 0x01 ! 125: #define ICMB_OKERR 0x02 ! 126: #define ICMB_ETIME 0x04 ! 127: #define ICMB_ERESET 0x05 ! 128: #define ICMB_ETARCMD 0x06 ! 129: #define ICMB_ERESEL 0x80 ! 130: #define ICMB_ESEL 0x81 ! 131: #define ICMB_EABORT 0x82 ! 132: #define ICMB_ESRESET 0x83 ! 133: #define ICMB_EHRESET 0x84 ! 134: ! 135: struct wds_setup { ! 136: u_char cmd; ! 137: u_char scsi_id; ! 138: u_char buson_t; ! 139: u_char busoff_t; ! 140: u_char xx; ! 141: u_char mbaddr[3]; ! 142: u_char nomb; ! 143: u_char nimb; ! 144: }; ! 145: ! 146: #define WDS_NOMB 16 ! 147: #define WDS_NIMB 8 ! 148: #define MAXSIMUL 8 ! 149: struct wds { ! 150: u_short addr; ! 151: struct wds_req wdsr[MAXSIMUL]; ! 152: struct wds_mb ombs[WDS_NOMB], imbs[WDS_NIMB]; ! 153: int devs; ! 154: } wds[NWDS]; ! 155: ! 156: static int wdsunit = 0; ! 157: int wds_debug = 0; ! 158: ! 159: int wdsprobe(), wdsattach(), wdsintr(); ! 160: ! 161: struct isa_driver wdsdriver = { ! 162: wdsprobe, ! 163: wdsattach, ! 164: "wds", ! 165: }; ! 166: ! 167: int wds_scsi_cmd(); ! 168: void wds_minphys(); ! 169: long wds_adapter_info(); ! 170: ! 171: struct scsi_switch wds_switch = { ! 172: wds_scsi_cmd, ! 173: wds_minphys, ! 174: 0, 0, ! 175: wds_adapter_info, ! 176: 0, 0, 0, ! 177: }; ! 178: ! 179: p2x(p, x) ! 180: u_char *p; ! 181: u_long x; ! 182: { ! 183: p[0] = (x & 0x00ff0000) >> 16; ! 184: p[1] = (x & 0x0000ff00) >> 8; ! 185: p[2] = (x & 0x000000ff); ! 186: } ! 187: ! 188: u_char * ! 189: x2p(x) ! 190: u_char *x; ! 191: { ! 192: u_long q; ! 193: ! 194: q = ((x[0]<<16) & 0x00ff0000) + ((x[1]<<8) & 0x0000ff00) + (x[2] & 0x000000ff); ! 195: return (u_char *)q; ! 196: } ! 197: ! 198: wdsprobe(dev) ! 199: struct isa_device *dev; ! 200: { ! 201: scsi_debug = PRINTROUTINES | TRACEOPENS | TRACEINTERRUPTS | ! 202: SHOWREQUESTS | SHOWSCATGATH | SHOWINQUIRY | SHOWCOMMANDS; ! 203: ! 204: if(wdsunit > NWDS) ! 205: return 0; ! 206: ! 207: dev->id_unit = wdsunit; ! 208: wds[wdsunit].addr = dev->id_iobase; ! 209: ! 210: if(wds_init(dev) != 0) ! 211: return 0; ! 212: wdsunit++; ! 213: return 1; ! 214: } ! 215: ! 216: void ! 217: wds_minphys(bp) ! 218: struct buf *bp; ! 219: { ! 220: int base = (int)bp->b_un.b_addr & (PAGESIZ-1); ! 221: ! 222: if(base + bp->b_bcount > PAGESIZ) ! 223: bp->b_bcount = PAGESIZ - base; ! 224: } ! 225: ! 226: struct wds_req * ! 227: wdsr_alloc(unit) ! 228: int unit; ! 229: { ! 230: struct wds_req *r; ! 231: int x; ! 232: int i; ! 233: ! 234: r = NULL; ! 235: x = splbio(); ! 236: for(i=0; i<MAXSIMUL; i++) ! 237: if(wds[unit].wdsr[i].busy == 0) { ! 238: r = &wds[unit].wdsr[i]; ! 239: r->busy = 1; ! 240: break; ! 241: } ! 242: if(r == NULL) { ! 243: splx(x); ! 244: return NULL; ! 245: } ! 246: ! 247: r->ombn = -1; ! 248: for(i=0; i<WDS_NOMB; i++) ! 249: if(wds[unit].ombs[i].stat==0) { ! 250: wds[unit].ombs[i].stat = 1; ! 251: r->ombn = i; ! 252: break; ! 253: } ! 254: if(r->ombn == -1 ) { ! 255: r->busy = 0; ! 256: splx(x); ! 257: return NULL; ! 258: } ! 259: splx(x); ! 260: return r; ! 261: } ! 262: ! 263: int ! 264: wds_scsi_cmd(sxp) ! 265: struct scsi_xfer *sxp; ! 266: { ! 267: struct wds_req *r; ! 268: int unit = sxp->adapter; ! 269: u_short base; ! 270: u_char c, *p; ! 271: int i; ! 272: ! 273: base = wds[unit].addr; ! 274: ! 275: /*printf("scsi_cmd\n");*/ ! 276: ! 277: if( sxp->flags & SCSI_RESET) { ! 278: printf("reset!\n"); ! 279: return COMPLETE; ! 280: } ! 281: ! 282: /* XXX DEBUG */ ! 283: if( sxp->targ != 1) { ! 284: printf("ignoring target %d/%d\n", sxp->targ, sxp->lu); ! 285: sxp->error = XS_TIMEOUT; ! 286: return HAD_ERROR; ! 287: } ! 288: ! 289: r = wdsr_alloc(unit); ! 290: if(r==NULL) { ! 291: printf("no request slot available!\n"); ! 292: sxp->error = XS_DRIVER_STUFFUP; ! 293: return TRY_AGAIN_LATER; ! 294: } ! 295: r->done = 0; ! 296: r->sxp = sxp; ! 297: ! 298: printf("wds%d: target %d/%d req %8x flags %08x len %d: ", unit, ! 299: sxp->targ, sxp->lu, r, sxp->flags, sxp->cmdlen); ! 300: for(i=0, p=(u_char *)sxp->cmd; i<sxp->cmdlen; i++) ! 301: printf("%02x ", p[i]); ! 302: printf("\n"); ! 303: printf(" data %08x datalen %08x\n", sxp->data, sxp->datalen); ! 304: ! 305: if(sxp->flags & SCSI_DATA_UIO) { ! 306: printf("UIO!\n"); ! 307: sxp->error = XS_DRIVER_STUFFUP; ! 308: return TRY_AGAIN_LATER; ! 309: } ! 310: ! 311: p2x(&wds[unit].ombs[r->ombn].addr[0], KVTOPHYS(&r->cmd)); ! 312: printf("%08x/%08x mbox@%08x: %02x %02x %02x %02x\n", ! 313: &r->cmd, KVTOPHYS(&r->cmd), &wds[unit].ombs[0], ! 314: wds[unit].ombs[r->ombn].stat, wds[unit].ombs[r->ombn].addr[0], ! 315: wds[unit].ombs[r->ombn].addr[1], wds[unit].ombs[r->ombn].addr[2]); ! 316: ! 317: bzero(&r->cmd, sizeof r->cmd); ! 318: r->cmd.cmd = WDSX_SCSICMD; ! 319: r->cmd.targ = (sxp->targ << 5) | sxp->lu; ! 320: bcopy(sxp->cmd, &r->cmd.scb, sxp->cmdlen<12 ? sxp->cmdlen : 12); ! 321: p2x(&r->cmd.len[0], sxp->datalen); ! 322: p2x(&r->cmd.data[0], sxp->datalen ? KVTOPHYS(sxp->data) : 0); ! 323: r->cmd.write = (sxp->flags&SCSI_DATA_IN)? 0x80 : 0x00; ! 324: p2x(&r->cmd.next[0], KVTOPHYS(&r->sense)); ! 325: ! 326: bzero(&r->sense, sizeof r->sense); ! 327: r->sense.cmd = r->cmd.cmd; ! 328: r->sense.targ = r->cmd.targ; ! 329: r->sense.scb.opcode = REQUEST_SENSE; ! 330: p2x(&r->sense.data[0], KVTOPHYS(&sxp->sense)); ! 331: p2x(&r->sense.len[0], sizeof sxp->sense); ! 332: r->sense.write = 0x80; ! 333: ! 334: /*printf("wdscmd: "); ! 335: for(i=0, p=(u_char *)&r->cmd; i<sizeof r->cmd; i++) ! 336: printf("%02x ", p[i]); ! 337: printf("\n");*/ ! 338: ! 339: if(sxp->flags & SCSI_NOMASK) { ! 340: outb(base+WDS_HCR, WDSH_DRQEN); ! 341: r->polled = 1; ! 342: } else ! 343: r->polled = 0; ! 344: ! 345: c = WDSC_MSTART(r->ombn); ! 346: if( wds_cmd(base, &c, sizeof c) != 0) { ! 347: printf("wds%d: unable to start outgoing mbox\n", unit); ! 348: r->busy = 0; ! 349: /* XXX need to free mailbox */ ! 350: return TRY_AGAIN_LATER; ! 351: } ! 352: ! 353: DELAY(10000); ! 354: /*printf("%08x/%08x mbox: %02x %02x %02x %02x\n", &r->cmd, KVTOPHYS(&r->cmd), ! 355: wds[unit].ombs[r->ombn].stat, wds[unit].ombs[r->ombn].addr[0], ! 356: wds[unit].ombs[r->ombn].addr[1], wds[unit].ombs[r->ombn].addr[2]);*/ ! 357: ! 358: if(sxp->flags & SCSI_NOMASK) { ! 359: repoll: printf("wds%d: polling.", unit); ! 360: i = 0; ! 361: while( (inb(base+WDS_STAT) & WDS_IRQ) == 0) { ! 362: printf("."); ! 363: DELAY(10000); ! 364: if(++i == 10) { ! 365: printf("failed %02x\n", inb(base+WDS_IRQSTAT)); ! 366: /*r->busy = 0;*/ ! 367: sxp->error = XS_TIMEOUT; ! 368: return HAD_ERROR; ! 369: } ! 370: } ! 371: printf("got one!\n"); ! 372: wdsintr(unit); ! 373: if(r->done) { ! 374: r->sxp->flags |= ITSDONE; ! 375: if(r->sxp->when_done) ! 376: (*r->sxp->when_done)(r->sxp->done_arg, ! 377: r->sxp->done_arg2); ! 378: r->busy = 0; ! 379: return r->ret; ! 380: } ! 381: goto repoll; ! 382: } ! 383: ! 384: outb(base+WDS_HCR, WDSH_IRQEN|WDSH_DRQEN); ! 385: printf("wds%d: successfully queued\n", unit); ! 386: return SUCCESSFULLY_QUEUED; ! 387: } ! 388: ! 389: long ! 390: wds_adapter_info(unit) ! 391: int unit; ! 392: { ! 393: return 1; ! 394: } ! 395: ! 396: int ! 397: wdsintr(unit) ! 398: { ! 399: struct wds_cmd *pc, *vc; ! 400: struct wds_mb *in; ! 401: u_char stat; ! 402: u_char c; ! 403: ! 404: /*printf("stat=%02x\n", inb(wds[unit].addr + WDS_STAT));*/ ! 405: DELAY(1000); ! 406: c = inb(wds[unit].addr + WDS_IRQSTAT); ! 407: printf("wdsintr: %02x\n", c); ! 408: if( (c&WDSI_MASK) == WDSI_MSVC) { ! 409: DELAY(1000); ! 410: c = c & ~WDSI_MASK; ! 411: in = &wds[unit].imbs[c]; ! 412: ! 413: printf("incoming mailbox %02x@%08x: ", c, in); ! 414: printf("%02x %02x %02x %02x\n", ! 415: in->stat, in->addr[0], in->addr[1], in->addr[2]); ! 416: pc = (struct wds_cmd *)x2p(&in->addr[0]); ! 417: vc = (struct wds_cmd *)PHYSTOKV(pc); ! 418: stat = in->stat; ! 419: printf("p=%08x v=%08x stat %02xx\n", pc, vc, stat); ! 420: wds_done(unit, vc, stat); ! 421: in->stat = 0; ! 422: ! 423: outb(wds[unit].addr + WDS_IRQACK, 0xff); ! 424: } ! 425: } ! 426: ! 427: wds_done(unit, c, stat) ! 428: int unit; ! 429: struct wds_cmd *c; ! 430: u_char stat; ! 431: { ! 432: struct wds_req *r; ! 433: int i; ! 434: ! 435: r = (struct wds_req *)NULL; ! 436: for(i=0; i<MAXSIMUL; i++) ! 437: if( c == &wds[unit].wdsr[i].cmd ) { ! 438: /*printf("found at req slot %d\n", i);*/ ! 439: r = &wds[unit].wdsr[i]; ! 440: break; ! 441: } ! 442: if(r == (struct wds_req *)NULL) { ! 443: printf("failed to find request!\n"); ! 444: return; ! 445: } ! 446: ! 447: printf("wds%d: cmd %8x stat %2x/%2x %2x/%2x\n", unit, c, ! 448: r->cmd.stat, r->cmd.venderr, r->sense.stat, r->sense.venderr); ! 449: ! 450: r->done = 1; ! 451: /* XXX need to free mailbox */ ! 452: r->ret = HAD_ERROR; ! 453: switch(r->cmd.stat) { ! 454: case ICMB_OK: ! 455: /*XXX r->sxp->sense.valid = 0; ! 456: r->sxp->error = 0;*/ ! 457: r->ret = COMPLETE; ! 458: break; ! 459: case ICMB_OKERR: ! 460: printf("scsi err %02x\n", c->venderr); ! 461: /*XXX r->sxp->sense.error_code = c->venderr; ! 462: r->sxp->sense.valid = 1;*/ ! 463: r->ret = COMPLETE; ! 464: break; ! 465: case ICMB_ETIME: ! 466: r->sxp->error = XS_TIMEOUT; ! 467: r->ret = HAD_ERROR; ! 468: break; ! 469: case ICMB_ERESET: ! 470: case ICMB_ETARCMD: ! 471: case ICMB_ERESEL: ! 472: case ICMB_ESEL: ! 473: case ICMB_EABORT: ! 474: case ICMB_ESRESET: ! 475: case ICMB_EHRESET: ! 476: r->sxp->error = XS_DRIVER_STUFFUP; ! 477: r->ret = HAD_ERROR; ! 478: break; ! 479: } ! 480: if(r->polled==0) { ! 481: r->sxp->flags |= ITSDONE; ! 482: if(r->sxp->when_done) ! 483: (*r->sxp->when_done)(r->sxp->done_arg, r->sxp->done_arg2); ! 484: r->busy = 0; ! 485: } ! 486: return; ! 487: } ! 488: ! 489: int ! 490: wdsattach(dev) ! 491: struct isa_device *dev; ! 492: { ! 493: int unit = dev->id_unit; ! 494: ! 495: /*printf("start attach...\n");*/ ! 496: scsi_attachdevs(unit, wds[unit].devs, &wds_switch); ! 497: /*printf("done attach...\n");*/ ! 498: return; ! 499: } ! 500: ! 501: wds_init(dev) ! 502: struct isa_device *dev; ! 503: { ! 504: struct wds_setup init; ! 505: u_short base; ! 506: u_char *p, c; ! 507: int unit, i; ! 508: ! 509: unit = dev->id_unit; ! 510: base = wds[unit].addr; ! 511: ! 512: /* ! 513: * Sending a command causes the CMDRDY bit to clear. ! 514: */ ! 515: c = inb(base+WDS_STAT); ! 516: for(i=0; i<4; i++) ! 517: if( (inb(base+WDS_STAT) & WDS_RDY) != 0) { ! 518: goto ready; ! 519: DELAY(10); ! 520: } ! 521: return 1; ! 522: ! 523: ready: ! 524: outb(base+WDS_CMD, WDSC_NOOP); ! 525: if( inb(base+WDS_STAT) & WDS_RDY) ! 526: return 1; ! 527: ! 528: /* ! 529: * the controller exists. reset and init. ! 530: */ ! 531: outb(base+WDS_HCR, WDSH_SCSIRESET|WDSH_ASCRESET); ! 532: DELAY(3); ! 533: outb(base+WDS_HCR, WDSH_DRQEN); ! 534: DELAY(20000); ! 535: ! 536: isa_dmacascade(dev->id_drq); ! 537: ! 538: if( (inb(base+WDS_STAT) & (WDS_RDY)) != WDS_RDY) { ! 539: printf("wds%d: waiting for controller to become ready", unit); ! 540: for(i=0; i<6; i++) { ! 541: if( (inb(base+WDS_STAT) & (WDS_RDY)) == WDS_RDY) ! 542: break; ! 543: printf("."); ! 544: DELAY(10000); ! 545: } ! 546: if( (inb(base+WDS_STAT) & (WDS_RDY)) != WDS_RDY) { ! 547: printf("failed\n"); ! 548: return 1; ! 549: } ! 550: } ! 551: ! 552: bzero(&init, sizeof init); ! 553: init.cmd = WDSC_INIT; ! 554: init.scsi_id = 0; ! 555: init.buson_t = 24; ! 556: init.busoff_t = 48; ! 557: p2x(&init.mbaddr[0], KVTOPHYS(&wds[unit].ombs[0])); ! 558: init.xx = 0; ! 559: init.nomb = WDS_NOMB; ! 560: init.nimb = WDS_NIMB; ! 561: ! 562: /*p = (u_char *)&init; ! 563: printf("wds%d: %08x %08x init: ", unit, ! 564: &wds[unit].ombs[0], KVTOPHYS(&wds[unit].ombs[0])); ! 565: for(i=0; i<sizeof init; i++) ! 566: printf("%02x ", p[i]); ! 567: printf("\n");*/ ! 568: ! 569: wds_wait(base+WDS_STAT, WDS_RDY, WDS_RDY); ! 570: if( wds_cmd(base, &init, sizeof init) != 0) { ! 571: printf("wds%d: wds_cmd failed\n", unit); ! 572: return 1; ! 573: } ! 574: wds_wait(base+WDS_STAT, WDS_INIT, WDS_INIT); ! 575: ! 576: wds_wait(base+WDS_STAT, WDS_RDY, WDS_RDY); ! 577: c = WDSC_DISUNSOL; ! 578: if( wds_cmd(base, &c, sizeof c) != 0) { ! 579: printf("wds%d: wds_cmd failed\n", unit); ! 580: return 1; ! 581: } ! 582: ! 583: return 0; ! 584: } ! 585: ! 586: wds_cmd(base, p, l) ! 587: u_short base; ! 588: u_char *p; ! 589: int l; ! 590: { ! 591: int i; ! 592: u_char c; ! 593: ! 594: i = 0; ! 595: while(i < l) { ! 596: while( ((c=inb(base+WDS_STAT)) & WDS_RDY) == 0) ! 597: ; ! 598: ! 599: outb(base+WDS_CMD, *p); ! 600: ! 601: while( ((c=inb(base+WDS_STAT)) & WDS_RDY) == 0) ! 602: ; ! 603: ! 604: if(c & WDS_REJ) ! 605: return 1; ! 606: p++; ! 607: i++; ! 608: } ! 609: while( ((c=inb(base+WDS_STAT)) & WDS_RDY) == 0) ! 610: ; ! 611: if(c & WDS_REJ) ! 612: return 1; ! 613: /*printf("wds_cmd: %02x\n", inb(base+WDS_STAT));*/ ! 614: return 0; ! 615: } ! 616: ! 617: wds_wait(reg, mask, val) ! 618: { ! 619: while( (inb(reg) & mask) != val) ! 620: ; ! 621: } ! 622: ! 623: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.