/**
 * @file avmb1.c
 *
 * AvmB1 - Specific functions for AVM-B1 ISA and PCI.
 *
 * Copyright: 2000-2003 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: avmb1.c,v 1.2.2.1 2006/02/28 21:35:08 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 __AVMB1__

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





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





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





/* table of possible i/o port base addresses */
static int g_aiB1IsaIoBaseTable [] =
   {
      0x150, 0x250, 0x300, 0x340
   };

/* table of possible irqs and the values to send to the i/o based B1 card */
static int g_aiB1IsaIrqTable [16] =
   {
      0,
      0,
      0,
      192,                      /* irq 3 */
      32,                       /* irq 4 */
      160,                      /* irq 5 */
      96,                       /* irq 6 */
      224,                      /* irq 7 */
      0,
      64,                       /* irq 9 */
      80,                       /* irq 10 */
      208,                      /* irq 11 */
      48,                       /* irq 12 */
      0,
      0,
      112                       /* irq 15 */
   };





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





/* --- performing board specific parts of CAPI calls --- */

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

/* reset and download a controller */
static unsigned avmb1_io_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 controllers --- */

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

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

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

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



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

/**
 * Check for working B1 ISA / PCI v3 / PCICMA / M1 / M2 at given i/o port
 * resource.
 *
 * @param dev                   I: The device to check.
 * @param pResIoBase            I: The i/o port resource to check.
 * @param cardType              I: The card type to detect (B1 ISA or B1 PCI v3).
 *
 * @retval 0                    A B1 ISA / PCI v3 was successfully identified at
 *                              the i/o port specified.
 * @retval -1                   There is no B1 ISA / PCI v3 at the i/o port or
 *                              it is not working.
 */
static int detect_b1_io
   (device_t          dev,
    struct resource  *pResIoBase,
    AvmAicCardType_t  cardType);

/**
 * Check for working B1 PCI v4 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 B1 PCI v4 was successfully identified at
 *                              memory and i/o port specified.
 * @retval -1                   There is no B1 PCI v4 at memory and i/o port or
 *                              it is not working.
 */
static int detect_b1_dma
   (device_t         dev,
    struct resource *pResMem,
    struct resource *pResIo);

/* initialize the softc structure */
static int avmb1_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 B1 ISA
        ------------------
        
        Parameters:
           dev                  I: device data for the card to probe

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

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

   /* initialize softc structure */
   pSc = device_get_softc (dev);
   bzero (pSc, sizeof (*pSc));
   pSc->iUnit = device_get_unit (dev);
   
   /* 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;
   for (i = 0; i < ARRAY_COUNT (g_aiB1IsaIoBaseTable); i++)
   {
      if (pSc->resInfo.iIoBase == g_aiB1IsaIoBaseTable [i])
      {
         break;
      }
   }
   if (i >= ARRAY_COUNT (g_aiB1IsaIoBaseTable))
   {
      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->resInfo.pResIo == NULL)
   {
      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 ((size_t) (pSc->resInfo.iIrq) >=
          sizeof (g_aiB1IsaIrqTable) / sizeof (g_aiB1IsaIrqTable [0]) ||
       g_aiB1IsaIrqTable [pSc->resInfo.iIrq] == 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_b1_io (dev, pSc->resInfo.pResIo, AVMAIC_CARD_TYPE_B1_ISA) != 0)
   {
      (void) bus_release_resource (dev, SYS_RES_IRQ,
                                   pSc->resInfo.iRidIrq, pSc->resInfo.pResIrq);
      pSc->resInfo.pResIrq = NULL;
      avmmisc_detach (dev);
      return (ENXIO);
   }
   
   /* finally release all previously allocated resources */
   (void) bus_release_resource (dev, SYS_RES_IRQ,
                                pSc->resInfo.iRidIrq, pSc->resInfo.pResIrq);
   pSc->resInfo.pResIrq = NULL;
   avmmisc_detach (dev);

   /* declare the device to be a AVM-B1 ISA */
   device_set_flags (dev, 0);
   device_set_desc (dev, avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_B1_ISA));
   
   /* if we get here, the card is identified successfully */
   return (0);
} /* avmb1_isa_probe */





