/**
 * @file avmt1.c
 *
 * AvmT1 - Specific functions for AVM-T1 ISA and PCI.
 *
 * Copyright: 2000-2003 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: avmt1.c,v 1.1.2.1 2006/02/28 21:35:10 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/malloc.h>
#include <sys/errno.h>
#include <machine/bus.h>
#include <sys/bus.h>
#include <sys/rman.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/avmio.h>
#include <c4b/avmaic/avmdma.h>
#include <c4b/avmaic/avmaic_misc.h>
#include <c4b/avmaic/avmt1.h>





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





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





/* table of possible irqs and the values to send to the T1 ISA card */
static int g_aiT1IsaIrqTable [16] =
   {
      0,
      0,
      0,
      0x80,                     /* irq 3 */
      0,
      0x90,                     /* irq 5 */
      0,
      0xA0,                     /* irq 7 */
      0,
      0xB0,                     /* irq 9 */
      0xC0,                     /* irq 10 */
      0xD0,                     /* irq 11 */
      0xE0,                     /* irq 12 */
      0,
      0,
      0xF0                      /* irq 15 */
   };



/* --- some board specific constants --- */

#define AVMAIC_HEMA_VERSION_ID  0
#define AVMAIC_HEMA_PAL_ID      0

#define AVMAIC_T1F_IREADY       0x01
#define AVMAIC_T1F_IHALF        0x02
#define AVMAIC_T1F_IFULL        0x04
#define AVMAIC_T1F_IEMPTY       0x08
#define AVMAIC_T1F_IFLAGS       0x0F

#define AVMAIC_T1F_OREADY       0x10
#define AVMAIC_T1F_OHALF        0x20
#define AVMAIC_T1F_OEMPTY       0x40
#define AVMAIC_T1F_OFULL        0x80
#define AVMAIC_T1F_OFLAGS       0xF0

#define AVMAIC_T1_FIFO_INPBSIZE 512
#define AVMAIC_T1_FIFO_OUTBSIZE 256





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





/* --- performing (ISA) board specific parts of CAPI calls --- */

/* send a CAPI message to the controller */
static unsigned avmt1_isa_put_message
   (AvmAicSc_t       *pSc,
    AvmAicPortData_t *pPortData,
    unsigned          uApplID,
    struct mbuf      *pmbMsg);

/* reset and download a controller */
static unsigned avmt1_isa_reset_ctlr
                                        /* CAPI_OK or CAPI error value */
   (AvmAicSc_t *pSc,                    /* I: softc structure ident. the board
                                         */
    size_t               nNumDataBlocks,/* I: Number of data blocks to be sent
                                         *    to the controller; length of
                                         *    paDataBlocks
                                         */
    CAPICtlrDataBlock_t *paDataBlocks); /* I: Array of data blocks to be sent
                                         *    to the controller
                                         */



/* --- handling interrupt calls of the (ISA) controllers --- */

/* the interrupt routine for an i/o based T1 */
static void avmt1_isa_intr
   (AvmAicSc_t *pSc);

/* receive a CAPI message (but not a data-b3-indication) from a controller */
static void avmt1_isa_receive_message
   (AvmAicSc_t *pSc);
   
/* receive a data-b3-indication from an i/o based controller */
static void avmt1_isa_receive_data_b3_ind
   (AvmAicSc_t *pSc);
   
/* receive acknowledge from initialization */
static void avmt1_isa_receive_init
   (AvmAicSc_t *pSc);

/* handle a message for a completed task */
static void avmt1_isa_receive_task_ready
   (AvmAicSc_t *pSc);

/* handle a debugging message */
static void avmt1_isa_receive_debugmsg
   (AvmAicSc_t *pSc);




/* --- T1 ISA specific functions for direct controller communication --- */

/* determine if fast link is enabled at i/o address */
static __inline int avmt1_isa_is_fastlink
   (bus_space_tag_t    t,               /* I: bus tag for i/o port access */
    bus_space_handle_t h);              /* I: bus handle for i/o port access */

/* get status of fifo buffer */
static __inline unsigned char avmt1_isa_fifo_status
   (bus_space_tag_t    t,               /* I: bus tag for i/o port access */
    bus_space_handle_t h);              /* I: bus handle for i/o port access */

/* get slice of data from controller */
static size_t avmt1_isa_get_slice	/* length of the slice read, may be
					 * larger than the buffer size
					 * provided
					 */
   (bus_space_tag_t     t,              /* I: bus tag for i/o port access */
    bus_space_handle_t  h,              /* I: bus handle for i/o port access */
    unsigned char      *paucBuf,	/* O: address of the destination buffer
					 *    for the slice
					 */
    size_t              nBufLen);	/* I: length of the destination buf. */

/* write slice of data to controller */
static int avmt1_isa_put_slice          /* 0: data successfully written
					 * other: write error
					 */
   (bus_space_tag_t      t,
    bus_space_handle_t   h,
    const unsigned char *paucBuf,	/* I: address of the data buffer to
					 *    write
					 */
    size_t               nBufLen);	/* I: length of the data buffer */



/* --- miscellaneous helper functions --- */

/**
 * Check for working T1 ISA at given i/o port resource.
 *
 * @param dev                   I: The device to check.
 * @param pResIoBase            I: The i/o port resource to check.
 * @param iIoBase               I: The i/o port base as an integer.
 * @param iIrq                  I: The assigned irq number.
 * @param uBoardNo              I: The T1 ISA board number (0-3).
 *
 * @retval 0                    A T1 ISA was successfully identified at i/o port
 *                              specified.
 * @retval -1                   There is no T1 ISA at the i/o port or it is not
 *                              working.
 */
static int detect_t1_isa
   (device_t         dev,
    struct resource *pResIoBase,
    int              iIoBase,
    int              iIrq,
    unsigned int     uBoardNo);

/**
 * Check for working T1 PCI at given memory and i/o port resource.
 *
 * @param dev                   I: The device to check.
 * @param pResMem               I: The memory address to check.
 * @param pResIo                I: The i/o port resource to check.
 *
 * @retval 0                    A T1 PCI was successfully identified at memory
 *                              and i/o port specified.
 * @retval -1                   There is no T1 PCI at memory and i/o port or it
 *                              is not working.
 */
static int detect_t1_dma
   (device_t         dev,
    struct resource *pResMem,
    struct resource *pResIo);

/* initialize the softc structure */
static int avmt1_init_softc             /* 0: structure initialized successfully
                                         * errno value: failure
                                         */
   (device_t    dev,                    /* I: the device to initialize */
    AvmAicSc_t *pSc);                   /* I: the softc structure of the device
                                         *    to initialize
                                         */





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





/*
        Probe for a T1 ISA
        ------------------
        
        Parameters:
           dev                  I: device data for the card to probe

        Result:
           0                    card found
           errno value          card not found, specified error occurred
*/

