[alt.sources] ST-0x 386/ix driver v1.0 pt3/6

tin@szebra.szebra.uucp (Tin Le) (09/17/90)

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@____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@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@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@W
M0 #_-4 W0 #_-= W0 !HE@M  .AT!   @\00:/ F0 !HN0M  .AB!   @\0(
M:+T+0 #H500  %EH>"%  .AV.   66A )T  Z/\"  !9#[8%0"=  #UY    
M=!(/M@5 )T  /5D    /A1H!  !HY M  .@4!   66@<#$  Z D$  !9:$ G
M0 #HO@(  %D/M@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@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@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@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@_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@SVH&4   '03@[W\_O__ '4*N/____];7E_)PXN%_/[__UM>
M7\G#D ^V!D:%P(O8=0Z+A?S^__];7E_)PY"0D ^V@Y4<0 "I"    '1F@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____@_LE=0\/M@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@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@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)