/*
        Attach a B1 ISA
        ---------------
        The attach function of a B1 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 avmb1_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_B1_ISA;
   iRes = avmb1_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 B1 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 *) avmb1_io_intr, pSc,
                          &(pSc->resInfo.hIrqCookie));

   /* reset card to get into defined state */
   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);
} /* avmb1_isa_attach */





/*
        attach a B1 PCI v3 or v4
        ------------------------
        
        Parameters:
           dev                  I: device data for card to attach

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

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

   /* determine if B1 PCI v3 or v4 - distinction is made by checking existence
    * of a second i/o port base address and a memory i/o range (may be turned
    * off through a kernel tunable parameter)
    */
   fFound = getenv_int (AVMAIC_TUNABLE_NO_B1_PCI_V4_SUPPORT, &iNoPciV4Support);
   if (fFound)
   {
      DBG (LOG_TRACE, pSc->iUnit,
           "Tunable kernel variable \"%s\" is set to %d",
           AVMAIC_TUNABLE_NO_B1_PCI_V4_SUPPORT, iNoPciV4Support);
   }
   pSc->iUnit = device_get_unit (dev);
   if ((! fFound || iNoPciV4Support == 0) &&
       bus_get_resource (dev, SYS_RES_IOPORT, 0x14, &ulBase, &ulCount) == 0 &&
       bus_get_resource (dev, SYS_RES_IOPORT, 0x18, &ulBase, &ulCount) == 0 &&
       bus_get_resource (dev, SYS_RES_MEMORY, 0x10, &ulBase, &ulCount) == 0)
   {
      DBG (LOG_TRACE, pSc->iUnit,
           "Board recognized as B1 PCI v4 (two i/o port ranges and a memory mapped i/o range found)");
      pSc->cardType = AVMAIC_CARD_TYPE_B1_PCI_V4;
      pSc->resInfo.iRidIo  = 0x18;
      pSc->resInfo.iRidMem = 0x10;
   }
   else
   {
      DBG (LOG_TRACE, pSc->iUnit,
           "Board recognized as B1 PCI v3 (either only one i/o port range or no memory mapped i/o range found)");
      pSc->cardType = AVMAIC_CARD_TYPE_B1_PCI_V3;
      pSc->resInfo.iRidIo  = 0x14;
      pSc->resInfo.iRidMem = 0;
   }
   
   /* fill the softc structure */
   iRes = avmb1_init_softc (dev, pSc);
   if (iRes != 0)
   {
      avmmisc_detach (dev);
      return (iRes);
   }

   /* allocate required i/o range */
   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 if B1 PCI v4 */
   if (pSc->cardType == AVMAIC_CARD_TYPE_B1_PCI_V4)
   {
      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);
   }
   else
   {
      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.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 (pSc->cardType == AVMAIC_CARD_TYPE_B1_PCI_V4)
   {
      /* check for working B1 PCI v4 */
      if (detect_b1_dma (dev, pSc->resInfo.pResMem, pSc->resInfo.pResIo) != 0)
      {
         (void) bus_release_resource (dev, SYS_RES_IRQ,
                                      pSc->resInfo.iRidIrq,
                                      pSc->resInfo.pResIrq);
         pSc->resInfo.pResIrq = NULL;
         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;
   }
   else
   {
      /* check for working B1 PCI v3 */
      if (detect_b1_io (dev, pSc->resInfo.pResIo, pSc->cardType) != 0)
      {
         (void) bus_release_resource (dev, SYS_RES_IRQ,
                                      pSc->resInfo.iRidIrq,
                                      pSc->resInfo.pResIrq);
         pSc->resInfo.pResIrq = NULL;
         avmmisc_detach (dev);
         return (ENXIO);
      }

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

      /* reset card to get into defined state */
      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);
} /* avmb1_pci_attach */





/**
 * Probe for a B1 PCMCIA / M1 / M2.
 *
 * The probe operation for these card types checks if the required i/o ports
 * and the irq is available. Additionally some check is made to check for a
 * working card.
 *
 * @param dev                   I: The device for the card to probe.
 * @param cardType              I: The card type to probe for (only B1 PCMCIA,
 *                                 M1 or M2 allowed).
 *
 * @retval 0                    The card device was found to work.
 * @retval Else                 The card device is not working or the resources
 *                              cannot be acquired. The result is an
 *                              appropriate errno value.
 */

int avmb1_pccard_probe
   (device_t         dev,
    AvmAicCardType_t cardType)
{
   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);
   
   DBG (LOG_TRACE, pSc->iUnit, "Probe for \"%s\"",
        device_get_desc (dev));

   /* allocate required i/o range for an availability check */
   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\n");
      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 == NULL)
   {
      device_printf (dev,
                     "Probe failed, unable to allocate i/o port range 0x%03X-0x%02X\n",
                     pSc->resInfo.iIoBase,
                     pSc->resInfo.iIoBase + (int) ulCount);
      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;
   pSc->resInfo.pResIrq = bus_alloc_resource
                             (dev, SYS_RES_IRQ,
                              &(pSc->resInfo.iRidIrq),
                              0UL, ~0UL, 1, 0);
   if (! pSc->resInfo.pResIrq)
   {
      device_printf (dev, "Probe failed, unable to allocate irq %d\n",
                     pSc->resInfo.iIrq);
      avmmisc_detach (dev);
      return (ENXIO);
   }

#if 0
   /* Reset card to get into defined state, perform a complete reset */
   DBG (LOG_DEBUG, pSc->iUnit, "Perform controller reset");
   avmio_reset (rman_get_bustag (pSc->resInfo.pResIo),
                rman_get_bushandle (pSc->resInfo.pResIo));
   avmio_reset (rman_get_bustag (pSc->resInfo.pResIo),
                rman_get_bushandle (pSc->resInfo.pResIo));
   DELAY (100);
#endif /* 0 */

   /* check for existing and working card */
   if (detect_b1_io (dev, pSc->resInfo.pResIo, cardType) != 0)
   {
      (void) bus_release_resource (dev, SYS_RES_IRQ,
                                   pSc->resInfo.iRidIrq, pSc->resInfo.pResIrq);
      pSc->resInfo.pResIrq = NULL;
      avmmisc_detach (dev);
      return (ENXIO);
   }
   
   /* finally release all previously allocated resources */
   (void) bus_release_resource (dev, SYS_RES_IRQ,
                                pSc->resInfo.iRidIrq, pSc->resInfo.pResIrq);
   pSc->resInfo.pResIrq = NULL;
   avmmisc_detach (dev);

   /* Note: The device description is set by the PC-Card subssytem according to
    *       the CIS data of the card.
    */

   /* if we get here, the card is identified successfully */
   DBG (LOG_TRACE, pSc->iUnit, "Probe successful");
   return (0);
} /* avmb1_pccard_probe */





