#define JET960UNIT 0		/* ugly ugly ugly HACK HACK HACK */
/*
 * FIX:    fix this soon
 * NOTE:   various types of notes about the implementation
 * ASK:    questions to be answered
 * CHECK:  check this at some later date
 */

/*
 * these are the defines that change the behavior of this driver, they
 * can be set as options in the kernel config files.  they could be
 * set here if you really wanted to.

 IJET_LOOPBACK              software loopback in ijetifoutput()
 IJET_CELL_COUNTS           keep rcv and xmt cell counts
 IJET_INTELMERGESPACKETS    try to handle merged packets
 IJET_N48HACK               avoid merged packets and assume we don't get any
 IJET_LOSES_INTERRUPTS      assume we lose interrupts occasionally
 IJET_CREDIT                host coarse grain credit
 IJET_DYN_CREDIT            host fine grain credit
 JET960_CREDIT              i960 coarse grain credit
 JET960_DYN_CREDIT          i960 fine grain credit
 IJET_PERVC_LIMITS          limits on xmt'd pkts per vc
 */

#ifdef IJET_LOOPBACK
int loopback = 0;
#endif

int ijet_testing_minvr = 0x100000;
int ijet_testing_maxvr = 0x0;

/* check some things for sanity */
/* must not have both of these defined at the same time*/
#if defined(IJET_N48HACK) && defined(IJET_INTELMERGESPACKETS)
#error do not define both IJET_N48HACK and IJET_INTELMERGESPACKETS
#endif

#define IJET_CREDIT_VR_SIZE 48

/*
 * This macro uses % twice since the way % handles negative
 * operands is compiler dependent.
 */
#define ATM_DATA_SIZE     48
#define TRAILER_ALIGNMENT 40
#define TRAILER_OFFSET(x) \
   ((ATM_DATA_SIZE+TRAILER_ALIGNMENT-((x)%ATM_DATA_SIZE))%ATM_DATA_SIZE)

/* FIX: get rid of unnecessary includes */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/buf.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/syslog.h>
#include <sys/device.h>
#include <sys/malloc.h>

#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/vm_extern.h>

extern vm_map_t kernel_map;

#include <net/if.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <net/netisr.h>
#include <net/route.h>
#include <net/if_llc.h>

#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>

/*
#include <dev/pci/pcireg.h> 
#include <dev/pci/pcivar.h> 
*/

#include "/afs/cs.cmu.edu/project/nectar-austin/jetland/include/pcireg.h"
#include "/afs/cs.cmu.edu/project/nectar-austin/jetland/include/pcivar.h"

#include "/afs/cs.cmu.edu/project/nectar-austin/jetland/include/ijet_coding.h"
#include "/afs/cs.cmu.edu/project/nectar-austin/jetland/include/ijet_types.h"
#include "/afs/cs.cmu.edu/project/nectar-austin/jetland/include/ijet_intr.h"
#include "/afs/cs.cmu.edu/project/nectar-austin/jetland/include/ijet_ll.h"
#include "/afs/cs.cmu.edu/project/nectar-austin/jetland/include/if_ijet_user.h"
#include "/afs/cs.cmu.edu/project/nectar-austin/jetland/include/if_ijet.h"
#include "/afs/cs.cmu.edu/project/nectar-austin/jetland/include/jet960_internals.h"
#include "/afs/cs.cmu.edu/project/nectar-austin/jetland/include/jet960.h"
#include "/afs/cs.cmu.edu/project/nectar-austin/jetland/include/jetmsg.h"
/*
#include <dev/pci/jetland/include/ijet_coding.h> 
#include <dev/pci/jetland/include/ijet_types.h> 
#include <dev/pci/jetland/include/ijet_intr.h> 
#include <dev/pci/jetland/include/ijet_ll.h> 
#include <dev/pci/jetland/include/if_ijet_user.h> 
#include <dev/pci/jetland/include/if_ijet.h> 
#include <dev/pci/jetland/include/jet960_internals.h> 
#include <dev/pci/jetland/include/jet960.h> 
#include <dev/pci/jetland/include/jetmsg.h>
*/

/*
 * if we want profiling to work....redefine private
 */
#undef private
#define private 

#define BLI bufferLengthIndicator

#define jet_Vrp2VrIndex(ijets, vrp) ((vr_t *) (vrp)-ijets->ll_vrBase)
#define jet_Vrp2VrIndex0(ijets, vrp) ((vr_t *) (vrp)-ijets->ll_vrPartition)
#define jet_VrIndex2Vrp(ijets, index) (&ijets->ll_vrBase[index])

/*
 * FIX::
 *   change these to vm_offset_t??
 */
#define IJET_SRAM2MIRROR(vrp) ((vr_t *) (((vm_offset_t) (vrp)) - \
					 ijets->mirror2sramoffset))
#define IJET_MIRROR2SRAM(mvrp) ((vr_t *) (((vm_offset_t) (mvrp)) + \
					 ijets->mirror2sramoffset))

#define jet_MVrp2VrIndex(ijets, mvrp) \
   jet_Vrp2VrIndex(ijets, IJET_MIRROR2SRAM(mvrp))
#define jet_MVrp2VrIndex0(ijets, mvrp) \
   jet_Vrp2VrIndex0(ijets, IJET_MIRROR2SRAM(mvrp))
#define jet_VrIndex2MVrp(ijets, index) \
   IJET_SRAM2MIRROR(jet_VrIndex2Vrp(ijets, index))


/*
 * FIX:: make everything take ijets rather than unit.
 */
private bool ijetprobe     (struct device *parent, struct device *self,
			    void *aux);
private void ijetattach    (struct device *parent, struct device *self,
			    void *aux);
private int  ijetintr      (void *ijets);

private bool ijetifinit    (ijet_softc_t *ijets);
private void ijetifstop    (ijet_softc_t *ijets);
private int  ijetifioctl   (struct ifnet *ifp, u_long command, caddr_t data);
private int  ijetifoutput  (struct ifnet *ifp, struct mbuf *mbuf,
			    struct sockaddr *sa, struct rtentry *rt);
private int  ijetifwatchdog(short unit);

private bool ijetrcvinit   (ijet_softc_t *ijets);
private void ijetrcv       (ijet_softc_t *ijets, vr_t *vrp, bool mboxtail);

private void ijetIpvcTimer (void *arg);
private void ijetVinceFailed (ijet_softc_t *ijets);
private bool ijetxmtinit   (ijet_softc_t *ijets);
private void ijetxmtbuildvrs (ijet_softc_t *ijets, vcid_t vcid);
private void ijetxmtdone   (ijet_softc_t *ijets, vr_t *vrp, bool mboxtail);
private void ijetcheckxmt  (ijet_softc_t *ijets);
private void ijetaddxmtvr  (ijet_softc_t *ijets, vr_t *vrp);
private void ijetxmtdoxmt(ijet_softc_t *ijets, vcid_t vcid);

private void ijetrcvwalker(ijet_softc_t *ijets);
private void ijetxmtwalker(ijet_softc_t *ijets);
private void ijetflist     (ijet_softc_t *ijets, freelist_t freelist);
#if (defined(IJET_CREDIT) || defined(IJET_DYN_CREDIT))
private bool ijetcreditinit(ijet_softc_t *ijets);
private void ijetaddcreditvr(ijet_softc_t *ijets, vr_t *vrp);
private void ijetcreditwalker(ijet_softc_t *ijets);
private void ijetcreditmboxhandler(ijet_softc_t *ijets);
private void ijetcreditflist(ijet_softc_t *ijets, freelist_t freelist);
private vcid_t cellvpivci(ijet_credit_cell_t *cell);
#endif


private void ijetifstop    (ijet_softc_t *ijets);
private void ijetreset     (ijet_softc_t *ijets);
private bool ijetcheckcfg  (ijet_softc_t *ijets, ijet_ioctl_setup_t *setup);
#ifdef IJET_INTELMERGESPACKETS
private int  findEndingVr  (ijet_softc_t *ijets, vr_t *headp, vr_t *tailp,
			    int traileroffset, vr_t **nextpkt, int pdulen);
#endif
private void VrFreeRoutine (ijet_softc_t *ijets, vr_t *vrp, int bli);
private void VrAttachRoutine(ijet_softc_t *ijets);

extern int hz;                  /* No. of clock ticks per second */

#ifdef JET960_CREDIT
public volatile bool *ijetFlag;
public volatile turn_t *ijet960Turn;
public volatile uint8 *ijet_960CreditTable;
public volatile uint8 *ijet_960RequestTable;
static bool i960up = FALSE;

#define CS_ENTRY_PROTOCOL()                            \
  do {                                                 \
    ijetFlag[HOST] = TRUE;                             \
    *ijet960Turn = I960;                               \
    while (ijetFlag[I960] && (*ijet960Turn == I960));  \
    debugp("Host entering critical section\n");          \
  } while (0)

#define CS_EXIT_PROTOCOL()                      \
  do {                                          \
    ijetFlag[HOST] = FALSE;                     \
    debugp("Host exiting critical section\n");    \
  } while(0) 

#endif


#ifdef alpha
#define	vtophys(va)							\
	({								\
		pci_moffset_t pcipa[2];					\
		int rv;							\
									\
		if ((rv = PCI_DMA_MAP(ijets->ijet_pdf, ijets->ijet_pdfa, \
		    (vm_offset_t)va, 1 /* XXX */, &pcipa[0],                \
                    PCI_DMA_ADDRBOUND_NONE)) != 1)	                \
			panic("%s: %d: pcidma_map ret != 1 (%d)",	\
			    __FILE__, __LINE__, rv);			\
		if (pcipa[0] != (vtophys(va) | 0x40000000))		\
			panic("%s: %d: pcidma_map pa (%lx) bad (not %lx)", \
			    __FILE__, __LINE__, pcipa[0],		\
			    vtophys(va) | 0x40000000);			\
		pcipa[0];						\
	})

#define pci_conf_read(tag, reg) \
   PCI_CONF_READ(ijets->pda->pda_conffns, ijets->pda->pda_confarg, \
                 tag, reg)
#define pci_conf_write(tag, reg, value) \
   PCI_CONF_WRITE(ijets->pda->pda_conffns, ijets->pda->pda_confarg, \
                  tag, reg, value)
#endif


/*
 * This is referenced by ioconf.c:  a file created during the config
 * process.  It is how our probe and attach routines get registered.
 * The standard open/close/ioctl routines are registed in the
 * conf.c file.
 */
public struct cfdriver ijetcd = {
  NULL, (char *) "ijet", (cfmatch_t) ijetprobe, ijetattach,
  DV_IFNET, sizeof(ijet_softc_t)
};

/*
 * Standard simple probe routine.   We should be called for every
 * PCI device on the bus.  We check the configuration ID to see
 * if it matches ours.  Return True on success.
 */
private bool ijetprobe(struct device *parent, struct device *self, void *aux)
     begin(ijetprobe);
{
#ifndef alpha  
  struct pci_attach_args *pa = aux;
#else
  struct pcidev_attach_args *pa = aux;
#endif
  
  /* Check that this PCI board is ours. */
#ifndef alpha
  if (pa->pa_id != IJET_CONFIG_ID)
#else
  if (pa->pda_id != IJET_CONFIG_ID)
#endif
    ret FALSE;

  ret TRUE;
}
end(ijetprobe);

/*
 * ijetattach()
 *
 * Now that the board has been found, we can do some basic initialization.
 *
 * Map in the various memory segments, and initialize the interface
 * structure.  Notify the lower level that the board is present.
 */
void ijetattach(struct device *parent, struct device *self, void *aux)
     begin(ijetattach);
{
  ijet_softc_t *ijets = (void *) self;
  struct ifnet *ifp = &ijets->ijet_if;
#ifdef alpha
  pci_moffset_t physaddr;
  pci_msize_t size;
  int cacheable;
#else
  vm_offset_t physaddr;
#endif
  int loop;
  
#ifndef alpha  
  struct pci_attach_args *pa = aux;
#else
  struct pcidev_attach_args *pa = aux;
  ijets->pda = pa;
#endif alpha

  /* save this, we may need it at some later date */
#ifndef alpha
  ijets->pa_tag = pa->pa_tag;
#else
  ijets->pa_tag = pa->pda_tag;
  ijets->ijet_pdf = pa->pda_dmafns;
  ijets->ijet_pdfa = pa->pda_dmaarg;
#endif alpha
  
  /*
   * Map the pci memory segments.
   * The 4 regions are local memory, registers, SONET, flash
   */
#ifndef alpha
  if (pci_map_mem(pa->pa_tag, IJET_PCI_SRAM,
		  &ijets->sramBase,
		  &physaddr))
    ret;
  if (pci_map_mem(pa->pa_tag, IJET_PCI_ASIC,
		  &ijets->asicBase,
		  &physaddr))
    ret;
  if (pci_map_mem(pa->pa_tag, IJET_PCI_SONET,
		  &ijets->sonetBase,
		  &physaddr))
    ret;
#else
  if (PCI_FIND_MEM(pa->pda_conffns, pa->pda_confarg,
                   pa->pda_tag, IJET_PCI_SRAM, &physaddr,
                   &size, &cacheable))
    return;
  warn("SRAM cacheable was %d, now 1\n", cacheable);
  cacheable = 1;
  ijets->sramBase = PCI_MEM_MAP(pa->pda_memfns, pa->pda_memarg,
                                physaddr, size, cacheable);
  warn("mapping SRAM: phys = %p, virt = %p, size = 0x%x\n",
       (char *) (vm_offset_t) physaddr, (char *) ijets->sramBase, size);
  
  if (PCI_FIND_MEM(pa->pda_conffns, pa->pda_confarg,
                   pa->pda_tag, IJET_PCI_ASIC, &physaddr,
                   &size, &cacheable))
    return;
  warn("SRAM cacheable was %d, now 0\n", cacheable);
  cacheable = 0;
  ijets->asicBase = PCI_MEM_MAP(pa->pda_memfns, pa->pda_memarg,
                                physaddr, size, cacheable);
  warn("mapping ASIC: phys = %p, virt = %p, size = 0x%x\n",
       (char *) (vm_offset_t) physaddr, (char *) ijets->asicBase, size);
  
  if (PCI_FIND_MEM(pa->pda_conffns, pa->pda_confarg,
                   pa->pda_tag, IJET_PCI_SONET, &physaddr,
                   &size, &cacheable))
    return;
  warn("SRAM cacheable was %d, now 0\n", cacheable);
  cacheable = 0;
  ijets->sonetBase = PCI_MEM_MAP(pa->pda_memfns, pa->pda_memarg,
                                 physaddr, size, cacheable);
  warn("mapping SONET: phys = %p, virt = %p, size = 0x%x\n",
       (char *) (vm_offset_t) physaddr, (char *) ijets->sonetBase, size);
  
#endif

  /*
   * Reset the board.
   */
  ijetreset(ijets);

  /*
   * Now set up the interrupt mappings.
   */
#ifdef OLD_CURRENT  
  ijets->ijet_ih.ih_fun = ijetintr;
  ijets->ijet_ih.ih_arg = ijets;
  ijets->ijet_ih.ih_level = IPL_NET;
  if (pci_map_int(pa->pa_tag, &ijets->ijet_ih) != 0)
    ret;
#else
#ifndef alpha
  ijets->ijet_ih = pci_map_int(pa->pa_tag, PCI_IPL_NET, ijetintr, ijets);
#else
  ijets->ijet_ih = PCI_MAP_INT(pa->pda_intrfns, pa->pda_intrarg,
                               pa->pda_tag, pa->pda_pin, pa->pda_line,
                               IPL_NET, ijetintr, ijets);
#endif
  if (ijets->ijet_ih == NULL) {
    warn("couldn't map interrupt!!!\n");
    ret;
  }
#endif

  /* initialize ifnet structure */
  ifp->if_unit =     ijets->ijet_dev.dv_unit;
  ifp->if_name =     ijetcd.cd_name;
  ifp->if_mtu =      IJET_ATM_MTU;
  ifp->if_type =     IFT_OTHER;
  ifp->if_addrlen =  sizeof(vcid_t);    /* ??USEFUL?? */
  ifp->if_hdrlen =   IJET_ATM_HDR;
#ifdef IJET_N48HACK
  ifp->if_hdrlen =   IJET_ATM_HDR+sizeof(uint32);
#endif
  ifp->if_timer =    0;
  ifp->if_reset =    0;
  ifp->if_output =   ijetifoutput;
  ifp->if_start =    0;
  ifp->if_ioctl =    (typeof (ifp->if_ioctl)) ijetifioctl;  /* HACK */
  ifp->if_watchdog = (void *) ijetifwatchdog;
  ifp->if_flags |=   (IFF_NOTRAILERS | IFF_MULTICAST);

  /* attach the interface */
  if_attach(ifp);

  /*
   * Initialize Vince stuff
   */
  ijets->vince_flag = FALSE;
  ijets->vince_proc = 0;
  ijets->next_arp = -1;
  for (loop=0; loop<MAX_ARPS; loop++) ijets->pending_arps[loop] = 0;
  

  /*
   * Initialize address resolution table.
   *
   * Put all available entries on the free list.
   * Start hash table empty, users must fill via ioctl.
   */
  ijets->art_free = &ijets->art_pool[0];
  for(loop=0; loop<IJET_ART_SIZE-1; loop++)
    ijets->art_pool[loop].next = &ijets->art_pool[loop+1];
  ijets->art_pool[loop].next = NULL;
  
  for(loop=0; loop<IJET_ART_HASH; loop++)
    ijets->art[loop] = NULL;

  timeout(ijetIpvcTimer, ijets, 5*hz);  /* poll idle vcs every 5 sec */
  warn("device attached ok!\n");
  ret;
}
end(ijetattach);

