/*
 * Thinking Machines Modifications:
 *
 $Log: cip.c,v $
 * Revision 1.16  1992/07/28  18:13:51  sean
 * Resolve problem with huge blocksize transfers found at WGEO.  Transfers
 * that were larger than minphys() would allow (16mb) would become corrupt.
 *
 * Revision 1.15  1992/07/09  14:37:42  sean
 * o Fix reset-on-reboot problem with stc driver.  Driver now recognises the
 *   Storagetek drive on the first open.
 *
 * o Make both drivers print out their cvs revisions when rebooting.
 *
 * Revision 1.14  1992/07/07  18:39:56  sean
 * stc.c: implement basic ICRC support
 * cip.c: fix crashes at WGEO associated with touching vme pages incorrectly,
 *        turn on SCSI bus parity
 * stcreg.h: fix saber warnings when loaded into libcmt
 *
 * Revision 1.13  1992/06/25  15:08:24  sean
 * Fix problem for WGEO that resulted by having a piece of code #ifdef-ed
 * out by accident.
 *
 * Revision 1.12  1992/05/05  22:10:46  sean
 * Driver changes to ciprico and storagetek driver to prevent two bugs:
 *
 * o Generating a bad trap doing vme<->vme transfers when doing fast &
 *   frequent IO operations on multiple stackers
 *
 * o Panicing due to a dup biodone when a timeout occurs during a rewind
 *
 * Revision 1.11  1991/11/04  17:20:38  sharakan
 * Unambiguated if statement.
 *
 * Revision 1.10  1991/07/22  18:41:34  sharakan
 * Changes to avoid DVMA for VME<->VME transfers, and to allow bigger xfers.
 *
 * Revision 1.9  90/12/28  17:29:26  sharakan
 * Fixed old bug WRT resid not being initialized.
 * 
 * Revision 1.8  90/11/19  22:40:55  ric
 * no check for unaligned data
 * 
 * Revision 1.7  90/10/19  11:54:36  ric
 * cleaned up use of bzero aftrer allocating qio
 * structs
 * 
 * Revision 1.6  90/10/11  12:02:08  ric
 * #ifdef'ed DEBUG the printf's that were
 * in the intr routine
 * 
 * Revision 1.5  90/10/09  10:33:45  nesheim
 * update for SunOS 4.1
 * 
 * Revision 1.4  90/10/04  16:05:54  nesheim
 * changed #include to work with /sys/tmc (no /sys/cip)
 * 
 * Revision 1.3  90/10/01  11:49:19  ric
 * trashed internal memory allocation routines,
 * removed extraneous memory free call
 * 
 * Revision 5.0.1.1  90/09/11  09:56:47  ric
 * fixed mb structs
 * 
 * Revision 5.0  90/08/17  16:10:13  ric
 * *** empty log message ***
 * 
 * Revision 4.8  90/08/16  11:09:45  ric
 * replaced "cip_init" error withright name
 * 
 * Revision 4.7  90/08/16  11:04:34  ric
 * passes lint
 * 
 * Revision 4.6  90/08/16  10:45:07  ric
 * trimmed down version
 * 
 * 
 */

/*
 *  Rimfire 3500 Driver routines:
 *  
 *  cipc_probe -           probe for controller
 *  cipc_slave -           probe for a slave device
 *  cipc_init  -           initialize the controller
 *  cipc_hardreset -       reset the controller, reset command queues
 *  cipc_attach -          attach a slave device to the controller
 *  cipc_go -              called by mbgo() to send command
 *  cipc_done -            cleanup after transfer to/from slave
 *  cipc_intr -            interrupt handling
 *  cipc_ustart -          called by a high level SCSI routine sleep until 
 *                        transfer is possible
 *  cipc_start -           executes next command in command queue
 *  cipc_done -            process next buffer for slave
 *  cipc_cmd -             used by high level intr routine to operate on
 *                        SCSI bus
 *  cipc_getstat -         Return the completion status of a SCSI command issue
 *                        in polling mode - only used by sd().
 *  cipc_cmd_wait -        Used only by sd to wait for the completion of a command
 *                        issued in polling mode -- not implemented
 *  cipc_off -             make a unit offline  -- not implemented
 *  cipc_scsi_reset -      reset the scsi bus  -- not implemented
 *  cipc_dmacount -        used to obtain the residual count of the last DVMA
 *  cipc_deque -           remove timed out IP request and inform it's intr handler
 *  cipc_ext_cmd -         Send a type 0 command to the controller
 *  
 */

#ifndef lint
static  char rcsid[] = "$Id cip.c,v 1.14 $";
#endif

/* The include file rfsd.h is created during the configuration process on Sun
 * machines.  Its name itself is dependent on the configuration process.
 * Basically, what is needed in the include file are two defines:
 *      NCIPC    - Defines how many RF 3500's are plugged into the system
 */
#include "cipc.h"


/* Don't bother compiling if there aren't any drives. */
#if NCIPC > 0

/* Include whatever system include files are needed here. */
#include <sys/param.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/buf.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/map.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/dk.h>
#include <sys/mtio.h> 
#include <sys/uio.h>
#include <vm/seg.h>

#include "../sun/dkio.h"
#include "../sun/dklabel.h"
#include "../machine/psl.h"
#include "../machine/pte.h"
#include "../sundev/mbvar.h"
#include "../sundev/scsi.h"

#include "../tmc/cip_def.h"               
#include "../tmc/cip_err.h"

/*
 * SUN interface data structures
 */

#define B_VME_ADDR 0x02000000	/* !!!!! MUST MATCH DEFINE IN STC.C !!!! */

/*
 * subroutines called via SCSI interface
 */
int cipc_ustart(), cipc_start(), cipc_done(), cipc_cmd(),
  cipc_getstat(), cipc_cmd_wait(), cipc_off(), cipc_scsi_reset(),
  cipc_dmacount(), cipc_go(), cipc_deque();

/*
 * subroutines called via mainbus (mb) routines
 */
int cipc_probe(), cipc_slave(), cipc_intr(), cipc_attach(),  cipc_slave();

/*
 * Local subroutines
 */
static int cipc_ext_cmd();
static int cipc_init_slave();
static int cipc_init();

#define DEBUG 1

#define MAXSLAVES (7 * 4)  /* 7 SCSI id's * 4 logical units */
#define ALIGNSIZE 8	/* For CMIO transfers */

struct mb_device *cipc_info[NCIPC*MAXSLAVES];  	/* per slave */
struct mb_ctlr *cipc_cinfo[NCIPC];		/* per controller */

struct mb_driver cipcdriver = { /* the controller's driver structure */
  cipc_probe,             /* the probe routine */
  cipc_slave,             /* the slave routine */
  cipc_attach,            /* the attach routine */
  cipc_go,                /* the go routine */
  cipc_done,              /* the done routine */
  0,		          /* no polling routine */
  sizeof(RF35REG),        /* memory  requirements */
  "cip",                  /* the device name */
  cipc_info,	          /* mbdinit backpointer */
  "cipc",                 /* the controller name */
  cipc_cinfo,         	  /* mbcinit backpointer */
  MDR_BIODMA,             /* we do DMA transfers */
  0                       /* interrupt routine linked list */
  };

struct scsi_ctlr_subr cipc_subr = { /* SUN scsi controller interface routines */
  cipc_ustart,
  cipc_start,
  cipc_done,
  cipc_cmd,
  cipc_getstat,
  cipc_cmd_wait,
  cipc_off,
  cipc_scsi_reset,
  cipc_dmacount,
  cipc_go,
  cipc_deque,
};

