/**
 * @file avmdma.c
 *
 * AvmDma - common functions for AVM-B1 PCI v4 and AVM-T1 PCI.
 *
 * Copyright: 2000-2003 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: avmdma.c,v 1.1.2.1 2006/02/28 21:35:09 thomas Exp $
 * Project  CAPI for BSD
 * Target   avmaic - CAPI manager driver for AVM active ISDN controllers
 * @date    01.01.2000
 * @author  "Thomas Wintergerst" <twinterg@gmx.de>
 * <p>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * </p>
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");

/* system includes */
#include <sys/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/errno.h>
#include <machine/bus.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/socket.h> /* struct sockaddr for net/if.h */
#include <net/if.h> /* struct if_queue */
#include <vm/vm.h>
#include <vm/pmap.h>
#include <machine/resource.h>
#include <machine/clock.h>

/* import includes */
#include <c4b/kcapimgr/capi_drv.h>

#define __AVMDMA__

/* local includes */
#include <c4b/avmaic/avmaic_var.h>
#include <c4b/avmaic/avmlli.h>
#include <c4b/avmaic/avmaic_misc.h>
#include <c4b/avmaic/avmdma.h>





/* === public definitions ================================================ */





/* === private definitions =============================================== */





/* --- the registers of the AMCC memory mapped i/o chip --- */

#define AVMAIC_AMCC_RXPTR       0x24
#define AVMAIC_AMCC_RXLEN       0x28
#define AVMAIC_AMCC_TXPTR       0x2C
#define AVMAIC_AMCC_TXLEN       0x30

#define AVMAIC_AMCC_INTCSR      0x38
#  define AVMAIC_EN_READ_TC_INT         0x00008000
#  define AVMAIC_EN_WRITE_TC_INT        0x00004000
#  define AVMAIC_EN_TX_TC_INT           AVMAIC_EN_READ_TC_INT
#  define AVMAIC_EN_RX_TC_INT           AVMAIC_EN_WRITE_TC_INT
#  define AVMAIC_AVM_FLAG               0x30000000

#  define AVMAIC_ANY_S5933_INT          0x00800000
#  define AVMAIC_READ_TC_INT            0x00080000
#  define AVMAIC_WRITE_TC_INT           0x00040000
#  define AVMAIC_TX_TC_INT              AVMAIC_READ_TC_INT
#  define AVMAIC_RX_TC_INT              AVMAIC_WRITE_TC_INT
#  define AVMAIC_MASTER_ABORT_INT       0x00100000
#  define AVMAIC_TARGET_ABORT_INT       0x00200000
#  define AVMAIC_BUS_MASTER_INT         0x00200000
#  define AVMAIC_ALL_INT                0x000C0000

#define AVMAIC_AMCC_MCSR        0x3C
#  define AVMAIC_A2P_HI_PRIORITY        0x00000100
#  define AVMAIC_EN_A2P_TRANSFERS       0x00000400
#  define AVMAIC_P2A_HI_PRIORITY        0x00001000
#  define AVMAIC_EN_P2A_TRANSFERS       0x00004000
#  define AVMAIC_RESET_A2P_FLAGS        0x04000000
#  define AVMAIC_RESET_P2A_FLAGS        0x02000000





/* === prototypes for private functions ================================== */





/* initialize the dma engine for normal operation */
static void avmdma_init_for_operation
   (AvmAicSc_t *pSc);

/* enqueue the send init command for a controller */
static unsigned avmdma_send_init
   (AvmAicSc_t *pSc);

/* enqueue the send pollack command for a controller */
static unsigned avmdma_send_pollack
   (AvmAicSc_t *pSc);

/**
 * Start the transfer of pending messages to the controller if not busy.
 */
static void avmdma_start_tx
   (AvmAicSc_t *pSc);

/* receive and handle a pending message from the controller */
static void avmdma_handle_rx
   (AvmAicSc_t *pSc);





/* === definition of public functions ==================================== */





/**
 * Read byte of data from the controller with timeout.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 * @param pucVal                O: The value read if result null.
 *
 * @retval 0                    Byte was successfully read.
 * @retval -1                   Failure.
 */

int avmdma_io_get_byte
   (bus_space_tag_t     iot,
    bus_space_handle_t  ioh,
    unsigned char      *pucVal)
{
   struct timeval tvStart;
   struct timeval tvCurr;
   int            iCurrTime;

   /* wait for some time for filled receive buffer */
   getmicrotime (&tvStart);
   iCurrTime = 0;
   while (! avmdma_io_rx_full (iot, ioh) &&
          iCurrTime <= AVMAIC_MAX_IO_WAIT_BYTE)
   {
      getmicrotime (&tvCurr);
      timevalsub (&tvCurr, &tvStart);
      iCurrTime = tvCurr.tv_usec * hz / 1000000 + tvCurr.tv_sec * hz;
   }
   if (iCurrTime > AVMAIC_MAX_IO_WAIT_BYTE && ! avmdma_io_rx_full (iot, ioh))
   {
      DBG0 (LOG_ERROR,
            "Tag 0x%03X, handle 0x%03X: Timeout waiting for filled receive buffer (%d ticks)",
            (unsigned) iot, (unsigned) ioh, iCurrTime);
      return (-1);
   }
   *pucVal = bus_space_read_1 (iot, ioh, AVMAIC_REG_READ);
   return (0);
} /* avmdma_io_get_byte */





/**
 * Write a byte of data to the controller with timeout.
 *
 * @param iot                   I: Tag for i/o operations.
 * @param ioh                   I: Handle for i/o operations.
 * @param ucVal                 I: The value to write.
 *
 * @retval 0                    The write operation was successful.
 * @retval -1                   The write operation failed because of a timeout.
 */

int avmdma_io_put_byte
   (bus_space_tag_t    iot,
    bus_space_handle_t ioh,
    unsigned char      ucVal)
{
   struct timeval tvStart;
   struct timeval tvCurr;
   int            iCurrTime;

   /* wait for empty send buffer */
   getmicrotime (&tvStart);
   iCurrTime = 0;
   while (! avmdma_io_tx_empty (iot, ioh) &&
          iCurrTime <= AVMAIC_MAX_IO_WAIT_BYTE)
   {
      getmicrotime (&tvCurr);
      timevalsub (&tvCurr, &tvStart);
      iCurrTime = tvCurr.tv_usec * hz / 1000000 + tvCurr.tv_sec * hz;
   }
   if (iCurrTime > AVMAIC_MAX_IO_WAIT_BYTE && ! avmdma_io_tx_empty (iot, ioh))
   {
      DBG0 (LOG_ERROR,
            "Tag 0x%03X, handle 0x%03X: Timeout waiting for empty send buffer (%d ticks)",
            (unsigned) iot, (unsigned) ioh, iCurrTime);
      return (-1);
   }

   bus_space_write_1 (iot, ioh, AVMAIC_REG_WRITE, ucVal);

   return (0);
} /* avmdma_io_put_byte */





/**
 * Read data from internal controller registers.
 *
 * @param iot                   I: But tag for communication.
 * @param ioh                   I: Bus handle for communication.
 * @param iReg                  I: The register to read from.
 * @param puVal                 O: The value read from the register if result 0.
 *
 * @retval 0                    The register was read successfully.
 * @retval -1                   Failure.
 */

int avmdma_io_read_reg
   (bus_space_tag_t     iot,
    bus_space_handle_t  ioh,
    int                 iReg,
    unsigned int       *puVal)
{
   if (avmdma_io_put_byte (iot, ioh, AVMAIC_READ_INTREG) != 0 ||
       avmdma_io_put_word (iot, ioh, (unsigned int) iReg) != 0 ||
       avmdma_io_get_word (iot, ioh, puVal) != 0)
   {
      return (-1);
   }
   return (0);
} /* avmdma_io_read_reg */





/**
 * Write data to internal controller registers.
 *
 * @param iot                   I: But tag for communication.
 * @param ioh                   I: Bus handle for communication.
 * @param iReg                  I: The register to write to.
 * @param uVal                  I: The value to write.
 *
 * @retval 0                    The register was successfully written to.
 * @retval -1                   Failure.
 */

int avmdma_io_write_reg
   (bus_space_tag_t    iot,
    bus_space_handle_t ioh,
    int                iReg,
    unsigned int       uVal)
{
   if (avmdma_io_put_byte (iot, ioh, AVMAIC_WRITE_INTREG) != 0 ||
       avmdma_io_put_word (iot, ioh, (unsigned int) iReg) != 0 ||
       avmdma_io_put_word (iot, ioh, uVal) != 0)
   {
      return (-1);
   }
   return (0);
} /* avmdma_io_write_reg */