/**
 * Attach a B1 PCMCIA / M1 / M2 to its bus.
 *
 * @param dev                   I: The device for the card to attach.
 * @param cardType              I: The card type for the card to attach.
 *
 * @retval 0                    The card was successfully attached.
 * @retval Else                 Failure, the result is an appropriate errno
 *                              value.
 */

int avmb1_pccard_attach
   (device_t         dev,
    AvmAicCardType_t cardType)
{
   AvmAicSc_t    *pSc;
   int            iRes;
   unsigned long  ulBase;
   unsigned long  ulCount;
   
   DBG (LOG_TRACE, device_get_unit (dev), "Attaching \"%s\"",
        device_get_desc (dev));

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

   /* allocate required i/o range */
   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);
   
   /* the B1 PCMCIA / M1 / M2 do 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.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_b1_io (dev, pSc->resInfo.pResIo, pSc->cardType) != 0)
   {
      (void) bus_release_resource (dev, SYS_RES_IRQ,
                                   pSc->resInfo.iRidIrq, pSc->resInfo.pResIrq);
      pSc->resInfo.pResIrq = NULL;
      avmmisc_detach (dev);
      return (ENXIO);
   }

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

   /* reset card to get into defined state */
   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);
} /* avmb1_pccard_attach */





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





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

static unsigned avmb1_io_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 (avmio_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 (avmio_put_slice (t, h,
                              (unsigned char *) (pmbData->m_data),
                              pmbData->m_len) < 0)
         {
            uRes = CME_OS_RESOURCE_ERROR;
         }
      }
      else
      {
         if (avmio_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);
} /* avmb1_io_put_message */





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

