BETA St-01 (p2 of 3)

Michael Grenier mike at cimcor.mn.org
Wed Jan 2 12:06:38 AEST 1991


#!/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



More information about the Comp.unix.sysv386 mailing list