/**
 * Detect a dma based board (B1 PCI v4 or T1 PCI).
 *
 * @param dev                   I: The device to check.
 * @param memt                  I: Bus tag for memory mapped communication.
 * @param memh                  I: Bus handle for memory mapped communication.
 * @param iot                   I: Bus tag for i/o port based communication.
 * @param ioh                   I: Bus handle for i/o port based communication.
 * @param cardType              I: The card type to check for (only B1 PCI v4 or
 *                                 T1 PCI allowed).
 *
 * @retval 0                    A board of the card type specified was detected.
 * @retval -1                   No DMA based board found or the board is not
 *                              working.
 */

int avmdma_detect
   (device_t           dev,
    bus_space_tag_t    memt,
    bus_space_handle_t memh,
    bus_space_tag_t    iot,
    bus_space_handle_t ioh,
    AvmAicCardType_t   cardType)
{
   u_int32_t ulVal1;
   u_int32_t ulVal2;
   
   /* perform reset */
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_MCSR, 0);
   tsleep (&memt, PZERO, AVMIO_READY_WAIT_MSG, hz / 100);
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_MCSR, 0x0F000000);
   tsleep (&memt, PZERO, AVMIO_READY_WAIT_MSG, hz / 100);
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_MCSR, 0);
   tsleep (&memt, PZERO, AVMIO_READY_WAIT_MSG, 42 * hz / 1000);
   
   /* set length of transfer buffers to zero */
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_RXLEN, 0);
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_TXLEN, 0);
   
   /* clear any pending interrupt */
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_INTCSR, 0);
   
   /* and now do some magic checks */
   ulVal1 = avmdma_mem_readl (memt, memh, AVMAIC_AMCC_MCSR);
   if (ulVal1 != 0x000000E6)
   {
      device_printf (dev,
                     "Probe failed to card type \"%s\": MCSR register not equal to 0x000000E6 (0x%08X)\n",
                     avmmisc_get_card_type_name (cardType), ulVal1);
      return (-1);
   }
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_RXPTR, 0xFFFFFFFF);
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_TXPTR, 0xFFFFFFFF);
   ulVal1 = avmdma_mem_readl (memt, memh, AVMAIC_AMCC_RXPTR);
   ulVal2 = avmdma_mem_readl (memt, memh, AVMAIC_AMCC_TXPTR);
   if (ulVal1 != 0xFFFFFFFC || ulVal2 != 0xFFFFFFFC)
   {
      device_printf (dev,
                     "Probe failed to card type \"%s\": memory pointer not aligned on 4-byte boundary (RXPTR 0x%08X, TXPTR 0x%08X)\n",
                     avmmisc_get_card_type_name (cardType), ulVal1, ulVal2);
      return (-1);
   }
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_RXPTR, 0);
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_TXPTR, 0);
   ulVal1 = avmdma_mem_readl (memt, memh, AVMAIC_AMCC_RXPTR);
   ulVal2 = avmdma_mem_readl (memt, memh, AVMAIC_AMCC_TXPTR);
   if (ulVal1 != 0 || ulVal2 != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\": memory pointer not zero (RXPTR 0x%08X, TXPTR 0x%08X)\n",
                     avmmisc_get_card_type_name (cardType), ulVal1, ulVal2);
      return (-1);
   }
   bus_space_write_1 (iot, ioh, 0x10, 0);
   bus_space_write_1 (iot, ioh, 0x07, 0);
   bus_space_write_1 (iot, ioh, AVMAIC_REG_INSTAT, 0x02);
   bus_space_write_1 (iot, ioh, AVMAIC_REG_OUTSTAT, 0x02);
   ulVal1 = bus_space_read_1 (iot, ioh, AVMAIC_REG_INSTAT);
   ulVal2 = bus_space_read_1 (iot, ioh, AVMAIC_REG_OUTSTAT);
   if ((ulVal1 & 0xFE) != 0x02 || ulVal2 != 0x03)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\": state registers failed (1) (INSTAT 0x%08X, OUTSTAT 0x%08X)\n",
                     avmmisc_get_card_type_name (cardType), ulVal1, ulVal2);
      return (-1);
   }
   bus_space_write_1 (iot, ioh, AVMAIC_REG_INSTAT, 0);
   bus_space_write_1 (iot, ioh, AVMAIC_REG_OUTSTAT, 0);
   ulVal1 = bus_space_read_1 (iot, ioh, AVMAIC_REG_INSTAT);
   ulVal2 = bus_space_read_1 (iot, ioh, AVMAIC_REG_OUTSTAT);
   if ((ulVal1 & 0xFE) != 0x00 || ulVal2 != 0x01)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\": state registers failed (2) (INSTAT 0x%08X, OUTSTAT 0x%08X)\n",
                     avmmisc_get_card_type_name (cardType), ulVal1, ulVal2);
      return (-1);
   }
   
   /* passed all tests */
   DBG (LOG_DEBUG, device_get_unit (dev),
        "All checks successful regarding DMA functionality");
   return (0);
} /* avmdma_detect */





/*
        reset a dma based board
        -----------------------
*/

void avmdma_reset
   (bus_space_tag_t    memt,
    bus_space_handle_t memh,
    bus_space_tag_t    iot,
    bus_space_handle_t ioh,
    AvmAicCardType_t   cardType)
{
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_INTCSR, 0);
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_MCSR, 0);
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_RXLEN, 0);
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_TXLEN, 0);
   
   bus_space_write_1 (iot, ioh, 0x10, 0);
   bus_space_write_1 (iot, ioh, 0x07, 0);
   
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_MCSR, 0);
   DELAY (10 * 1000);
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_MCSR, 0x0F000000);
   DELAY (10 * 1000);
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_MCSR, 0);
   
   if (cardType == AVMAIC_CARD_TYPE_B1_PCI_V4)
   {
      DELAY (10 * 1000);
   }
   else
   {
      DELAY (42 * 1000);
   }
   
} /* avmdma_reset */





/*
        register a CAPI application at a controller
        -------------------------------------------
*/

unsigned avmdma_app_register
   (AvmAicSc_t       *pSc,
    AvmAicPortData_t *pPortData,
    unsigned          uMaxLogicalConnections,
    unsigned          uMaxBDataBlocks,
    unsigned          uMaxBDataLen,
    unsigned          uApplID)
{
   struct mbuf          *pmb;
   AvmLliSendRegister_t *pSendRegMsg;
   unsigned              uSizeMsgBuf;

   /* get mbuf for register message to controller */
   pmb = kcapi_get_mbuf (2 + 1 + sizeof (AvmLliSendRegister_t));
   if (! pmb)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Unable to send %s: Out of mbufs", "AVMAIC_SEND_REGISTER");
      return (CME_OS_RESOURCE_ERROR);
   }
   
   /* fill the mbuf with the controller message; first two bytes 0 means "no
    * CAPI message", for CAPI messages this were the message length
    */
   uSizeMsgBuf = (uMaxLogicalConnections + 1) * 1024;
   mtod (pmb, unsigned short *) [0] = 0;
   mtod (pmb, unsigned char *) [2] = AVMAIC_SEND_REGISTER;
   pSendRegMsg = (AvmLliSendRegister_t *) &((mtod (pmb, unsigned char *)) [3]);
   C_PUT_DWORD (pSendRegMsg->dwApplID, uApplID);
   C_PUT_DWORD (pSendRegMsg->dwSizeMsgBuffer, uSizeMsgBuf);
   C_PUT_DWORD (pSendRegMsg->dwNumB3Connections, uMaxLogicalConnections);
   C_PUT_DWORD (pSendRegMsg->dwNumB3Blocks, uMaxBDataBlocks);
   C_PUT_DWORD (pSendRegMsg->dwB3Size, uMaxBDataLen);
   pmb->m_len = 2 + 1 + sizeof (AvmLliSendRegister_t);;
   
   /* insert controller message into queue for controller */
   if (_IF_QFULL (&(pSc->sendQueue)))
   {
      kcapi_free_mbuf (pmb);
      DBG (LOG_TRACE, pSc->iUnit,
           "Unable to enqueue %s: send queue full", "AVMAIC_SEND_REGISTER");
      return (CME_PUT_QUEUE_FULL);
   }
   _IF_ENQUEUE (&(pSc->sendQueue), pmb);
   DBG (LOG_DEBUG, pSc->iUnit, "Message %s enqueued", "AVMAIC_SEND_REGISTER");
   
   /* send next message to controller if it is not busy */
   avmdma_start_tx (pSc);
   
   /* suppress warnings */
   (void) pPortData;
   
   return (CAPI_OK);
} /* avmdma_app_register */





