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 0
mike@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