extern struct scsi_unit_subr scsi_unit_subr[];
extern struct scsi_ctlr cipcctlrs[];

extern struct timeval time;

/*
 * Software copy of the controllers status register + other useful fields
 */
struct cipc_softc {
  int cipc_flags;             /* Status of the controller */
  RF35REG *cipc_addr;         /* Cached address of device register */
  int cipc_last_polled_cmd_status;
  int cipc_last_polled_cmd_resid;
  int cipc_bsybit;            /* BSY bit of the Rimfire status port as the
			       * controller pictures it.  Set to ZERO on 
			       * controller reset and toggle for each type
			       * zero channel attention
			       */
  int cipc_type;              /*
			       * This is the controller type of each 
			       * controller.
			       * The value is read from the status register 
			       * of the board after a reset.
			       */
  RETID       cipc_idstat;    /* 'Identify' status */
  int cipc_unit;              /* Cached unit */
  int cipc_nactive;			/* Number of active qio structs */
  struct cipc_qio *cipc_active;	/* active qio structs the driver currently has */
  int	   cipc_nfree;			/* Number of free qio structs */
  struct cipc_qio *cipc_free_qio;	/* Free list of qio structs */
} cipc_softc[NCIPC];

/* cipc_flags bits */
#define CIPC_INITTED     0x1     /* cipc_init() has been called */
#define CIPC_PRESENT     0x2     /* Probe routine found it */
#define CIPC_QIO_SLEEP   0x4
#define CIPC_TIMEING     0x8     /* Timer running */

/* DEBUGGING FLAG values */
#define	CIPC_PROBE	0x1	/* Debug probe routine */
#define CIPC_CMD	0x2	/* Print debug info for each command sent */
#define CIPC_SCSI	0x4	/* Print debug info for the scsi_ctlr routines */
#define CIPC_SLAVE	0x8	/* Print debug info for the slave routines */
#define CIPC_CTLR	0x10	/* Debug ctlr resets, etc */
#define CIPC_TRACE	0x20	/* Print each entry & return to driver routines */
#define CIPC_ATTACH	0x40	/* Debug attach procedure */
#define CIPC_INT	0x80	/* Trace interrupts */
#define CIPC_INIT	0x100	/* Trace the init routine */

static int	cipc_debug	= 0;

/* General Options Flag.
 * NOTE: Suns DO NOT support the BMT option.
 */
static int      cipc_options     = (DIS|PAR);

/*
 * QIO handling macros
 */
#define GET_QIO(cipc, qio) \
  (qio) = (cipc)->cipc_free_qio; \
  if (qio == NULL) \
  panic("cipc: out of qio's"); \
  (cipc)->cipc_free_qio = (qio)->cipq_next;\
  (cipc)->cipc_nfree--;

#define FREE_QIO(cipc, qio)\
  (qio)->cipq_next = (cipc)->cipc_free_qio;\
  (cipc)->cipc_free_qio = (qio); \
  if ((cipc)->cipc_flags & CIPC_QIO_SLEEP) \
  wakeup(&(cipc)->cipc_free_qio); \
  (cipc)->cipc_flags &= ~CIPC_QIO_SLEEP; \
  (cipc)->cipc_nfree++;

/*
 *
 *      Subroutine:             cipc_probe
 *
 *      Calling Sequence:       cipc_probe(address, controller)
 *                                                                            
 *      Called by:              UNIX initialization                           
 *                                                                            
 *      Calling Parameters:     address of device registers                   
 *                              controller's unit number                      
 *                                                                            
 *      Local Variables:                                                      
 *                              cipc_regs - pointer to device address         
 *                              cip - pointer to cipc_softc entry             
 *                              sc - pointer to scsi_ctlr entry               
 *                              timer - timeout variable                      
 *                                                                            
 *      Calls Subroutines:      pokec                                         
 *                                                                            
 *      Public/Global Variables:                                              
 *                                                                            
 *      Description:                                                          
 *              This routine is called at startup time to probe for a         
 *              controller, to reset it, and to set configuration flags       
 *              in its scsi_ctlr entry                                        
 *                                                                            
 */
int
cipc_probe(addr, ctlr)
     caddr_t addr;
     int ctlr;
{
  register RF35REG *cipc_regs = (RF35REG *) addr;
  struct cipc_softc *cipc = &cipc_softc[ctlr];
  struct scsi_ctlr *sc = &cipcctlrs[ctlr];
  dword timer = 0;
  dword reset_time = RESET_DELAY * 1000000;

#ifdef DEBUG
  if ((cipc_debug & CIPC_TRACE) || (cipc_debug & CIPC_PROBE))
    printf("cipc_probe: controller %d at %x\n", ctlr, cipc_regs);
#endif

  if (ctlr >= NCIPC) {
    printf("cipc_probe: bad controller number %d\n", ctlr);
    return(0);
  }
  
  /* Reset controller and check if it exists */
  if (pokec((char *)&cipc_regs->reset, 1) != 0) {        /* its not there */
    printf("cipc_probe: no controller present at %x\n", addr);
    return(0);
  }
  cipc->cipc_bsybit = 0; 		/* This bit toggles */
  
  /* Wait for reset to complete */
  while ((cipc_regs->status & SWAB(STATRST)) != SWAB(RESETDONE))
#ifdef MAXWAIT
    if (++timer > MAXWAIT) {
      printf("cipc_probe: Cannot reset controller: status %x\n",
             SWAB(cipc_regs->status));
      return(0);
    }
#else MAXWAIT
  ;
#endif MAXWAIT

  if (reset_time) {
#ifndef STANDALONE      
    printf("cipc%d: waiting %d second%s for SCSI bus reset completion.\n",
           ctlr, RESET_DELAY, reset_time ==  1000000 ? "" : "s");
#endif
    DELAY(reset_time);
  }

  cipc->cipc_type = cipc_regs->status & STATCTYPE;
  if (cipc->cipc_type != STATRF3500){
    printf("Unknown controller type\n");
    return(0);
  }
  cipc->cipc_addr = cipc_regs;
  cipc->cipc_unit = ctlr;
  cipc->cipc_flags |= CIPC_PRESENT;
  
  /* with 4 byte transfer count, assume BIG_DMA */
  sc->c_flags = SCSI_PRESENT|SCSI_BIG_DMA_OK; 
#ifdef SCSI_VME
  /* SunOS 4.1 doesn't define this */
  sc->c_flags |= SCSI_VME;
#endif
  sc->c_reg = (int) addr;
  sc->c_ss = &cipc_subr;
  sc->c_name = "cipc";
  return(sizeof(RF35REG));
}

/*
 *                                                                            
 *      Subroutine:             cipc_slave                                    
 *                                                                            
 *      Calling Sequence:       cipc_slave(dev, addr)                         
 *                                                                            
 *      Called by:              UNIX configuration startup for each slave in  
 *                              configuration file                            
 *                                                                            
 *      Calling Parameters:     dev - pointer to device structure             
 *                              addr - controllers I/O address - unused       
 *                                                                            
 *      Local Variables:  	un - pointer to slave's scsi_unit structure
 *				cipc - pointer to ctlr cipc_soft structure
 *                                                                            
 *      Calls Subroutines:      cipc_init()
 * 				slave routine, ss_unit_ptr(), to get pointer to un
 *                                                                            
 *      Description:                                                          
 *		This routine is called to "probe" each of a controller's
 *		slave units. The first time through, the controller itself
 *		is initialized.        
 *                                                                            
 *              The ss_unit_ptr() routine returns a pointer to the unit's     
 *              scsi_unit structure if it has been configured. If so,
 *              simply initialize the md_driver structure appropriately.                        
 *                                                                            
 */