/*
        release a CAPI application at a controller
        ------------------------------------------
*/

unsigned avmdma_app_release
   (AvmAicSc_t       *pSc,
    AvmAicPortData_t *pPortData,
    unsigned          uApplID)
{
   struct mbuf         *pmb;
   AvmLliSendRelease_t *pSendRelMsg;

   /* get mbuf for register message to controller */
   pmb = kcapi_get_mbuf (2 + 1 + sizeof (AvmLliSendRelease_t));
   if (! pmb)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Unable to send %s: Out of mbufs", "AVMAIC_SEND_RELEASE");
      return (CME_OS_RESOURCE_ERROR);
   }
   
   /* fill the mbuf with the controller message; first two bytes 0 means "no
    * CAPI message", for CAPI messages this were the message length
    */
   mtod (pmb, unsigned short *) [0] = 0;
   mtod (pmb, unsigned char *) [2] = AVMAIC_SEND_RELEASE;
   pSendRelMsg = (AvmLliSendRelease_t *) &((mtod (pmb, unsigned char *)) [3]);
   C_PUT_DWORD (pSendRelMsg->dwApplID, uApplID);
   pmb->m_len = 2 + 1 + sizeof (AvmLliSendRelease_t);
   
   /* prepare to wait for completion of release operation */
   pSc->fWaitingForRc = 1;
   
   /* insert controller message into queue for controller */
   if (_IF_QFULL (&(pSc->sendQueue)))
   {
      pSc->fWaitingForRc = 0;
      kcapi_free_mbuf (pmb);
      DBG (LOG_TRACE, pSc->iUnit,
           "Unable to enqueue %s: Send queue full", "AVMAIC_SEND_RELEASE");
      return (CME_PUT_QUEUE_FULL);
   }
   _IF_ENQUEUE (&(pSc->sendQueue), pmb);
   DBG (LOG_DEBUG, pSc->iUnit, "Message %s enqueued", "AVMAIC_SEND_RELEASE");
   
   /* send next message to controller if it is not busy */
   avmdma_start_tx (pSc);
   
   /* now wait for completion of release operation */
   (void) cv_timedwait (&(pSc->cvNotify), &(pSc->mtxAccess), 30 * hz);
   pSc->fWaitingForRc = 0;
   
   return (CAPI_OK);
} /* avmdma_app_release */





/**
 * Send a CAPI message to the controller.
 */

unsigned avmdma_put_message
   (AvmAicSc_t       *pSc,
    AvmAicPortData_t *pPortData,
    unsigned          uApplID,
    struct mbuf      *pmbMsg)
{
   unsigned  uCapiCmd;
   u_int32_t dwCid;

   /* extract some data needed later */
   uCapiCmd = CAPI_GET_CMD (mtod (pmbMsg, CAPIMsg_t *));
   dwCid = CAPI_GET_CID (mtod (pmbMsg, CAPIMsg_t *));
   
   /* if the controller is currently busy, the message must be rejected */
   if (pSc->state != AVMAIC_STATE_READY ||
       pPortData->state != AVMAIC_STATE_READY)
   {
      DBG (LOG_DEBUG, pSc->iUnit,
           "Port %u: Unable to enqueue CAPI message 0x%04X: Controller %u busy",
           pPortData->uPortIdx, uCapiCmd, pPortData->uUniqueCapiCtlrNum);
      return (CME_PUT_QUEUE_FULL);
   }
   
   /* insert controller message into queue for controller */
   if (_IF_QFULL (&(pSc->sendQueue)))
   {
      DBG (LOG_TRACE, pSc->iUnit,
           "Unable to enqueue CAPI message 0x%04X: Send queue full", uCapiCmd);
      return (CME_PUT_QUEUE_FULL);
   }
   _IF_ENQUEUE (&(pSc->sendQueue), pmbMsg);
   DBG (LOG_DEBUG, pSc->iUnit, "CAPI message 0x%04X enqueued", uCapiCmd);
   
   /* send next message to controller if it is not busy */
   avmdma_start_tx (pSc);
   
   return (CAPI_OK);
} /* avmdma_put_message */





/*
        reset and download a controller
        -------------------------------
        
        Parameters:
           pSc                  I: softc structure ident. the board
           nNumDataBlocks       I: Number of data blocks to be sent to the
                                   controller; length of paDataBlocks
           paDataBlocks         I: Array of data blocks to be sent to the
                                   controller

        Result:
           CAPI_OK or CAPI error value
*/

unsigned avmdma_reset_ctlr
   (AvmAicSc_t          *pSc,
    size_t               nNumDataBlocks,
    CAPICtlrDataBlock_t *paDataBlocks)
{
   bus_space_tag_t     memt = pSc->resInfo.memTag;
   bus_space_handle_t  memh = pSc->resInfo.memHandle;
   bus_space_tag_t     iot  = pSc->resInfo.ioTag;
   bus_space_handle_t  ioh  = pSc->resInfo.ioHandle;
   AvmAicPortData_t   *pPortData;
   struct mbuf        *pmb;
   unsigned            uRes;
   int                 i;

   if (nNumDataBlocks > 0)
   {
      DBG (LOG_INFO, pSc->iUnit,
           "Load firmware to controller %u, length of software %zu bytes",
	        pSc->aPortData [0].uUniqueCapiCtlrNum,
           paDataBlocks [0].nLenDataBlock);
      if (nNumDataBlocks > 1)
      {
         DBG (LOG_INFO, pSc->iUnit,
              "Length of configuration data %zu bytes",
              paDataBlocks [1].nLenDataBlock);
      }
   }
   else
   {
      DBG (LOG_INFO, pSc->iUnit, "Unloading controller %u",
	        pSc->aPortData [0].uUniqueCapiCtlrNum);
   }

   /* perform a complete reset */
   avmdma_reset (memt, memh, iot, ioh, pSc->cardType);
   avmdma_reset (memt, memh, iot, ioh, pSc->cardType);
   pSc->state = AVMAIC_STATE_INITIALIZING;
   pSc->nPortsInitialized = 0;

   /* clean the send queue if there are still messages queued */
   pmb = NULL;
   do
   {
      _IF_DEQUEUE (&(pSc->sendQueue), pmb);
      kcapi_free_mbuf (pmb);
   } while (pmb != NULL);
   
   /* if only reset was requested: ready */
   if (nNumDataBlocks <= 0)
   {
      pSc->state = AVMAIC_STATE_DOWN;
      DBG (LOG_INFO, pSc->iUnit, "Unloading controller successful");
      return (CAPI_OK);
   }
   
   /* so at least one data block is specified, treat it as the firmware file and
    * load it onto the controller
    */
   uRes = avmmisc_load_t4_file (pSc,
                                paDataBlocks [0].nLenDataBlock,
                                paDataBlocks [0].paucDataBlock);
   if (uRes != CAPI_OK)
   {
      avmdma_reset (memt, memh, iot, ioh, pSc->cardType);
      pSc->state = AVMAIC_STATE_DOWN;
      return (uRes);
   }
   DBG (LOG_INFO, pSc->iUnit, "Firmware successfully loaded");

   /* if a second data block is specified, it must be the controller
    * configuration; send it to the controller
    */
   if (nNumDataBlocks > 1)
   {
      uRes = avmmisc_load_config (pSc,
                                  paDataBlocks [1].nLenDataBlock,
                                  paDataBlocks [1].paucDataBlock);
      if (uRes != CAPI_OK)
      {
	 avmdma_reset (memt, memh, iot, ioh, pSc->cardType);
      	 pSc->state = AVMAIC_STATE_DOWN;
	 return (uRes);
      }
      DBG (LOG_INFO, pSc->iUnit, "Configuration successfully loaded");
   }

   /* check if controller successfully loaded */
   if (! avmmisc_is_loaded (pSc))
   {
      printf ("%s%d: Controller does not respond, firmware and configuration load failed\n",
	      pSc->szDriverName, pSc->iUnit);
      avmdma_reset (memt, memh, iot, ioh, pSc->cardType);
      pSc->state = AVMAIC_STATE_DOWN;
      return (CME_OS_RESOURCE_ERROR);
   }

   /* initialize the dma engine for normal operation */
   avmdma_init_for_operation (pSc);
   
   /* initialize some port specific data before sending AVMAIC_SEND_INIT */
   for (i = 0; i < pSc->nPorts; i++)
   {
      pPortData = &(pSc->aPortData [i]);
      pPortData->state = AVMAIC_STATE_INITIALIZING;
      bzero (pPortData->acVersionBuf, sizeof (pPortData->acVersionBuf));
   }

   /* after the SEND_INIT command the board will be ready for operation, but
    * before sending the first message to the controller we must wait until the
    * first AVMAIC_RECEIVE_START that will set the state to ready
    */
   pSc->state = AVMAIC_STATE_BUSY;

   /* initialize controller software with number of applications and logical
    * connections
    */
   DBG (LOG_TRACE, pSc->iUnit,
        "Send message AVMAIC_SEND_INIT to the controller");
   pSc->uRc = 0xFFFFFFFF;
   pSc->fWaitingForRc = 1;
   uRes = avmdma_send_init (pSc);
   if (uRes != CAPI_OK)
   {
      pSc->fWaitingForRc = 0;
      avmdma_reset (memt, memh, iot, ioh, pSc->cardType);
      pSc->state = AVMAIC_STATE_DOWN;
      printf ("%s%d: Unable to do controller initialization after download, controller busy\n",
              pSc->szDriverName, pSc->iUnit);
      return (uRes);
   }

   /* wait for controller response to initialization request */
   DBG (LOG_DEBUG, pSc->iUnit, "Waiting for AVMAIC_RECIEVE_INIT");
   (void) cv_timedwait (&(pSc->cvNotify), &(pSc->mtxAccess), 10 * hz);
   pSc->fWaitingForRc = 0;
   if (pSc->uRc != CAPI_OK)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Timeout waiting for AVMAIC_RECEIVE_INIT or error occurred");
      pSc->state = AVMAIC_STATE_DOWN;
      avmdma_reset (memt, memh, iot, ioh, pSc->cardType);
      uRes = pSc->uRc;
      printf ("%s%d: Controller initialization after download failed\n",
	      pSc->szDriverName, pSc->iUnit);
      if (uRes == 0xFFFFFFFF)
      {
         return (CRE_OS_RESOURCE_ERROR);
      }
      return (uRes);
   }
   DBG (LOG_DEBUG, pSc->iUnit, "Received AVMAIC_RECEIVE_INIT, cv signaled");

   /* the version buffer of the port data structure is filled now, translate it
    * into a better usable format
    */
   for (i = 0; i < pSc->nPorts; i++)
   {
      pPortData = &(pSc->aPortData [i]);
      avmmisc_parse_version (pPortData);
      switch (pPortData->abCapiProfile [45])
      {
         case 0:
            /* the card type is delivered by the controller, just log it */
            DBG (LOG_INFO, pSc->iUnit, "Board delivers card type \"%s\"",
                 pPortData->szCardType);
            break;

         case 6:
            /* card claims to be B1 PCI v3.0 */
            if (pSc->cardType != AVMAIC_CARD_TYPE_B1_PCI_V3)
            {
               printf ("%s%d: Board claims to be \"%s\" but was detected as \"%s\"\n",
                       pSc->szDriverName, pSc->iUnit,
                       avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_B1_PCI_V3),
                       avmmisc_get_card_type_name (pSc->cardType));
            }
            break;

         case 7:
            /* card claims to be B1 PCI v4.0 */
            if (pSc->cardType != AVMAIC_CARD_TYPE_B1_PCI_V4)
            {
               printf ("%s%d: Board claims to be \"%s\" but was detected as \"%s\"\n",
                       pSc->szDriverName, pSc->iUnit,
                       avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_B1_PCI_V4),
                       avmmisc_get_card_type_name (pSc->cardType));
            }
            break;

         case 3:
            /* card type should be (unsupported) B1 PCMCIA */
         case 4:
            /* card type should be (unsupported) M1 */
         case 5:
            /* card type should be (unsupported) M2 */
         default:
            break;
      }
   }

   DBG (LOG_INFO, pSc->iUnit,
        "Controller successfully loaded and initialized");

   return (CAPI_OK);
} /* avmdma_reset_ctlr */