int avmt1_isa_probe
   (device_t dev)
{
   AvmAicSc_t    *pSc;
   unsigned long  ulBase;
   unsigned long  ulCount;
   int            iRes;

   /* initialize softc structure */
   pSc = device_get_softc (dev);
   bzero (pSc, sizeof (*pSc));
   pSc->iUnit = device_get_unit (dev);
   pSc->uT1IsaBoardNo = (device_get_flags (dev) & 0x0F);
   
   /* check for valid i/o port number */
   pSc->resInfo.iRidIo = 0;
   iRes = bus_get_resource (dev, SYS_RES_IOPORT, pSc->resInfo.iRidIo,
                            &ulBase, &ulCount);
   if (iRes != 0)
   {
      device_printf (dev,
                     "probe failed, unable to get i/o port range (error code "
                        "%d)\n",
                      iRes);
      return (ENXIO);
   }
   pSc->resInfo.iIoBase = (int) ulBase;
   if (! ((ulBase & 0x0007) == 0 && (ulBase & 0x0030) != 0x0030))
   {
      device_printf (dev, "probe failed, invalid i/o port base 0x%03x\n",
                     pSc->resInfo.iIoBase);
      return (ENXIO);
   }

   /* allocate required i/o range */
   (void) bus_set_resource (dev, SYS_RES_IOPORT,
                            pSc->resInfo.iRidIo, pSc->resInfo.iIoBase,
                            AVMAIC_IO_PORT_RANGE);
   pSc->resInfo.pResIo = bus_alloc_resource
                            (dev, SYS_RES_IOPORT,
                             &(pSc->resInfo.iRidIo),
                             0UL, ~0UL, 1, RF_ACTIVE);
   if (! pSc)
   {
      device_printf (dev,
                     "probe failed, unable to allocate i/o port range 0x%03x, "
                        "0x%02x\n",
                     pSc->resInfo.iIoBase, AVMAIC_IO_PORT_RANGE);
      return (ENXIO);
   }
   
   /* check for valid irq */
   pSc->resInfo.iRidIrq = 0;
   iRes = bus_get_resource (dev, SYS_RES_IRQ, pSc->resInfo.iRidIrq,
                            &ulBase, &ulCount);
   if (iRes != 0)
   {
      device_printf (dev,
                     "probe failed, unable to get irq number (error code "
                        "%d)\n",
                      iRes);
      avmmisc_detach (dev);
      return (ENXIO);
   }
   pSc->resInfo.iIrq = (int) ulBase;
   if (g_aiT1IsaIrqTable [pSc->resInfo.iIrq & 0x0F] == 0)
   {
      device_printf (dev, "probe failed, invalid irq %d\n", pSc->resInfo.iIrq);
      avmmisc_detach (dev);
      return (ENXIO);
   }

   /* allocate required irq */
   (void) bus_set_resource (dev, SYS_RES_IRQ,
                            pSc->resInfo.iRidIrq, pSc->resInfo.iIrq, 1);
   pSc->resInfo.pResIrq = bus_alloc_resource
                             (dev, SYS_RES_IRQ,
                              &(pSc->resInfo.iRidIrq),
                              0UL, ~0UL, 1, RF_ACTIVE);
   if (! pSc->resInfo.pResIrq)
   {
      device_printf (dev, "probe failed, unable to allocate irq %d\n",
                     pSc->resInfo.iIrq);
      avmmisc_detach (dev);
      return (ENXIO);
   }

   /* check for existing and working card */
   if (detect_t1_isa (dev, pSc->resInfo.pResIo,
                      pSc->resInfo.iIoBase, pSc->resInfo.iIrq,
                      pSc->uT1IsaBoardNo) != 0)
   {
      avmmisc_detach (dev);
      return (ENXIO);
   }
   
   /* finally release all previously allocated resources */
   avmmisc_detach (dev);

   /* declare the device to be a AVM-T1 ISA */
   device_set_flags (dev, 0x10 | pSc->uT1IsaBoardNo);
   device_set_desc (dev, avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_T1_ISA));
   
   /* if we get here, the card is identified successfully */
   return (0);
} /* avmt1_isa_probe */





/*
        Attach a T1 ISA
        ---------------
        The attach function of a T1 ISA has not much to do. It only may reset
        the controller and ask some bytes of information. All real actions to
        attach the controller to the CAPI manager is done when downloading.
        
        Parameters:
           dev                  I: device data for card to attach

        Result:
           0                    card successfully attached
           errno value          error attaching card
*/

int avmt1_isa_attach
   (device_t dev)
{
   AvmAicSc_t *pSc = device_get_softc (dev);
   int         iRes;
   
   DBG (LOG_TRACE, pSc->iUnit, "Attaching \"%s\"", device_get_desc (dev));

   /* fill the softc structure */
   pSc->iUnit = device_get_unit (dev);
   pSc->cardType = AVMAIC_CARD_TYPE_T1_ISA;
   iRes = avmt1_init_softc (dev, pSc);
   if (iRes != 0)
   {
      avmmisc_detach (dev);
      return (iRes);
   }

   /* allocate required i/o range */
   pSc->resInfo.pResIo = bus_alloc_resource
                            (dev, SYS_RES_IOPORT,
                             &(pSc->resInfo.iRidIo),
                             0UL, ~0UL, 1, RF_ACTIVE);
   if (! pSc->resInfo.pResIo)
   {
      device_printf (dev,
                     "attach failed, unable to allocate i/o port range "
                        "0x%03x-0x%03x\n",
                     pSc->resInfo.iIoBase,
                     (unsigned int) rman_get_end (pSc->resInfo.pResIo));
      avmmisc_detach (dev);
      return (ENXIO);
   }
   pSc->resInfo.ioTag    = rman_get_bustag (pSc->resInfo.pResIo);
   pSc->resInfo.ioHandle = rman_get_bushandle (pSc->resInfo.pResIo);
   
   /* the T1 ISA does not use memory mapped i/o */
   pSc->resInfo.iMemBase  = 0;
   pSc->resInfo.iRidMem   = 0;
   pSc->resInfo.pResMem   = NULL;
   pSc->resInfo.memTag    = 0;
   pSc->resInfo.memHandle = 0;

   /* allocate required irq */
   pSc->resInfo.pResIrq = bus_alloc_resource
                             (dev, SYS_RES_IRQ,
                              &(pSc->resInfo.iRidIrq),
                              0UL, ~0UL, 1, RF_ACTIVE);
   if (! pSc->resInfo.pResIrq)
   {
      device_printf (dev, "attach failed, unable to allocate irq %d\n",
                     pSc->resInfo.iIrq);
      avmmisc_detach (dev);
      return (ENXIO);
   }

   /* register interrupt routine */
   (void) bus_setup_intr (dev, pSc->resInfo.pResIrq,
                          INTR_TYPE_NET | INTR_MPSAFE,
                          (driver_intr_t *) avmt1_isa_intr, pSc,
                          &(pSc->resInfo.hIrqCookie));

   /* reset card to get into defined state */
   avmio_t1_disable_interrupt (pSc->resInfo.ioTag, pSc->resInfo.ioHandle);
   avmio_reset (pSc->resInfo.ioTag, pSc->resInfo.ioHandle);

   /* get card revision number */
   pSc->iClass = avmio_inp (pSc->resInfo.ioTag,
                            pSc->resInfo.ioHandle,
                            AVMAIC_REG_ANALYSE);
   pSc->iRevision = avmio_inp (pSc->resInfo.ioTag,
                               pSc->resInfo.ioHandle,
                               AVMAIC_REG_REVISION);

   DBG (LOG_INFO, pSc->iUnit,
        "ISDN controller \"%s\" identified, class %d, revision %d",
	pSc->szCardName, pSc->iClass, pSc->iRevision);

   return (0);
} /* avmt1_isa_attach */





/*
        attach a T1 PCI
        ---------------
        
        Parameters:
           dev                  I: device data for card to attach

        Result:
           0                    card successfully attached
           errno value          error attaching card
*/

