#ifndef VCIMULTINIC_COMMON_H
#define VCIMULTINIC_COMMON_H

static int	vcimultinic_intr(void *p);

/// output
/// input
static void	vcimultinic_ifstart(struct ifnet *ifp);
static int	vcimultinic_ifioctl(struct ifnet *ifp, unsigned long cmd, void *data);
static void	vcimultinic_ifstop(struct ifnet *ifp, int disable);
static void	vcimultinic_ifwatchdog(struct ifnet *ifp);
/// drain

static void	vcimultinic_ifpoll(void *v);

///////////////////////////////////////////////////////////////////////////////////////////////////

// Initialize the up/down state change in the device

/* activate */
/* The autoconfiguration framework may call the driver's activate function */
/*      to notify the driver of a change in the resources that have been allo- */
/*      cated to it.  For example, an Ethernet driver has to be notified if the */
/*      network stack is being added or removed from the kernel.  The first argu- */
/*      ment to the activate function self is a pointer to the driver's device */
/*      structure.  It is the same argument as passed to the attach function. */
/*      The second argument act describes the action.  Valid actions are */
/* DVACT_ACTIVATE (activate the device) and DVACT_DEACTIVATE (deactivate the */
/* 							   device).  If the action is not supported the activate function should */
/*      return EOPNOTSUPP.  The DVACT_DEACTIVATE call will only be made if the */
/*      DVACT_ACTIVATE call was successful.  The activate function is called in */
/*      interrupt context. */