/*
        the interrupt routine for a dma based board
        -------------------------------------------
*/

void avmdma_intr
   (AvmAicSc_t *pSc)
{
   bus_space_tag_t    memt = pSc->resInfo.memTag;
   bus_space_handle_t memh = pSc->resInfo.memHandle;
   u_int32_t          ulStatus;
   u_int32_t          ulNewIcsr;
   
   mtx_lock (&(pSc->mtxAccess));
   if (pSc->fOpInProgress != 0 && ! pSc->fWaitingForRc)
   {
      DBG (LOG_DEBUG, pSc->iUnit, "Operation still in progress");
   }
   if (pSc->fIntrActive != 0)
   {
      mtx_unlock (&(pSc->mtxAccess));
      DBG (LOG_ERROR, pSc->iUnit, "Nested interrupt handler call");
      return;
   }
   pSc->fIntrActive = 1;
   
   DBG (LOG_IRQ, pSc->iUnit, "Interrupt handler called");
        
   /* check if the interrupt is really caused by our hardware */
   ulStatus = avmdma_mem_readl (memt, memh, AVMAIC_AMCC_INTCSR);
   if ((ulStatus & AVMAIC_ANY_S5933_INT) == 0)
   {
      /* must be other hardware that caused the interrupt */
      pSc->fIntrActive = 0;
      mtx_unlock (&(pSc->mtxAccess));
      return;
   }

   /* switch off any receive or send irqs to the board if it is active */
   ulNewIcsr = pSc->ulDmaIcsr | (ulStatus & AVMAIC_ALL_INT);
   if ((ulStatus & AVMAIC_TX_TC_INT) != 0)
   {
      ulNewIcsr &= ~AVMAIC_EN_TX_TC_INT;
   }
   if ((ulStatus & AVMAIC_RX_TC_INT) != 0)
   {
      ulNewIcsr &= ~AVMAIC_EN_RX_TC_INT;
   }
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_INTCSR, ulNewIcsr);

   /* check receive irq, i.e. if we received some bytes */
   if ((ulStatus & AVMAIC_RX_TC_INT) != 0)
   {
      unsigned char *pucRecvBuf = pSc->paucRecvMsgBuf;
      u_int32_t      ulRxLen;
      
      /* if no length information is available, we just received the length of
       * a pending data block --> save the length and prepare to receive the
       * data block itself
       */
      if (pSc->ulRecvMsgBufLength == 0)
      {
         bus_dmamap_sync (pSc->dmaRecv.dmat, pSc->dmaRecv.map,
                          BUS_DMASYNC_POSTREAD);
         pSc->ulRecvMsgBufLength = le32toh (*((u_int32_t *) pucRecvBuf));
         ulRxLen = (pSc->ulRecvMsgBufLength + 3) & ~0x0003;
         bus_dmamap_sync (pSc->dmaRecv.dmat, pSc->dmaRecv.map,
                          BUS_DMASYNC_PREREAD);
         avmdma_mem_writel (memt, memh, AVMAIC_AMCC_RXPTR,
                            pSc->dmaRecv.ulPhysAddr);
         avmdma_mem_writel (memt, memh, AVMAIC_AMCC_RXLEN, ulRxLen);
      }
      /* else: there is already length information, now we got the data block
       * itself --> handle the completely received message
       */
      else
      {
         avmdma_handle_rx (pSc);
         
         /* prepare to receive the next message (i.e. first its length) */
         pSc->ulRecvMsgBufLength = 0;
         bus_dmamap_sync (pSc->dmaRecv.dmat, pSc->dmaRecv.map,
                          BUS_DMASYNC_PREREAD);
         avmdma_mem_writel (memt, memh, AVMAIC_AMCC_RXPTR,
                            pSc->dmaRecv.ulPhysAddr);
         avmdma_mem_writel (memt, memh, AVMAIC_AMCC_RXLEN, sizeof (u_int32_t));
      }
   }
   
   /* check for send irq, i.e. if the controller is able to accept a new message
    */
   if ((ulStatus & AVMAIC_TX_TC_INT) != 0)
   {
      /* switch off the send irq, may be switched on again if there is a message
       * queued for the controller
       */
      pSc->ulDmaIcsr &= ~AVMAIC_EN_TX_TC_INT;
      
      /* transfer any pending message to the controller */
      avmdma_start_tx (pSc);
   }

   /* re-enable receive irq and send irq if needed */
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_INTCSR, pSc->ulDmaIcsr);

   pSc->fIntrActive = 0;
   mtx_unlock (&(pSc->mtxAccess));
   
} /* avmdma_intr */





/*
        receive a CAPI message (but not a data-b3-indication) from a controller
        -----------------------------------------------------------------------
*/