int avmt1_pci_attach
   (device_t dev)
{
   AvmAicSc_t       *pSc = device_get_softc (dev);
   int               iRes;
   unsigned long     ulBase;
   unsigned long     ulCount;
   
   DBG (LOG_TRACE, pSc->iUnit, "Attaching \"%s\"", device_get_desc (dev));

   /* fill the softc structure */
   pSc->iUnit = device_get_unit (dev);
   pSc->cardType = AVMAIC_CARD_TYPE_T1_PCI;
   iRes = avmt1_init_softc (dev, pSc);
   if (iRes != 0)
   {
      avmmisc_detach (dev);
      return (iRes);
   }

   /* allocate required i/o range */
   pSc->resInfo.iRidIo = 0x14;
   iRes = bus_get_resource (dev, SYS_RES_IOPORT, pSc->resInfo.iRidIo,
                            &ulBase, &ulCount);
   if (iRes != 0)
   {
      device_printf (dev, "attach failed, unable to get i/o port range\n");
      avmmisc_detach (dev);
      return (iRes);
   }
   pSc->resInfo.iIoBase = (int) ulBase;
   pSc->resInfo.pResIo = bus_alloc_resource
                            (dev, SYS_RES_IOPORT,
                             &(pSc->resInfo.iRidIo),
                             0UL, ~0UL, 1, RF_ACTIVE);
   if (! pSc->resInfo.pResIo)
   {
      device_printf (dev,
                     "attach failed, unable to allocate i/o port range "
                        "0x%04x-0x%04x\n",
                     pSc->resInfo.iIoBase,
                     (unsigned int) rman_get_end (pSc->resInfo.pResIo));
      avmmisc_detach (dev);
      return (ENXIO);
   }
   pSc->resInfo.ioTag    = rman_get_bustag (pSc->resInfo.pResIo);
   pSc->resInfo.ioHandle = rman_get_bushandle (pSc->resInfo.pResIo);
   
   /* allocate i/o memory range */
   pSc->resInfo.iRidMem = 0x10;
   iRes = bus_get_resource (dev, SYS_RES_MEMORY, pSc->resInfo.iRidMem,
                            &ulBase, &ulCount);
   if (iRes != 0)
   {
      device_printf (dev, "attach failed, unable to get i/o memory range\n");
      avmmisc_detach (dev);
      return (iRes);
   }
   pSc->resInfo.iMemBase = (int) ulBase;
   pSc->resInfo.pResMem = bus_alloc_resource
                             (dev, SYS_RES_MEMORY, &(pSc->resInfo.iRidMem),
                              0UL, ~0UL, 1, RF_ACTIVE);
   if (! pSc->resInfo.pResMem)
   {
      device_printf (dev,
                     "attach failed, unable to allocate i/o memory at "
                        "0x%08x\n",
                     pSc->resInfo.iMemBase);
      avmmisc_detach (dev);
      return (iRes);
   }
   pSc->resInfo.memTag    = rman_get_bustag (pSc->resInfo.pResMem);
   pSc->resInfo.memHandle = rman_get_bushandle (pSc->resInfo.pResMem);
   
   /* allocate required irq */
   pSc->resInfo.iRidIrq = 0;
   iRes = bus_get_resource (dev, SYS_RES_IRQ, pSc->resInfo.iRidIrq,
                            &ulBase, &ulCount);
   if (iRes != 0)
   {
      device_printf (dev, "attach failed, unable to get irq\n");
      avmmisc_detach (dev);
      return (ENXIO);
   }
   pSc->resInfo.iIrq = (int) ulBase;
   pSc->resInfo.pResIrq = bus_alloc_resource
                             (dev, SYS_RES_IRQ, &(pSc->resInfo.iRidIrq),
                              0UL, ~0UL, 1, RF_ACTIVE | RF_SHAREABLE);
   if (! pSc->resInfo.pResIrq)
   {
      device_printf (dev, "attach failed, unable to allocate irq %d\n",
                     pSc->resInfo.iIrq);
      avmmisc_detach (dev);
      return (ENXIO);
   }

   /* check for working card at assigned resources */
   if (detect_t1_dma (dev, pSc->resInfo.pResMem, pSc->resInfo.pResIo) != 0)
   {
      avmmisc_detach (dev);
      return (ENXIO);
   }

   /* register interrupt routine */
   (void) bus_setup_intr (dev, pSc->resInfo.pResIrq,
                          INTR_TYPE_NET | INTR_MPSAFE,
                          (driver_intr_t *) avmdma_intr, pSc,
                          &(pSc->resInfo.hIrqCookie));

   /* reset card to get into defined state */
   avmdma_reset (pSc->resInfo.memTag, pSc->resInfo.memHandle,
                 pSc->resInfo.ioTag, pSc->resInfo.ioHandle,
                 pSc->cardType);
   pSc->ulDmaIcsr = 0;
   
   /* get card revision number */
   pSc->iClass = avmio_inp (pSc->resInfo.ioTag,
                            pSc->resInfo.ioHandle,
                            AVMAIC_REG_ANALYSE);
   pSc->iRevision = avmio_inp (pSc->resInfo.ioTag,
                               pSc->resInfo.ioHandle,
                               AVMAIC_REG_REVISION);

   DBG (LOG_INFO, pSc->iUnit,
        "ISDN controller \"%s\" identified, class %d, revision %d",
	pSc->szCardName, pSc->iClass, pSc->iRevision);

   return (0);
} /* avmt1_pci_attach */





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





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

static unsigned avmt1_isa_put_message
   (AvmAicSc_t       *pSc,
    AvmAicPortData_t *pPortData,
    unsigned          uApplID,
    struct mbuf      *pmbMsg)
{
   bus_space_tag_t     t = pSc->resInfo.ioTag;
   bus_space_handle_t  h = pSc->resInfo.ioHandle;
   unsigned            uRes;
   struct mbuf        *pmbData;
   unsigned            uCapiCmd;
   u_int32_t           dwCid;
   u_int8_t            bCtlrMsg;
   size_t              nMsgLen;
   
   /* extract some data needed later */
   uCapiCmd = CAPI_GET_CMD ((CAPIMsg_t *) (pmbMsg->m_data));
   dwCid    = CAPI_GET_CID ((CAPIMsg_t *) (pmbMsg->m_data));
   
   /* 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);
   }
   
   /* distinguish between Data-B3-Requests and other messages */
   if (uCapiCmd == CAPI_REQUEST (C_DATA_B3))
   {
      /* Note: For security reasons we send this message without the 64bit data
       *       block pointer. The CAPI manager checked the message for validity
       *       before this function is called.
       */
      bCtlrMsg = AVMAIC_SEND_DATA_B3_REQ;
      nMsgLen = sizeof (CAPIMsgHead_t) + sizeof (CAPIDataB332Req_t);
      if (nMsgLen > pmbMsg->m_len)
      {
         nMsgLen = pmbMsg->m_len;
      }
   }
   else
   {
      bCtlrMsg = AVMAIC_SEND_MESSAGE;
      nMsgLen = pmbMsg->m_len;
   }
   
   /* now start sending the CAPI message to the controller */
   uRes = CAPI_OK;
   if (avmio_safe_put_byte (t, h, bCtlrMsg) < 0)
   {
      DBG (LOG_ERROR, pSc->iUnit, "Unable to send %s",
           (uCapiCmd == CAPI_REQUEST (C_DATA_B3))
              ? "AVMAIC_SEND_DATA_B3_REQ" : "AVMAIC_SEND_MESSAGE");
      return (CME_BUSY);
   }
   if (avmt1_isa_put_slice (t, h,
                            (unsigned char *) (pmbMsg->m_data), nMsgLen) < 0)
   {
      uRes = CME_OS_RESOURCE_ERROR;
   }
   