/*
 * ijetintr()
 *
 * A short stub routine primarily for testing...we just pass the
 * interrupt on to the lower level.  We will eventually call the
 * lower level interrupt routine directly.
 */
int ijetintr(void *arg)
     begin(ijetintr);
{
  ijet_softc_t *ijets = (ijet_softc_t *) arg;
  static int entry = 0;
  if (++entry > 1)
    warn("ijetintr():  in handler multiple times!!!!!\n");
  ijet_InterruptHandler(ijets);
  entry--;
  ret 0;
}
end(ijetintr);

/*
 * ijetIpvcTimer()   
 *
 * Times out all IP SVCs according to RFC 1577.  For every IP SVC idle for more
 * than 15 minutes, Vince is signalled to intiate a RELEASE on that connection
 * and close the SVC.  The SVC is removed from the hash table by a call to 
 * IJETIOC_DEL_ART from Vince. 
 *
 */
private void ijetIpvcTimer(void *arg)
{ 
    int loop;
    int s = splnet();
    static uint8 count = '0';	/* HACK HACK HACK::: remove destroy kill */
    ijet_art_ent_t *harteprev, *harte;
    ijet_softc_t *ijets = (void *) arg;
   
    if (debug_directTextScreenMapDone) {
      debug_directTextScreenp[75] = 0x0c00 | count++;
      if (count == '9' + 1) count = '0';
    }

    for (loop = 0; loop < IJET_ART_HASH; loop++) {
        harte = ijets->art[loop];
        harteprev = NULL;
        while (harte != NULL) {
            if (harte->flag & IP_SVC) {
                harte->idle += 5; /* increment idle time by 5 sec */
                if (harte->idle > LLC_VC_TIMEOUT) {
                    char ip[60];

                    sprintf(ip, "ijetCloseIpVc %d %d\n", harte->tx_VCid,
                            harte->rx_VCid);
                    debug("Sending command %s\n", ip);
                    skip_jetmsg_rx_interface (ip, strlen(ip));
		    /*
		     * HACK:  We set the idle time of a timed out VC to
		     * zero in order to avoid repeated calls to Vince to
		     * shutdown the VC.  Hopefully, Vince will teardown
		     * the connection and remove the VC from the hash table
		     */
		    harte->idle = 0;
                }
            }
            harteprev = harte;
            harte = harte->next;
        }
    }
    ijets->vince_keep_alive += 5;
    if ((ijets->vince_keep_alive >= VINCE_TIMEOUT) && (ijets->vince_flag)) {
      ijetVinceFailed(ijets);
      ijets->vince_flag = FALSE;
    }
    timeout(ijetIpvcTimer, ijets, 5*hz); /* schedule another timer interrupt */
                                         /* after 5 seconds */
    splx(s);
}

private void ijetVinceFailed(ijet_softc_t *ijets)
{
  ijet_art_ent_t *arte, **arteprev;
  int loop;

  warn("Vince keep alive timeout occurred: Destroying all existing SVCs\n");
  /* flush ARP table */
  for (loop = 0; loop < IJET_ART_HASH; loop++) {
    arteprev = &ijets->art[loop];
    arte = *arteprev;
    while (arte != NULL) {
      /* If SVC delete from hash table */  
      if (arte->flag == IP_SVC) {
        ll_CloseRx(ijets, arte->rx_VCid);  /* close tx and rx VCs */
        ll_CloseTx(ijets, arte->tx_VCid);
        *arteprev = arte->next;
        arte->next = ijets->art_free;     /* place entry on freelist */
        ijets->art_free = arte; 
        arte = *arteprev;
      }
      else {
        arteprev = &arte->next;
        arte = arte->next;
      }
    }
  }
}

/*
 * ijetifinit()
 *
 * I don't know when this should normally get called.  For now, we're
 * going to call it when we are ifconfig'd up.  Return success/failure.
 *
 * FIX:: We do not have a way to reset/reinitialize/stop....well, not
 *       more than once.
 */
bool ijetifinit(ijet_softc_t *ijets)
     begin(ijetifinit);
{
  vr_t *vrp;
  struct ifnet *ifp = &ijets->ijet_if;
  int i;

  if ((ijets->flags&IJET_FL_UCODEIS) == 0) {
    warn("ijet%d: ijetifinit(): No ucode downloaded\n", IJETS2UNIT(ijets));
    ret FALSE;
  }
  if ((ijets->flags&IJET_FL_CFGINIT) == 0) {
    warn("ijet%d: ijetifinit(): No setup paramaters configured!\n",
	 IJETS2UNIT(ijets));
    ret FALSE;
  }

  /*
   * We need to know the maximum number of VRs that we'll use...
   * plus a few extra for slop.
   * ASK:  how important are the extra VRs??
   */
  ijets->total_vrs = ijets->xmt_VRs;
  for (i=0; i<IJET_NUM_FREELISTS; i++)
    ijets->total_vrs += ijets->rcv_cnt[i];
  ijets->total_vrs += IJET_EXTRA_VRS;

#if (defined(IJET_CREDIT) || defined(IJET_DYN_CREDIT))
  ijets->total_vrs += ijets->credit_VRs;
#endif  
  {
      ll_error_t error;
      /* kosak HACK the +1024 is for my harp stuff */
      if ((error = ll_Init(ijets, ijets->rcv_VCids, ijets->xmt_VCids,
                           ijets->total_vrs + 2048,
                           IJET_STATIC_TABLE_SIZE, IJET_DYNO_LISTS,
                           IJET_DYNO_ENTRIES, IJET_RXVC_EXTRACT,
                           ijets->ucode, ijets->ucodesize)) < 0) {
          ijetreset(ijets);
          warn ("Init error!  code is %d\n", error);
          panic ("init error");  /* kosak HACK */
          ret FALSE;
      }
  }
  
  if (!ijetxmtinit(ijets) || !ijetrcvinit(ijets)
#if (defined(IJET_CREDIT) || defined(IJET_DYN_CREDIT))
      || !ijetcreditinit(ijets)
#endif
      ) {
    ijetreset(ijets);
    ret FALSE;
  }
  
  /* create something to handle our mailboxes */
  vrp = ijets->rcv_mbox_vrp;
  bzerovrp(vrp);
  JET_FLUSH();
  ijets->rcv_mbox_free = TRUE;
  ijets->rcv_mbox = ll_AllocMailbox(ijets, NULL,
				    (ll_handler_t) ijetrcvwalker, vrp);

  vrp = ijets->xmt_mbox_vrp;
  bzerovrp(vrp);
  JET_FLUSH();
  ijets->xmt_mbox_free = TRUE;
  ijets->xmt_mbox = ll_AllocMailbox(ijets, (ll_handler_t) ijetxmtwalker,
				    (ll_handler_t) ijetxmtwalker, vrp);

#if (defined(IJET_CREDIT) || defined(IJET_DYN_CREDIT))
  vrp = ijets->credit_mbox_vrp;
  bzerovrp(vrp);
  JET_FLUSH();
  ijets->credit_mbox_free = TRUE;
  ijets->credit_mbox = ll_AllocMailbox(ijets, NULL, NULL, vrp);
#endif
  
  ifp->if_flags |= IFF_RUNNING;	        /* CHECK:: correct place for this? */
  ret TRUE;
}
end(ijetifinit);

#ifdef JET960_CREDIT
public void ijet960Init(vm_offset_t creditTable, vm_offset_t requestTable,
                        vm_offset_t flag, vm_offset_t turn)
{
  ijet_960CreditTable = (uint8 *) creditTable;
  ijet_960RequestTable = (uint8 *) requestTable;
  ijetFlag = (bool *) flag;
  ijet960Turn = (turn_t *) turn;
  i960up = TRUE;
  debugp("Credit table at 0x%x, request table at 0x%x\n",
         (uint32) creditTable, (uint32) requestTable);
  debugp("Flag at 0x%x, turn at 0x%x\n", (uint32) flag, (uint32) turn);
}
#endif


/*
 *  ijetifioctl ()
 *
 *  Starts and stops the interface.
 *
 *  Returns:  0 on success, error otherwise.
 */
int ijetifioctl(struct ifnet *ifp, u_long command, caddr_t data)
     begin(ijetifioctl);
{
  ijet_softc_t *ijets = ijetcd.cd_devs[ifp->if_unit];
  struct ifaddr *ifa = (struct ifaddr *)data;
  int s, error=0;
  
  s = splimp();
  
  switch (command) {
  case SIOCSIFADDR:
    ifp->if_flags |= IFF_UP;
    switch (ifa->ifa_addr->sa_family) {
    case AF_INET:
      debug("calling ijetifinit() from AF_INET\n");
      ijetifinit(ijets);
      break;
    }
    break;
  case SIOCSIFFLAGS:
    if ((ifp->if_flags & IFF_UP) == 0 &&
	(ifp->if_flags & IFF_RUNNING) != 0) {
      /*
       * If interface is marked down and it is running, then
       * stop it.
       */
      ijetifstop(ijets);
      ifp->if_flags &= ~IFF_RUNNING;
    } else if ((ifp->if_flags & IFF_UP) != 0 &&
	       (ifp->if_flags & IFF_RUNNING) == 0) {
      /*
       * If interface is marked up and it is stopped, then
       * start it.
       */
      debug("calling ijetifinit() because marked up and is stopped\n");
      ijetifinit(ijets);
    } else {
      /*
       * Reset the interface to pick up changes in any other
       * flags that affect hardware registers.  None that I
       * am aware of at this time.
       */
      warn("ifp->if_flags = 0x%x\n", ifp->if_flags);
      /*      ijetifstop(ijets);
	      ijetifinit(ijets); */
    }
    break;

    /*
     * Multicast ioctls.  Does nothing for now.
     */
  case SIOCADDMULTI:
  case SIOCDELMULTI:
    break;

  default: {
    warn ("ioctl 0x%x not implemented\n", command);
    error = EINVAL;
  } break;

  }

  (void) splx(s);
  ret error;
}
end(ijetifioctl);

int ijetifwatchdog(short unit)
     begin(ijetifwatchdog);
{
  ret 0;
}
end(ijetifwatchdog);

/*
 *  ijetifoutput ()
 *
 *  Checks net, mem, and VCs before transmission.  If everything ok then calls routine to begin process .
 *
 *  Returns:  0 if successful or an error otherwise.
 */
int  ijetifoutput(struct ifnet *ifp, struct mbuf *m,
		  struct sockaddr *dst, struct rtentry *rt)
     begin(ijetifoutput);
{
  ijet_softc_t *ijets = ijetcd.cd_devs[ifp->if_unit];
  int s, error=0;
  uint32 ip_dst, vcid=0; /* kosak HACK avoid compiler warning */
  ijet_art_ent_t *harte;
  uint16 type;
  struct llc *llc;
  int loop;
  
  if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
    error = ENETDOWN;
    goto bad;
  }

#ifdef IJET_LOOPBACK
  if (loopback)
  {
    struct ifqueue *inq;

    s = splimp();
    schednetisr(NETISR_IP);
    inq = &ipintrq;

    if (IF_QFULL(inq)) {
      warn("IF_QFULL!!\n");
      IF_DROP(inq);		/* CHECK:: what does IF_DROP() do??? */
      m_freem(m);
      ijets->ijet_if.if_ierrors++;
    } else {
      IF_ENQUEUE(inq, m);
      ijets->ijet_if.if_ipackets++;
    }

    splx(s);
    ret error;
  }
#endif

  /*
   * if we don't have a link on which to place this packet, we
   * have to drop it.
   */
  if (ijets->xmt_mbuf_pool == NULL) {
    error = ENOBUFS;
    goto bad;
  }

  switch (dst->sa_family) {
  case AF_INET:
    /*
     * Translate the internet address to a VCid
     */
    ip_dst = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
    harte = ijets->art[(ip_dst >> 24) & (IJET_ART_HASH - 1)];
    while(harte != NULL){
      if(harte->ipaddr == ip_dst){
	/*
	 * Found entry in table for this destination.
	 */
	vcid = harte->tx_VCid;
        harte->idle = 0;
	break;
      }
      harte = harte->next;
    }
    if(harte == NULL){
      /*
       * No entry in table for this destination.
       */
      if (ijets->vince_flag) {
         int found = 0;
         char ip[60];

         for (loop=0; loop<MAX_ARPS; loop++)
           if (ijets->pending_arps[loop] == ip_dst) found = 1;
         if (found || (ijets->next_arp == MAX_ARPS - 1)) goto bad;
         ijets->next_arp++;
         ijets->pending_arps[ijets->next_arp] = ip_dst;
         debug("ijetifoutput: asking vince to establish SVC for %x\n",
                        ip_dst);
         sprintf (ip, "ijetGetIpVc ip:%d.%d.%d.%d\n",
                ip_dst & 255, (ip_dst >> 8) & 255,
                (ip_dst >> 16) & 255, ip_dst >> 24);
          warn ("Sending command %s\n", ip);
          skip_jetmsg_rx_interface (ip, strlen(ip));
         /*
          * In the meantime, we have queue the packet somewhere.  For now
          * I will just drop it.
          */
         goto bad;
      }

      else {
        debug("ijetifoutput: no entry in our table for %x\n", ip_dst);
        error = EHOSTUNREACH;
        goto bad;
      }
    }
    type = ETHERTYPE_IP;
    break;
  default:
    /*
     * Nothing else is currently supported.
     */
    warn("ijet%d:: ijetifoutput: can't support af%d\n", ifp->if_unit,
	   dst->sa_family);
    error = EAFNOSUPPORT;
    goto bad;
  };

#ifdef IJET_PERVC_LIMITS
  {
    ijet_xmt_vc_t *xmtvcp = &ijets->xmtvc[vcid];
    if (xmtvcp->pkt_count >= xmtvcp->pkt_threshold) {
      debugt("over threshold -- dropping packet for vcid %d\n", vcid);
      error = ENOBUFS;
      goto bad;
    }

    /*
     * there are probably some good arguments for doing this later...
     */
    xmtvcp->pkt_count++;
  }
#endif
  
  /*
   * place snap
   */
  M_PREPEND(m, sizeof(struct llc), M_DONTWAIT);
  if (m == NULL) {
      error = ENOBUFS;
      goto bad;
  };
  
  llc = (struct llc *) (mtod(m, caddr_t));
  llc->llc_dsap = LLC_SNAP_LSAP;
  llc->llc_ssap = LLC_SNAP_LSAP;
  llc->llc_un.type_snap.control = LLC_UI;
  llc->llc_un.type_snap.org_code[0] = 0;
  llc->llc_un.type_snap.org_code[1] = 0;
  llc->llc_un.type_snap.org_code[2] = 0;
  llc->llc_un.type_snap.ether_type = htons((u_short)type);
  