void avmdma_receive_message
   (AvmAicSc_t *pSc)
{
   unsigned                uApplID;
   unsigned                uUniqueCtlrNum;
   struct mbuf            *pmbMsg;
   size_t                  nMsgLen;
   AvmLliReceiveMessage_t *pRecvMsg;
   AvmAicPortData_t       *pPortData;

   DBG (LOG_DEBUG, pSc->iUnit, "Got %s, total length %lu",
        "AVMAIC_RECEIVE_MESSAGE", (unsigned long) (pSc->ulRecvMsgBufLength));

   pRecvMsg = (AvmLliReceiveMessage_t *) &(pSc->paucRecvMsgBuf [1]);
   uApplID = C_GET_DWORD (pRecvMsg->dwApplID);
   nMsgLen = C_GET_DWORD (pRecvMsg->dwLength);
   if (nMsgLen > sizeof (CAPIMsg_t))
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Received %zu message bytes instead of maximum %zu",
           nMsgLen, sizeof (CAPIMsg_t));
      nMsgLen = sizeof (CAPIMsg_t);
   }
   
   /* allocate an mbuf for the message */
   pmbMsg = kcapi_get_mbuf (nMsgLen);
   if (pmbMsg)
   {
      /* now copy the message into the mbuf */
      bcopy (&(pRecvMsg->capiMsg), pmbMsg->m_data, nMsgLen);
      DBG (LOG_DEBUG, pSc->iUnit, "Got CAPI message 0x%04X, length %u (%u)",
           (unsigned) CAPI_GET_CMD (mtod (pmbMsg, CAPIMsg_t *)),
           (unsigned) pmbMsg->m_len,
           (unsigned) CAPI_GET_LEN (mtod (pmbMsg, CAPIMsg_t *)));
   }
   else
   {
      printf ("%s%d: %s: Out of mbufs for CAPI message 0x%04X, length %zu\n",
              pSc->szDriverName, pSc->iUnit, __FUNCTION__,
              (unsigned) CAPI_GET_CMD (&(pRecvMsg->capiMsg)), nMsgLen);
   }
   
   /* determine which port is addressed by the CID of the message */
   pPortData = avmmisc_get_port_from_capi_ctlr_num
                  (pSc,
                   CAPI_GET_CID (&(pRecvMsg->capiMsg)) & CAPI_CIDMASK_CTLR);
   if (! pPortData)
   {
      printf ("%s%d: %s: Unable to find port for CAPI controller no. %u of CAPI message 0x%04X",
              pSc->szDriverName, pSc->iUnit, __FUNCTION__,
              (unsigned)
                 (CAPI_GET_CID (&(pRecvMsg->capiMsg)) & CAPI_CIDMASK_CTLR),
              (unsigned) CAPI_GET_CMD (&(pRecvMsg->capiMsg)));
      kcapi_free_mbuf (pmbMsg);
      return;
   }

   DBG (LOG_DEBUG, pSc->iUnit, "Got CAPI message 0x%04X, length %zu",
        (unsigned) CAPI_GET_CMD (&(pRecvMsg->capiMsg)), nMsgLen);
        
   /* finally forward the CAPI message to the CAPI manager */
   uUniqueCtlrNum = pPortData->uUniqueCapiCtlrNum;
   mtx_unlock (&(pSc->mtxAccess));
   kcapi_ctlr_receive_capi_message (uUniqueCtlrNum, uApplID, pmbMsg);
   mtx_lock (&(pSc->mtxAccess));
   
} /* avmdma_receive_message */





/**
 * Receive a data-b3-indication from an i/o based controller.
 */

void avmdma_receive_data_b3_ind
   (AvmAicSc_t *pSc)
{
   unsigned                uApplID;
   unsigned                uUniqueCtlrNum;
   struct mbuf            *pmbMsg;
   struct mbuf            *pmbData;
   size_t                  nMsgLen;
   size_t                  nDataOffset;
   size_t                  nDataLen;
   AvmLliReceiveMessage_t *pRecvMsg;
   AvmAicPortData_t       *pPortData;

   DBG (LOG_DEBUG, pSc->iUnit, "Got %s, total length %lu",
        "AVMAIC_RECEIVE_DATA_B3_IND",
        (unsigned long) (pSc->ulRecvMsgBufLength));

   pRecvMsg = (AvmLliReceiveMessage_t *) &(pSc->paucRecvMsgBuf [1]);
   uApplID = C_GET_DWORD (pRecvMsg->dwApplID);
   nMsgLen = C_GET_DWORD (pRecvMsg->dwLength);
   if (nMsgLen > sizeof (CAPIMsg_t))
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Received %zu message bytes instead of maximum %zu",
           nMsgLen, sizeof (CAPIMsg_t));
      nMsgLen = sizeof (CAPIMsg_t);
   }
   nDataOffset = 1 + 2 * sizeof (u_int32_t) + nMsgLen;
   nDataLen = (size_t) *((u_int32_t *) &(pSc->paucRecvMsgBuf [nDataOffset]));
   nDataLen = le32toh (nDataLen);
   nDataOffset += sizeof (u_int32_t);
   if (nDataOffset + nDataLen > AVMAIC_MSGBUF_SIZE)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Received %zu Data-B3 bytes instead of maximum %zu",
           nDataLen, (size_t) AVMAIC_MSGBUF_SIZE - nDataOffset);
      nDataLen = AVMAIC_MSGBUF_SIZE - nDataOffset;
   }
   
   /* be sure the message length includes space for the 64bit pointer */
   if (nMsgLen < sizeof (CAPIMsgHead_t) + sizeof (CAPIDataB3Ind_t))
   {
      nMsgLen = sizeof (CAPIMsgHead_t) + sizeof (CAPIDataB3Ind_t);
   }
   
   /* allocate an mbuf for the message */
   pmbMsg = kcapi_get_mbuf (nMsgLen);
   if (pmbMsg)
   {
      /* now copy the message into the mbuf */
      /* Note: The buffer is always big enough to include the 64bit pointer for
       *       Data-B3-Indications.
       */
      bcopy (&(pRecvMsg->capiMsg), pmbMsg->m_data, nMsgLen);
      C_PUT_QWORD (mtod (pmbMsg, CAPIMsg_t *)->info.data_b3_ind.qwData64, 0);

      /* "correct" the CAPI message length for the 64bit data pointer */
      C_PUT_WORD (mtod (pmbMsg, CAPIMsg_t *)->head.wLen, nMsgLen);
   }
   else
   {
      printf ("%s%d: %s: Out of mbufs for CAPI message 0x%04X, length %zu\n",
              pSc->szDriverName, pSc->iUnit, __FUNCTION__,
              (unsigned) CAPI_GET_CMD (&(pRecvMsg->capiMsg)), nMsgLen);
   }
   
   /* now allocate an mbuf for the data block */
   if (nDataLen == 0)
   {
      DBG (LOG_ERROR, pSc->iUnit, "Empty data block signaled, length %zu",
           nDataLen);
   }
   if (pmbMsg && nDataLen > 0)
   {
      pmbData = kcapi_get_mbuf (nDataLen);
      if (pmbData)
      {
         /* now copy the data-b3 block into the mbuf */
         bcopy (&(pSc->paucRecvMsgBuf [nDataOffset]),
                pmbData->m_data, nDataLen);

         /* insert the data-b3 mbuf into the mbuf of the message */
         pmbMsg->m_next = pmbData;

         DBG (LOG_DEBUG, pSc->iUnit,
              "Got Data-B3-Ind, message length %u (%u), data-b3 length %u (%u)",
              (unsigned) pmbMsg->m_len,
              (unsigned) CAPI_GET_LEN (mtod (pmbMsg, CAPIMsg_t *)),
              (unsigned) pmbData->m_len,
              (unsigned)
                 C_GET_WORD
                    (mtod (pmbMsg, CAPIMsg_t *)->info.data_b3_ind.wLen));

         C_PUT_WORD (((CAPIMsg_t *) (pmbMsg->m_data))->info.data_b3_ind.wLen,
                     pmbData->m_len);
      }
      else
      {
         printf ("%s%d: %s: Out of Data-B3 mbufs for CAPI message 0x%04X, length %zu, data length %zu\n",
                 pSc->szDriverName, pSc->iUnit, __FUNCTION__,
                 (int) CAPI_GET_CMD (mtod (pmbMsg, CAPIMsg_t *)),
                 nMsgLen, nDataLen);
         C_PUT_WORD (mtod (pmbMsg, CAPIMsg_t *)->info.data_b3_ind.wLen, 0);
      }
   }
   else if (pmbMsg)
   {
      C_PUT_WORD (mtod (pmbMsg, CAPIMsg_t *)->info.data_b3_ind.wLen, 0);
   }
   
   /* determine which port is addressed by the CID of the message */
   pPortData = avmmisc_get_port_from_capi_ctlr_num
                  (pSc,
                   CAPI_GET_CID (&(pRecvMsg->capiMsg)) & CAPI_CIDMASK_CTLR);
   if (! pPortData)
   {
      printf ("%s%d: %s: Unable to find port for CAPI controller no. %u of CAPI message 0x%04X",
              pSc->szDriverName, pSc->iUnit, __FUNCTION__,
              (unsigned) (CAPI_GET_CID (&(pRecvMsg->capiMsg)) &
                             CAPI_CIDMASK_CTLR),
              (int) CAPI_GET_CMD (&(pRecvMsg->capiMsg)));
      kcapi_free_mbuf (pmbMsg);
      return;
   }

   DBG (LOG_DEBUG, pSc->iUnit,
        "Got CAPI message 0x%04X, length %zu, Data-B3 length %zu",
        CAPI_GET_CMD (mtod (pmbMsg, CAPIMsg_t *)), nMsgLen, nDataLen);
        
   /* finally forward the CAPI message to the CAPI manager */
   uUniqueCtlrNum = pPortData->uUniqueCapiCtlrNum;
   mtx_unlock (&(pSc->mtxAccess));
   kcapi_ctlr_receive_capi_message (uUniqueCtlrNum, uApplID, pmbMsg);
   mtx_lock (&(pSc->mtxAccess));
   
} /* avmdma_receive_data_b3_ind */