   /* if the message is a data-b3-request the data block must follow */
   if (uCapiCmd == CAPI_REQUEST (C_DATA_B3))
   {
      pmbData = pmbMsg->m_next;
      if (pmbData)
      {
         if (avmt1_isa_put_slice (t, h,
                                  (unsigned char *) (pmbData->m_data),
                                  pmbData->m_len) < 0)
         {
            uRes = CME_OS_RESOURCE_ERROR;
         }
      }
      else
      {
         if (avmt1_isa_put_slice (t, h, NULL, 0) < 0)
         {
            uRes = CME_OS_RESOURCE_ERROR;
         }
      }
   }
   
   /* if the result is CAPI_OK free the mbuf as it is not needed anymore */
   if (uRes == CAPI_OK)
   {
      kcapi_free_mbuf (pmbMsg);
   }
   else
   {
      DBG (LOG_ERROR, pSc->iUnit, "Error 0x%04X putting CAPI message 0x%04X",
	   uRes, uCapiCmd);
   }

   return (uRes);
} /* avmt1_isa_put_message */





/*
        reset and download a controller
        -------------------------------
*/

static unsigned avmt1_isa_reset_ctlr
                                        /* CAPI_OK or CAPI error value */
   (AvmAicSc_t          *pSc,           /* I: softc structure ident. the board
                                         */
    size_t               nNumDataBlocks,/* I: Number of data blocks to be sent
                                         *    to the controller; length of
                                         *    paDataBlocks
                                         */
    CAPICtlrDataBlock_t *paDataBlocks)  /* I: Array of data blocks to be sent
                                         *    to the controller
                                         */
{
   AvmAicPortData_t   *pPortData;
   bus_space_tag_t     t = pSc->resInfo.ioTag;
   bus_space_handle_t  h = pSc->resInfo.ioHandle;
   unsigned            uRes;
   
   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 */
   avmio_t1_disable_interrupt (t, h);
   avmio_reset (t, h);
   avmio_reset (t, h);
   pSc->state = AVMAIC_STATE_INITIALIZING;
   pSc->nPortsInitialized = 0;

   /* 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 T4 file and load
    * it onto the controller
    */
   avmio_reset (t, h);
   uRes = avmmisc_load_t4_file (pSc,
			        paDataBlocks [0].nLenDataBlock,
			        paDataBlocks [0].paucDataBlock);
   if (uRes != CAPI_OK)
   {
      avmio_reset (t, h);
      pSc->state = AVMAIC_STATE_DOWN;
      return (uRes);
   }
   DBG (LOG_INFO, pSc->iUnit, "Firmware successfully loaded");

   /* disable generation of interrupts by the controller so the following
    * action is not disturbed by the controller
    */
   avmio_t1_disable_interrupt (t, h);

   /* 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)
      {
	 avmio_reset (t, h);
      	 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);
      avmio_reset (t, h);
      pSc->state = AVMAIC_STATE_DOWN;
      return (CME_OS_RESOURCE_ERROR);
   }

   /* enable irq on controller */
   (void) bus_space_write_1 (t, h, AVMAIC_REG_INSTAT, 0x00);
   (void) bus_space_write_1 (t, h, AVMAIC_REG_INSTAT, 0x02);
   (void) bus_space_write_1 (t, h, AVMAIC_REG_T1_IRQMASTER, 0x08);

   pPortData = &(pSc->aPortData [0]);
   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");
   bzero (pPortData->acVersionBuf, sizeof (pPortData->acVersionBuf));
   pSc->uRc = 0xFFFFFFFF;
   pSc->fWaitingForRc = 1;
   if (avmio_safe_put_byte (t, h, AVMAIC_SEND_INIT) < 0)
   {
      pSc->fWaitingForRc = 0;
      avmio_reset (t, h);
      pSc->state = AVMAIC_STATE_DOWN;
      printf ("%s%d: Unable to do controller initialization after download, controller busy\n",
              pSc->szDriverName, pSc->iUnit);
      return (CME_OS_RESOURCE_ERROR);
   }
   if (avmio_put_word (t, h, e_iAvmAicMaxApplications) < 0 ||
       avmio_put_word (t, h,
                       e_iAvmAicMaxNcciPerChannel * pPortData->nBChns) < 0 ||
       avmio_put_word (t, h, pPortData->uUniqueCapiCtlrNum - 1) < 0)
   {
      pSc->uRc = CME_OS_RESOURCE_ERROR;
   }

   /* wait for controller response to initialization request */
   (void) cv_timedwait (&(pSc->cvNotify), &(pSc->mtxAccess), 5 * hz);
   pSc->fWaitingForRc = 0;
   if (pSc->uRc != CAPI_OK)
   {
      pSc->state = AVMAIC_STATE_DOWN;
      avmio_reset (t, h);
      uRes = pSc->uRc;
      printf ("%s%d: Controller initialization after download failed\n",
	      pSc->szDriverName, pSc->iUnit);
      return (uRes);
   }

   /* the version buffer of the port data structure is filled now, translate it
    * into a better usable format
    */
   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);
} /* avmt1_isa_reset_ctlr */





/*
        the interrupt routine for an i/o based T1
        -----------------------------------------
*/

static void avmt1_isa_intr
   (AvmAicSc_t *pSc)
{
   bus_space_tag_t    t = pSc->resInfo.ioTag;
   bus_space_handle_t h = pSc->resInfo.ioHandle;
   int                iTimeout;
   int                cmd;

   DBG (LOG_IRQ, pSc->iUnit, "interrupt handler called");
        
   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;
   
   /* check if there is really data to fetch for this controller */
   if (! avmio_rx_full (t, h))
   {
      pSc->fIntrActive = 0;
      mtx_unlock (&(pSc->mtxAccess));
      DBG (LOG_IRQ, pSc->iUnit, "receive buffer not full");
      return;
   }

   /* loop until the receive buffer of the controller is empty */
   do
   {
      /* read command from controller */
      iTimeout = AVMAIC_MAX_IO_WAIT_BYTE;
      cmd = avmio_get_byte (t, h, &iTimeout);

      /* evaluate command */
      switch (cmd)
      {
         case AVMAIC_RECEIVE_MESSAGE:
            avmt1_isa_receive_message (pSc);
            break;

         case AVMAIC_RECEIVE_DATA_B3_IND:
            avmt1_isa_receive_data_b3_ind (pSc);
            break;

         case AVMAIC_RECEIVE_NEW_NCCI:
            avmio_receive_new_ncci (pSc);
            break;

         case AVMAIC_RECEIVE_FREE_NCCI:
            avmio_receive_free_ncci (pSc);
	    break;

         case AVMAIC_RECEIVE_START:
            iTimeout = AVMAIC_MAX_IO_WAIT_BYTE;
            (void) avmio_put_byte (t, h, AVMAIC_SEND_POLLACK, &iTimeout);
            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");
            }
	    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_INIT:
            avmt1_isa_receive_init (pSc);
	    break;

         case AVMAIC_RECEIVE_TASK_READY:
            avmt1_isa_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:
            avmt1_isa_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;

         case 0xFF:
            printf ("%s%d: %s: card reset?\n",
                    pSc->szDriverName, pSc->iUnit, __FUNCTION__);
            break;

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

   } while (avmio_rx_full (t, h));

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





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

static void avmt1_isa_receive_message
   (AvmAicSc_t *pSc)
{
   AvmAicPortData_t   *pPortData = &(pSc->aPortData [0]);
   bus_space_tag_t     t = pSc->resInfo.ioTag;
   bus_space_handle_t  h = pSc->resInfo.ioHandle;
   unsigned            uApplID;
   unsigned            uUniqueCtlrNum;
   struct mbuf        *pmbMsg = NULL;
   size_t              nMsgLen;

   DBG (LOG_DEBUG, pSc->iUnit, "Got %s", "AVMAIC_RECEIVE_MESSAGE");

   /* first read the application id from the controller */
   uApplID = (unsigned) avmio_get_word (t, h);
   
   /* read CAPI message from the controller to buffer for the card */
   nMsgLen = avmt1_isa_get_slice (t, h,
                                  pSc->paucRecvMsgBuf, AVMAIC_MSGBUF_SIZE);
   if (nMsgLen > AVMAIC_MSGBUF_SIZE)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Received %zu message bytes instead of maximum %u",
           nMsgLen, (unsigned) AVMAIC_MSGBUF_SIZE);
      nMsgLen = AVMAIC_MSGBUF_SIZE;
   }
   
   /* allocate an mbuf for the message */
   pmbMsg = kcapi_get_mbuf (nMsgLen);
   if (pmbMsg)
   {
      /* now copy the message into the mbuf */
      bcopy (pSc->paucRecvMsgBuf, 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 ((CAPIMsg_t *) (pSc->paucRecvMsgBuf)),
              nMsgLen);
   }
   
   DBG (LOG_DEBUG, pSc->iUnit, "Got CAPI message 0x%04X, length %zu",
        (unsigned) CAPI_GET_CMD ((CAPIMsg_t *) (pSc->paucRecvMsgBuf)),
        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));
   
} /* avmt1_isa_receive_message */
   




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