int
cipc_slave(device, addr)
     register struct mb_device *device;
     caddr_t addr;
{
  struct scsi_unit *un;
  struct cipc_softc *cipc = &cipc_softc[device->md_ctlr];
  
#ifdef DEBUG
  if ((cipc_debug & CIPC_TRACE) || (cipc_debug & CIPC_SLAVE))
    printf("entering cipc_slave: device 0x%x at 0x%x\n", device, addr);
#endif

  /* Return pointer to slave's unit structure */
  un = (struct scsi_unit *)
    (*scsi_unit_subr[TYPE(device->md_flags)].ss_unit_ptr)(device);
  /* Set unit's unit structure's controller to cipc_ctrl[] */
  un->un_c = &cipcctlrs[device->md_ctlr]; 
  
  if (!(cipc->cipc_flags & CIPC_INITTED)) { /* Probing first slave on ctlr */
    cipc->cipc_flags |= CIPC_INITTED;
    cipc_init(cipc, device, CIPC_CTLR);	    /* Initialize controller options */
    printf("cipc%d: Ciprico Rimfire firmware rev %d, engineering rev %d, %d/%d/%d\n", 
           cipc->cipc_unit, cipc->cipc_idstat.fwrev, cipc->cipc_idstat.engrev,
           cipc->cipc_idstat.month, cipc->cipc_idstat.day,
           cipc->cipc_idstat.year);
    printf("cipc%d: CMIOP Ciprico driver rev $Revision: 1.16 $\n",cipc->cipc_unit);
  }
  
  /* Pick up SCSI high level driver name */
  device->md_driver->mdr_dname = scsi_unit_subr[TYPE(device->md_flags)].ss_devname;
  device->md_driver->mdr_cname = "cipc";
  /* controller is a block device & does Mainbus DMA */
  device->md_driver->mdr_flags = MDR_BIODMA;
  return (1);
}

/*
 *                                                                            
 *      Subroutine:             cipc_attach                                    
 *                                                                            
 *      Calling Sequence:       cipc_attach(device)                            
 *                                                                            
 *      Called by:              UNIX configuration startup                    
 *                                                                            
 *      Calling Parameters:     device - the device to do the housework for   
 *                                                                            
 *      Local Variables:        cipc - the controller software copy of         
 *                              the device registers                          
 *                                                                            
 *      Calls Subroutines:      scsi subroutine ss_attach() of device
 *				cipc_init()
 *                                                                            
 *      Description:                                                          
 *              This function does preliminary setup work for a               
 *              specified slave number.                                       
 *                                                                            
 */
int
cipc_attach(device)
     register struct mb_device *device;
{
  struct cipc_softc *cipc = &cipc_softc[device->md_ctlr];
  
#ifdef DEBUG
  if ((cipc_debug & CIPC_TRACE) || (cipc_debug & CIPC_ATTACH))
    printf("cipc_attach: device: 0x%x, ctlr: %d, flags: %d, md_mc: 0x%x)\n", 
	   device, device->md_ctlr, device->md_flags, device->md_mc);
#endif DEBUG
  /* 
   * Fill in the scsi_unit structure for the
   * slave before calling its attach routine
   */
  (void)(*scsi_unit_subr[TYPE(device->md_flags)].ss_attach)(device);

  cipc_init(cipc, device, CIPC_SLAVE);	/* Set up type specific ctlr options */
  return(1);
}

/*
 *                                                                            
 *      Subroutine:             cipc_hardreset                                
 *                                                                            
 *      Calling Sequence:       cipc_hardreset(*cipc_softc)                   
 *                                                                            
 *      Called by:              cipc_init                                     
 *                                                                            
 *      Calling Parameters:     software copy of device registers             
 *                                                                            
 *      Local Variables:        cipc_regs - pointer to device registers       
 *                              xpb - extended parameter block
 *				qio - pointer to qio structure
 *                              gopt - general options parameter block        
 *                              timer - timeout variable                      
 *                                                                            
 *      Calls Subroutines:      cipc_ext_cmd                                  
 *                                                                            
 *      Public/Global Variables: None                                         
 *                                                                            
 *      Description:                                                          
 *              This function does performs a hard reset of the device and    
 *              clears & sets boards general options.
 *                                                                            
 */

static
cipc_hardreset(cipc)
register struct cipc_softc *cipc;
{
  register RF35REG *cipc_regs = cipc->cipc_addr;
  register EXTPB *xpb;
  register struct cipc_qio *qio;
  GOPTPB *gopt;
  dword timer;
  
#ifdef DEBUG
  if ((cipc_debug & CIPC_TRACE) || (cipc_debug & CIPC_CTLR))
    printf("cipc_hardreset: resetting ctlr%d\n", cipc->cipc_unit);
#endif DEBUG

  /* Reset controller and check if it exists */
  if (pokec((char *)&cipc_regs->reset, 1) != 0) {        /* its not there */
    cipc->cipc_flags |= 0;
    printf("cipc_hardreset: no controller present at %x\n", cipc);
    return(0);
  }
  cipc->cipc_bsybit = 0;
  
  /* Wait for reset to complete */
  timer = 0;
  while ((cipc_regs->status & SWAB(STATRST)) != SWAB(RESETDONE))

#ifdef MAXWAIT
    if (++timer > MAXWAIT) {
      printf("cipc_hardreset: Cannot reset controller: status %x\n",
             SWAB(cipc_regs->status));
      cipc->cipc_flags |= 0;
      return(0);
    }
#else MAXWAIT
  ;
#endif MAXWAIT
  
  /* 
   * After reset, we issue set the General Board Options
   */
  GET_QIO(cipc,qio);
  bzero(qio, sizeof(*qio));
  xpb = xpb = &qio->qio_xpb;

  gopt = (GOPTPB *)&xpb->pb;
  bzero((char *)gopt, sizeof(EXTPB));
  gopt->id = 0;
  gopt->optflags = cipc_options;
  gopt->throttle = THROTTLE;
  gopt->ownid = CIP_SCSI_ID;
  gopt->targetid = RF3500_ID;
  gopt->command = C_OPTION;
  
  if (cipc_ext_cmd(cipc, xpb, CIPC_WAIT) != SE_NO_ERROR) {
    printf("cipc_hardreset: Cannot set General Board Options!\n");
    pb_print( &xpb->pb );
    sb_print( &xpb->sb );
    FREE_QIO(cipc,qio);
    return(0);
  }
  FREE_QIO(cipc,qio);
  return(1);
}

/*
 *                                                                            
 *      Subroutine:             cipc_init                                     
 *                                                                            
 *      Calling Sequence:       cipc_init(cipc, device, flag)                           
 *                                                                            
 *      Called by:              cipc_slave()                                  
 *                                                                            
 *      Calling Parameters:     cipc_softc pointer for controller             
 *                              pointer to mb struct of slave
 *				flag indicates who is getting init'ed
 *      Local Variables:        mc - pointer to mb_ctrl entry                 
 *                              un - pointer to device's scsi unit struct
 *				qio - qio struct used for issuing commands
 *				xpb - pointer to param blk in active qio
 *                              timer - timeout variable                      
 *                                                                            
 *      Calls Subroutines:      cipc_ext_cmd(), cipc_init_slave()
 *                                                                            
 *      Description:                                                          
 *              This initializes the Rimfire controller if flag is set to CIPC_INIT or
 *		CIPC_CTLR.  cipc_init_slave() is called for the specified
 *		SCSI slave unit if the flag is CIPC_SLAVE.
 *                                                                            
 */

