/**
 * @file avmaic.c
 *
 * AvmAic - The avmaic CAPI manager driver module for *BSD.
 *
 * Copyright: 2000-2006 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: avmaic.c,v 1.5.2.1 2006/02/28 21:35:07 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/module.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/sysproto.h>
#include <sys/mbuf.h>
#include <machine/bus.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <machine/resource.h>
#include <sys/sysctl.h>
#include <isa/isavar.h>
#include <dev/pci/pcivar.h>
#include <dev/pccard/pccardvar.h>
#if defined (__FreeBSD_version) && __FreeBSD_version >= 503000
#  include <pccarddevs.h>
#endif /* __FreeBSD_version && __FreeBSD_version >= 503000 */

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

#define __AVMAIC__

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





/* === Private definitions =============================================== */





/* --- ids to identify AVM cards for the pci bus --- */

#define AVMAIC_AVM_PCI_VENDOR_ID        0x1244
#define AVMAIC_DEC_PCI_VENDOR_ID        0x1011

#define AVMAIC_B1PCI_DEVICE_ID          0x0700
#define AVMAIC_C4_DEVICE_ID             0x0800
#define AVMAIC_C2_DEVICE_ID             0x1100
#define AVMAIC_T1PCI_DEVICE_ID          0x1200
#define AVMAIC_DEC_21285_DEVICE_ID      0x1065



/* --- Definitions to identify AVM PCMCIA cards --- */

/* Note: Until no "official" definition for the vendor and product strings for
 *       the cards supported by AVM and this driver are placed into
 *       "pccarddevs.h", we must provide these definitions here.
 */

#ifndef PCMCIA_VENDOR_AVM
/** Vendor id for AVM PCMCIA cards. */
#define PCMCIA_VENDOR_AVM               0x01A7
#endif /* PCMCIA_VENDOR_AVM */

#ifndef PCMCIA_PRODUCT_AVM_B1PCMCIA

   /** Product id for the AVM B1-PCMCIA. */
   #define PCMCIA_PRODUCT_AVM_B1PCMCIA  0x0301

   /** Product string for the AVM B1-PCMCIA. */
   #define PCMCIA_STR_AVM_B1PCMCIA      "AVM B1 PCMCIA ISDN-controller"

   /** Default CIS structure for the AVM B1-PCMCIA. */
   #define PCMCIA_CIS_AVM_B1PCMCIA      { NULL, NULL, NULL, NULL }

#endif /* PCMCIA_PRODUCT_AVM_B1PCMCIA */

/** The list of PC-Cards supported by this driver. */
static struct pccard_product g_aAvmAicPccardProducts [] =
   {
#if defined (PCCARD_API_LEVEL) && PCCARD_API_LEVEL >= 6
      PCMCIA_CARD_D (AVM, B1PCMCIA),
#else /* PCCARD_API_LEVEL && PCCARD_API_LEVEL >= 6 */
      PCMCIA_CARD_D (AVM, B1PCMCIA, 0),
#endif /* PCCARD_API_LEVEL && PCCARD_API_LEVEL >= 6 */
      { NULL }
   };



/* --- support for sysctl variables --- */

/** The node for the avmaic device driver under "capi". */
SYSCTL_NODE (_capi, OID_AUTO, avmaic, CTLFLAG_RW, 0,
             "AVM active ISDN controller driver");

/** String needed for the version variable of the driver. */
static char g_szVersion [16] = "";

/** The variable for the version number of the driver. */
SYSCTL_STRING (_capi_avmaic, OID_AUTO, version, CTLFLAG_RD,
               g_szVersion, 0, "Version number");

/** The variable for the logging level. */
SYSCTL_INT (_capi_avmaic, OID_AUTO, loglevel, CTLFLAG_RW,
            &e_iAvmAicLogLevel, 0, "Logging level");

/** The variable for the maximum number of registered applications. */
SYSCTL_INT (_capi_avmaic, OID_AUTO, max_applications, CTLFLAG_RD,
            &e_iAvmAicMaxApplications, 0,
            "Maximum number of registered applications");

/**
 * The variable for the maximum number of logical connections per physical
 * channel.
 */
SYSCTL_INT (_capi_avmaic, OID_AUTO, max_ncci_per_channel, CTLFLAG_RD,
            &e_iAvmAicMaxNcciPerChannel, 0,
            "Maximum number of logical connections (NCCIs) per physical channel (PLCI)");



/* --- support for tunable 'constants' in the kernel environment --- */

TUNABLE_INT (AVMAIC_TUNABLE_LOGLEVEL, &e_iAvmAicLogLevel);
TUNABLE_INT (AVMAIC_TUNABLE_MAX_APPLICATIONS, &e_iAvmAicMaxApplications);
TUNABLE_INT (AVMAIC_TUNABLE_MAX_NCCI_PER_CHANNEL, &e_iAvmAicMaxNcciPerChannel);





/* === Definitions for public declarations =============================== */





/**
 * Mapping from CAPI controller number to the port data.
 *
 * @note CAPI defines a maximum of 127 controller numbers from 1 to 127.
 */
AvmAicPortData_t *e_apMapCapiCtlrToPortData [128] = { NULL };



/* --- the logging functionality --- */

#ifdef AVMAIC_LOG_LEVEL
int e_iAvmAicLogLevel = AVMAIC_LOG_LEVEL;
#else /* AVMAIC_LOG_LEVEL */
int e_iAvmAicLogLevel = LOG_ERROR;
#endif /* AVMAIC_LOG_LEVEL */



/* --- some parameters tunable by kernel environment variables --- */

int e_iAvmAicMaxApplications   = AVMAIC_MAX_APPLICATIONS;
int e_iAvmAicMaxNcciPerChannel = AVMAIC_MAX_NCCI_PER_CHANNEL;





/* === Prototypes for private functions ================================== */





/* --- Driver functions for the ISA driver --- */

/**
 * Probe for installed isa controller.
 *
 * This function is called to check if the (ISA) device specified does really
 * exist and is working (at least as far it is possible without any firmware).
 *
 * In this function the only real action performed besides checking the maximum
 * number of supported units is to distinguish between the two supported ISA
 * boards: B1 and T1. The real probe  operation will be performed in separate
 * functions.
 *
 * @param dev                    I: The device entry for the board to probe.
 *
 * @retval 0                     The board device was found and is working.
 * @retval Else                  The board was not found or is not working. The
 *                               result value is an errno value.
 */
static int avmaic_isa_probe
   (device_t dev);

/**
 * Attach installed controller to isa bus.
 *
 * This function is called to prepare a device for later use. After successful
 * execution of this function, the device shall be ready for operation.
 *
 * For the avmaic ISA device driver this means to setup the data maintained for
 * the board, to prepare the board for the firmware download and to register the
 * board (i.e. all its controllers) at the CAPI manager.
 *
 * In this function the only action performed besides checking the maximum
 * number of supported units is to distinguish between the two supported ISA
 * boards: B1 and T1. The real attach operation will be performed in separate
 * functions.
 *
 * @param dev                    I: The device entry for the board to attach.
 *
 * @retval 0                     The board was successfully attached.
 * @retval Else                  An errno value representing the error occurred
 *                               during the attach operation.
 */
static int avmaic_isa_attach
   (device_t dev);



/* --- Driver functions for the PCI driver --- */

/**
 * Probe for an installed PCI controller.
 *
 * This function is called to check if the (PCI) device specified can be driven
 * by this device driver. This is accomplished by checking the PCI vendor and
 * sub-vendor id of the device. If these ids match any of the values for the
 * active AVM boards handled by this driver, the probe operation will be
 * successful.
 *
 * @param dev                    I: The device entry for the board to probe.
 *
 * @retval 0                     The board device was found.
 * @retval Else                  The device specified does not represent an
 *                               active AVM board driven by this device driver.
 *                               The result value is an errno value.
 */