static void avmt1_isa_receive_data_b3_ind
   (AvmAicSc_t *pSc)
{
   AvmAicPortData_t   *pPortData = &(pSc->aPortData [0]);
   bus_space_tag_t     t = pSc->resInfo.ioTag;
   bus_space_handle_t  h = pSc->resInfo.ioHandle;
   unsigned            uApplID;
   unsigned            uUniqueCtlrNum;
   struct mbuf        *pmbMsg;
   struct mbuf        *pmbData;
   size_t              nMsgLen;
   size_t              nDataLen;

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

   /* first read the application id from the controller */
   uApplID = (unsigned) avmio_get_word (t, h);
   
   /* read CAPI message from the controller to buffer for the card */
   nMsgLen = avmt1_isa_get_slice
                (t, h, pSc->paucRecvMsgBuf, AVMAIC_MSGBUF_SIZE);
   if (nMsgLen > AVMAIC_MSGBUF_SIZE)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Received %zu message bytes instead of maximum %u",
           nMsgLen, (unsigned) AVMAIC_MSGBUF_SIZE);
      nMsgLen = AVMAIC_MSGBUF_SIZE;
   }

   /* 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);
   }
   
   /* now read the Data-B3 block, store it behind the message */
   nDataLen = avmt1_isa_get_slice (t, h, &(pSc->paucRecvMsgBuf [nMsgLen]),
                                   AVMAIC_MSGBUF_SIZE - nMsgLen);
   if (nMsgLen + nDataLen > AVMAIC_MSGBUF_SIZE)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Received %zu Data-B3 bytes instead of maximum %zu",
           nDataLen, (size_t) AVMAIC_MSGBUF_SIZE - nMsgLen);
      nDataLen = AVMAIC_MSGBUF_SIZE - nMsgLen;
   }

   /* allocate an mbuf for the message itself */
   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 (pSc->paucRecvMsgBuf, 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 ((CAPIMsg_t *) (pSc->paucRecvMsgBuf)),
              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)
      {
         bcopy (&(pSc->paucRecvMsgBuf [nMsgLen]), pmbData->m_data, nDataLen);
         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,
                     nDataLen);
      }
      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__,
                 (unsigned) 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);
   }
   
   /* finally forward the complete 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));

} /* avmt1_isa_receive_data_b3_ind */




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

static void avmt1_isa_receive_init
   (AvmAicSc_t *pSc)
{
   AvmAicPortData_t *pPortData = &(pSc->aPortData [pSc->nPortsInitialized]);

   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;
   }
   
   pPortData->nLenVersion =
      avmt1_isa_get_slice (pSc->resInfo.ioTag,
                           pSc->resInfo.ioHandle,
		           pPortData->acVersionBuf,
		           sizeof (pPortData->acVersionBuf));
   if (pPortData->nLenVersion > sizeof (pPortData->acVersionBuf))
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Received %zu version bytes instead of maximum %zu",
           pPortData->nLenVersion, sizeof (pPortData->acVersionBuf));
      pPortData->nLenVersion = sizeof (pPortData->acVersionBuf);
   }
   else
   {
      DBG (LOG_DEBUG, pSc->iUnit, "Got version buffer of length %zu",
           pPortData->nLenVersion);
   }
   
   /* mark the one and only 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++;
   
   /* the board is now 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;

   /* 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));
      }
   }

} /* avmt1_isa_receive_init */





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

static void avmt1_isa_receive_task_ready
   (AvmAicSc_t *pSc)
{
   bus_space_tag_t    t = pSc->resInfo.ioTag;
   bus_space_handle_t h = pSc->resInfo.ioHandle;
   unsigned           uApplID;
   size_t             nMsgLen;

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

   /* read the data for the controller message */
   uApplID = avmio_get_word (t, h);
   nMsgLen = avmt1_isa_get_slice
                (t, h, pSc->paucRecvMsgBuf, AVMAIC_MSGBUF_SIZE);

   /* check for avoided buffer overflow */
   if (nMsgLen >= AVMAIC_MSGBUF_SIZE)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Received %zu task bytes instead of maximum %u",
           nMsgLen, (unsigned) (AVMAIC_MSGBUF_SIZE - 1));
      nMsgLen = AVMAIC_MSGBUF_SIZE - 1;
   }
   
   pSc->paucRecvMsgBuf [nMsgLen] = '\0';
   for ( ;
        nMsgLen > 0 &&
           (pSc->paucRecvMsgBuf [nMsgLen] == '\n' ||
            pSc->paucRecvMsgBuf [nMsgLen] == '\r');
        nMsgLen--)
   {
      pSc->paucRecvMsgBuf [nMsgLen] = '\0';
   }
   
   DBG (LOG_INFO, pSc->iUnit, "Task \"%s\" ready for appl. id %u",
        (char *) &(pSc->paucRecvMsgBuf [0]), uApplID);
   
} /* avmt1_isa_receive_task_ready */





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

static void avmt1_isa_receive_debugmsg
   (AvmAicSc_t *pSc)
{
   bus_space_tag_t    t = pSc->resInfo.ioTag;
   bus_space_handle_t h = pSc->resInfo.ioHandle;
   size_t             nMsgLen;

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

   /* read the data for the controller message */
   nMsgLen = avmt1_isa_get_slice
                (t, h, pSc->paucRecvMsgBuf, AVMAIC_MSGBUF_SIZE);

   /* check for avoided buffer overflow */
   if (nMsgLen >= AVMAIC_MSGBUF_SIZE)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Received %zu debug message bytes instead of maximum %u",
           nMsgLen, (unsigned) (AVMAIC_MSGBUF_SIZE - 1));
      nMsgLen = AVMAIC_MSGBUF_SIZE - 1;
   }
   
   pSc->paucRecvMsgBuf [nMsgLen] = '\0';
   for ( ;
        nMsgLen > 0 &&
           (pSc->paucRecvMsgBuf [nMsgLen] == '\n' ||
            pSc->paucRecvMsgBuf [nMsgLen] == '\r');
        nMsgLen--)
   {
      pSc->paucRecvMsgBuf [nMsgLen] = '\0';
   }
   
   DBG (LOG_INFO, pSc->iUnit, "DEBUG MESSAGE: \"%s\"",
        (char *) &(pSc->paucRecvMsgBuf [0]));
   
} /* avmt1_isa_receive_debugmsg */