/*
        receive information about a new established B3 connection
        ---------------------------------------------------------
*/

void avmdma_receive_new_ncci
   (AvmAicSc_t *pSc)
{
   AvmLliReceiveNewNcci_t *pRecvNewNcci;
   AvmAicPortData_t       *pPortData;
   unsigned                uApplID;
   u_int32_t               dwNcci;
   unsigned                uWinSize;
   
   DBG (LOG_DEBUG, pSc->iUnit, "Got AVMAIC_RECEIVE_NEW_NCCI");

   pRecvNewNcci = (AvmLliReceiveNewNcci_t *) &(pSc->paucRecvMsgBuf [1]);
   uApplID  = C_GET_DWORD (pRecvNewNcci->dwApplID);
   dwNcci   = C_GET_DWORD (pRecvNewNcci->dwNcci);
   uWinSize = C_GET_DWORD (pRecvNewNcci->dwWindowSize);
   
   /* check for valid window size */
   if (uWinSize > 8)
   {
      printf ("%s%d: %s: Got invalid window size %u, use %u\n",
              pSc->szDriverName, pSc->iUnit, __FUNCTION__, uWinSize, 8);
      uWinSize = 8;
   }
   
   /* determine which port is addressed by the NCCI */
   pPortData = avmmisc_get_port_from_capi_ctlr_num
                  (pSc, dwNcci & CAPI_CIDMASK_CTLR);
   if (! pPortData)
   {
      printf ("%s%d: %s: Unable to find port for CAPI controller no. %u of NCCI 0x%08X",
              pSc->szDriverName, pSc->iUnit, __FUNCTION__,
              (unsigned) (dwNcci & CAPI_CIDMASK_CTLR), dwNcci);
      return;
   }

   DBG (LOG_TRACE, pSc->iUnit,
        "New B3 connection signalled, appl. id %u, NCCI 0x%08X, window size %u",
        uApplID, (unsigned) dwNcci, uWinSize);
    
} /* avmdma_receive_new_ncci */





/*
        receive information about a released B3 connection or application
        -----------------------------------------------------------------
*/

void avmdma_receive_free_ncci
   (AvmAicSc_t *pSc)
{
   AvmLliReceiveFreeNcci_t *pRecvFreeNcci;
   unsigned                 uApplID;
   u_int32_t                dwNcci;
   
   DBG (LOG_DEBUG, pSc->iUnit, "Got AVMAIC_RECEIVE_FREE_NCCI");

   pRecvFreeNcci = (AvmLliReceiveFreeNcci_t *) &(pSc->paucRecvMsgBuf [1]);
   uApplID  = C_GET_DWORD (pRecvFreeNcci->dwApplID);
   dwNcci   = C_GET_DWORD (pRecvFreeNcci->dwNcci);
   
   /* distinguish between application release or NCCI release */
   if (dwNcci == 0xFFFFFFFF)
   {
      /* application release --> store the result of the release call and
       * wakeup any waiting process
       */
      DBG (LOG_DEBUG, pSc->iUnit, "Got release for appl. id %u",
           uApplID);
      if (pSc->fWaitingForRc)
      {
         pSc->uRc = CAPI_OK;
         pSc->fWaitingForRc = 0;
         cv_broadcast (&(pSc->cvNotify));
      }
      
      DBG (LOG_INFO, pSc->iUnit, "Appl. id %u released", uApplID);
   }
   else
   {
      /* NCCI release, nothing to do */
      DBG (LOG_TRACE, pSc->iUnit,
           "NCCI 0x%08X released for appl. id %u",
           (unsigned) dwNcci, uApplID);
   }
   
} /* avmdma_receive_free_ncci */





/*
        receive acknowledge from initialization
        ---------------------------------------
*/

void avmdma_receive_init
   (AvmAicSc_t *pSc)
{
   AvmAicPortData_t *pPortData;

   DBG (LOG_DEBUG, pSc->iUnit, "Got AVMAIC_RECEIVE_INIT");

   if (pSc->nPortsInitialized >= pSc->nPorts)
   {
      printf ("%s%d: %s: Attempt to initialize more ports (%zu) than available (%zu) on controller\n",
              pSc->szDriverName, pSc->iUnit, __FUNCTION__,
              pSc->nPortsInitialized + 1, pSc->nPorts);
      pSc->uRc = CRE_CAPI_NOT_INSTALLED;
      if (pSc->fWaitingForRc)
      {
         pSc->fWaitingForRc = 0;
         cv_broadcast (&(pSc->cvNotify));
      }
      return;
   }
   
   /* address the port for the initialization message */
   pPortData = &(pSc->aPortData [pSc->nPortsInitialized]);
   
   /* copy the version data to the port private memory for later evaluation */
   pPortData->nLenVersion =
      (size_t) le32toh (*((u_int32_t *) &(pSc->paucRecvMsgBuf [1])));
   if (pPortData->nLenVersion > sizeof (pPortData->acVersionBuf))
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Received %zu version bytes instead of maximum %zu for port %zu",
           pPortData->nLenVersion, sizeof (pPortData->acVersionBuf),
           pSc->nPortsInitialized);
      pPortData->nLenVersion = sizeof (pPortData->acVersionBuf);
   }
   bcopy (&(pSc->paucRecvMsgBuf [1 + sizeof (u_int32_t)]),
          pPortData->acVersionBuf, pPortData->nLenVersion);
   DBG (LOG_DEBUG, pSc->iUnit,
        "Copied %zu version bytes from message of length %lu for port %u",
        pPortData->nLenVersion, (unsigned long) (pSc->ulRecvMsgBufLength),
        pPortData->uPortIdx);
   
   /* mark this port of the board as active, the board itself will be marked
    * active by the function waiting for AVMAIC_RECEIVE_INIT
    */
   pPortData->state = AVMAIC_STATE_READY;
   pSc->nPortsInitialized++;
   
   /* wakeup waiting initialization process if all ports are initialized */
   if (pSc->nPortsInitialized >= pSc->nPorts)
   {
      pSc->uRc = CAPI_OK;
      if (pSc->fWaitingForRc)
      {
         pSc->fWaitingForRc = 0;
         cv_broadcast (&(pSc->cvNotify));
      }
   }

} /* avmdma_receive_init */





/*
        handle a message for a completed task
        -------------------------------------
*/