static int avmaic_pci_probe
   (device_t dev);

/**
 * Attach an installed controller to the PCI bus.
 *
 * This function is called to prepare a device for later use. After successful
 * execution of this function, the device shall be ready for operation.
 *
 * For the avmaic PCI device driver this means to setup the data maintained for
 * the board, to prepare the board for the firmware download and to register the
 * board (i.e. all its controllers) at the CAPI manager.
 *
 * In this function the only action performed besides checking the maximum
 * number of supported units is to distinguish between the supported PCI
 * boards: B1, C2, C4 and T1. The real attach operation will be performed in
 * separate functions.
 *
 * @param dev                    I: The device entry for the board to attach.
 *
 * @retval 0                     The board was attached successfully.
 * @retval Else                  An errno value representing the error occurred
 *                               during the attach operation.
 */
static int avmaic_pci_attach
   (device_t dev);



/* --- Driver functions for the PC-Card driver --- */

/**
 * Match a PC-Card device to the list of supported ISDN controllers.
 *
 * This function is called to check that a PC-Card device is supported by this
 * driver. The vendor and product id of the device is checked against the list
 * of supported PC-Cards.
 *
 * @param dev                   I: The device entry for the card to probe.
 *
 * @retval 0                    The card vendor and product ids are known to
 *                              this driver.
 * @retval Else                 The device specified is not supported by this
 *                              driver. The result value is an appropriate
 *                              errno value.
 */
static int avmaic_pccard_match
   (device_t dev);

/**
 * Probe for installed PC-Card controller.
 *
 * This function is called to check if the (PC-Card) device specified can be
 * driven by this device driver. For PC-Cards the vendor and product ids are
 * already verified to be supported by a previous call to
 * avmaic_pccard_match(). The remaining task for the probe routine is to check
 * if the card is really working. The B1-PCMCIA is nearly identical to the
 * B1-ISA, so the probe operation to execute is the same.
 *
 * @param dev                   I: The device entry for the card to probe.
 *
 * @retval 0                    The PC-Card device is working as expected.
 * @retval Else                 The device specified does not represent an
 *                              active AVM card driven by this device driver.
 *                              The result value is an errno value.
 */
static int avmaic_pccard_probe
   (device_t dev);

/**
 * Attach an installed controller to the PC-Card bus.
 *
 * This function is called to prepare a device for later use. After successful
 * execution of this function, the device shall be ready for operation.
 *
 * For the avmaic PC-Card device driver this means to setup the data maintained
 * for the board, to prepare the board for the firmware download and to
 * register the board at the CAPI manager.
 *
 * In this function the only action performed besides checking the maximum
 * number of supported units is to distinguish between the supported PC-Card
 * boards (currently only the B1-PCMCIA, maybe also the M1 and M2 in the
 * future).The real attach operation will be performed in separate functions.
 * For the B1-PCMCIA the operation is identical to the one for the B1-ISA.
 *
 * @param dev                    I: The device entry for the card to attach.
 *
 * @retval 0                     The card was attached successfully.
 * @retval Else                  An errno value representing the error occurred
 *                               during the attach operation.
 */
static int avmaic_pccard_attach
   (device_t dev);



/* --- Bus independent driver methods --- */

/**
 * Detach installed controller from its bus.
 *
 * The detach function is called when the device driver module is unloaded from
 * memory. It must turn the device specified into disabled state, because the
 * device will be destroyed and all related memory removed after this call.
 *
 * For the avmaic driver this requires to unregister the board (i.e. the CAPI
 * controllers related to the board) at the CAPI manager. Last a reset operation
 * must be performed to leave the board in a disabled state and all its
 * resources are released.
 *
 * @param dev                    I: The device entry for the board to detach.
 *
 * @retval 0                     The board was successfully detached.
 * @retval Else                  An errno value representing the error occurred
 *                               during the detach operation.
 */
static int avmaic_detach
   (device_t dev);

/**
 * Shut down installed controller.
 *
 * This function is called on system shutdown. The avmaic device driver will
 * perform a reset operation on the specified device to leave it in a disabled
 * state. This will also terminate all currently running connections.
 *
 * @param dev                    I: The device entry for the board to shutdown.
 *
 * @retval 0                     The board was successfully shut down.
 * @retval Else                  An errno value representing the error occurred
 *                               during the shutdown operation.
 */
static void avmaic_shutdown
   (device_t dev);



/* --- prototypes for the functions to be registered at the CAPI manager --- */

static CAPIDrv_RegisterFct_t   avmaic_capidrv_register;
static CAPIDrv_ReleaseFct_t    avmaic_capidrv_release;
static CAPIDrv_PutMessageFct_t avmaic_capidrv_put_message;
static CAPIDrv_ResetCtlrFct_t  avmaic_capidrv_reset_ctlr;



/* --- several helper functions --- */

/**
 * Register a board at the CAPI manager.
 *
 * This function is called after the hardware related operations of the attach
 * function for a board are successfully completed. For every ISDN port of the
 * board specified by dev and pSc the registration structure is filled an the
 * port is registered as a CAPI controller at the CAPI manager.
 *
 * @pre This function must be called with controller access obtained through
 *      avmmisc_get_ctlr_access().
 *
 * @param dev                    I: The device entry for the board to register.
 * @param pSc                    I/O: The softc structure of the board to
 *                                  register.
 *
 * @retval 0                     The register operation was successful.
 * @retval Else                  An errno value representing the error occurred
 *                               during the register operation. In most cases
 *                               this will be a CAPI result value.
 */
static int RegisterAtCapiMgr
   (device_t    dev, 
    AvmAicSc_t *pSc);



/* --- Handling module load and unload operations --- */

/**
 * Unregister all controllers at the CAPI manager.
 *
 * This function is called when the avmaic kernel module is unloaded from
 * memory. It must perform a controller release operation for all CAPI
 * controllers maintained by this driver. The result will be to release all
 * memory related to these CAPI controllers and the termination of all still
 * active connections for each controller.
 *
 * @param None.
 *
 * @return Nothing.
 */
static void UnregisterAllControllers (void);

/**
 * Handling of module load/unload events.
 *
 * This the handler function of all kernel module related events. On module load
 * some internal data structures are initialized. All board device releated
 * startup operations are triggered by other mechanisms.
 *
 * On module unload all maintained CAPI controllers will be unregistered at the
 * CAPI manager and rendered to disabled state.
 *
 * @param mod                    I: The handle of _this_ module.
 * @param iType                  I: The type of module event that occurred (in
 *                                  fact of type modeventtype_t).
 * @param pUnused                I: Unused address of module specific data.
 */
static int avmaic_modevent
   (module_t  mod,
    int       iType,
    void     *pUnused);
    




/* --- definitions to register the driver with the kernel --- */

/** Flag for received first MOD_LOAD. */
static int g_fLoadEventReceived = 0;

/** The array of driver methods for the ISA device driver. */
static device_method_t g_avmaic_isa_methods [] =
   {
      DEVMETHOD (device_probe,    avmaic_isa_probe),
      DEVMETHOD (device_attach,   avmaic_isa_attach),
      DEVMETHOD (device_detach,   avmaic_detach),
      DEVMETHOD (device_shutdown, avmaic_shutdown),
      /*DEVMETHOD (device_suspend,  avmaic_isa_suspend),*/
      /*DEVMETHOD (device_resume,   avmaic_isa_resume),*/
      { NULL, NULL }
   };

/** The ISA device driver. */
static driver_t g_avmaic_isa_driver =
   {
      AVMAIC_DEVICE_CLASS_NAME, g_avmaic_isa_methods, sizeof (AvmAicSc_t)
   };

/** ISA PNP ids for the devices. Currently there is none. */
static struct isa_pnp_id g_avmaic_pnp_ids [] =
   {
      { 0x0, NULL }
   };