/*
        determine if fast link is enabled at i/o address
        ------------------------------------------------
        
        Parameter:
           t                    I: bus tag for i/o port access
           h                    I: bus handle for i/o port access
        
        Result:
           0                    fast link is not enabled
           1                    fast link is enabled
*/

static __inline int avmt1_isa_is_fastlink
   (bus_space_tag_t    t,
    bus_space_handle_t h)
{
   return ((bus_space_read_1 (t, h, AVMAIC_REG_T1_IDENT) & ~0x82) == 0x01);
} /* avmt1_isa_is_fastlink */




   
/*
        get status of fifo buffer
        -------------------------
        Parameter:
           t                    I: bus tag for i/o port access
           h                    I: bus handle for i/o port access
        
        Result:
           the current content of the fifo status register
*/

static __inline unsigned char avmt1_isa_fifo_status
   (bus_space_tag_t    t,
    bus_space_handle_t h)
{
   return (bus_space_read_1 (t, h, AVMAIC_REG_T1_FIFOSTAT));
} /* avmt1_isa_fifo_status */





/*
	get slice of data from controller
	---------------------------------

	Parameter:
           t                    I: bus tag for i/o port access
           h                    I: bus handle for i/o port access
	   paucBuf		O: address of destination buffer for the slice
	   nBufLen		I: length of the destination buffer

	Result:
	   length of the slice read, may be larger than nBufLen
*/

static size_t avmt1_isa_get_slice
   (bus_space_tag_t     t,
    bus_space_handle_t  h,
    unsigned char      *paucBuf,
    size_t              nBufLen)
{
   unsigned int uLen;
   int          i;
   int          status;
   int          iTimeout = AVMAIC_MAX_IO_WAIT_SLICE;

   /* read length of pending data */
   uLen = avmio_get_word (t, h);

   /* if the destination buffer is not large enough, first fill only the buffer
    */
   if (uLen <= nBufLen)
   {
      i = uLen;
   }
   else
   {
      i = nBufLen;
   }
   if (avmt1_isa_is_fastlink (t, h))
   {
      while (i > 0)
      {
         status = avmt1_isa_fifo_status (t, h) &
                  (AVMAIC_T1F_IREADY | AVMAIC_T1F_IHALF);
         if (i >= AVMAIC_T1_FIFO_INPBSIZE)
         {
            status |= AVMAIC_T1F_IFULL;
         }
         switch (status)
         {
            case AVMAIC_T1F_IREADY | AVMAIC_T1F_IHALF | AVMAIC_T1F_IFULL:
               bus_space_read_multi_1 (t, h, AVMAIC_REG_READ,
                                       paucBuf, AVMAIC_T1_FIFO_INPBSIZE);
               paucBuf += AVMAIC_T1_FIFO_INPBSIZE;
               i -= AVMAIC_T1_FIFO_INPBSIZE;
               break;
               
            case AVMAIC_T1F_IREADY | AVMAIC_T1F_IHALF:
               bus_space_read_multi_1 (t, h, AVMAIC_REG_READ, paucBuf, i);
               paucBuf += i;
               i = 0;
               break;
               
            default:
               *paucBuf++ = avmio_get_byte (t, h, &iTimeout);
               i--;
               break;
         }
      }
   }
   else
   {
      while (i-- > 0)
      {
         *paucBuf++ = avmio_get_byte (t, h, &iTimeout);
      }
   }

   /* if the destination buffer is too short, the remaining bytes are read from
    * the controller but discarded
    */
   if (uLen > nBufLen)
   {
      i = uLen - nBufLen;
      while (i-- > 0)
      {
	 (void) avmio_get_byte (t, h, &iTimeout);
      }
   }

   /* return the real length of the slice */
   return (uLen);
} /* avmt1_isa_get_slice */





/*
	write slice of data to controller
	---------------------------------

	Parameter:
           t                    I: bus tag for i/o port access
           h                    I: bus handle for i/o port access
	   paucBuf		I: address of the data buffer to write
	   nBufLen		I: length of the data buffer

	Result:
	   0			data written successfully
	   other		write error
*/

static int avmt1_isa_put_slice
   (bus_space_tag_t      t,
    bus_space_handle_t   h,
    const unsigned char *paucBuf,
    size_t               nBufLen)
{
   int i;
   int status;
   int iTimeout = AVMAIC_MAX_IO_WAIT_SLICE;

   /* first write length of the buffer */
   (void) avmio_put_word (t, h, nBufLen);

   /* now write every byte of the data buffer specified */
   i = (int) nBufLen;
   if (avmt1_isa_is_fastlink (t, h))
   {
      while (i > 0)
      {
         status = avmt1_isa_fifo_status (t, h) &
                  (AVMAIC_T1F_OREADY | AVMAIC_T1F_OHALF);
         if (i >= AVMAIC_T1_FIFO_OUTBSIZE)
         {
            status |= AVMAIC_T1F_OEMPTY;
         }
         switch (status)
         {
            case AVMAIC_T1F_OREADY | AVMAIC_T1F_OHALF | AVMAIC_T1F_OEMPTY:
               bus_space_write_multi_1 (t, h, AVMAIC_REG_WRITE,
                                        paucBuf, AVMAIC_T1_FIFO_OUTBSIZE);
               paucBuf += AVMAIC_T1_FIFO_OUTBSIZE;
               i -= AVMAIC_T1_FIFO_OUTBSIZE;
               break;
               
            case AVMAIC_T1F_OREADY | AVMAIC_T1F_OHALF:
               bus_space_write_multi_1 (t, h, AVMAIC_REG_WRITE,
                                        paucBuf, i);
               paucBuf += i;
               i = 0;
               break;
               
            default:
               if (avmio_put_byte (t, h, *paucBuf, &iTimeout) != 0)
               {
                  return (-1);
               }
               ++paucBuf;
               break;
         }
      }
   }
   else
   {
      while (i-- > 0)
      {
         if (avmio_put_byte (t, h, *paucBuf, &iTimeout) != 0)
         {
            return (-1);
         }
         ++paucBuf;
      }
   }

   return (0);
} /* avmt1_isa_put_slice */





/**
 * Check for working T1 ISA at given i/o port resource.
 *
 * @param dev                   I: The device to check.
 * @param pResIoBase            I: The i/o port resource to check.
 * @param iIoBase               I: The i/o port base as an integer.
 * @param iIrq                  I: The assigned irq number.
 * @param uBoardNo              I: The T1 ISA board number (0-3).
 *
 * @retval 0                    A T1 ISA was successfully identified at i/o port
 *                              specified.
 * @retval -1                   There is no T1 ISA at the i/o port or it is not
 *                              working.
 */

