|
|
1.1 ! root 1: /* ! 2: * This is the generic SCSI part of the ! 3: * Adaptec AHA154x host adapter driver for the AT. ! 4: * ! 5: * $Log: scsi.c,v $ ! 6: * Revision 1.2 92/08/04 12:54:20 bin ! 7: * upd for ker59 ! 8: * ! 9: * Revision 1.10 92/01/17 11:38:37 bin ! 10: * update by hal... looks like final 321 version ! 11: * ! 12: * Revision 1.10 92/01/17 03:51:13 hal ! 13: * Cleanup for 3.2.1. ! 14: * ! 15: * Revision 1.9 91/11/11 12:32:58 hal ! 16: * Get SD_HDS and SD_SPT from tboot. ! 17: * ! 18: * Revision 1.8 91/10/25 14:50:39 hal ! 19: * Make DMA channel patchable. ! 20: * ! 21: * Revision 1.7 91/10/22 13:40:36 hal ! 22: * Use sys on kernel header includes. ! 23: * ! 24: * Revision 1.6 91/06/10 13:28:11 hal ! 25: * Refix startup problem with HDGETA. Text cleanup. ! 26: * ! 27: * Revision 1.5 91/06/10 12:58:04 hal ! 28: * Partial fix for HDGETA failing if partition table absent. ! 29: * ! 30: * Revision 1.4 91/06/03 13:50:06 hal ! 31: * Add HDSETA. ! 32: * ! 33: * Revision 1.3 91/05/08 11:00:30 root ! 34: * Make number of heads - SD_HDS - patchable for Tandy. ! 35: * ! 36: * Revision 1.2 91/05/01 04:50:11 root ! 37: * Debug code and d_time/sw_active imbalance fixed. ! 38: * ! 39: * Revision 1.1 91/04/30 11:02:22 root ! 40: * Shipped with COH 3.1.0 ! 41: * ! 42: */ ! 43: ! 44: #include <sys/coherent.h> ! 45: #include <sys/fdisk.h> ! 46: #include <sys/hdioctl.h> ! 47: #include <sys/sdioctl.h> ! 48: #include <sys/buf.h> ! 49: #include <sys/con.h> ! 50: #include <sys/stat.h> ! 51: #ifdef _I386 ! 52: #include <sys/uproc.h> ! 53: #endif /* _I386 */ ! 54: #include <errno.h> ! 55: #include <sys/scsiwork.h> ! 56: #include <sys/typed.h> ! 57: #ifdef _I386 ! 58: #include <sys/mmu.h> ! 59: #endif /* _I386 */ ! 60: ! 61: #ifndef _I386 ! 62: extern saddr_t sds; ! 63: #endif /* _I386 */ ! 64: extern short n_atdr; ! 65: ! 66: /* ! 67: * Configurable parameters ! 68: * ! 69: * Adaptec ROM translates at 64 heads, except the Tandy version, which ! 70: * uses 16 heads. Kernel variable SD_HDS is patchable for this reason. ! 71: */ ! 72: #define DEF_AHA_HDS 64 ! 73: #define DEF_AHA_SPT 32 ! 74: ! 75: int SD_HDS = 0; ! 76: int SD_SPT = 0; ! 77: ! 78: #define NDRIVE (8 * 4) /* 8 SCSI ids and 4 LUNs */ ! 79: #define SDMAJOR 13 /* Major Device Number */ ! 80: ! 81: /* ! 82: * user configurable parameters ! 83: */ ! 84: int SDIRQ = 11; /* Interrupt */ ! 85: int SDBASE = 0x0330; /* Port base */ ! 86: int SDDMA = 5; /* Used for first party DMA */ ! 87: ! 88: /* ! 89: * LUN --------++ ! 90: * device macros Special-+ || ! 91: * minor device bits are of the form: 76543210 ! 92: * ||| || ! 93: * SCSI ID--+++ || ! 94: * Partition ----++ ! 95: * Partition mapping: ! 96: * ! 97: * Description Special Bit Partition # Device Type ! 98: * ----------- ----------- ----------- ------ ---- ! 99: * partition a 0 00 /dev/sd??a disk ! 100: * partition b 0 01 /dev/sd??b disk ! 101: * partition c 0 10 /dev/sd??c disk ! 102: * partition d 0 11 /dev/sd??d disk ! 103: * partition table 1 00 /dev/sd??x disk ! 104: * no rewind tape 1 01 /dev/sd??n tape ! 105: * UNALLOCATED 1 10 --- ???? ! 106: * rewind tape device 1 11 /dev/sd?? tape ! 107: */ ! 108: #define DRIVENO(minor) (((minor) >> 2) & 0x1F) /* SCSI ID + LUN */ ! 109: #define SCSIID(minor) (((minor) >> 4) & 0x7) /* SCSI ID */ ! 110: #define LUN(minor) (((minor) >> 2) & 0x3) /* Logical Unit Number */ ! 111: #define PARTITION(minor) ((minor) & 0x3) /* Partition */ ! 112: #define sdmkdev(maj, s, drv) makedev((maj), ((s)|((drv)<<2))) ! 113: ! 114: /* ! 115: * Driver configuration. ! 116: */ ! 117: void sdload(); ! 118: void sdunload(); ! 119: void sdopen(); ! 120: void sdclose(); ! 121: void sdread(); ! 122: void sdwrite(); ! 123: int sdioctl(); ! 124: void sdblock(); ! 125: int sdwatch(); ! 126: int nulldev(); ! 127: int nonedev(); ! 128: ! 129: CON sdcon = { ! 130: DFBLK|DFCHR, /* Flags */ ! 131: SDMAJOR, /* Major index */ ! 132: sdopen, /* Open */ ! 133: sdclose, /* Close */ ! 134: sdblock, /* Block */ ! 135: sdread, /* Read */ ! 136: sdwrite, /* Write */ ! 137: sdioctl, /* Ioctl */ ! 138: nulldev, /* Powerfail */ ! 139: sdwatch, /* Timeout */ ! 140: sdload, /* Load */ ! 141: sdunload /* Unload */ ! 142: }; ! 143: ! 144: /* ! 145: * host adapter routines ! 146: */ ! 147: int aha_load(); /* initialize host adapter, DMA */ ! 148: void aha_unload(); /* shutdown the host adapter */ ! 149: int aha_start(); /* see if there's work */ ! 150: int aha_command(); ! 151: ! 152: /* ! 153: * Partition Parameters - copied from disk. ! 154: * ! 155: * There are NPARTN positions for the user partitions in array PPARM, ! 156: * plus 1 additional position to span the entire drive. ! 157: * Array pparmp[] contains a pointer to a kalloc()'ed PPARM ! 158: * entry if the drive actually exists, is a disk drive and if someone ! 159: * has attmpted to read a partition table from the drive. ! 160: */ ! 161: typedef struct fdisk_s PPARM[NPARTN + 1]; /* 4 partitions + whole drive */ ! 162: static PPARM *pparmp[NDRIVE]; /* one per possible drive */ ! 163: #define WHOLE_DRIVE NPARTN /* index for whole drive */ ! 164: #define PNULL ((PPARM *)0) ! 165: ! 166: /* ! 167: * Per disk controller data. ! 168: * Only one host adapter; no more, no less. ! 169: */ ! 170: static ! 171: scsi_work_t sd; ! 172: ! 173: static BUF dbuf; /* For raw I/O */ ! 174: static int sw_active; ! 175: ! 176: /** ! 177: * ! 178: * void ! 179: * sdload() - load routine. ! 180: * ! 181: * Action: The controller is reset and the interrupt vector is grabbed. ! 182: * The drive characteristics are set up at this time. ! 183: */ ! 184: static void ! 185: sdload() ! 186: { ! 187: FIFO *ffp; ! 188: typed_space *tp; ! 189: extern typed_space boot_gift; ! 190: ! 191: /* ! 192: * Initialize Drive Controller. ! 193: */ ! 194: sw_active = 0; ! 195: if (aha_load(SDDMA, SDIRQ, SDBASE, &sd) < 0) { ! 196: SET_U_ERROR(ENXIO, "aha_load() failed."); ! 197: return; ! 198: } ! 199: ! 200: /* ! 201: * Set values for # of heads and # of sectors per track. ! 202: * ! 203: * AHA translation mode uses the same # of heads ! 204: * and the same # of sectors per track for all drives. ! 205: * ! 206: * If these values are already patched, leave them alone. ! 207: * Otherwise, look in the data area written by tboot. ! 208: * If nothing from tboot, use default values. ! 209: */ ! 210: if (SD_HDS == 0 || SD_SPT == 0) { ! 211: /* heads & spt not both patched */ ! 212: SD_HDS = DEF_AHA_HDS; ! 213: SD_SPT = DEF_AHA_SPT; ! 214: if (F_NULL != (ffp = fifo_open(&boot_gift, 0))) { ! 215: if (T_NULL != (tp = fifo_read(ffp))) { ! 216: BIOS_DISK *bdp = (BIOS_DISK *)tp->ts_data; ! 217: if ((T_BIOS_DISK == tp->ts_type) && ! 218: (n_atdr == bdp->dp_drive) ) { ! 219: /* got values from tboot */ ! 220: SD_HDS = bdp->dp_heads; ! 221: SD_SPT = bdp->dp_sectors; ! 222: } ! 223: } ! 224: fifo_close(ffp); ! 225: } ! 226: } ! 227: printf(" SD_HDS=%d SD_SPT=%d\n", SD_HDS, SD_SPT); ! 228: ! 229: /* aha_device_info(); */ /* enable after this gets fixed */ ! 230: } ! 231: ! 232: /** ! 233: * ! 234: * void ! 235: * sdunload() - unload routine. ! 236: */ ! 237: static void ! 238: sdunload() ! 239: { ! 240: register int i; ! 241: ! 242: if (sw_active > 0) ! 243: printf("aha154x: sdunload() athough %d active\n", sw_active); ! 244: aha_unload(SDIRQ); ! 245: for (i = 0; i < NDRIVE; ++i) ! 246: if (pparmp[i] != PNULL) ! 247: kfree(pparmp[i]); /* free any partition tables */ ! 248: } ! 249: ! 250: /* ! 251: * int ! 252: * sdgetpartitions(dev) - load partition table for specified drive ! 253: * ! 254: * - return 1 on success and 0 on failure ! 255: */ ! 256: int sdgetpartitions(dev) ! 257: dev_t dev; ! 258: { ! 259: register int i; ! 260: scsi_cmd_t sc; ! 261: unsigned char *buffer; ! 262: struct fdisk_s *fdp; ! 263: int d = DRIVENO(minor(dev)); ! 264: ! 265: pparmp[d] = kalloc(sizeof *pparmp[0]); ! 266: fdp = (struct fdisk_s *) pparmp[d]; /* point to first entry */ ! 267: #ifdef _I386 ! 268: buffer = palloc(36+1); ! 269: #else /* _I386 */ ! 270: buffer = kalloc(36+1); ! 271: #endif /* _I386 */ ! 272: ! 273: if (buffer == NULL || pparmp[d] == PNULL) { ! 274: printf("aha154x: out of kernel memory\n"); ! 275: #ifdef _I386 ! 276: SET_U_ERROR(ENOMEM, "aha154x: out of kernel memory"); ! 277: #else /* _I386 */ ! 278: u.u_error = EKSPACE; ! 279: #endif /* _I386 */ ! 280: return 0; ! 281: } ! 282: kclear(pparmp[d], sizeof *pparmp[0]); ! 283: sc.unit = d; ! 284: sc.block = 0L; ! 285: sc.blklen = 0; ! 286: ! 287: #ifdef _I386 ! 288: /* sc.buffer is a virtual-physical address (Ciaran Space.) */ ! 289: sc.buffer = vtovp(buffer); ! 290: #else /* _I386 */ ! 291: sc.buffer = VTOP2(buffer, sds); ! 292: #endif /* _I386 */ ! 293: ++drvl[SDMAJOR].d_time; ! 294: ! 295: sc.cmd = ScmdREADCAPACITY; ! 296: sc.buflen = 8; ! 297: ! 298: for(i = 0; i < sc.buflen; ++i) ! 299: buffer[i] = 0; ! 300: ! 301: /* ! 302: * If we call aha_command() only once we get a capacity of ! 303: * 0. All ScmdREADCAPACITY calls after the first return a ! 304: * correct answer. ! 305: * ! 306: * This may be a bug in the aha154x. ! 307: */ ! 308: aha_command(&sc); ! 309: aha_command(&sc); ! 310: ! 311: T_PIGGY( 0x20000, { ! 312: printf("buffer ="); ! 313: for(i = 0; i < sc.buflen; ++i) ! 314: printf(" %x", buffer[i]); ! 315: printf("\n"); ! 316: }); ! 317: ! 318: sc.block = (buffer[0]<<8) | buffer[1]; ! 319: sc.block <<= 16; ! 320: sc.block |= (buffer[2]<<8) | buffer[3]; ! 321: ! 322: sc.blklen = (buffer[6]<<8) | buffer[7]; ! 323: ! 324: T_PIGGY( 0x20000, { ! 325: printf("SCSI %D. blocks of size %d\n", sc.block, sc.blklen); ! 326: } ); ! 327: ! 328: #ifdef _I386 ! 329: pfree(buffer); ! 330: #else /* _I386 */ ! 331: kfree(buffer); ! 332: #endif /* _I386 */ ! 333: fdp[WHOLE_DRIVE].p_size = sc.block; ! 334: if (0 == fdp[WHOLE_DRIVE].p_size) { ! 335: /* ! 336: * If we are just opening this drive, make it so we can ! 337: * read the first block without an error. ! 338: */ ! 339: fdp[WHOLE_DRIVE].p_size = 1; ! 340: } ! 341: ! 342: --drvl[SDMAJOR].d_time; ! 343: return fdisk(sdmkdev(major(dev), SDEV, d), pparmp[d]); ! 344: } ! 345: ! 346: /** ! 347: * ! 348: * void ! 349: * sdopen(dev, mode) ! 350: * dev_t dev; ! 351: * int mode; ! 352: * ! 353: * Input: dev = disk device to be opened. ! 354: * mode = access mode [IPR,IPW, IPR+IPW]. ! 355: * ! 356: * Action: Validate the minor device. ! 357: * Update the paritition table if necessary. ! 358: */ ! 359: static void ! 360: sdopen(dev, mode) ! 361: register dev_t dev; ! 362: { ! 363: register int p; /* partition */ ! 364: register int d; /* drive (SCSI ID + LUN) */ ! 365: struct fdisk_s *fdp; /* one partition entry */ ! 366: ! 367: if (minor(dev) & SDEV) { ! 368: if (PARTITION(minor(dev)) != 0) { /* tape device ? */ ! 369: /* not yet! */ ! 370: SET_U_ERROR(ENXIO, "No tape yet"); ! 371: devmsg(dev, "No tape yet"); ! 372: } else { ! 373: ++drvl[SDMAJOR].d_time; ! 374: ++sw_active; ! 375: } ! 376: return; ! 377: } ! 378: ! 379: d = DRIVENO(minor(dev)); ! 380: p = PARTITION(minor(dev)); ! 381: ! 382: /* ! 383: * If partition not defined read partition characteristics. ! 384: */ ! 385: if (pparmp[d] == PNULL) /* no entry yet for this drive ? */ ! 386: if (!sdgetpartitions(dev)) { ! 387: SET_U_ERROR(ENXIO, "sdgetpartitions() failed."); ! 388: return; ! 389: } ! 390: /* ! 391: * Ensure partition lies within drive boundaries and is non-zero size. ! 392: */ ! 393: fdp = (struct fdisk_s *) pparmp[d]; ! 394: if ((fdp[p].p_base+fdp[p].p_size) > fdp[WHOLE_DRIVE].p_size) { ! 395: #ifdef _I386 ! 396: SET_U_ERROR(EINVAL, "Partition exceeds drive size."); ! 397: #else /* _I386 */ ! 398: u.u_error = EBADFMT; ! 399: #endif /* _I386 */ ! 400: } else if (fdp[p].p_size == 0) { ! 401: SET_U_ERROR(ENODEV, "No such partition."); ! 402: } else { ! 403: ++drvl[SDMAJOR].d_time; ! 404: ++sw_active; ! 405: } ! 406: } ! 407: ! 408: void sdclose(dev) ! 409: { ! 410: --drvl[SDMAJOR].d_time; ! 411: --sw_active; ! 412: } ! 413: ! 414: /** ! 415: * ! 416: * void ! 417: * sdread(dev, iop) - write a block to the raw disk ! 418: * dev_t dev; ! 419: * IO * iop; ! 420: * ! 421: * Input: dev = disk device to be written to. ! 422: * iop = pointer to source I/O structure. ! 423: * ! 424: * Action: Invoke the common raw I/O processing code. ! 425: */ ! 426: static void ! 427: sdread(dev, iop) ! 428: dev_t dev; ! 429: IO *iop; ! 430: { ! 431: ioreq(&dbuf, iop, dev, BREAD, BFRAW|BFBLK|BFIOC); ! 432: } ! 433: ! 434: /** ! 435: * ! 436: * void ! 437: * sdwrite(dev, iop) - write a block to the raw disk ! 438: * dev_t dev; ! 439: * IO * iop; ! 440: * ! 441: * Input: dev = disk device to be written to. ! 442: * iop = pointer to source I/O structure. ! 443: * ! 444: * Action: Invoke the common raw I/O processing code. ! 445: */ ! 446: static void ! 447: sdwrite(dev, iop) ! 448: dev_t dev; ! 449: IO *iop; ! 450: { ! 451: ioreq(&dbuf, iop, dev, BWRITE, BFRAW|BFBLK|BFIOC); ! 452: } ! 453: ! 454: /** ! 455: * ! 456: * int ! 457: * sdioctl(dev, cmd, arg) ! 458: * dev_t dev; ! 459: * int cmd; ! 460: * char * vec; ! 461: * ! 462: * Input: dev = disk device to be operated on. ! 463: * cmd = input/output request to be performed. ! 464: * vec = (pointer to) optional argument. ! 465: * ! 466: * Action: Validate the minor device. ! 467: * Update the paritition table if necessary. ! 468: */ ! 469: static int ! 470: sdioctl(dev, cmd, vec) ! 471: register dev_t dev; ! 472: int cmd; ! 473: char * vec; ! 474: { ! 475: int i; /* Integer for abusing. */ ! 476: int d; /* Drive number. */ ! 477: hdparm_t hdparm; ! 478: struct fdisk_s *fdp; ! 479: int do_getpt = 0; /* 1 if need to call sdgetpartitions() */ ! 480: ! 481: d = DRIVENO(minor(dev)); ! 482: ! 483: /* ! 484: * Identify input/output request. ! 485: */ ! 486: switch (cmd) { ! 487: ! 488: case HDGETA: ! 489: /* ! 490: * If haven't loaded partition table yet for this drive, ! 491: * try to do it now. Note sdgetpartitions() will fail ! 492: * if there is a new drive (e.g. no signature). But all ! 493: * we need is allocation of pparmp[d] and capacity read ! 494: * properly from the drive. ! 495: */ ! 496: if (pparmp[d] == PNULL) { ! 497: do_getpt = 1; /* REALLY just want Read Capacity */ ! 498: ! 499: i = sdgetpartitions(dev); ! 500: ! 501: if (pparmp[d] == NULL) { ! 502: SET_U_ERROR(ENXIO, "sdgetparitions() failed."); ! 503: return -1; ! 504: } ! 505: } ! 506: fdp = (struct fdisk_s *) pparmp[d]; ! 507: *(short *)&hdparm.landc[0] = ! 508: *(short *)&hdparm.ncyl[0] = fdp[WHOLE_DRIVE].p_size ! 509: / (SD_HDS * SD_SPT); ! 510: hdparm.nhead = SD_HDS; ! 511: hdparm.nspt = SD_SPT; ! 512: kucopy(&hdparm, vec, sizeof hdparm); ! 513: /* ! 514: * I know it's ugly. But it gets around startup Catch-22. ! 515: * ! 516: * The fdisk command needs HDGETA. HDGETA invokes ! 517: * sdgetpartitions(), but we want to call it again ! 518: * after the partition table has been created by the fdisk ! 519: * command. ! 520: */ ! 521: if (do_getpt) { ! 522: kfree(pparmp[d]); ! 523: pparmp[d] = PNULL; /* force re-read of p. table */ ! 524: } ! 525: return 0; ! 526: case HDSETA: ! 527: /* ! 528: * Set hard disk attributes. ! 529: */ ! 530: fdp = (struct fdisk_s *) pparmp[d]; ! 531: ukcopy(vec, &hdparm, sizeof hdparm); ! 532: SD_HDS = hdparm.nhead; ! 533: SD_SPT = hdparm.nspt; ! 534: fdp[WHOLE_DRIVE].p_size = ! 535: (long)(*(short *)&hdparm.ncyl[0]) ! 536: * (long)SD_HDS * (long)SD_SPT; ! 537: ! 538: return 0; ! 539: case SCSI_HA_CMD: ! 540: return aha_ioctl(cmd, vec); ! 541: case SCSI_CMD: ! 542: return 0; ! 543: case SCSI_CMD_IN: ! 544: return 0; ! 545: case SCSI_CMD_OUT: ! 546: return 0; ! 547: ! 548: default: ! 549: SET_U_ERROR( EINVAL, "Illegal SCSI ioctl command." ); ! 550: return -1; ! 551: } ! 552: } ! 553: ! 554: /** ! 555: * ! 556: * void ! 557: * sdblock(bp) - queue a block to the disk ! 558: * ! 559: * Input: bp = pointer to block to be queued. ! 560: * ! 561: * Action: Queue a block to the disk. ! 562: * Make sure that the transfer is within the disk partition. ! 563: */ ! 564: static void ! 565: sdblock(bp) ! 566: register BUF *bp; ! 567: { ! 568: register scsi_work_t *sw; ! 569: register int s; ! 570: struct fdisk_s *fdp; ! 571: ! 572: int p = PARTITION(minor(bp->b_dev)); ! 573: int drv = DRIVENO(minor(bp->b_dev)); ! 574: ! 575: if (minor(bp->b_dev) & SDEV) ! 576: p = WHOLE_DRIVE; ! 577: bp->b_resid = bp->b_count; ! 578: ! 579: fdp = (struct fdisk_s *) pparmp[drv]; ! 580: ! 581: /* ! 582: * Range check disk region. ! 583: */ ! 584: if (pparmp[drv] == PNULL) { ! 585: if (p == WHOLE_DRIVE) { ! 586: #if 0 ! 587: /* Why did we only allow people to access the first block of WHOLE_DRIVE? ! 588: in cases where there was not a valid partition table? */ ! 589: if ((bp->b_bno != 0) || (bp->b_count != BSIZE)) { ! 590: bp->b_flag |= BFERR; ! 591: bdone(bp); ! 592: return; ! 593: } ! 594: #endif ! 595: } else { ! 596: printf("aha154x: no partition table\n"); ! 597: bp->b_flag |= BFERR; ! 598: bdone(bp); ! 599: return; ! 600: } ! 601: } else if ((bp->b_bno + (bp->b_count/BSIZE)) > fdp[p].p_size) { ! 602: ! 603: T_PIGGY( 0x20000 , { ! 604: printf("\n(bp->b_bno: %x + (bp->b_count: %x /BSIZE): %x): %x > ", ! 605: bp->b_bno, bp->b_count, (bp->b_count/BSIZE), ! 606: (bp->b_bno + (bp->b_count/BSIZE))); ! 607: printf(" fdp[p].p_size: %x\n", fdp[p].p_size); ! 608: } ); ! 609: ! 610: bp->b_flag |= BFERR; ! 611: bdone(bp); ! 612: return; ! 613: } ! 614: ! 615: bp->b_actf = NULL; ! 616: #ifdef _I386 ! 617: sw = (scsi_work_t *)palloc(sizeof(*sw)); ! 618: T_PIGGY(0x100000, printf("sw(%x)", sw); ); ! 619: #else /* _I386 */ ! 620: sw = (scsi_work_t *)kalloc(sizeof(*sw)); ! 621: #endif /* _I386 */ ! 622: if (sw == (scsi_work_t *)0) { ! 623: printf("aha154x: out of kernel memory\n"); ! 624: bp->b_flag |= BFERR; ! 625: bdone(bp); ! 626: return; ! 627: } ! 628: sw->sw_bp = bp; ! 629: sw->sw_drv = drv; ! 630: sw->sw_type = 0; ! 631: if (p != WHOLE_DRIVE) ! 632: sw->sw_bno = fdp[p].p_base + bp->b_bno; ! 633: else ! 634: sw->sw_bno = bp->b_bno; ! 635: sw->sw_retry = 1; ! 636: ! 637: T_PIGGY( 0x20000, ! 638: printf("sdblock: drv %x bno %x:%x bp=%x, flag = %x\n", ! 639: drv, (long)sw->sw_bno, bp, bp->b_flag); ! 640: ); ! 641: ! 642: s = sphi(); ! 643: if (sd.sw_actf == NULL) ! 644: sd.sw_actf = sw; ! 645: else ! 646: sd.sw_actl->sw_actf = sw; ! 647: sd.sw_actl = sw; ! 648: spl(s); ! 649: ! 650: aha_start(); ! 651: } ! 652: ! 653: sdwatch() ! 654: { ! 655: register int i; ! 656: ! 657: if (0 != (i = aha_start())) { ! 658: T_PIGGY( 0x20000, printf("sdwatch: started %d actions\n", i); ); ! 659: } ! 660: ! 661: if ( 0!= (i = aha_completed())) { ! 662: T_PIGGY( 0x20000, printf("sdwatch: completed %d actions\n", i); ); ! 663: } ! 664: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.