/** The array of driver methods for the PCI device driver. */
static device_method_t g_avmaic_pci_methods [] =
   {
      /* device interface */
      DEVMETHOD (device_probe,     avmaic_pci_probe),
      DEVMETHOD (device_attach,    avmaic_pci_attach),
      DEVMETHOD (device_detach,    avmaic_detach),
      DEVMETHOD (device_shutdown,  avmaic_shutdown),
      /*DEVMETHOD (device_suspend,  avmaic_pci_suspend),*/
      /*DEVMETHOD (device_resume,   avmaic_pci_resume),*/
      { NULL, NULL }
   };

/** The PCI device driver. */
static driver_t g_avmaic_pci_driver =
   {
      AVMAIC_DEVICE_CLASS_NAME, g_avmaic_pci_methods, sizeof (AvmAicSc_t)
   };

/** The array of driver methods for the PC-Card device driver. */
static device_method_t g_avmaic_pccard_methods [] =
   {
#if defined (PCCARD_API_LEVEL) && PCCARD_API_LEVEL >= 6

      /* PC-Card device interface */
      DEVMETHOD (device_probe,    avmaic_pccard_probe),
      DEVMETHOD (device_attach,   avmaic_pccard_attach),
      DEVMETHOD (device_detach,   avmaic_detach),
      DEVMETHOD (device_shutdown, avmaic_shutdown),

#else /* PCCARD_API_LEVEL && PCCARD_API_LEVEL >= 6 */

      /* device interface */
      DEVMETHOD (device_probe,    pccard_compat_probe),
      DEVMETHOD (device_attach,   pccard_compat_attach),
      DEVMETHOD (device_detach,   avmaic_detach),
      DEVMETHOD (device_shutdown, avmaic_shutdown),
      
      /* PC-Card interface */
      DEVMETHOD (card_compat_match, avmaic_pccard_match),
      DEVMETHOD (card_compat_probe, avmaic_pccard_probe),
      DEVMETHOD (card_compat_attach, avmaic_pccard_attach),

#endif /* PCCARD_API_LEVEL && PCCARD_API_LEVEL >= 6 */

      { NULL, NULL }
   };

/** The PC-Card device driver. */
static driver_t g_avmaic_pccard_driver =
   {
      AVMAIC_DEVICE_CLASS_NAME, g_avmaic_pccard_methods, sizeof (AvmAicSc_t)
   };

/**
 * The device class for the avmaic device driver.
 *
 * It is shared by the ISA and the PCI driver. So the unit numbers are unique
 * for all controllers, ISA or PCI.
 */
static devclass_t g_avmaic_devclass;

/** The module definition for the ISA device driver. */
DRIVER_MODULE (avmaic, isa, g_avmaic_isa_driver, g_avmaic_devclass,
               avmaic_modevent, NULL);

/** The module definition for the PCI device driver. */
DRIVER_MODULE (avmaic, pci, g_avmaic_pci_driver, g_avmaic_devclass,
               avmaic_modevent, NULL);

/** The module definition for the PC-Card device driver. */
DRIVER_MODULE (avmaic, pccard, g_avmaic_pccard_driver, g_avmaic_devclass,
               avmaic_modevent, NULL);

MODULE_VERSION (avmaic,
                (AVMAIC_DRIVER_VERSION_MAJOR << 16) |
                   AVMAIC_DRIVER_VERSION_MINOR);

/**
 * @note The CAPI manager interface for controller drivers is not expected to
 *       change within a major version. So any minor version will do, provided
 *       the major version is as expected.
 */
MODULE_DEPEND (avmaic, kcapimgr,
               (CAPIMAN_VERSION_MAJOR << 16) | CAPIMAN_VERSION_MINOR,
               (CAPIMAN_VERSION_MAJOR << 16) | CAPIMAN_VERSION_MINOR,
               (CAPIMAN_VERSION_MAJOR << 16) | 0x0000FFFF);





/* === Implementation of public functions ================================ */





/* === Implementation of private functions =============================== */





/**
 * Probe for an installed ISA controller.
 *
 * This function is called to check if the (ISA) device specified does really
 * exist and is working (at least as far it is possible without any firmware).
 *
 * In this function the only real action performed besides checking the maximum
 * number of supported units is to distinguish between the two supported ISA
 * boards: B1 and T1. The real probe  operation will be performed in separate
 * functions.
 *
 * @param dev                    I: The device entry for the board to probe.
 *
 * @retval 0                     The board device was found and is working.
 * @retval Else                  The board was not found or is not working. The
 *                               result value is an errno value.
 */

static int avmaic_isa_probe
   (device_t dev)
{
   u_int32_t ulFlags;
   int       iRes;

   /* AVM active controllers are pure isa or pci boards, no isa-PnP devices */
   iRes = ISA_PNP_PROBE (device_get_parent (dev), dev, g_avmaic_pnp_ids);
   if (iRes == ENXIO)
   {
      return (ENXIO);
   }
   
   /* determine which card type should be probed, flags 0 means B1 ISA, 0x1X is
    * T1 ISA with board number in the lower nibble
    */
   ulFlags = device_get_flags (dev);
   if (ulFlags == 0)
   {
      iRes = avmb1_isa_probe (dev);
   }
   else if ((ulFlags & 0x0F) == 0x10)
   {
      iRes = avmt1_isa_probe (dev);
   }
   else
   {
      /* no or unknown flag value, first check for B1 and then for T1 */
      iRes = avmb1_isa_probe (dev);
      if (iRes != 0)
      {
         iRes = avmt1_isa_probe (dev);
         if (iRes != 0)
         {
            device_printf (dev,
                           "%s: ERROR: Neither AVM-B1 ISA nor AVM-T1 ISA found\n",
                           __FUNCTION__);
         }
      }
   }

   return (iRes);
} /* avmaic_isa_probe */





/**
 * Attach an installed controller to the ISA bus.
 *
 * This function is called to prepare a device for later use. After successful
 * execution of this function, the device shall be ready for operation.
 *
 * For the avmaic ISA device driver this means to setup the data maintained for
 * the board, to prepare the board for the firmware download and to register the
 * board (i.e. all its controllers) at the CAPI manager.
 *
 * In this function the only action performed besides checking the maximum
 * number of supported units is to distinguish between the two supported ISA
 * boards: B1 and T1. The real attach operation will be performed in separate
 * functions.
 *
 * @param dev                    I: The device entry for the board to attach.
 *
 * @retval 0                     The board was successfully attached.
 * @retval Else                  An errno value representing the error occurred
 *                               during the attach operation.
 */

static int avmaic_isa_attach
   (device_t dev)
{
   u_int32_t ulFlags;
   int       iRes;

   /* determine which card type should be attached, flags 0 means B1 ISA, 0x1X
    * is T1 ISA with board number in the lower nibble
    */
   ulFlags = device_get_flags (dev);
   if (ulFlags == 0)
   {
      iRes = avmb1_isa_attach (dev);
   }
   else if ((ulFlags & 0x10) != 0)
   {
      iRes = avmt1_isa_attach (dev);
   }
   else
   {
      device_printf (dev,
                     "%s: ERROR: Unknown flag value 0x%X for AVM active ISA card\n",
                     __FUNCTION__, device_get_flags (dev));
      iRes = ENXIO;
   }

   /* if attaching the hardware was successful, we must register each port of
    * the controller at the CAPI manager
    */
   if (iRes == 0)
   {
      iRes = RegisterAtCapiMgr (dev, device_get_softc (dev));
      if (iRes != 0)
      {
         avmmisc_detach (dev);
      }
   }

   return (iRes);
} /* avmaic_isa_attach */





