/* ia_rfred.c */

/* RFred (receive side) low level device and buffer management */

/* Copyright (c) 2000  James M. Westall, Dept of Computer Science,
 *                     Clemson University, Clemson SC 29634 USA
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * To obtain a copy of the GNU General Public License write to the
 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * This software is derived from software developed by the Interphase
 * corporation and released by them in September 2000. The software
 * released by Interphase carried the following copyright notice:
 *
 *
 *
 *                          Copyright (C) 1993
 *                Interphase Corporation, Dallas, TX 75234
 *                          All Rights Reserved
 *
 * This code contains confidential information and  trade  secrets  of
 * Interphase Corporation which shall not be reproduced or transferred
 * to other programs or disclosed to others or used for  manufacturing
 * or any other purpose without prior written permission of Interphase
 * Corporation.  Use of copyright notice is precautionary and does not
 * imply publication or intent thereof.
 */

/*H
 * FILE: ia_rfred.c
 *
 * DESCRIPTION:
 *   This file contains the Reassembly FRED code for atlantic.
 *
 * FUNCTIONS:
 * EXPORTED:
 *   see below.
 *
 * STATIC:
 *   see below.
 *
 * GLOBAL DATA:
 *
 */

#include "ia_defs.h"

/*
 * Exported routines
 */
int ia_rfred_init (ia_softc_t *);
void ia_rfred_handler(ia_softc_t *softc);
void ia_host_rx_intr (ia_softc_t *softc);


/*
 * External functions
 */


/*
 * Internal routines
 */
static   int  ia_proc_rcv_pkt(ia_softc_t *);
static   int  ia_set_up_dma (ia_softc_t *, r_buf_desc *);
static   void ia_free_desc (ia_softc_t *, r_buf_desc *, ia_rx_buf_t *);
static   void ia_proc_rcv_excpt (ia_softc_t *softc);

extern   u_int ia_aal5;
extern   u_int ia_rx_putq;
extern   u_int ia_debug;

#define  IGNORE_R_RSE    1  /* Ignore roll-over sequence errors */
#define  IGNORE_R_CER    1  /* Ignore Packet crc errors for AAL3/4 */
#define  DISPLAY_SZ_ERR  0  /* Display packet size errors    */

int drop_sig_pkt = 0;
static int lastseq = 0;

/*F
 * FUNCTION: ia_rfred_init
 *
 * DESCRIPTION:
 * Do the following to initialize R-FRED
 *
 * o Setup descriptor table
 * o Initialize Packet Complete Queue
 * o Initialize Small Free Queue ** Not used
 * o Initialize Packet Complete Queue
 * o Initialize Exception Queue
 * o Initialize the Reassembly Table
 * o Initialize the VC table
 * o Initialize CBR and RAW queues ** Not used
 * o Initialize packet aging related registers
 * o Set mode and mask registers
 * o Set R-FRED on-line
 *
 * ARGUMENTS:
 * softc    Device state structure
 *
 * ENVIRONMENT:
 * Called from ia_attach
 *
 * RETURN:
 * IA_SUCCESS  If FFRED initialized sucessfully
 * IA_FAIL     On failure
 *
 * NOTES:
 */


/**/
/* Initialize RFred */