/*
**	Interrupt service routine.
**/
static int
//vcimultinic_intr(struct ifnet *ifp)
vcimultinic_intr(void *p)
{
  struct vcimultinic_softc *sc = p;

  sc->sc_intr_count.ev_count++;

#ifdef VCIMULTINIC_DEBUG
  /* aprint_normal_dev(sc->sc_dev, "Interrupted %llu time\n", sc->sc_intr_count.ev_count); */
#endif

  return 1;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

/*
**	Initiate a packet/container to be sent
**/
static void
vcimultinic_ifstart(struct ifnet *ifp)
{
  struct vcimultinic_softc	*sc = ifp->if_softc;
  struct mbuf			*mb_head = NULL;
  uint32_t			reg = 0;
  uint				size = 0;
  uint				padding = 0;
  uint8_t			*buf = sc->tx_buf->container[sc->tx_buf->current_sw_buf];


#ifdef VCIMULTINIC_DEBUG
  aprint_normal_dev(sc->sc_dev, "[%s] Sending a packet/container\n", __func__);
#endif

  ///////////////////////////////////////////

  // Check that the interface is really UP and RUNNING
  // and that we are not in a stopped state (IFF_OACTIVE)
  if ((ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING)
    return;

  ///////////////////////////////////////////

  /*
   * Grab a paquet for output
   */
  IFQ_DEQUEUE(&ifp->if_snd, mb_head);
  if (mb_head == NULL)
    {
#ifdef VCIMULTINIC_DEBUG
      aprint_normal_dev(sc->sc_dev, "[%s] nothing to send\n", __func__);
#endif
      return;
    }

  ///////////////////////////////////////////

  /*
   * Get the size of the next packet to handle
   */
  size = mb_head->m_pkthdr.len;
#ifdef VCIMULTINIC_DEBUG
  aprint_normal_dev(sc->sc_dev, "[%s] packet size is %u\n", __func__, size);
#endif

  ///////////////////////////////////////////

  /*
   * Check if we have to add padding
   */
  if (size < (ETHER_MIN_LEN - ETHER_CRC_LEN)) // We add padding
    {
      padding = ETHER_MIN_LEN - ETHER_CRC_LEN - size;
#ifdef VCIMULTINIC_DEBUG
      aprint_normal("vcimultinic: [%s] we should add %u bytes of padding to that packet (%u + %u = %u)\n", __func__, padding, size, padding, ETHER_MIN_LEN - ETHER_CRC_LEN);
#endif
    }
#ifdef VCIMULTINIC_DEBUG
  else /* We do not add padding */
    {
      aprint_normal("vcimultinic: [%s] we have no padding to add to that packet (%u + %u > %u)\n", __func__, size, padding, ETHER_MIN_LEN - ETHER_CRC_LEN);
    }
#endif

  /*
   * has our buffer enough space?
   */
  if ((size + padding) >= (VMN_CONTAINER_SIZE - sc->tx_buf->wptr))
    { // not enough space, we close that container, send it and write in the next one
#ifdef VCIMULTINIC_DEBUG
      aprint_normal("vcimultinic: [%s] buffer is full, sending buffer and trying to write the packet to the other buffer\n", __func__);
#endif

      // Sending one container
      vmn_chan_send_container(sc, buf);

      // We update buf pointer
      buf = sc->tx_buf->container[sc->tx_buf->current_sw_buf];
    }

  /* padding = (align - (offset mod align)) mod align */
  /* new offset = offset + padding = offset + (align - (offset mod align)) mod align */

  // enough room : yes, a buffer has just been freed
#ifdef VCIMULTINIC_DEBUG
  aprint_normal("vcimultinic: [%s] copying packet from mbuf to our buffer\n", __func__);
#endif
  // Copy the packet from the mbuf to our buffer
  m_copydata(mb_head, 0, size, &(buf[sc->tx_buf->wptr]));
  buf[sc->tx_buf->pkt_ptr]     = (size + padding) & 0x0000FFFF;
  buf[sc->tx_buf->pkt_ptr + 1] = ((size + padding) & 0xFFFF0000) >> 16;
  // incremente the metadata ptr
  sc->tx_buf->pkt_ptr += 2;
  // incremente the write ptr
  sc->tx_buf->wptr += size + padding;
#ifdef VCIMULTINIC_DEBUG
  aprint_normal("vcimultinic: [%s] alignement aligned from %u", __func__, sc->tx_buf->wptr);
#endif
  // (MUST be aligned on 32 bits word)
  sc->tx_buf->wptr += (4 - (sc->tx_buf->wptr % 4)) % 4;
#ifdef VCIMULTINIC_DEBUG
  aprint_normal(" to %u\n", sc->tx_buf->wptr);
#endif

  ///////////////////////////////////////////

  // MUST BE DONE DIFFERENTLY
  /* reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, g_mac4_addr_offset); */
  reg = 0x1;
  ifp->if_opackets += reg & 0x00ffffff;

  return;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

/*
**	IOCTL handler
**/
static int
vcimultinic_ifioctl(struct ifnet *ifp, unsigned long cmd, void *data)
{
  /* struct tl_softc *sc = ifp->if_softc; */
  int s;
  int ret = 1;

/* #ifdef VCIMULTINIC_DEBUG */
/*   aprint_normal("vcimultinic: Entering %s with cmd = %lu\n", __func__, cmd); */
/* #endif */

  s = splnet();
  ret = ether_ioctl(ifp, cmd, data);
  if (ret == ENETRESET)
    {
#ifdef VCIMULTINIC_DEBUG
      aprint_normal("vcimultinic: [%s] : ENETRESET\n", __func__);
#endif
    if (ifp->if_flags & IFF_RUNNING)
      {
#ifdef VCIMULTINIC_DEBUG
	aprint_normal("vcimultinic: [%s] : IFF_RUNNING\n", __func__);
#endif
      }
    ret = 0;
  }

  splx(s);

  return ret;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

/*
**	Stop the interface.
**/
static void
vcimultinic_ifstop(struct ifnet *ifp, int disable)
{
  struct vcimultinic_softc	*sc = ifp->if_softc;
  int s;

#ifdef VCIMULTINIC_DEBUG
  aprint_normal("vcimultinic: Entering %s\n", __func__);
#endif

  /* Stop the clock. */
  callout_stop(&sc->sc_tick_ch);

  // get a lock on the controller
  s = splnet();

  // Desactivate the controller
  ifp->if_flags &= ~IFF_RUNNING;

  // release the lock on the controller
  splx(s);

  return;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

/*
**	May be useful for polling
**/
static void
vcimultinic_ifwatchdog(struct ifnet	*ifp)
{
  /* struct wm_softc *sc = arg; */
  /* struct ifnet *ifp = &sc->sc_ethercom.ec_if; */
  /* int s; */

#ifdef VCIMULTINIC_DEBUG
  aprint_normal("vcimultinic: Entering %s\n", __func__);
#endif

  /* s = splnet(); */

  /* splx(s); */

  return;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

static void
vcimultinic_ifpoll(void *v)
{
  struct vcimultinic_softc	*sc = v;
  uint8_t			*wbuf = sc->tx_buf->container[sc->tx_buf->current_sw_buf];
  uint8_t			*rbuf = sc->rx_buf->container[sc->rx_buf->current_sw_buf];

  /* #ifdef VCIMULTINIC_DEBUG */
  /*   aprint_normal("vcimultinic: Entering %s\n", __func__); */
  /* #endif */

  // check if the container has something to send
  if (sc->tx_buf->wptr != 34 * 4)
    {
      // Sending one container if any to send
      vmn_chan_send_container(sc, wbuf);
    }

  // Getting container if any to receive
  if (vmn_chan_receive_container(sc, rbuf) != 0)
    {
      // We received a container and we have to give its packets to the upper layer
      vmn_chan_unpack_rx_container(sc, rbuf);
    }

  // Reading and updating stats
  vmn_chan_update_ifstats(sc);

  /* read statistics every seconds */
  callout_reset(&sc->sc_tick_ch, MY_HZ, vcimultinic_ifpoll, sc);
}

///////////////////////////////////////////////////////////////////////////////////////////////////


/// Function for GMII link interrupt???

/// Function to reset the interface??? Not supported by the hardware


#endif /* VCIMULTINIC_COMMON_H */