static unsigned avmb1_io_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_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_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);
   }

   /* send irq number to ISA controller */
   if (pSc->cardType == AVMAIC_CARD_TYPE_B1_ISA)
   {
      (void) avmio_disable_interrupt (t, h);
      (void) avmio_outp (t, h,
                         AVMAIC_REG_RESET,
                         g_aiB1IsaIrqTable [pSc->resInfo.iIrq]);
      (void) avmio_enable_interrupt (t, h);
   }
   /* enable irq for other controllers */
   else
   {
      (void) avmio_disable_interrupt (t, h);
      (void) avmio_outp (t, h, AVMAIC_REG_RESET, 0xF0);
      (void) avmio_enable_interrupt (t, h);
   }

   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");
   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);
      if (pSc->uRc == 0xFFFFFFFF)
      {
         uRes = CME_OS_RESOURCE_ERROR;
      }
      else
      {
         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 3:
         /* card claims to be B1 PCMCIA */
         if (pSc->cardType != AVMAIC_CARD_TYPE_B1_PCMCIA)
         {
            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_PCMCIA),
                    avmmisc_get_card_type_name (pSc->cardType));
         }
         break;

      case 4:
         /* card claims to be M1 */
         if (pSc->cardType != AVMAIC_CARD_TYPE_M1)
         {
            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_M1),
                    avmmisc_get_card_type_name (pSc->cardType));
         }
         break;

      case 5:
         /* card claims to be M2 */
         if (pSc->cardType != AVMAIC_CARD_TYPE_M2)
         {
            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_M2),
                    avmmisc_get_card_type_name (pSc->cardType));
         }
         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;
      
      default:
         break;
   }

   /* again enable interrupts for the controller */
   avmio_disable_interrupt (t, h);
   msleep (pSc, &(pSc->mtxAccess), PZERO, AVMIO_READY_WAIT_MSG, hz / 10);
   avmio_enable_interrupt (t, h);

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

   return (CAPI_OK);
} /* avmb1_io_reset_ctlr */





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

static void avmb1_io_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)
   {
      /* Note: This may happen because for each interrupt we loop to get
       *       several messages if available. But the board raises an interrupt
       *       for every message delivered. So after fetching more than one
       *       message the next interrupt may occur before a result waiting
       *       function call took its result and so this operation is still in
       *       progress, although its return code is already stored to be
       *       fetched.
       */
      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:
            avmb1_io_receive_message (pSc);
            break;

         case AVMAIC_RECEIVE_DATA_B3_IND:
            avmb1_io_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:
            if (pSc->state == AVMAIC_STATE_BUSY)
            {
               pSc->state = AVMAIC_STATE_READY;
               DBG (LOG_TRACE, pSc->iUnit,
                    "Ready to receive data again (AVMAIC_RECEIVE_START)");
            }
            else
            {
               DBG (LOG_IRQ, pSc->iUnit,
                    "Still ready to receive data (AVMAIC_RECEIVE_START)");
            }
            break;

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

         case AVMAIC_RECEIVE_INIT:
            avmb1_io_receive_init (pSc);
            break;

         case AVMAIC_RECEIVE_TASK_READY:
            avmb1_io_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:
            avmb1_io_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__, cmd);
            break;
      }

   } while (avmio_rx_full (t, h));

   pSc->fIntrActive = 0;
   mtx_unlock (&(pSc->mtxAccess));
   
   DBG (LOG_IRQ, pSc->iUnit, "Left interrupt handler");
        
} /* avmb1_io_intr */





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

static void avmb1_io_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 = avmio_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",
        (int) 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));
   
} /* avmb1_io_receive_message */
   




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

static void avmb1_io_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 = avmio_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 = avmio_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 %u",
           nDataLen, (unsigned) (AVMAIC_MSGBUF_SIZE - (unsigned) 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)",
              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__,
                 (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));

} /* avmb1_io_receive_data_b3_ind */




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

static void avmb1_io_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 =
      avmio_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));
      }
   }

} /* avmb1_io_receive_init */





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

static void avmb1_io_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 = avmio_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);
   
} /* avmb1_io_receive_task_ready */





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

static void avmb1_io_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 = avmio_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]));
   
} /* avmb1_io_receive_debugmsg */