#ifdef IJET_N48HACK
  {
    struct mbuf *mtmp = m;
    int len = 0;
    while (mtmp) {
      len += mtmp->m_len;
      mtmp = mtmp->m_next;
    }

    if ((len%48) == 0) {
      M_PREPEND(m, sizeof(uint32), M_DONTWAIT);
      if (m == NULL) {
	error = ENOBUFS;
	goto bad;
      };
      * (uint32 *) (mtod(m, caddr_t)) = 0xdeadbeef;
    }
  }
#endif

  /*
   * Queue message on interface, and start output if interface
   * not yet active.
   */

  s = splimp();
  ijetxmt(ijets, m, vcid);
  splx(s);
  
  ret error;
    
 bad:
  s = splimp();
  if (m) {
    m_freem(m);
    /*
     * Be agressive w/ receive buffers.
     */

#ifdef IJET_LOSES_INTERRUPTS
    ijetrcvwalker(ijets);		/* HACK:: */
#if (defined(IJET_CREDIT) || defined(IJET_DYN_CREDIT))
    ijetupdatecredit(ijets);	        /* HACK:: */
#endif      
    ijetxmtwalker(ijets);		/* HACK:: */
#endif
    VrAttachRoutine(ijets);
  }
  
  ijets->ijet_if.if_oerrors++;

  splx(s);
  ret error;
}
end(ijetifoutput);


/* 
  * ijetrcvinit ()
  *
  * reserve physical memory for VRs (build freelists for header and data and initialize) 
  * and allocate some VRs for receiving
  */
bool ijetrcvinit(ijet_softc_t *ijets)
     begin(ijetrcvinit);
{
  vr_t **vr_table;
  int i, j, num_vcids;
  ijet_rcv_vc_t *rcv_vcs;
  vm_offset_t *vabase;
  
  /*
   * we need to allocate two freelist vr pools....the mailbox will
   * be created by the init routine.  These pages need to be wired
   * down.  The way this gets done is highly OS dependent.
   * kmem_alloc() locks the pages for us.
   */

  /* Create a table for physical to virtual mappings */
  if ((vabase=(vm_offset_t *) malloc(ijets->total_vrs*
				     sizeof(vm_offset_t),
				     M_DEVBUF, M_NOWAIT)) == NULL) {
    warn("ijetrcvinit(): malloc failed for phys-virt mappings\n");
    ret FALSE;
  }
  ijets->ptov = vabase;

  /*
   * Make them NULL to begin with
   */
  bzero(vabase, ijets->total_vrs*sizeof(vm_offset_t));

  /* set up the free lists */
  for (i=0; i<IJET_NUM_FREELISTS; i++) {
    int num_vrs = ijets->rcv_cnt[i];
    
    if ((ijets->rcv_flist[i]=
	 ll_AllocFreelist(ijets, NULL,
			  (ll_emptyListHandler_t) ijetflist)) < 0) {
      warn("ijetrcvinit(): failed to allocate freelist!\n");
      ret FALSE;
    }
    
    /* Allocate a temporary table to store the VR pointers. */
    if ((vr_table= (vr_t **) malloc(num_vrs*sizeof(vr_t *),
				    M_TEMP, M_NOWAIT)) == NULL) {
      warn("ijetrcvinit(): malloc failed for temporary vr table!\n");
      ret FALSE;
    }
    
    /* Allocate the VRs */
    if (ll_AllocateVrs(ijets, num_vrs, vr_table) != num_vrs) {
      warn("ijetrcvinit(): couldn't allocate %d VRs!\n", num_vrs);
      free(vr_table, M_TEMP);
      ret FALSE;
    }

    debugw("rcv is using vrs 0x%x -- 0x%x\n",
	   jet_Vrp2VrIndex(ijets,vr_table[0]),
	   jet_Vrp2VrIndex(ijets,vr_table[num_vrs-1]));
    if (ijet_testing_minvr > jet_Vrp2VrIndex(ijets,vr_table[0]))
      ijet_testing_minvr = jet_Vrp2VrIndex(ijets,vr_table[0]);
    if (ijet_testing_maxvr < jet_Vrp2VrIndex(ijets,vr_table[num_vrs-1]))
      ijet_testing_maxvr = jet_Vrp2VrIndex(ijets,vr_table[num_vrs-1]);
    
    /* Put them on the freelist */
    for (j=0; j<num_vrs; j++)
      if (i==0 && j== 0) {
	/*
	 * Before we attach to the freelist we're going to "borrow"
	 * one of the VRs to use for our initial mbox entry
	 */
	ijets->rcv_mbox_vrp = vr_table[0];
      }
      else
	VrFreeRoutine(ijets, IJET_SRAM2MIRROR(vr_table[j]), i);
    
    free(vr_table, M_TEMP);   /* clean up */
  }
  
  /*
   * That should take care of the freelist initialization.
   * now we need to allocate some structures for rcv pdu
   * reassembly, since we will end up taking things out of
   * the mbox prior to pdu completion for some VCs.
   */
  
  num_vcids = ijets->rcv_VCids;
  if ((rcv_vcs=(ijet_rcv_vc_t *) malloc(sizeof(ijet_rcv_vc_t)*num_vcids,
					    M_DEVBUF, M_NOWAIT)) == NULL) {
    warn("ijetrcvinit(): malloc failed for vcidlist!\n");
    ret FALSE;
  }
  
  /*
   * Make all the vr chains empty.
   */
  ijets->rcvvc = rcv_vcs;
  for (i=0; i<num_vcids; i++) {
    rcv_vcs[i].vr_chain_head = rcv_vcs[i].vr_chain_tail = NULL;
    rcv_vcs[i].active = FALSE;
  }

  /*
   * OK...allocate whatever VRs we can
   */
  VrAttachRoutine(ijets);
  
  ret TRUE;
}
end(ijetrcvinit);

/*
 * ijetrcv()
 *
 * this routine gets called as a result of the interrupt handler
 * handing us a notification of pkt arrival.  Basically just transfers
 * a packet up to the "if" layer.
 *
 * We need to store our VRs on a per VC basis until we get end
 * of packet.
 */
void ijetrcv(ijet_softc_t *ijets, vr_t *mvrp, bool mboxtail)
     begin(ijetrcv);
{
  ijet_rcv_vc_t *rcv_vc;
  vr_t *headp, *nextp, *pktendvrp, *nextpktvrp = NULL;
  vcid_t vcid;
  int totlen, pdulen, nextoffset=0, size;
  vm_offset_t base;
  bool pdu_end; 
  struct ifqueue *inq;
  struct mbuf *m, *top, **mp;
  bool merged = FALSE;
  int n48hack = 0;
  uint16 snapEtherType;

  /*
   * Grab the fields out of the vrp.  if accesses into sram
   * are slow we should grab 32 bit chunks at a time;  assuming
   * 32 bit bus accesses.
   */
  vcid = mvrp->message.vcid;
  rcv_vc = &ijets->rcvvc[vcid];
  pdu_end = mvrp->message.endOfPdu;
  
  /*
   * We may be receiving from multiple VC's at the
   * same time, so we need to have a way to link VRs on a
   * per VC basis until the packet is complete.
   *
   * For now, the VR chains sit in an array indexed by VCid.
   */
  if (rcv_vc->vr_chain_head == NULL) {
    rcv_vc->vr_chain_head = rcv_vc->vr_chain_tail = mvrp;
  }
  else {
    rcv_vc->vr_chain_tail->message.nextIndex = jet_MVrp2VrIndex(ijets, mvrp);
    rcv_vc->vr_chain_tail = mvrp;
  }
  
  if (!pdu_end)
    ret;

  while (1) {
    /*
     * We need to find pkt end since it may not be the vrp we just
     * received.
     */
    pktendvrp = rcv_vc->vr_chain_head;
    size = MCLBYTES-pktendvrp->message.remain;
    while (!(pktendvrp->message.endOfPdu)) {
      pktendvrp = jet_VrIndex2MVrp(ijets, pktendvrp->message.nextIndex);
      size += MCLBYTES-pktendvrp->message.remain;
    }

#ifdef IJET_CELL_COUNTS
    /* FIX::
     *   no magic numbers
     */
    rcv_vc->active = TRUE;
    if ((size % 48) == 0) rcv_vc->cellcount += (size/48); /* HACK! HACK! */
    else rcv_vc->cellcount += ((size+8)+47)/48;
#endif
    
#ifndef IJET_INTELMERGESPACKETS

    /*
     * Check for a CRC error, dropping the packet if found...
     * no reason to do anything else.
     */
    if (pktendvrp->message.crcError) {
      ijets->ijet_if.if_ierrors++;
      base = ijets->ptov[jet_MVrp2VrIndex0(ijets, rcv_vc->vr_chain_head)];
      if (*(uint32 *)base == 0xdeadbeef) base += sizeof(uint32);
      pdulen = ntohs(* (unsigned short *) (base+sizeof(short) + 
                                           sizeof(struct llc)));
      pdulen += sizeof(struct llc);
      warn("crcError!! on vcid %d, exp %d cells, rx %d cells\n",
            vcid, (pdulen + 55)/48, (size % 48) ? (size + 55)/48 : (size/48));
      goto rcv_drop;
    }
#endif IJET_INTELMERGESPACKETS
    
    headp = rcv_vc->vr_chain_head;
    
    /* FIX:: if the adaptor worked this would be headp->message.BLI */
    base = ijets->ptov[jet_MVrp2VrIndex0(ijets, headp)];
    
    /*
     * If this is not the first time through, we may have an offset
     * to contend with.
     */
    if (merged) {
      base     += nextoffset;
      size     -= nextoffset;
    }
    else {
      nextoffset = 0;
      nextpktvrp = NULL;
    }
    
#ifdef IJET_N48HACK
    if (*(uint32 *) base == 0xdeadbeef) {
      base += sizeof(uint32);
      n48hack = sizeof(uint32);
    }
    else
      n48hack = 0;
#endif

    /*
     * NOTE:: FIX:: we should check that we have a valid
     * snap header, especially since we can't trust the
     * pdulen in merged packets.
     */
    /*JETMSG*/
    debug ("Checking LLC header\n");
    {
      struct llc *llc = (struct llc *) base;
      {
        debug ("dsap 0x%x ssap 0x%x control 0x%x orgcode 0x%x 0x%x 0x%x "
               "ether_type 0x%x\n",
               llc->llc_dsap, llc->llc_ssap, llc->llc_un.type_snap.control,
               llc->llc_un.type_snap.org_code[0],
               llc->llc_un.type_snap.org_code[1],
               llc->llc_un.type_snap.org_code[2],
               llc->llc_un.type_snap.ether_type);
      }
          
      if ((llc->llc_dsap == LLC_SNAP_LSAP) &&
          (llc->llc_ssap == LLC_SNAP_LSAP) &&
          (llc->llc_un.type_snap.control == LLC_UI) &&
          (llc->llc_un.type_snap.org_code[0] == 0) &&
          (llc->llc_un.type_snap.org_code[1] == 0) &&
          (llc->llc_un.type_snap.org_code[2] == 0)) {
        snapEtherType = ntohs(llc->llc_un.type_snap.ether_type);
        debug ("valid LLC header, type=%d\n", snapEtherType);
      } else {
        debug ("BAD LLC header\n");
        goto rcv_drop; /* is this right? */
      }
    }
      
    base += sizeof(struct llc);

#ifdef IJET_INTELMERGESPACKETS
    if (pktendvrp->message.crcError) {
      int traileroffset;
      /*
       * We're going to try and retrieve the first packet anyway.
       */

      debug("ijetrcv(): received pkt w/ crcError, trying anyway\n");
      pdulen = ntohs(* (unsigned short *) (base+sizeof(short)));

      totlen = pdulen+sizeof(struct llc);
      merged = TRUE;

      if (totlen > size) {
	warn("dropping pkt because totlen %d > rcv'd bytes! %d\n",
	       totlen, size);

	ijets->ijet_if.if_ierrors++;
	merged = FALSE;
	goto rcv_drop;
      }

      /*
       * TRAILEROFFSET is always 40 since the problem occurs w/ pkts
       * of 48*n
       */
      /*      traileroffset = totlen+TRAILER_OFFSET(totlen); */
      traileroffset = totlen+40;

      /*
       * If there was a previous merged packet, we may not be on a
       * VR boundary.
       */
      traileroffset += nextoffset;

      if ((totlen % 48) ||
	  ((nextoffset=findEndingVr(ijets, headp, pktendvrp, 
				    traileroffset, &nextpktvrp,
				    totlen))) < 0) {
	merged = FALSE;
	if (totlen != pktendvrp->message.pduLength) {
	  /*
	   * Didn't find a matching trailer, and the pdulen
	   * is not that of the vr w/ endOfPdu set.
	   */
	  warn("dropping pkt because findEndingVr failed!\n");
          warn("totlen = %d, pktendvrp->pdulen = %d\n",
                 totlen, pktendvrp->message.pduLength);
	  ijets->ijet_if.if_ierrors++;
	  goto rcv_drop;
	}
	else {
          debug("No matching trailer, but pdulen %d = iplen!\n", pdulen);
	  /*
	   * Didn't find the matching trailer, but our length is that
	   * of the VR w/ endOfPdu set.
	   *
	   * Assume that this is the last pkt.  This may not be
	   * true...but there is no valid trailer anyway.
	   *
	   * Treat it as if it was an ok packet.
	   */
	}
      }
      else
        debug("found matching trailer...the world is great!\n");
    }
    else
#endif IJET_INTELMERGESPACKETS
      {
	pdulen = pktendvrp->message.pduLength-sizeof(struct llc)-n48hack;
	merged = FALSE;
      }
    
    MGETHDR(m, M_DONTWAIT, MT_DATA);
    if (m == NULL) {            /* hmmm...no mbuf headers */
      warn ("MGETHDR failed\n");
      ijets->ijet_if.if_ierrors++;
      goto rcv_drop;
    }
    m->m_pkthdr.rcvif = &ijets->ijet_if;
    
    top = NULL;
    mp = &top;
    m->m_pkthdr.len = pdulen;

    /*
     * Note the following loop does *not* use endOfPdu, because we
     * may not have the bit set due to adaptor bug.  We'll trust
     * the length.
     */
    totlen = pdulen;
    while (totlen > 0) {
      if (top) {
	/*
	 * not the first time through the loop...so grab another
	 * mbuf
	 */
	MGET(m, M_DONTWAIT, MT_DATA);
	if (m == NULL) {
	  m_freem(top);
	  warn("MGET FAILED!\n");
          ijets->ijet_if.if_ierrors++;
          goto rcv_drop;
	}
      }

      /*
       * We dma'd directly into an mbuf...so be clever.
       * basically do the same things that MCLGET does except
       * we do not allocate another buffer, just up the
       * reference count.
       *
       * NOTE:: if we were certain we wouldn't get multiple packets
       * in the same VR, we could forget about updating the
       * refcnt and just pass it up the IP stack.  
       */
      m->m_ext.ext_buf = (caddr_t)
	ijets->ptov[jet_MVrp2VrIndex0(ijets, headp)];
      m->m_data = (caddr_t) base;
      m->m_ext.ext_size = MCLBYTES;
      m->m_flags |= M_EXT;
      m->m_len = min(totlen, MCLBYTES-(m->m_data-m->m_ext.ext_buf));
      ++mclrefcnt[mtocl(m->m_ext.ext_buf)];
      
      *mp = m;
      mp = &m->m_next;
      
      totlen -= m->m_len;

      if (totlen)
	if (headp != mvrp) {
	  headp = jet_VrIndex2MVrp(ijets, headp->message.nextIndex);
          base = ijets->ptov[jet_MVrp2VrIndex0(ijets, headp)];
        }
	else {
	  warn("ijetrcv: tried to read beyond last VR!\n");
	  m_freem(top);
          ijets->ijet_if.if_ierrors++;
	  goto rcv_drop;
	}
    }
    
    debug("ijetrcv: pkt of length %d arrived on vcid %d\n",
          top->m_pkthdr.len, vcid);
    debug("ijetrcv: and was passed up the IP stack!\n");
    /*
     * Hand the packet to the network module
     */

    if (snapEtherType == ETHERTYPE_IP) {
      debug ("it's an IP thing, so go the usual way\n");
      schednetisr(NETISR_IP);
      inq = &ipintrq;
      
      if (IF_QFULL(inq)) {
        IF_DROP(inq);		/* CHECK:: what does IF_DROP() do??? */
        m_freem(top);
        warn("IF_QFULL!!\n");
        ijets->ijet_if.if_ierrors++;
      } else {
        IF_ENQUEUE(inq, top);
        ijets->ijet_if.if_ipackets++;
      }
    } else {
      debug ("it's my thing, so go my way.  vpivci=%d?\n", vcid);
      /*HACK: vcid should actually be "vpivci", but for receive they're
        often the same, if the phase of the moon is right.  Fix later */
      ip_jetmsg_rx_interface (ijets, top, vcid, snapEtherType);
    }
    
  rcv_drop:
    /*
     * Now walk through and free the VRs on the chain.  We have
     * to free the cluster mbuf too since we don't yet support
     * external free calls.
     */
    headp = rcv_vc->vr_chain_head;
    while (1) {

      /*
       * Don't free the VR if it's where the next merged pkt starts
       */
      if (merged && (headp == nextpktvrp))
	break;

      /*
       * Don't free it if it is end of PDU
       */
      if (headp->message.endOfPdu)
	break;
      /*
       * We have to stuff them on one at a time, since they may be of
       * differing sizes... we *might* be able to stuff the first
       * one seperately and the rest *should* be the same size if we're
       * doing header/data split.
       */
      nextp = jet_VrIndex2MVrp(ijets, headp->message.nextIndex);
      
      /*
       * Free the VR and associated mbuf
       */
      VrFreeRoutine(ijets, headp, 0);
      headp = nextp;
    }

    /*
     * We're done if we don't have merged packets and this is vr that
     * caused this routine to be called.
     */
    if ((!merged) && (headp == mvrp))
      break;

    rcv_vc->vr_chain_head = headp;
      
    if ((!merged) || (headp != nextpktvrp)) {
      if (!merged)
	warn("hmm...adjusting vr chain and we're not merged!\n");
      /*
       * Hmmm...still more stuff on the chain, prepare to go back through
       * the loop again, handling the next packet
       */
      
      /* adjust the head of chain */
      rcv_vc->vr_chain_head = jet_VrIndex2MVrp(ijets,
					       headp->message.nextIndex);
      VrFreeRoutine(ijets, headp, 0);
    }

    /*
     * If there is another merged packet, we have to be at the correct
     * vr now.  If not, we'll just blow the entire chain away.
     */
    if (merged && (rcv_vc->vr_chain_head != nextpktvrp)) {
      warn("Merged packet, but something doesn't add up!\n");
      merged = FALSE;
      goto rcv_drop;
    }
  }

  /*
   * Here, we're done w/ the entire chain...nothing is left.
   */
  if (mboxtail) {
    /*
     * However, we can't free the last vrp if we still need it in
     * the mailbox.
     */
    ijets->rcv_mbox_free = TRUE;
  }
  else {
    /*
     * OK...we can free it, since there is still more in our
     * mbox to work on.
     */

    /* FIX:: should be flist headp->message.BLI */
    VrFreeRoutine(ijets, headp, 0);
  }
  
  /* in any case, rcv_vc is now empty ....well it better be */
  rcv_vc->vr_chain_head = rcv_vc->vr_chain_tail = NULL;
}
end(ijetrcv);