/**
 * Probe for an installed PCI controller.
 *
 * This function is called to check if the (PCI) device specified can be driven
 * by this device driver. This is accomplished by checking the PCI vendor and
 * sub-vendor id of the device. If these ids match any of the values for the
 * active AVM boards handled by this driver, the probe operation will be
 * successful.
 *
 * @param dev                    I: The device entry for the board to probe.
 *
 * @retval 0                     The board device was found.
 * @retval Else                  The device specified does not represent an
 *                               active AVM board driven by this device driver.
 *                               The result value is an errno value.
 */

static int avmaic_pci_probe
   (device_t dev)
{
   unsigned      vid;
   unsigned      did;
   unsigned      subvid;
   unsigned      subdid;
   int           iRes;
   unsigned long ulBase;
   unsigned long ulCount;
   
   /* determine vendor and device id */
   vid = pci_get_vendor (dev);
   did = pci_get_device (dev);
   
   /* AVM boards C2 and C4 use vendor and device id from DEC */
   if (vid == AVMAIC_DEC_PCI_VENDOR_ID &&
       did == AVMAIC_DEC_21285_DEVICE_ID)
   {
      /* now check for sub vendor and sub device id from AVM */
      subvid = pci_get_subvendor (dev);
      subdid = pci_get_subdevice (dev);
      if (subvid == AVMAIC_AVM_PCI_VENDOR_ID)
      {
         if (subdid == AVMAIC_C2_DEVICE_ID)
         {
            device_set_desc (dev,
                             avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_C2));
            return (0);
         }
         if (subdid == AVMAIC_C4_DEVICE_ID)
         {
            device_set_desc (dev,
                             avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_C4));
            return (0);
         }
      }
      device_printf (dev,
                     "AVM vendor id 0x%04X and DEC device ID 0x%04X found, but sub ids 0x%04X, 0x%04X are unknown\n",
                     vid, did, subvid, subdid);
      return (ENXIO);
   }
   
   /* other AVM boards: check for AVM vendor id */
   if (vid != AVMAIC_AVM_PCI_VENDOR_ID)
   {
      return (ENXIO);
   }
   
   /* check for known device */
   switch (did)
   {
      case AVMAIC_B1PCI_DEVICE_ID:
         /* maybe B1 PCI v3 or v4 - distinction is made by existence of a second
          * i/o port base
          */
         /* Note: This is just a guess from the AVM driver for Linux! A B1 PCI
          *       v4 passes this test successfully. But if a B1 PCI v3 "behaves"
          *       as expected, must be checked with a real B1 PCI card v3 if
          *       available or when acknowledged by AVM!
          */
         iRes = bus_get_resource (dev, SYS_RES_IOPORT, 0x18, &ulBase, &ulCount);
         if (iRes == 0)
         {
            /* there is a second i/o port base address, assume B1 PCI v4 (with
             * dma support)
             */
            device_set_desc (dev,
                             avmmisc_get_card_type_name
                                (AVMAIC_CARD_TYPE_B1_PCI_V4));
         }
         else
         {
            /* there is no second i/o port base address, assume B1 PCI v3
             * (without dma support)
             */
            device_set_desc (dev,
                             avmmisc_get_card_type_name
                                (AVMAIC_CARD_TYPE_B1_PCI_V3));
         }

         return (0);
         
      case AVMAIC_T1PCI_DEVICE_ID:
         device_set_desc (dev,
                          avmmisc_get_card_type_name
                             (AVMAIC_CARD_TYPE_T1_PCI));
         return (0);
         
      default:
         device_printf (dev,
                        "AVM vendor id 0x%04X found, but device id 0x%04X is unknown\n",
                        vid, did);
         break;
   }
   
   return (ENXIO);
} /* avmaic_pci_probe */





/**
 * Attach an installed controller to the PCI bus.
 *
 * This function is called to prepare a device for later use. After successful
 * execution of this function, the device shall be ready for operation.
 *
 * For the avmaic PCI device driver this means to setup the data maintained for
 * the board, to prepare the board for the firmware download and to register the
 * board (i.e. all its controllers) at the CAPI manager.
 *
 * In this function the only action performed besides checking the maximum
 * number of supported units is to distinguish between the supported PCI
 * boards: B1, C2, C4 and T1. The real attach operation will be performed in
 * separate functions.
 *
 * @param dev                    I: The device entry for the board to attach.
 *
 * @retval 0                     The board was attached successfully.
 * @retval Else                  An errno value representing the error occurred
 *                               during the attach operation.
 */

static int avmaic_pci_attach
   (device_t dev)
{
   AvmAicSc_t *pSc;
   unsigned    subdid;
   int         iRes;

   /* determine and set card type */
   pSc = device_get_softc (dev);
   pSc->iUnit = device_get_unit (dev);
   switch (pci_get_device (dev))
   {
      case AVMAIC_B1PCI_DEVICE_ID:
         iRes = avmb1_pci_attach (dev);
         break;
         
      case AVMAIC_DEC_21285_DEVICE_ID:
         subdid = pci_get_subdevice (dev);
         if (subdid == AVMAIC_C2_DEVICE_ID)
         {
            iRes = avmarm_attach (dev, AVMAIC_CARD_TYPE_C2);
         }
         else if (subdid == AVMAIC_C4_DEVICE_ID)
         {
            iRes = avmarm_attach (dev, AVMAIC_CARD_TYPE_C4);
         }
         else
         {
            device_printf (dev,
                           "Got unsupported device with device id 0x%04X, sub device id 0x%04X to attach\n",
                           pci_get_device (dev), pci_get_subdevice (dev));
            iRes = ENXIO;
         }
         break;
         
      case AVMAIC_T1PCI_DEVICE_ID:
         iRes = avmt1_pci_attach (dev);
      
      default:
         device_printf (dev, "Got unsupported device with id 0x%04X to attach\n",
                        pci_get_device (dev));
         iRes = ENXIO;
         break;
   }

   /* if attaching the hardware was successful, we must register each port of
    * the controller at the CAPI manager
    */
   if (iRes == 0)
   {
      iRes = RegisterAtCapiMgr (dev, pSc);
      if (iRes != 0)
      {
         avmmisc_detach (dev);
      }
   }

   return (iRes);
} /* avmaic_pci_attach */





/**
 * Match a PC-Card device to the list of supported ISDN controllers.
 *
 * This function is called to check that a PC-Card device is supported by this
 * driver. The vendor and product id of the device is checked against the list
 * of supported PC-Cards.
 *
 * @param dev                   I: The device entry for the card to probe.
 *
 * @retval 0                    The card vendor and product ids are known to
 *                              this driver.
 * @retval Else                 The device specified is not supported by this
 *                              driver. The result value is an appropriate
 *                              errno value.
 */

static int avmaic_pccard_match
   (device_t dev)
{
   const struct pccard_product *p;
   
   /* check if the device specified is one of our supported products */
   p = pccard_product_lookup (dev, g_aAvmAicPccardProducts,
                              sizeof (g_aAvmAicPccardProducts [0]), NULL);
   if (p == NULL)
   {
      return (ENXIO);
   }
   
   /* the card is supported, set its device name */
   if (p->pp_name != NULL)
   {
      device_set_desc (dev, p->pp_name);
      DBG (LOG_TRACE, device_get_unit (dev),
           "PC-Card matches, vendor id 0x%04X, product id 0x%04X, product name \"%s\"",
           (int) (p->pp_vendor), (int) (p->pp_product), p->pp_name);
   }
   else
   {
      DBG (LOG_TRACE, device_get_unit (dev),
           "PC-Card matches, vendor id 0x%04X, product id 0x%04X, product name not set",
           (int) (p->pp_vendor), (int) (p->pp_product));
   }
   return (0);
} /* avmaic_pccard_match */