/**
 * Check for working B1 ISA / PCI v3 / PCICMA / M1 / M2 at given i/o port
 * resource.
 *
 * @param dev                   I: The device to check.
 * @param pResIoBase            I: The i/o port resource to check.
 * @param cardType              I: The card type to detect (B1 ISA or B1 PCI v3).
 *
 * @retval 0                    A B1 ISA / PCI v3 was successfully identified at
 *                              the i/o port specified.
 * @retval -1                   There is no B1 ISA / PCI v3 at the i/o port or
 *                              it is not working.
 */

static int detect_b1_io
   (device_t          dev,
    struct resource  *pResIoBase,
    AvmAicCardType_t  cardType)
{
   bus_space_tag_t    t;
   bus_space_handle_t h;
   int                iUnit;
   u_char             b1;
   u_char             b2;
   int                onoff;
   int                i;

   /* resource tag and handle are needed further on */
   t = rman_get_bustag (pResIoBase);
   h = rman_get_bushandle (pResIoBase);
   iUnit = device_get_unit (dev);
   
   /* Reset card to get into defined state, perform a complete reset */
   DBG (LOG_DEBUG, iUnit, "Perform controller reset");
   avmio_reset (t, h);
   /*avmio_reset (t, h);*/
   DELAY (100);

   /* check upper 6 bits of the status register */
   DBG (LOG_DEBUG, iUnit, "Check upper 6 bits of the status register");
   b1 = avmio_inp (t, h, AVMAIC_REG_INSTAT);
   b2 = avmio_inp (t, h, AVMAIC_REG_OUTSTAT);
   if ((b1 & 0xfc) != 0 || (b2 & 0xfc) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", upper 6 bits of status registers must be zero, inreg 0x%X, outreg 0x%X\n",
	             avmmisc_get_card_type_name (cardType),
                     (unsigned) b1, (unsigned) b2);
      return (-1);
   }

   /* check bit 2 of status register (enable irq) */
   DBG (LOG_DEBUG, iUnit, "Check bit 2 of status register (enable irq)");
   (void) avmio_outp (t, h, AVMAIC_REG_INSTAT, 0x02);
   (void) avmio_outp (t, h, AVMAIC_REG_OUTSTAT, 0x02);
   b1 = (avmio_inp (t, h, AVMAIC_REG_INSTAT) & 0xfe);
   b2 = (avmio_inp (t, h, AVMAIC_REG_OUTSTAT) & 0xfe);
   if (b1 != 0x02 || b2 != 0x02)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", unable to enable irq\n",
	             avmmisc_get_card_type_name (cardType));
      return (-1);
   }

   /* check again bit 2 of status register (disable irq) */
   DBG (LOG_DEBUG, iUnit, "Check bit 2 of status register (disable irq)");
   (void) avmio_outp (t, h, AVMAIC_REG_INSTAT, 0x0);
   (void) avmio_outp (t, h, AVMAIC_REG_OUTSTAT, 0x0);
   b1 = (avmio_inp (t, h, AVMAIC_REG_INSTAT) & 0xfe);
   b2 = (avmio_inp (t, h, AVMAIC_REG_OUTSTAT) & 0xfe);
   if (b1 != 0 || b2 != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", unable to disable irq\n",
                     avmmisc_get_card_type_name (cardType));
      return (-1);
   }

   /* toggle test bit several times */
   DBG (LOG_DEBUG, iUnit, "Toggle test bit");
   for (onoff = !0, i = 0; i < 10; i++)
   {
      avmio_set_test_bit (t, h, onoff, cardType);
      if (avmio_get_test_bit (t, h, cardType) != onoff)
      {
         device_printf (dev,
                        "Probe failed for card type \"%s\", switching of test bit not successful (switched %d times)\n",
                        avmmisc_get_card_type_name (cardType),
                        i + 1);
         return (-1);
      }
      onoff = ! onoff;
   }

   /* finally one last check, but not for M1 */
   if (cardType != AVMAIC_CARD_TYPE_M1)
   {
      DBG (LOG_DEBUG, iUnit, "Check state register 1");
      b1 = (avmio_read_reg (t, h, AVMAIC_INTREG_STAT1) & 0x0f);
      if (b1 != 0x01)
      {
         device_printf (dev,
                        "Probe failed for card type \"%s\", state register 1 not 0x01 (0x%02X\n",
   	             avmmisc_get_card_type_name (cardType),
                        b1);
         return (-1);
      }
   }

   /* if we reach this point, the card was successfully identified */
   DBG (LOG_DEBUG, iUnit, "All checks successful");
   return (0);
} /* detect_b1_io */