static int detect_t1_isa
   (device_t         dev,
    struct resource *pResIoBase,
    int              iIoBase,
    int              iIrq,
    unsigned int     uBoardNo)
{
   bus_space_tag_t    t;
   bus_space_handle_t h;
   int                iUnit;
   unsigned           uReverseBoardNo;
   unsigned char      aucCRegs [8];
   unsigned char      uc;
   int                i;

   t = rman_get_bustag (pResIoBase);
   h = rman_get_bushandle (pResIoBase);
   iUnit = device_get_unit (dev);

   uReverseBoardNo = ((uBoardNo & 0x01) << 3) | ((uBoardNo & 0x02) << 1) |
                     ((uBoardNo & 0x04) >> 1) | ((uBoardNo & 0x08) >> 3);
   aucCRegs [0] = (AVMAIC_HEMA_VERSION_ID << 4) | (uReverseBoardNo & 0x0F);
   aucCRegs [1] = 0x00;         /* fast and slow link connected to CON1 */
   aucCRegs [2] = 0x05;         /* fast link 20 MBit, slow link 20 MBit */
   aucCRegs [3] = 0;
   aucCRegs [4] = 0x11;         /* zero wait state */
   aucCRegs [5] = g_aiT1IsaIrqTable [iIrq & 0x0F];
   aucCRegs [6] = 0;
   aucCRegs [7] = 0;
   
   /* board reset */
   DBG (LOG_DEBUG, iUnit, "Perform board reset");
   bus_space_write_1 (t, h, AVMAIC_REG_T1_RESETBOARD, 0x0F);
   tsleep (pResIoBase, PZERO, AVMIO_READY_WAIT_MSG, hz / 10);
   (void) bus_space_read_1 (t, h, AVMAIC_REG_OUTSTAT + AVMAIC_T1_FASTLINK);
   
   /* write board configuration */
   DBG (LOG_DEBUG, iUnit, "Write board configuration");
   uc = (iIoBase >> 4) & 0xFF;
   for (i = 1; i <= 0x0F; i++)
   {
      bus_space_write_1 (t, h, i, uc);
   }
   bus_space_write_1 (t, h, AVMAIC_HEMA_PAL_ID & 0x0F, uc);
   bus_space_write_1 (t, h, AVMAIC_HEMA_PAL_ID >> 4, aucCRegs [0]);
   for (i = 1; i < 7; i++)
   {
      bus_space_write_1 (t, h, 0, aucCRegs [i]);
   }
   bus_space_write_1 (t, h, uc & 0x03, aucCRegs [7]);
   
   /* perform link reset operation */
   DBG (LOG_DEBUG, iUnit, "Perform link reset operation");
   tsleep (pResIoBase, PZERO, AVMIO_READY_WAIT_MSG, hz / 10);
   bus_space_write_1 (t, h, AVMAIC_REG_T1_RESETLINK + AVMAIC_T1_FASTLINK, 0);
   bus_space_write_1 (t, h, AVMAIC_REG_T1_RESETLINK + AVMAIC_T1_SLOWLINK, 0);
   tsleep (pResIoBase, PZERO, AVMIO_READY_WAIT_MSG, hz / 100);
   bus_space_write_1 (t, h, AVMAIC_REG_T1_RESETLINK + AVMAIC_T1_FASTLINK, 1);
   bus_space_write_1 (t, h, AVMAIC_REG_T1_RESETLINK + AVMAIC_T1_SLOWLINK, 1);
   tsleep (pResIoBase, PZERO, AVMIO_READY_WAIT_MSG, hz / 10);
   bus_space_write_1 (t, h, AVMAIC_REG_T1_RESETLINK + AVMAIC_T1_FASTLINK, 0);
   bus_space_write_1 (t, h, AVMAIC_REG_T1_RESETLINK + AVMAIC_T1_SLOWLINK, 0);
   tsleep (pResIoBase, PZERO, AVMIO_READY_WAIT_MSG, hz / 100);
   bus_space_write_1 (t, h, AVMAIC_REG_T1_ANALYSE + AVMAIC_T1_FASTLINK, 0);
   DELAY (5 * 1000);
   bus_space_write_1 (t, h, AVMAIC_REG_T1_ANALYSE + AVMAIC_T1_SLOWLINK, 0);
   
   /* do some checks */
   DBG (LOG_DEBUG, iUnit, "Check several state registers");
   uc = bus_space_read_1 (t, h, AVMAIC_REG_OUTSTAT + AVMAIC_T1_FASTLINK);
   if (uc != 0x01)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", fast link tx buffer not empty (0x%02X)\n",
                     avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_T1_ISA),
                     (unsigned) uc);
      return (-1);
   }
   uc = bus_space_read_1 (t, h, AVMAIC_REG_INSTAT + AVMAIC_T1_FASTLINK);
   if (uc != 0x00)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", fast link rx buffer not empty (0x%02X)\n",
                     avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_T1_ISA),
                     (unsigned) uc);
      return (-1);
   }
   uc = bus_space_read_1 (t, h, AVMAIC_REG_T1_IRQENABLE + AVMAIC_T1_FASTLINK);
   if (uc != 0x00)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", fast link irq enable register not 0 (0x%02X)\n",
                     avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_T1_ISA),
                     (unsigned) uc);
      return (-1);
   }
   uc = bus_space_read_1 (t, h, AVMAIC_REG_T1_FIFOSTAT + AVMAIC_T1_FASTLINK);
   if ((uc & 0xF0) != 0x70)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", fast link fifo state register failed (0x%02X)\n",
                     avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_T1_ISA),
                     (unsigned) uc);
      return (-1);
   }
   uc = bus_space_read_1 (t, h, AVMAIC_REG_T1_IRQMASTER + AVMAIC_T1_FASTLINK);
   if ((uc & 0x0E) != 0x00)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", fast link irq master register failed (0x%02X)\n",
                     avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_T1_ISA),
                     (unsigned) uc);
      return (-1);
   }
   uc = bus_space_read_1 (t, h, AVMAIC_REG_T1_IDENT + AVMAIC_T1_FASTLINK);
   if (uc != 0x01)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", fast link ident register failed (0x%02X)\n",
                     avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_T1_ISA),
                     (unsigned) uc);
      return (-1);
   }
   uc = bus_space_read_1 (t, h, AVMAIC_REG_OUTSTAT + AVMAIC_T1_SLOWLINK);
   if (uc != 0x01)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", slow link tx buffer not empty (0x%02X)\n",
                     avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_T1_ISA),
                     (unsigned) uc);
      return (-1);
   }
   uc = bus_space_read_1 (t, h, AVMAIC_REG_T1_IRQMASTER + AVMAIC_T1_SLOWLINK);
   if ((uc & 0x0E) != 0x00)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", slow link irq master register failed (0x%02X)\n",
                     avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_T1_ISA),
                     (unsigned) uc);
      return (-1);
   }
   uc = bus_space_read_1 (t, h, AVMAIC_REG_T1_IDENT + AVMAIC_T1_SLOWLINK);
   if (uc != 0x01)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", slow link ident register failed (0x%02X)\n",
                     avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_T1_ISA),
                     (unsigned) uc);
      return (-1);
   }
   
   /* if we reach this point, the card was successfully identified */
   DBG (LOG_DEBUG, iUnit, "All checks successful");
   return (0);
} /* detect_t1_isa */





/**
 * Check for working T1 PCI at given memory and i/o port resource.
 *
 * @param dev                   I: The device to check.
 * @param pResMem               I: The memory address to check.
 * @param pResIo                I: The i/o port resource to check.
 *
 * @retval 0                    A T1 PCI was successfully identified at memory
 *                              and i/o port specified.
 * @retval -1                   There is no T1 PCI at memory and i/o port or it
 *                              is not working.
 */