static
cipc_init(cipc, device, flag)
     register struct cipc_softc *cipc;
     register struct mb_device *device;
     register int flag;
{
  struct mb_ctlr *mc = cipc_cinfo[cipc->cipc_unit];
  register struct scsi_unit *un;
  register struct cipc_qio *qio;
  struct cipc_qio *active_iopbs;
  register EXTPB *xpb;
  int s;
  int i;
  
#ifdef DEBUG
  if ((cipc_debug & CIPC_TRACE) || (cipc_debug & CIPC_INIT))
    printf("cipc_init: cipc: 0x%x, device: 0x%x, flag: %d\n", cipc, device, flag);
#endif DEBUG

  if (mc)
    s = splr(pritospl(mc->mc_intpri));
  else
    s = splhigh();
  
  if ((flag == CIPC_CTLR) || (flag == CIPC_INIT)){ /* Init controller */ 
    if (cipc->cipc_free_qio == (struct cipc_qio *) NULL) {
      int qio_size = sizeof (struct cipc_qio) + ALIGNSIZE;
      register u_long qio_buf;

      /* alloc memory for qio structs */
      for (i=0; i < NQIO; i++ ) {
	qio_buf = (u_long)rmalloc(iopbmap, qio_size);
	if (qio_buf == NULL)
		panic("cipc_init: rmalloc failure");
	qio = (struct cipc_qio *)((qio_buf & ~(ALIGNSIZE - 1)) + ALIGNSIZE);
	bzero((caddr_t)qio, sizeof(struct cipc_qio));
	FREE_QIO(cipc,qio);
      }
    }
      
    cipc_hardreset(cipc);	/* Reset controller */
    
    GET_QIO(cipc,qio);
    bzero(qio, sizeof(*qio));
    xpb = &qio->qio_xpb;     /* Issue 'identify' command to the adapter */
    xpb->pb.targetid = RF3500_ID;
    xpb->pb.scdb.cmd = C_IDENTIFY;
      
    if (cipc_ext_cmd(cipc, xpb, CIPC_WAIT) == SE_NO_ERROR)
      bcopy(&xpb->sb, &cipc->cipc_idstat, sizeof(RETID));
    else
      panic("cipc_init: Identify command failed\n");
    FREE_QIO(cipc, qio);    
  } else if (flag == CIPC_SLAVE) /* Init only this slave device */
    cipc_init_slave(cipc, device);

  if (flag == CIPC_INIT) { /* Need to reinit each of the slaves as well */
    register struct mb_device *md;

    for (md = mbdinit; md->md_driver; md++)
      if ((md->md_mc) &&  /* Defined and belongs to this ctlr */
	  (md->md_mc->mc_addr == (caddr_t)cipc->cipc_addr))
	cipc_init_slave(cipc, md);
  }

  /* 
   * Complete any entries in the cmdq in progress, 
   * and free all active QIO structs 
   */
  un = (struct scsi_unit *)  /* Return pointer to slave's unit structure */
    (*scsi_unit_subr[TYPE(device->md_flags)].ss_unit_ptr)(device);
  if (active_iopbs = cipc->cipc_active)
    printf("cipc_init: cipc%d timing out active commands:\n", cipc->cipc_unit);
  cipc->cipc_active = NULL;
  cipc->cipc_nactive = 0;
  while (qio = active_iopbs) {
    /*
     * Tell the higher levels that this IOPB errored out
     */
    
    pb_print(&qio->qio_xpb, sizeof(EXTPB));
    if (qio->cipq_un) {
      qio->cipq_un->un_c->c_un = qio->cipq_un;
      bzero(&qio->cipq_un->un_scb, sizeof (struct scsi_scb));
      (void)(*scsi_unit_subr[TYPE(qio->cipq_un->un_md->md_flags)].ss_intr)
	(qio->cipq_un->un_c, 0, 
	 (un == qio->cipq_un) ? SE_TIMEOUT: SE_FATAL);
    }
    
    active_iopbs = qio->cipq_next;
    bzero(qio, sizeof (*qio)); /* Free its iopb map resources ???? */
    FREE_QIO(cipc, qio);
  }
  splx(s);
  return(1);
}

/*
 *                                                                            
 *      Subroutine:             cipc_init_slave                                     
 *                                                                            
 *      Calling Sequence:       cipc_init_slave(cipc, device)
 *                                                                            
 *      Called by:              cipc_slave()                                  
 *                                                                            
 *      Calling Parameters:     cipc_softc pointer for controller             
 *                              pointer to mb struct of slave
 *
 *      Calls Subroutines:      cipc_ext_cmd                                  
 *                                                                            
 *      Public/Global Variables: cipc_cinfo                               
 *                                                                            
 *      Description:                                                          
 *              This initializes the specified slave unit.
 * 		The "unit options" command sets unit specific
 * 		flags in the controller.
 */
static
cipc_init_slave(cipc, device)
  register struct cipc_softc *cipc;
  register struct mb_device *device;
{
  register struct cipc_qio *qio;
  register UOPPB *upb;

  GET_QIO(cipc,qio);	/* use for issuing following commands */
  bzero(qio, sizeof(*qio));
  
  upb = (UOPPB *) &qio->qio_xpb.pb;
  
  upb->distimeout = 100000; 	/* measured in increments of 0.1 sec */
  upb->unitid = TARGET(device->md_slave);    /* Target id */
  upb->targetid = RF3500_ID;
  upb->seltimeout = 256;        /* Default is 0 */
  /* set the request extended sense length */
  upb->reqlength = 32;
  if ((TYPE(device->md_flags) == SCSI_TAPE) || /* SUN SCSI tape unit */
      (TYPE(device->md_flags) == SCSI_STC)) { /* TMC Storage Tek type == SCSI_OPTICAL */
    upb->retrycntrl = 0;
    upb->retrylimit = 0;
  } else {   /* SCSI_DISK, SCSI_FLOPPY */
    upb->retrycntrl = RCRBE | RCRCE | RCRPE | RCISB | SCINT;
    upb->retrylimit = 3;
  }
  upb->uflags = 0;
  upb->uflags |= UF_ISE;  /* Ignore soft errors - let high level driver retry */
  upb->uflags |= UF_SYN;  /* try to negotiate synchronous offset with device */
  upb->uflags |= UF_IDI;  /* several active commands at same time for target */
  
  upb->command = C_UNITOPT;
  if (cipc_ext_cmd(cipc, &qio->qio_xpb, CIPC_WAIT) != SE_NO_ERROR)
    printf("cipc_init_slave: cannot access slave device\n");
  FREE_QIO(cipc,qio);
}

/*
 *                                                                            
 *      Subroutine:     cipc_intr 
 *
 *      Calling Sequence: cipc_intr(unit)
 *
 *      Called By:      Unix interrupt service mechanism
 *
 *	Calling Parameter: unit number of ctlr
 *
 *      Local Variables:
 *		cipc:	pointer to software copy of ctlr status regs
 *		qio:	pointer to active qio struct
 *		un:	pointer to active scsi device
 *              sb:     pointer to the copmleted status block
 *              error:  error in command execution                            
 *                                                                            
 *      Calls Subroutines: ss_intr() of slave unit that caused intr
 *                                                                            
 *      Public/Global Variables:
 *              cipc->cipc_cmdq:  command list
 *
 *      Description:
 *		Find the first completed status block from the active
 *		qio's & dequeue the associated qio from the device.
 *		Map any errors into something understood at the high level
 *		then invoke the high level intr routine for the proper
 *		slave.
 */