int
ia_rfred_init (ia_softc_t *softc)
{
   rfred_t     *rf = softc->brd_regs.rfred;
   rfred_t     *rfL = &softc->brd_regs_ll.rfred;
   u_int    mem_brd;
   u_int    buffer_brd;
   ushort_t *qptr;
   ushort_t *ptr16;
   r_buf_desc  *desc;
   f_vc_table  *vc;
   uint_t      val;
   int      i;

   printk("IA 5515: RFred initialization entered \n");

   ia_put32 (&rf->mask_reg, 0xffff);
   ia_put32 (&rf->mode_reg_0, 0);
   ia_put32 (&rf->cmd_reg, R_SWRESET);

#ifdef DBG_INIT
   printk("IA 5515: RFred reset complete... \n");
#endif

/*
 * Initialize buffer descriptors
 *
 * Note: Descriptor 0 is not used. NUM_RX_DESC does
 * not contain descriptor 0.
 */

   mem_brd = softc->brd_regs_ph.rfred_mem + R_DESC_START;
   buffer_brd = softc->brd_regs_ph.buf_mem + softc->rx_packet_ram;

   desc = (r_buf_desc *) (softc->brd_regs.rfred_mem + R_DESC_START);
   ia_put32 (&rf->desc_base, (mem_brd >> 16) & 0xffff);

   memset((caddr_t)desc, 0, sizeof (f_buf_desc));
   desc++;

#ifdef DBG_INIT
   printk("IA 5515: %d Rx descriptors of size %d at %x \n",
                     softc->num_rx_desc, sizeof (f_buf_desc), desc);
#endif

   for (i=0; i<softc->num_rx_desc; i++)
   {
      memset((caddr_t)desc, 0, sizeof (f_buf_desc));
      ia_putw (&desc->desc_stat, R_LARGE);
      ia_putw (&desc->buf_low, (ushort_t) (buffer_brd & 0xffff));
      ia_putw (&desc->buf_high, (ushort_t) (buffer_brd >> 16));

      desc++;
      buffer_brd += RX_BUF_SIZE;
   }

/*
 * Initialize Large Free Queue and place the free
 * descriptors in it.
 */

   mem_brd = softc->brd_regs_ph.rfred_mem + R_LFQ_START;
   ia_put32 (&rf->queue_base, (mem_brd >> 16) & 0xffff);

   val = mem_brd & 0xffff;
   ia_put32(&rf->lrg_st_adr, val);
   ia_put32(&rf->lrg_ed_adr, val + (softc->num_rx_desc*sizeof(ushort_t)));
   ia_put32(&rf->lrg_rd_ptr, val);
   ia_put32(&rf->lrg_wr_ptr, val + (softc->num_rx_desc*sizeof(ushort_t)));

   qptr = (ushort_t *) (softc->brd_regs.rfred_mem + R_LFQ_START);
   for (i=0; i<softc->num_rx_desc; i++)
   {
      ia_putw (qptr, (ushort_t)i + 1);
      qptr++;
   }

   ia_put32 (&rf->lrg_buf_chk, RX_BUF_SIZE - 52);
   ia_put32 (&rf->lrg_buf_size, RX_BUF_SIZE);

/*
 * Initialize Small Free Queue.
 * -- Small Free Queue does not get used at
 *    this time. It is initialized to be empty.
 */

   mem_brd = softc->brd_regs_ph.rfred_mem + R_SFQ_START;

   val = mem_brd & 0xffff;
   ia_put32 (&rf->sml_st_adr, val);
   ia_put32 (&rf->sml_ed_adr, val + (1 * sizeof(ushort_t)));
   ia_put32 (&rf->sml_rd_ptr, val);
   ia_put32 (&rf->sml_wr_ptr, val);

   ia_put32 (&rf->sml_buf_chk, 0); /* 1024 - 52 */

/*
 * Initialize Packet Complete Queue.
 */

   mem_brd = softc->brd_regs_ph.rfred_mem + R_PCQ_START;

   val = mem_brd & 0xffff;
   ia_put32 (&rf->pcq_st_adr, val);
   ia_put32 (&rf->pcq_ed_adr, val + (softc->num_rx_desc*sizeof(ushort_t)));
   ia_put32 (&rf->pcq_rd_ptr, val);
   ia_put32 (&rf->pcq_wr_ptr, val);

/*
 * Initialize Exception Queue.
 */

   mem_brd = softc->brd_regs_ph.rfred_mem + R_EXCP_START;
   val = mem_brd & 0xffff;
   ia_put32 (&rf->excp_st_adr, val);
   ia_put32 (&rf->excp_ed_adr, val + (NUM_RX_EXCP * sizeof(R_ERROR_Q)));
   ia_put32 (&rf->excp_rd_ptr, val);
   ia_put32 (&rf->excp_wr_ptr, val);

/*
 * Initialize the Reassembly Table
 *
 * Currently VC only assembly is done. So the number of
 * entries needed in the Reassembly table is same as the
 * size of VC table.
 */

   mem_brd = softc->brd_regs_ph.rfred_mem + R_REASSM_START;
   ia_put32 (&rf->reass_base, (mem_brd >> 16) & 0xff);
   ptr16 = (ushort_t *) (softc->brd_regs.rfred_mem + R_REASSM_START);
   for (i=0; i < R_REASSM_SIZE; i++)
   {
      if (ia_aal5)
         ia_putw (ptr16++, AALTYP_5);
      else
         ia_putw (ptr16++, AALTYP_3);
   }

/*
 * Initialize the VC table.
 */

   mem_brd = softc->brd_regs_ph.rfred_mem + R_VC_START;
   val = 0;
   i = 64 * 1024;
   while (i != R_NUM_VC)
   {
      i /= 2;
      val++;
   }
   val |= ((mem_brd >> 8) & 0xfff8);
   ia_put32 (&rf->vc_lkup_base, val);

   mem_brd = softc->brd_regs_ph.rfred_mem + R_REASSM_START;
   ptr16 = (ushort_t *) (softc->brd_regs.rfred_mem + R_VC_START);
   for (i=0; i < R_NUM_VC; i++)
   {
      ia_putw (ptr16++, mem_brd & 0xffff);
      mem_brd += sizeof (ushort_t);
   }

/*
 * Initialize CBR and RAW queue
 *
 * Currently CBR and RAW handling is not provided. The
 * initialization is done here for completeness. The queues
 * are initialized to be in full state.
 */

   buffer_brd = softc->brd_regs_ph.buf_mem + softc->ram_size - RX_CBR_RAM;
   ia_put32 (&rf->cbr_base_adr, (buffer_brd >> 16) & 0xffff);

   val = buffer_brd & 0xffff;
   ia_put32 (&rf->cbr_st_adr, val);
   ia_put32 (&rf->cbr_ed_adr, val + 64);
   ia_put32 (&rf->cbr_rd_ptr, val);
   ia_put32 (&rf->cbr_wr_ptr, val);

   buffer_brd = softc->brd_regs_ph.buf_mem + softc->ram_size - RX_RAW_RAM;
   val = buffer_brd & 0xffff;
   ia_put32 (&rf->raw_st_adr, val);
   ia_put32 (&rf->raw_ed_adr, val + 64);
   ia_put32 (&rf->raw_rd_ptr, val);
   ia_put32 (&rf->raw_wr_ptr, val);

/*
 * Initialize packet aging related registers
 *
 * Set packet timeout to occur in about 30 seconds.
 * Set Packet Aging Interval count register to overflow in
 * about 50 us. [ (0x1000-0xC00) * 50 ns)  ~=  50 us ]
 *
 * 40 us * 64K ~= 3 s.
 * 3 s * (0x100 - 0xF6) ~= 30 sec.
 */

/* ia_put32 (&rf->pkt_tm_cnt, 0xF6C0); */
/* ia_put32 (&rf->pkt_tm_cnt, 0xF6FC); */
   ia_put32 (&rf->pkt_tm_cnt, 0xF6FE);

   ptr16 = (ushort_t *)(softc->brd_regs_ph.rfred_mem + R_REASSM_START);
   i = ((u_int)ptr16 >> 9) & 0xff;
   ptr16 += R_REASSM_SIZE - 1;
   i |= (((u_int)ptr16 >> 1) & 0xff00);
   ia_put32 (&rf->tmout_range, i);

/*
 * Set VP Filter register
 * Only VC reassembly
 */
   ia_put32 (&rf->vp_filter, 0xff00);

/*
 * Set ATM F5 check register
 * NOT USED
 */
   ia_put32 (&rf->qamcc_chk, 0xff00);

/*
 * Copy real rfred registers into the local copy
 */
   for (i=0; i<(sizeof (rfred_t))/4; i++)
      ((uint_t *)rfL)[i] = ia_get32(&(((uint_t *)rf)[i])) & 0xffff;

/*
 * Set mode register 0 and 1.
 * Set mask register.
 * Set 16 bit interface for OC-3 PHY
 */
   ia_put32 (&rf->mode_reg_0, R_CM_WAIT_EN |
              R_PM_REQADR |
              R_CI_BNDRY_CHK |
              R_CI_WIDTH16);

   ia_put32 (&rf->mode_reg_1, R_PM_LENDIAN |
              R_PM_CYCST_ACT |
              R_CM_CYCST_ACT |
              R_CC_GFC_EN |
              R_CC_OAMF5_EN |
              R_COSET_EN);

   ia_or32 (&rf->mode_reg_0, R_ON_LINE);
   i = ia_get32 (&rf->intr_status_reg);
/* ia_put32 (&rf->mask_reg, R_SML_FREEQ_MT); */
   ia_put32 (&rf->mask_reg, 0);

   printk("IA 5515: RFred initialization completed \n");
#ifdef DBG_INIT
   printk("IA 5515: RFred state register is %x \n", rf->state_reg);
#endif
   return   IA_SUCCESS;
}