void avmdma_receive_task_ready
   (AvmAicSc_t *pSc)
{
   unsigned                  uApplID;
   size_t                    nMsgLen;
   size_t                    nOffset;
   AvmLliReceiveTaskReady_t *pRecvTaskReady;

   DBG (LOG_DEBUG, pSc->iUnit, "Got AVMAIC_RECEIVE_TASK_READY");

   pRecvTaskReady = (AvmLliReceiveTaskReady_t *) &(pSc->paucRecvMsgBuf [1]);
   uApplID = C_GET_DWORD (pRecvTaskReady->dwApplID);
   nMsgLen = C_GET_DWORD (pRecvTaskReady->dwLength);
   nOffset = 1 + sizeof (*pRecvTaskReady);
   if (nMsgLen >= AVMAIC_MSGBUF_SIZE - nOffset)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Received %zu task bytes instead of maximum %zu",
           nMsgLen, (size_t) AVMAIC_MSGBUF_SIZE - nOffset - 1);
      nMsgLen = AVMAIC_MSGBUF_SIZE - nOffset - 1;
   }
   
   pSc->paucRecvMsgBuf [nOffset + nMsgLen] = '\0';
   for ( ;
        nMsgLen > 0 &&
           (pSc->paucRecvMsgBuf [nOffset + nMsgLen] == '\n' ||
            pSc->paucRecvMsgBuf [nOffset + nMsgLen] == '\r');
        nMsgLen--)
   {
      pSc->paucRecvMsgBuf [nOffset + nMsgLen] = '\0';
   }
   
   DBG (LOG_INFO, pSc->iUnit, "Task \"%s\" ready for appl. id %u",
        (char *) &(pSc->paucRecvMsgBuf [nOffset]), uApplID);
   
} /* avmcma_receive_task_ready */





/*
        handle a debugging message
        --------------------------
*/

void avmdma_receive_debugmsg
   (AvmAicSc_t *pSc)
{
   size_t                   nMsgLen;
   size_t                   nOffset;
   AvmLliReceiveDebugMsg_t *pRecvDbgMsg;

   DBG (LOG_DEBUG, pSc->iUnit, "Got AVMAIC_RECEIVE_DEBUGMSG");

   pRecvDbgMsg = (AvmLliReceiveDebugMsg_t *) &(pSc->paucRecvMsgBuf [1]);
   nMsgLen = C_GET_DWORD (pRecvDbgMsg->dwLength);
   nOffset = 1 + sizeof (*pRecvDbgMsg);
   if (nMsgLen >= AVMAIC_MSGBUF_SIZE - nOffset)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Received %zu debug message bytes instead of maximum %zu",
           nMsgLen, (size_t) AVMAIC_MSGBUF_SIZE - nOffset - 1);
      nMsgLen = AVMAIC_MSGBUF_SIZE - nOffset - 1;
   }
   
   pSc->paucRecvMsgBuf [nOffset + nMsgLen] = '\0';
   for ( ;
        nMsgLen > 0 &&
           (pSc->paucRecvMsgBuf [nOffset + nMsgLen] == '\n' ||
            pSc->paucRecvMsgBuf [nOffset + nMsgLen] == '\r');
        nMsgLen--)
   {
      pSc->paucRecvMsgBuf [nOffset + nMsgLen] = '\0';
   }
   
   DBG (LOG_INFO, pSc->iUnit, "DEBUG MESSAGE: \"%s\"",
        (char *) &(pSc->paucRecvMsgBuf [nOffset]));
   
} /* avmdma_receive_debugmsg */





/* === definition of private functions =================================== */





/*
        initialize the dma engine for normal operation
        ----------------------------------------------
        Note: This function must be called with privilege level SPLC4B().
*/

static void avmdma_init_for_operation
   (AvmAicSc_t *pSc)
{
   bus_space_tag_t    memt = pSc->resInfo.memTag;
   bus_space_handle_t memh = pSc->resInfo.memHandle;
   bus_space_tag_t    iot  = pSc->resInfo.ioTag;
   bus_space_handle_t ioh  = pSc->resInfo.ioHandle;
   
   pSc->ulDmaIcsr = AVMAIC_AVM_FLAG;
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_INTCSR, pSc->ulDmaIcsr);
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_MCSR,
                      AVMAIC_EN_A2P_TRANSFERS |
                         AVMAIC_EN_P2A_TRANSFERS |
                         AVMAIC_A2P_HI_PRIORITY |
                         AVMAIC_P2A_HI_PRIORITY |
                         AVMAIC_RESET_A2P_FLAGS |
                         AVMAIC_RESET_P2A_FLAGS);

   bus_space_write_1 (iot, ioh, 0x07, 0x30);
   bus_space_write_1 (iot, ioh, 0x10, 0xF0);
   
   pSc->ulRecvMsgBufLength = 0;
   bus_dmamap_sync (pSc->dmaRecv.dmat, pSc->dmaRecv.map, BUS_DMASYNC_PREREAD);
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_RXPTR, pSc->dmaRecv.ulPhysAddr);
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_RXLEN, sizeof (u_int32_t));
   pSc->ulDmaIcsr |= AVMAIC_EN_RX_TC_INT;
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_INTCSR, pSc->ulDmaIcsr);
   
} /* avmdma_init_for_operation */





/*
        enqueue the send init command for a controller
        ----------------------------------------------
*/

static unsigned avmdma_send_init
   (AvmAicSc_t *pSc)
{
   struct mbuf      *pmb;
   AvmLliSendInit_t *pSendInitMsg;

   /* get mbuf for message to controller */
   pmb = kcapi_get_mbuf (2 + 1 + sizeof (AvmLliSendInit_t));
   if (! pmb)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Unable to send %s: Out of mbufs", "AVMAIC_SEND_INIT");
      return (CME_OS_RESOURCE_ERROR);
   }
   
   /* fill the mbuf with the controller message; first two bytes 0 means "no
    * CAPI message", for CAPI messages this were the message length
    */
   mtod (pmb, u_int16_t *) [0] = 0;
   mtod (pmb, unsigned char *) [2] = AVMAIC_SEND_INIT;
   pSendInitMsg = (AvmLliSendInit_t *) &((mtod (pmb, unsigned char *)) [3]);
   C_PUT_DWORD (pSendInitMsg->dwNumApplications, e_iAvmAicMaxApplications);
   C_PUT_DWORD (pSendInitMsg->dwNumNccis,
                e_iAvmAicMaxNcciPerChannel * pSc->aPortData [0].nBChns);
   C_PUT_DWORD (pSendInitMsg->dwBoardNumber,
                pSc->aPortData [0].uUniqueCapiCtlrNum - 1);
   pmb->m_len = 2 + 1 + sizeof (AvmLliSendInit_t);
   
   /* insert controller message into queue for controller */
   if (_IF_QFULL (&(pSc->sendQueue)))
   {
      kcapi_free_mbuf (pmb);
      DBG (LOG_TRACE, pSc->iUnit,
           "Unable to enqueue %s: Send queue full", "AVMAIC_SEND_INIT");
      return (CME_PUT_QUEUE_FULL);
   }
   _IF_ENQUEUE (&(pSc->sendQueue), pmb);
   DBG (LOG_DEBUG, pSc->iUnit, "Message %s enqueued", "AVMAIC_SEND_INIT");
   
   /* send next message to controller if it is not busy */
   avmdma_start_tx (pSc);
   
   return (CAPI_OK);
} /* avmdma_send_init */





/*
        enqueue the send pollack command for a controller
        -------------------------------------------------
*/

static unsigned avmdma_send_pollack
   (AvmAicSc_t *pSc)
{
   struct mbuf *pmb;

   /* get mbuf for message to controller */
   pmb = kcapi_get_mbuf (2 + 1);
   if (! pmb)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Unable to send %s: Out of mbufs", "AVMAIC_SEND_POLLACK");
      return (CME_OS_RESOURCE_ERROR);
   }
   
   /* fill the mbuf with the controller message; first two bytes 0 means "no
    * CAPI message", for CAPI messages this were the message length
    */
   mtod (pmb, u_int16_t *) [0] = 0;
   mtod (pmb, unsigned char *) [2] = AVMAIC_SEND_POLLACK;
   pmb->m_len = 3;
   
   /* insert controller message into queue for controller */
   if (_IF_QFULL (&(pSc->sendQueue)))
   {
      kcapi_free_mbuf (pmb);
      DBG (LOG_TRACE, pSc->iUnit,
           "Unable to enqueue %s: Send queue full", "AVMAIC_SEND_POLLACK");
      return (CME_PUT_QUEUE_FULL);
   }
   _IF_ENQUEUE (&(pSc->sendQueue), pmb);
   DBG (LOG_IRQ, pSc->iUnit, "Message %s enqueued", "AVMAIC_SEND_POLLACK");
   
   /* send next message to controller if it is not busy */
   avmdma_start_tx (pSc);
   
   return (CAPI_OK);
} /* avmdma_send_pollack */





/**
 * Start the transfer of pending messages to the controller if not busy.
 */