/*
 * ijetxmtinit()
 *
 * set up the necessary buffers and vr pools for this device
 */
bool ijetxmtinit(ijet_softc_t *ijets)
     begin(ijetxmtinit);
{
  vr_t **vr_table;
  int num_vrs, num_vcids, i, num_mbufs;
  ijet_xmt_vc_t *perVC;
  ijet_mbuf_t *mbuf_pool;
  char *sram;

  /*
   * Allocate some space to mirror SRAM.
   * NOTE::
   *   We should just mirror the VR space.  We could wire
   *   this space down, but then grabbing contiguous space
   *   becomes a problem.  For now, we'll just malloc the space.
   */

  if ((sram=malloc(IJET_SRAM_SIZE, M_DEVBUF, M_NOWAIT)) == NULL) {
    warn("ijetxmtinit(): couldn't malloc sram mirror!\n");
    ret FALSE;
  }
  ijets->sram = (vr_t *) sram;
  ijets->mirror2sramoffset = (vm_offset_t) ijets->sramBase -
	 (vm_offset_t) sram;
  warn("sram mirror from %p - %p\n", (char *) sram,
	 (char *) sram+IJET_SRAM_SIZE);
  warn("sram at %p - %p\n", (char *) ijets->sramBase,
	 (char *) ijets->sramBase + IJET_SRAM_SIZE);
  bzero(sram, IJET_SRAM_SIZE);
  
  /*
   * We need some number of vr pointers, this can either
   * be done statically, or we can allow our user agent to
   * set this for us.  in either case, we'll assume that our 
   * interface structure has this number.
   */

  num_vrs = ijets->xmt_VRs;
  debug("ijetxmtinit(): getting %d xmt vrs", num_vrs);
  vr_table = malloc(sizeof(vr_t *)*num_vrs, M_TEMP, M_NOWAIT);
  if (ll_AllocateVrs(ijets, num_vrs, vr_table) != num_vrs) {
    warn("ijetxmtinit(): couldn't allocate %d VRs!\n", num_vrs);
    free(vr_table, M_TEMP);
    ret FALSE;
  }

  debugw("xmt is using vrs 0x%x -- 0x%x\n",
	 jet_Vrp2VrIndex(ijets,vr_table[0]),
	 jet_Vrp2VrIndex(ijets,vr_table[num_vrs-1]));
  if (ijet_testing_minvr > jet_Vrp2VrIndex(ijets,vr_table[0]))
    ijet_testing_minvr = jet_Vrp2VrIndex(ijets,vr_table[0]);
  if (ijet_testing_maxvr < jet_Vrp2VrIndex(ijets,vr_table[num_vrs-1]))
    ijet_testing_maxvr = jet_Vrp2VrIndex(ijets,vr_table[num_vrs-1]);
  

  /*
   * Link all the VRs together and put them on the freelist.
   */
  for (i=0; i<num_vrs; i++)
    if (i==0 || i==num_vrs-1)
      IJET_SRAM2MIRROR(vr_table[i])->vr.nextIndex = IJET_NO_NEXT_VR;
    else
      IJET_SRAM2MIRROR(vr_table[i])->vr.nextIndex =
	jet_Vrp2VrIndex(ijets, vr_table[i+1]);

  /*
   * steal one for the mailbox
   */
  ijets->xmt_mbox_vrp = vr_table[0];
  ijets->xmt_freeVR_head = IJET_SRAM2MIRROR(vr_table[1]);
  ijets->xmt_freeVR_tail = IJET_SRAM2MIRROR(vr_table[num_vrs-1]);
  ijets->xmt_VR_freecnt = num_vrs-1;
  free(vr_table, M_TEMP);
  
  num_vcids = ijets->xmt_VCids;
  if ((perVC = (ijet_xmt_vc_t *) malloc(num_vcids*sizeof(ijet_xmt_vc_t),
                                        M_DEVBUF, M_DONTWAIT)) == NULL) {
    warn("ijet%d: ijetxmtinit():: Couldn't allocate per VC memory!\n",
	   IJETS2UNIT(ijets));
    ret FALSE;
  }

  /*
   * Make everything NULL to start with.
   */
  bzero(perVC, num_vcids*sizeof(ijet_xmt_vc_t));
  ijets->xmtvc = perVC;

  for (i=0; i<num_vcids; i++)
    ijets->xmtvc[i].credit = 0xff;
  
  /*
   * Allocate a pool of ijet_mbuf_t structs.  There should be at least
   * as many as we will have packets outstanding.  Link them together
   * and save as pool.  Initialize pool_cnt to num created.
   */
  num_mbufs = ijets->xmt_mbufs;
  if ((mbuf_pool = (ijet_mbuf_t *) malloc(num_mbufs*sizeof(ijet_mbuf_t),
					  M_DEVBUF, M_DONTWAIT)) == NULL) {
    warn("ijet%d: ijetxmtinit(): Couldn't allocate xmt mbuf pool!\n",
	   IJETS2UNIT(ijets));
    ret FALSE;
  }

  bzero(mbuf_pool, num_mbufs*sizeof(ijet_mbuf_t));
  /*
   * Link all these together.
   */
  for (i=0; i<num_mbufs; i++)
    if (i == num_mbufs-1)
      mbuf_pool[i].next = NULL;
    else
      mbuf_pool[i].next = &mbuf_pool[i+1];

  ijets->xmt_mbuf_pool = mbuf_pool;
  ijets->xmt_mbuf_base = mbuf_pool;
 
  /* kosak moved the vpivci2vcid stuff here because he wants it! */
         
  /*
   * Create a table for vpivci to vcid mappings.
   */
         
  {
    uint32 *region;
  
    if ((region=malloc(ijets->maxvpivci*sizeof(uint32), 
                       M_DEVBUF, M_NOWAIT)) == NULL) {
      warn("ijetcreditinit(): malloc failed for vcid mappings!\n");
      ret FALSE;
    }  
    ijets->vpivci2vcid = region;
    bzero(region, ijets->maxvpivci*sizeof(uint32));
  }
  
  ret TRUE;
}
end(ijetxmtinit);

#if (defined(IJET_CREDIT) || defined(IJET_DYN_CREDIT))
/*
  *  ijetcreditinit ()
  *
  *  Reserve physical mem for freelist for credit handling VRs.
  *
  *  Returns:  Success (TRUE = 1) / Failure (False = 0)
  */
bool ijetcreditinit(ijet_softc_t *ijets)
     begin(ijetcreditinit);
{
  int num_vrs, i, size, vrsperpage, numpages, index0;
  vr_t **vr_table;
  vm_offset_t base;

  /*
   * Create a freelist for credit VRs.
   */
  if ((ijets->credit_flist=ll_AllocFreelist(ijets,
					    (ll_handler_t)
					    ijetcreditmboxhandler,
					    (ll_emptyListHandler_t)
					    ijetcreditflist)) < 0) {
    warn("ijetcreditinit(): failed to allocate freelist!\n");
    ret FALSE;
  }

  num_vrs = ijets->credit_VRs;;
  /* Allocate a temporary table to store the VR pointers. */
  if ((vr_table= (vr_t **) malloc(num_vrs*sizeof(vr_t *),
				  M_TEMP, M_NOWAIT)) == NULL) {
    warn("ijetcreditinit(): malloc failed for temporary vr table!\n");
    ret FALSE;
  }
    
  /* Allocate the VRs */
  if (ll_AllocateVrs(ijets, num_vrs, vr_table) != num_vrs) {
    warn("ijetcreditinit(): couldn't allocate %d VRs!\n", num_vrs);
    free(vr_table, M_TEMP);
    ret FALSE;
  }

  debugw("credit is using vrs 0x%x -- 0x%x\n",
	 jet_Vrp2VrIndex(ijets,vr_table[0]),
	 jet_Vrp2VrIndex(ijets,vr_table[num_vrs-1]));
  if (ijet_testing_minvr > jet_Vrp2VrIndex(ijets,vr_table[0]))
    ijet_testing_minvr = jet_Vrp2VrIndex(ijets,vr_table[0]);
  if (ijet_testing_maxvr < jet_Vrp2VrIndex(ijets,vr_table[num_vrs-1]))
    ijet_testing_maxvr = jet_Vrp2VrIndex(ijets,vr_table[num_vrs-1]);

  /*
   * HACK:: FIX::
   * this should be user settable
   */
  ijets->creditinterval = num_vrs>>2;	/* must be at least 2 */
  ijets->creditmarker = 0;
  
  /*
   * Allocate some space for them to point to.  Use kmem_alloc so it is
   * wired down.
   */
  vrsperpage = NBPG/IJET_CREDIT_VR_SIZE;
  numpages = (num_vrs+vrsperpage-1)/vrsperpage;
  size = numpages*NBPG;

  if ((base=kmem_alloc(kernel_map, size)) <= 0) {
    warn("ijetcreditinit(): couldn't allocate kernel memory for VRs!\n");
    free(vr_table, M_TEMP);
    ret FALSE;
  }
  ijets->credit_base = base;
  ijets->credit_base_size = size;

  for (i=0; i<num_vrs; i++) {
    bzerovrp(vr_table[i]);
    if (i==0 || i==num_vrs-1)
      vr_table[i]->vr.nextIndex = IJET_NO_NEXT_VR;
    else
      vr_table[i]->vr.nextIndex = jet_Vrp2VrIndex(ijets, vr_table[i+1]);

    /* note where the first credit cell will be written */
    if (i==1)
      ijets->currentcreditva = (ijet_credit_cell_t *) base;
    
    index0 = jet_Vrp2VrIndex0(ijets, vr_table[i]);
    ijets->ptov[index0] = base;

    /* fill the credit value w/ the marker value */
    ((ijet_credit_cell_t *) base)->unused0 = 0xBABE;
    
    vr_table[i]->vr.address = vtophys(base);
    vr_table[i]->vr.remain  = IJET_CREDIT_VR_SIZE;
    vr_table[i]->vr.length  = IJET_CREDIT_VR_SIZE;
    vr_table[i]->vr.sarLocation = SAR_HOST;

    base += IJET_CREDIT_VR_SIZE;
    if ((base&(NBPG-1))+IJET_CREDIT_VR_SIZE > NBPG)
      base = (base+IJET_CREDIT_VR_SIZE)&~(NBPG-1);

    if (i==0)			/* We saved one for the mbox.  */
      ijets->credit_mbox_vrp = vr_table[0];
    else			/* attach the rest to the freelist */
      ijetaddcreditvr(ijets, vr_table[i]);

    JET_FLUSH();
  }
  ijets->lastcreditva = (ijet_credit_cell_t *) base;

  free(vr_table, M_TEMP);

  ret TRUE;
}
end(ijetcreditinit);
#endif

/*
 * Credit change:  july 3rd
 *
 * Just set up the VR chain here, we'll actually check for
 * credit prior to xmittal.
 */