/**
 * Probe for installed PC-Card controller.
 *
 * This function is called to check if the (PC-Card) device specified can be
 * driven by this device driver. For PC-Cards the vendor and product ids are
 * already verified to be supported by a previous call to
 * avmaic_pccard_match(). The remaining task for the probe routine is to check
 * if the card is really working. The B1-PCMCIA is nearly identical to the
 * i/o based B1-PCI v3, so the probe operation to execute is about the same.
 *
 * @todo The card types M1 and M2 are currently not really supported, because
 *       we do not know their product ids. Only when we know them, we can
 *       distinguish between the three card types.
 *
 * @param dev                   I: The device entry for the card to probe.
 *
 * @retval 0                    The PC-Card device is working as expected.
 * @retval Else                 The device specified does not represent an
 *                              active AVM card driven by this device driver.
 *                              The result value is an errno value.
 */

static int avmaic_pccard_probe
   (device_t dev)
{
   int iRes;

#if defined (PCCARD_API_LEVEL) && PCCARD_API_LEVEL >= 6

   /* The newer API has no disinct method for matching the card type any more,
    * it is performed in the context of the probe method
    */
   iRes = avmaic_pccard_match (dev);

#else /* PCCARD_API_LEVEL && PCCARD_API_LEVEL */

   /* Note: As soon as we know the product ids of the M1 and M2 we can
    *       distinguish between the three basically supported card types. For
    *       now we simply assume a B1 PCMCIA.
    */
   iRes = avmb1_pccard_probe (dev, AVMAIC_CARD_TYPE_B1_PCMCIA);
   if (iRes == 0)
   {
      device_set_desc (dev,
                       avmmisc_get_card_type_name
                          (AVMAIC_CARD_TYPE_B1_PCMCIA));
   }

#endif /* PCCARD_API_LEVEL && PCCARD_API_LEVEL */

   return (iRes);
} /* avmaic_pccard_probe */





/**
 * Attach an installed controller to the PC-Card bus.
 *
 * This function is called to prepare a device for later use. After successful
 * execution of this function, the device shall be ready for operation.
 *
 * For the avmaic PC-Card device driver this means to setup the data maintained
 * for the board, to prepare the board for the firmware download and to
 * register the board at the CAPI manager.
 *
 * In this function the only action performed besides checking the maximum
 * number of supported units is to distinguish between the supported PC-Card
 * boards B1-PCMCIA, M1 and M2. The real attach operation will be performed in
 * a separate function.
 *
 * @todo The card types M1 and M2 are currently not really supported, because
 *       we do not know their product ids. Only when we know them, we can
 *       distinguish between the three card types.
 *
 * @param dev                    I: The device entry for the card to attach.
 *
 * @retval 0                     The card was attached successfully.
 * @retval Else                  An errno value representing the error occurred
 *                               during the attach operation.
 */

static int avmaic_pccard_attach
   (device_t dev)
{
   AvmAicSc_t *pSc;
   int         iRes;

   /* determine and set card type */
   /* Note: As soon as we know the product ids of the M1 and M2 we can
    *       distinguish between the three basically supported card types. For
    *       now we simply assume a B1 PCMCIA.
    */
   pSc = device_get_softc (dev);
   pSc->iUnit = device_get_unit (dev);
   iRes = avmb1_pccard_attach (dev, AVMAIC_CARD_TYPE_B1_PCMCIA);

   /* if attaching the hardware was successful, we must register each port of
    * the controller at the CAPI manager
    */
   if (iRes == 0)
   {
      iRes = RegisterAtCapiMgr (dev, pSc);
      if (iRes != 0)
      {
         avmmisc_detach (dev);
      }
   }

   return (iRes);
} /* avmaic_pccard_attach */





/**
 * Detach installed controller from its bus.
 *
 * The detach function is called when the device driver module is unloaded from
 * memory. It must turn the device specified into disabled state, because the
 * device will be destroyed and all related memory removed after this call.
 *
 * For the avmaic driver this requires to unregister the board (i.e. the CAPI
 * controllers related to the board) at the CAPI manager. Last a reset operation
 * must be performed to leave the board in a disabled state and all its
 * resources are released.
 *
 * @param dev                    I: The device entry for the board to detach.
 *
 * @retval 0                     The board was successfully detached.
 * @retval Else                  An errno value representing the error occurred
 *                               during the detach operation.
 */

static int avmaic_detach
   (device_t dev)
{
   /* CAPI release and controller reset is performed by the shutdown call */
   (void) avmaic_shutdown (dev);

   /* finally release all bus resources */
   avmmisc_detach (dev);

   return (0);
} /* avmaic_detach */





/**
 * Shut down installed controller.
 *
 * This function is called on system shutdown. The avmaic device driver will
 * perform a reset operation on the specified device to leave it in a disabled
 * state. This will also terminate all currently running connections.
 *
 * @param dev                    I: The device entry for the board to shutdown.
 *
 * @retval 0                     The board was successfully shut down.
 * @retval Else                  An errno value representing the error occurred
 *                               during the shutdown operation.
 */

static void avmaic_shutdown
   (device_t dev)
{
   AvmAicSc_t       *pSc;
   AvmAicPortData_t *pPort;
   int               s;
   int               i;

   /* perform CAPI release */
   pSc = (AvmAicSc_t *) device_get_softc (dev);
   s = avmmisc_get_ctlr_access (pSc);
   if (pSc->state != AVMAIC_STATE_DOWN &&
       pSc->state != AVMAIC_STATE_INITIALIZING)
   {
      for (i = pSc->nPorts - 1; i >= 0; i--)
      {
         pPort = &(pSc->aPortData [i]);
         if (pPort->uUniqueCapiCtlrNum != 0)
         {
            mtx_unlock (&(pSc->mtxAccess));
            (void) kcapi_ctlr_release (pPort->uUniqueCapiCtlrNum);
            mtx_lock (&(pSc->mtxAccess));
            pPort->uUniqueCapiCtlrNum = 0;
         }
      }
   }

   /* perform generic controller reset */
   avmmisc_disable (pSc);
   pSc->state = AVMAIC_STATE_DOWN;
   avmmisc_release_ctlr_access (pSc, s);

} /* avmaic_shutdown */





/**
 * Register an application at a controller.
 *
 * This function will first perform several checks on the parameters supplied.
 * If the parameters passed are verified successfully, the registration request
 * is forwarded to the controller type specific function. The result is
 * delivered to the caller.
 *
 * @param uDrvCtlrNum           I: Driver specific controller number.
 * @param uMaxLogicalConnections
 *                              I: Maximum number of active B3-connections.
 * @param uMaxBDataBlocks       I: Maximum number of unacknowledged incoming
 *                                 data blocks per connection.
 * @param uMaxBDataLen          I: Maximum data block length to use.
 * @param puApplID              I/O: When calling this function the CAPI manager
 *                                 provides its assigned applicatoin id. On
 *                                 successful return the controller driver must
 *                                 fill in his assigned internal appl. id if it
 *                                 needs application id mapping. If no
 *                                 application id mapping is needed by the
 *                                 controller, this value must be left
 *                                 unchanged.
 *
 * @retval CAPI_OK              Application registration was successful.
 * @retval Else                 An error occurred, the result is one of the CAPI
 *                              result values defined in capi_result.h.
 */