int
cipc_intr(unit)
     int unit;
{
  register struct cipc_softc *cipc = &cipc_softc[unit];
  register struct cipc_qio *qio;
  register struct scsi_unit *un;
  register STATBLK *sb;
  register byte error = 0;
  register long resid = 0;
  
#ifdef DEBUG
  if ((cipc_debug & CIPC_TRACE) || (cipc_debug & CIPC_INT))
    printf("cipc_intr: unit = %d\n",unit);
#endif DEBUG
  
  if (cipc->cipc_active == NULL) {
    printf("cipc%d: interrupt with queue empty!\n", unit);
    return;
  }
  /*
   * Now find the first completed extended parameter block
   */
  for (qio = cipc->cipc_active; qio; qio = qio->cipq_next) 
    if (qio->qio_xpb.sb.flags & ST_CC)
      break;
  if (qio == NULL) { /* No finished status block found */
#ifdef DEBUG
    printf("cipc_intr: no completed status block in cipc%d queue! nactive=%d\n",
	   unit, cipc->cipc_nactive);
    for (qio = cipc->cipc_active; qio; qio = qio->cipq_next) 
      printf("Active IOPB 0x%x, status 0x%x\n", &qio->qio_xpb, qio->qio_xpb.sb.flags);
#endif DEBUG
    return;
  }
  un = qio->cipq_un;		/* SCSI unit structure that interrupted */
  sb =  &qio->qio_xpb.sb; 	/* finished status block */
  /*
   * Dequeue this one from the device
   */
  cipc->cipc_nactive--;
  if (qio->cipq_prev)
    qio->cipq_prev->cipq_next = qio->cipq_next;
  if (qio->cipq_next)
    qio->cipq_next->cipq_prev = qio->cipq_prev;
  if (qio == cipc->cipc_active) {
    cipc->cipc_active = qio->cipq_next;
    if (cipc->cipc_active)
      cipc->cipc_active->cipq_prev = NULL;
  }
  
  /* Errors!!! */
  error = sb->error;
  
  if (sb->flags & ST_SOFT) {
    printf("cipc_intr: controller soft error\n");
    sb_print(sb);
    error = EE_NOERR;
  }
  
  switch (error) {
  case EE_NOERR:
    error = SE_NO_ERROR;
    break;
    
  case EE_SCSIERR:  
    if (((sb->scsistat & STATMASK) == CHECK_COND) || /*  punt up */
	((sb->scsistat & STATMASK) == DEVICE_BUSY)) { /* and this? */
      error = SE_RETRYABLE;
    } else {      /* Assume fatal error */
      printf("cipc%d: unknown SCSI status %x\n",
	     unit, sb->scsistat);
      error = SE_FATAL;
    }
    break;
    
  case EE_SCSISELTO:              /* SCSI Select Timeout */
    error =  SE_TIMEOUT;
    break;
    
  default:                        /* Rimfire internal error */
    if (error < EE_FRMERR)
      printf("cipc%d: error %x detected, %s\n",
	     unit, sb->error, cipc_cntrlerr[sb->error]);
    else {
      printf("cipc%d: adapter err %x,", unit, error);
      printf("internal firmware error %x\n", sb->error);
    }     
    un->un_scb.ha_er = 1;
    error = SE_FATAL;
    break;
  }
  
  if (error)
    resid = qio->qio_xpb.pb.count;

  if (error == SE_TIMEOUT) /* Controller is probably confused. reset it */
    cipc_init(cipc, un->un_md, CIPC_INIT);

  un->un_c->c_un = un;
  /* copy back the cdb and scb */
  bcopy(&qio->qio_xpb.pb.scdb, &un->un_cdb, sizeof(qio->qio_xpb.pb.scdb));  
  bzero(&un->un_scb, sizeof(qio->qio_xpb.pb.scdb));
  *((char *)&un->un_scb) = (sb->scsistat & STATMASK);
  /*
   * Call the higher level interrupt routine
   */
  (void)(*un->un_ss->ss_intr)(un->un_c, resid, error);
    
  FREE_QIO(cipc, qio);		/* release qio */
}

/*
 *                                                                            
 *      Subroutine:     cipc_start
 *
 *      Calling Sequence: cipc_start(un)
 *
 *      Called By:      SCSI high level routine to process pending buffer
 *
 *      Calling Parameters: pointer to scsi slave scsi_unit struct
 *
 *      Local Variables:
 *              bp:  pointer to the buffer header
 *              md:  pointer to main bus device structure
 *
 *      Calls Subroutines:    scsi slave ss_start() routine
 *			      mbugo(), cipc_go(), cipc_done()
 *
 *      Description:
 *        Process next buffer in slave "un"'s linked list
 *
 */
int
cipc_start(un)
     struct scsi_unit *un;
{
  register struct buf *bp;
  register struct mb_device *md;

#ifdef DEBUG
  if ((cipc_debug & CIPC_TRACE) || (cipc_debug & CIPC_SCSI))
    printf("cipc_start:\n");
#endif DEBUG
  
  md = un->un_md;
  if (md->md_utab.b_forw) { /* b_forw is already active */
    return;
  }
  bp = md->md_utab.b_actf;    /* Take first buffer in unit's queue */
  md->md_utab.b_forw = bp;
  
  if (bp == NULL) {            /* No buffers in queue */
    return;
  }
  
  /* Call the high-level start routine */
  if ((*un->un_ss->ss_start)(bp, un)) {
    un->un_c->c_un = un;	/* Set un to be the scsi unit using the bus */
    if (bp == &un->un_sbuf && /* using special command buffer */
	((un->un_flags & SC_UNF_DVMA) == 0) && /* No special DVMA */
	((un->un_flags & SC_UNF_SPECIAL_DVMA) == 0)) {
      (void) cipc_go(un->un_md);
      return;
    }
    if (bp != &un->un_sbuf)
      {

#ifdef DEBUG
	  if (cipc_debug & CIPC_CMD)
	    printf("cipc_start: VME<->VME\n");
#endif

        if( bp->b_flags & B_VME_ADDR ) {
	  (void) cipc_go(un->un_md);
	  return;
	} else {
	  /* Call  mbgo to get DVMA space. It calls cipc_go */
	  (void) mbugo(un->un_md);
	  return;
	}
      } else {
	/* Call  mbgo to get DVMA space. It calls cipc_go */
	(void) mbugo(un->un_md);
	return;
      }
  } else {
    cipc_done(md);
  }
}

/*
 *
 *      Subroutine:     cipc_go
 *
 *      Calling Sequence: cipc_go(mb_device *md)
 *
 *      Called By:   SCSI high level routine to process pending transfer or
 *			by cipc_start to process pending transfer
 *                                                                            
 *      Calling Parameters:     mb_device structure of active slave
 *
 *      Local Variables:
 *              bp:  pointer to the buffer header
 *		qio: pointer to active qio structure
 *              un:  pointer to active scsi unit structure
 *		cipc: pointer to cipc_soft structure of ctrl
 *              xpb: pointer to active EXTPB structure in active qio
 *
 *      Calls Subroutines: scsi slave ss_mkcdb() to set up scsi cmd block,
 *			   cipc_ext_cmd() to carry out command
 *
 *      Description:                                                          
 *       This is called via mbugo() when the necessary mainbus space as been  
 *       allocated.  It is always called at disk interrupt priority.          
 */