/**
 * Check for working B1 PCI v4 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 B1 PCI v4 was successfully identified at
 *                              memory and i/o port specified.
 * @retval -1                   There is no B1 PCI v4 at memory and i/o port or
 *                              it is not working.
 */

static int detect_b1_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;
   int                i;

   /* 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);
   
   /* first perform check of general dma functionality */
   if (avmdma_detect (dev, memt, memh, iot, ioh,
                      AVMAIC_CARD_TYPE_B1_PCI_V4) != 0)
   {
      return (-1);
   }
   
   /* now try to switch the test bit some times */
   DBG (LOG_DEBUG, iUnit, "Switch test bit on");
   for (i = 0; i < 5; i++)
   {
      if (avmdma_io_set_test_bit (iot, ioh, 1) != 0)
      {
	 device_printf (dev,
                        "Probe failed for card type \"%s\", switching on test bit not successful (switched %d times)\n",
		        avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_B1_PCI_V4),
                        i + 1);
         return (-1);
      }
      if (avmdma_io_get_test_bit (iot, ioh) != 1)
      {
	 device_printf (dev,
                        "Probe failed for card type \"%s\", reading switched on test bit not successful (switched %d times)\n",
		        avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_B1_PCI_V4),
                        i + 1);
         return (-1);
      }
   }
   DBG (LOG_DEBUG, iUnit, "Switch test bit off");
   for (i = 0; i < 5; i++)
   {
      if (avmdma_io_set_test_bit (iot, ioh, 0) != 0)
      {
	 device_printf (dev,
                        "Probe failed for card type \"%s\", switching off test bit not successful (switched %d times)\n",
		        avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_B1_PCI_V4),
                        i + 1);
         return (-1);
      }
      if (avmdma_io_get_test_bit (iot, ioh) != 0)
      {
	 device_printf (dev,
                        "Probe failed for card type \"%s\", reading switched off test bit not successful (switched %d times)\n",
		        avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_B1_PCI_V4),
                        i + 1);
         return (-1);
      }
   }
   
   /* if we reach this point, the card was successfully identified */
   DBG (LOG_DEBUG, iUnit, "All checks successful");
   return (0);
} /* detect_b1_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 avmb1_init_softc
   (device_t    dev,
    AvmAicSc_t *pSc)
{
   AvmAicPortData_t *pPortData;

   if (pSc->cardType == AVMAIC_CARD_TYPE_B1_PCI_V4)
   {
      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  = avmb1_io_put_message;
      pSc->pfnResetCtlr   = avmb1_io_reset_ctlr;
   }
   
   switch (pSc->cardType)
   {
      case AVMAIC_CARD_TYPE_B1_ISA:
      case AVMAIC_CARD_TYPE_B1_PCI_V3:
      case AVMAIC_CARD_TYPE_B1_PCI_V4:
      case AVMAIC_CARD_TYPE_B1_PCMCIA:
      default:
         snprintf (pSc->szCardName, sizeof (pSc->szCardName),
                   "AVMB1-%d", pSc->iUnit + 1);
         break;

      case AVMAIC_CARD_TYPE_M1:
         snprintf (pSc->szCardName, sizeof (pSc->szCardName),
                   "AVMM1-%d", pSc->iUnit + 1);
         break;

      case AVMAIC_CARD_TYPE_M2:
         snprintf (pSc->szCardName, sizeof (pSc->szCardName),
                   "AVMM2-%d", pSc->iUnit + 1);
         break;
   }
   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             = 2;

   pSc->ulRecvMsgBufLength = 0;
   pSc->ulSendMsgBufLength = 0;
   if (pSc->cardType == AVMAIC_CARD_TYPE_B1_PCI_V4)
   {
      int iRes;
      
      /* B1 PCI v4 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
   {
      /* B1 ISA, B1 PCMCIA and B1 PCI v3 use 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);
} /* avmb1_init_softc */