static void avmdma_start_tx
   (AvmAicSc_t *pSc)
{
   bus_space_tag_t     memt = pSc->resInfo.memTag;
   bus_space_handle_t  memh = pSc->resInfo.memHandle;
   struct mbuf        *pmbMsg;
   struct mbuf        *pmbData;
   u_int32_t           ulTxLen;
   unsigned char      *p;
   
   /* if the transmitter is already busy accepting a message: wait, do nothing
    * here
    */
   if ((pSc->ulDmaIcsr & AVMAIC_EN_TX_TC_INT) != 0)
   {
      DBG (LOG_IRQ, pSc->iUnit, "Transmitter busy");
      return;
   }
   
   /* the board is ready to accept a new message, dequeue one and send it */
   _IF_DEQUEUE (&(pSc->sendQueue), pmbMsg);
   if (! pmbMsg)
   {
      DBG (LOG_IRQ, pSc->iUnit, "Queue empty");
      /* no message in queue, ready */
      return;
   }
   
   /* distinguish between CAPI messages and control messages for the board;
    * if the first two bytes of a message are zero, it is a control message
    */
   if (mtod (pmbMsg, unsigned short *) [0] == 0)
   {
      bcopy (mtod (pmbMsg, unsigned char *) + 2, pSc->dmaSend.pVirtAddr,
             pmbMsg->m_len - 2);
      p = pSc->dmaSend.pVirtAddr + pmbMsg->m_len - 2;
   }
   /* else: this is a CAPI message and the first two bytes are the message
    * length
    */
   else
   {
      unsigned uCmd;
      size_t   nMsgLen;
      
      /* For security reasons we send Data-B3-Requests without the 64bit data
       * block pointer. The controller might not like messages that long. The
       * CAPI manager checked the message for validity before this function is
       * called.
       */
      uCmd = CAPI_GET_CMD (mtod (pmbMsg, CAPIMsg_t *));
      if (uCmd == CAPI_REQUEST (C_DATA_B3))
      {
         nMsgLen = sizeof (CAPIMsgHead_t) + sizeof (CAPIDataB332Req_t);
         if (nMsgLen > pmbMsg->m_len)
         {
            nMsgLen = pmbMsg->m_len;
         }
      }
      else
      {
         nMsgLen = pmbMsg->m_len;
      }
      
      /* leave one byte for the message command stored later */
      p = avmmisc_mem_put_slice (&(pSc->dmaSend.pVirtAddr [1]),
                                 mtod (pmbMsg, unsigned char *),
                                 nMsgLen);
      
      /* for Data-B3-Requests the data block must follow the CAPI message */
      if (uCmd == CAPI_REQUEST (C_DATA_B3))
      {
         /* store the message command to use */
         pSc->dmaSend.pVirtAddr [0] = AVMAIC_SEND_DATA_B3_REQ;
         
         pmbData = pmbMsg->m_next;
         if (pmbData && pmbData->m_len > 0)
         {
            p = avmmisc_mem_put_slice (p, mtod (pmbData, unsigned char *),
                                       pmbData->m_len);
         }
         else
         {
            DBG (LOG_TRACE, pSc->iUnit,
                 "Got Data-B3-Request without data block");
            p = avmmisc_mem_put_slice (p, NULL, 0);
         }
      }
      else
      {
         /* store the message command to use */
         pSc->dmaSend.pVirtAddr [0] = AVMAIC_SEND_MESSAGE;
      }
   }
   /* the send buffer is now filled and p points after the last byte written */
   DBG ((pSc->dmaSend.pVirtAddr [0] == AVMAIC_SEND_POLLACK)
           ? LOG_IRQ : LOG_DEBUG,
        pSc->iUnit, "Send buffer filled with message 0x%02X",
        (unsigned) (pSc->dmaSend.pVirtAddr [0]));
   
   /* the mbuf(s) is/are no longer needed */
   kcapi_free_mbuf (pmbMsg);
   
   /* save the length of the send buffer */
   pSc->ulSendMsgBufLength = (unsigned long) (p - pSc->dmaSend.pVirtAddr);
   
   /* determine the length rounded up to 4-byte allignment */
   ulTxLen = (pSc->ulSendMsgBufLength + 3) & ~0x00000003;
   
   /* tell the board to accept the new message */
   bus_dmamap_sync (pSc->dmaSend.dmat, pSc->dmaSend.map, BUS_DMASYNC_PREWRITE);
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_TXPTR,
                      pSc->dmaSend.ulPhysAddr);
   avmdma_mem_writel (memt, memh, AVMAIC_AMCC_TXLEN, ulTxLen);
   pSc->ulDmaIcsr |= AVMAIC_EN_TX_TC_INT;
   if (! pSc->fIntrActive)
   {
      avmdma_mem_writel (memt, memh, AVMAIC_AMCC_INTCSR, pSc->ulDmaIcsr);
   }

   DBG ((pSc->dmaSend.pVirtAddr [0] == AVMAIC_SEND_POLLACK)
           ? LOG_IRQ : LOG_DEBUG,
        pSc->iUnit,
        "Board starts accepting message 0x%02X, length %lu (%lu) from addr 0x%p (phys 0x%08zX - 0x%08zX)",
        (unsigned) (pSc->dmaSend.pVirtAddr [0]),
        (unsigned long) pSc->ulSendMsgBufLength, (unsigned long) ulTxLen,
        pSc->dmaSend.pVirtAddr,
        (size_t) (pSc->dmaSend.ulPhysAddr),
        (size_t) (pSc->dmaSend.ulPhysAddr + pSc->ulSendMsgBufLength));

} /* avmdma_start_tx */





/*
        receive and handle a pending message from the controller
        --------------------------------------------------------
        This function is called after a complete message was received from the
        controller. The message is expected to be in the buffer at
        pSc->paucRecvMsgBuf, the length is in pSc->ulRecvMsgBufLength. The first
        byte of the message is the message indicator identifying the information
        in the following bytes.
*/

static void avmdma_handle_rx
   (AvmAicSc_t *pSc)
{
   unsigned char ucCmd;
   
   bus_dmamap_sync (pSc->dmaRecv.dmat, pSc->dmaRecv.map, BUS_DMASYNC_POSTREAD);

   ucCmd = pSc->paucRecvMsgBuf [0];
   switch (ucCmd)
   {
      case AVMAIC_RECEIVE_MESSAGE:
         avmdma_receive_message (pSc);
         break;

      case AVMAIC_RECEIVE_DATA_B3_IND:
         avmdma_receive_data_b3_ind (pSc);
         break;

      case AVMAIC_RECEIVE_START:
         if (pSc->state == AVMAIC_STATE_BUSY)
         {
            pSc->state = AVMAIC_STATE_READY;
            DBG (LOG_TRACE, pSc->iUnit, "Ready to receive data again (%s)",
                 "AVMAIC_RECEIVE_START");
         }
         else
         {
            DBG (LOG_IRQ, pSc->iUnit, "Still ready to receive data (%s)",
                 "AVMAIC_RECEIVE_START");
         }
         (void) avmdma_send_pollack (pSc);
         break;

      case AVMAIC_RECEIVE_STOP:
         DBG (LOG_TRACE, pSc->iUnit, "Unable to receive data (%s)",
              "AVMAIC_RECEIVE_STOP");
         if (pSc->state == AVMAIC_STATE_READY)
         {
            pSc->state = AVMAIC_STATE_BUSY;
         }
         break;

      case AVMAIC_RECEIVE_NEW_NCCI:
         avmdma_receive_new_ncci (pSc);
         break;

      case AVMAIC_RECEIVE_FREE_NCCI:
         avmdma_receive_free_ncci (pSc);
         break;

      case AVMAIC_RECEIVE_INIT:
         avmdma_receive_init (pSc);
         break;

      case AVMAIC_RECEIVE_TASK_READY:
         avmdma_receive_task_ready (pSc);
         break;

      case AVMAIC_RECEIVE_POLL:
         printf ("%s%d: %s: Got unexpected message AVMAIC_RECEIVE_POLL\n",
                 pSc->szDriverName, pSc->iUnit, __FUNCTION__);
         break;

      case AVMAIC_RECEIVE_DEBUGMSG:
         avmdma_receive_debugmsg (pSc);
         break;

      case AVMAIC_RECEIVE_POLLDWORD:
         printf ("%s%d: %s: Got unexpected message AVMAIC_RECEIVE_POLLDWORD\n",
                 pSc->szDriverName, pSc->iUnit, __FUNCTION__);
         break;

      default:
         printf ("%s%d: %s: Unknown message 0x%X from controller\n",
                 pSc->szDriverName, pSc->iUnit, __FUNCTION__, (int) ucCmd);
         break;
   }

} /* avmdma_handle_rx */