void ijetxmtbuildvrs(ijet_softc_t *ijets, vcid_t vcid)
     begin(ijetxmtbuildvrs);
{
  ijet_xmt_vc_t *xmtvcp = &ijets->xmtvc[vcid];
  int len, pdulen;
  vr_t *mvrp=NULL, *vrp, *vr_head, *vr_tail;
  struct mbuf *m;
  
  while (xmtvcp->pre_head) {

    /*
     * Make sure we can schedule the entire packet now.
     *
     * NOTE::
     * We're going w/ the dumb, straightforward approach to credit.  We
     * only schedule packets while we have enough credit to send the
     * entire thing.  For IP, this is probably ok.
     *
     * The correct, slower approach is to keep sending while we have
     * credit; making sure that we always end on packet boundaries or
     * send complete cells.
     */
    m = xmtvcp->pre_head->m;
    
    if (xmtvcp->pre_head->count > ijets->xmt_VR_freecnt)
      break;

    vr_head = vr_tail = NULL;
    xmtvcp->pre_head->head = xmtvcp->pre_head->tail = NULL;
    pdulen = 0;

    /*
     * walk the entire chain and schedule it
     */
    while (m) {
      while (m && m->m_len <= 0)
        m = m->m_next;

      if (m == NULL) {
        if (mvrp == NULL) {
	  /*
	   * Don't really need to panic(), but I'd like to know if this
	   * case ever happens.
	   */
          warn("zero or negative length mbuf!!!\n");
          panic("<= 0 length mbuf");
        }
        break;
      }
        
      len = m->m_len;

      mvrp = ijets->xmt_freeVR_head;
      if (--ijets->xmt_VR_freecnt == 0)
        ijets->xmt_freeVR_head = ijets->xmt_freeVR_tail = NULL;
      else
        ijets->xmt_freeVR_head = jet_VrIndex2MVrp(ijets, mvrp->vr.nextIndex);

      bzerovrp(mvrp);
      mvrp->vr.address = vtophys(mtod(m, caddr_t));
      mvrp->vr.length = vcid;
      mvrp->vr.remain = len;
      mvrp->vr.sarLocation = SAR_HOST;
      pdulen += len;

      /*
       * Place this onto our VR chain (if any).
       */
      if (vr_head)
        vr_tail->vr.nextIndex = jet_MVrp2VrIndex(ijets, mvrp);
      else
        vr_head = mvrp;
      vr_tail = mvrp;

      m = m->m_next;
    }

    /* Mark endOfPdu.*/
    mvrp->vr.endOfPdu = 1;
    mvrp->vr.pduLength = pdulen;
    mvrp = NULL;

    /*
     * How much credit do we need?  At least 1 for trailer, may be safer
     * to add 2 due to padding.
     */

    /* ASK:  what is the rationale for the following: */
    xmtvcp->pre_head->count = (pdulen + 55)/ATM_DATA_SIZE;
    /* xmtvcp->pre_head->count = pdulen/ATM_DATA_SIZE+1; */

    /* Copy to SRAM, save the head and tail for scheduling */
    xmtvcp->pre_head->head = vrp = IJET_MIRROR2SRAM(vr_head);
    bcopyvrp(vr_head, vrp);
    JET_FLUSH();
    while (vr_head->vr.nextIndex) {
      vr_head = jet_VrIndex2MVrp(ijets, vr_head->vr.nextIndex);
      vrp = IJET_MIRROR2SRAM(vr_head);
      bcopyvrp(vr_head, vrp);
      JET_FLUSH();
    }
    xmtvcp->pre_head->tail = vrp;

    /* We can move this whole list to the done queue. */
    if (xmtvcp->asm_head)
      xmtvcp->asm_tail->next = xmtvcp->pre_head;
    else
      xmtvcp->asm_head = xmtvcp->pre_head;
    xmtvcp->asm_tail = xmtvcp->pre_head;
    xmtvcp->pre_head = xmtvcp->pre_head->next;
    if (xmtvcp->pre_head == NULL)
      xmtvcp->pre_tail = NULL;
    xmtvcp->asm_tail->next = NULL;
    
    ijets->xmt_asm_needed--;
  }

  if (xmtvcp->asm_head) {
#if (defined(IJET_CREDIT) || defined(IJET_DYN_CREDIT))
    /* HACK:: check for credit */
    ijetupdatecredit(ijets);
#endif
    ijetxmtdoxmt(ijets, vcid);
  }
}
end(ijetxmtbuildvrs);


/*
 * ijetxmt()
 *
 * This does the real work in queueing up a pkt for transmission
 * on the adaptor.  This routine gets called from ijetifoutstart().
 *
 */
void ijetxmt(ijet_softc_t *ijets, struct mbuf *m, vcid_t VCid)
     begin(ijetxmt);
{
  ijet_mbuf_t *ijetmbufp, *tail;
  ijet_xmt_vc_t *xmtvcp;
  struct mbuf *mtmp;
  int cnt;

  /*
   * At this point we know we have enough resources, else the mbuf
   * wouldn't have been dequeued and we would have never been
   * called.  Therefore, just pass this down to the lower
   * level where it will be xmitted on one of the VCs
   */

  xmtvcp = &ijets->xmtvc[VCid];

  /*
   * FIX::
   * basic process is to:
   *  tack mbuf chain onto the per VC ijet_mbuf_t chain.
   *  call our credit_xmt routine
   */

  /*
   * Grab an mbuf chain link from the free list.
   */
  ijetmbufp = ijets->xmt_mbuf_pool;
  ijets->xmt_mbuf_pool = ijets->xmt_mbuf_pool->next;
  
  ijetmbufp->m = m;
  for (mtmp=m, cnt=0; mtmp; mtmp=mtmp->m_next, cnt++);
  ijetmbufp->count = cnt;

  ijetmbufp->next = NULL;
  
  tail = xmtvcp->pre_tail;

  if (tail == NULL)
    xmtvcp->pre_head = ijetmbufp;
  else
    tail->next = ijetmbufp;
    
  xmtvcp->pre_tail = ijetmbufp;

  ijets->xmt_asm_needed++;
  ijetxmtbuildvrs(ijets, VCid);
}
end(ijetxmt);

/*
 * ijetxmtdone()
 *
 * We shouldn't try to free anything until we get end of pdu.
 * Also, this means the adaptor must GUARANTEE that things are transmitted
 * in order for a particular VCid.
 */
void ijetxmtdone(ijet_softc_t *ijets, vr_t *mvrp, bool mboxtail)
     begin(ijetxmtdone);
{
  vcid_t vcid = mvrp->message.vcid;
  bool pdu_end = mvrp->message.endOfPdu;
  ijet_xmt_vc_t *xmtvcp;

  /*
   * End of PDU is our signal to free the mbuf chain.  Otherwise,
   * we just free the xmt VRs
   */

  if (mboxtail)
    ijets->xmt_mbox_free = TRUE;
  else
    ijetaddxmtvr(ijets, mvrp);

  if (!pdu_end)
    ret;

  /** test
  warn("Received xmt done!!\n"); **/
  /*
   * Update our pkt stats.
   */
  ijets->ijet_if.if_opackets++;

  /*
   * We could sanity check the VCid for boundedness, but we don't.
   * If we get a bogus VCid, we're hosed anyway...but the statement
   * below could get a VM pagefault trap if we're really unlucky.
   */
  xmtvcp = &ijets->xmtvc[vcid];

#ifdef IJET_PERVC_LIMITS
  if (--xmtvcp->pkt_count < 0)	/* check the limits just in case */
    xmtvcp->pkt_count = 0;
#endif
  
  if (xmtvcp->done_head == NULL) {
    warn("ijet%d: ijetxmtdone(): Received endOfPdu on VC %d w/o xmt!\n",
	   IJETS2UNIT(ijets), vcid);
    ret;
  }

  m_freem(xmtvcp->done_head->m);

  {
    ijet_mbuf_t *tmp = xmtvcp->done_head->next;
    xmtvcp->done_head->next = ijets->xmt_mbuf_pool;
    ijets->xmt_mbuf_pool = xmtvcp->done_head;
    xmtvcp->done_head = tmp;
  }

  if (xmtvcp->done_head == NULL)
    xmtvcp->done_tail = NULL;

  /* NOTE:: maybe a better place for this?? */
  if (xmtvcp->asm_head) {
#if (defined(IJET_CREDIT) || defined(IJET_DYN_CREDIT))
    ijetupdatecredit(ijets);
#endif
    ijetxmtdoxmt(ijets, vcid);
  }
  
  /* HACK::
   * See if we can place any more rcv VRs out there.
   */
  VrAttachRoutine(ijets);
  
  ret;
}
end(ijetxmtdone);

/*
 * standard device interface routines
 */

/*
 * ijetopen()
 *
 * open our device.  make sure the device exists.  do we want to
 * allow exclusive opens?
 *
 * return: 0 on success
 */
int ijetopen(dev_t dev, int flag)
     begin(ijetopen);
{
  int unit = DEV2UNIT(dev);
  ijet_softc_t *ijets;

  debug("ijetopen: called!\n");
  if (unit<0 || unit>=ijetcd.cd_ndevs) {
    warn("ijet%d: ijetopen: invalid unit number for device!\n", unit);
    ret ENXIO;
  }

  ijets = ijetcd.cd_devs[unit];

  /* we don't use this next value at the present time */
  ijets->opencnt++;
  ret 0;
}
end(ijetopen);

/*
 * ijetclose()
 *
 * just here for completeness right now
 *
 * returns: 0, no matter what
 */
int ijetclose(dev_t dev, int flag, int mode, struct proc *p)
     begin(ijetclose);
{
  ijet_softc_t *ijets = ijetcd.cd_devs[DEV2UNIT(dev)];
  int loop;

  ijets->opencnt--;
  if (p == ijets->vince_proc) {
    ijets->vince_flag = FALSE; 
    ijets->vince_proc = 0;
    ijets->next_arp = -1;
    for (loop=0; loop<MAX_ARPS; loop++) ijets->pending_arps[loop] = 0;
    ijetVinceFailed(ijets);  /* This is a normal vince exit not failure */
  } 
  ret 0;
}
end(ijetclose);


/*
 * ijetioctl()
 *
 * handle ioctl requests from the user via an open file descriptor
 *
 * here we can return status blocks, load ucode, etc
 */
int ijetioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
begin(ijetioctl); 
{
  int unit = DEV2UNIT(dev);
  ijet_softc_t *ijets = ijetcd.cd_devs[unit];
  int error=0;
	
  /*
   * So we don't have to muck w/ alignment for the time being, we'll
   * stage memory transfers through a temporary buffer.   This isn't
   * necessary on the i386, I believe.
   */

  /*
   * Main ioctl processing switch
   */
  switch(cmd) {

    /* checks for mem space to load ucode.  if available, it will load it in. */
  case IJETIOC_LOAD_UCODE: {
    iocopy_req_t *iocpr = (iocopy_req_t *) data;
    if (ijets->ucode)
      free(ijets->ucode, M_DEVBUF);
    ijets->ucodesize = iocpr->len;
    if((ijets->ucode = malloc(ijets->ucodesize, M_DEVBUF, M_NOWAIT)) == NULL)
      ret ENOMEM;
    copyin((char *)iocpr->user, ijets->ucode, ijets->ucodesize);
    ijets->flags |= IJET_FL_UCODEIS;
  }
    break;
    
    /* copys from system mem space to user mem space */
  case IJETIOC_GET_MEM: {
    iocopy_req_t *iocpr = (iocopy_req_t *) data;
    JET_FLUSH();
    copyout(iocpr->system, iocpr->user, iocpr->len);
  }
    break;

    /* copys from user mem space to system mem space */
  case IJETIOC_PUT_MEM: {
    iocopy_req_t *iocpr = (iocopy_req_t *) data;
    copyin(iocpr->user, iocpr->system, iocpr->len);
    JET_FLUSH();
  }
    break;
    
    /* extract 8 bits of system mem address into first 8 bits of a 32 bit field*/
  case IJETIOC_GET_UINT8: {
    iocopy_req_t *iocpr = (iocopy_req_t *) data;
    JET_FLUSH();
    iocpr->val = * (uint8 *) iocpr->system;
  }
    break;
    
    /* extract first 8 bits of 32 bit field and use it as system address space */
  case IJETIOC_PUT_UINT8: {
    iocopy_req_t *iocpr = (iocopy_req_t *) data;
    * (uint8 *) iocpr->system = iocpr->val;
    JET_FLUSH();
  }
    break;
  
    /* copies 32 bit address of system data */
  case IJETIOC_GET_UINT32: {
    iocopy_req_t *iocpr = (iocopy_req_t *) data;
    JET_FLUSH();
    iocpr->val = * (uint32 *) iocpr->system;
  }
    break;
    
    /* assigns new 32 bit address of system data */
  case IJETIOC_PUT_UINT32: {
    iocopy_req_t *iocpr = (iocopy_req_t *) data;
    * (uint32 *) iocpr->system = iocpr->val;
    JET_FLUSH();
  }
    break;
    
    /* initializes device with setup data unless data is invalid */
  case IJETIOC_SETUP: {
    ijet_ioctl_setup_t *setup = (ijet_ioctl_setup_t *) data;
    if (!ijetcheckcfg(ijets, setup))
      error = ERANGE;		/* better than EINVAL?? */
    else
      ijets->flags |= IJET_FL_CFGINIT;
  }
    break;

  case IJETIOC_STRUCT_ADDR:
    /* return the address (in our space) of the primary data
     * structure.  The _GET_MEM ioctl can then be used to get
     * the data
     */
    *(ijet_softc_t **) data = ijets;
    break;

/* adding new entry by finding list of corresponding IP address if there is space */
  case IJETIOC_ADD_ART: {
    ijet_art_ent_t **artehead, *arte, *newarte=NULL;
    uint32 tmp;
    int found, loop;
    /* we just add this ARP entry to the chain, we need to decide
     * if we want to statically allocate some number of entries, or
     * to malloc them as needed
     */
    tmp = ((uint32 *) data)[0];         /* IP address */
    artehead = &ijets->art[(tmp>>24)&(IJET_ART_HASH-1)];
    arte = *artehead;

      if ((newarte=ijets->art_free) == NULL)
        ret ENOSPC;

      /* add new entry to head of chain */
      ijets->art_free = newarte->next;
      newarte->next = *artehead;
      *artehead = newarte;
      newarte->ipaddr = tmp;

      /* update pending arps array */
      found = -1;
      for (loop = 0; loop < MAX_ARPS; loop++)
         if (ijets->pending_arps[loop] == newarte->ipaddr) {
            found = loop;
            break;
         }
      if (found != -1) {
         ijets->next_arp--;
         for (loop = found; loop < MAX_ARPS - 1; loop++)
           ijets->pending_arps[loop] = ijets->pending_arps[loop+1];
      }
  
    newarte->tx_VCid = ((uint32 *) data)[1];
    newarte->rx_VCid = ((uint32 *) data)[2];
    newarte->flag = ((unsigned int *) data)[3];
    newarte->idle = 0;
  }
    break;

    /* remove entry based on IP address and transmit and receive VCI */
  case IJETIOC_DEL_ART: {
    ijet_art_ent_t *arte, **arteprev;
    uint32 ipaddr = ((uint32 *) data)[0];		/* ip address */
    uint32 tx_VCid = ((uint32 *) data)[1];
    uint32 rx_VCid = ((uint32 *) data)[2];

    /* remove an ARP table entry */
    arteprev = &ijets->art[(ipaddr>>24)&(IJET_ART_HASH-1)];
    arte = *arteprev;

    if ((tx_VCid == 0) && (rx_VCid == 0)) /* Hack for now */
      while (arte!=NULL && arte->ipaddr!=ipaddr) {
        arteprev = &arte->next;
        arte = arte->next;
    }
    else 
      while (arte!=NULL && ((arte->ipaddr!=ipaddr) || (arte->tx_VCid!=tx_VCid)
                            || (arte->rx_VCid!=rx_VCid))) {
        arteprev = &arte->next;
        arte = arte->next;
    }      

    if (arte == NULL)			/* not found on chain */
      ret EINVAL;
    else {
	/* Only Vince can delete SVCs */
	if ((arte->flag == IP_SVC) && (ijets->vince_proc != p)) {
            char ip[60];
            sprintf(ip, "ijetCloseIpVc %d %d\n", arte->tx_VCid, arte->rx_VCid);
            debug ("Sending command %s\n", ip);
            skip_jetmsg_rx_interface (ip, strlen(ip));
        } 
 	else {
            ll_CloseRx(ijets, arte->rx_VCid);  /* close tx and rx VCs */
            ll_CloseTx(ijets, arte->tx_VCid);
            *arteprev = arte->next;	/* update whatever pointed to us */
            arte->next = ijets->art_free;	/* add free list to us */
            ijets->art_free = arte;		/* point free list to me */
	}
    }
  }
    break;

/* create new table and copy data all entries from ART into it and load all that to user mem */
  case IJETIOC_GET_ART: {
    /* Get the ARP table */
    iocopy_req_t *iocpr = (iocopy_req_t *) data;
    ijet_art_ent_t *arte;
    int loop, nate = iocpr->len;
    typedef struct {
        uint32 ipaddr;
        uint32 vpvc;
        uint32 flag;
	uint32 fc;
        uint32 idle;
    } arpent_t;

    arpent_t *userArtBuffer = (arpent_t *) iocpr->user;
    arpent_t *kernelArtBuffer = NULL;
    int n = 0;			/* No. of arp entries returned */
   
    debug ("Number of ARP entries requested %d\n", nate);
    if ((kernelArtBuffer = (arpent_t *) 
	malloc(nate*sizeof(arpent_t), M_DEVBUF, M_WAITOK)) == NULL)
		ret ENOMEM;
    for (loop = 0; loop < IJET_ART_HASH; loop++) {
	arte = ijets->art[loop];
	while (arte != NULL) {
		if (n < nate) {
			kernelArtBuffer[n].ipaddr = arte->ipaddr;
			kernelArtBuffer[n].vpvc = arte->rx_VCid; /* Hack */
			kernelArtBuffer[n].flag = arte->flag;
			kernelArtBuffer[n].fc = ijets->xmtvc[arte->tx_VCid].fc;
			kernelArtBuffer[n].idle = arte->idle;
			debug ("IP addr 0x%x Vpvc %d Flag %d idle %d\n",
				arte->ipaddr, arte->rx_VCid, arte->flag,
				arte->idle);
			n++;
		}
		arte = arte->next;
	}
    }
    iocpr->len = n;
    debug ("Number of ARP entries returned %d\n", n);
    copyout(kernelArtBuffer, userArtBuffer, n*sizeof(arpent_t));
    free(kernelArtBuffer, M_DEVBUF);
    }
    break;

  case IJETIOC_GET_NATE: {
    /* Get the number of ARP table entries */
    ijet_art_ent_t *arte;
    int loop, nate = 0;
    
    for (loop = 0; loop < IJET_ART_HASH; loop++) {
	arte = ijets->art[loop];
	while (arte != NULL) {
		nate++;
		arte = arte->next;
	}
    }
    * (uint32 *) data = nate;
  }
  break;
    
  /* remove all entries on ART with a flag set to IP_PVC = 2 */
  case IJETIOC_FLUSH_ART: {
    ijet_art_ent_t *arte, **arteprev;
    int loop;
    /* flush ARP table */
    for (loop = 0; loop < IJET_ART_HASH; loop++) {
	arteprev = &ijets->art[loop];
	arte = *arteprev;
	while (arte != NULL) {
	/* If PVC delete from hash table */  
	    if (arte->flag == IP_PVC) {
	        ll_CloseRx(ijets, arte->rx_VCid);  /* close tx and rx VCs */
	        ll_CloseTx(ijets, arte->tx_VCid);
		*arteprev = arte->next;
                arte->next = ijets->art_free;     /* place entry on freelist */
                ijets->art_free = arte; 
                arte = *arteprev;
            }
	/* If SVC signal Vince to initiate RELEASE */
            else {
                char ip[60];
 	        sprintf(ip, "ijetCloseIpVc %d %d\n", arte->tx_VCid,
                        arte->rx_VCid);
		debug("Sending command %s\n", ip);
		skip_jetmsg_rx_interface (ip, strlen(ip));
		arteprev = &arte->next;
		arte = arte->next;
	    }
	}
      }
  }
  break;

  /* prepares for transmitting data, assigned VC type, flow control, ... etc */
  case IJETIOC_OPEN_TX: {
    /* NOTE: do some error checking on this one... */
    ijet_ioctl_open_tx_t *txp = (ijet_ioctl_open_tx_t *) data;
    ijet_xmt_vc_t *xmtvcp;

    txp->vcid = ll_OpenTx(ijets, txp->processing_type, ijets->xmt_mbox,
			  txp->scheduling_type, txp->vpivci);
    xmtvcp = &ijets->xmtvc[txp->vcid];
    xmtvcp->fc = txp->fc; 
    if (txp->vpivci < 65536) {  /* kosak HACK */
      ijets->vpivci2vcid[txp->vpivci] = txp->vcid;
#if defined(JET960_CREDIT) || defined(JET960_DYN_CREDIT)
      /*
       * this completely blows...as we now need the 960 device entry
       * may be better to go to a unit number here.
       */
      jet960NotifyNewVc(UNIT2JET960S(0), txp->vpivci, txp->vcid);
#endif
    }
    else
      warn ("-----------------YOU GOTTA FIX THIS------------\n");
#ifdef IJET_PERVC_LIMITS
    {
      xmtvcp->pkt_count = 0;
      xmtvcp->pkt_threshold = ijets->xmt_mbufs;	/* maximum anyway?? */
      debugt("packet threshold for vcid %d is initially %d\n", txp->vcid,
	     xmtvcp->pkt_threshold);
    }
#endif
  } break;
    
  /* get VC type of received cell */
  case IJETIOC_OPEN_RX:
    {
      /* NOTE: do some error checking on this one... */
      ijet_ioctl_open_rx_t *rxp = (ijet_ioctl_open_rx_t *) data;
      /*
       * FIX::  if the adaptor worked...we'd have different
       *   head/data flists
       */
      if ((rxp->vcid = ll_OpenRx(ijets, rxp->processing_type, ijets->rcv_mbox,
			    ijets->rcv_flist[0], ijets->rcv_flist[0],
			    &(rxp->vpivci))) < 0) {
		ret EINVAL;
      }
    }
    break;

#if (defined(IJET_CREDIT) || defined(IJET_DYN_CREDIT))
  /* get VC type of received credit cell */
  case IJETIOC_OPEN_CREDIT:
    {
      /* NOTE: do some error checking on this one... */
      ijet_ioctl_open_rx_t *rxp = (ijet_ioctl_open_rx_t *) data;
      /*
       * FIX::  if the adaptor worked...we'd have different
       *   head/data flists
       */
      if ((rxp->vcid = ll_OpenRx(ijets, rxp->processing_type,
				 ijets->credit_mbox,
			    ijets->credit_flist, ijets->credit_flist,
			    &(rxp->vpivci))) < 0) {
		ret EINVAL;
      }
    }
    break;

    /* */
  case IJETIOC_RESET_CREDIT:
    {
      int i;
      for (i=0; i<ijets->xmt_VCids; i++)
        ijets->xmtvc[i].credit = *(uint32*)data;
      debugd("credit reset at %d for vcids 0 - %d\n",
	     *(uint32*) data, ijets->xmt_VCids);
      debugd("first credit cell at %p, last at %p\n",
	     &(ijets->xmtvc[0].credit),
	     &(ijets->xmtvc[ijets->xmt_VCids-1].credit));
      ijetcheckxmt(ijets);
    }
  break;

  /* set flow control and credits for a particular VC */
  case IJETIOC_SET_FLOW_CONTROL:
    {
      uint32 vpvc = ((uint32 *) data)[0];
      uint32 fc = ((uint32 *) data)[1];
      ijet_xmt_vc_t *xmtvcp;

      xmtvcp = &ijets->xmtvc[ijets->vpivci2vcid[vpvc]];
      xmtvcp->fc = fc;
      xmtvcp->credit = 0xff;
    }
  break;
#endif    

  /* get data off of pci bus */
  case IJETIOC_READ_PCI: {
    int reg = ((int *) data)[0];
    pci_conftag_t tag = (pci_conftag_t) ((u_long *) data)[2];
    ((pci_confreg_t *) data)[1] = pci_conf_read(tag, reg);
  }
    break;
    
    /* write data onto pci bus */
  case IJETIOC_WRITE_PCI: {
    int reg = ((int *) data)[0];
    pci_conftag_t tag = (pci_conftag_t) ((u_long *) data)[2];
    pci_confreg_t value = (pci_confreg_t) ((uint32 *) data)[1];
    pci_conf_write(tag, reg, value);
  }
    break;
    
#if IJETD      
  case IJETIOC_DEBUG_MASK:
    ijet_debugmask = *(uint32*)data;
    warn("setting debug mask to %d\n", ijet_debugmask);
    break;
#endif      

#if IJETD
    /* move debugging data into user mem space */
  case IJETIOC_READLOG: {
    iocopy_req_t *iocpr = (iocopy_req_t *) data;
    int   userStart  = iocpr->val;
    char *userBuffer = iocpr->user;
    int   userLength = iocpr->len;
    
    iocpr->len = 0;
    if (userStart != ijet_debugLogIndex) {
      int bytesTransferred;
      
      if (userStart > ijet_debugLogIndex) { /* handle wrap case */
        bytesTransferred =
          min (IJET_DEBUG_LOG_SIZE-userStart, userLength);
        copyout(&ijet_debugLogBuffer[userStart], userBuffer,
                bytesTransferred);
        userLength -= bytesTransferred;
        userBuffer += bytesTransferred;
        userStart   = 0;
        
        iocpr->len += bytesTransferred;
      }
      
      bytesTransferred = min(ijet_debugLogIndex-userStart, userLength);
      copyout(&ijet_debugLogBuffer[userStart], userBuffer,
              bytesTransferred);
      iocpr->len += bytesTransferred;
      iocpr->val = (userStart+bytesTransferred) % IJET_DEBUG_LOG_SIZE;
    }
  } break;
#endif
    
  case IJETIOC_SET_SCHEDULER: {
    uint32 *p = (uint32*)data;
    ll_SetScheduler (ijets, p[0], p[1], p[2], p[3]);
  }
    break;

  case IJETIOC_VINCE_INIT: {
    int loop;
    ijets->vince_flag = TRUE;
    ijets->vince_proc = p;
    ijets->next_arp = -1;
    for(loop=0;loop<MAX_ARPS;loop++) ijets->pending_arps[loop] = 0;
  }
    break;

  case IJETIOC_VINCE_KEEP_ALIVE: {
    ijets->vince_keep_alive = 0;
    ijets->vince_flag = TRUE;
  }
    break;

#ifdef IJET_CELL_COUNTS
    /* set number of recieved, to be transmited, and credit cells to 0 for all VCs */
  case IJETIOC_CLR_CELL_COUNTS: {
    int loop;
    /* rcv */
    for (loop=0; loop<ijets->rcv_VCids; loop++)
      ijets->rcvvc[loop].cellcount = 0;
    /* xmt */
    for (loop=0; loop<ijets->xmt_VCids; loop++)
      ijets->xmtvc[loop].cellcount = 0;
    /* credit */
#if (defined(IJET_CREDIT) || defined(IJET_DYN_CREDIT))
    for (loop=0; loop<ijets->xmt_VCids; loop++)
      ijets->xmtvc[loop].creditCellCount = 0;
#endif
#if defined(JET960_CREDIT) || defined(IJET960_DYN_CREDIT)
    jet960ClearCreditCounts(JET960UNIT);
#endif
  } break;

  /* count cells for transmist, receive, or credit for all VCs */
  case IJETIOC_GET_CELL_COUNTS: {
    ijet_cell_count_ioctl_t *cc = (ijet_cell_count_ioctl_t *) data;
    int *user_buffer = cc->buffer;
    int loop;

    switch (cc->dir) {
    case IJET_XMT:
      if (cc->bufferlen == 0) {
	cc->bufferlen = ijets->xmt_VCids;
	ret 0;
      }
      cc->bufferlen = min(cc->bufferlen, ijets->xmt_VCids);
      for (loop=0; loop<cc->bufferlen; loop++)
	copyout(&ijets->xmtvc[loop].cellcount, user_buffer++, sizeof(int));
      break;

#if (defined(IJET_CREDIT) || defined(IJET_DYN_CREDIT))
    case IJET_CR:
      if (cc->bufferlen == 0) {
        cc->bufferlen = ijets->xmt_VCids;
        return 0;
      }
      cc->bufferlen = min(cc->bufferlen, ijets->xmt_VCids);
      for (loop = 0; loop < cc->bufferlen; loop++)
        copyout(&ijets->xmtvc[loop].creditCellCount, user_buffer++, 
                sizeof(int));
      break;
#endif
#if defined(JET960_CREDIT) || defined(JET960_DYN_CREDIT)
    case IJET_CR: {
      int *kernel_buffer = NULL;
      if (cc->bufferlen == 0) {
        cc->bufferlen = ijets->xmt_VCids;
        return 0;
      }
      cc->bufferlen = min(cc->bufferlen, ijets->xmt_VCids);
      if ((kernel_buffer = (int *) 
           malloc(cc->bufferlen*sizeof(int), M_DEVBUF, M_WAITOK)) == NULL)
        return ENOMEM;
      jet960GetCreditCounts(JET960UNIT, kernel_buffer, cc->bufferlen);
      copyout(kernel_buffer, user_buffer, cc->bufferlen * sizeof(int));
      free(kernel_buffer, M_DEVBUF);
    }
      break;
#endif
    case IJET_RCV:
      if (cc->bufferlen == 0) {
	cc->bufferlen = ijets->rcv_VCids;
	ret 0;
      }
      cc->bufferlen = min(cc->bufferlen, ijets->rcv_VCids);
      for (loop=0; loop<cc->bufferlen; loop++)
        if (ijets->rcvvc[loop].active)
	  copyout(&ijets->rcvvc[loop].cellcount, 
                  &user_buffer[ijets->vpivci2vcid[loop]], sizeof(int));
      break;
    default:
      ret EINVAL;
    }
    
  } break;
#endif

#ifdef IJET_LOOPBACK
  case IJETIOC_TOGGLE_LOOPBACK:
    loopback = !loopback;
    warn("loopback is now %s\n", loopback ? "ON" : "OFF");
    break;
#endif

#ifdef IJET_PERVC_LIMITS
    /* set max number of packet transmissions for a VC */
  case IJETIOC_SET_PERVC_LIMIT: {
    uint32 vpvc = ((uint32 *) data)[0];
    uint32 threshold = ((uint32 *) data)[1];
    ijet_xmt_vc_t *xmtvcp = &ijets->xmtvc[ijets->vpivci2vcid[vpvc]];

    if (threshold < ijets->xmt_mbufs) {
      xmtvcp->pkt_threshold = threshold;
      debugt("packet threshold for VPVC %d is now %d\n", vpvc, threshold);
    }
  } break;
#endif

  case IJETIOC_START_NEW_UCODE: {
    *(ijets->magicCommandLocationp) = 0;
    *(ijets->magicVcFreezep) = 0;
    IssueCommand3(ijets, JET_DYN_VR_LINK_OPCODE<<27, 0, 0);
  } break;

  case IJETIOC_STOP_NEW_UCODE: {
    *(ijets->magicCommandLocationp) = 1;
  } break;

  case IJETIOC_TOGGLE_OC12: {

    struct ifnet *ifp = &ijets->ijet_if;
    if (ifp->if_flags & IFF_RUNNING)
      warn("toggling IJET_FL_OC12 flag!!! already ifconfig'd!!!\n");
    
    if (ijets->flags & IJET_FL_OC12) {
      debug("Turning OC12 off..\n");
      ijets->flags &= ~IJET_FL_OC12;
    }
    else {
      debug("Turning OC12 on..\n");
      ijets->flags |= IJET_FL_OC12;
    }
  } break;

  default:
    ret EINVAL;
  }

  ret error;
}
end(ijetioctl);

/*
 * ijetmboxhandler()
 *
 * this routine is the mailbox walker for the ip stuff.
 *
 * NOTE:: the mbox_vrp is an actual vr address in SRAM, while we
 *   pass the mirrored copy to the rcv() routine
 */
void ijetrcvwalker(ijet_softc_t *ijets)
begin(ijetrcvwalker);
{
  int nextIndex;
  vr_t *vrp, *mvrp;
  bool last_item = FALSE;

  vrp = ijets->rcv_mbox_vrp;
  mvrp = IJET_SRAM2MIRROR(vrp);
  JET_FLUSH();
  nextIndex = vrp->message.nextIndex;

  /** test
  warn(" entering rcvwalker\n"); **/
  
  while (nextIndex != IJET_NO_NEXT_VR) {
    /*
     * copy the nextIndex into mirrorland...
     *
     * HACK HACK HACK FIX FIX FIX
     *  can this work?? due to race conditions...think about it!!!!
     */
    mvrp->message.nextIndex = nextIndex;

    if (ijets->rcv_mbox_free) {
      VrFreeRoutine(ijets, mvrp, 0);
      ijets->rcv_mbox_free = FALSE;
    }

    vrp = jet_VrIndex2Vrp(ijets, nextIndex);

    if (vrp == ijets->rcv_mbox_vrp) {
      warn("yikess....VR looped onto itself in rcv!!\n");
      panic("vr loop");
    }
    
    ijets->rcv_mbox_vrp = vrp;

    /** test
    warn("RCV VR index %d\n", nextIndex); **/

    /*
     * copy the whole thing over...
     */
    mvrp = IJET_SRAM2MIRROR(vrp);
    JET_FLUSH();
    bcopyvrp(vrp, mvrp);

    /*
     * check for sanity.  I dont know if this is the right place -- prashant
     */
    if (vrp->message.vcid == 0) {
      debug_DumpToScreen('D', 
                         "Mailbox VR not in message format!!! Rejecting!");
      panic("Rx: Race!!!!!");
      break;
    }

    JET_FLUSH();
    nextIndex = vrp->message.nextIndex;
    if (nextIndex == IJET_NO_NEXT_VR)
      last_item = TRUE;

    ijetrcv(ijets, mvrp, last_item);

    /* if (mvrp->message.vcid == 0) {
      debug_DumpToScreen('D', "Receive mailbox VR not in message format!!!!");
    }*/
    
  }
}
end(ijetrcvwalker);