int
cipc_go(md)
     register struct mb_device *md;
{
  register struct buf *bp;
  register struct cipc_qio *qio;
  register struct scsi_unit *un;
  register struct cipc_softc *cipc;
  struct mb_ctlr *mc;
  EXTPB *xpb;
  int unit, pfnum;
  
#ifdef DEBUG
  if ((cipc_debug & CIPC_TRACE) || (cipc_debug & CIPC_SCSI))
    printf("cipc_go:\n");
#endif DEBUG
  un = (struct scsi_unit *)
    (*scsi_unit_subr[TYPE(md->md_flags)].ss_unit_ptr)(md);
  mc = un->un_mc;
  cipc = &cipc_softc[mc->mc_ctlr];
  
  /*
   * Check to be sure there's a buffer waiting to
   * be processed
   */
  bp = md->md_utab.b_forw;
  if (bp == NULL) {
    panic("cipc_go: no buffer in active queue");
  }
  
  /*
   * Fill in virtual address of buffer
   */
  if ((bp != &un->un_sbuf) && ((un->un_flags & SC_UNF_GET_SENSE) == 0))
    {

      if (bp->b_flags & B_VME_ADDR) {

#ifdef DEBUG
	  if (cipc_debug & CIPC_CMD)
	      printf("cipc_go: VME<->VME\n");
#endif
	  pfnum = hat_getkpfnum(bp->b_un.b_addr);
	  un->un_baddr = (unsigned) ctob(((pfnum & PG_PFNUM) & (~PG_TYPE))) | 
	      ((unsigned)bp->b_un.b_addr & PAGEOFFSET);
      } else
	  un->un_baddr = MBI_ADDR(md->md_mbinfo);

    } else
      un->un_baddr = MBI_ADDR(md->md_mbinfo); /* Mainbus resource alloc info */

  un->un_dma_addr = un->un_dma_count = 0;

  /*
   * The dk_XX[] arrays are kept per configured drive.
   * Each new drive is given a new number
   * This is not kept per controller, unit, etc.
   */
  if ((unit = un->un_md->md_dk) >= 0) {
    dk_busy |= 1<<unit;
    dk_xfer[unit]++;
    if (bp->b_flags & B_READ)
      dk_read[unit]++;
    dk_wds[unit] += bp->b_bcount >> 6;
  }
  
  GET_QIO(cipc, qio);
  bzero(qio, sizeof (*qio));
  qio->cipq_un = un;
  
  /* 
   * Call the high-level mkcdb routine.
   * This sets up the un->un_dma_addr & un->un_dma_count fields
   * as well
   */
  (void)(*un->un_ss->ss_mkcdb)(un);
  
  xpb = &qio->qio_xpb;
  bzero((char *)xpb, sizeof(EXTPB));
  xpb->pb.pb_id = xpb->sb.sb_id = (dword) qio;
  xpb->pb.targetid = un->un_target;
  xpb->pb.addrmod  = EXT_SUPER_DATA;
  xpb->pb.vmeaddr =  (dword)un->un_dma_addr;
  
  if (un->un_flags & SC_UNF_BIG_DMA) { 
    xpb->pb.count = *(int *)&un->un_dma_count;
    un->un_flags &= ~SC_UNF_BIG_DMA;
  } else
    xpb->pb.count = un->un_dma_count;

  /*  copy in the cdb  */
  bcopy(&un->un_cdb, &qio->qio_xpb.pb.scdb, sizeof(qio->qio_xpb.pb.scdb));
  
  if (cipc_ext_cmd(cipc,xpb,CIPC_INTR) != SE_NO_ERROR) {
    FREE_QIO(cipc, qio);
    un->un_c->c_un = un;
    (void)(*un->un_ss->ss_intr)(un->un_c, 0, SE_FATAL);
  } else {	/* Sent successfully */
    cipc->cipc_nactive++;
    qio->cipq_next = cipc->cipc_active;
    cipc->cipc_active = qio;
    if (qio->cipq_next)
      qio->cipq_next->cipq_prev = qio;
    qio->cipq_prev = NULL;
    qio->cipq_time = time.tv_sec; /* Time-stamp it */
  }
}

/*
 *
 *      Subroutine:     cipc_ustart
 *
 *      Calling Sequence: cipc_done(scsi_unit *un)
 *
 *      Called By:      SCSI high level routine to wait for transfer
 *
 *      Calling Parameters:
 *              un:  pointer to active scsi unit structure
 *
 *      Local Variables:
 *              cipc: pointer to cipc_soft structure of ctrl
 *
 *      Calls Subroutines: sleep()
 *
 *      Description:                                                          
 *              The high level scsi transfer routine calls this when it
 *              wants to initiate a transfer to the controller.  This waits
 *              until space is freed in the ctrl's command queue
 */
int
cipc_ustart(un)
  struct scsi_unit *un;
{
  register struct cipc_softc *cipc = &cipc_softc[un->un_mc->mc_ctlr];
  
#ifdef DEBUG
  if ((cipc_debug & CIPC_TRACE) || (cipc_debug & CIPC_SCSI))
    printf("cipc_ustart:\n");
#endif DEBUG
  
  while (cipc->cipc_nfree < 5) {
    cipc->cipc_flags |= CIPC_QIO_SLEEP;
    sleep(&cipc->cipc_free_qio, QIOPRI);
  }
}

/*
 *
 *      Subroutine:     cipc_done
 *
 *      Calling Sequence: cipc_done(mb_device *md)
 *
 *      Called By:      SCSI high level routine to conclude transfer
 *
 *      Calling Parameters:
 *              md:  pointer to main bus device structure
 *
 *      Local Variables:
 *              bp:  pointer to the buffer header
 *              un:  pointer to active scsi unit structure
 *
 *      Calls Subroutines: cipc_start(), iodone()
 *
 *      Description:
 *              the high level Interrupt routine calls this
 *              to indicate completion of the SCSI bus transaction. Only
 *              called if this isn't a DMA transfer.  If it IS a dma
 *              transfer, it will be called from mbudone after the mainbus
 *              space has been reclaimed.  It is always called at disk
 *              interrupt priority.
 */
int
cipc_done(md)
     struct mb_device *md;
{
  register struct scsi_unit *un;
  register struct buf *bp = md->md_utab.b_forw;
  
#ifdef DEBUG
  if ((cipc_debug & CIPC_TRACE) || (cipc_debug & CIPC_SCSI))
    printf("cipc_done:\n");
#endif DEBUG
  un = (struct scsi_unit *)
    (*scsi_unit_subr[TYPE(md->md_flags)].ss_unit_ptr)(md);
  
  /* advance mb_device queue */
  md->md_utab.b_actf = bp->av_forw;
  /* 
   * we are done, so clear buf in active position of 
   * md's queue. then call iodone to complete i/o
   */
  md->md_utab.b_forw = NULL;
  
  if (bp == NULL) {
    printf("cipc_done: no buf? un = 0x%x, flags=0x%x\n", 
	   un, un->un_flags);
    printf("        cmd=%x, count=0x%x, dma_addr=0x%x, target=0x%x\n",
	   un->un_cmd, 
	   un->un_dma_count, 
	   un->un_dma_addr, 
	   un->un_target);
  } else
    (void) iodone(bp);
  
  (void) cipc_start(un);
}

/*
 *
 *      Subroutine:     cipc_cmd
 *
 *      Calling Sequence: cipc_cmd(c, un, intr)
 *
 *      Called By:      SCSI high level routine to execute command
 *
 *      Calling Parameters:
 *		c:   pointer to scsi_ctlr struct of active ctlr
 *              un:  pointer to active scsi unit structure
 *		intr: flag set if asynch transfer wanted
 *
 *      Local Variables:
 *		qio: pointer to active qio struct
 *		md:  pointer to active mb device (of scsi_unit)
 *		cipc: pointer to cipc_soft struct of active ctlr
 *		ret: value to be returned
 *
 *      Calls Subroutines:      cipc_ext_cmd()
 *
 *      Description:
 *              Called from the interrupt routine of the high level driver
 *              to do an operation on the SCSI bus.  Only called if the high  
 *              level driver is already in posession of the bus
 */
