From: CSRG Date: Wed, 2 Nov 1983 08:51:35 +0000 (-0800) Subject: BSD 4_2 development X-Git-Tag: BSD-4_3^2~17 X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/commitdiff_plain/43c6572f5d363d6c31af757213df50bf1fc10530 BSD 4_2 development Work on file usr/src/sys/vaxuba/rk.c Work on file usr/src/sys/vax/tu.c Work on file usr/src/new/new/spms/bin/pinstall Synthesized-from: CSRG/cd1/4.2 --- diff --git a/usr/src/new/new/spms/bin/pinstall b/usr/src/new/new/spms/bin/pinstall new file mode 100755 index 0000000000..22f3960aba --- /dev/null +++ b/usr/src/new/new/spms/bin/pinstall @@ -0,0 +1,60 @@ +#! /bin/csh -f +# +# Install SPMS Software Project Management System +# + +if (!(-d /usr/new)) then + echo "/usr/new: No such directory" + exit(1) +endif +if (!(-d /usr/new/lib)) then + echo "/usr/new/lib: No such directory" + exit(1) +endif + +# pexec relies upon the PROJECT environment variable +# which MUST be set to the root directory of the project. +cd .. +setenv PROJECT $cwd + +set PATH = (/usr/new /usr/ucb /bin /usr/bin) + +#install libraries +foreach p (libhash libpdb libslist libspms libtree) + cd $PROJECT/src/lib/$p/src + sed -e '/###/,$d' < Makefile > $$Mf + mv $$Mf Makefile + make install +end + +#install programs in /usr/new +cd $PROJECT/src/bin +foreach p (*) + cd $PROJECT/src/bin/$p + sed -e '/###/,$d' < Makefile > $$Mf + mv $$Mf Makefile + make install +end +rehash + +# reconstruct Makefile dependencies and update +pexec -Tupdate "mkmf; make update" + +# clean up +pexec -Tclean "make clean" + +#install manual pages in /usr/man/mann +cp $PROJECT/man/mann/*.n /usr/man/mann + +# create ~root^ and ~root^usr projects +cd / +mkproject -d -Tproject ~root^ +cd /usr/src +mkproject -Nusr -Tproject ~root^.../usr/src > /dev/null << 'EOF' +/usr source +'EOF' + +# connect spms project to ~root^usr +mkproject -d ~root^usr/new/spms + +exit(0) diff --git a/usr/src/sys/vax/tu.c b/usr/src/sys/vax/tu.c new file mode 100644 index 0000000000..3b6df410c8 --- /dev/null +++ b/usr/src/sys/vax/tu.c @@ -0,0 +1,725 @@ +/* %M 6.2 83/10/12 */ + +#if defined(VAX750) || defined(VAX730) +/* + * TU58 DECtape II device driver + * + * TU58 console cassette driver (for VAX-11/750 or VAX-11/730). + * The TU58 is treated as a block device (only). Error detection and + * recovery is not extensive, but sufficient for most situations. It is + * assumed that the TU58 will follow the RSP (or MRSP) protocol exactly, + * very few protocol errors are checked for. It is also assumed that + * the 730 uses Modified RSP (MRSP), while the 750 may use either RSP + * or MRSP depending on whether defined(MRSP) is true or not. + * In the case of a 750 without MRSP, the only way for the CPU to + * keep up with the tu58 is to lock out virtually everything else. + * + * NOTE: Reading large amounts of data from the tu58 is likely + * to crash your system if you are running multiuser. + * ******FOR SINGLE USER USE ONLY***** + */ +#include "../h/param.h" +#include "../h/systm.h" +#include "../h/buf.h" +#include "../h/conf.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/kernel.h" + +#include "../vax/cpu.h" +#include "../vax/mtpr.h" +#include "../vax/rsp.h" + +#define printd if(tudebug) printf +#ifdef printd +int tudebug; /* printd */ +#endif printd + +#define NTU ((cpu == VAX_750) ? 1 : 2) +#define DNUM 01 /* mask for drive number (should match NTU) */ +#define NTUBLK 512 /* number of blocks on a TU58 cassette */ +#define WRV 02 /* bit in minor dev => write w. read verify */ +#define NTUQ 2 /* # of blocks which can be queued up */ +#define TUIPL ((cpu == VAX_750) ? 0x17 : 0x14) + +#ifndef MRSP +#define MRSP (cpu != VAX_750) +#endif + +/* + * State information + */ +struct tu { + u_char *tu_rbptr; /* pointer to buffer for read */ + int tu_rcnt; /* how much to read */ + u_char *tu_wbptr; /* pointer to buffer for write */ + int tu_wcnt; /* how much to write */ + int tu_state; /* current state of tansfer operation */ + int tu_flag; /* read in progress flag */ + char *tu_addr; /* real buffer data address */ + int tu_count; /* real requested count */ + int tu_serrs; /* count of soft errors */ + int tu_cerrs; /* count of checksum errors */ + int tu_herrs; /* count of hard errors */ + char tu_dopen[2]; /* drive is open */ +} tu; + + +/* + * Device register bits + */ +#define READY 0200 /* transmitter ready */ +#define DONE 0200 /* receiver done */ +#define IE 0100 /* interrupt enable */ +#define BREAK 1 /* send break */ + +struct packet tucmd; /* a command sent to the TU58 */ +struct packet tudata; /* a command or data returned from TU58 */ + +char *tustates[TUS_NSTATES] = { + "INIT1", "INIT2", "IDLE", "SENDH", "SENDD", "SENDC", "SENDR", + "SENDW", "GETH", "GETD", "GETC", "GET", "WAIT", "RCVERR", "CHKERR" +}; + +u_char tunull[2] = { 0, 0 }; /* nulls to send for initialization */ +u_char tuinit[2] = { TUF_INITF, TUF_INITF }; /* inits to send */ +static char tu_pcnt[2]; /* pee/vee counters */ +int tutimer = 0; +int tuwake(); +struct buf tutab; /* I/O queue header */ + +/* + * Open the TU58 + */ +/*ARGSUSED*/ +tuopen(dev, flag) +{ + extern int tuwatch(); + register s; + +#ifdef lint + turintr(); tuwintr(); +#endif + if ((minor(dev)&DNUM) >= NTU) + return (ENXIO); + if (tu.tu_dopen[minor(dev)&DNUM]) + return (EBUSY); + if (tutimer++ == 0) + timeout(tuwatch, (caddr_t)0, hz); + + s = splx(TUIPL); + tu.tu_dopen[minor(dev)&DNUM]++; + /* + * If the cassette's already initialized, + * just enable interrupts and return. + */ + if (tu.tu_state == TUS_IDLE) { + mtpr(CSRS, IE); + goto ok; + } + + /* + * Must initialize, reset the cassette + * and wait for things to settle down. + */ + tureset(); + sleep((caddr_t)&tu, PZERO+1); + tutab.b_active = NULL; + if (tu.tu_state != TUS_IDLE) { + tu.tu_state = TUS_INIT1; + tu.tu_dopen[minor(dev)&DNUM] = 0; + tu.tu_rcnt = tu.tu_wcnt = 0; + mtpr(CSTS, 0); + mtpr(CSRS, 0); + splx(s); + return (EIO); + } +ok: + splx(s); + return (0); +} + +/* + * Close the TU58, but make sure all + * outstanding i/o is complete first.. + */ +tuclose(dev, flag) + dev_t dev; + int flag; +{ + int s, unit = minor(dev); + struct buf *bp, *last = NULL; + + s = splx(TUIPL); + while (tu_pcnt[unit]) + sleep(&tu_pcnt[unit], PRIBIO); + /* + * No more writes are pending, scan the + * buffer queue for oustanding reads from + * this unit. + */ + for (bp = tutab.b_actf; bp; bp = bp->b_actf) { + if (bp->b_dev == dev) + last = bp; + } + if (last) { + last->b_flags |= B_CALL; + last->b_iodone = tuwake; + sleep((caddr_t)last, PRIBIO); + } + tu.tu_dopen[unit&DNUM] = 0; + if (!tu.tu_dopen[0] && !tu.tu_dopen[1]) { + tutimer = 0; + mtpr(CSRS, 0); + tu.tu_flag = 0; + } + splx(s); +} + +tuwake(bp) + struct buf *bp; +{ + wakeup(bp); +} + +/* + * Reset the TU58 + */ +tureset() +{ + + mtpr(CSRS, 0); + tu.tu_state = TUS_INIT1; + tu.tu_wbptr = tunull; + tu.tu_wcnt = sizeof (tunull); + tucmd.pk_flag = TUF_CMD; + tucmd.pk_mcount = sizeof (tucmd) - 4; + tucmd.pk_mod = 0; + tucmd.pk_seq = 0; + tucmd.pk_sw = MRSP ? TUSW_MRSP : 0; + tutab.b_active++; + mtpr(CSTS, IE | BREAK); + tuxintr(); /* start output */ +} + +/* + * Strategy routine for block I/O + */ +tustrategy(bp) + register struct buf *bp; +{ + register int s; + + if (bp->b_blkno >= NTUBLK) { + bp->b_flags |= B_ERROR; + iodone(bp); + return; + } + if ((bp->b_flags&B_READ) == 0) + tu_pee(&tu_pcnt[minor(bp->b_dev)&DNUM]); + bp->av_forw = NULL; + s = splx(TUIPL); + if (tutab.b_actf == NULL) + tutab.b_actf = bp; + else + tutab.b_actl->av_forw = bp; + tutab.b_actl = bp; + if (tutab.b_active == NULL) + tustart(); + splx(s); +} + +/* + * Start the transfer + */ +tustart() +{ + register struct buf *bp; + int s; + + if ((bp = tutab.b_actf) == NULL) + return; + s = splx(TUIPL); + if (tu.tu_state != TUS_IDLE) { + tureset(); + splx(s); + return; + } + tutab.b_active++; + tutab.b_errcnt = 0; + tucmd.pk_op = bp->b_flags&B_READ ? TUOP_READ : TUOP_WRITE; + tucmd.pk_mod = ((bp->b_flags&B_READ) == 0 && (minor(bp->b_dev)&WRV)) ? + TUMD_WRV : 0; + tucmd.pk_unit = (minor(bp->b_dev)&DNUM); + tucmd.pk_sw = MRSP ? TUSW_MRSP : 0; + tucmd.pk_count = tu.tu_count = bp->b_bcount; + tucmd.pk_block = bp->b_blkno; + tucmd.pk_chksum = + tuchk(*((short *)&tucmd), (u_short *)&tucmd.pk_op, + (int)tucmd.pk_mcount); + tu.tu_state = bp->b_flags&B_READ ? TUS_SENDR : TUS_SENDW; + tu.tu_addr = bp->b_un.b_addr; + tu.tu_wbptr = (u_char *)&tucmd; + tu.tu_wcnt = sizeof (tucmd); + tuxintr(); + splx(s); +} + +/* + * TU58 receiver interrupt + */ +turintr() +{ + register struct buf *bp; + register int c; + + c = mfpr(CSRD)&0xff; + if (MRSP) { + while ((mfpr(CSTS)&READY) == 0) + ; + mtpr(CSTD, TUF_CONT); /* ACK */ + if (tu.tu_rcnt) { + *tu.tu_rbptr++ = c; + if (--tu.tu_rcnt) + return; + } + } + + /* + * Switch on the state of the transfer. + */ + switch(tu.tu_state) { + + /* + * Probably an overrun error, + * cannot happen if MRSP is used + */ + case TUS_RCVERR: + mtpr(CSRS, 0); /* flush */ + printf("overrun error, transfer restarted\n"); /* DEBUG */ + tu.tu_serrs++; + tu_restart(); + break; + + /* + * If we get an unexpected "continue", + * start all over again... + */ + case TUS_INIT2: + tu.tu_state = c == TUF_CONT ? TUS_IDLE : TUS_INIT1; + tu.tu_flag = 0; + wakeup((caddr_t)&tu); + tustart(); + break; + + /* + * Only transition from this state + * is on a "continue", so if we don't + * get it, reset the world. + */ + case TUS_WAIT: /* waiting for continue */ + switch(c) { + case TUF_CONT: /* got the expected continue */ + tu.tu_flag = 0; + tudata.pk_flag = TUF_DATA; + tudata.pk_mcount = MIN(128, tu.tu_count); + tudata.pk_chksum = + tuchk(*((short *)&tudata), (caddr_t)tu.tu_addr, + (int)tudata.pk_mcount); + tu.tu_state = TUS_SENDH; + tu.tu_wbptr = (u_char *)&tudata; + tu.tu_wcnt = 2; + tuxintr(); + break; + + case TUF_CMD: /* sending us an END packet...error */ + tu.tu_state = TUS_GET; + tu.tu_rbptr = (u_char *) &tudata; + tu.tu_rcnt = sizeof (tudata) - 1; + tu.tu_flag = 1; + mtpr (CSTS, 0); + *tu.tu_rbptr = c; + break; + + case TUF_INITF: + tureset(); + break; + + default: /* something random...bad news */ + tu.tu_state = TUS_INIT1; + break; + } + break; + + case TUS_SENDW: + if (c != TUF_CONT && c != TUF_INITF) + goto bad; + tureset(); + break; + + /* + * Got header, now get data; amount to + * fetch is included in packet. + */ + case TUS_GETH: + if (MRSP && (tudata.pk_flag == TUF_DATA)) + tu.tu_rbptr = (u_char *)tu.tu_addr; + tu.tu_rcnt = tudata.pk_mcount; + tu.tu_state = TUS_GETD; + break; + + /* + * Got the data, now fetch the checksum. + */ + case TUS_GETD: + tu.tu_rbptr = (u_char *)&tudata.pk_chksum; + tu.tu_rcnt = sizeof (tudata.pk_chksum); + tu.tu_state = TUS_GETC; + break; + + case TUS_CHKERR: /* from tudma only */ + tu.tu_cerrs++; + goto tus_get; + + case TUS_GET: + if (MRSP) + /* + * The checksum has already been calculated and + * verified in the pseudo DMA routine + */ + goto tus_get; + + case TUS_GETC: + /* got entire packet */ + if (tudata.pk_chksum != + tuchk(*((short *)&tudata), (u_short *) + (tudata.pk_flag == TUF_DATA ? + (u_short *) tu.tu_addr : (u_short *)&tudata.pk_op), + (int)tudata.pk_mcount)) + tu.tu_cerrs++; +tus_get: + if (tudata.pk_flag == TUF_DATA) { + /* data packet, advance to next */ + tu.tu_addr += tudata.pk_mcount; + tu.tu_count -= tudata.pk_mcount; + tu.tu_state = TUS_GETH; + tu.tu_rbptr = (u_char *)&tudata; /* next packet */ + tu.tu_rcnt = 2; + } else if (tudata.pk_flag==TUF_CMD && tudata.pk_op==TUOP_END) { + /* end packet, idle and reenable transmitter */ + tu.tu_state = TUS_IDLE; + tu.tu_flag = 0; + mtpr(CSTS, IE); + printd("ON "); + if ((bp = tutab.b_actf) == NULL) { + printf("tu%d: no bp, active %d\n", + tudata.pk_unit, tutab.b_active); + tustart(); + return; + } + if (tudata.pk_mod > 1) { /* hard error */ + bp->b_flags |= B_ERROR; + tu.tu_herrs++; + printf("tu%d: hard error bn%d,", + minor(bp->b_dev)&DNUM, bp->b_blkno); + printf(" pk_mod %o\n", tudata.pk_mod&0377); + } else if (tudata.pk_mod != 0) /* soft error */ + tu.tu_serrs++; + tutab.b_active = NULL; + tutab.b_actf = bp->av_forw; + bp->b_resid = tu.tu_count; + if ((bp->b_flags&B_READ) == 0) + tu_vee(&tu_pcnt[minor(bp->b_dev)&DNUM]); + iodone(bp); + tustart(); + } else { + /* + * Neither data nor end: data was lost + * somehow, restart the transfer + */ + mtpr(CSRS, 0); /* flush the rest */ + tu_restart(); + tu.tu_serrs++; + } + break; + + case TUS_IDLE: + case TUS_INIT1: + break; + + default: +bad: + if (c == TUF_INITF) { + printf("tu%d protocol error, state=", + (int)tudata.pk_unit); + printstate(tu.tu_state); + printf(", op=%x, cnt=%d, block=%d\n", + tucmd.pk_op, tucmd.pk_count, tucmd.pk_block); + tutab.b_active = NULL; + if (bp = tutab.b_actf) { + bp->b_flags |= B_ERROR; + tutab.b_actf = bp->av_forw; + if ((bp->b_flags&B_READ) == 0) + tu_vee(&tu_pcnt[minor(bp->b_dev)&DNUM]); + iodone(bp); + } + tu.tu_state = TUS_INIT1; + } else { + printf("tu%d: receive state error, state=", + (int)tudata.pk_unit); + printstate(tu.tu_state); + printf(", byte=%x\n", c & 0xff); + if (tutab.b_actf) + tu_restart(); + else + wakeup((caddr_t)&tu); + } + } +} + +/* + * TU58 transmitter interrupt + */ +tuxintr() +{ + +top: + if (tu.tu_wcnt) { + /* still stuff to send, send one byte */ + while ((mfpr(CSTS) & READY) == 0) + ; + mtpr(CSTD, *tu.tu_wbptr++); + tu.tu_wcnt--; + return; + } + + /* + * Last message byte was sent out. + * Switch on state of transfer. + */ + if (tudebug) { + printf("tuxintr: state="); + printstate(tu.tu_state); + } + switch(tu.tu_state) { + + /* + * Two nulls have been sent, remove break, and send inits + */ + case TUS_INIT1: + mtpr(CSTS, IE); + printd("ON2 "); + tu.tu_state = TUS_INIT2; + tu.tu_wbptr = tuinit; + tu.tu_wcnt = sizeof (tuinit); + goto top; + + /* + * Inits have been sent, wait for a continue msg. + */ + case TUS_INIT2: + (void) mfpr(CSRD); + mtpr(CSRS, IE); + tu.tu_flag = 1; + break; + + /* + * Read cmd packet sent, get ready for data + */ + case TUS_SENDR: + tu.tu_state = TUS_GETH; + tu.tu_rbptr = (u_char *)&tudata; + tu.tu_rcnt = 2; + tu.tu_flag = 1; + mtpr(CSTS, 0); /* disable transmitter interrupts */ + printd("OFF "); + break; + + /* + * Write cmd packet sent, wait for continue + */ + case TUS_SENDW: + tu.tu_state = TUS_WAIT; + tu.tu_flag = 1; + if ((mfpr(CSRS)&IE) == 0) { + printf("NO IE\n"); + mtpr(CSRS, IE); + } + break; + + /* + * Header sent, send data. + */ + case TUS_SENDH: + tu.tu_state = TUS_SENDD; + tu.tu_wbptr = (u_char *)tu.tu_addr; + tu.tu_wcnt = tudata.pk_mcount; + goto top; + + /* + * Data sent, follow with checksum. + */ + case TUS_SENDD: + tu.tu_state = TUS_SENDC; + tu.tu_wbptr = (u_char *)&tudata.pk_chksum; + tu.tu_wcnt = sizeof tudata.pk_chksum; + goto top; + + /* + * Checksum sent, wait for continue. + */ + case TUS_SENDC: + /* + * Updata buffer address and count. + */ + tu.tu_addr += tudata.pk_mcount; + tu.tu_count -= tudata.pk_mcount; + if (tu.tu_count) { + tu.tu_state = TUS_WAIT; + tu.tu_flag = 1; + break; + } + + /* + * End of transmission, get ready for end packet. + */ + tu.tu_state = TUS_GET; + tu.tu_rbptr = (u_char *)&tudata; + tu.tu_rcnt = sizeof (tudata); + tu.tu_flag = 1; + mtpr(CSTS, 0); + printd("OFF2 "); + break; + + /* + * Random interrupt, probably from MRSP ACK + */ + case TUS_IDLE: + + default: + break; + + } + if (tudebug) { + printd(" new tu_state="); + printstate(tu.tu_state); + } +} + +/* + * Compute checksum TU58 fashion + */ +#ifdef lint +tuchk(word, cp, n) + register word; + register unsigned short *cp; + int n; +{ + register int c = n >> 1; + register long temp; + + do { + temp = *cp++; /* temp, only because vax cc won't *r++ */ + word += temp; + } while (--c > 0); + if (n & 1) + word += *(unsigned char *)cp; + while (word & 0xffff0000) + word = (word & 0xffff) + ((word >> 16) & 0xffff); + return (word); +} +#else +tuchk(word0, wp, n) + register int word0; /* r11 */ + register char *wp; /* r10 */ + register int n; /* r9 */ +{ + asm("loop:"); + asm(" addw2 (r10)+,r11"); /* add a word to sum */ + asm(" adwc $0,r11"); /* add in carry, end-around */ + asm(" acbl $2,$-2,r9,loop"); /* done yet? */ + asm(" blbc r9,ok"); /* odd byte count? */ + asm(" movzbw (r10),r10"); /* yes, get last byte */ + asm(" addw2 r10,r11"); /* add it in */ + asm(" adwc $0,r11"); /* and the carry */ + asm("ok:"); + asm(" movl r11,r0"); /* return sum */ +} +#endif + +tuwatch() +{ + register int s; + register struct buf *bp; + + if (tutimer == 0) + return; + + if (tu.tu_flag == 0) { /* if no read in progress - skip */ + timeout(tuwatch, (caddr_t)0, hz); + return; + } + if (tu.tu_flag++ <= 40) { + timeout(tuwatch, (caddr_t)0, hz); + return; + } + printf("tu%d: read stalled\n", tudata.pk_unit); +#ifdef TUDEBUG + printf("%X %X %X %X %X %X %X %X\n", tu.tu_rbptr, tu.tu_rcnt, + tu.tu_wbptr, tu.tu_wcnt, tu.tu_state, tu.tu_flag, + tu.tu_addr, tu.tu_count); +#endif + s = splx(TUIPL); + tu.tu_flag = 0; + (void) mfpr(CSRD); + mtpr(CSRS, IE); /* in case we were flushing */ + mtpr(CSTS, IE); + tu.tu_state = TUS_IDLE; + if (!tutab.b_active) { + wakeup((caddr_t)&tu); + goto retry; + } + if (++tutab.b_errcnt <= 1) { + tustart(); + goto retry; + } + if (bp = tutab.b_actf) { + bp->b_flags |= B_ERROR; + if ((bp->b_flags&B_READ) == 0) + tu_vee(&tu_pcnt[minor(bp->b_dev)&DNUM]); + iodone(bp); + } +retry: + splx(s); + timeout(tuwatch, (caddr_t)0, hz); +} + +tu_pee(cp) + char *cp; +{ + register int s; + + s = splx(TUIPL); + if (++(*cp) > NTUQ) + sleep(cp, PRIBIO); + splx(s); +} + +tu_vee(cp) + char *cp; +{ + register int s; + + s = splx(TUIPL); + if (--(*cp) <= NTUQ) + wakeup(cp); + splx(s); +} + +tu_restart() +{ + tureset(); + timeout(tustart, (caddr_t)0, hz * 3); +} + +#endif diff --git a/usr/src/sys/vaxuba/rk.c b/usr/src/sys/vaxuba/rk.c new file mode 100644 index 0000000000..45c258c8c8 --- /dev/null +++ b/usr/src/sys/vaxuba/rk.c @@ -0,0 +1,790 @@ +/* rk.c 6.2 83/10/11 */ + +#include "rk.h" +#if NHK > 0 +int rkpip; /* DEBUG */ +int rknosval; /* DEBUG */ +#ifdef RKDEBUG +int rkdebug; +#endif +#ifdef RKBDEBUG +int rkbdebug; +#endif +/* + * RK611/RK0[67] disk driver + * + * This driver mimics up.c; see it for an explanation of common code. + * + * TODO: + * Learn why we lose an interrupt sometime when spinning drives down + */ +#include "../machine/pte.h" + +#include "../h/param.h" +#include "../h/systm.h" +#include "../h/buf.h" +#include "../h/conf.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/map.h" +#include "../h/vm.h" +#include "../h/dk.h" +#include "../h/cmap.h" +#include "../h/dkbad.h" +#include "../h/uio.h" +#include "../h/kernel.h" + +#include "../vax/cpu.h" +#include "../vaxuba/ubareg.h" +#include "../vaxuba/ubavar.h" +#include "../vaxuba/rkreg.h" + +struct rk_softc { + int sc_softas; + int sc_ndrive; + int sc_wticks; + int sc_recal; +} rk_softc[NHK]; + +/* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */ +struct size { + daddr_t nblocks; + int cyloff; +} rk7_sizes[8] ={ + 15884, 0, /* A=cyl 0 thru 240 */ + 10032, 241, /* B=cyl 241 thru 392 */ + 53790, 0, /* C=cyl 0 thru 814 */ + 0, 0, + 0, 0, + 0, 0, + 27786, 393, /* G=cyl 393 thru 813 */ + 0, 0, +}, rk6_sizes[8] ={ + 15884, 0, /* A=cyl 0 thru 240 */ + 11154, 241, /* B=cyl 241 thru 409 */ + 27126, 0, /* C=cyl 0 thru 410 */ + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, +}; +/* END OF STUFF WHICH SHOULD BE READ IN PER DISK */ + +short rktypes[] = { RK_CDT, 0 }; + +int rkprobe(), rkslave(), rkattach(), rkdgo(), rkintr(); +struct uba_ctlr *rkminfo[NHK]; +struct uba_device *rkdinfo[NRK]; +struct uba_device *rkip[NHK][4]; + +u_short rkstd[] = { 0777440, 0 }; +struct uba_driver hkdriver = + { rkprobe, rkslave, rkattach, rkdgo, rkstd, "rk", rkdinfo, "hk", rkminfo, 1 }; +struct buf rkutab[NRK]; +short rkcyl[NRK]; +struct dkbad rkbad[NRK]; +struct buf brkbuf[NRK]; + +struct rkst { + short nsect; + short ntrak; + short nspc; + short ncyl; + struct size *sizes; +} rkst[] = { + NRKSECT, NRKTRK, NRKSECT*NRKTRK, NRK7CYL, rk7_sizes, + NRKSECT, NRKTRK, NRKSECT*NRKTRK, NRK6CYL, rk6_sizes, +}; + +u_char rk_offset[16] = + { RKAS_P400,RKAS_M400,RKAS_P400,RKAS_M400,RKAS_P800,RKAS_M800,RKAS_P800, + RKAS_M800,RKAS_P1200,RKAS_M1200,RKAS_P1200,RKAS_M1200,0,0,0,0 + }; + +struct buf rrkbuf[NRK]; + +#define b_cylin b_resid + +#ifdef INTRLVE +daddr_t dkblock(); +#endif + +int rkwstart, rkwatch(); + +rkprobe(reg) + caddr_t reg; +{ + register int br, cvec; + +#ifdef lint + br = 0; cvec = br; br = cvec; + rkintr(0); +#endif + ((struct rkdevice *)reg)->rkcs1 = RK_CDT|RK_IE|RK_CRDY; + DELAY(10); + ((struct rkdevice *)reg)->rkcs1 = RK_CDT; + return (sizeof (struct rkdevice)); +} + +rkslave(ui, reg) + struct uba_device *ui; + caddr_t reg; +{ + register struct rkdevice *rkaddr = (struct rkdevice *)reg; + + ui->ui_type = 0; + rkaddr->rkcs1 = RK_CCLR; + rkaddr->rkcs2 = ui->ui_slave; + rkaddr->rkcs1 = RK_CDT|RK_DCLR|RK_GO; + rkwait(rkaddr); + DELAY(50); + if (rkaddr->rkcs2&RKCS2_NED || (rkaddr->rkds&RKDS_SVAL) == 0) { + rkaddr->rkcs1 = RK_CCLR; + return (0); + } + if (rkaddr->rkcs1&RK_CERR && rkaddr->rker&RKER_DTYE) { + ui->ui_type = 1; + rkaddr->rkcs1 = RK_CCLR; + } + return (1); +} + +rkattach(ui) + register struct uba_device *ui; +{ + + if (rkwstart == 0) { + timeout(rkwatch, (caddr_t)0, hz); + rkwstart++; + } + if (ui->ui_dk >= 0) + dk_mspw[ui->ui_dk] = 1.0 / (60 * NRKSECT * 256); + rkip[ui->ui_ctlr][ui->ui_slave] = ui; + rk_softc[ui->ui_ctlr].sc_ndrive++; + rkcyl[ui->ui_unit] = -1; + ui->ui_flags = 0; +} + +rkopen(dev) + dev_t dev; +{ + register int unit = minor(dev) >> 3; + register struct uba_device *ui; + + if (unit >= NRK || (ui = rkdinfo[unit]) == 0 || ui->ui_alive == 0) + return (ENXIO); + return (0); +} + +rkstrategy(bp) + register struct buf *bp; +{ + register struct uba_device *ui; + register struct rkst *st; + register int unit; + register struct buf *dp; + int xunit = minor(bp->b_dev) & 07; + long bn, sz; + int s; + + sz = (bp->b_bcount+511) >> 9; + unit = dkunit(bp); + if (unit >= NRK) + goto bad; + ui = rkdinfo[unit]; + if (ui == 0 || ui->ui_alive == 0) + goto bad; + st = &rkst[ui->ui_type]; + if (bp->b_blkno < 0 || + (bn = dkblock(bp))+sz > st->sizes[xunit].nblocks) + goto bad; + bp->b_cylin = bn/st->nspc + st->sizes[xunit].cyloff; + s = spl5(); + dp = &rkutab[ui->ui_unit]; + disksort(dp, bp); + if (dp->b_active == 0) { + (void) rkustart(ui); + bp = &ui->ui_mi->um_tab; + if (bp->b_actf && bp->b_active == 0) + (void) rkstart(ui->ui_mi); + } + splx(s); + return; + +bad: + bp->b_flags |= B_ERROR; + iodone(bp); + return; +} + +rkustart(ui) + register struct uba_device *ui; +{ + register struct buf *bp, *dp; + register struct uba_ctlr *um; + register struct rkdevice *rkaddr; + + if (ui == 0) + return; + dk_busy &= ~(1<ui_dk); + dp = &rkutab[ui->ui_unit]; + um = ui->ui_mi; + rkaddr = (struct rkdevice *)um->um_addr; + if (um->um_tab.b_active) { + rk_softc[um->um_ctlr].sc_softas |= 1<ui_slave; + return; + } + if ((bp = dp->b_actf) == NULL) + return; + rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_CERR; + rkaddr->rkcs2 = ui->ui_slave; + rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_DCLR|RK_GO; + rkwait(rkaddr); + if ((rkaddr->rkds & RKDS_VV) == 0 || ui->ui_flags == 0) { + /* SHOULD WARN SYSTEM THAT THIS HAPPENED */ + struct rkst *st = &rkst[ui->ui_type]; + struct buf *bbp = &brkbuf[ui->ui_unit]; + + rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_PACK|RK_GO; + ui->ui_flags = 1; + bbp->b_flags = B_READ|B_BUSY; + bbp->b_dev = bp->b_dev; + bbp->b_bcount = 512; + bbp->b_un.b_addr = (caddr_t)&rkbad[ui->ui_unit]; + bbp->b_blkno = st->ncyl*st->nspc - st->nsect; + bbp->b_cylin = st->ncyl - 1; + dp->b_actf = bbp; + bbp->av_forw = bp; + bp = bbp; + rkwait(rkaddr); + } + if (dp->b_active) + goto done; + dp->b_active = 1; + if ((rkaddr->rkds & RKDS_DREADY) != RKDS_DREADY) + goto done; + if (rk_softc[um->um_ctlr].sc_ndrive == 1) + goto done; + if (bp->b_cylin == rkcyl[ui->ui_unit]) + goto done; + rkaddr->rkcyl = bp->b_cylin; + rkcyl[ui->ui_unit] = bp->b_cylin; + rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_IE|RK_SEEK|RK_GO; + if (ui->ui_dk >= 0) { + dk_busy |= 1<ui_dk; + dk_seek[ui->ui_dk]++; + } + goto out; +done: + if (dp->b_active != 2) { + dp->b_forw = NULL; + if (um->um_tab.b_actf == NULL) + um->um_tab.b_actf = dp; + else + um->um_tab.b_actl->b_forw = dp; + um->um_tab.b_actl = dp; + dp->b_active = 2; + } +out: + return; +} + +rkstart(um) + register struct uba_ctlr *um; +{ + register struct buf *bp, *dp; + register struct uba_device *ui; + register struct rkdevice *rkaddr; + struct rkst *st; + daddr_t bn; + int sn, tn, cmd; + +loop: + if ((dp = um->um_tab.b_actf) == NULL) + return; + if ((bp = dp->b_actf) == NULL) { + um->um_tab.b_actf = dp->b_forw; + goto loop; + } + um->um_tab.b_active++; + ui = rkdinfo[dkunit(bp)]; + bn = dkblock(bp); + st = &rkst[ui->ui_type]; + sn = bn%st->nspc; + tn = sn/st->nsect; + sn %= st->nsect; + rkaddr = (struct rkdevice *)ui->ui_addr; +retry: + rkaddr->rkcs1 = RK_CCLR; + rkaddr->rkcs2 = ui->ui_slave; + rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_DCLR|RK_GO; + rkwait(rkaddr); + if ((rkaddr->rkds&RKDS_SVAL) == 0) { + rknosval++; + goto nosval; + } + if (rkaddr->rkds&RKDS_PIP) { + rkpip++; + goto retry; + } + if ((rkaddr->rkds&RKDS_DREADY) != RKDS_DREADY) { + printf("rk%d: not ready", dkunit(bp)); + if ((rkaddr->rkds&RKDS_DREADY) != RKDS_DREADY) { + printf("\n"); + rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_DCLR|RK_GO; + rkwait(rkaddr); + rkaddr->rkcs1 = RK_CCLR; + rkwait(rkaddr); + um->um_tab.b_active = 0; + um->um_tab.b_errcnt = 0; + dp->b_actf = bp->av_forw; + dp->b_active = 0; + bp->b_flags |= B_ERROR; + iodone(bp); + goto loop; + } + printf(" (came back!)\n"); + } +nosval: + rkaddr->rkcyl = bp->b_cylin; + rkcyl[ui->ui_unit] = bp->b_cylin; + rkaddr->rkda = (tn << 8) + sn; + rkaddr->rkwc = -bp->b_bcount / sizeof (short); + if (bp->b_flags & B_READ) + cmd = rktypes[ui->ui_type]|RK_IE|RK_READ|RK_GO; + else + cmd = rktypes[ui->ui_type]|RK_IE|RK_WRITE|RK_GO; + um->um_cmd = cmd; + (void) ubago(ui); +} + +rkdgo(um) + register struct uba_ctlr *um; +{ + register struct rkdevice *rkaddr = (struct rkdevice *)um->um_addr; + + um->um_tab.b_active = 2; /* should now be 2 */ + rkaddr->rkba = um->um_ubinfo; + rkaddr->rkcs1 = um->um_cmd|((um->um_ubinfo>>8)&0x300); +} + +rkintr(rk11) + int rk11; +{ + register struct uba_ctlr *um = rkminfo[rk11]; + register struct uba_device *ui; + register struct rkdevice *rkaddr = (struct rkdevice *)um->um_addr; + register struct buf *bp, *dp; + int unit; + struct rk_softc *sc = &rk_softc[um->um_ctlr]; + int as = (rkaddr->rkatt >> 8) | sc->sc_softas; + + sc->sc_wticks = 0; + sc->sc_softas = 0; + if (um->um_tab.b_active == 2 || sc->sc_recal) { + um->um_tab.b_active = 1; + dp = um->um_tab.b_actf; + bp = dp->b_actf; + ui = rkdinfo[dkunit(bp)]; + dk_busy &= ~(1 << ui->ui_dk); + if (bp->b_flags&B_BAD) + if (rkecc(ui, CONT)) + return; + if (rkaddr->rkcs1 & RK_CERR) { + int recal; + u_short ds = rkaddr->rkds; + u_short cs2 = rkaddr->rkcs2; + u_short er = rkaddr->rker; +#ifdef RKDEBUG + if (rkdebug) { + printf("cs2=%b ds=%b er=%b\n", + cs2, RKCS2_BITS, ds, + RKDS_BITS, er, RKER_BITS); + } +#endif + if (er & RKER_WLE) { + printf("rk%d: write locked\n", dkunit(bp)); + bp->b_flags |= B_ERROR; + } else if (++um->um_tab.b_errcnt > 28 || + ds&RKDS_HARD || er&RKER_HARD || cs2&RKCS2_HARD) { +hard: + harderr(bp, "rk"); + printf("cs2=%b ds=%b er=%b\n", + cs2, RKCS2_BITS, ds, + RKDS_BITS, er, RKER_BITS); + bp->b_flags |= B_ERROR; + sc->sc_recal = 0; + } else if (er & RKER_BSE) { + if (rkecc(ui, BSE)) + return; + else + goto hard; + } else { + if ((er & (RKER_DCK|RKER_ECH)) == RKER_DCK) { + if (rkecc(ui, ECC)) + return; + } else + um->um_tab.b_active = 0; + } + if (cs2&RKCS2_MDS) { + rkaddr->rkcs2 = RKCS2_SCLR; + goto retry; + } + recal = 0; + if (ds&RKDS_DROT || er&(RKER_OPI|RKER_SKI|RKER_UNS) || + (um->um_tab.b_errcnt&07) == 4) + recal = 1; + rkaddr->rkcs1 = RK_CCLR; + rkaddr->rkcs2 = ui->ui_slave; + rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_DCLR|RK_GO; + rkwait(rkaddr); + if (recal && um->um_tab.b_active == 0) { + rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_IE|RK_RECAL|RK_GO; + rkcyl[ui->ui_unit] = -1; + sc->sc_recal = 0; + goto nextrecal; + } + } +retry: + switch (sc->sc_recal) { + + case 1: + rkaddr->rkcyl = bp->b_cylin; + rkcyl[ui->ui_unit] = bp->b_cylin; + rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_IE|RK_SEEK|RK_GO; + goto nextrecal; + case 2: + if (um->um_tab.b_errcnt < 16 || + (bp->b_flags&B_READ) == 0) + goto donerecal; + rkaddr->rkatt = rk_offset[um->um_tab.b_errcnt & 017]; + rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_IE|RK_OFFSET|RK_GO; + /* fall into ... */ + nextrecal: + sc->sc_recal++; + rkwait(rkaddr); + um->um_tab.b_active = 1; + return; + donerecal: + case 3: + sc->sc_recal = 0; + um->um_tab.b_active = 0; + break; + } + ubadone(um); + if (um->um_tab.b_active) { + um->um_tab.b_active = 0; + um->um_tab.b_errcnt = 0; + um->um_tab.b_actf = dp->b_forw; + dp->b_active = 0; + dp->b_errcnt = 0; + dp->b_actf = bp->av_forw; + bp->b_resid = -rkaddr->rkwc * sizeof(short); + iodone(bp); + if (dp->b_actf) + rkustart(ui); + } + as &= ~(1<ui_slave); + } + for (unit = 0; as; as >>= 1, unit++) + if (as & 1) { + ui = rkip[rk11][unit]; + if (ui) { + rkustart(rkip[rk11][unit]); + } else { + rkaddr->rkcs1 = RK_CCLR; + rkaddr->rkcs2 = unit; + rkaddr->rkcs1 = RK_DCLR|RK_GO; + rkwait(rkaddr); + rkaddr->rkcs1 = RK_CCLR; + } + } + if (um->um_tab.b_actf && um->um_tab.b_active == 0) + rkstart(um); + if (((rkaddr->rkcs1) & RK_IE) == 0) + rkaddr->rkcs1 = RK_IE; +} + +rkwait(addr) + register struct rkdevice *addr; +{ + + while ((addr->rkcs1 & RK_CRDY) == 0) + ; +} + +rkread(dev, uio) + dev_t dev; + struct uio *uio; +{ + register int unit = minor(dev) >> 3; + + if (unit >= NRK) + return (ENXIO); + return (physio(rkstrategy, &rrkbuf[unit], dev, B_READ, minphys, uio)); +} + +rkwrite(dev, uio) + dev_t dev; + struct uio *uio; +{ + register int unit = minor(dev) >> 3; + + if (unit >= NRK) + return (ENXIO); + return (physio(rkstrategy, &rrkbuf[unit], dev, B_WRITE, minphys, uio)); +} + +rkecc(ui, flag) + register struct uba_device *ui; +{ + register struct rkdevice *rk = (struct rkdevice *)ui->ui_addr; + register struct buf *bp = rkutab[ui->ui_unit].b_actf; + register struct uba_ctlr *um = ui->ui_mi; + register struct rkst *st; + struct uba_regs *ubp = ui->ui_hd->uh_uba; + caddr_t addr; + int reg, npf, o, cmd, ubaddr; + int bn, cn, tn, sn; + + if (flag == CONT) + npf = bp->b_error; + else + npf = btop((rk->rkwc * sizeof(short)) + bp->b_bcount); + reg = btop(um->um_ubinfo&0x3ffff) + npf; + o = (int)bp->b_un.b_addr & PGOFSET; + bn = dkblock(bp); + st = &rkst[ui->ui_type]; + cn = bp->b_cylin; + sn = bn%st->nspc + npf; + tn = sn/st->nsect; + sn %= st->nsect; + cn += tn/st->ntrak; + tn %= st->ntrak; + ubapurge(um); + switch (flag) { + case ECC: + { + register int i; + int bit, byte, mask; + + npf--; + reg--; + printf("rk%d%c: soft ecc sn%d\n", dkunit(bp), + 'a'+(minor(bp->b_dev)&07), bp->b_blkno + npf); + mask = rk->rkec2; + i = rk->rkec1 - 1; /* -1 makes 0 origin */ + bit = i&07; + i = (i&~07)>>3; + byte = i + o; + while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) { + addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ + (byte & PGOFSET); + putmemc(addr, getmemc(addr)^(mask<rkwc == 0) { + um->um_tab.b_active = 0; + return (0); + } + npf++; + reg++; + break; + } + + case BSE: +#ifdef RKBDEBUG + if (rkbdebug) + printf("rkecc, BSE: bn %d cn %d tn %d sn %d\n", bn, cn, tn, sn); +#endif + if ((bn = isbad(&rkbad[ui->ui_unit], cn, tn, sn)) < 0) + return(0); + bp->b_flags |= B_BAD; + bp->b_error = npf + 1; + bn = st->ncyl*st->nspc - st->nsect - 1 - bn; + cn = bn/st->nspc; + sn = bn%st->nspc; + tn = sn/st->nsect; + sn %= st->nsect; +#ifdef RKBDEBUG + if (rkbdebug) + printf("revector to cn %d tn %d sn %d\n", cn, tn, sn); +#endif + rk->rkwc = -(512 / sizeof (short)); + break; + + case CONT: +#ifdef RKBDEBUG + if (rkbdebug) + printf("rkecc, CONT: bn %d cn %d tn %d sn %d\n", bn,cn,tn,sn); +#endif + bp->b_flags &= ~B_BAD; + rk->rkwc = -((bp->b_bcount - (int)ptob(npf)) / sizeof (short)); + if (rk->rkwc == 0) + return (0); + break; + } + rk->rkcs1 = RK_CCLR; + rk->rkcs2 = ui->ui_slave; + rk->rkcs1 = rktypes[ui->ui_type]|RK_DCLR|RK_GO; + rkwait(rk); + rk->rkcyl = cn; + rk->rkda = (tn << 8) | sn; + ubaddr = (int)ptob(reg) + o; + rk->rkba = ubaddr; + cmd = (bp->b_flags&B_READ ? RK_READ : RK_WRITE)|RK_IE|RK_GO; + cmd |= (ubaddr >> 8) & 0x300; + cmd |= rktypes[ui->ui_type]; + rk->rkcs1 = cmd; + um->um_tab.b_active = 2; /* continuing */ + um->um_tab.b_errcnt = 0; /* error has been corrected */ + return (1); +} + +rkreset(uban) + int uban; +{ + register struct uba_ctlr *um; + register struct uba_device *ui; + register rk11, unit; + + for (rk11 = 0; rk11 < NHK; rk11++) { + if ((um = rkminfo[rk11]) == 0 || um->um_ubanum != uban || + um->um_alive == 0) + continue; + printf(" hk%d", rk11); + um->um_tab.b_active = 0; + um->um_tab.b_actf = um->um_tab.b_actl = 0; + rk_softc[um->um_ctlr].sc_recal = 0; + rk_softc[um->um_ctlr].sc_wticks = 0; + if (um->um_ubinfo) { + printf("<%d>", (um->um_ubinfo>>28)&0xf); + um->um_ubinfo = 0; + } + for (unit = 0; unit < NRK; unit++) { + if ((ui = rkdinfo[unit]) == 0) + continue; + if (ui->ui_alive == 0 || ui->ui_mi != um) + continue; + rkutab[unit].b_active = 0; + (void) rkustart(ui); + } + (void) rkstart(um); + } +} + +rkwatch() +{ + register struct uba_ctlr *um; + register rk11, unit; + register struct rk_softc *sc; + + timeout(rkwatch, (caddr_t)0, hz); + for (rk11 = 0; rk11 < NHK; rk11++) { + um = rkminfo[rk11]; + if (um == 0 || um->um_alive == 0) + continue; + sc = &rk_softc[rk11]; + if (um->um_tab.b_active == 0) { + for (unit = 0; unit < NRK; unit++) + if (rkutab[unit].b_active && + rkdinfo[unit]->ui_mi == um) + goto active; + sc->sc_wticks = 0; + continue; + } +active: + sc->sc_wticks++; + if (sc->sc_wticks >= 20) { + sc->sc_wticks = 0; + printf("hk%d: lost interrupt\n", rk11); + ubareset(um->um_ubanum); + } + } +} + +#define DBSIZE 20 + +rkdump(dev) + dev_t dev; +{ + struct rkdevice *rkaddr; + char *start; + int num, blk, unit; + struct size *sizes; + register struct uba_regs *uba; + register struct uba_device *ui; + register short *rp; + struct rkst *st; + + unit = minor(dev) >> 3; + if (unit >= NRK) + return (ENXIO); +#define phys(cast, addr) ((cast)((int)addr & 0x7fffffff)) + ui = phys(struct uba_device *, rkdinfo[unit]); + if (ui->ui_alive == 0) + return (ENXIO); + uba = phys(struct uba_hd *, ui->ui_hd)->uh_physuba; + ubainit(uba); + rkaddr = (struct rkdevice *)ui->ui_physaddr; + num = maxfree; + start = 0; + rkaddr->rkcs1 = RK_CCLR; + rkaddr->rkcs2 = unit; + rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_DCLR|RK_GO; + rkwait(rkaddr); + if ((rkaddr->rkds & RKDS_VV) == 0) { + rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_IE|RK_PACK|RK_GO; + rkwait(rkaddr); + } + st = &rkst[ui->ui_type]; + sizes = phys(struct size *, st->sizes); + if (dumplo < 0 || dumplo + num >= sizes[minor(dev)&07].nblocks) + return (EINVAL); + while (num > 0) { + register struct pte *io; + register int i; + int cn, sn, tn; + daddr_t bn; + + blk = num > DBSIZE ? DBSIZE : num; + io = uba->uba_map; + for (i = 0; i < blk; i++) + *(int *)io++ = (btop(start)+i) | (1<<21) | UBAMR_MRV; + *(int *)io = 0; + bn = dumplo + btop(start); + cn = bn/st->nspc + sizes[minor(dev)&07].cyloff; + sn = bn%st->nspc; + tn = sn/st->nsect; + sn = sn%st->nsect; + rkaddr->rkcyl = cn; + rp = (short *) &rkaddr->rkda; + *rp = (tn << 8) + sn; + *--rp = 0; + *--rp = -blk*NBPG / sizeof (short); + *--rp = rktypes[ui->ui_type]|RK_GO|RK_WRITE; + rkwait(rkaddr); + if (rkaddr->rkcs1 & RK_CERR) + return (EIO); + start += blk*NBPG; + num -= blk; + } + return (0); +} + +rksize(dev) + dev_t dev; +{ + int unit = minor(dev) >> 3; + struct uba_device *ui; + struct rkst *st; + + if (unit >= NRK || (ui = rkdinfo[unit]) == 0 || ui->ui_alive == 0) + return (-1); + st = &rkst[ui->ui_type]; + return (st->sizes[minor(dev) & 07].nblocks); +} +#endif