static unsigned avmaic_capidrv_register
   (unsigned  uDrvCtlrNum,
    unsigned  uMaxLogicalConnections,
    unsigned  uMaxBDataBlocks,
    unsigned  uMaxBDataLen,
    unsigned *puApplID)
{
   AvmAicSc_t       *pSc;
   AvmAicPortData_t *pPortData;
   int               s;
   unsigned          uRes;
   
   /* get softc structure and port data for the controller specified */
   if (uDrvCtlrNum >= ARRAY_COUNT (e_apMapCapiCtlrToPortData) ||
       e_apMapCapiCtlrToPortData [uDrvCtlrNum] == NULL)
   {
      DBG (LOG_ERROR, -1, "Invalid controller no. %u", uDrvCtlrNum);
      return (CRE_CAPI_NOT_INSTALLED);
   }
   pPortData = e_apMapCapiCtlrToPortData [uDrvCtlrNum];
   pSc = pPortData->pSc;
   
   /* check parameters */
   if (! puApplID)
   {
      DBG (LOG_ERROR, pSc->iUnit, "Invalid app.id pointer %p", puApplID);
      return (CRE_INVALID_PARAM);
   }
   if (uMaxLogicalConnections == 0)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Appl. id %u: Invalid null value for max. no. connections",
           *puApplID);
      return (CRE_INVALID_CONNECT_NUM);
   }
   
   /* check for valid unit and port state */
   s = avmmisc_get_ctlr_access (pSc);
   if (pSc->state < AVMAIC_STATE_READY ||
       pPortData->state < AVMAIC_STATE_READY)
   {
      avmmisc_release_ctlr_access (pSc, s);
      DBG (LOG_ERROR, pSc->iUnit, "port %u: controller %u not loaded",
           pPortData->uPortIdx, uDrvCtlrNum);
      return (CRE_CAPI_NOT_INSTALLED);
   }
   if (pSc->state != AVMAIC_STATE_READY ||
       pPortData->state != AVMAIC_STATE_READY)
   {
      avmmisc_release_ctlr_access (pSc, s);
      DBG (LOG_TRACE, pSc->iUnit, "port %u: controller %u busy",
           pPortData->uPortIdx, uDrvCtlrNum);
      return (CRE_BUSY);
   }
   
   /* limit the number of B3 connections to the maximum for the controller;
    * maybe the application registers for several controllers, so the total
    * number of connections is higher than the current controller may support
    */
   if (uMaxLogicalConnections > e_iAvmAicMaxNcciPerChannel * pPortData->nBChns)
   {
      uMaxLogicalConnections = e_iAvmAicMaxNcciPerChannel * pPortData->nBChns;
   }
   
   /* perform the registration operation according to the card type */
   if (pSc->pfnAppRegister)
   {
      uRes = pSc->pfnAppRegister (pSc, pPortData,
                                  uMaxLogicalConnections,
                                  uMaxBDataBlocks,
                                  uMaxBDataLen,
                                  *puApplID);
   }
   else
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Invalid function pointer 0x%p registering appl. id %u at controller %u",
           pSc->pfnAppRegister, *puApplID, uDrvCtlrNum);
      uRes = CRE_CAPI_NOT_INSTALLED;
   }
   
   if (uRes == CAPI_OK)
   {
      DBG (LOG_TRACE, pSc->iUnit,
           "Port %u: Appl. id %u successfully registered at controller %u",
           pPortData->uPortIdx, *puApplID, uDrvCtlrNum);
   }
   
   avmmisc_release_ctlr_access (pSc, s);

   return (uRes);
} /* avmaic_capidrv_register */





/**
 * Releasing an application at a controller.
 *
 * This function will first perform several checks on the parameters supplied.
 * If the parameters passed are verified successfully, the release request is
 * forwarded to the controller type specific function. The result is delivered
 * to the caller.
 *
 * @param uDrvCtlrNum           I: Driver specific controller number.
 * @param uCtlrApplID           I: Controller specific application id from
 *                                 registration. If no application id mapping is
 *                                 needed, this is the public CAPI manager
 *                                 assigned id.
 *
 * @retval CAPI_OK              Application release was successful.
 * @retval Else                 An error occurred, the result is one of the CAPI
 *                              result values defined in capi_result.h.
 */

static unsigned avmaic_capidrv_release
   (unsigned uDrvCtlrNum,
    unsigned uCtlrApplID)
{
   AvmAicSc_t       *pSc;
   AvmAicPortData_t *pPortData;
   int               s;
   unsigned          uRes;
   
   /* get softc structure and port data for the controller specified */
   if (uDrvCtlrNum >= ARRAY_COUNT (e_apMapCapiCtlrToPortData) ||
       e_apMapCapiCtlrToPortData [uDrvCtlrNum] == NULL)
   {
      DBG (LOG_ERROR, -1, "Invalid controller no. %u", uDrvCtlrNum);
      return (CRE_CAPI_NOT_INSTALLED);
   }
   pPortData = e_apMapCapiCtlrToPortData [uDrvCtlrNum];
   pSc = pPortData->pSc;
   s = avmmisc_get_ctlr_access (pSc);
   
   /* check for valid unit and port state */
   if (pSc->state < AVMAIC_STATE_READY ||
       pPortData->state < AVMAIC_STATE_READY)
   {
      avmmisc_release_ctlr_access (pSc, s);
      DBG (LOG_ERROR, pSc->iUnit, "Port %u: Controller %u not loaded",
           pPortData->uPortIdx, uDrvCtlrNum);
      return (CRE_CAPI_NOT_INSTALLED);
   }
   
   /* perform the release operation according to the card type */
   if (pSc->pfnAppRelease)
   {
      uRes = pSc->pfnAppRelease (pSc, pPortData, uCtlrApplID);
   }
   else
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Invalid function pointer 0x%p releasing appl. id %u at controller %u",
           pSc->pfnAppRelease, uCtlrApplID, uDrvCtlrNum);
      uRes = CRE_CAPI_NOT_INSTALLED;
   }
   
   if (uRes == CAPI_OK)
   {
      DBG (LOG_TRACE, pSc->iUnit,
           "Port %u: Appl. id %u successfully released at controller %u",
           pPortData->uPortIdx, uCtlrApplID, uDrvCtlrNum);
   }
   
   avmmisc_release_ctlr_access (pSc, s);

   return (uRes);
} /* avmaic_capidrv_release */





/**
 * Send a CAPI message to a controller.
 *
 * This function will first perform several checks on the parameters supplied.
 * If the parameters passed are verified successfully, the put-message request
 * is forwarded to the controller type specific function. The result is
 * delivered to the caller.
 *
 * @param uDrvCtlrNum           I: Driver specific controller number.
 * @param uCtlrApplID           I: Controller specific application id from
 *                                 registration.
 * @param pmbMsg                I: The CAPI message to send. A Data-B3-Request
 *                                 contains the address of a second mbuf in the
 *                                 pmbMsg->m_next member. If the function call
 *                                 returns with CAPI_OK, the driver has taken
 *                                 over ownership of the mbuf(s). It is
 *                                 responsible to release it (them) when it
 *                                 (they) are not needed any more.
 *
 * @retval CAPI_OK              The driver accepted the message.
 * @retval Else                 An error occurred, the result is one of the CAPI
 *                              result values defined in capi_result.h.
 */

static unsigned avmaic_capidrv_put_message
   (unsigned     uDrvCtlrNum,
    unsigned     uCtlrApplID,
    struct mbuf *pmbMsg)
{
   AvmAicSc_t       *pSc;
   AvmAicPortData_t *pPortData;
   int               s;
   unsigned          uCmd;
   unsigned          uRes;
   
   /* get softc structure and port data for the controller specified */
   if (uDrvCtlrNum >= ARRAY_COUNT (e_apMapCapiCtlrToPortData) ||
       e_apMapCapiCtlrToPortData [uDrvCtlrNum] == NULL)
   {
      DBG0 (LOG_ERROR, "Invalid controller no. %u", uDrvCtlrNum);
      return (CME_CAPI_NOT_INSTALLED);
   }
   pPortData = e_apMapCapiCtlrToPortData [uDrvCtlrNum];
   pSc = pPortData->pSc;
   s = avmmisc_get_ctlr_access (pSc);
   
   /* check for valid unit and port state */
   if (pSc->state < AVMAIC_STATE_READY ||
       pPortData->state < AVMAIC_STATE_READY)
   {
      avmmisc_release_ctlr_access (pSc, s);
      DBG (LOG_ERROR, pSc->iUnit, "Port %u: Controller %u not loaded",
           pPortData->uPortIdx, uDrvCtlrNum);
      return (CME_CAPI_NOT_INSTALLED);
   }
   if (pSc->state != AVMAIC_STATE_READY ||
       pPortData->state != AVMAIC_STATE_READY)
   {
      avmmisc_release_ctlr_access (pSc, s);
      DBG (LOG_TRACE, pSc->iUnit, "Port %u: Controller %u busy",
           pPortData->uPortIdx, uDrvCtlrNum);
      return (CME_BUSY);
   }
   