int
cipc_cmd(c, un, intr)
     struct scsi_ctlr *c;
     struct scsi_unit *un;
     int intr;
{
  register struct cipc_qio *qio;
  register struct mb_device *md = un->un_md;
  register struct cipc_softc *cipc = &cipc_softc[c->c_un->un_mc->mc_ctlr];
  register EXTPB *xpb;
  int ret;
  
#ifdef DEBUG
  if ((cipc_debug & CIPC_TRACE) || (cipc_debug & CIPC_SCSI))
    printf("cipc_cmd:\n");
#endif DEBUG
  if (cipc->cipc_free_qio == NULL)
    return SE_RETRYABLE;  

  GET_QIO(cipc,qio);
  bzero(qio, sizeof(*qio));

  qio->cipq_un = un;
  xpb = &qio->qio_xpb;
  xpb->pb.pb_id = xpb->sb.sb_id = (dword) qio;    /* Set up the xpb */
  xpb->pb.targetid = (byte)un->un_target;
  if (un->un_flags & SC_UNF_BIG_DMA)
    xpb->pb.count = *(int *)&un->un_dma_count;
  else
    xpb->pb.count = un->un_dma_count;
  xpb->pb.addrmod  = EXT_SUPER_DATA;
  xpb->pb.vmeaddr = (un->un_dma_addr) ? un->un_dma_addr : MBI_ADDR(md->md_mbinfo);
  
  /* copy in the cdb. */
  bcopy(&un->un_cdb, &qio->qio_xpb.pb.scdb, sizeof(qio->qio_xpb.pb.scdb));

  ret = cipc_ext_cmd(cipc, xpb, intr ? CIPC_INTR : 0);   /* Send it !! */
  if ((ret != SE_NO_ERROR) || (intr == 0)) {   /* Failed */
    FREE_QIO(cipc, qio);
    return(ret);
  }
  /* 
   * xpb was sent to the board successfully.
   * Keep track of what we gave to the board 
   */
  cipc->cipc_nactive++;
  qio->cipq_next = cipc->cipc_active;
  cipc->cipc_active = qio;
  if (qio->cipq_next)
    qio->cipq_next->cipq_prev = qio;
  qio->cipq_prev = NULL;
  
  /* Time-stamp it */
  qio->cipq_time = time.tv_sec;
  return (0);
}

/*
 *
 *      Subroutine:             cipc_getstat
 *
 *      Calling Sequence:       cipc_getstat(un, recursive)
 *
 *      Description:
 *		Used to obtain the residual count of the last DVMA 
 *              in polling mode.  (only used by sd())
 */
int
cipc_getstat(un, recursive)
  struct scsi_unit *un;
  int recursive;
{
  register struct cipc_softc *cipc = &cipc_softc[un->un_mc->mc_ctlr];
  
#ifdef DEBUG
  printf("cipc_getstat(0x%x, 0x%x)\n", un, recursive);
#endif
  return (cipc->cipc_last_polled_cmd_status);
}

/*
 *
 *      Subroutine:             cipc_dmacount
 *
 *      Calling Sequence:       cipc_dmacount(c)
 *
 *      Calling Parameters: pointer to scsi_ctlr struct
 *
 *      Description:
 *              Used in polling mode to obtain the residual count of the
 *              last DVMA.
 */
int
cipc_dmacount(c)
     struct scsi_ctlr *c;
{
  struct cipc_softc *cipc = &cipc_softc[c->c_un->un_mc->mc_ctlr];
  
#ifdef DEBUG
  printf("cipc_dmacount(0x%x)\n", c);
#endif
  return(cipc->cipc_last_polled_cmd_status);
}

/*
 *
 *      Subroutine:             cipc_cmd_wait                                  
 *                                                                            
 *      Calling Sequence:       cipc_cmd_wait(scsi_ctlr *c)                    
 *
 *      Description:
 *              Used only by sd to wait for the completion of a command
 *               in polling mode.
 *                 NOT IMPLEMENTED
 */
int
cipc_cmd_wait(c)
     struct scsi_ctlr *c;
{
#ifdef DEBUG
  printf("cipc_cmd_wait(0x%x)\n", c);
#endif
}

/*
 *
 *      Subroutine:             cipc_off
 *
 *      Calling Sequence:       cipc_off(un)
 *
 *      Description:
 *                              Mark a unit offline
 *                 NOT IMPLEMENTED
 */
int
cipc_off(un)
     struct scsi_unit *un;
{
#ifdef DEBUG
  printf("cipc_off(0x%x)\n", un);
#endif
}

/*
 *
 *      Subroutine:             cipc_scsi_reset
 *
 *      Calling Sequence:      cipc_scsi_reset(c, un)
 *
 *      Calls Subroutines:      cipc_init()
 *                                                                            
 *      Description:                                                          
 *                 Reset the scsi bus - the Ciprico Rimfire uses 
 *                 a device reset to reset the SCSI bus.
 */
int
cipc_scsi_reset(c, un)
     struct scsi_ctlr *c;
     struct scsi_unit *un;
{
  struct cipc_softc *cipc = &cipc_softc[un->un_mc->mc_ctlr];
  
  printf("cipc_scsi_reset: cipc%d (0x%x) reset.\n", cipc->cipc_unit, c);
  cipc_init(cipc, un->un_md, CIPC_INIT);  /* Reinit ctlr & all slaves */
  return (0);
}

/*
 *
 *      Subroutine:             cipc_deque
 *
 *      Calling Sequence:       cipc_deque(c, un)        
 *                                                                            
 *      Called by: high level scsi tmeout routine
 *
 *                                                                            
 *      Calls Subroutines:      cipc_init()
 *                                                                            
 *      Description:                                                          
 * 		If ctrl is active, reset it by calling cipc_init().
 */
int
cipc_deque(c, un)
     struct scsi_ctlr *c;
     struct scsi_unit *un;
{
  struct cipc_softc *cipc = &cipc_softc[un->un_mc->mc_ctlr];
  
#ifdef DEBUG
  printf("cipc_deque: cltr: 0x%x, unit: 0x%x\n",c, un);
#endif
  if (cipc->cipc_active) {
    printf("cipc%d: deque, resetting controller...\n", cipc->cipc_unit);
    cipc_init(cipc, un->un_md, CIPC_INIT);  /* Reinit ctlr & all slaves */
    return (0);
  } else {
    printf("cipc%d: deque called with no active qio's!\n",
	   cipc->cipc_unit);
    return (1);
  }
}  

/*
 *
 *      Subroutine:             cipc_ext_cmd
 *
 *      Calling Sequence:       cipc_ext_cmd(cipc, xpb, intr)
 *
 *      Called by:              all routines which send commands
 *
 *      Calling Parameters:
 *			cipc: pointer to cipc_soft of active ctlr
 *			xpb: pointer to extended param blk to be sent
 *			intr: flag which is set if asynch transfer to be done
 *
 *      Local Variables:
 *			mc: pointer to mb_ctlr of active controller
 *			cipc_reg: pointer to hardware device command register
 *                      msw, lsw: MSW and LSW of a long word
 *			xpb_addr: DVMA addr of extended parameter block
 *			waitfor: indicates which value of device busy bit to wait for
 *			timer: used to time out non-responsive command
 *
 *      Description:
 *              A type 0 command is sent to the RF 3500.  The address of the
 *              extended parameter block is written to the address buffer port
 *              and a 0 is written to the channel attention port.  The status
 *              block is then polled until the command completes.
 *		If an error occurs, the error is mapped into one of the
 *		high level SCSI errors.
 *		In polling mode, the routine busy waits. In interrupt mode,
 *		it sets up the interrupt type and returns.
 *                                                                            
 */

