ST-0x 386/ix driver v1.0 pt3/6
Tin Le
tin at szebra.szebra.uucp
Mon Sep 17 16:30:06 AEST 1990
Seagate ST-0x 386/ix driver v1.0
#!/bin/sh
# this is st02.03 (part 3 of a multipart archive)
# do not concatenate these parts, unpack them in order with /bin/sh
# file scsi.c continued
#
if test ! -r _shar_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
) < _shar_seq_.tmp || exit 1
if test ! -f _shar_wnt_.tmp; then
echo 'x - still skipping scsi.c'
else
echo 'x - continuing file scsi.c'
sed 's/^X//' << 'SHAR_EOF' >> 'scsi.c' &&
X
static void startbufferio(unit,bp)
int unit;
struct buf *bp;
{
X 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
/* 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
static void startpendingreq(unit,bp)
int unit;
struct buf *bp;
{
X register struct buf *ap;
X register 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
/* 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
static void marknotbusy(unit,completion)
int unit,completion;
{
X register int x;
X register struct buf *ap;
X
#ifdef DEBUG0
X printf("scsi: marknotbusy unit=%d completion=%d\n",
X unit,completion);
#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);
#ifdef DEBUG0
X printf("scsi: marknotbusy returning\n");
#endif
X splx(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
void scsiintr()
{
X register int a;
X register int x;
X register int unit;
X long l;
X
X if (!(*cmdport & STSEL))
X {
#ifdef DEBUG0
X printf("scsi: intr ignored (no SEL)\n");
#endif
X return; /* The controller should only generate interrupts when select
X rises. */
X }
X for (l=0;l<20000l;l++)
X if (*cmdport & STIO)
X goto gotio;
#ifdef DEBUG0
X printf("scsi: intr ignored (IO timeout)\n");
#endif
X return;
X gotio:
X a=(*scsidataport) & 0xff;
X if (!(a & MYADDR))
X {
#ifdef DEBUG
X printf("scsi: intr ignored (not my addr); addr=%x\n",a);
#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 {
#ifdef DEBUG
X printf("scsi: intr ignored (invalid id); unit=%d a=%x\n",unit,a);
#endif
X return;
X }
X if (unit >= SCSIMAXDRIVES)
X {
#ifdef DEBUG
X printf("scsi: intr ignored (unit %d >= SCSIMAXDRIVES %d)\n",
X unit,SCSIMAXDRIVES);
#endif
X return;
X }
X x=splnointrs();
X if (d[unit].connected || !d[unit].busy)
X {
#ifdef DEBUG
X printf("scsi: intr ignored (internal state): unit=%d connected=%d busy=%d\n",
X unit,d[unit].connected,d[unit].busy);
#endif
X splx(x);
X return;
X }
X if (d[unit].xferpolled)
X { /* ooops... This is not the way it was supposed to happen... */
#ifdef DEBUG
X printf("scsi: intr ignored (xfer is polled); unit=%d\n",unit);
#endif
X splx(x);
X return;
X }
X *cmdport=CMDBASE|CMDBSY|CMDENABLE; /* acknowledge reselection */
X for (l=0;l<20000l;l++)
X if (!(*cmdport & STSEL))
X goto selreleased;
X /* timeout waiting for sel to be released */
X *cmdport=CMDBASE;
#ifdef DEBUG
X printf("scsi: intr ignored (timeout waiting for SEL to be released); unit=%d\n",
X unit);
#endif
X splx(x);
X return;
X selreleased:
X for (l=0;l<20000l;l++)
X if (*cmdport & STBSY)
X goto selectedandhavebsy;
X /* timeout waiting for sel to be released */
X *cmdport=CMDBASE;
#ifdef DEBUG
X printf("scsi: intr ignored (timeout waiting for BSY after SEL to be released); unit=%d\n",
X unit);
#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 doxferbufagain:
X d[unit].connected=0; /* just in case */
X if (a != COK && a != CDISCONNECT)
X { /* We got an error. We must retry the operation, and if the retry
X count has elapsed, complete the operation with error. */
X if (d[unit].xferretries <= 0 || a == CBUSBUSY || a == CNOCONNECT)
X {
#ifdef DEBUG
X printf("scsi: intr: cmd failed (%d); returning error\n",a);
#endif
X *cmdport=CMDBASE;
X marknotbusy(unit,a); /* This may start a new operation */
X }
X else
X {
X d[unit].xferretries--;
#ifdef DEBUG
X printf("scsi: intr: retrying command\n");
#endif
X a=startscsi(unit); /* this will restart the command */
X goto doxferbufagain;
X }
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
/* 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
void scsitick()
{
X register int a;
X register int unit;
X register int 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].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 */
#ifdef DEBUG
X printf("scsi: local intr driven xfer woken up by timer tick; unit=%d\n",
X unit);
#endif
X wakeup(&d[unit].connected); /* !connected tells it to retry */
X continue;
X }
X a=CTIMEOUT;
X retrytickforbuf:
X if (a == COK || a == CDISCONNECT)
X continue;
X if (d[unit].xferretries == 0 || a == CBUSBUSY || a == CNOCONNECT)
X {
#ifdef DEBUG
X printf("scsi: block xfer fails in timer tick; unit=%d, err=%d\n",
X unit,a);
#endif
X marknotbusy(unit,a); /* This may start a new operation */
X continue;
X }
X d[unit].xferretries--;
#ifdef DEBUG
X printf("scsi: xfer retried in timer tick; unit=%d, err=%d\n",
X unit,a);
#endif
X a=startscsi(unit);
X goto retrytickforbuf;
X }
X }
X timeout(scsitick,0,TICKSPERSECOND/10);
X splx(x);
}
X
/* This is the normal strcpy */
X
static void strcpy(d,s)
register unchar *d;
register unchar *s;
{
X while (*s)
X *d++=(*s++);
X *d='\0';
}
X
/* This implements the request sense command. This returns a C* status. */
X
static int requestsense(unit,buf,len,polled)
int unit,len,polled;
unchar *buf;
{
X unchar 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;
X cmd[5]=0;
X
X return doscsicmd(unit,cmd,buf,len,2,3,1,0,polled,NULL);
}
X
/* This tests for drive readiness (with the scsi test unit ready command).
X This returns a C* status. */
X
static int testready(unit)
int unit;
{
X unchar 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
/* This issues the inquiry command to the scsi drive to get its drive type
X and other characteristics. This returns a C* status. */
X
static int doinquiry(unit,buf,len,polled)
int unit,len,polled;
unchar *buf;
{
X unchar 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
/* This reads the storage capacity and block size of the scsi drive */
X
static int readcapacity(unit,buf,len,polled)
int unit,len,polled;
unchar *buf;
{
X unchar 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
/* This is used to initialize the drive at system startup time */
X
static int initdrive(unit)
int unit;
{
X int a,bs;
X unchar 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 }
#ifdef DEBUG0
X printf("scsi: initdrive: requestsense ok\n");
#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 }
#ifdef DEBUG0
X printf("scsi: initdrive: doinquiry ok\n");
#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: %ldMB (%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(" %ldMB",(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
/*---------------------------------------------------------------------
-- scsiinit()
-----------------------------------------------------------------------*/
void scsiinit()
{
X register int a;
X extern char *sptalloc();
X
X printf("\n%s\n",COPYRIGHT);
X if (!baseaddr)
X baseaddr=(unchar *)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 for (a=0;a<SCSIMAXDRIVES;a++)
X {
X initdrive(a);
X }
X printf("\n");
}
X
void scsiopen(dev,flags)
dev_t dev;
int flags;
{
X int unit=UNITNO(minor(dev)),
X part=PARTNO(minor(dev)),
X x;
X
#ifdef DEBUG0
X printf("scsiopen: unit=%d part=%d\n",unit,part);
#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
void scsiclose()
{
#ifdef DEBUG0
X printf("scsiclose called\n");
#endif
X /* do nothing */
}
X
void scsistrategy(bp)
register struct buf *bp;
{
X int unit=UNITNO(minor(bp->b_dev)),
X part=PARTNO(minor(bp->b_dev)),
X nsecs,x;
X register long sec;
X register struct buf *ap;
X register struct buf **app;
X
#ifdef DEBUG0
X printf("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);
#endif
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=splnointrs();
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
/* raw io read on the device */
X
void scsiread(dev)
int dev;
{
X physio(scsistrategy,&scsibuf,dev,B_READ);
}
X
/* raw io write on the device */
X
void scsiwrite(dev)
int dev;
{
X physio(scsistrategy,&scsibuf,dev,B_WRITE);
}
X
/* This formats the entire scsi drive. */
X
static int formatscsidrive(unit,blocksize,interleave)
int unit,blocksize,interleave;
{
X unchar cmd[10], buf[12];
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;
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]=(unchar)(blocksize>>16 & 0xff);
X buf[10]=(unchar)(blocksize>>8 & 0xff);
X buf[11]=(unchar)(blocksize & 0xff);
X
X if (doscsicmd(unit,cmd,buf,12,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]=(unchar)(interleave>>8 & 0xff);
X cmd[4]=(unchar)(interleave & 0xff);
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
/* This checks that the current user is the super-user. Returns 0 if not. */
X
static int chksuper()
{
X if (u.u_uid != 0)
X {
X u.u_error=EPERM;
X return 0;
X }
X return 1;
}
X
/* ioctl() for this device */
X
int scsiioctl(dev,cmd,arg,mode)
int dev, cmd, mode;
unchar *arg;
{
X int unit=UNITNO(minor(dev)),
X part=PARTNO(minor(dev));
X unchar *cp;
X
#ifdef DEBUG0
X printf("scsiioctl: unit=%d part=%d cmd=%d arg=%lx mode=%d\n",
X unit,part,cmd,arg,mode);
#endif /* DEBUG */
X
X u.u_error=0;
X if (unit >= SCSIMAXPARTS ||
X (part != SCSIMAXPARTS-1 && part >= d[unit].nparts))
X {
X u.u_error=EINVAL;
X return(1);
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 break;
X d[unit].blocksize=fuword((int *)arg);
X break;
X case SCSIIOFORMAT:
X if (!chksuper())
X break;
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 default:
X u.u_error=EINVAL;
X break;
X }
X return(0);
}
X
int scsiprint()
{
X printf("scsiprint called:\n");
X return(0);
}
SHAR_EOF
echo 'File scsi.c is complete' &&
chmod 0644 scsi.c ||
echo 'restore of scsi.c failed'
Wc_c="`wc -c < 'scsi.c'`"
test 43689 -eq "$Wc_c" ||
echo 'scsi.c: original size 43689, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= scsi.h ==============
if test -f 'scsi.h' -a X"$1" != X"-c"; then
echo 'x - skipping scsi.h (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting scsi.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'scsi.h' &&
/*
X
9/16/90 Tin Le
X - Released to the world version 1.0
X
X Modified for Interactive 386/ix v2.0.2 with some bug fixes
X and minor enhancements.
X
SCSI disk driver for unix system V (Microport system V/386)
This driver uses the ST-01 controller. This supports multiple initiators
and multiple targets.
X
Copyright (c) 9.6.1988 Tatu Yl|nen
Copyright (c) 16.9.1988 Tin Le
X All rights reserved.
X
*/
X
#define SCSIMAXDRIVES 4 /* max # disk drives supported */
#define SCSIMAXPARTS 16 /* max partitions/drive */
X
typedef struct scsidrivest
{
X unsigned 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 unsigned 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 unsigned char *xferbuf; /* transfer buffer address */
X int xferlen; /* data len to transfer */
X unsigned 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 unsigned char *savedbuf; /* saved buffer */
X int savedlen; /* saved lenght */
X unsigned char *savedcmd; /* saved command */
X
X unsigned char *origbuf; /* original buffer */
X int origlen; /* original length */
X unsigned 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 */
} SCSIDRIVE;
X
#define SCSIIOREADCAP 1 /* read capacity of partition or drive
X (if part == 15); arg=&capacity */
#define SCSIIOREADTYPE 2 /* read drive type name (as a string); arg=buf */
#define SCSIIOFORMAT 3 /* reformat the drive; arg=&interleave */
#define SCSIIOSETBLK 4 /* set drive block size for format; arg=&size */
#define SCSIIOINITUNIT 5 /* re-initializes the unit, reading partition table */
#define SCSIIOGETBLKSZ 6 /* read sector size */
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
/* External defines for scsi routines 8/13/90 TL */
extern void scsiintr();
extern void scsitick();
extern scsiioctl();
extern void scsiinit();
extern void scsiopen();
extern void scsiclose();
extern void scsistrategy();
extern void scsiread();
extern void scsiwrite();
extern void sendtoscsi();
extern void getfromscsi();
X
/* ctypes defines for scsiinit() 8/20/90 TL */
#define isspace(c) ((c) == ' ' ? 1 : ((c) == '\t' ? 1 : \
X ((c) == '\n' ? 1 : ((c) == '\r' ? 1 : 0))))
X
#define isxdigit(c) ((c) >= '0' && (c) <= '9' ? 1 : \
X ((c) >= 'a' && (c) <= 'f' ? 1 : \
X ((c) >= 'A' && (c) <= 'F' ? 1 : 0)))
SHAR_EOF
chmod 0644 scsi.h ||
echo 'restore of scsi.h failed'
Wc_c="`wc -c < 'scsi.h'`"
test 3587 -eq "$Wc_c" ||
echo 'scsi.h: original size 3587, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= scsiasm.s ==============
if test -f 'scsiasm.s' -a X"$1" != X"-c"; then
echo 'x - skipping scsiasm.s (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting scsiasm.s (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'scsiasm.s' &&
X .file "scsiasm.s"
/
/ fast transfer to/from the scsi controller
/
/ Copyright (c) 1988 Tatu Yl|nen
/ All rights reserved.
/
X
X .text
X .align 4
X .globl getfromscsi
X
getfromscsi:
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
sendtoscsi:
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
chmod 0644 scsiasm.s ||
echo 'restore of scsiasm.s failed'
Wc_c="`wc -c < 'scsiasm.s'`"
test 805 -eq "$Wc_c" ||
echo 'scsiasm.s: original size 805, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= scsipart.c ==============
if test -f 'scsipart.c' -a X"$1" != X"-c"; then
echo 'x - skipping scsipart.c (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting scsipart.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'scsipart.c' &&
/*
X
SCSI disk partitioning program
X
Copyright (c) 9.6.1988 Tatu Yl|nen
Copyright (c) 16.9.1990 Tin Le
X All rights reserved.
X
*/
X
#include <stdio.h>
#include <fcntl.h>
#include "scsi.h"
X
#define DEV "/dev/rscsi%ds" /* must be raw device to use ioctls */
#define BDEV "/dev/scsi%ds" /* must be block device to use lseeks */
X
char cdevname[80];
char bdevname[80];
unsigned char drivename[80];
unsigned char buf[4096];
int capacity;
int interleave,blocksize;
X
int nparts;
long partstart[SCSIMAXPARTS];
long partlen[SCSIMAXPARTS];
X
int scsiunit;
int scsihandle;
int scsiblkhandle;
X
int asknumber(s,low,high)
char *s;
int low,high;
{
X int a;
X
X while (1)
X {
X printf("%s [%d-%d]: ",s, low, high);
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
readparts()
{
X unsigned char *cp;
X
X lseek(scsiblkhandle,0l,0);
X if (read(scsiblkhandle,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
saveparts()
{
X int a;
X unsigned 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(scsiblkhandle,0l,0);
X if (write(scsiblkhandle,buf,1024) != 1024)
X {
X printf("error saving partition table\n");
X exit(1);
X }
}
X
int addpart(s,l)
long s,l;
{
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
int delpart(n)
int n;
{
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
printparts()
{
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
main()
{
X int a;
X long s,l;
X int errcount=10;
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 scsiunit=asknumber("Enter number of the scsi disk you wish to partition?",
X 0,7);
X sprintf(cdevname,DEV,scsiunit);
X sprintf(bdevname,BDEV,scsiunit);
X scsihandle=open(cdevname,O_RDWR);
X if (scsihandle == -1)
X {
X perror(cdevname);
X exit(1);
X }
X scsiblkhandle=open(bdevname,O_RDWR);
X if (scsiblkhandle == -1)
X {
X perror(bdevname);
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 while (ioctl(scsihandle,SCSIIOSETBLK,&blocksize) == -1 ||
X ioctl(scsihandle,SCSIIOFORMAT,&interleave) == -1 && errcount-->0) ;
X if (errcount <= 0)
X {
X perror("Format failure");
X exit(1);
X }
X if (ioctl(scsihandle,SCSIIOREADCAP,&capacity) == -1)
X perror("SCSIIOREADCAP ioctl");
X nparts=0;
X saveparts();
X printf("Format complete. Drive capacity is %d blocks.\n",capacity);
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 }
}
SHAR_EOF
chmod 0644 scsipart.c ||
echo 'restore of scsipart.c failed'
Wc_c="`wc -c < 'scsipart.c'`"
test 6168 -eq "$Wc_c" ||
echo 'scsipart.c: original size 6168, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= scsipart.uu ==============
if test -f 'scsipart.uu' -a X"$1" != X"-c"; then
echo 'x - skipping scsipart.uu (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting scsipart.uu (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'scsipart.uu' &&
begin 755 scsipart
M3 $$ $--]";(<P ;P( !P P$+ 0 '$@ &0= "$&P U - #L
M"$ +G1E>'0 #0 T !Q( #0 ( "YD
M871A [ A .P(0 !D'0 [$@ $ N8G-S
M % F0 !0)D A!L " +F-O;6UE;G0
M '@- !09@ ( ,.0D)"#[ B+[(M%"(U4A1")
M%>P(0 !2C54,4E#HW____VH Z. S "#Q 3H] , (/$#%#HQT< &H N $
M ": < ],.0D)#K7?]U$/]U#/]U"&@4"4 Z*\) "#Q!"-1?Q0:"$)
M0 #H/@H (/$"#T! =16+10PY1?Q\#8M%$#E%_'\%BT7\ZQAH) E .AU
M"0 66AX(4 Z)8] !9ZZ7)PU6+[%#KG9"0D.DR 0 D&H :@#_-=@W0 #H
MOQT (/$#&@ ! :$ G0 #_-=@W0 #H6T8 (/$##T ! =!-H/PE .B?
M" 66H!Z -' !9QP5,-T ,=%_$ G0 #IB )"X#P #D%3#=
M 'P;:%X)0 #HY @ %G'!4PW0 Z:T "0H4PW0 "+5?P/MA+!XA"+
M3?P/MDD!P>$( ]&+3?P/MDD" ]&)%(50-T H4PW0 "+5?P/ME(#P>(0BTW\
M#[9)!,'A" /1BTW\#[9)!0/1B12%D#= /\%3#= (-%_ :+1?R . /A6W_
M__^+1?R > $ #X5 at ____BT7\@'@" ^%4____XM%_(!X P /A4;___^+1?R
M> 0 #X4Y____BT7\@'@% ^%+/___\G#58OL4.G&_O__Z>( "0QT7\
M ,=%^$ G0 #K<HM%^(M5_(L4E5 W0 #!^A"($(M%^(M5_(L4E5 W0 #!^@B(
M4 &+1?B+5?R*%)50-T B% "BT7XBU7\BQ25D#= ,'Z$(A0 XM%^(M5_(L4
ME9 W0 #!^@B(4 2+1?B+5?R*%)60-T B% %_T7\@T7X!J%,-T .47\?(1J
M!FH _W7XZ%Y$ "#Q QJ &H _S78-T Z.0; "#Q QH 0 &A )T _S78
M-T Z"1% "#Q P] 0 '03:) )0 #H0 < %EJ >@H10 6<G#58OL@^P(
MZ13___^0D.M,N \ Y!4PW0 !\$FBN"4 Z \' !9N #K*Z%,-T
MBU4(B12%4#= *%,-T BU4,B12%D#= /\%3#= +@! ZP#)PU6+[.NO
MD.MQ at WT( 'P*H4PW0 Y10A\$FC#"4 Z+@& !9N #K38M%"(E%_.LM
MBT7\BU7\0HL4E5 W0 ")%(50-T BT7\BU7\0HL4E9 W0 ")%(60-T _T7\
MH4PW0 !(.47\?,C_#4PW0 "X 0 .L R<-5B^Q0ZXF0D)#K8_\U0#= &C=
M"4 Z$8& "#Q C'1?P ZR>+1?S_-(60-T BT7\_S2%4#= /]U_&@!
M"D Z!D& "#Q!#_1?RA3#= #E%_'S/@SU,-T '4+:"$*0 #H]@4 %G)
MPU6+[%#KEY#IFP0 )#'1? * :#L*0 #HU04 %EH=PI .C*!0 66B<
M"D Z+\% !9:-\*0 #HM 4 %EJ!VH : @+0 #HX?O__X/$#*/0-T _S70
M-T :#T+0 !H4"9 .@$$P @\0,_S70-T :$L+0 !HH"9 .CL$@ @\0,
M:@)H4"9 .AY0@ @\0(H]0W0 "X_____SD%U#= '43:% F0 #HQ at 0 %EJ
M >@J0P 66H":* F0 #H14( (/$"*/8-T N/____\Y!=@W0 !U$VB@)D
MZ)($ !9:@'H]D( %EH\"9 &H"_S74-T Z+,] "#Q P]_____W4+:%@+
M0 #H8P0 %EH0#= &H!_S74-T Z(P] "#Q P]_____W4+:&T+0 #H/ 0
M %EH2#= &H&_S74-T Z&4] "#Q P]_____W4+:($+0 #H%00 %G_-4 at W
M0 #_-4 W0 #_-= W0 !HE at M .AT! @\00:/ F0 !HN0M .AB! @\0(
M:+T+0 #H500 %EH>"% .AV. 66A )T Z/\" !9#[8%0"= #UY
M=!(/M at 5 )T /5D /A1H! !HY M . at 4! 66@<#$ Z D$ !9:$ G
M0 #HO@( %D/M at 5 )T /7D !T%@^V!4 G0 ]60 '0(:@'HRD$ %EH
M ! &H :$4,0 #H!/K__X/$#*-(-T :B)J &AS#$ Z.[Y__^#Q RC1#=
M .L :$@W0 !J!/\UU#= .A6/ @\0,/?____]TY&A$-T :@/_-=0W0 #H
M.CP (/$##W_____=0J+1?#_3?"%P'^^@WWP '\3:+4,0 #HV@( %EJ >@^
M00 66A -T :@'_-=0W0 #H^SL (/$##W_____=0MHQ Q .BK @ 6<<%
M3#= #H^_K___\U0#= &C8#$ Z <# "#Q AJ &H%_S74-T Z+4[
M "#Q P]_____W4+: @-0 #H90( %EH\"9 &H"_S74-T Z(X[ "#Q P]
M_____W4+:!T-0 #H/@( %EH0#= &H!_S74-T Z&<[ "#Q P]_____W4+
M:#(-0 #H%P( %EH2#= &H&_S74-T Z$ [ "#Q P]_____W4+:$8-0 #H
M\ $ %GH"OG__X7 =0K'!4PW0 Z/O[__]J"6H!:%L-0 #H@?C__X/$
M#(E%_(M%_.G( D/\U0#= &H :)8-0 #H8/C__X/$#(E%^*% -T *T7X
M4&H :+<-0 #H1?C__X/$#(E%]/]U]/]U^.C0^O__@\0(Z9T "0H4PW0 !(
M4&H :-D-0 #H&/C__X/$#(E%_/]U_.CZ^O__6>MX:/L-0 #HP0$ %EJ >BI
M/P 66 at 9#D Z*X! !9Z(SY__]J &H%_S74-T Z%DZ "#Q P]_____W4+
M:#$.0 #H"0$ %EJ .AM/P 66A&#D Z'(! !9ZQPM 0 'SL/0@ !W
MY3T ?-[_)(7P"$ Z?;^___)PU6+[(/L$.E;^___D%6+[(/L"%=64XMU
M"(EU^(,]>"% !_,&AX(4 Z*X- !9/?____]U$CEU^ ^%A@ #/ 6UY?
MR<.0D/\-?"% /\%>"% (L]>"% %=J"O\U?"% %;H0P$ (/$$(7 B]AT
M!(O[*_Z+QP/P*05X(4 3U\(4 @SUX(4 'T%,\#K!I"A>"% ^^%84A
M0 "+%)4\)4 *Q5\(4 .]!]"VAX(4 Z#TX !9A=L/A%?___].Q at 8 BT7X
M6UY?R<-5B^Q75K]8#D H:@00 Y!3 F0 !]#*$P)D BSR%; Y /]U".@)
M/@ 68OPA?9T'U;_=0AJ N@//@ @\0,:@)H9 at Y &H"Z/X] "#Q Q7Z-T]
M !94%=J NCK/0 @\0,:@%H:0Y &H"Z-H] "#Q Q>7\G#D)"058OL4%>-
M10R+T ^^!90A0 "I @ '45#[X%E"% *F ="B #90A0 ":(@A0 !2
M_W4(Z%D5 "#Q R+^ ^^!90A0 "I( '0*N/____]?R<.0D(O'7\G#D)"0
M5U:+?"0,BW0D$(I4)!2+3"0827PFK*HZPG0;27P=K*HZPG0227P4K*HZPG0)
M27P+K*HZPG7<B\=>7\,SP%Y?PU6+[%"-10R+T%+_=0AH>"% .AA @\0,
MR<-5B^Q0C440B]!2_W4,_W4(Z$< "#Q S)PY"058OL@^P45U:+?0B-11"+
M\,9%^ &+QXE%](E%\%?HOCP %F)1>S&1?D\5O]U#(U%[%#H"0 (/$#%Y?
MR<.0D%6+[('L% $ %=64XM]"(MU#,>%_/[__P #'!:090 QP6H
M&4 .F; @ D)"0_PVD&4 5_^U]/[__^@J$P @\0(/?____\/A) !
M #I?0( )"0D(/[*G43QX7P_O__ ^V!D:+V.L+D,>%\/[__P$ #'
MA?C^__\ ZQR0D&N%^/[__PH#PRTP B87X_O__#[8&1HO8#[:#E1Q
M *D$ ==B#O?C^__\ =0K'A?C^______]_B\.)A>S^__\]; '0)@[WL
M_O__:'4&#[8&1HO8A=L/A,D! "#^UMU&HV% /___U!6Z"D( "#Q B%P(OP
M#X2J 0 #[:#E1Q *D! =!/'A>S^__]L #[:#EAU (O8 at _MN=%^#
M/:@90 =5:#^V-T48/[6W1,_P6D&4 _P]]"5?H'PH %GK"8M'! ^V /]'
M!(F%]/[__P^V@)4<0 "I" '70_PVD&4 5_^U]/[__^CI$0 @\0(/?__
M__]T4XO#/5L !T)#UC =!T]:0 ^$N #UN =$D]<P ^%
MI@ (U%$%!7C84 ____4/^U^/[__U/_M?#^___H, 8 .F@ D)"0@[W\
M_O__ ^%Y0 +C_____6UY?R<.0@[WL_O__:'4?BT40!00 ")11"+0/QF
MBQ6D&4 9HD0Z:H "0D(.][/[__VQU'XM%$ 4$ B440BT#\BQ6D&4
MB1#IA )"0D)"+11 %! (E%$(M _(L5I!E (D0ZVB-11!05_^U[/[_
M__^U^/[__U/_M?#^___H&P$ (/$&(F%[/[__X.][/[__P!T$8N%\/[__P&%
M_/[__^LKD)"0 at SVH&4 '03@[W\_O__ '4*N/____];7E_)PXN%_/[__UM>
M7\G#D ^V!D:%P(O8=0Z+A?S^__];7E_)PY"0D ^V at Y4<0 "I" '1F at SVH
M&4 '71_P6D&4 _P]]"U?H:0@ %GK"Y"0BT<$#[8 _T<$B87T_O__#[:
ME1Q *D( =<[_#:090 !7_[7T_O__Z#$0 "#Q @]_____W6#QP6H&4
M 0 .ET____ at _LE=0\/M at 9&B]B#^R4/A?#\____!:090 #_#WT*5^CX!P
M6>L*D(M'! ^V /]'!(F%]/[__SO##X6=_/__Z3#___]5B^R#[&A75E.+?1B-
M1<"+\,=%M #'1; QT6L ,=%J #'1:0 QT6@
M (M%#"UD ?$<]% '= A<!\//\DA:P90 "0_T6HQT6X"@ .L3QT6X
M" .L*D)"0QT6X$ /\%I!E /\/?1-7Z%$' !9ZQ.0D#/ 6UY?R<.0
MBT<$#[8 _T<$B]@]*P '09/2T !T#STP =#SIT@( )"0D/]%I/]-
M$ ^.PP( /\%I!E /\/?0I7Z ' !9ZPJ0BT<$#[8 _T<$B]B#^S /A9L"
M "#?0QI#X61 @ @WT0 0^.AP( /\%I!E /\/?0I7Z,0& !9ZPJ0BT<$
M#[8 _T<$B$6_#[[ /7@ !T#P^^1;\]6 ^%VP ^^1PT]/ '0%
M at S\ ="/_!:090 #_#WT)5^A[!@ 6>L)BT<$#[8 _T<$B$6^ZRR0D&H!5@^^
M1PU0Z/<V "#Q P] 0 '4)B@:(1;[K!I"0QD6^__\%I!E ^^1;X/MH"5
M'$ J8 !T/,=%N! "#?1 "?Q__#:090 !7#[Y%OE#H"@X (/$"/]-
M$.FV 0 D)"0#[Y%OHO8 at VT0 NFD 0 D/\-I!E %</OD6^4.C;#0 @\0(
SHAR_EOF
true || echo 'restore of scsipart.uu failed'
fi
echo 'End of part 3'
echo 'File scsipart.uu is continued in part 4'
echo 4 > _shar_seq_.tmp
exit 0
--
+-----------------------------------------------------------------
Station Zebra ....!{claris,zorch}!szebra!tin
Sunnyvale, CA (408) 739-1520 24hrs Telebit+ 300-19200bps
Pub *NIX, Usenet and mail (no fee)
More information about the Alt.sources
mailing list