ia_put_regs(
ia_softc_t *softc)
{
   rfred_t *rf = softc->brd_regs.rfred;
   unsigned short rp;
   unsigned short wp;
   rp = ia_getw(&rf->lrg_rd_ptr);
   wp = ia_getw(&rf->lrg_wr_ptr);
   printk("lrg rp = %x lrg wp = %x \n", rp, wp);
   rp = ia_getw(&rf->pcq_rd_ptr);
   wp = ia_getw(&rf->pcq_wr_ptr);
   printk("pcq rp = %x pcq wp = %x \n", rp, wp);
   rp = ia_getw(&rf->state_reg);

/* Don't even think about it... reading this reg */
/* will clear any pending interrupts.            */
/* wp = ia_get32(&rf->intr_status_reg) & 0xffff; */

   printk("state = %x \n", rp);
}

/*F
 * FUNCTION: ia_rfred_handler
 *
 * DESCRIPTION:
 * This routine is called to do R-fred processing. It reads
 * R-fred interrupt status register and the state register
 * to determine what needs to be done.
 *
 * ARGUMENTS:
 * softc    Device state structure
 *
 * ENVIRONMENT:
 * Called from main interrupt handler ia_intr()
 *
 * RETURN: nothing
 *
 * NOTES:
 *
 */

static int min_free = 1000;

void
ia_rfred_handler(
ia_softc_t *softc)
{
   rfred_t *rf = softc->brd_regs.rfred;
   rfred_t *rfL = &softc->brd_regs_ll.rfred;
   ushort_t    state;
   int n;
   ulong_t     ptr;
   ulong_t     ptr1;
   ulong_t     ptr2;
   ulong_t     fdc;
   ulong_t     status;

/* Begin by getting the interrupt status   */

   status = ia_get32(&rf->intr_status_reg) & 0xffff;

/* Here we determine how many free buffers live in the */
/* the receive free queue.. The answer, unfortunately, */
/* always appears to be either plenty or 0.            */

#if 0
   ptr = ia_get32 (&rf->lrg_rd_ptr);
   ptr1 = rfL->lrg_wr_ptr - rfL->lrg_st_adr;
   ptr2 = (ptr & 0xffff)  - rfL->lrg_st_adr;

   if (ptr1 < ptr2)
      ptr1 += rfL->lrg_ed_adr - rfL->lrg_st_adr;

   fdc = (ptr1 - ptr2) >> 1;
   if (fdc < 28)
   {
      printk("IA 5515: RFL size is now %d \n", fdc);
      printk("Ptr1 is %x and Ptr2 is %x \n", ptr1, ptr2);
   }
#endif

/*
 * Packet received
 */
   if (status & R_PKT_RCVD)
   {
      state = ia_get32 (&rf->state_reg) & 0xffff;
#ifdef DBG_RECV
      printk("IA 5515: RFred reception. State is %x \n", state);
#endif
      while (!(state & R_PCQ_EMPTY))
      {
         if (ia_proc_rcv_pkt (softc) == IA_FAIL)
         {
            printk("IA 5515: proc rcvd packet returned errror \n");
            break;
         }
         state = ia_get32 (&rf->state_reg) & 0xffff;
      }
   }

/* If the descriptor queue is empty then the packet receive
 * queue is probably full!!! Adding this stuff here seems
 * to have removed a nasty hang on full PRC empty LFQ
 */

   if (status & R_LRG_FREEQ_MT)
   {
      state = ia_get32 (&rf->state_reg) & 0xffff;

#ifdef DBG_RECV
      printk("IA 5515: RFred MT. State is %x \n", state);
      ia_put_regs(softc);
#endif

      while (!(state & R_PCQ_EMPTY))
      {
         if (ia_proc_rcv_pkt (softc) == IA_FAIL)
         {
            printk("IA 5515: procMT rcvd packet returned errror \n");
            break;
         }
         state = ia_get32 (&rf->state_reg) & 0xffff;
      }
   }

/*
 * Quick check to see if any other status needs handling
 */
   if ((status & ~R_PKT_RCVD) == 0)
      return;

/* Exception received */

   if (status & R_EXCP_RCVD)
   {
      state = ia_get32 (&rf->state_reg) & 0xffff;
      printk("IA 5515: RFred exception at %d.%06d. State is %x Status is %x\n",
                       softc->tod->tv_sec, softc->tod->tv_usec, state, status);

      if (softc->logrx == 0)
         softc->logrx = 1;

#ifdef DBG_RECV
      ia_put_regs(softc);
#endif
      while (!(state & R_EXCPQ_EMPTY))
      {
          ia_proc_rcv_excpt (softc);
          state = ia_get32 (&rf->state_reg) & 0xffff;
      }
   }

/* Dropped packet counter overflow */

   if (status & R_PKT_CTR_OF)
   {
      printk("IA 5515: RFred dropped packet counter overflow \n");
      softc->rfred_stat.drop_rxpkt += 0x10000;
      softc->rfred_stat.drop_rxpkt += ia_get32 (&rf->drp_pkt_cntr) &
                           0xffff;
   }

/* Error counter */

   if (status & R_ERR_CTR_OF)
   {
      printk("IA 5515: RFred dropped cell counter overflow \n");
      softc->rfred_stat.drop_cell += 0x10000;
      softc->rfred_stat.drop_cell += ia_get32 (&rf->err_cntr) & 0xffff;
   }

/* Dropped CBR cell counter overflow */

   if (status & R_CBR_CTR_OF)
   {
      printk("IA 5515: RFred CBR cell counter overflow \n");
      softc->rfred_stat.drop_cbr += 0x10000;
      softc->rfred_stat.drop_cbr += ia_get32 (&rf->drp_cbr_cntr) & 0xffff;
   }

/* Received cell counter overflow */
   if (status & R_CELL_CTR_OF)
   {
   /* TODO should be 64 bit entity */
   /* printk("IA 5515: RFred Cell counter overflow \n"); */
      softc->rfred_stat.rx_cell += ia_get32 (&rf->cell_ctr0) & 0xffff;
      softc->rfred_stat.rx_cell += ia_get32 (&rf->cell_ctr1) << 16;
   }

/* Large free desc queue was empty, one or more packet dropped */
   if (status & R_LRG_FREEQ_MT)
   {
#ifdef DBG_RECV
      printk("IA 5515: RFred dropped packet.. Large FQ empty \n");
      ia_put_regs(softc);
#endif
      softc->rfred_stat.large_freeqmt++;
   }

/* Small free desc queue was empty, one or more packet dropped */
   if (status & R_SML_FREEQ_MT)
   {
      printk("IA 5515: RFred dropped packet.. Small FQ empty \n");
      softc->rfred_stat.small_freeqmt++;
   }

/* Exception Q full, one or more errors dropped */
   if (status & R_EXCPQ_FL_I)
   {
   /* printk("IA 5515: RFred dropped packet.. Exception FQ empty \n"); */
      state = ia_get32 (&rf->state_reg) & 0xffff;
#ifdef DBG_RECV
      printk("IA 5515: RFred exception. State is %x \n", state);
      ia_put_regs(softc);
#endif
      while (!(state & R_EXCPQ_EMPTY))
      {
          ia_proc_rcv_excpt (softc);
          state = ia_get32 (&rf->state_reg) & 0xffff;
      }
      softc->rfred_stat.excp_full++;
   }

/* Sync cell received */
   if (status & R_CBR_RCVD & ia_debug)
   {
      printk("IA 5515: RFred received sync cell \n");
   }

/* Raw cell received */
   if (status & R_RAW_RCVD & ia_debug)
   {
      printk("IA 5515: RFred received raw cell \n");
   }

#ifdef DBG_RECV
   ptr = ia_get32 (&rf->lrg_rd_ptr);
   ptr1 = rfL->lrg_wr_ptr - rfL->lrg_st_adr;
   ptr2 = (ptr & 0xffff)  - rfL->lrg_st_adr;

   if (ptr1 < ptr2)
      ptr1 += rfL->lrg_ed_adr - rfL->lrg_st_adr;


   fdc = (ptr1 - ptr2) >> 1;
   if (fdc < 28)
   {
      printk("IA 5515: End RFL size is now %d \n", fdc);
      printk("Ptr1 is %x and Ptr2 is %x \n", ptr1, ptr2);
   }
#endif

}