static int detect_t1_dma
   (device_t         dev,
    struct resource *pResMem,
    struct resource *pResIo)
{
   bus_space_tag_t    memt;
   bus_space_handle_t memh;
   bus_space_tag_t    iot;
   bus_space_handle_t ioh;
   int                iUnit;
   unsigned           u;

   /* resource tag and handle are needed further on */
   memt  = rman_get_bustag (pResMem);
   memh  = rman_get_bushandle (pResMem);
   iot   = rman_get_bustag (pResIo);
   ioh   = rman_get_bushandle (pResIo);
   iUnit = device_get_unit (dev);
   
   /* reset the board to get into defined state */
   avmdma_reset (memt, memh, iot, ioh, AVMAIC_CARD_TYPE_T1_PCI);
   
   /* first perform check of general dma functionality */
   if (! avmdma_detect (dev, memt, memh, iot, ioh, AVMAIC_CARD_TYPE_T1_PCI))
   {
      return (-1);
   }
   
   /* now check the transputer */
   if (avmdma_io_write_reg (iot, ioh, 0x80001000, 0x11) != 0 ||
       avmdma_io_write_reg (iot, ioh, 0x80101000, 0x22) != 0 ||
       avmdma_io_write_reg (iot, ioh, 0x80201000, 0x33) != 0 ||
       avmdma_io_write_reg (iot, ioh, 0x80301000, 0x44) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", first series of register writes failed\n",
                     avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_T1_PCI));
      return (-1);
   }
   if (avmdma_io_read_reg (iot, ioh, 0x80001000, &u) != 0 || u != 0x11 ||
       avmdma_io_read_reg (iot, ioh, 0x80101000, &u) != 0 || u != 0x22 ||
       avmdma_io_read_reg (iot, ioh, 0x80201000, &u) != 0 || u != 0x33 ||
       avmdma_io_read_reg (iot, ioh, 0x80301000, &u) != 0 || u != 0x44)
   {
      device_printf (dev,
                     "probe failed for card type \"%s\", first series of register reads failed\n",
                     avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_T1_PCI));
      return (-1);
   }
   if (avmdma_io_write_reg (iot, ioh, 0x80001000, 0x55) != 0 ||
       avmdma_io_write_reg (iot, ioh, 0x80101000, 0x66) != 0 ||
       avmdma_io_write_reg (iot, ioh, 0x80201000, 0x77) != 0 ||
       avmdma_io_write_reg (iot, ioh, 0x80301000, 0x88) != 0)
   {
      device_printf (dev,
                     "probe failed for card type \"%s\", second series of register writes failed\n",
                     avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_T1_PCI));
      return (-1);
   }
   if (avmdma_io_read_reg (iot, ioh, 0x80001000, &u) != 0 || u != 0x55 ||
       avmdma_io_read_reg (iot, ioh, 0x80101000, &u) != 0 || u != 0x66 ||
       avmdma_io_read_reg (iot, ioh, 0x80201000, &u) != 0 || u != 0x77 ||
       avmdma_io_read_reg (iot, ioh, 0x80301000, &u) != 0 || u != 0x88)
   {
      device_printf (dev,
                     "probe failed for card type \"%s\", second series of register reads failed\n",
                     avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_T1_PCI));
      return (-1);
   }
   
   /* if we reach this point, the card was successfully identified */
   DBG (LOG_DEBUG, iUnit, "All checks successful");
   return (0);
} /* detect_t1_dma */





/*
        initialize the softc structure
        ------------------------------
        
        Parameters:
           dev                  I: the device to initialize
           pSc                  I: the softc structure of the device to
                                   initialize

        Result:
           0                    structure initialized successfully
           errno value          failure
*/

static int avmt1_init_softc
   (device_t    dev,
    AvmAicSc_t *pSc)
{
   AvmAicPortData_t *pPortData;

   if (pSc->cardType == AVMAIC_CARD_TYPE_T1_PCI)
   {
      pSc->pfnAppRegister = avmdma_app_register;
      pSc->pfnAppRelease  = avmdma_app_release;
      pSc->pfnPutMessage  = avmdma_put_message;
      pSc->pfnResetCtlr   = avmdma_reset_ctlr;
   }
   else
   {
      pSc->pfnAppRegister = avmio_app_register;
      pSc->pfnAppRelease  = avmio_app_release;
      pSc->pfnPutMessage  = avmt1_isa_put_message;
      pSc->pfnResetCtlr   = avmt1_isa_reset_ctlr;
   }
   
   snprintf (pSc->szCardName, sizeof (pSc->szCardName),
             "AVMT1-%d", pSc->iUnit + 1);
   strncpy (pSc->szDriverName, device_get_driver (dev)->name,
            sizeof (pSc->szDriverName) - 1);
   pSc->state = AVMAIC_STATE_DOWN;
   pSc->nPorts = 1;
   pSc->nPortsInitialized = 0;

   pPortData = &(pSc->aPortData [0]);
   pPortData->pSc                = pSc;
   pPortData->uPortIdx           = 0;
   pPortData->state              = AVMAIC_STATE_DOWN;
   strncpy (pPortData->szCtlrPortName, pSc->szCardName,
            sizeof (pPortData->szCtlrPortName) - 1);
   pPortData->uUniqueCapiCtlrNum = 0;
   pPortData->uDrvCapiCtlrNum    = 0;
   pPortData->nBChns             = 30;

   pSc->ulRecvMsgBufLength = 0;
   pSc->ulSendMsgBufLength = 0;
   if (pSc->cardType == AVMAIC_CARD_TYPE_T1_PCI)
   {
      int iRes;
      
      /* T1 PCI uses DMA for communication, so we need two DMA maps for sending
       * and receiving
       */
      iRes = avmmisc_init_busdma (dev, pSc);
      if (iRes != 0)
      {
         return (iRes);
      }
   }
   else
   {
      /* T1 ISA uses i/o based communication, so we only need some input buffer
       * without any restrictions
       */
      MALLOC (pSc->paucRecvMsgBuf, unsigned char *,
              AVMAIC_MSGBUF_SIZE, M_DEVBUF, M_WAITOK);
      if (pSc->paucRecvMsgBuf == NULL)
      {
         device_printf (dev,
                        "Attach failed, out of memory for receive data buffer\n");
         return (ENOMEM);
      }
   }
   pSc->ulDmaIcsr = 0;
   pSc->sendQueue.ifq_len = 0;
   pSc->sendQueue.ifq_maxlen = pSc->nPorts * pSc->aPortData [0].nBChns * 16;
   pSc->sendQueue.ifq_drops = 0;

   /* The number of B-channels must be copied to the profile. So it is possible
    * to register the controller at i4b before the software is downloaded.
    */
   pPortData->abCapiProfile [2] = (pPortData->nBChns & 0xFF);
   pPortData->abCapiProfile [3] = ((pPortData->nBChns & 0xFF00) >> 8);

   pSc->fIntrActive   = 0;
   pSc->fOpInProgress = 0;
   pSc->fWaitingForRc = 0;
   pSc->uRc           = CAPI_OK;
   
   mtx_init (&(pSc->mtxAccess), AVMMISC_ACCESS_MUTEX_MSG, NULL, MTX_DEF);
   cv_init (&(pSc->cvNotify), AVMMISC_NOTIFY_CONDITION_MSG);
   
   return (0);
} /* avmt1_init_softc */