   /* perform the put message operation according to the card type */
   uCmd = CAPI_GET_CMD (mtod (pmbMsg, CAPIMsg_t *));
   if (pSc->pfnPutMessage)
   {
      uRes = pSc->pfnPutMessage (pSc, pPortData, uCtlrApplID, pmbMsg);
   }
   else
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Invalid function pointer 0x%p for kcapi_put_message, appl. id %u, controller %u",
           pSc->pfnPutMessage, uCtlrApplID, uDrvCtlrNum);
      uRes = CRE_CAPI_NOT_INSTALLED;
   }
   
   if (uRes == CAPI_OK)
   {
      DBG (LOG_DEBUG, pSc->iUnit,
           "Port %u: CAPI message 0x%04X successfully sent to controller %u for appl. id %u",
           pPortData->uPortIdx, uCmd, uDrvCtlrNum, uCtlrApplID);
   }
   
   avmmisc_release_ctlr_access (pSc, s);

   return (uRes);
} /* avmaic_capidrv_put_message */





/**
 * Reset and/or download the controller.
 *
 * This call is controller specific, its support is optional. But for active
 * ISDN adapters it is the only way to download the software and settings onto
 * the adapter. Even passive controllers may obtain the configuration for the
 * line the controller is attached to.
 *
 * How many data blocks must be specified and what their contents must look like
 * is driver specific. You have to look at the specification for the driver of
 * the controller in question. In general every driver that needs some download
 * will come with its own downloading program that indirectly calls this
 * function.
 *
 * The avmaic device driver expects one or two data blocks (if this is not only
 * a reset operation with no data blocks). The first block must contain the
 * firmware to download to the board. The second optional data block is the
 * configuration for the board. Currently all ports of a multiport-board must
 * share the same D-channel protocol and other configuration data.
 *
 * @note This call is only allowed if the calling processes effective user id
 *       has superuser privileges.
 *
 * @param uUniqueCtlrNum        I: Unique controller number assigned by the CAPI
 *                                 manager on controller registration, to reset
 *                                 or download.
 * @param nNumDataBlocks        I: Number of data blocks to be sent to the
 *                                 controller. It is the length of the array at
 *                                 paDataBlocks.
 * @param paDataBlocks          I: Array of data blocks to be sent to the
 *                                 controller.
 *
 * @retval CAPI_OK              The download operation was successful.
 * @retval Else                 An error occurred, the result is one of the CAPI
 *                              result values defined in capi_result.h.
 */

static unsigned avmaic_capidrv_reset_ctlr
   (unsigned             uUniqueCtlrNum,
    size_t               nNumDataBlocks,
    CAPICtlrDataBlock_t *paDataBlocks)
{
   AvmAicSc_t       *pSc;
   AvmAicPortData_t *pPortData;
   int               s;
   unsigned          uRes;
   unsigned          uMaj;
   unsigned          uMin;
   unsigned          uRev;
   size_t            n;
   int               i;
   
   /* get softc structure and port data for the controller specified */
   if (uUniqueCtlrNum >= ARRAY_COUNT (e_apMapCapiCtlrToPortData) ||
       e_apMapCapiCtlrToPortData [uUniqueCtlrNum] == NULL)
   {
      DBG0 (LOG_ERROR, "Invalid controller no. %u", uUniqueCtlrNum);
      return (CME_CAPI_NOT_INSTALLED);
   }
   pPortData = e_apMapCapiCtlrToPortData [uUniqueCtlrNum];
   pSc = pPortData->pSc;
   s = avmmisc_get_ctlr_access (pSc);
   
   /* download is only allowed for the first port of a board */
   if (pPortData->uPortIdx != 0)
   {
      avmmisc_release_ctlr_access (pSc, s);
      DBG (LOG_ERROR, pSc->iUnit,
           "Port %u: Download only allowed on port 0",
           pPortData->uPortIdx);
      return (CRE_RESET_NOT_SUPPORTED);
   }
   
   /* perform the put message operation according to the card type */
   if (pSc->pfnResetCtlr)
   {
      uRes = pSc->pfnResetCtlr (pSc, nNumDataBlocks, paDataBlocks);
   }
   else
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Invalid function pointer 0x%p for resetting controller %u",
           pSc->pfnResetCtlr, uUniqueCtlrNum);
      uRes = CRE_CAPI_NOT_INSTALLED;
   }
   
   /* if the download was successful, all ports of the board must be enabled
    * at the CAPI manager
    */
   if (uRes == CAPI_OK && pSc->state >= AVMAIC_STATE_READY)
   {
      DBG (LOG_TRACE, pSc->iUnit, "Board \"%s\" successfully downloaded",
           pSc->szCardName);
           
      for (i = 0; i < pSc->nPorts; i++)
      {
         pPortData = &(pSc->aPortData [i]);
         
         /* first some version data must be transfered to the register params
          */
         /* transform the version information according to AVM spec. */
         uMaj = uMin = uRev = 0;
         sscanf (pPortData->szDriverVersion, "%u.%u-%u", &uMaj, &uMin, &uRev);
         pPortData->regParams.uManufacturerMajor =
            ((uMaj << 4) | ((uMin / 10) & 0x0F));
         pPortData->regParams.uManufacturerMinor =
            (((uMin % 10) << 4) | (uRev & 0x0F));
         /* AVM's serial numbers are sometimes longer than 7 characters, so we
          * only deliver the _last_ 7 characters of the serial number
          */
         n = strlen (pPortData->szSerialNo);
         if (n >= sizeof (pPortData->regParams.szSerialNumber))
         {
            strcpy (pPortData->regParams.szSerialNumber,
                    pPortData->szSerialNo + n + 1 -
                       sizeof (pPortData->regParams.szSerialNumber));
         }
         else
         {
            strcpy (pPortData->regParams.szSerialNumber, pPortData->szSerialNo);
         }
         /* the rest is only copy */
         bcopy (pPortData->abCapiProfile, &(pPortData->regParams.profile),
                sizeof (pPortData->regParams.profile));
         if (pSc->nPorts > 1)
         {
            pPortData->regParams.ulFlags =
               CAPI_CTLRFLAG_REGISTER_AT_FIRST_PORT |
               CAPI_CTLRFLAG_RESET_FIRST_PORT;
         }
         else
         {
            pPortData->regParams.ulFlags = 0;
         }
         
         /* now perform the enable operation */
         uUniqueCtlrNum = pPortData->uUniqueCapiCtlrNum;
         mtx_unlock (&(pSc->mtxAccess));
         uRes = kcapi_ctlr_enable (uUniqueCtlrNum, uUniqueCtlrNum);
         mtx_lock (&(pSc->mtxAccess));
         if (uRes == CAPI_OK)
         {
            DBG (LOG_TRACE, pSc->iUnit,
                 "Port %u: Controller %u successfully enabled at the CAPI manager",
                 pPortData->uPortIdx, uUniqueCtlrNum);
         }
         else
         {
            DBG (LOG_TRACE, pSc->iUnit,
                 "Port %u: Failed to enable controller %u at the CAPI manager",
                 pPortData->uPortIdx, uUniqueCtlrNum);
         }
      }
   }

   avmmisc_release_ctlr_access (pSc, s);
   
   return (uRes);
} /* avmaic_capidrv_reset_ctlr */