/*
  *  ijetxmtwalker ()
  *
  *  Moves VCI and buffers to mailbox for host to recycle after xmit.
  */
void ijetxmtwalker(ijet_softc_t *ijets)
begin(ijetxmtwalker);
{
  int nextIndex;
  vr_t *vrp, *mvrp;
  vcid_t vcid;
  bool last_item = FALSE;

  vrp = ijets->xmt_mbox_vrp;
  mvrp = IJET_SRAM2MIRROR(vrp);
  JET_FLUSH();
  nextIndex = vrp->message.nextIndex;

  while (nextIndex != IJET_NO_NEXT_VR) {

    if (ijets->xmt_mbox_free) {
      ijetaddxmtvr(ijets, mvrp);
      ijets->xmt_mbox_free = FALSE;
    }

    vrp = jet_VrIndex2Vrp(ijets, nextIndex);

    /*
     * check for sanity.  I dont know if this is the right place -- prashant
     */
    if (vrp->message.vcid == 0) {
      debug_DumpToScreen('D', 
                         "Mailbox VR not in message format!!! Rejecting!");
      panic("Tx: Race!!!!!");
      break;
    }

    ijets->xmt_mbox_vrp = vrp;

    /*
     * copy the whole thing over...
     */
    mvrp = IJET_SRAM2MIRROR(vrp);
    JET_FLUSH();
    bcopyvrp(vrp, mvrp);
    vcid = mvrp->message.vcid;

    JET_FLUSH();
    nextIndex = vrp->message.nextIndex;
    if (nextIndex == IJET_NO_NEXT_VR)
      last_item = TRUE;
    
    ijetxmtdone(ijets, mvrp, last_item);
  }
}
end(ijetxmtwalker);

void ijetflist(ijet_softc_t *ijets, freelist_t freelist)
     begin(ijetflist);
{

  warn("ijetflist:: out of VR's on freelist %d\n", freelist);
  /*
   * I think the adaptor doesn't recover from this error at all.
   * Just in case I'm wrong, check immediately for available VRs.
   * If none, walk the mbox in case we missed any.
   */
  VrAttachRoutine(ijets);
  /*
   * FIX::
   *  what should I call???
   */
  ijetrcvwalker(ijets);
}
end(ijetflist);

#if (defined(IJET_CREDIT) || defined(IJET_DYN_CREDIT))
void ijetcreditflist(ijet_softc_t *ijets, freelist_t freelist)
     begin(ijetcreditflist);
{
  /*
   * Just call the credit handler.
   */
  ijetcreditmboxhandler(ijets);
}
end(ijetcreditflist);
#endif

void ijetifstop(ijet_softc_t *ijets)
     begin(ijetifstop);
{
  /*
   * We may want stop to be different than reset
   * ASK corey if there is a way to just stop reception
   * of packets.  I can stop transmittal by marking the
   * interface down.
   *
   * For now, we'll just call ijetreset.
   */
  ijetreset(ijets);
}
end(ijetifstop);

/*
 * This is an entire reset.
 */
void ijetreset(ijet_softc_t *ijets)
     begin(ijetreset);
{
  uint32 pciregs[IJET_PCI_SAVE];
  int i, len;

  /*
   * Save our pci state.
   */
  for (i=0; i<IJET_PCI_SAVE; i+=sizeof(uint32))
    pciregs[i] = pci_conf_read(ijets->pa_tag, i);
  
  /*
   * reset the adapter
   */
  pci_conf_write(ijets->pa_tag, IJET_PCI_RESET, 0x1);

  /* Now wait for the interrupt line to become 0 or we've spun enough */
  for (i=0; i<IJET_RESET_WAIT; i++)
    if ((pci_conf_read(ijets->pa_tag, PCI_INTERRUPT_REG)&
	 PCI_INTERRUPT_LINE_MASK) == 0)
      break;

  /*
   * The board is (hopefully) reset.
   * Write our state back.  We might want to write the command
   * register last??  CHECK::
   */
  for (i=0; i<IJET_PCI_SAVE; i+=sizeof(uint32))
    pci_conf_write(ijets->pa_tag, i, pciregs[i]);

  /*
   * Now we need to free/invalidate our state.  We'll call a lower
   * level routine to notify it of the reset when we're done.
   */

  /*
   * Some random buffers we've allocated.
   */
  if (ijets->xmt_mbuf_base)
    free(ijets->xmt_mbuf_base, M_DEVBUF);
  if (ijets->rcvvc)
    free(ijets->rcvvc, M_DEVBUF);
  if (ijets->ptov)
    free(ijets->ptov, M_DEVBUF);
  
  /*
   * Zero out most of the ijets struct.  We could use offsetof(), but
   * that's probably not very portable.  We'll use the following, which
   * is what offsetof() probably does anyway.
   */
  len = sizeof(ijet_softc_t) - (uint) &((ijet_softc_t *)NULL)->flags;
  bzero((char *) &ijets->flags, len);
  
  /* mark the freelists as invalid */
  for (i=0; i<IJET_NUM_FREELISTS; i++)
    ijets->rcv_flist[i] = -1;

  /*
   * Now notify the lower level that everything has been reset.
   */
  jet_Goose(ijets, ijets->sramBase, IJET_SRAM_SIZE,
	    ijets->asicBase, ijets->sonetBase);
}
end(ijetreset);


/*
 *  ijetcheckcfg ()
 *
 *  Copies setup information to a specific device's data structure.
 *
 *  Returns:  TRUE (1) on success, FALSE (0) if setup information is invalid
 */
bool ijetcheckcfg(ijet_softc_t *ijets, ijet_ioctl_setup_t *setup)
     begin(ijetcheckcfg);
{
  int *val = (int *) setup;
  int i;

  /* FIX:: check if there are any other restrictions */
  /* all values must be >= 0 */
  for (i=0; i<sizeof(ijet_ioctl_setup_t)/sizeof(int); i++)
    if (val[i] < 0)
      ret FALSE;

  /*
   * The cfg params seem to be OK.  Go ahead and save them in the
   * ijets struct.
   */
  ijets->xmt_VRs = setup->xmt_VRs;
  ijets->xmt_VCids = setup->xmt_VCids;
  ijets->xmt_mbufs = setup->xmt_mbufs;
  for (i=0; i<IJET_NUM_FREELISTS; i++)
    ijets->rcv_cnt[i] = setup->rcv_cnt[i];
  ijets->rcv_VCids = setup->rcv_VCids;
  ijets->credit_VRs = setup->credit_VRs;
  ijets->maxvpivci = setup->maxvpivci;
  
  ret TRUE;
}
end(ijetcheckcfg);

/*
 * Hopefully, this will be faster than a regular bzero, since
 * we know we can make word-sized accesses.  Also, hopefully, the PCI
 * bus manager will put these into a single burst.
 */
void bzerovrp(vr_t *vrp)
     begin(bzerovrp);
{
  uint32 *word = (uint32 *) vrp;

  /* NOTE:: this is dependent on sizeof(vr_t) == 16 */
  word[0] = 0;
  word[1] = 0;
  word[2] = 0;
  word[3] = 0;
}
end(bzerovrp);

void bcopyvrp(vr_t *vrsrc, vr_t *vrdst)
     begin(bcopyvrp);
{
  uint32 *src = (uint32 *) vrsrc;
  uint32 *dst = (uint32 *) vrdst;
  dst[0] = src[0]; 
  dst[1] = src[1]; 
  dst[2] = src[2]; 
  dst[3] = src[3]; 
}
end(bcopyvrp);

#ifdef IJET_INTELMERGESPACKETS
/*
 * This routine checks that the pdulen found in the trailer equals
 * ip_len+snap.  If not, return -1; otherwise, return offset of
 * where the next pkt would begin and set nextpkt to that vrp.
 *
 * Assumes pdulen is found on an even byte boundary.  If you allocate
 * odd-sized VRs you deserve to die.
 */
private int findEndingVr(ijet_softc_t *ijets, vr_t *headp, vr_t *tailp,
			 int traileroffset, vr_t **nextpkt, int pdulen)
     begin(findEndingVr);
{
  int trailerexcess = 6;
  int pdulenoffset = traileroffset+2; /* FIX:: no magic numbers */
  int datainvr, tpdulen;
  vm_offset_t base;
  
  if (traileroffset < 0)
    return -1;

  while (1) {
    datainvr = MCLBYTES-headp->message.remain;
    
    if (datainvr > pdulenoffset) {
      base = ijets->ptov[jet_MVrp2VrIndex0(ijets, headp)]+pdulenoffset;

      if ((tpdulen=ntohs(*(unsigned short *) base)) != pdulen) {
	debug("pdulen in trailer (%d) did not match IP len (%d)\n",
	       tpdulen, pdulen);
	return -1;
      }

      /*
       * Now find the start of the next packet.
       */
      if (datainvr > pdulenoffset+trailerexcess) {
	*nextpkt = headp;
	return pdulenoffset+trailerexcess;
      }

      /*
       * Crap.  Only part of the trailer was in this VR.
       */
      if (headp == tailp)
	return -1;

      *nextpkt = jet_VrIndex2MVrp(ijets, headp->message.nextIndex);
      return pdulenoffset+trailerexcess-datainvr;
    }

    pdulenoffset -= datainvr;
    if (headp == tailp)
      return -1;

    headp = jet_VrIndex2MVrp(ijets, headp->message.nextIndex);
  } 
}
end(findEndingVr);
#endif IJET_INTELMERGESPACKETS

/*
  *  VrAttachRoutine ()
  *
  *  Recycles VR rcv list by giving VRs default values and putting them back on to VR free list.  
  *  Also updates SRAM.
  */
void VrAttachRoutine(ijet_softc_t *ijets)
begin(VrAttachRoutine);
{
  vr_t *mvrp, *vrp;
  int index0;
  caddr_t base;

  /* try to associate an mbuf w/ the head of the list */
  while ((mvrp=ijets->rcv_VR_head) != NULL) {
    
    MCLALLOC(base, M_DONTWAIT);
    if (base == NULL) {
      debug("VrAttachRoutine:: MCLALLOC failed!\n");
      ret;
    }
    
    if (ijets->rcv_VR_tail == mvrp) {
      ijets->rcv_VR_head = NULL;
      ijets->rcv_VR_tail = NULL;
    }
    else
      ijets->rcv_VR_head = jet_VrIndex2MVrp(ijets, mvrp->vr.nextIndex);
    
    /*
     * Put it back on the freelist.
     */
    index0 = jet_MVrp2VrIndex0(ijets, mvrp);
    ijets->ptov[index0] = (vm_offset_t) base;
    mvrp->vr.address = vtophys(base);
    mvrp->vr.remain = MCLBYTES;
    mvrp->vr.sarLocation = SAR_HOST;
    mvrp->vr.nextIndex = IJET_NO_NEXT_VR;

    vrp = IJET_MIRROR2SRAM(mvrp);
    bcopyvrp(mvrp, vrp);
    JET_FLUSH();
    /*
     * NOTE::
     * we're not handling BLI's correctly yet
     */
    ll_AppendVrlistToFreelist(ijets, ijets->rcv_flist[mvrp->vr.BLI],
			      vrp, vrp);
  }
}
end(VrAttachRoutine);

/* 
  *  VrFreeRoutine ()
  *
  *  Resets vr and adds it to free list.
  */
void VrFreeRoutine(ijet_softc_t *ijets, vr_t *mvrp, int bli)
     begin(VrFreeRoutine);
{
  int index0 = jet_MVrp2VrIndex0(ijets, mvrp);

  bzerovrp(mvrp);
  mvrp->vr.BLI = bli;
  
  /*
   * Free any associated cluster mbuf.  We can't just reuse this cluster
   * since it may have data moving up the IP stack already.
   */
  if (ijets->ptov[index0]) {
    MCLFREE(ijets->ptov[index0]);
    ijets->ptov[index0] = 0;
  }

  if (ijets->rcv_VR_head == NULL)
    ijets->rcv_VR_head = mvrp;
  else
    ijets->rcv_VR_tail->vr.nextIndex = jet_MVrp2VrIndex(ijets, mvrp);

  ijets->rcv_VR_tail = mvrp;

  /*
   * Check to see if there are any available clusters.
   */
  VrAttachRoutine(ijets);
}
end(VrFreeRoutine);

/*
  *  ijetcheckxmt ()
  *
  *  Sets up VR chains for all VCs if not already done.
  */
void ijetcheckxmt(ijet_softc_t *ijets)
     begin(ijetcheckxmt);
{
  int vcid = ijets->xmt_round_robin;
  int last = vcid;

  while (ijets->xmt_asm_needed && ijets->xmt_VR_freecnt > 0) {
    if (ijets->xmtvc[vcid].pre_head)
      ijetxmtbuildvrs(ijets, vcid);

    vcid = (vcid+1) % ijets->xmt_VCids;

    if (vcid == last)
      break;
  }

  ijets->xmt_round_robin = vcid;
}
end(ijetcheckxmt);

/*
  *  ijetaddxmtvr ()
  *
  *  Adds a free VR to VR freelist.
  */
void ijetaddxmtvr(ijet_softc_t *ijets, vr_t *mvrp)
     begin(ijetaddxmtvr);
{
  mvrp->vr.nextIndex = IJET_NO_NEXT_VR;
  
  if (ijets->xmt_freeVR_head)
    ijets->xmt_freeVR_tail->vr.nextIndex = jet_MVrp2VrIndex(ijets, mvrp);
  else
    ijets->xmt_freeVR_head = mvrp;

  ijets->xmt_freeVR_tail = mvrp;
  ijets->xmt_VR_freecnt++;
  if (ijets->xmt_asm_needed) {
    /*
     * Hmm...we were xmt VR starved prior to this and something is
     * waiting for VRs.
     */
    ijetcheckxmt(ijets);
  }
}
end(ijetaddxmtvr);


#if (defined(IJET_CREDIT) || defined(IJET_DYN_CREDIT))
/*
 * This routine is the mailbox handler for credit cells.
 *
 * Since we don't want to use old credit cells, we adapt the following
 * scheme:
 *
 * --  walk the entire mailbox, updating the credit values in the xmtvc
 *     table.  create a link from each of these vcs to the next.
 * --  once the mailbox is empty, we now follow this linked list through
 *     the xmt vc table, scheduling packets as appropriate.
 * --  we should probably check the mbox for new credit cells from time
 *     to time if we feel this process may take too long
 *
 */