static
cipc_ext_cmd(cipc, xpb, intr)
     register struct cipc_softc *cipc;
     register EXTPB *xpb;
     register int intr;
{
  register struct mb_ctlr *mc = cipc_cinfo[cipc->cipc_unit];
  register RF35REG *cipc_reg;
  register word lsw, msw;
  register u_long xpb_addr;
  register int waitfor;
  register long timer;
  struct vec *vp;
  int error, compcode, scsistat;
  int s;
  
  /* Clear the status block */
  bzero((char *)&xpb->sb, sizeof(STATBLK));
  cipc_reg = cipc->cipc_addr;
  
#ifdef DEBUG
  if ((cipc_debug & CIPC_TRACE) || (cipc_debug & CIPC_CMD)) {
    printf("cipc_ext_cmd: cipc_unit: 0x%x, xpb: 0x%x, intr: %d\n",
	   cipc->cipc_unit, xpb, intr);
    printf("cipc_ext_cmd: status port = %x\n", SWAB(cipc_reg->status));
  }
#endif

  if (mc)
    s = splr(pritospl(mc->mc_intpri));
  else
    s = splhigh();
  
  if (mc == NULL && intr != 0)
    panic("cipc_ext_cmd: null mb_ctlr info!");
    
  if (intr == CIPC_INTR) {   /* Set up for DMA transfer interrupt */
    vp = mc->mc_intr;
    xpb->intr = (mc->mc_intpri << 8) | vp->v_vec;
    xpb->resv0 = 0;
    xpb->resv1 = 0;
    /* Inhibit Request Sense - trying it off */
    xpb->pb.flags |= IRS;
    xpb->pb.addrmod = EXT_SUPER_DATA;
  } else /* intr == CIPC_WAIT */
    xpb->intr = 0;
  
  /* Wait until we can write to address buffer port */
  timer = 0L;
  waitfor = cipc_softc[mc->mc_ctlr].cipc_bsybit;
  while ((cipc_reg->status & 0x1) != waitfor)
#ifdef TOOLONG
    if (++timer > TOOLONG) {
      printf("cipc_ext_cmd: timeout ... BSY\n");
      printf("cipc_ext_cmd: status word(%x), want %d\n", cipc_reg->status,
		waitfor);
      (void) splx(s);
      panic("cipc_ext_cmd .. BSY");
      return(SE_TIMEOUT);
    }
#else TOOLONG
  ;
#endif TOOLONG
  /* Tell the controller our system characteristics */
  /* Write the control and address modifier to the address buffer port */
  cipc_reg->addrbuf = SWAB(CNTRL << 8 | EXT_SUPER_DATA);

  /* convert address of xpb to a dword for shifting */
  xpb_addr = (u_long) xpb - (u_long) DVMA; 
  msw = SWAB(xpb_addr >> 16 & 0xffff);
  lsw = SWAB(xpb_addr & 0xffff);

#ifdef DEBUG
  if (cipc_debug & CIPC_CMD)
    printf("cipc_ext_cmd: cipc_reg=%x xpb=%x msw=%x lsw=%x\n",
           cipc_reg, xpb, msw, lsw);
#endif

  /* write the msw and lsw of the pb to the address buffer port */
  cipc_reg->addrbuf = msw;
  cipc_reg->addrbuf = lsw;

  /* Get the controllers attention */
  cipc_reg->attention = 0;
  cipc_softc[mc->mc_ctlr].cipc_bsybit ^= 1;    /* Toggle the bit */

  /*
   * If we're running in interrupt mode, we're done
   */
  if (intr) {
    (void) splx(s);
    return(SE_NO_ERROR);
  }

  /* Wait for the command to complete */
  timer = 0;
  while ((xpb->sb.flags & ST_CC) != ST_CC)
    if (++timer > MAXWAIT) {
      printf("cipc%d: cipc_ext_cmd timeout ... no ST_CC\n",
	mc->mc_ctlr);
      printf("cipc%d: status port = %x\n", SWAB(cipc_reg->status));
      sb_print(&xpb->sb);
      (void) splx(s);
      return(SE_FATAL);
    }

  compcode = xpb->sb.error;
  scsistat = xpb->sb.scsistat & STATMASK;
  /* 
   * First convert combined cipc completion code and SCSI status
   * to generic code,
   * one of SE_NO_ERROR, SE_RETRYABLE, SE_FATAL, or SE_TIMEOUT
   *
   */
  
  switch(compcode) {
  case EE_NOERR:
    error = SE_NO_ERROR;
    break;
    
  case EE_SCSISELTO:
    error = SE_TIMEOUT;
    break;
    
  case EE_SCSIERR:                /* SCSI ERROR */
    if ((scsistat == CHECK_COND) || (scsistat == DEVICE_BUSY)) { 
      error = SE_RETRYABLE;
    } else {      /* Assume fatal error */
      printf("cipc%d: unknown SCSI status %x\n",
	     mc->mc_ctlr, xpb->sb.scsistat);
      error = SE_FATAL;
    }
    break;

  default:
    printf("cipc%d: Controller reported error (%s), SCSI status 0x%x\n",
	   mc->mc_ctlr, cipc_cntrlerr[compcode], (scsistat&STATMASK));
    sb_print(&xpb->sb);
    error = SE_FATAL;
  }

  cipc->cipc_last_polled_cmd_status = compcode;  
  if (error == 0)
    cipc->cipc_last_polled_cmd_resid = xpb->pb.count;
  else
    cipc->cipc_last_polled_cmd_resid = 0;

  splx(s);
  return(error);
}

/*
 *
 *      Subroutine:             pb_print
 *
 *      Calling Sequence:       pb_print(pb)
 *
 *      Description:
 *              Displays parameter block of the extended parameter
 * 		block.
 */
static
pb_print(pb)
     PARMBLK *pb;
{
  printf("PB: 0x%x\n", pb);
  printf("id: 0x%x\n", pb->pb_id);
  printf("resv: 0x%x flags: 0x%x addrmod: 0x%x targetid: %d\n",
	 pb->resv, pb->flags, pb->addrmod, pb->targetid);
  printf("vmeaddr: 0x%x\n", pb->vmeaddr);
  printf("count: 0x%x\n", pb->count);
  printf("SCSI information:\n");
  printf("cmd: 0x%x, lun: 0x%x, tag: 0x%x\n",
	 pb->scdb.cmd, pb->scdb.lun, pb->scdb.tag);
}

/*
 *
 *      Subroutine:             sb_print
 *
 *      Calling Sequence:       sb_print(sb)
 *
 *      Description:
 *		Displays status block of the extended parameter
 * 		block
 */
static
sb_print(sb)
     STATBLK *sb;
{
  printf("SB: 0x%x\n", sb);
  printf("id: 0x%x\n", sb->sb_id);
  printf("zero: 0x%x scsistat: 0x%x error: 0x%x flags: %d\n",
	 sb->zero, sb->scsistat, sb->error, sb->flags);
  printf("Extra information:\n");
  printf("class: 0x%x, segment: 0x%x, scsiflags: 0x%x, infob3 0x%x\n",
	 sb->class ,sb->segment ,sb->scsiflags ,sb->infob3);
  printf("infob4: 0x%x, infob5: 0x%x, infob6: 0x%x, exlen: 0x%x\n",
	 sb->infob4, sb->infob5, sb->infob6, sb->exlength);  
}

#endif NCIPC