/**/
/* Allocate a Linux SK buffer */

int ia_get_rx_skb(
ia_softc_t     *softc,
int            vci,
int            data_len,
struct sk_buff **skbadloc)
{
   struct atm_vcc *vcc;
   struct sk_buff *skb;

#ifdef DBG_RECV
   printk("IA 5515: get_rx_skb for vci %d len %d \n", vci, data_len);
   printk("IA 5515: get_vcc returned %x \n", vcc);
#endif

/* Recover the linux VCC stucture associated with the vci */
/* If the vcc is zero then the vci is not open and we     */
/* just want to dump the packet.                          */

   ia5515_getvcc(vci, &vcc);
   if (vcc == 0)
   {
      return(IA_FAIL);
   }

/* drop_sig_pkt can be set by a diagnostic/test routine to */
/* force the dropping of a single signaling packet         */

   if ((vcc->vci == 5) && drop_sig_pkt)
   {
       drop_sig_pkt = 0;
       printk("IA 5515: Dropped sig rcv at %d.%06d \n",
                        softc->tod->tv_sec, softc->tod->tv_usec);
      return(IA_FAIL);
   }

/* Attempt to allocate a buffer */

#if LINUX_VERSION_CODE >= 0x20312
   if (!(skb = atm_alloc_charge(vcc, data_len, GFP_ATOMIC)))
   {
#else
   if (atm_charge(vcc, atm_pdu2truesize(data_len)))
   {
      skb = alloc_skb(data_len, GFP_ATOMIC);
      if (!skb)
      {
          printk("IA 5515: No skb... packet dropped \n");
          vcc->stats->rx_drop++;
          atm_return(vcc, atm_pdu2truesize(data_len));
          return(IA_FAIL);
      }
   }
   else
   {
      printk("IA: Rx over the rx_quota %ld\n", vcc->rx_quota);
#endif
      if (vcc->vci < 32)
         printk("IA 5515: Dropping packets on %d\n", vcc->vci);
      return(IA_FAIL);
   }
   *skbadloc = skb;
   return(IA_SUCCESS);
}

/*F
 * FUNCTION: ia_proc_rcv_pkt
 *
 * DESCRIPTION:
 * Process received packets. Get completed descriptors from the
 * Packet Complete Queue. If packet looks good, call ia_set_up_dma()
 * to setup flipper to move the packet data from the board memory to
 * the host memory. If the packet is being dropped, place the
 * completed descriptor into the Large Packet Ready Queue.
 *
 * ARGUMENTS:
 * softc    Device state structure
 *
 * ENVIRONMENT:
 * Called from ia_rfred_handler to process received packets
 *
 * RETURN:
 * IA_FAIL     If no host receive buffers available
 * IA_SUCCESS  Otherwise
 *
 * NOTES:
 *
 */
static int
ia_proc_rcv_pkt (ia_softc_t *softc)
{
   rfred_t *rf    = softc->brd_regs.rfred;
   rfred_t *rfL   = &softc->brd_regs_ll.rfred;
   ushort_t pcqrp =  rfL->pcq_rd_ptr;
   ushort_t    desc_num;
   r_buf_desc  *desc;
   ushort_t    status;
   int         drop_it = 0;
   int         ret;
   uint_t      val;
   int         n;

/* If the read pointer and the write pointer are the same here, */
/* we have a SERIOUS problem.                                   */

   val = ia_get32 (&rf->pcq_wr_ptr);
   if (pcqrp == (val & 0xffff))
   {
      printk("IA 5515: Fatal pcqrp error \n");
      return;
   }

/* Get the next descriptor in Packet Complete Queue */

   desc_num = ia_getw((ushort_t *)(softc->brd_regs.rfred_mem
                                               + rfL->pcq_rd_ptr));
   desc_num &= 0x1fff;
   desc = (r_buf_desc *)(softc->brd_regs.rfred_mem + R_DESC_START);
   desc += desc_num;

#ifdef DBG_RECV
   printk("IA 5515: Receive interrupt on vci %d \n", desc->vci);
   printk("IA 5515: Processing recv for desc num %d \n", desc_num);
#endif

/*
 * Update local and real PCQ read pointer
 */

   rfL->pcq_rd_ptr += 2;
   if (rfL->pcq_rd_ptr > rfL->pcq_ed_adr)
      rfL->pcq_rd_ptr = rfL->pcq_st_adr;

   ia_put32 (&rf->pcq_rd_ptr, rfL->pcq_rd_ptr);

/*
 * If no host receive buffer available, drop the packet.
 * This should NEVER occur in practice since there should be
 * a one to one correspondence between host buffer structures
 * and receive buffers on the board.
 */

   if (!softc->rx_buf_free)
   {
      printk("IA 5515: rx_buf_free is null.. packet dropped \n");
   /*
    * The stuff that is commented out here is discussed below..
    */
#if 0
      ia_or32 (&rf->mask_reg, R_EXCP_RCVD | R_PKT_RCVD | R_LRG_FREEQ_MT);
      softc->rx_state |= RX_NO_BUFS;
#endif
      softc->rfred_stat.rx_no_host_buf++;
      ia_free_desc(softc, desc, (ia_rx_buf_t *)0);
      return IA_FAIL;
   }


   status = ia_getw(&desc->desc_stat);

#ifdef DBG_RECV
   printk("IA 5515: Descriptor status is %x \n", status);
#endif

   if (status & R_CNG)
      softc->rfred_stat.cng++;

/*
 * Save error stats
 */
   status = (ushort_t) (status & R_PKT_ERROR);
   if (status)
   {
      printk("IA 5515: Packet error status is %x \n", status);
      drop_it++;
#ifdef __SNMP__
      softc->ia_ierrors[ATM_INTERFACE]++;
#endif __SNMP__
      if (status & R_CPE)
      {
         if (status & R_CF)
            softc->rfred_stat.cf++;
         if (status & R_CCE)
            softc->rfred_stat.cce++;
         if (status & R_CSE)
            softc->rfred_stat.cse++;
         if (status & R_SQE)
            softc->rfred_stat.sqe++;
      }
      else
      {
         if (status & R_EOF)
            softc->rfred_stat.eof++;
         if (status & R_PTE)
         {
            softc->rfred_stat.pte++;
#ifdef __SNMP__
            softc->rfred_stat.new_pte[desc->vci & R_VC_MASK]++;
#endif __SNMP__
         }
         if (status & R_CER)
            if (ia_aal5)
            {
               softc->rfred_stat.cer++;
#ifdef __SNMP__
               softc->rfred_stat.new_cer[desc->vci & R_VC_MASK]++;
#endif __SNMP__
            }
            else
#if (IGNORE_R_CER == 0)
            {
               softc->rfred_stat.cer++;
#ifdef __SNMP__
               softc->rfred_stat.new_cer[desc->vci & R_VC_MASK]++;
#endif __SNMP__
            }
#else
               drop_it = 0;
#endif
         if (status & R_OFL)
         {
            softc->rfred_stat.ofl++;
#ifdef __SNMP__
            softc->rfred_stat.new_ofl[desc->vci & R_VC_MASK]++;
#endif __SNMP__
         }
      }
      if (status & R_PEP)
         softc->rfred_stat.pep++;
      if (status & R_BADDESC)
         softc->rfred_stat.baddesc++;
   }

   if (drop_it)
   {
      printk("Dropping packet \n");
      ia_free_desc (softc, desc, (ia_rx_buf_t *)0);
      return IA_SUCCESS;
   }

   softc->rx_buf_free->desc = desc;
   ret = ia_set_up_dma (softc, desc);
   if (ret == IA_FAIL)
      ia_free_desc (softc, desc, (ia_rx_buf_t *)0);

   return IA_SUCCESS;
}

/*F
 * FUNCTION: ia_set_up_dma
 *
 * DESCRIPTION:
 * Setup flipper to DMA the packet data from the board memory
 * to the host memory. Get a free host buffer to DMA the
 * packet to. Save the VC number and packet length for later.
 * Build DLE and kick the flipper. Place the host buffer on the
 * DMA list.
 *
 *
 * ARGUMENTS:
 * softc    Device state structure
 * desc     Buffer descriptor pointer of received packet
 *
 * ENVIRONMENT:
 * Called from ia_proc_rcv_pkt().
 *
 * RETURN: nothing
 *
 * NOTES:
 *
 */
static int
ia_set_up_dma (ia_softc_t *softc, r_buf_desc *desc)
{
   ia_dle_list_t  *list = &softc->list[RX_LIST];
   struct sk_buff *skb;
   ia_rx_buf_t    *rx_buf;
   DLE            *dle;
   ulong_t        buf_addr;
   ulong_t        dma_addr;
   size_t         len;
   int            status;

   buf_addr = ia_getw(&desc->buf_high) << 16 | ia_getw (&desc->buf_low);
   dma_addr = ia_getw(&desc->dma_high) << 16 | ia_getw (&desc->dma_low);
   len = dma_addr - buf_addr;

#ifdef DBG_RECV
   printk("IA 5515: DMA setup buf = %x dma = %x len = %x \n",
                      buf_addr, dma_addr, len);
#endif

   if (len > RX_BUF_SIZE)
   {
#ifdef __SNMP__
      softc->ia_norcvbuf[ATM_INTERFACE]++;
#endif __SNMP__
      printk("IA 5515: \
         ia_set_up_dma_n: bad len %d (desc len %d) (vci %d)",
         len, ia_getw (&desc->length), ia_getw (&desc->vci));
      return IA_FAIL;
   }

   status = ia_get_rx_skb(softc, desc->vci & R_VC_MASK, len, &skb);
   if (status == IA_FAIL)
      return(IA_FAIL);


/*
 * Get a free receive buffer from the free queue.
 */
   rx_buf = softc->rx_buf_free;
   softc->rx_buf_free = rx_buf->next;

/*
 * Save the length of this packet.
 */
   rx_buf->len = len;
   rx_buf->skb = skb;
/* printk("RXB is %x SKB is %x  Len is %d \n",
             rx_buf, skb, len);      */

/*
 * Build DLEs to transfer the data to the board
 */
   dle = list->list_write;

   dle->dle_local_addr = buf_addr;
   dle->dle_sys_addr = virt_to_bus(skb->data);
   dle->dle_count = len;
   dle->dle_mode  = DLE_INT_ENABLE;

   if (++dle == list->list_end)
       dle = list->list_start;

   list->list_write = dle;

   rx_buf->next = NULL;
   if (!softc->rx_buf_dmahead)
      softc->rx_buf_dmahead = rx_buf;
   else
      softc->rx_buf_dmatail->next = rx_buf;
   softc->rx_buf_dmatail = rx_buf;

#ifdef DBG_RECV
   printk("IA 5515: DMA buf head is now %x desc %x \n",
               softc->rx_buf_dmahead->addr, rx_buf->desc);
#endif

   softc->driver_stat.rx_buf_on_dma_q++;

/*
 * Kick off the DMA by incrementing the transection counter
 */

   ia_put32(softc->brd_regs.rx_tc, 1);

   return IA_SUCCESS;
}

/*F
 * FUNCTION: ia_host_rx_intr
 *
 * DESCRIPTION:
 * This routine is called to process DLE list 1 (Rx list)
 * interrupts. This interrupt occures after DMA IOP has moved
 * packet(s) to the host memory. Go through all the DLEs that
 * have been processed. For each completed DLE there is an rx
 * buffer on the DMA rx buffer chain. Send the packets associated
 * with the rx buffer upstream.
 *
 * ARGUMENTS:
 * softc    Device state structure
 *
 * ENVIRONMENT:
 *
 * RETURN: nothing
 *
 * NOTES:
 *
 */
void
ia_host_rx_intr(
ia_softc_t *softc)
{
   ia_flip_t      *flip = softc->brd_regs.flipper;
   ia_dle_list_t  *list = &softc->list[RX_LIST];
   rfred_t        *rf = softc->brd_regs.rfred;
   ushort_t       state;
   ia_rx_buf_t    *rx_buf;
   DLE            *current_dle;
   DLE            *dle;
   size_t         len;
   uint_t         dle_offset;
   uint_t         dle_offset1;
   CS5_TRAILER    *trl5;
   int            data_len;
   int            i;
   int            toc;
   int            status;
   struct atm_vcc *vcc;
   struct sk_buff *skb;

/*
 * Read the offset into the DLE list.
 * DLE receive list register is not de-bounced. Read it until
 * two consecutive values are the same to be sure that the right
 * value is read.
 * NOTE: In almost all the cases only two reads of the receive
 *       list register will be required.
 */
   dle_offset = ia_get32 (&flip->fl_receive_list) & (SIZE_DLE_SPACE - 1);
   dle_offset1 = ia_get32 (&flip->fl_receive_list) & (SIZE_DLE_SPACE - 1);
   while (dle_offset != dle_offset1)
   {
      dle_offset = dle_offset1;
      dle_offset1 =
         ia_get32 (&flip->fl_receive_list) & (SIZE_DLE_SPACE - 1);
   }

   current_dle = (DLE *)((u_long)list->list_start + dle_offset);

#ifdef DBG_RECV
   printk("IA 5515: Recv DLE offset is %x \n", dle_offset);
   printk("         DLE sys_addr is %x \n", current_dle->dle_sys_addr);
#endif

   dle = list->list_read;
   while (dle != current_dle)
   {

#ifdef DBG_RECV
      printk("IA 5515: DLE is %x -- current DLE is %x \n",
                dle, current_dle);
#endif

   /* This stuff is to support the case in which the DMA transfers
    * employ multiple DLE's.. Only the last one will be int enabled
    */
      if (dle->dle_mode != DLE_INT_ENABLE)
      {
         printk("IA5515: Yeow... DLE w/o INT_ENABLE \n");
         if (++dle == list->list_end)
            dle = list->list_start;
         continue;
      }

   /* The dmahead pointer is the head of a list of rx buffer
    * structures that have been scheduled for DMA transfer
    */
      rx_buf = softc->rx_buf_dmahead;
      if (rx_buf == 0)
      {
         printk("IA 5515: Yeow... dma head rx_buf was 0! \n");
         return;
      }

#ifdef DBG_RECV
      printk("IA_5515: Consuming buffer at %x desc %x \n", rx_buf->addr,
                       rx_buf->desc);
#endif

   /*
    * Get the received packet
    */
      softc->rx_buf_dmahead = softc->rx_buf_dmahead->next;
      rx_buf->next = NULL;
      len = rx_buf->len;

      trl5 = (CS5_TRAILER *)(rx_buf->skb->data + len - sizeof(CS5_TRAILER));
      data_len = swap(trl5->length);
   /* if ((softc->tv.tv_sec  - softc->lastrx.tv_sec) > 4) */
      if (softc->logrx)
      {
         do_gettimeofday(&softc->tv);
         printk("IA 5515: %d.%06d rcv (%d, %d) on vci %d last at %d.%06d \n",
              softc->tv.tv_sec, softc->tv.tv_usec, len,
              data_len, rx_buf->desc->vci & R_VC_MASK,
              softc->lastrx.tv_sec, softc->lastrx.tv_usec);
         softc->logrx = 0;
      }
      softc->lastrx.tv_sec = softc->tv.tv_sec;
      softc->lastrx.tv_usec = softc->tv.tv_usec;

   {
   unsigned long  *wloc;
   struct sk_buff  *skb;

             skb = rx_buf->skb;
             wloc = (unsigned long *)skb->data;
             wloc += 4 + 5 + 2;
             if (ntohl(*wloc) == 0x1a2b3c4d)
             {
                int seq;
                wloc += 2;
                seq = ntohl(*wloc);
                if (seq < lastseq)
                {
                   printk("OOO in recv %d %d \n", seq, lastseq);
                }
                else if (seq != lastseq)
                {
                   printk("Drops in recv %d %d \n", seq, lastseq);
                }
            /*  printk("Seq is %d \n", seq);  */
                lastseq = seq + 1;
             }
    }

#ifdef DBG_RECV

      if ((rx_buf->desc->vci & R_VC_MASK) == 5)
      {
         do_gettimeofday(&softc->tv);
         printk("IA 5515: %d.%06d rcv (%d, %d) on vci %d \n",
              softc->tv.tv_sec, softc->tv.tv_usec, len,
              data_len, rx_buf->desc->vci & R_VC_MASK);
      }

      printk("IA 5515: Received (%d, %d) bytes on vci %d \n", len,
              data_len, (u_int)(ia_getw(&rx_buf->desc->vci) & R_VC_MASK));

      printk("IA 5515: received message is:" );
      for (i = 0; i < data_len; i++)
      {
         if ((i %16) == 0)
            printk("\n");
         printk("%02x ", (unsigned char)rx_buf->skb->data[i]);
      }
      printk("\n");
#endif


#ifdef DBG_RECV
      printk("host int: Rxb is %x skb is %x \n", rx_buf, rx_buf->skb);
#endif

   /* Recover skb address and length from buffer descriptor */

      skb = rx_buf->skb;
      skb->len = data_len;
      softc->driver_stat.rx_packets++;
      softc->driver_stat.rx_bytes += len;

      ia5515_getvcc(rx_buf->desc->vci & R_VC_MASK, &vcc);

      if (vcc != NULL)
      {
         if (vcc->push != NULL)
         {
            ATM_SKB(skb)->vcc = vcc;      /* Heikki Vatianinen */
            ATM_SKB(skb)->iovcnt = 0;
            skb->len = data_len;

#ifdef DBG_RECV
            printk("Push address is %x \n", vcc->push);
#endif
            vcc->push(vcc, skb);
          }
      }
      ia_free_desc(softc, rx_buf->desc, rx_buf);


done:
      if (++dle == list->list_end)
         dle = list->list_start;
   }

   list->list_read = dle;

#if 0
/*
 * The following code in the Solaris driver appears to have been
 * designed to do the following.  When no host buffers are
 * available for dma-ing the packets off the board, the solaris
 * driver tried to keep buffering them up on the board (-> more
 * board space than host space). Then it trys to transfer 'em
 * en masse when the buffer shortage clears.  Since the board
 * buffers appear to be a scarcer resource that the host buffers
 * I just throw 'em away when the arrive at the board and so
 * this stuff is not needed.
 *
 * If receive packet (and exception) interrupts blocked
 * because of lack of host buffers, enable them now.
 */
   if ((softc->rx_state & RX_NO_BUFS) && (softc->rx_buf_free) )
   {
      rfred_t     *rf = softc->brd_regs.rfred;
      ushort_t state;

      ia_and32 (&rf->mask_reg, ~(R_EXCP_RCVD | R_PKT_RCVD | R_LRG_FREEQ_MT));
      softc->rx_state &= ~RX_NO_BUFS;

      /*
       * In this state R_PCQ_EMPTY interrupt will not
       * be generated. Process all the packets in the
       * PCQ here.
       */
       state = ia_get32 (&rf->state_reg) & 0xffff;
       while (!(state & R_PCQ_EMPTY))
       {
          if (ia_proc_rcv_pkt (softc) == IA_FAIL)
            break;
           state = ia_get32 (&rf->state_reg) & 0xffff;
      }
   }
#endif

/* This code is included to reduce latency in handling ready */
/* packets and to defend against the possiblity of a lost    */
/* interrupt for a ready packet.                             */

   state = ia_get32 (&rf->state_reg) & 0xffff;
   while (!(state & R_PCQ_EMPTY))
   {
       if (ia_proc_rcv_pkt (softc) == IA_FAIL)
          break;
       state = ia_get32 (&rf->state_reg) & 0xffff;
   }
}


/**/
/* Ioctl level standalone driver packet receive routine */

int  ia_recv5u(
ia_softc_t *softc,
int   vcid,          /* logical channel id. */
char *message,       /* Pointer to data.    */
int  *len)           /* Length of message   */
{
   ia_pvc_desc_t *pvc;
   ia_rx_buf_t   *rx_buf;
   CS5_TRAILER   *trl5;

   pvc = softc->pvctab + vcid;

   while (pvc->rdybufhead == NULL)
   {
#ifdef DBG_RECV
      printk("ia_recv5u: Sleeping on buffer queue empty \n");
#endif
      interruptible_sleep_on(&pvc->input_wait);
      if (signal_pending(current))
      {
         printk("ia_recv5u: Returning on sigint \n");
         return(-10);
      }
   }

   rx_buf = pvc->rdybufhead;
   pvc->rdybufhead = rx_buf->next;
   trl5 = (CS5_TRAILER *)(rx_buf->addr + rx_buf->len - sizeof(CS5_TRAILER));
   *len = swap(trl5->length);
   memcpy(message, rx_buf->addr, *len);
   ia_free_desc(softc, rx_buf->desc, rx_buf);
}

/**/
/* Print the contents of the free host buffer structure list */

void ia_dump_rx_free(
ia_softc_t *softc)
{
   ia_rx_buf_t *rx_buf;
   int c = 0;

   printk("IA 5515: Free Rx List \n");
   rx_buf = softc->rx_buf_free;
   while (rx_buf != 0)
   {
       printk("   %08x \n", rx_buf->addr);
       rx_buf = rx_buf->next;
       if (++c > softc->num_rx_desc)
       {
          printk("IA 5515: Free buffer list is corrupt \n");
          break;
       }
   }
}

/**/
/* Print the contents of the free host buffer structure list */

int ia_count_rx_free(
ia_softc_t *softc)
{
   ia_rx_buf_t *rx_buf;
   int c = 0;

/* printk("IA 5515: Free Rx List \n");  */
   rx_buf = softc->rx_buf_free;
   while (rx_buf != 0)
   {
       rx_buf = rx_buf->next;
       if (++c > softc->num_rx_desc)
       {
          printk("IA 5515: Free buffer list is corrupt \n");
          break;
       }
   }
   return(c);
}

/*F
 * FUNCTION: ia_free_desc
 *
 * DESCRIPTION:
 * This routine frees a descriptor into the Large Free Descriptor
 * queue.
 *
 * ARGUMENTS:
 * softc    Device state structure
 * desc     Pointer to the decsriptor being freed
 *
 * ENVIRONMENT:
 * Called from ia_proc_rcv_pkt() and ia_host_rx_intr().
 *
 * RETURN: nothing
 *
 * NOTES:
 *
 */
static void
ia_free_desc (
ia_softc_t   *softc,
r_buf_desc   *desc,
ia_rx_buf_t  *rx_buf)
{
   rfred_t      *rf = softc->brd_regs.rfred;
   rfred_t      *rfL = &softc->brd_regs_ll.rfred;
   int          desc_num;
   unsigned     short rp;
   unsigned     short wp;

/* First put the descriptor back on the large free buffer queue */

   ia_putw(&desc->desc_stat, R_LARGE);

   desc_num = desc - (r_buf_desc *)(softc->brd_regs.rfred_mem);

#ifdef DBG_RECV
   ia_put_regs(softc);
   printk("IA 5515: Freeing Rx descriptor number %d \n", desc_num);
   {
      rp = ia_getw(&rf->lrg_rd_ptr);
      wp = ia_getw(&rf->lrg_wr_ptr);
      printk("   rp = %x wp = %x \n", rp, wp);
   }
#endif
   ia_putw ((ushort_t *)(softc->brd_regs.rfred_mem + rfL->lrg_wr_ptr), desc_num);

   rfL->lrg_wr_ptr += 2;
   if (rfL->lrg_wr_ptr > rfL->lrg_ed_adr)
      rfL->lrg_wr_ptr = rfL->lrg_st_adr;

   rp = ia_getw(&rf->lrg_rd_ptr);
   if (rp ==  rfL->lrg_wr_ptr)
      printk("YYEEEOOWW --- write pointer wrap \n");
   ia_put32 (&rf->lrg_wr_ptr, rfL->lrg_wr_ptr);

   if (rx_buf == 0)
      return;

/* Now put the buffer structure on the free buffer list */
/* desc_num - 1 is used because the descriptors are 1   */
/* origin but the buffer table is 0 origin.             */

   if (softc->rx_buf_free == 0)
      softc->rx_buf_free = rx_buf;
   else
      softc->rx_buf_freetail->next = rx_buf;

   softc->rx_buf_freetail = rx_buf;
   rx_buf->next = 0;

#ifdef DBG_RECV
   ia_dump_rx_free(softc);
#endif
}


/*F
 * FUNCTION: ia_proc_rcv_excpt
 *
 * DESCRIPTION:
 * This routine is called to process receive exception conditions.
 * Go through all the entries in the exception queue and update
 * the statistics.
 *
 *
 * ARGUMENTS:
 * softc    Device state structure
 *
 * ENVIRONMENT:
 *
 * RETURN: nothing
 *
 * NOTES:
 *
 */
static void
ia_proc_rcv_excpt (ia_softc_t *softc)
{
   rfred_t     *rf = softc->brd_regs.rfred;
   rfred_t  *rfL = &softc->brd_regs_ll.rfred;
   ushort_t excprp = rfL->excp_rd_ptr;
   R_ERROR_Q   *err_buf;
   uint_t      val;

   val = ia_get32 (&rf->excp_wr_ptr);
   if (excprp == (val & 0xffff))
   {
      printk("IA 5515: Yeow.. excprp error \n");
      return;
   }

   err_buf = (R_ERROR_Q *)(softc->brd_regs.rfred_mem + rfL->excp_rd_ptr);

   val = ia_getw(&err_buf->error);
#ifdef DBG_RECV
   printk("IA 5515: Receive exception code %x \n", val);
#endif

   switch (val & R_EXCPT_MASK)
   {
   case R_OOS_COM:
      softc->rfred_stat.oos_com++;
      break;
   case R_OOS_EOM:
      softc->rfred_stat.oos_eom++;
      break;
   case R_NSLD:
      softc->rfred_stat.nsld++;
      break;
   case R_NLD:
      softc->rfred_stat.nld++;
      break;
   case R_INVLDVC:
      softc->rfred_stat.last_inv_vc = ia_getw (&err_buf->vc_label);
      softc->rfred_stat.invalid_vc++;
      break;
   case R_INVLDVP:
      softc->rfred_stat.last_inv_vp = ia_getw (&err_buf->vc_label);
      softc->rfred_stat.invalid_vp++;
      break;
   default:
      if (ia_debug > 0)
          printk("IA 5515: ia_proc_rcv_excpt: unknown exception %d",
                     val & R_EXCPT_MASK);
   }

/*
 * Update local and real Exception Queue read pointer
 */
   rfL->excp_rd_ptr += 4;
   if (rfL->excp_rd_ptr > rfL->excp_ed_adr)
      rfL->excp_rd_ptr = rfL->excp_st_adr;
   ia_put32 (&rf->excp_rd_ptr, rfL->excp_rd_ptr);
#ifdef DBG_RECV
   printk("IA 5515: Setting excp_rd_ptr to %x \n", rfL->excp_rd_ptr);
#endif


/*
 * No need to free the descriptor???? hmmm??? jmw
 *
 */

}