void ijetcreditwalker(ijet_softc_t *ijets)
     begin(ijetcreditwalker);
{
  int nextIndex, index0;
  vr_t *mboxvrp = ijets->credit_mbox_vrp;
  vr_t *mvrp;
  ijet_credit_cell_t *cell;
  vcid_t vcid;
  ijet_xmt_vc_t *xmtvc_head=NULL, *xmtvc_tail=NULL, *xmtvcp;
  /*
   * HACK HACK HACK::
   *  correct way is to get endofpdu bit
   */
  JET_FLUSH();
  nextIndex = mboxvrp->message.nextIndex;

  while (1) {

    /*
     * pull everything out of the credit mailbox
     */
    while (nextIndex != IJET_NO_NEXT_VR) {
      
      /*
       * put the VR back on the free list
       */
      mvrp = IJET_SRAM2MIRROR(mboxvrp);
      bzerovrp(mvrp);
      index0 = jet_Vrp2VrIndex0(ijets, mboxvrp);
      mvrp->vr.address = vtophys(ijets->ptov[index0]);
      mvrp->vr.remain = IJET_CREDIT_VR_SIZE;
      mvrp->vr.sarLocation = SAR_HOST;
      mvrp->vr.nextIndex = IJET_NO_NEXT_VR;
      bcopyvrp(mvrp, mboxvrp);
      JET_FLUSH();
      ijetaddcreditvr(ijets, mboxvrp);
      
      mboxvrp = jet_VrIndex2Vrp(ijets, nextIndex);

      /* grab the credit value of the mbox head*/
      cell = (ijet_credit_cell_t *) ijets->ptov[jet_Vrp2VrIndex0(ijets,
								 mboxvrp)];
      vcid = ijets->vpivci2vcid[cellvpivci(cell)];
      xmtvcp = &ijets->xmtvc[vcid];
      xmtvcp->credit = cell->value;
      xmtvcp->creditCellCount++;
      debugd("credit value for vcid %d is now 0x%x\n", vcid, cell->value);

      /*
       * If this VC was already in our linked list, we don't link to it
       * again.
       */
      if (!(xmtvcp->flags&IJET_XF_NEWCREDIT)) {
	xmtvcp->flags |= IJET_XF_NEWCREDIT;
	if (xmtvc_tail)
	  xmtvc_tail->nextxmtvc = xmtvcp;
	else
	  xmtvc_head = xmtvcp;
	
	xmtvc_tail = xmtvcp;
      }

      JET_FLUSH();
      nextIndex = mboxvrp->message.nextIndex;
    }

    /*
     * mailbox is now empty, now follow the linked list scheduling
     * packets as appropriate
     */
    if (xmtvc_head == NULL)
      break;

#ifdef IJET_DYN_CREDIT
    /*
     * Now update credit values for the VC in the linked list
     */
    {
      vcid_t vcid = xmtvc_head - ijets->xmtvc;
      uint8 credit = ijets->xmtvc[vcid].credit;
      jet_intel_hack_cmd_t *secondWord = (jet_intel_hack_cmd_t*)&vcid;
      secondWord->opcode = JET_INTEL_HACK_OPCODE;
      IssueCommand3 (ijets, JET_STA_VR_LINK_OPCODE << 27, vcid, credit);
    }
#endif

    /*
     * FIX:: ??
     * should we check those VRs in BUSYWAIT too?
     */
    if (xmtvc_head->flags&IJET_XF_CREDITWAIT)
      ijetxmtdoxmt(ijets, xmtvc_head-ijets->xmtvc);

    xmtvc_head->flags &= ~IJET_XF_NEWCREDIT;
    if (xmtvc_head == xmtvc_tail)
      xmtvc_head = xmtvc_tail = NULL;
    else
      xmtvc_head = xmtvc_head->nextxmtvc;
  }
  
  ijets->credit_mbox_vrp = mboxvrp;
}
end(ijetcreditwalker);

/*
  *  ijetupdatecredit ()
  *
  *  Updates credit value for all VCs and prepares the xmt scheduling.
  */
void ijetupdatecredit(ijet_softc_t *ijets)
     begin(ijetupdatecredit);
{
  ijet_credit_cell_t *cell = ijets->currentcreditva;
  vcid_t vcid;
  ijet_xmt_vc_t *xmtvc_head=NULL, *xmtvc_tail=NULL, *xmtvcp;
  static uint8 minCredit = 255;

  while (1) {
    /* walk the circular buffer */
    while (cell->unused0 != 0xBABE) {
      vcid = ijets->vpivci2vcid[cellvpivci(cell)];
      xmtvcp = &ijets->xmtvc[vcid];
      xmtvcp->credit = cell->value;
      if (cell->value < minCredit) {
	warn("Minimum credit received is %d\n", cell->value);
	minCredit = cell->value;
      }
      xmtvcp->creditCellCount++;
      debugd("credit value for vcid %d is now %d\n", vcid, cell->value);
      cell->unused0 = 0xBABE;

      /* move the cell address...jumping page boundaries where
       * appropriate
       */
      cell++;
      if (((vm_offset_t)(cell+1)&(NBPG-1)) < IJET_CREDIT_VR_SIZE)
	cell = (ijet_credit_cell_t *) (((vm_offset_t) (++cell)) & ~ (NBPG-1));

      /* if we're past the end of our circular buffer,
       * reset back to the beginning
       */
      if (cell >= ijets->lastcreditva)
        cell = (ijet_credit_cell_t *) ijets->credit_base;
      
      /*
       * If this VC was already in our linked list, we don't link to it
       * again.
       */
      if (!(xmtvcp->flags&IJET_XF_NEWCREDIT)) {
	xmtvcp->flags |= IJET_XF_NEWCREDIT;
	if (xmtvc_tail)
	  xmtvc_tail->nextxmtvc = xmtvcp;
	else
	  xmtvc_head = xmtvcp;
	
	xmtvc_tail = xmtvcp;
      }
    }

    /* check for outgoing transmits */
    if (xmtvc_head == NULL)
      break;

#ifdef IJET_DYN_CREDIT
    /*
     * Now update credit values for the VC in the linked list
     */
    {
      vcid_t vcid = xmtvc_head - ijets->xmtvc;
      uint8 credit = ijets->xmtvc[vcid].credit;
      jet_intel_hack_cmd_t *secondWord = (jet_intel_hack_cmd_t*)&vcid;
      secondWord->opcode = JET_INTEL_HACK_OPCODE;
      IssueCommand3 (ijets, JET_STA_VR_LINK_OPCODE << 27, vcid, credit);
    }
#endif

    if (xmtvc_head->flags&IJET_XF_CREDITWAIT)
      ijetxmtdoxmt(ijets, xmtvc_head-ijets->xmtvc);
  
    xmtvc_head->flags &= ~IJET_XF_NEWCREDIT;
    if (xmtvc_head == xmtvc_tail)
      xmtvc_head = xmtvc_tail = NULL;
    else
      xmtvc_head = xmtvc_head->nextxmtvc;
  }

  ijets->currentcreditva = cell;
}
end(ijetupdatecredit);

/*
  *  ijetcreditmboxhandler ()
  *
  *  Goes thru and empties credit mailbox.  Then makes a call to update credit for all VCs.
  */
void ijetcreditmboxhandler(ijet_softc_t *ijets)
     begin(ijetcreditmboxhandler);
{
  int nextIndex, index0;
  vr_t *mboxvrp = ijets->credit_mbox_vrp;
  vr_t *mvrp;

  JET_FLUSH();
  nextIndex = mboxvrp->message.nextIndex;
  while (nextIndex != IJET_NO_NEXT_VR) {
    /* put the VR back on the free list */
    mvrp = IJET_SRAM2MIRROR(mboxvrp);
    bzerovrp(mvrp);
    index0 = jet_Vrp2VrIndex0(ijets, mboxvrp);
    mvrp->vr.address = vtophys(ijets->ptov[index0]);
    mvrp->vr.remain = IJET_CREDIT_VR_SIZE;
    mvrp->vr.sarLocation = SAR_HOST;
    mvrp->vr.nextIndex = IJET_NO_NEXT_VR;
    bcopyvrp(mvrp, mboxvrp);
    JET_FLUSH();
    ijetaddcreditvr(ijets, mboxvrp);
    
    mboxvrp = jet_VrIndex2Vrp(ijets, nextIndex);
    JET_FLUSH();
    nextIndex = mboxvrp->message.nextIndex;
  }

  ijets->credit_mbox_vrp = mboxvrp;
  ijetupdatecredit(ijets);
}
end(ijetcreditmboxhandler);

/*
 * Add creditvrs, the last vr in the chain will be treated as a freelist
 * marker due to the 2nd word hack.  since we have some vrs that we wish
 * to use as markers anyway, we wait for one of these to arrive before
 * appending.
 */
void ijetaddcreditvr(ijet_softc_t *ijets, vr_t *vrp)
     begin(ijetaddcreditvr);
{
  /* if a chain exists, attach it to the end */
  if (ijets->creditvrtail != NULL)
    ijets->creditvrtail->vr.nextIndex = jet_Vrp2VrIndex(ijets, vrp);
  else
    ijets->creditvrhead = vrp;
  
  ijets->creditvrtail = vrp;
  
  /* return if this one should not have a freelist marker */
  if (++ijets->creditmarker != ijets->creditinterval)
    ret;
  
  vrp->vr.freeListMarker = 1;
  ijets->creditmarker = 0;

  JET_FLUSH();
  ll_AppendVrlistToFreelist(ijets, ijets->credit_flist,
			    ijets->creditvrhead, vrp);
  
  ijets->creditvrtail = NULL;
}
end(ijetaddcreditvr);
#endif

#ifdef JET960_CREDIT
public void ijetCreditNotify(ijet_softc_t *ijets, uint32 vcid)
begin();
{
  ijetxmtdoxmt(ijets, vcid);
}
end();
#endif


/* 
 * ijetxmtdoxmt ()
 *
 * Checks credit for packet transmission and then schedules packet if everything ok.
 */
void ijetxmtdoxmt(ijet_softc_t *ijets, vcid_t vcid)
     begin(ijetxmtdoxmt);
{
  ijet_xmt_vc_t *xmtvcp = &ijets->xmtvc[vcid];
  
  if (ll_txBusy(ijets, vcid)) {
    xmtvcp->flags |= IJET_XF_BUSYWAIT;
    /* HACK!!! HACK!!! HACK!!! */
#if (defined(IJET_DYN_CREDIT) || defined(JET960_DYN_CREDIT))
    ll_enableIfTxStalled(ijets, vcid); 
#endif
    ret;
  }

  xmtvcp->flags &= ~IJET_XF_BUSYWAIT;
  
  if (xmtvcp->asm_head == NULL)
    ret;
    
#ifdef IJET_CREDIT
  /*
   * note:: it's possible to get here from the creditwalker routine,
   * therefore we should not call creditwalker from here.
   */
  if (xmtvcp->fc == FC_CREDIT) {
    debugd("Packet size %d cells credit %d cells\n", xmtvcp->asm_head->count,
           xmtvcp->credit);
    if (xmtvcp->asm_head->count > xmtvcp->credit) {
      debugx("not sending due to lack of credit %d < %d!\n",
             xmtvcp->asm_head->count, xmtvcp->credit);
      if (!(xmtvcp->flags&IJET_XF_CREDITWAIT)) {
        xmtvcp->flags |= IJET_XF_CREDITWAIT; 
        /* here we schedule buffer switch interrupts if necessary */
        if (++ijets->credit_waiting == 1) {
          debugx("Turning end of PDU interrupts on!\n");
          ll_ChangeEndOfPDUHandler(ijets,
				   ijets->credit_mbox,
				   (ll_handler_t) ijetupdatecredit);
        }
      }
      ret;
    }
    
    if (xmtvcp->flags&IJET_XF_CREDITWAIT) {
      if (--ijets->credit_waiting == 0) {
        debugx("Turning end of PDU interrupts off!...not!!!\n");
        ll_ChangeEndOfPDUHandler(ijets, ijets->credit_mbox, NULL);
      }
      
      xmtvcp->flags &= ~IJET_XF_CREDITWAIT;
    }
    
    xmtvcp->credit -= xmtvcp->asm_head->count;
  }
#endif

#ifdef JET960_CREDIT
{

  int s;

  if (i960up) {
    s = splimp();
    CS_ENTRY_PROTOCOL(); 
    if (ijetFlag[TESTLOC] != HOST)
      debug_DumpToScreen('D', "Mutual exclusion broken: After entry!!!!!!!!");
    if (xmtvcp->asm_head->count > ijet_960CreditTable[vcid]) {
      debugx("Available credit %d cells Required credit %d cells vcid %d\n", 
             ijet_960CreditTable[vcid], xmtvcp->asm_head->count, vcid);
      ijet_960RequestTable[vcid] = xmtvcp->asm_head->count; 
      
      if (ijetFlag[TESTLOC] != HOST)
        debug_DumpToScreen('D', "Mutual exclusion broken: Before exit!!!!!!");
      CS_EXIT_PROTOCOL();
      splx(s); 
      return; 
    }
    else {
      ijet_960CreditTable[vcid] -= xmtvcp->asm_head->count;
      debugp("Credit on vcid %d is now %d\n", vcid, ijet_960CreditTable[vcid]);
      if (ijetFlag[TESTLOC] != HOST)
        debug_DumpToScreen('D', "Mutual exclusion broken: Before exit!!!!!!");
      CS_EXIT_PROTOCOL();
      splx(s);
    } 
  }
}
#endif 

#if (defined(IJET_DYN_CREDIT) || defined(JET960_DYN_CREDIT))
{
  /*
   * Write the cellcount to a SRAM location 5 for the dynamic scheduler 
   * ASK:  Is the count calculated below the same as xmtvcp->asm_head->count?
   */
  /* debugp("Appending %d cells\n", 
         ((xmtvcp->asm_head->tail->vr.pduLength+8)+47)/48);*/

  /* ASK HACK FIX::  what does prashant really want here???? */
  *(uint32*)(((char *) ijets->sramBase)+5) = 
    ((xmtvcp->asm_head->tail->vr.pduLength+8)+47)/48 + 2; 
  
  /* Now wait for a while before you append 
   * HACK!! HACK!! HACK!!
   */
   /* delay(10000); */  /* one millisecond delay */
}
#endif

#if 0
  {
    uint8 credit = ll_getCredit(ijets, vcid);

    if (xmtvcp->asm_head->count > credit) {
      if (!(xmtvcp->flags&IJET_XF_CREDITWAIT)) {
        xmtvcp->flags |= IJET_XF_CREDITWAIT; 
        /* here we schedule buffer switch interrupts if necessary */
        if (++ijets->credit_waiting == 1) {
          debugx("Turning end of PDU interrupts on!\n");
          ll_ChangeEndOfPDUHandler(ijets,
				   ijets->credit_mbox,
				   (ll_handler_t) ijetupdatecredit);
        }
      }
    }
    else
      if (xmtvcp->flags&IJET_XF_CREDITWAIT) {
        if (--ijets->credit_waiting == 0) {
          debugx("Turning end of PDU interrupts off!\n");
          ll_ChangeEndOfPDUHandler(ijets, ijets->credit_mbox, NULL);
        }
        
        xmtvcp->flags &= ~IJET_XF_CREDITWAIT;
      }
  }
#endif 

#ifdef IJET_CELL_COUNTS
  {
    /* if we know we're only transmitting a single packet, we can trust
     * the pdulength field in the final vr.  otherwise, we have to run
     * the through the entire list.
     *
     * FIX::
     *  no magic numbers
     */
    xmtvcp->cellcount +=
      ((xmtvcp->asm_head->tail->vr.pduLength+8)+47)/48;
  }
#endif

  /* schedule */
  ll_AppendVrThenScheduleDynamic(ijets, 0, vcid, 63,
				    xmtvcp->asm_head->head,
				    xmtvcp->asm_head->tail);
  /* ll_AppendVrThenScheduleStatic(ijets, vcid,
				xmtvcp->asm_head->head,
				xmtvcp->asm_head->tail);  */

  /* move to the done list */
  if (xmtvcp->done_tail)
    xmtvcp->done_tail->next = xmtvcp->asm_head;
  else
    xmtvcp->done_head = xmtvcp->asm_head;
  xmtvcp->done_tail = xmtvcp->asm_head;
  xmtvcp->asm_head = xmtvcp->asm_head->next;
  if (xmtvcp->asm_head == NULL)
    xmtvcp->asm_tail = NULL;
  xmtvcp->done_tail->next = NULL;
  
}
end(ijetxmtdoxmt);

#if (defined(IJET_CREDIT) || defined(IJET_DYN_CREDIT))
private vcid_t cellvpivci(ijet_credit_cell_t *cell)
     begin(cellvpivci);
{
  vcid_t rvalue = (cell->highvci<<4)+cell->lowvci;
  ret rvalue;
}
end(cellvpivci);
#endif
