mike@cimcor.mn.org (Michael Grenier) (01/02/91)
#!/bin/sh
# this is st01.02 (part 2 of st01)
# do not concatenate these parts, unpack them in order with /bin/sh
# file scsi.c continued
#
if touch 2>&1 | fgrep 'amc' > /dev/null
then TOUCH=touch
else TOUCH=true
fi
if test ! -r shar3_seq_.tmp; then
echo "Please unpack part 1 first!"
exit 1
fi
(read Scheck
if test "$Scheck" != 2; then
echo "Please unpack part $Scheck next!"
exit 1
else
exit 0
fi
) < shar3_seq_.tmp || exit 1
echo "x - Continuing file scsi.c"
sed 's/^X//' << 'SHAR_EOF' >> scsi.c &&
X if (d[unit].xferphys)
X { /* directly to user space */
X if (le > sizeof(rawiobuf))
X le=sizeof(rawiobuf);
X getfromscsi(rawiobuf,le);
X strlog(0, unit, 0, SL_TRACE, "Read into raw buffer\n");
X if (copyout(rawiobuf,d[unit].xferbuf,le) == -1) {
X strlog(0, unit, 1, SL_TRACE, "Copyout failed to transfer into raw buffer \n");
X return 0;
X }
X } else
X getfromscsi(d[unit].xferbuf,le);
X }
X while ((*cmdport & (STMSG|STCD|STIO|STREQ)) == (0|0|STIO|STREQ))
X {
X le=(*scsidataport);
X for (l=0;l<1000l;l++)
X if (*cmdport & STREQ)
X break;
X }
X return 1;
X}
X
X/* This is called when we are connected to the target on the scsi bus.
X This will do any exchange of data with the target. The dialog is
X controlled by the target. This will remain connected until the
X target sends a disconnect message, the command is complete, or a timeout
X if encountered. There should be no interrupts while this is executing,
X as the unit should be connected all the time. This returns a C* completion
X status. Normally, this should return quite fast. This will never sleep
X and will also be called at interrupt time. With dumb drives not supporting
X disconnect (are there any?) this would block the system for the duration
X of this call. This will only mark the drive not busy if the command
X completed successfully. If an error is returned, the drive has not
X been marked not busy. */
X
Xstatic int doxfernosleep(unit)
Xint unit;
X{
X int a;
X long l;
X
X for (l=0;l<1000000l || d[unit].xfertimeout == 0;l++)
X {
X if (!(*cmdport & STBSY))
X {
X#ifdef DEBUG
X strlog(0, unit, 0, SL_ERROR, "scsi: doxfernosleep: !STBSY unit %d\n",unit);
X#endif
X d[unit].connected=0;
X return CERROR; /* we are no longer connected??? */
X }
X if (!(*cmdport & STREQ))
X continue; /* loop until target requesting something */
X#ifdef DEBUG1
X strlog(0, unit, 0, SL_ERROR, "scsi: doxfernosleep: new state=%x\n",*cmdport);
X#endif
X switch ((*cmdport & (STMSG|STCD|STIO)) & 0xff)
X {
X case 0|0|0: /* data out */
X if (!dataout(unit))
X {
X strlog(0, unit, 0, SL_ERROR, "scsi: dataout returned error; unit=%d\n",unit);
X return CERROR;
X }
X break;
X case 0|0|STIO: /* data in */
X if (!datain(unit))
X {
X#ifdef DEBUG
X strlog(0, unit, 0, SL_ERROR, "scsi: datain returned error; unit=%d\n",unit);
X#endif
X return CERROR;
X }
X break;
X case 0|STCD|0: /* command out */
X *scsidataport=(*d[unit].xfercmd++);
X break;
X case 0|STCD|STIO: /* status in */
X d[unit].xferstatus=(*scsidataport);
X break;
X case STMSG|STCD|0: /* msg out */
X /* we should never get here. We don't want to send a message.
X Lets just drop attention and hope the drive understands. */
X#ifdef DEBUG0
X strlog(0, unit, 0, SL_ERROR, "scsi: unexpected msg out state; status=%x\n",*cmdport);
X#endif
X *scsidataport=MSGNOP; /* send a no-operation message */
X *cmdport=CMDBASE|CMDENABLE;
X break;
X case STMSG|STCD|STIO: /* msg in */
X a=(*scsidataport) & 0xff;
X switch (a)
X {
X case MSGCOMPLETE:
X d[unit].connected=0;
X *cmdport=CMDBASE;
X#ifdef DEBUG0
X strlog(0, unit, 0, SL_ERROR, "scsi: command complete message received\n");
X#endif
X if (d[unit].xferstatus == 0) /* completed succesfully */
X {
X marknotbusy(unit,COK);
X return COK;
X }
X print_status(unit, d[unit].xferstatus);
X return CERROR;
X case MSGSAVEDATAPTR:
X d[unit].savedbuf=d[unit].xferbuf;
X d[unit].savedlen=d[unit].xferlen;
X break;
X case MSGRESTOREPTR:
X d[unit].xferbuf=d[unit].savedbuf;
X d[unit].xferlen=d[unit].savedlen;
X d[unit].xfercmd=d[unit].savedcmd;
X break;
X case MSGDISCONNECT:
X d[unit].connected=0;
X d[unit].xfertime=1;
X *cmdport=CMDBASE;
X#ifdef DEBUG0
X strlog(0, unit, 0, SL_ERROR, "scsi: disconnected\n");
X#endif
X return CDISCONNECT;
X case MSGMSGREJECT:
X break; /* the target rejected some message... Who cares. */
X case MSGNOP:
X break; /* this should not be sent by the target, but... */
X case MSGIDENTIFY:
X break; /* we don't care about targets identify messages */
X default:
X if (a & 0x80)
X break; /* assume it is an identify message */
X strlog(0, unit, 0, SL_ERROR, "scsi: unknown message received from drive %d: %x\n",
X unit,a);
X break;
X }
X break;
X default:
X /* unexpected stack state. Now I don't know what to do. Lets
X hope the drive changes to another state. */
X
X strlog(0, unit, 0, SL_ERROR, "scsi: unexpected bus state: status=%x\n",*cmdport);
X break;
X }
X }
X return CTIMEOUT;
X}
X
X/* This implements polled wait for reconnect. This is mainly used at
X system initialization time when the interrupt system may not be fully
X initialized. This returns true if reconnect was encountered.
X If there is no successful reconnect, this will time out after a few
X seconds and return false. */
X
Xstatic int polledwaitreconnect(unit)
Xint unit;
X{
X long l;
X unsigned char ch;
X
X *cmdport=CMDBASE&~CMDENINTR;
X for (l=0;l<2000000l;l++)
X {
X if ((*cmdport & (STSEL|STIO|STBSY)) != (STSEL|STIO|0))
X continue;
X ch=(*scsidataport);
X if (!(ch & MYADDR))
X {
X#ifdef DEBUG
X strlog(0, unit, 0, SL_ERROR, "scsi: polled releselection was not for me: %x\n",ch);
X#endif
X continue;
X }
X ch&=~MYADDR;
X if (!(ch & (1 << unit)))
X {
X#ifdef DEBUG
X strlog(0, unit, 0, SL_ERROR, "scsi: reselecting (polled) unit other than expected: %x\n",
X ch);
X#endif
X continue;
X }
X *cmdport=(CMDBASE&~CMDENINTR)|CMDBSY|CMDENABLE;
X for (l=0;l<100000l;l++)
X if (!(*cmdport & STSEL))
X break;
X for (l=0;l<100000l;l++)
X if (!(*cmdport & STBSY))
X break;
X *cmdport=CMDBASE|CMDENABLE;
X d[unit].connected=1;
X return 1;
X }
X *cmdport=CMDBASE;
X#ifdef DEBUG
X strlog(0, unit, 0, SL_ERROR, "scsi: timeout polled wait for reselection from %d\n",unit);
X#endif
X return 0;
X}
X
X/* This starts the scsi command. Interrupts may be enabled when this is
X called. When this retuns, either the drive must have been marked not
X busy (error or completion), or the target has disconnected and the drive
X will be marked not busy when an interrupt or timeout comes. A failure
X to mark the drive not busy will block the drive from all future
X requests. If retries are made for a command, this will be called to
X start the retry. */
X
Xstatic int startscsi(unit)
Xint unit;
X{
X int a;
X
X d[unit].xferbuf=d[unit].savedbuf=d[unit].origbuf;
X d[unit].xferlen=d[unit].savedlen=d[unit].origlen;
X d[unit].xfercmd=d[unit].savedcmd=d[unit].origcmd;
X
X startagain:
X
X#ifdef DEBUG0
X strlog(0, unit, 0, SL_ERROR, "scsi: arbitrating for %d\n",unit);
X#endif
X a=arbitrate(unit);
X if (a != COK) /* arbitrate does the necessary retries */
X return a;
X
X#ifdef DEBUG0
X strlog(0, unit, 0, SL_ERROR, "scsi: arbitration complete\n");
X#endif
X while (1)
X {
X a=doxfernosleep(unit);
X#ifdef DEBUG0
X strlog(0, unit, 0, SL_ERROR, "scsi: doxfernosleep returned %d\n",a);
X#endif
X if (a == CDISCONNECT)
X { /* The target disconnected */
X if (d[unit].xferpolled)
X {
X#ifdef DEBUG0
X strlog(0, unit, 0, SL_ERROR, "scsi: polled wait\n");
X#endif
X if (!polledwaitreconnect(unit))
X goto retry;
X#ifdef DEBUG0
X strlog(0, unit, 0, SL_ERROR, "scsi: polled wait complete - reconnected\n");
X#endif
X continue;
X }
X if (d[unit].currentbuf)
X { /* We are doing io for a buffer */
X /* All we have to do is to return; intr will call iodone. */
X d[unit].xfertime=1; /* enable timeouts */
X return CDISCONNECT;
X }
X
X /* disconnect; we do not have a buffer but may use intrs */
X /* This is not too efficient, as the delay from wakeup to
X continuing execution might be substantial, but this is not
X a typical case, as transfers do not normally go to
X internal buffers. */
X d[unit].xfertime=1; /* enable timeouts */
X if (sleep(&d[unit].connected,MYSLEEPPRI|PCATCH) == 1)
X { /* cought a signal */
X d[unit].busy=0; /* I guess this is an atomic operation */
X return CERROR;
X }
X if (!d[unit].connected)
X goto retry; /* it must have been a timeout */
X continue;
X }
X if (a == COK || a == CNOCONNECT || a == CBUSBUSY)
X {
X if (a != COK)
X marknotbusy(unit,a);
X return a;
X }
X goto retry;
X }
X retry:
X /* a possibly recoverable error was encountered */
X#ifdef DEBUG
X strlog(0, unit, 0, SL_ERROR, "scsi: startscsi: retrying or failing\n");
X#endif
X if (d[unit].xferretries > 1)
X {
X d[unit].xferretries--;
X goto startagain;
X }
X if (a == CTIMEOUT || a == CBUSBUSY)
X resetscsibus(); /* in case the drive was hanging on the bus */
X d[unit].connected=0;
X *cmdport=CMDBASE;
X marknotbusy(unit,a);
X return a; /* too many retries - return error */
X}
X
X/* This executes the given command on the unit. This returns command status
X (C* constants). There is no need to retry the operation after calling
X this. */
X
Xstatic int doscsicmd(unit,cmd,buf,len,timeout,retries,slow,phys,polled,bp)
Xint unit, /* drive number */
X len, /* buffer size */
X timeout, /* timeout in 1/10 secs if != 0 */
X retries, /* number of tries before returning failure; 1=try only once */
X slow, /* set to true if slow transfer (only true for read & write) */
X phys, /* set to true if xfer directly to/from user space (raw io) */
X polled; /* set to true if polled transfer */
Xchar *cmd, /* command to execute */
X *buf; /* transfer buffer address */
Xstruct buf *bp; /* io buffer being executed, or NULL */
X{
X int x;
X
X#ifdef DEBUG0
X strlog(0, unit, 0, SL_ERROR, "scsi: cmd unit=%d buf=%x len=%d timeout=%d retries=%d slow=%d phys=%d polled=%d bp=%x\n",
X unit,buf,len,timeout,retries,slow,phys,polled,bp);
X#endif
X
X x=splnointrs();
X if (d[unit].busy)
X {
X if (bp)
X {
X splx(x);
X return CBUSY;
X }
X strlog(0, unit, 0, SL_ERROR, "scsi : Sleeping, waiting for idle target\n");
X while (d[unit].busy)
X {
X splx(x);
X if (sleep(&d[unit].connected,MYSLEEPPRI) == 1)
X { /* cought a signal */
X return CERROR;
X }
X x=splnointrs();
X }
X }
X d[unit].origbuf=buf;
X d[unit].origlen=len;
X d[unit].origcmd=cmd;
X d[unit].xferslow=slow;
X d[unit].xferstatus=0x01; /* indicates error */
X d[unit].xfertimeout=timeout?timeout+1:0;
X d[unit].xfertime=0;
X d[unit].xferretries=retries;
X d[unit].xferphys=phys;
X d[unit].xferpolled=polled;
X d[unit].currentbuf=bp;
X d[unit].busy=1;
X splx(x);
X
X return startscsi(unit);
X}
X
Xstatic int dorw(unit,sec,buf,len,flags,polled,bp)
Xint unit;
Xlong sec;
Xchar *buf;
Xint len,flags,polled;
Xstruct buf *bp;
X{
X char cmd[10];
X int nblocks,a;
X
X a=d[unit].blocksize;
X if (a == 0)
X a=512;
X nblocks=(len+a-1)/a;
X
X strlog(0, unit, 0, SL_TRACE, "scsi: dorw: unit=%d sec=%d buf=%x len=%d flags=%x polled=%d bp=%x\n",
X unit,sec,buf,len,flags,polled,bp);
X
X d[unit].scsicmd = cmd[0]=(flags & B_READ)?SCSIREAD:SCSIWRITE;
X cmd[1]=0x00; /* LU & RADDR */
X cmd[2]=sec >> 24;
X cmd[3]=sec >> 16;
X cmd[4]=sec >> 8;
X cmd[5]=sec;
X cmd[6]=0;
X d[unit].logical_block = sec;
X cmd[7]=nblocks >> 8;
X cmd[8]=nblocks;
X d[unit].num_blocks = nblocks;
X cmd[9]=0;
X
X return doscsicmd(unit,cmd,buf,len,RWTIMEOUT,3,0,flags & B_PHYS,polled,bp);
X}
X
X/* This starts an io operation on the given buffer. This is called when
X a new buffer is added to the io queue, and when a previous operation has
X completed, to start io on the next buffer. If the unit is busy, this will
X do nothing. If it is not busy, this will start the request. This should
X be called with splnointrs. Any routines called by this will not change
X the interrupt level to a lower value. */
X
Xstatic int startbufferio(unit,bp)
Xint unit;
Xstruct buf *bp;
X{
X return dorw(unit,BLPTOSEC(unit,PARTNO(minor(bp->b_dev)),bp->b_blkno),
X bp->b_un.b_addr,bp->b_bcount,bp->b_flags,0,bp);
X}
X
X/* This will start a pending io request in the system. If bp (the previous
X request) is non-NULL, this will first remove bp from the list of
X pending requests. This will then start a new request if there are any.
X This can only be called with splnointrs, and when the unit is not busy. */
X
Xstatic startpendingreq(unit,bp)
Xint unit;
Xstruct buf *bp;
X{
X struct buf *ap;
X int x;
X
X x=splnointrs();
X if (bp)
X {
X ap=bp->av_forw;
X if (!ap)
X if (d[unit].reqlist != bp)
X ap=d[unit].reqlist;
X }
X else
X ap=d[unit].reqlist;
X /* ap is the next request to process, or NULL if there are none pending. */
X if (bp)
X {
X if (bp == d[unit].reqlist)
X d[unit].reqlist=bp->av_forw;
X if (bp->av_back)
X bp->av_back->av_forw=bp->av_forw;
X if (bp->av_forw)
X bp->av_forw->av_back=bp->av_back;
X bp->av_forw=NULL;
X bp->av_back=NULL;
X /* bp has now been removed from the list of requests. */
X }
X
X if (ap) /* start the next pending request if there are any */
X startbufferio(unit,ap);
X
X splx(x);
X}
X
X/* This marks the unit not busy. This is used to mark the completion
X of a command. This must absolutely be called exactly once for each and
X every i/o request made. If the request was for an io buffer, this will
X set b_flags&B_ERROR according to the completion; COK marks good completion.
X If there are any processes sleeping for the drive to become not busy,
X this will wake them up. If there is any pending block io, this will
X start i/o for the next buffer. After a call to this, all data in the
X d[unit] structure for the previous request will have been lost and the
X next operation may be in progress. The scsi driver and controller should
X be set to bus free phase before calling this. */
X
Xstatic marknotbusy(unit,completion)
Xint unit,completion;
X{
X int x;
X struct buf *ap;
X
X#ifdef DEBUG0
X strlog(0, unit, 0, SL_ERROR, "scsi: marknotbusy unit=%d completion=%d\n",
X unit,completion);
X#endif
X x=splnointrs();
X d[unit].busy=0;
X d[unit].connected=0; /* just in case */
X d[unit].xfertime=0; /* we don't want any timeouts any more */
X ap=d[unit].currentbuf;
X if (ap)
X {
X if (completion != COK)
X ap->b_flags|=B_ERROR;
X }
X else
X if (!d[unit].xferpolled)
X wakeup(&d[unit].connected);
X startpendingreq(unit,ap); /* This will start any pending io */
X if (ap)
X iodone(ap);
X#ifdef DEBUG0
X strlog(0, unit, 0, SL_ERROR, "scsi: marknotbusy returning\n");
X#endif
X splx(x);
X}
X
X/* This is the scsi interrupt service routine. This is called with a priority
X lower than that of the timer tick, which is used to detect timeouts.
X This is called (this ignores other calls) when a target is reselecting this
X initiator. This will continue processing the reconnected request, and
X if the request completes, this will (in lower-level routines) start the
X next request automatically. */
X
Xscsiintr()
X{
X int a,x,unit;
X long l;
X /* beg M000 */
X static long error_count = 0L;
X static int error_delta = 1;
X /* end M000 */
X
X if (!(*cmdport & STSEL))
X {
X#ifdef DEBUG0
X strlog(0, unit, 0, SL_ERROR, "scsi: intr ignored (no SEL)\n");
X#endif
X return; /* The controller should only generate interrupts when select
X rises. */
X }
X for (l=0;l<10000l;l++)
X if (*cmdport & STIO)
X goto gotio;
X#ifdef DEBUG0
X strlog(0, unit, 0, SL_ERROR, "scsi: intr ignored (IO timeout)\n");
X#endif
X return;
X gotio:
X a=(*scsidataport) & 0xff;
X if (!(a & MYADDR))
X {
X#ifdef DEBUG
X strlog(0, unit, 0, SL_ERROR, "scsi: intr ignored (not my addr); addr=%x\n",a);
X#endif
X return;
X }
X a&=(~MYADDR);
X for (unit=0;unit < 8;unit++)
X if (a & (unsigned char)(1<<unit))
X break;
X if (unit >= 8 || (a & ~(unsigned char)(1<<unit)))
X {
X#ifdef DEBUG
X strlog(0, unit, 0, SL_ERROR, "scsi: intr ignored (invalid id); unit=%d a=%x\n",unit,a);
X#endif
X return;
X }
X if (unit >= SCSIMAXDRIVES)
X {
X#ifdef DEBUG
X strlog(0, unit, 0, SL_ERROR, "scsi: intr ignored (unit %d >= SCSIMAXDRIVES %d)\n",
X unit,SCSIMAXDRIVES);
X#endif
X return;
X }
X x=splnointrs();
X if (d[unit].connected || !d[unit].busy)
X {
X#ifdef DEBUG
X /* beg M000 */
X if (!(++error_count%error_delta)) {
X error_delta *= 2;
X strlog(0, unit, 0, SL_ERROR, "scsi: intr ignored (internal state): unit=%d connected=%d busy=%d (%ld/%d)\n",
X unit,d[unit].connected,d[unit].busy,error_count,error_delta);
X }
X /* end M000 */
X#endif
X splx(x);
X return;
X }
X if (d[unit].xferpolled)
X { /* ooops... This is not the way it was supposed to happen... */
X#ifdef DEBUG
X /* beg M000 */
X if (!(++error_count%error_delta)) {
X error_delta *= 2;
X strlog(0, unit, 0, SL_ERROR, "scsi: intr ignored (xfer is polled); unit=%d (%ld/%d)\n",
X unit,error_count,error_delta);
X }
X /* end M000 */
X#endif
X splx(x);
X return;
X }
X *cmdport=CMDBASE|CMDBSY|CMDENABLE; /* acknowledge reselection */
X for (l=0;l<10000l;l++)
X if (!(*cmdport & STSEL))
X goto selreleased;
X /* timeout waiting for sel to be released */
X *cmdport=CMDBASE;
X#ifdef DEBUG
X strlog(0, unit, 0, SL_ERROR, "scsi: intr ignored (timeout waiting for sel to be released); unit=%d\n",
X unit);
X#endif
X splx(x);
X return;
X selreleased:
X for (l=0;l<10000l;l++)
X if (*cmdport & STBSY)
X goto selectedandhavebsy;
X /* timeout waiting for sel to be released */
X *cmdport=CMDBASE;
X#ifdef DEBUG
X strlog(0, unit, 0, SL_ERROR, "scsi: intr ignored (timeout waiting for bsy after sel to be released); unit=%d\n",
X unit);
X#endif
X splx(x);
X return;
X selectedandhavebsy:
X *cmdport=CMDBASE|CMDENABLE;
X d[unit].connected=1;
X d[unit].xfertime=0;
X intrserviced=1;
X splx(x); /* allow timer ticks */
X if (d[unit].currentbuf)
X {
X a=doxfernosleep(unit);
X d[unit].connected=0; /* just in case */
X if (a != COK && a != CDISCONNECT)
X {
X strlog(0, unit, 0, SL_ERROR, "scsi: intr: cmd failed (%d); returning error\n",a);
X *cmdport=CMDBASE;
X marknotbusy(unit,a); /* This may start a new operation */
X }
X }
X else
X { /* it must be an interrupt-driven operation to an internal buffer */
X wakeup(&d[unit].connected);
X /* leave the connected indicator on, and do no further processing
X here. This will signal the sleeping operation that we are once
X again connected. */
X }
X intrserviced=0;
X return;
X}
X
X/* This is called using timeout() every 1/10th of a second. This is used
X to solve timeout conditions related to lost interrupts and the like.
X Note that this is usually entered with a priority higher than that of
X the scsi driver. splnointrs should be defined so that this interrupt
X is also masked out. There may also be a drawback in using splnointrs:
X if the system clock is incremented with these timer interrupts, it might
X lose some ticks when the scsi disk is being used. I hope Microport has
X implemented the system clock in some more clever way. */
X
Xscsitick()
X{
X int a,unit,x;
X
X x=splnointrs();
X if (!intrserviced) /* if in the middle of a scsi interrupt, do nothing */
X {
X for (unit=0;unit<SCSIMAXDRIVES;unit++)
X {
X if (!d[unit].blocksize || !d[unit].busy || d[unit].connected || d[unit].xfertime == 0 ||
X d[unit].xfertimeout == 0 || d[unit].xferpolled)
X continue;
X d[unit].xfertime++;
X if (d[unit].xfertime < d[unit].xfertimeout)
X continue;
X /* the timeout has elapsed. We can only assume that we have lost an
X interrupt or there are problems on the target which prevent it from
X reconnecting and completing the command. */
X d[unit].xfertime=0; /* will be reset in retry if appropriate */
X if (!d[unit].currentbuf)
X { /* interrupt-driven transter to local buffer */
X strlog(0, unit, 0, SL_ERROR, "scsi: local intr driven xfer woken up by timer tick; unit=%d\n",
X unit);
X wakeup(&d[unit].connected); /* !connected tells it to retry */
X continue;
X }
X a=COK; /* was CTIMEOUT - this will now disable retries */
X retrytickforbuf:
X if (a == COK || a == CDISCONNECT)
X continue;
X if (d[unit].xferretries == 0 || a == CBUSBUSY || a == CNOCONNECT)
X {
X strlog(0, unit, 0, SL_ERROR, "scsi: block xfer fails in timer tick; unit=%d, err=%d\n",
X unit,a);
X marknotbusy(unit,a); /* This may start a new operation */
X continue;
X }
X d[unit].xferretries--;
X strlog(0, unit, 0, SL_ERROR, "scsi: TIMEOUT : xfer retried in timer tick; unit=%d, err=%d\n",
X unit,a);
X a=startscsi(unit); /* NO!!!!! */
X goto retrytickforbuf;
X }
X }
X timeout(scsitick,0,HZ);
X splx(x);
X}
X
X/* This is the normal strcpy */
X
Xstatic strcpy(d,s)
Xchar *d,*s;
X{
X while (*s)
X *d++=(*s++);
X *d=0;
X}
X
X/* This implements the request sense command. This returns a C* status. */
X
Xstatic int requestsense(unit,buf,len,polled)
Xint unit,len,polled;
Xchar *buf;
X{
X char cmd[6];
X
X if (len > 18)
X len=18;
X cmd[0]=SCSIREQSENSE;
X cmd[1]=0;
X cmd[2]=0;
X cmd[3]=0;
X cmd[4]=len; /* Length of 0 forces non-extended sense information */
X cmd[5]=0;
X
X return doscsicmd(unit,cmd,buf,len,2,3,1,0,polled,NULL);
X}
X
X
X/* This implements the request sense command. This returns a C* status. */
X
Xstatic int reassign(unit,bad_block)
Xint unit, bad_block;
X{
X char cmd[6];
X static rbuf[8];
X
X cmd[0]=SCSIREASSIGN;
X cmd[1]=0;
X cmd[2]=0;
X cmd[3]=0;
X cmd[4]=0; /* Length of 0 forces non-extended sense information */
X cmd[5]=0;
X
X rbuf[0] = 0;
X rbuf[1] = 0;
X rbuf[2] = 0;
X rbuf[3] = 4; /* 4 bytes in defect list */
X
X /* Defect list follows containing only one defect */
X
X rbuf[4] = bad_block >> 24;
X rbuf[5] = (bad_block >> 16) & 0xff;
X rbuf[6] = (bad_block >> 8) & 0xff;
X rbuf[7] = bad_block & 0xff;
X
X return doscsicmd(unit,cmd,rbuf,8,200,1,1,0, 1 /* Polled */,NULL);
X}
X
X/* This tests for drive readyness (with the scsi test unit ready command).
X This returns a C* status. */
X
Xstatic int testready(unit)
Xint unit;
X{
X char cmd[6];
X
X cmd[0]=SCSITESTREADY;
X cmd[1]=0;
X cmd[2]=0;
X cmd[3]=0;
X cmd[4]=0;
X cmd[5]=0;
X
X return doscsicmd(unit,cmd,NULL,0,1,1,1,0,1,NULL);
X}
X
X/* This issues the inquiry command to the scsi drive to get its drive type
X and other characteristics. This returns a C* status. */
X
Xstatic int doinquiry(unit,buf,len,polled)
Xint unit,len,polled;
Xchar *buf;
X{
X char cmd[6];
X
X if (len > 36)
X len=36;
X cmd[0]=SCSIINQUIRY;
X cmd[1]=0;
X cmd[2]=0;
X cmd[3]=0;
X cmd[4]=len;
X cmd[5]=0;
X return doscsicmd(unit,cmd,buf,len,150,3,1,0,polled,NULL);
X /* the timeout is quite long to allow time for startup */
X}
X
X/* This reads the storage capacity and block size of the scsi drive */
X
Xstatic int readcapacity(unit,buf,len,polled)
Xint unit,len,polled;
Xchar *buf;
X{
X char cmd[10];
X
X if (len > 8)
X len=8;
X
X cmd[0]=SCSIREADCAPACITY;
X cmd[1]=0;
X cmd[2]=0;
X cmd[3]=0;
X cmd[4]=0;
X cmd[5]=0;
X cmd[6]=0;
X cmd[7]=0;
X cmd[8]=0;
X cmd[9]=0;
X
X if (doscsicmd(unit,cmd,buf,len,150,2,1,0,polled,NULL) != COK)
X return 0; /* the timeout period is quite long to allow time for startup */
X return 1;
X}
X
X/* This is used to initialize the drive at system startup time */
X
Xstatic initdrive(unit)
Xint unit;
X{
X int a,bs;
X char buf[100];
X long s,l;
X unsigned char *cp;
X
X d[unit].blocksize=0;
X d[unit].busy=0;
X d[unit].connected=0;
X d[unit].nparts=0;
X d[unit].nomsgs=0;
X
X a=testready(unit);
X if (a != COK)
X {
X if (a != CERROR && a != CBUSY)
X return 0; /* no point in waiting */
X printf("Waiting for unit %d powerup...\n",unit);
X for (l=0;l<10000000l;l++)
X if (l % 100000l == 0)
X {
X a=testready(unit);
X if (a == COK)
X break;
X }
X if (a != COK)
X {
X printf("Powerup timeout on drive %d\n",unit);
X return 0;
X }
X }
X a=requestsense(unit,buf,sizeof(buf),1);
X if (a == CNOCONNECT || a == CBUSBUSY)
X return 0;
X if (a != COK)
X {
X printf("scsi drive %d is not responding properly.\n",unit);
X return 0;
X }
X#ifdef DEBUG0
X printf("scsi: initdrive: requestsense ok\n");
X#endif
X a=doinquiry(unit,buf,sizeof(buf),1);
X if (a != COK)
X {
X printf("scsi drive %d: inquiry failed.\n",unit);
X return 0;
X }
X#ifdef DEBUG0
X printf("scsi: initdrive: doinquiry ok\n");
X#endif
X if (buf[0] != 0)
X {
X printf("scsi drive %d is on a direct access device\n",unit);
X return 0;
X }
X buf[buf[4]+6]=0;
X strcpy(d[unit].drivename,buf+8);
X if (!readcapacity(unit,buf,sizeof(buf),1))
X {
X d[unit].capacity=0;
X bs=d[unit].blocksize=512;
X printf("scsi drive %d: cannot read capacity\n",unit);
X }
X else
X {
X bs=d[unit].blocksize=((unsigned char)buf[6]<<8)+(unsigned char)buf[7];
X if (bs > BSIZE)
X printf("scsi drive %d: blocksize=%d BSIZE=%d not supported\n",
X unit,bs,BSIZE);
X d[unit].capacity=(long)((unsigned char)buf[0]<<24)+
X (long)((unsigned char)buf[1]<<16)+
X (long)((unsigned char)buf[2]<<8)+
X (long)((unsigned char)buf[3]);
X }
X printf("scsi drive %d: %ldM (%d byte sectors): %s\n",
X unit,(d[unit].capacity*d[unit].blocksize+524288l)/1048576l,
X d[unit].blocksize,d[unit].drivename);
X a=dorw(unit,0l,buf,sizeof(buf),B_READ,1,NULL);
X if (a != COK)
X {
X printf("scsi drive %d: could not read partition table\n",unit);
X return 0;
X }
X for (cp=(unsigned char *)buf,a=0;
X a<SCSIMAXPARTS-1 &&
X (cp[0] || cp[1] || cp[2] || cp[3] || cp[4] || cp[5]);
X a++,cp+=6)
X {
X s=(long)(cp[0]<<16)+(cp[1]<<8)+cp[2];
X l=(long)(cp[3]<<16)+(cp[4]<<8)+cp[5];
X if (s == 0)
X {
X s++;
X l--;
X }
X d[unit].parts[a].start=s;
X d[unit].parts[a].len=l;
X if (a == 0)
X printf("partitions:");
X printf(" %ldM",(l*bs+524288l)/1048576l);
X }
X if (a != 0)
X printf("\n");
X d[unit].nparts=a;
X d[unit].parts[SCSIMAXPARTS-1].start=0;
X d[unit].parts[SCSIMAXPARTS-1].len=d[unit].capacity;
X return 1;
X}
X
Xscsiinit()
X{
X int a;
X extern char *sptalloc();
X
X printf("\n%s\n",COPYRIGHT);
X if (!baseaddr)
X baseaddr=sptalloc(SCSISIZE/PAGESIZE,PG_P,(int)(SCSIBASE/PAGESIZE),
X NOSLEEP);
X if (!baseaddr)
X {
X printf("scsi driver error: could not sptalloc controller memory\n");
X return;
X }
X cmdport=baseaddr+SCSICONTROLOFS;
X scsidataport=baseaddr+SCSIDATAOFS;
X
X timeouting=0;
X for (a=0;a<SCSIMAXDRIVES;a++)
X {
X d[a].currentbuf=NULL;
X d[a].reqlist=NULL;
X }
X resetscsibus();
X for (a=0;a<SCSIMAXDRIVES;a++)
X {
X initdrive(a);
X }
X printf("\n");
X}
X
Xscsiopen(dev,flags)
Xdev_t dev;
Xint flags;
X{
X int unit=UNITNO(minor(dev)),
X part=PARTNO(minor(dev)),
X x;
X
X#ifdef DEBUG0
X strlog(0, unit, 0, SL_ERROR, "scsiopen: unit=%d part=%d\n",unit,part);
X#endif
X if (!timeouting)
X {
X x=splnointrs(); /* in case scsitick makes any assumptions about spl */
X scsitick();
X splx(x);
X timeouting=1;
X }
X if (unit >= SCSIMAXDRIVES ||
X (part != SCSIMAXPARTS-1 &&
X (d[unit].blocksize == 0 || part >= d[unit].nparts)))
X u.u_error=ENXIO;
X}
X
Xscsiclose()
X{
X#ifdef DEBUG0
X strlog(0, unit, 0, SL_ERROR, "scsiclose called\n");
X#endif
X /* do nothing */
X}
X
X
Xscsiprint()
X{
X#ifdef DEBUG
X strlog(0, unit, 0, SL_ERROR, "scsiprint called\n");
X#endif
X /* do nothing */
X}
X
Xscsistrategy(bp)
Xstruct buf *bp;
X{
X int unit=UNITNO(minor(bp->b_dev)),
X part=PARTNO(minor(bp->b_dev)),
X nsecs,x;
X long sec;
X struct buf *ap,**app;
X
X strlog(0, unit, 0, SL_TRACE, "scsistrategy: unit=%d part=%d b_dev=%x b_bcount=%d b_blkno=%d b_flags=%x\n",
X unit,part,bp->b_dev,bp->b_bcount,bp->b_blkno,bp->b_flags);
X
X if (unit >= SCSIMAXDRIVES || d[unit].blocksize == 0 ||
X bp->b_bcount % d[unit].blocksize != 0)
X {
X bp->b_flags|=B_ERROR;
X bp->b_resid=bp->b_bcount;
X iodone(bp);
X return;
X }
X if (part == SCSIMAXPARTS-1)
X sec=BLTOSEC(unit,bp->b_blkno);
X else
X {
X sec=BLTOSEC(unit,bp->b_blkno);
X if (part >= d[unit].nparts || sec > d[unit].parts[part].len)
X {
X bp->b_flags|=B_ERROR;
X bp->b_resid=bp->b_bcount;
X iodone(bp);
X return;
X }
X if (sec == d[unit].parts[part].len)
X {
X bp->b_resid=bp->b_bcount;
X iodone(bp);
X return;
X }
X nsecs=(bp->b_bcount+d[unit].blocksize-1)/d[unit].blocksize;
X if (sec+nsecs > d[unit].parts[part].len)
X {
X nsecs=d[unit].parts[part].len-sec;
X bp->b_resid=bp->b_bcount-nsecs*d[unit].blocksize;
X }
X else
X bp->b_resid=0;
X sec+=d[unit].parts[part].start;
X }
X
X if (bp->b_flags & B_PHYS) { /* raw read/write request */
X /* We do the command immediately, this will actually sleep */
X /* down the call tree in doscsicmd() */
X
X if (dorw(unit,BLPTOSEC(unit,PARTNO(minor(bp->b_dev)),bp->b_blkno),
X bp->b_un.b_addr,bp->b_bcount,bp->b_flags,
X 0 /* Not polled */,
X 0 /* No current buffer! transfer done in user context */) != COK) {
X bp->b_flags|=B_ERROR;
X bp->b_resid=bp->b_bcount;
X }
X iodone(bp);
X return;
X }
X
X
X x=splnointrs();
X
X /* Place on queue for this unit */
X
X for (app=(&d[unit].reqlist),ap=NULL;
X *app;
X ap=(*app),app=(&(*app)->av_forw))
X {
X if (sec < BLPTOSEC(unit,part,(*app)->b_blkno))
X {
X bp->av_back=ap;
X (*app)->av_back=bp;
X bp->av_forw=(*app);
X *app=bp;
X goto haveinserted;
X }
X }
X *app=bp;
X bp->av_forw=NULL;
X bp->av_back=ap;
X haveinserted:
X if (!d[unit].busy)
X startbufferio(unit,bp);
X splx(x);
X}
X
X/* raw io read on the device */
X
Xscsiread(dev)
Xint dev;
X{
X strlog(0, minor(dev), 0, SL_TRACE, "scsiread on unit %d\b", UNITNO(minor(dev)));
X physio(scsistrategy,NULL,dev,B_READ);
X}
X
X/* raw io write on the device */
X
Xscsiwrite(dev)
Xint dev;
X{
X strlog(0, minor(dev), 0, SL_TRACE, "scsiwrite on unit %d\b", UNITNO(minor(dev)));
X physio(scsistrategy,NULL,dev,B_WRITE);
X}
X
X/* This formats the entire scsi drive. */
X
Xstatic int formatscsidrive(unit,blocksize,interleave)
Xint unit,blocksize,interleave;
X{
X char cmd[10],buf[30];
X
X if (blocksize == 0)
X blocksize=512;
X printf("scsi: formatting unit %d with blocksize=%d, interleave=%d\n",
X unit,blocksize,interleave);
X
X cmd[0]=SCSIMODESELECT;
X cmd[1]=0;
X cmd[2]=0;
X cmd[3]=0;
X/* cmd[4]=12; /* Parameter length */
X cmd[4]=12+16; /* Parameter length with read ahead enabled */
X cmd[5]=0;
X
X buf[0]=0;
X buf[1]=0;
X buf[2]=0;
X buf[3]=8;
X buf[4]=0;
X buf[5]=0;
X buf[6]=0;
X buf[7]=0;
X buf[8]=0;
X buf[9]=blocksize>>16;
X buf[10]=blocksize>>8;
X buf[11]=blocksize;
X
X /* Enable read ahead */
X
X buf[12] = 0x38; /* Page code */
X buf[13] = 14; /* Size of Page 38 structure which follows */
X buf[14] = 0x11; /* Turn on cache */
SHAR_EOF
echo "End of st01 part 2"
echo "File scsi.c is continued in part 3"
echo "3" > shar3_seq_.tmp
exit 0mike@cimcor.mn.org (Michael Grenier) (01/02/91)
#!/bin/sh
# this is st01.03 (part 3 of st01)
# do not concatenate these parts, unpack them in order with /bin/sh
# file scsi.c continued
#
if touch 2>&1 | fgrep 'amc' > /dev/null
then TOUCH=touch
else TOUCH=true
fi
if test ! -r shar3_seq_.tmp; then
echo "Please unpack part 1 first!"
exit 1
fi
(read Scheck
if test "$Scheck" != 3; then
echo "Please unpack part $Scheck next!"
exit 1
else
exit 0
fi
) < shar3_seq_.tmp || exit 1
echo "x - Continuing file scsi.c"
sed 's/^X//' << 'SHAR_EOF' >> scsi.c &&
X buf[15] = 0xff; /* Threshold */
X buf[16] = 0x34; /* Max Prefetch size */
X buf[17] = 0; /* Fill out rest of structure */
X buf[18] = 0; /* Fill out rest of structure */
X buf[19] = 0; /* Fill out rest of structure */
X buf[20] = 0; /* Fill out rest of structure */
X buf[21] = 0; /* Fill out rest of structure */
X buf[22] = 0; /* Fill out rest of structure */
X buf[23] = 0; /* Fill out rest of structure */
X buf[24] = 0; /* Fill out rest of structure */
X buf[25] = 0; /* Fill out rest of structure */
X buf[26] = 0; /* Fill out rest of structure */
X buf[27] = 0; /* Fill out rest of structure */
X
X if (doscsicmd(unit,cmd,buf,12 + 16,5,2,1,0,0,NULL) != COK)
X printf("scsi: warning: mode select command returned error from drive %d\n",
X unit);
X cmd[0]=SCSIFORMATUNIT;
X cmd[1]=0; /* primary and grown defect list only */
X cmd[2]=0; /* data pattern */
X cmd[3]=interleave>>8;
X cmd[4]=interleave;
X cmd[5]=0;
X
X if (doscsicmd(unit,cmd,NULL,0,0,0,1,0,0,NULL) != COK)
X {
X printf("scsi: format failure.\n");
X return 0;
X }
X printf("scsi: format complete.\n");
X return 1;
X}
X
X/* This checks that the current user is the super-user. Returns 0 if not. */
X
Xstatic int chksuper()
X{
X if (u.u_uid != 0)
X {
X u.u_error=EPERM;
X return 0;
X }
X return 1;
X}
X
X/* ioctl() for this device */
X
Xscsiioctl(dev,cmd,arg,mode)
Xint dev,cmd,mode;
Xchar *arg;
X{
X int unit=UNITNO(minor(dev)),
X part=PARTNO(minor(dev));
X char *cp;
X union io_arg hd_arg; /* hd(7) compatible arguments */
X
X strlog(0, unit, 0, SL_ERROR, "scsiioctl called: unit=%d part=%d cmd=%d arg=%d mode=%d\n",
X unit,part,cmd,arg,mode);
X
X if (unit >= SCSIMAXPARTS ||
X (part != SCSIMAXPARTS-1 && part >= d[unit].nparts))
X {
X u.u_error=EINVAL;
X return;
X }
X switch (cmd)
X {
X case SCSIIOREADCAP:
X if (part == 15)
X suword((int *)arg,(int)d[unit].capacity);
X else
X suword((int *)arg,(int)d[unit].parts[part].len);
X break;
X case SCSIIOREADTYPE:
X for (cp=d[unit].drivename;*cp;cp++,arg++)
X subyte(arg,*cp);
X subyte(arg,0);
X break;
X case SCSIIOSETBLK:
X if (!chksuper()){
X u.u_error=EPERM;
X break;
X }
X d[unit].blocksize=fuword((int *)arg);
X break;
X case SCSIIOREASSIGN: {
X int bad_block;
X if (!chksuper()){
X u.u_error=EPERM;
X break;
X }
X if ((bad_block = (int) arg) < 0) {
X strlog(0, unit, 0, SL_ERROR, "scsi: Reassign argument was less than zero!\n");
X u.u_error = EFAULT;
X break;
X }
X if (bad_block >= d[unit].parts[part].len) {
X strlog(0, unit, 0, SL_ERROR, "scsi: Reassign argument (%d) was greater than allowed for partition %d\n",
X bad_block, part);
X u.u_error = EINVAL;
X break;
X }
X strlog(0, unit, 0, SL_ERROR, "scsi: Reassigning unit %d, partition %d, block %d = ",
X unit, part, bad_block);
X bad_block += d[unit].parts[part].start;
X strlog(0, unit, 0, SL_ERROR, "raw block %d\n", bad_block);
X if (reassign(unit, bad_block) != COK) {
X strlog(0, unit, 0, SL_ERROR, " - Reassign of %d FAILED!\n", bad_block);
X u.u_error = EIO;
X }
X }
X break;
X case SCSIIORSENSE: {
X char buf[50], i;
X for (i = 0; i < sizeof(buf); i++)
X buf[i] = 0;
X if (requestsense(unit,buf,sizeof(buf),1) != COK) {
X u.u_error = EIO;
X break;
X }
X /* Copy Extended Sense Information */
X if (copyout(buf, (char *) arg, 6) == -1)
X u.u_error = EFAULT;
X }
X break;
X
X case SCSIIOFORMAT:
X if (!chksuper()) {
X u.u_error=EPERM;
X break;
X }
X if (!formatscsidrive(unit,d[unit].blocksize,fuword((int *)arg)))
X u.u_error=EIO;
X /* fall to next case */
X case SCSIIOINITUNIT:
X initdrive(unit);
X break;
X case SCSIIOGETBLKSZ:
X suword((int *)arg,d[unit].blocksize);
X break;
X
X /* UNIX V/386 hd(7) compatible ioctls */
X
X case V_CONFIG :
X printf("scsi: Attempt to set drive confuration ignored!\n");
X break;
X
X case V_REMOUNT : /* A do nothing command */
X break;
X
X case V_ADDBAD : /* use a SCSI reassign command, eventually */
X if (copyin(arg, &hd_arg, sizeof(hd_arg.ia_abs)) < 0) {
X u.u_error = EINVAL;
X break;
X }
X printf("scsi : Ignoring attempt to remap block %d via V_ADDBAD\n",
X hd_arg.ia_abs.bad_sector);
X strlog(0, unit, 0, SL_ERROR, "scsi : Ignoring attempt to remap block %d via V_ADDBAD\n",
X hd_arg.ia_abs.bad_sector);
X break;
X
X case V_GETPARMS : {/*This is not gaurenteed to work with any other than
X 512 byte sectors! */
X struct disk_parms dp;
X dp.dp_type = DPT_WINI;
X dp.dp_heads = 10; /* Make up something reasonable */
X dp.dp_cyls = 100;
X dp.dp_sectors = 44; /* Wren IV */
X dp.dp_secsiz = d[unit].blocksize;
X dp.dp_ptag = (unit == 15) ? V_BACKUP : V_USR;
X dp.dp_pflag = V_VALID;
X dp.dp_pstartsec = d[unit].parts[part].start;
X dp.dp_pnumsec = d[unit].parts[part].len;
X if (copyout(&dp, arg, sizeof(dp)) < 0 )
X u.u_error = EFAULT;
X break;
X }
X default:
X u.u_error=EINVAL;
X break;
X }
X}
SHAR_EOF
echo "File scsi.c is complete" &&
$TOUCH -am 0101184991 scsi.c &&
chmod 0644 scsi.c ||
echo "restore of scsi.c failed"
set `wc -c scsi.c`;Wc_c=$1
if test "$Wc_c" != "50082"; then
echo original size 50082, current size $Wc_c
fi
# ============= scsiasm.s ==============
echo "x - extracting scsiasm.s (Text)"
sed 's/^X//' << 'SHAR_EOF' > scsiasm.s &&
X .file "scsiasm.s"
X/
X/ fast transfer to/from the scsi controller
X/
X/ Copyright (c) 1988 Tatu Yl|nen
X/ All rights reserved.
X/
X
X .text
X .align 4
X .globl getfromscsi
X
Xgetfromscsi:
X pushl %ebp
X movl %esp,%ebp
X pushl %esi
X pushl %edi
X push %es
X movw %ds,%ax
X movw %ax,%es
X movl 8(%ebp),%edi
X movl 12(%ebp),%ecx
X movl scsidataport,%esi
X cld
X rep
X smovb
X pop %es
X popl %edi
X popl %esi
X popl %ebp
X ret
X
X .globl sendtoscsi
Xsendtoscsi:
X pushl %ebp
X movl %esp,%ebp
X pushl %esi
X pushl %edi
X push %es
X movw %ds,%ax
X movw %ax,%es
X movl 8(%ebp),%esi
X movl 12(%ebp),%ecx
X movl scsidataport,%edi
X cld
X rep
X smovb
X pop %es
X popl %edi
X popl %esi
X popl %ebp
X ret
SHAR_EOF
$TOUCH -am 1027195290 scsiasm.s &&
chmod 0644 scsiasm.s ||
echo "restore of scsiasm.s failed"
set `wc -c scsiasm.s`;Wc_c=$1
if test "$Wc_c" != "805"; then
echo original size 805, current size $Wc_c
fi
# ============= scsipart.c ==============
echo "x - extracting scsipart.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > scsipart.c &&
X/*
X
XSCSI disk partitioning program
X
XCopyright (c) 9.6.1988 Tatu Yl|nen
X All rights reserved.
X
X*/
X
X/* Modification history.
X *
X * M000 det@hawkmoon.MN.ORG -- Sat Dec 30 23:09:48 CST 1989
X * Moved raw scsi device name to user specified name. Ostensibly
X * it should be "/dev/rdsk/?sf" for consistency with the rest of
X * disk drive devices.
X * Added -h for a usage line.
X * Added -uunit# for command line specification of the scsi unit#.
X * Print out number of minutes format took at completion of format.
X */
X
X#include <stdio.h>
X#include <fcntl.h>
X/* beg M000 */
X#include <time.h>
X#include <string.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X/* end M000 */
X#include "st01.h"
X
X/* beg M000 */
X#define DEV "/dev/rdsk/%dsf" /* must be raw device to use ioctls */
X#define HELP "man scsipart"
X#define I_USAGE "Usage: %s [-hH] [-uunit#] special\n"
X#define then
X/* end M000 */
X
Xchar drivename[80];
Xchar buf[4096];
Xint capacity;
Xint interleave,blocksize;
X
Xint nparts;
Xlong partstart[SCSIMAXPARTS];
Xlong partlen[SCSIMAXPARTS];
X/* beg M000 */
Xlong t;
Xint drivenumber;
X
Xint scsiunit = -1;
X/* end M000 */
Xint scsihandle;
X
Xint asknumber(s,low,high)
Xchar *s;
Xint low,high;
X{
X int a;
X
X while (1)
X {
X /* beg M000 */
X printf("%s ",s);
X /* end M000 */
X if (scanf("%d",&a) == 1 && a >= low && a <= high)
X return a;
X printf("invalid input, try again.\n");
X fflush(stdin);
X }
X}
X
Xreadparts()
X{
X unsigned char *cp;
X
X lseek(scsihandle,0l,0);
X if (read(scsihandle,buf,1024) != 1024)
X {
X perror("Could not read partition table");
X exit(1);
X }
X for (nparts=0,cp=(unsigned char *)buf;
X cp[0] || cp[1] || cp[2] || cp[3] || cp[4] || cp[5];
X nparts++,cp+=6)
X {
X if (nparts >= SCSIMAXPARTS-1)
X {
X printf("Invalid partition table - assuming no partitions\n");
X nparts=0;
X return;
X }
X partstart[nparts]=(cp[0] << 16) + (cp[1] << 8) + cp[2];
X partlen[nparts]=(cp[3] << 16) + (cp[4] << 8) + cp[5];
X }
X}
X
Xsaveparts()
X{
X int a;
X char *cp;
X
X for (a=0,cp=buf;a<nparts;a++,cp+=6)
X {
X cp[0]=partstart[a]>>16;
X cp[1]=partstart[a]>>8;
X cp[2]=partstart[a];
X cp[3]=partlen[a]>>16;
X cp[4]=partlen[a]>>8;
X cp[5]=partlen[a];
X }
X memset(cp,0,6);
X lseek(scsihandle,0l,0);
X if (write(scsihandle,buf,1024) != 1024)
X {
X printf("error saving partition table\n");
X exit(1);
X }
X}
X
Xint addpart(s,l)
Xlong s,l;
X{
X if (nparts >= SCSIMAXPARTS-1)
X {
X printf("too many partitions\n");
X return 0;
X }
X partstart[nparts]=s;
X partlen[nparts]=l;
X nparts++;
X return 1;
X}
X
Xint delpart(n)
Xint n;
X{
X int a;
X
X if (n < 0 || n >= nparts)
X {
X printf("invalid partition number\n");
X return 0;
X }
X for (a=n;a<nparts-1;a++)
X {
X partstart[a]=partstart[a+1];
X partlen[a]=partlen[a+1];
X }
X nparts--;
X return 1;
X}
X
Xprintparts()
X{
X int a;
X
X printf("capacity=%ld. Defined partitions:\n",capacity);
X for (a=0;a<nparts;a++)
X {
X printf(" %d: start=%ld len=%ld blocks\n",a,partstart[a],partlen[a]);
X }
X if (nparts == 0)
X printf(" no partitions defined.\n");
X}
X
X/* beg M000 */
Xmain(argc,argv)
X int argc;
X char *argv[];
X/* end M000 */
X{
X int a;
X long s,l;
X /* beg M000 */
X char *pid;
X char *usage;
X extern char *optarg;
X extern int optind;
X struct stat stbuf;
X
X pid=strrchr(argv[0],'/');
X if (!pid) pid=argv[0]; else ++pid;
X usage=(char *)malloc(sizeof(I_USAGE)+strlen(pid)+1);
X sprintf(usage,I_USAGE,pid);
X while ((a=getopt(argc,argv,"hHu:"))!=EOF)
X {switch (a)
X {default:
X fprintf(stderr,usage);
X return(0);
X case 'H':
X return(system(HELP));
X case 'u':
X scsiunit=atoi(optarg);
X break;
X }
X }
X
X if (optind>=argc) /* there is NO default drive; for safety */
X then
X {fprintf(stderr,usage);
X return(1);
X }
X
X printf("\nscsi disk drive formatting and partitioning utility V1.0\n");
X printf("Copyright (c) 9.6.1988 Tatu Yl|nen\n\n");
X printf("Warning: It is easy to destroy data with this program. Abort now\n");
X printf("if you are not sure what you are doing.\n");
X
X /* make some simple consistency checks for the user...
X */
X if (stat(argv[optind],&stbuf))
X then
X {fprintf(stderr,"%s: no such file `%s'\n",pid,argv[optind]);
X return(2);
X }
X /* Why do i check the mode vs 30000 instead of doing "stbuf.st_mode&S_IFCHR" ?
X * Good question. the result was always TRUE for both raw and block devices.
X * I don't know why.
X */
X if (stbuf.st_mode>030000)
X then
X {fprintf(stderr,"%s: must be a raw device\n",pid);
X return(3);
X }
X
X if (scsiunit<0)
X then
X {sprintf(buf,"Enter scsi unit id for %s? ",argv[1]);
X scsiunit=asknumber(buf,0,7);
X }
X /* end M000 */
X scsihandle=open(argv[optind],O_RDWR);
X if (scsihandle == -1)
X {
X perror(argv[optind]);
X exit(1);
X }
X if (ioctl(scsihandle,SCSIIOREADTYPE,drivename) == -1)
X perror("SCSIIOREADTYPE ioctl");
X if (ioctl(scsihandle,SCSIIOREADCAP,&capacity) == -1)
X perror("SCSIIOREADCAP ioctl");
X if (ioctl(scsihandle,SCSIIOGETBLKSZ,&blocksize) == -1)
X perror("SCSIIOGETBLKSZ ioctl");
X printf("Drive %d: %d blocks, blocksize=%d\n",scsiunit,capacity,blocksize);
X printf("%s\n",drivename);
X printf("Do you wish to format the unit (y/n)?\n");
X fflush(stdin);
X gets(buf);
X if (buf[0] == 'y' || buf[0] == 'Y')
X {
X printf("FORMATTING WILL DESTROY ALL AND ANY DATA ON THE DRIVE.\n");
X printf("ARE YOU SURE YOU WANT TO DO THIS (Y/N)?\n");
X gets(buf);
X if (buf[0] != 'y' && buf[0] != 'Y')
X exit(1);
X blocksize=asknumber("Enter block size for the drive (usually 512)?",
X 0,4096);
X interleave=asknumber("Enter interleave factor for the drive (usually between 1 and 10)?",
X 0,34);
X /* beg M000 */
X time(&t);
X /* end M000 */
X if (ioctl(scsihandle,SCSIIOSETBLK,&blocksize) == -1 ||
X ioctl(scsihandle,SCSIIOFORMAT,&interleave) == -1)
X {
X perror("Format failure");
X exit(1);
X }
X /* beg M000 */
X printf("\007Format complete in %.1f minutes.\n",(time(0L)-t)/60.0);
X /* end M000 */
X if (ioctl(scsihandle,SCSIIOREADCAP,&capacity) == -1)
X perror("SCSIIOREADCAP ioctl");
X nparts=0;
X saveparts();
X /* beg M000 */
X printf("Drive capacity is %d blocks.\n",capacity);
X /* end M000 */
X }
X if (ioctl(scsihandle,SCSIIOINITUNIT,NULL) == -1)
X perror("SCSIIOINITUNIT ioctl");
X if (ioctl(scsihandle,SCSIIOREADTYPE,drivename) == -1)
X perror("SCSIIOREADTYPE ioctl");
X if (ioctl(scsihandle,SCSIIOREADCAP,&capacity) == -1)
X perror("SCSIIOREADCAP ioctl");
X if (ioctl(scsihandle,SCSIIOGETBLKSZ,&blocksize) == -1)
X perror("SCSIIOGETBLKSZ ioctl");
X if (!readparts())
X nparts=0;
X while (1)
X {
X printparts();
X a=asknumber("1 add partition 2 delete partition 8 quit (no save) 9 save",
X 1,9);
X switch (a)
X {
X case 1: /* add partition */
X s=asknumber("enter partition start in blocks?",0,capacity);
X l=asknumber("enter partition length in blocks?",0,capacity-s);
X addpart(s,l);
X break;
X case 2: /* delete partition */
X a=asknumber("enter partition number to delete?",0,nparts-1);
X delpart(a);
X break;
X case 8: /* quit no save */
X printf("partition table not modified\n");
X exit(1);
X case 9: /* quit, save modifications */
X printf("saving partition table\n");
X saveparts();
X if (ioctl(scsihandle,SCSIIOINITUNIT,NULL) == -1)
X perror("SCSIIOINITUNIT ioctl");
X exit(0);
X default:
X printf("invalid command\n");
X break;
X }
X }
X}
SHAR_EOF
$TOUCH -am 1226195090 scsipart.c &&
chmod 0644 scsipart.c ||
echo "restore of scsipart.c failed"
set `wc -c scsipart.c`;Wc_c=$1
if test "$Wc_c" != "7896"; then
echo original size 7896, current size $Wc_c
fi
# ============= sdevice ==============
echo "x - extracting sdevice (Text)"
sed 's/^X//' << 'SHAR_EOF' > sdevice &&
X* SCSI Host Device Driver
Xaha Y 1 5 1 11 330 332 0 0
Xaha N 1 5 1 0 0 0 0 0
Xasy Y 1 7 1 4 3f8 3ff 0 0
Xasy N 2 7 1 3 2f8 2ff 0 0
Xclone Y 1 0 0 0 0 0 0 0
Xcmos Y 1 5 1 8 0 0 0 0
Xcpyrt Y 1 0 0 0 0 0 0 0
Xdu N 1 0 0 0 0 0 0 0
X* SCSI ESIX Common Host Adapter Driver (SCSI)
Xecha Y 1 0 0 0 0 0 0 0
X*
X* ESIX Common Hard Disk Driver
X*
Xechd Y 1 0 0 0 0 0 0 0
Xfd Y 1 4 2 6 3f0 3f7 0 0
Xffs Y 1 0 0 0 0 0 0 0
Xfha N 1 5 1 12 0 0 ce000 cffff
Xfp Y 1 1 1 13 0 0 0 0
Xgentty Y 1 0 0 0 0 0 0 0
X*
X* ST506/ESDI Hard Disk Driver
X*
Xhd Y 1 5 1 14 320 32f 0 0
Xip Y 1 0 0 0 0 0 0 0
Xipc Y 1 0 0 0 0 0 0 0
Xiplan Y 1 0 0 0 0 0 0 0
Xippln Y 1 0 0 0 0 0 0 0
Xkd Y 1 6 3 1 60 62 0 0
Xldterm Y 16 0 0 0 0 0 0 0
Xlog Y 1 0 0 0 0 0 0 0
X*****************************************************************
X* *
X* WARNING : if the second field of any entry is changed, make *
X* sure that the corresponding parallel port entry in *
X* /etc/conf/pack.d/lp/space.c file is also updated *
X* before rebuilding the kernel. *
X* *
X*****************************************************************
Xlp N 1 3 2 7 3bc 3be 0 0
Xlp Y 2 3 2 7 378 37a 0 0
Xlp N 3 3 2 7 278 27a 0 0
Xmem Y 1 0 0 0 0 0 0 0
Xmsg Y 1 0 0 0 0 0 0 0
Xmux Y 1 0 0 0 0 0 0 0
Xnmi Y 1 0 0 0 0 0 0 0
Xosm Y 1 0 0 0 0 0 0 0
Xpln Y 1 0 0 0 0 0 0 0
Xprf Y 1 0 0 0 0 0 0 0
X* Pseudo Terminal Master side
Xptcp Y 1 0 0 0 0 0 0 0
Xptem Y 16 0 0 0 0 0 0 0
Xptm Y 16 0 0 0 0 0 0 0
Xpts Y 1 0 0 0 0 0 0 0
X* Pseudo Terminal Slave side
Xptyp Y 1 0 0 0 0 0 0 0
Xramd N 2 0 0 0 0 0 0 0
Xs52k N 1 0 0 0 0 0 0 0
X*
X* SCSI Hard Disk Driver
X*
Xschd Y 1 0 0 0 0 0 0 0
X* SCSI Magtape Device
Xscmt Y 1 0 0 0 0 0 0 0
Xscsi Y 1 5 1 5 0 0 df000 e0000
Xsem Y 1 0 0 0 0 0 0 0
Xshm Y 1 0 0 0 0 0 0 0
Xsl N 1 7 1 4 3f8 3ff 0 0
Xsl N 2 7 1 3 2f8 2ff 0 0
Xslip Y 1 0 0 0 0 0 0 0
Xsxt Y 1 0 0 0 0 0 0 0
Xtcp Y 1 0 0 0 0 0 0 0
Xtimod Y 1 0 0 0 0 0 0 0
Xtirdwr Y 1 0 0 0 0 0 0 0
Xudp Y 1 0 0 0 0 0 0 0
Xudpm Y 1 0 0 0 0 0 0 0
Xvx Y 1 0 0 0 0 0 0 0
Xweitek Y 1 0 0 0 0 0 0 0
Xxkb Y 1 0 0 0 0 0 0 0
Xxms Y 1 6 1 3 0 0 0 0
Xxsd Y 1 0 0 0 0 0 0 0
Xxsem Y 1 0 0 0 0 0 0 0
Xxt Y 1 0 0 0 0 0 0 0
SHAR_EOF
$TOUCH -am 1027195590 sdevice &&
chmod 0644 sdevice ||
echo "restore of sdevice failed"
set `wc -c sdevice`;Wc_c=$1
if test "$Wc_c" != "2074"; then
echo original size 2074, current size $Wc_c
fi
# ============= st01.h ==============
echo "x - extracting st01.h (Text)"
sed 's/^X//' << 'SHAR_EOF' > st01.h &&
X/*
X
XSCSI disk driver for unix system V (Microport system V/386)
XThis driver uses the ST-01 controller. This supports multiple initiators
Xand multiple targets.
X
XCopyright (c) 9.6.1988 Tatu Yl|nen
X All rights reserved.
X
X*/
X
X#define SCSIMAXDRIVES 4 /* max # disk drives supported */
X#define SCSIMAXPARTS 16 /* max partitions/drive */
X
Xtypedef struct scsidrivest
X{
X char drivename[64]; /* drive type identification string */
X int blocksize; /* logical block size; if 0, not present */
X long capacity; /* total disk capacity in blocks */
X char nomsgs; /* set if drive does not support messages */
X int nparts; /* # partitions */
X struct partst
X {
X long start; /* starting sector number */
X long len; /* partition length */
X } parts[SCSIMAXPARTS];
X
X char *xferbuf; /* transfer buffer address */
X int xferlen; /* data len to transfer */
X char *xfercmd; /* command to transfer */
X char xferslow; /* true if watch out for slow transfers */
X char xferstatus; /* status byte received from target */
X int xfertimeout; /* if nonzero, timeout in 1/10 sec */
X int xfertime; /* if nonzero, elapsed time waiting (1/10 sec) */
X int xferretries; /* if nonzero, retry the current request and decrement*/
X char xferphys; /* if true, transferring data with raw io */
X char xferpolled; /* if true, transfer must be polled */
X
X /* Info on current command */
X
X int num_blocks, logical_block, scsicmd;
X
X char *savedbuf; /* saved buffer */
X int savedlen; /* saved lenght */
X char *savedcmd; /* saved command */
X
X char *origbuf; /* original buffer */
X int origlen; /* original length */
X char *origcmd; /* original command */
X
X struct buf *reqlist; /* queued requests */
X struct buf *currentbuf; /* buffer being executed */
X
X char connected; /* true if connection to drive established */
X char busy; /* true if currently executing a command */
X} SCSIDRIVE;
X
X#define SCSIIOREADCAP 1 /* read capacity of partition or drive
X (if part == 15); arg=&capacity */
X#define SCSIIOREADTYPE 2 /* read drive type name (as a string); arg=buf */
X#define SCSIIOFORMAT 3 /* reformat the drive; arg=&interleave */
X#define SCSIIOSETBLK 4 /* set drive block size for format; arg=&size */
X#define SCSIIOINITUNIT 5 /* re-initializes the unit, reading partition table */
X#define SCSIIOGETBLKSZ 6 /* read sector size */
X#define SCSIIORSENSE 7 /* Return 4 byte request sense data */
X#define SCSIIOREASSIGN 8 /* reassign given block */
X
X/* Partitioning is done by writing on the first block of partition 15
X (the entire drive). Note that drive block size may not be the same as
X system block size. Partition table format: each entry 3 bytes start,
X 3 bytes length (in blocks, msb first), terminated by 6 zeroes. If first
X partition starts at 0, it will be moved to 1 automatically; this appears
X to be the convention under dos. */
X
SHAR_EOF
$TOUCH -am 1226163690 st01.h &&
chmod 0644 st01.h ||
echo "restore of st01.h failed"
set `wc -c st01.h`;Wc_c=$1
if test "$Wc_c" != "2929"; then
echo original size 2929, current size $Wc_c
fi
# ============= verify.c ==============
echo "x - extracting verify.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > verify.c &&
X
X#include <stdio.h>
X#include <fcntl.h>
X#include "st01.h"
X
X#define NBLOCKS 200 /* Number of blocks to read at a time */
X#define BLOCK_LEN (512*NBLOCKS) /* Number of bytes to read at a time */
X
Xint d_size, cur_block, rfd;
Xchar buf[BLOCK_LEN];
Xchar ans[10];
X
Xint check_sense(rfd)
Xint rfd;
X{
X unsigned char buf[10], address_valid, err_class, err_code, sense;
X unsigned long addr;
X static char *smsg[] =
X { "No Error", "CRC or other error that was recovered",
X "Not Ready", "Medium Error (Bad Block)", "Hardware Failure",
X "Illegal Request or parameter", "Unit Attention - removeable media",
X "Data is protected and not available or changeable",
X "Blank Check", "** Vendor Unique **", "Copy aborted",
X "Target Aborted Command for some reason",
X "Search Data command has found successful comparison",
X "Volume Overflow - unable to write full buffer",
X "Miscompare on a verify command",
X "**Reserved**"};
X
X if (ioctl(rfd, SCSIIORSENSE, buf) < 0) {
X fprintf(stderr,"Unable to get more detail on error..sorry\n");
X perror("Request Sense");
X return 0;
X }
X
X address_valid = buf[0] >> 7;
X err_class = (buf[0] >> 4) & 0x7;
X err_code = buf[0] & 0xf;
X sense = buf[2] & 0xf;
X
X addr = (buf[3] << 24l) +
X (buf[4] << 16l) +
X (buf[5] << 8l ) +
X buf[6];
X fprintf(stderr," Request Sense : Error Class 0x%x, Error Code 0x%x\n",
X err_class, err_code);
X fprintf(stderr," Sense key 0x%x = %s\n", sense, smsg[sense]);
X if (address_valid)
X fprintf(stderr," on logical block %u\n", addr);
X else
X fprintf(stderr,"Unable to determine logical block where failure occured\n");
X return 1;
X}
X
Xlocalize( cur_block )
Xint cur_block;
X{
X int final_block = cur_block + NBLOCKS - 1;
X fprintf(stderr,"Attempting to localize exact blocks\n");
X for (; cur_block <= final_block; cur_block ++){
X fprintf(stderr,"\r %07d ", cur_block);
X
X if (lseek(rfd, cur_block * 512, 0) < 0) {
X fprintf(stderr,"Unable to seek to block %d\n", cur_block);
X perror("lseek");
X exit(2);
X }
X
X if (read(rfd, buf, 512) < 0) {
X fprintf(stderr,"\n Block %07d failed - ",
X cur_block);
X perror("");
X check_sense(rfd);
X fprintf(stderr,"Do you wish to reassign this block? ");
X gets(ans);
X if ((ans[0]== 'Y') || (ans[0] == 'y'))
X if (ioctl(rfd, SCSIIOREASSIGN, cur_block) < 0)
X perror("Unable to reassign block ");
X else
X fprintf(stderr,"Reassign of %d complete\n", cur_block);
X }
X }
X}
X
X
Xint main(argc, argv)
Xint argc;
Xchar **argv;
X{
X int amount_to_read, amount_read;
X
X if (argc != 2) {
X fprintf(stderr,"Usage : verify raw_scsi_device \n");
X exit(1);
X }
X
X if ((rfd = open(argv[1], O_RDONLY)) < 0) {
X fputs("Unable to open character device!\n");
X perror(argv[1]);
X exit(2);
X }
X
X if (ioctl(rfd, SCSIIOREADCAP, &d_size) < 0) {
X fprintf(stderr,"Unable to get size of this partition\n");
X perror(argv[1]);
X exit(5);
X }
X
X fprintf(stderr,"Partition %s has %d blocks\n", argv[1], d_size);
X
X for (cur_block = 0; cur_block < d_size; cur_block += NBLOCKS){
X if (cur_block >= d_size - NBLOCKS)
X amount_to_read = (d_size - cur_block) * 512;
X else
X amount_to_read = NBLOCKS * 512;
X
X fprintf(stderr,"\r %07d ", cur_block);
X if (lseek(rfd, cur_block * 512, 0) < 0) {
X fprintf(stderr,"Unable to seek to block %d\n", cur_block);
X perror("lseek");
X exit(2);
X }
X if (read(rfd, buf, amount_to_read) != amount_to_read) {
X fprintf(stderr,"\n Blocks %07d - %07d failed : amount read = %d - ",
X cur_block, cur_block + amount_to_read / 512, amount_read);
X perror("");
X check_sense(rfd);
X localize(cur_block);
X }
X }
X}
X
X
SHAR_EOF
$TOUCH -am 0101184691 verify.c &&
chmod 0644 verify.c ||
echo "restore of verify.c failed"
set `wc -c verify.c`;Wc_c=$1
if test "$Wc_c" != "3950"; then
echo original size 3950, current size $Wc_c
fi
rm -f shar3_seq_.tmp
echo "You have unpacked the last part"
exit 0