/**
 * Register a board at the CAPI manager.
 *
 * This function is called after the hardware related operations of the attach
 * function for a board are successfully completed. For every ISDN port of the
 * board specified by dev and pSc the registration structure is filled an the
 * port is registered as a CAPI controller at the CAPI manager.
 *
 * @pre This function must be called with controller access obtained through
 *      avmmisc_get_ctlr_access().
 *
 * @param dev                    I: The device entry for the board to register.
 * @param pSc                    I/O: The softc structure of the board to
 *                                  register.
 *
 * @retval 0                     The register operation was successful.
 * @retval Else                  An errno value representing the error occurred
 *                               during the register operation. In most cases
 *                               this will be a CAPI result value.
 */

static int RegisterAtCapiMgr
   (device_t    dev, 
    AvmAicSc_t *pSc)
{
   AvmAicPortData_t         *pPortData;
   CAPICtlrRegisterParams_t *pRegParams;
   int                       i;
   unsigned                  uRes;

   /* check for valid address for board data */
   if (! pSc)
   {
      device_printf (dev, "Invalid address for softc structure\n");
      return (ENXIO);
   }

   /* register all ports of the board at the CAPI manager */
   for (i = 0; i < pSc->nPorts; i++)
   {
      /* fill the registration data for the port */
      pPortData = &(pSc->aPortData [i]);
      pRegParams = &(pPortData->regParams);
      pRegParams->uIfVersion = CAPI_DRIVER_IF_VERSION;
      pRegParams->pszCtlrName = pPortData->szCtlrPortName;
      pRegParams->pszCtlrTypeName = device_get_desc (dev);
      pRegParams->pszDriverName = pSc->szDriverName;
      pRegParams->uDrvUnitNumber = (unsigned) (pSc->iUnit);
      pRegParams->uNumPorts = pSc->nPorts;
      pRegParams->uPortIdx = i;
      if (pSc->nPorts > 1)
      {
         pPortData->regParams.ulFlags = CAPI_CTLRFLAG_REGISTER_AT_FIRST_PORT |
                                        CAPI_CTLRFLAG_RESET_FIRST_PORT;
      }
      else
      {
         pPortData->regParams.ulFlags = 0;
      }
      pRegParams->pszManufacturer = AVMAIC_MANUFACTURER_NAME;
      pRegParams->uDriverMajor    = AVMAIC_DRIVER_VERSION_MAJOR;
      pRegParams->uDriverMinor    = AVMAIC_DRIVER_VERSION_MINOR;
      bcopy (pPortData->abCapiProfile, &(pRegParams->profile),
             sizeof (pPortData->abCapiProfile));
      pRegParams->pfnRegister     = avmaic_capidrv_register;
      pRegParams->pfnRelease      = avmaic_capidrv_release;
      pRegParams->pfnPutMessage   = avmaic_capidrv_put_message;
      pRegParams->pfnResetCtlr    = avmaic_capidrv_reset_ctlr;
      pRegParams->pfnPutNotifyMsg = NULL;
      /* the remaining fields are filled when downloading the software */

      /* register the port as a controller at the CAPI manager */
      uRes = kcapi_ctlr_register (pRegParams,
                                  &(pPortData->uUniqueCapiCtlrNum));
      if (uRes != CAPI_OK)
      {
         device_printf (dev,
                        "Attach failed, error 0x%04X registering at the CAPI manager\n",
                        uRes);
         return (ENXIO);
      }
      if (pPortData->uUniqueCapiCtlrNum <= 0 ||
          pPortData->uUniqueCapiCtlrNum >
             ARRAY_COUNT (e_apMapCapiCtlrToPortData))
      {
         DBG (LOG_ERROR, pSc->iUnit,
              "Port %u: Got invalid controller number %u registering at the CAPI manager",
              (unsigned) i, pPortData->uUniqueCapiCtlrNum);
         return (ENXIO);
      }
      pPortData->uDrvCapiCtlrNum = pPortData->uUniqueCapiCtlrNum;

      /* set the mapping from the controller number to the softc structure */
      e_apMapCapiCtlrToPortData [pPortData->uUniqueCapiCtlrNum] = pPortData;

      device_printf (dev,
                     "\"%s\" successfully attached as CAPI controller %u\n",
                     pPortData->szCtlrPortName, pPortData->uUniqueCapiCtlrNum);
   }
   
   return (0);
} /* RegisterAtCapiMgr */





/**
 * Unregister all controllers at the CAPI manager.
 *
 * This function is called when the avmaic kernel module is unloaded from
 * memory. It must perform a controller release operation for all CAPI
 * controllers maintained by this driver. The result will be to release all
 * memory related to these CAPI controllers and the termination of all still
 * active connections for each controller.
 *
 * @param None.
 *
 * @return Nothing.
 */

static void UnregisterAllControllers (void)
{
   devclass_t        dc;
   device_t          dev;
   AvmAicSc_t       *pSc;
   AvmAicPortData_t *pPort;
   int               i;
   int               j;
   int               s;
   
   /* check if our device class was really created */
   dc = devclass_find (AVMAIC_DEVICE_CLASS_NAME);
   if (! dc)
   {
      return;
   }
   
   /* for all ports on all units perform CAPI manager release and hardware
    * reset
    */
   for (i = devclass_get_maxunit (dc) - 1; i >= 0; i--)
   {
      dev = devclass_get_device (dc, i);
      if (dev)
      {
         pSc = (AvmAicSc_t *) device_get_softc (dev);
         if (pSc)
         {
            s = avmmisc_get_ctlr_access (pSc);
            if (pSc->state != AVMAIC_STATE_DOWN &&
                pSc->state != AVMAIC_STATE_INITIALIZING)
            {
               for (j = pSc->nPorts - 1; j >= 0; j--)
               {
                  pPort = &(pSc->aPortData [j]);
                  if (pPort->uUniqueCapiCtlrNum != 0)
                  {
                     mtx_unlock (&(pSc->mtxAccess));
                     (void) kcapi_ctlr_release (pPort->uUniqueCapiCtlrNum);
                     mtx_lock (&(pSc->mtxAccess));
                     pPort->uUniqueCapiCtlrNum = 0;
                  }
               }
            }
            avmmisc_reset (pSc);
            pSc->state = AVMAIC_STATE_DOWN;
            avmmisc_release_ctlr_access (pSc, s);
         }
         
         /* Note: It would be nice if we could destroy all dynamically created
          *       (isa) devices during identification. But this information is
          *       not available at this point. So we must leave these devices
          *       existing. The identify routine will "see" these devices on the
          *       next module load and will not create new devices with the same
          *       resources.
          */
      }
   }
   
} /* UnregisterAllControllers */





/**
 * Handling of module load/unload events.
 *
 * This the handler function of all kernel module related events. On module load
 * some internal data structures are initialized. All board device releated
 * startup operations are triggered by other mechanisms.
 *
 * On module unload all maintained CAPI controllers will be unregistered at the
 * CAPI manager and rendered to disabled state.
 *
 * @param mod                    I: The handle of _this_ module.
 * @param iType                  I: The type of module event that occurred (in
 *                                  fact of type modeventtype_t).
 * @param pUnused                I: Unused address of module specific data.
 */

static int avmaic_modevent
   (module_t  mod,
    int       iType,
    void     *pUnused)
{
   switch (iType)
   {
      case MOD_LOAD:
         if (! g_fLoadEventReceived)
         {
            snprintf (g_szVersion, sizeof (g_szVersion), "%d.%d",
                      AVMAIC_DRIVER_VERSION_MAJOR, AVMAIC_DRIVER_VERSION_MINOR);
         }
         g_fLoadEventReceived = 1;
         break;
         
      case MOD_UNLOAD:
      case MOD_SHUTDOWN:
         /* for all hardware perform release with the CAPI manager and do a
          * hardware reset to release possibly active connections
          */
         UnregisterAllControllers ();
         g_fLoadEventReceived = 0;
         break;
         
      default:
         break;
   }
   
   return (0);
} /* avmaic_modevent */
