/**
 * @file kcapi_ctlr.c
 *
 * kcapi_ctlr.c - kernel CAPI manager for *BSD: CAPI controller handling.
 *
 * Copyright: 2000-2003 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: kcapi_ctlr.c,v 1.1.2.1 2006/02/28 21:35:26 thomas Exp $
 * Project  CAPI for BSD
 * Target   kcapimgr - The CAPI manager for *BSD
 * @date    12.08.2002
 * @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/mbuf.h>
#include <sys/proc.h>
#include <sys/kthread.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/lockmgr.h>
#include <sys/sema.h>
#include <sys/socket.h> /* for net/if.h */
#include <net/if.h> /* struct ifqueue */
#include <capi20.h>
#include <capi_bsd.h>

/* import includes */

#define __KCAPI_CTLR__

/* local includes */
#include <opt_kcapimgr.h>
#include <c4b/kcapimgr/capi_drv.h>
#include <c4b/kcapimgr/kcapimgr_var.h>
#include <c4b/kcapimgr/kcapi_appl.h>
#include <c4b/kcapimgr/kcapi_trace.h>
#include <c4b/kcapimgr/kcapi_ctlr.h>





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





/* --- some global counters available to all modules --- */

/** The current number of registered controllers. */
unsigned e_uNumCtlrs = 0;





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





/** The message for waiting for no active calls to a controller. */
#define WAIT_MSG_NO_CALLS       "waiting for no calls"

/** The sleep time in hz units while waiting for no active calls. */
#define SLEEP_VALUE_NO_CALLS    (hz / 10)

/** The timeout value in hz units for waiting for no active calls. */
#define TIMEOUT_NO_CALLS        (5 * hz)



/* --- the array of data for registered controllers --- */

/** Data structure maintained for every registered ISDN controller. */
typedef struct
{
   int                       fCtlrRegistered;
                                /**<
                                 * Flag for validity of the following fields. It
                                 * is only modified through the "atomic"
                                 * functions. On controller registration it must
                                 * be the last field set if registration is
                                 * successful, and the first field to be reset
                                 * on controller release.
                                 */
   unsigned                  uUniqueCtlrNum;
                                /**<
                                 * CAPI manager assigned unique controller
                                 * number. It is used as the index into the
                                 * array of registered controllers. It remains
                                 * constant during the lifetime of the
                                 * registration.
                                 */
   unsigned                  uMultiPortMainCtlr;
                                /**<
                                 * If the controller is part of a multi-port
                                 * card, this field is set to the CAPI
                                 * controller number of the main (first)
                                 * controller of the card. It remains constant
                                 * during the lifetime of the registration.
                                 */
   int                       fCtlrEnabled;
                                /**<
                                 * - 0
                                 *    The controller is disabled. The controller
                                 *    must not be used by any CAPI application
                                 *    call but for a controller reset.
                                 * - 1
                                 *    The controller is fully registered and
                                 *    enabled, thus ready for operation.
                                 */
   unsigned                  uNumActiveCalls;
                                /**<
                                 * The number of calls currently active for this
                                 * controller. This field works as a lock for
                                 * releasing and resetting the controller. These
                                 * operations are only allowed when the number
                                 * of active calls is null. A call in this
                                 * context is defined as a call into the driver
                                 * through the function pointers from the
                                 * controller registration. Before each call
                                 * this field is incremented (with the global
                                 * controller mutex locked). After the call the
                                 * value is decremented (again secured by the
                                 * controller mutex).
                                 */
   unsigned                  uDrvCtlrNum;
                                /**<
                                 * Driver specific controller number. If
                                 * controller does not need ctlr. no. mapping,
                                 * this value is equal to uUniqueCtlrNum.
                                 */
   CAPICtlrRegisterParams_t *pRegParams;
                                /**<
                                 * Registration data for controller. If
                                 * controller is registered, this field WILL
                                 * point to valid data.
                                 */

   /* The following two arrays are needed to map application ids assigned by
    * the CAPI manager to application ids assigned by a controller and vice
    * versa. Every registration and every release operation will update the
    * data. A value of zero means, the entry is not valid, i.e. the application
    * is not registered at this controller.
    */
   unsigned                  auAppMapManToCtlr [CAPI_MAX_APPLICATIONS];
   unsigned                  auAppMapCtlrToMan [CAPI_MAX_APPLICATIONS];
} CapiManCtlrInfo_t;

/** The array of controller information. */
static CapiManCtlrInfo_t g_aCtlrInfo [CAPI_MAX_CONTROLLERS] = { { 0 } };

/**<
 * The mutex for accessing controller data.
 *
 * This mutex must be obtained every time controller data in g_aCtlrInfo is
 * accessed or even when a controller is added or removed. It protects all
 * fields in the data structure for each controller.
 *
 * Before a call into the controller driver is made, the mutex must be obtained
 * and the current number of active calls must be incremented. This will lock
 * the controller for the release and reset operations. Before calling into the
 * driver, the mutex must be released to allow the driver to eventually sleep
 * on locks or wait for events on conditions, a.s.o. After the call the counter
 * must be decremented, again with the mutex locked.
 *
 * The reset and release operations are only allowed when the current number of
 * active driver calls is null. Within these functions the mutex must be
 * acquired and the counter checked. If it is greater than null, the thread must
 * release the mutex and wait for the counter to be null (or a timeout occurs).
 * This is best done by calling msleep(). When the counter has reached null, the
 * operation can proceed. Before releasing the mutex the must be disabled to not
 * let other threads do "regular" calls into the driver.
 *
 * This way of protection surely leaves some unsightly overhead in the reset and
 * controller release functions. But whereas these functions are called rather
 * seldom, the other very often called functions like kcapictlr_put_message()
 * only need to obtain a simple mutex, check some values and on success proceed
 * with the driver call. So these functions are implemented rather efficient.
 *
 * One might think a separate mutex for every controller would lead to more
 * parallelity. But in typical cases there will be only a few controllers in a
 * system (likely from one to four at maximum). And the code passages protected
 * by this single mutex are rather short. So a single mutex is sufficient. And
 * this keeps the design simple, reducing overhead that would otherwise lead to
 * more code.
 */
static struct mtx g_mtxCtlrData;



/* --- accepting messages from the controllers --- */

/**
 * The queue for messages from controllers to applications.
 *
 * All messages from controllers are placed into this queue for later handling.
 * So calls from controller drivers only go as far as to this queue.
 *
 * @note The access to this queue is protected by an internal mutex since
 *       REL-5.0. In earlier versions access must be obtained through SPLC4B().
 */
static struct ifqueue g_queueCtlrMsg;

/** Flag to stop the message handling thread. */
static volatile int g_fDoWorkLoop = 0;

/** Semaphore to signal a new message in the queue. */
static struct sema g_semCtlrMsg;

/** Semaphore for the message handler thread to signal its termination. */
static struct sema g_semThreadTerm;





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





/**
 * Thread function to handle messages received from controllers.
 */
static void kcapictlr_message_handler
   (void *pUnused);

/**
 * Handle an incoming CAPI message from a controller.
 */
static void kcapictlr_receive_capi_message
   (struct mbuf *pmbMsg);
   
/**
 * Perform controller mapping for a CAPI message.
 *
 * @pre The message specified is at least long enough to hold the message
 *      header.
 */
static void kcapictlr_do_ctlr_exchange
   (CAPIMsg_t *pMsg,
    unsigned   uNewCtlr);

/**
 * Forward a notification message to the originating controller.
 */
static void kcapictlr_forward_notify_message
   (struct mbuf *pmbMsg);





/* === implementation of CAPI application interface functions ============ */





/**
 * Ask for the manufacturer of a controller.
 *
 * @param uCtlr                 I: Controller number in question. If 0 is
 *                                 specified the manufacturer string of the CAPI
 *                                 manufacturer itself is returned in pszBuffer.
 * @param pszBuffer             O: Null terminated string for the manufacturer
 *                                 name. The buffer must be at least 64 bytes
 *                                 large.
 *
 * @retval CAPI_OK              The manufacturer string was successfully filled
 *                              into pszBuffer.
 * @retval Else                 An error occurred, the result is one of the CAPI
 *                              result values defined in capi_result.h
 */

unsigned kcapi_get_manufacturer
   (unsigned       uCtlr,
    unsigned char *pszBuffer)
{
   CapiManCtlrInfo_t *pCtlrInfo;
   unsigned           uRes;

   /* check for a request to the manufacturer string of the CAPI manager itself
    */
   if (uCtlr == 0)
   {
      strncpy (pszBuffer, CAPIMAN_MANUFACTURER_STRING, 63);
      pszBuffer [63] = '\0';
      uRes = CAPI_OK;
      if (e_uNumTracers > 0)
      {
         kcapitrace_appl_get_manufacturer (uCtlr, pszBuffer, uRes);
      }
      return (uRes);
   }
   
   mtx_lock (&g_mtxCtlrData);
   
   /* check for registered controller */
   if (uCtlr >= ARRAY_COUNT (g_aCtlrInfo) ||
       ! g_aCtlrInfo [uCtlr].fCtlrRegistered)
   {
      mtx_unlock (&g_mtxCtlrData);
      DBG (LOG_ERROR, "Controller %u not registered", uCtlr);
      uRes = CRE_CAPI_NOT_INSTALLED;
      kcapitrace_appl_get_manufacturer (uCtlr, pszBuffer, uRes);
      return (uRes);
   }
   pCtlrInfo = &(g_aCtlrInfo [uCtlr]);
   
   if (pszBuffer)
   {
      strncpy (pszBuffer, pCtlrInfo->pRegParams->pszManufacturer, 63);
      pszBuffer [63] = '\0';
   }

   mtx_unlock (&g_mtxCtlrData);

   uRes = CAPI_OK;
   if (e_uNumTracers > 0)
   {
      kcapitrace_appl_get_manufacturer (uCtlr, pszBuffer, uRes);
   }
   return (uRes);
} /* kcapi_get_manufacturer */





/**
 * Get the version of the CAPI implementation.
 *
 * @note All output arguments may be NULL if the corresponding version data is
 *       not needed by the caller.
 *
 * @param uCtlr                 I: Controller number in question. If 0 is
 *                                 specified the version numbers for the CAPI
 *                                 manager itself are returned. As there is no
 *                                 distinction between firmware and driver
 *                                 software, the manufacturer and the BSD
 *                                 version numbers will be the same.
 * @param puCAPIMajor           O: Major CAPI version (2).
 * @param puCAPIMinor           O: Minor CAPI version (0).
 * @param puManufacturerMajor   O: Major version of the controller firmware. If
 *                                 the board is a passive one and thus runs no
 *                                 firmware, this value will be the same as the
 *                                 BSD version.
 * @param puManufacturerMinor   O: Minor version of the controller firmware. If
 *                                 the board is a passive one and thus runs no
 *                                 firmware, this value will be the same as the
 *                                 BSD version.
 * @param puBSDMajor            O: Major version of the controller device driver.
 * @param puBSDMinor            O: Minor version of the controller device driver.
 *
 * @retval CAPI_OK              The version information was successfully filled
 *                              into the argument addresses that are not NULL.
 * @retval Else                 An error occurred, the result is one of the CAPI
 *                              result values defined in capi_result.h
 */

unsigned kcapi_get_version
   (unsigned  uCtlr,
    unsigned *puCAPIMajor,
    unsigned *puCAPIMinor,
    unsigned *puManufacturerMajor,
    unsigned *puManufacturerMinor,
    unsigned *puBSDMajor,
    unsigned *puBSDMinor)
{
   CapiManCtlrInfo_t *pCtlrInfo;
   unsigned           uRes;
   
   /* first fill in the CAPI version */
   if (puCAPIMajor)
   {
      *puCAPIMajor = 2;
   }
   if (puCAPIMinor)
   {
      *puCAPIMinor = 0;
   }

   /* controller null addresses the CAPI manager version itself */
   if (uCtlr == 0)
   {
      if (puManufacturerMajor)
      {
         *puManufacturerMajor = CAPIMAN_VERSION_MAJOR;
      }
      if (puManufacturerMinor)
      {
         *puManufacturerMinor = CAPIMAN_VERSION_MINOR;
      }
      if (puBSDMajor)
      {
         *puBSDMajor = CAPIMAN_VERSION_MAJOR;
      }
      if (puBSDMinor)
      {
         *puBSDMinor = CAPIMAN_VERSION_MINOR;
      }

      uRes = CAPI_OK;
      if (e_uNumTracers > 0)
      {
         kcapitrace_appl_get_version
            (uCtlr,
             puCAPIMajor ? *puCAPIMajor : 0,
             puCAPIMinor ? *puCAPIMinor : 0,
             puManufacturerMajor ? *puManufacturerMajor : 0,
             puManufacturerMinor ? *puManufacturerMinor : 0,
             puBSDMajor ? *puBSDMajor : 0,
             puBSDMinor ? *puBSDMinor : 0,
             uRes);
      }
      return (uRes);
   }

   mtx_lock (&g_mtxCtlrData);
   
   /* check for registered controller */
   if (uCtlr >= ARRAY_COUNT (g_aCtlrInfo) ||
       ! g_aCtlrInfo [uCtlr].fCtlrRegistered)
   {
      mtx_unlock (&g_mtxCtlrData);
      DBG (LOG_ERROR, "Controller %u not registered", uCtlr);
      uRes = CRE_CAPI_NOT_INSTALLED;
      kcapitrace_appl_get_version (uCtlr, 0, 0, 0, 0, 0, 0, uRes);
      return (uRes);
   }
   pCtlrInfo = &(g_aCtlrInfo [uCtlr]);
   
   if (puManufacturerMajor)
   {
      *puManufacturerMajor = pCtlrInfo->pRegParams->uManufacturerMajor;
   }
   if (puManufacturerMinor)
   {
      *puManufacturerMinor = pCtlrInfo->pRegParams->uManufacturerMinor;
   }
   if (puBSDMajor)
   {
      *puBSDMajor = pCtlrInfo->pRegParams->uDriverMajor;
   }
   if (puBSDMinor)
   {
      *puBSDMinor = pCtlrInfo->pRegParams->uDriverMinor;
   }

   mtx_unlock (&g_mtxCtlrData);
   
   uRes = CAPI_OK;
   if (e_uNumTracers > 0)
   {
      kcapitrace_appl_get_version
         (uCtlr,
          puCAPIMajor ? *puCAPIMajor : 0,
          puCAPIMinor ? *puCAPIMinor : 0,
          puManufacturerMajor ? *puManufacturerMajor : 0,
          puManufacturerMinor ? *puManufacturerMinor : 0,
          puBSDMajor ? *puBSDMajor : 0,
          puBSDMinor ? *puBSDMinor : 0,
          uRes);
   }
   return (uRes);
} /* kcapi_get_version */





/**
 * Get the serial number of a controller.
 *
 * @param uCtlr                 I: Controller number in question. If 0 is
 *                                 specified, the serial number of the CAPI
 *                                 manager itself is requested. As it does not
 *                                 support serial numbers, the result is an
 *                                 empty string.
 * @param pszBuffer             O: Null terminated string for the serial number
 *                                 of the controller. If the controller does not
 *                                 support serial numbers, this will result in
 *                                 an empty string. The buffer for the string
 *                                 must be at least 8 bytes large.
 *
 * @retval CAPI_OK              The serial number was successfully copied into
 *                              the buffer (even if it is empty).
 * @retval Else                 An error occurred, the result is one of the CAPI
 *                              result values defined in capi_result.h
 */

unsigned kcapi_get_serial_number
   (unsigned       uCtlr,
    unsigned char *pszBuffer)
{
   CapiManCtlrInfo_t *pCtlrInfo;
   unsigned           uRes;
   
   mtx_lock (&g_mtxCtlrData);
   
   /* check for registered controller */
   if (uCtlr >= ARRAY_COUNT (g_aCtlrInfo) ||
       ! g_aCtlrInfo [uCtlr].fCtlrRegistered)
   {
      mtx_unlock (&g_mtxCtlrData);
      DBG (LOG_ERROR, "Controller %u not registered", uCtlr);
      uRes = CRE_CAPI_NOT_INSTALLED;
      kcapitrace_appl_get_serial_number (uCtlr, pszBuffer, uRes);
      return (uRes);
   }
   pCtlrInfo = &(g_aCtlrInfo [uCtlr]);
   
   if (pszBuffer)
   {
      if (uCtlr == 0)
      {
         /* the CAPI manager itself has no serial number */
         pszBuffer [0] = '\0';
      }
      else
      {
         strncpy (pszBuffer, pCtlrInfo->pRegParams->szSerialNumber, 7);
         pszBuffer [7] = '\0';
      }
   }
   
   mtx_unlock (&g_mtxCtlrData);
   
   uRes = CAPI_OK;
   if (e_uNumTracers > 0)
   {
      kcapitrace_appl_get_serial_number (uCtlr, pszBuffer, uRes);
   }
   return (uRes);
} /* kcapi_get_serial_number */





/**
 * Get the profile of a controller or the number of controllers.
 *
 * @param uCtlr                 I: Controller number in question. If 0 is
 *                                 specified, only the total number of installed
 *                                 controllers is returned in the profile
 *                                 buffer.
 * @param pProfile              O: This buffer receives the profile data if the
 *                                 result is CAPI_OK.
 *
 * @retval CAPI_OK              The profile buffer was successfully filled (even
 *                              if only with the number of installed
 *                              controllers).
 * @retval Else                 An error occurred, the result is one of the CAPI
 *                              result values defined in capi_result.h
 */

unsigned kcapi_get_profile
   (unsigned             uCtlr,
    CAPIProfileBuffer_t *pProfile)
{
   CapiManCtlrInfo_t *pCtlrInfo;
   unsigned           uRes;
   
   /* if only the number of controllers is requested: return just this */
   if (uCtlr == 0)
   {
      unsigned u = atomic_load_acq_int (&e_uNumCtlrs);
      
      C_PUT_WORD (pProfile->wCtlr, u);
      
      uRes = CAPI_OK;
      if (e_uNumTracers > 0)
      {
         kcapitrace_appl_get_profile (uCtlr, pProfile, uRes);
      }
      return (uRes);
   }
   
   mtx_lock (&g_mtxCtlrData);
   
   /* check for registered controller */
   if (uCtlr >= ARRAY_COUNT (g_aCtlrInfo) ||
       ! g_aCtlrInfo [uCtlr].fCtlrRegistered)
   {
      mtx_unlock (&g_mtxCtlrData);
      DBG (LOG_ERROR, "Controller %u not registered", uCtlr);
      uRes = CRE_CAPI_NOT_INSTALLED;
      kcapitrace_appl_get_profile (uCtlr, pProfile, uRes);
      return (uRes);
   }
   pCtlrInfo = &(g_aCtlrInfo [uCtlr]);
   
   if (pProfile)
   {
      *pProfile = pCtlrInfo->pRegParams->profile;
   }

   mtx_unlock (&g_mtxCtlrData);

   uRes = CAPI_OK;
   if (e_uNumTracers > 0)
   {
      kcapitrace_appl_get_profile (uCtlr, pProfile, uRes);
   }
   return (uRes);
} /* kcapi_get_profile */





/**
 * Get additional info about the driver of a controller.
 *
 * @param uCtlr                 I: Controller number in question. Note that here
 *                                 controller number 0 is not allowed.
 * @param pDrvInfo              O: This buffer receives the driver data if the
 *                                 result is CAPI_OK.
 *
 * @retval CAPI_OK              The driver data buffer was successfully filled.
 * @retval Else                 An error occurred, the result is one of the CAPI
 *                              result values defined in capi_result.h
 */

unsigned kcapi_get_ctlr_driver_info
   (unsigned              uCtlr,
    CAPICtlrDriverInfo_t *pDrvInfo)
{
   CapiManCtlrInfo_t *pCtlrInfo;
   unsigned           uRes;
   
   mtx_lock (&g_mtxCtlrData);
   
   /* check for registered controller */
   if (uCtlr <= 0 || uCtlr >= ARRAY_COUNT (g_aCtlrInfo) ||
       ! g_aCtlrInfo [uCtlr].fCtlrRegistered)
   {
      mtx_unlock (&g_mtxCtlrData);
      DBG (LOG_ERROR, "Controller %u not registered", uCtlr);
      uRes = CRE_CAPI_NOT_INSTALLED;
      kcapitrace_appl_get_ctlr_driver_info (uCtlr, pDrvInfo, uRes);
      return (uRes);
   }
   pCtlrInfo = &(g_aCtlrInfo [uCtlr]);
   
   if (pDrvInfo)
   {
      bzero (pDrvInfo, sizeof (*pDrvInfo));
      C_PUT_WORD (pDrvInfo->wCtlr, uCtlr);
      strncpy (pDrvInfo->szCtlrName, pCtlrInfo->pRegParams->pszCtlrName,
               sizeof (pDrvInfo->szCtlrName) - 1);
      strncpy (pDrvInfo->szCtlrTypeName, pCtlrInfo->pRegParams->pszCtlrTypeName,
               sizeof (pDrvInfo->szCtlrTypeName) - 1);
      strncpy (pDrvInfo->szDriverName, pCtlrInfo->pRegParams->pszDriverName,
               sizeof (pDrvInfo->szDriverName) - 1);
      C_PUT_WORD (pDrvInfo->wDrvUnitNum,
                  pCtlrInfo->pRegParams->uDrvUnitNumber);
      C_PUT_WORD (pDrvInfo->wNumPorts,
                  pCtlrInfo->pRegParams->uNumPorts);
      C_PUT_WORD (pDrvInfo->wPortIdx,
                  pCtlrInfo->pRegParams->uPortIdx);
   }

   mtx_unlock (&g_mtxCtlrData);

   uRes = CAPI_OK;
   if (e_uNumTracers > 0)
   {
      kcapitrace_appl_get_ctlr_driver_info (uCtlr, pDrvInfo, uRes);
   }
   return (CAPI_OK);
} /* kcapi_get_ctlr_driver_info */




    
/**
 * 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. 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.
 *
 * If the number of data blocks to send down to the controller is specified as
 * null, the desired operation is only to reset the controller to disabled
 * state. No download is performed. After such a call the board is not usable
 * any more until another call to this function will download the firmware
 * again. This behaviour may be used to physically disable active boards so they
 * will in no way handle any ISDN connections any more.
 *
 * @note This call is only allowed if the calling processes user id is 0 or the
 *       user is member of the group with id 0.
 *
 * @param uCtlr                 I: Controller number to reset.
 * @param nNumDataBlocks        I: The number of data blocks to be sent to the
 *                                 controller. It is the length of the array at
 *                                 paDataBlocks.
 * @param paDataBlocks          I: The array of data blocks to be sent to the
 *                                 controller.
 *
 * @retval CAPI_OK              The reset operation was successful.
 * @retval Else                 An error occurred, the result is one of the CAPI
 *                              result values defined in capi_result.h
 */

unsigned kcapi_reset_ctlr
   (unsigned             uCtlr,
    size_t               nNumDataBlocks,
    CAPICtlrDataBlock_t *paDataBlocks)
{
   unsigned long      ulTimeout;
   CapiManCtlrInfo_t *pCtlrInfo;
   unsigned           uRes;
   
   /* check for completed initialization */
   if (! g_fDoWorkLoop)
   {
      printf ("kcapimgr: %s: initialization failed\n", __FUNCTION__);
      uRes = CME_CAPI_NOT_INSTALLED;
      kcapitrace_appl_reset_ctlr (uCtlr, nNumDataBlocks, paDataBlocks, uRes);
      return (uRes);
   }
   
   mtx_lock (&g_mtxCtlrData);
   
   /* loop until the controller is ready for this operation (the number of
    * active calls is null)
    */
   ulTimeout = 0;
   do
   {
      /* check for registered controller */
      if (uCtlr <= 0 || uCtlr >= ARRAY_COUNT (g_aCtlrInfo) ||
          ! g_aCtlrInfo [uCtlr].fCtlrRegistered)
      {
         mtx_unlock (&g_mtxCtlrData);
         DBG (LOG_ERROR, "Controller %u not registered", uCtlr);
         uRes = CRE_CAPI_NOT_INSTALLED;
         kcapitrace_appl_reset_ctlr (uCtlr, nNumDataBlocks, paDataBlocks, uRes);
         return (uRes);
      }
      
      /* if there are currently driver calls active to the controller, we must
       * wait until they are finished
       */
      if (g_aCtlrInfo [uCtlr].uNumActiveCalls > 0)
      {
         msleep (&(g_aCtlrInfo [uCtlr]), &g_mtxCtlrData, PZERO,
                 WAIT_MSG_NO_CALLS, SLEEP_VALUE_NO_CALLS);
         ulTimeout += SLEEP_VALUE_NO_CALLS;
      }
      
   } while (g_aCtlrInfo [uCtlr].uNumActiveCalls > 0 &&
            ulTimeout <= TIMEOUT_NO_CALLS);
   if (! g_aCtlrInfo [uCtlr].fCtlrRegistered)
   {
      mtx_unlock (&g_mtxCtlrData);
      DBG (LOG_ERROR, "Controller %u not registered", uCtlr);
      uRes = CRE_CAPI_NOT_INSTALLED;
      kcapitrace_appl_reset_ctlr (uCtlr, nNumDataBlocks, paDataBlocks, uRes);
      return (uRes);
   }
   if (g_aCtlrInfo [uCtlr].uNumActiveCalls > 0)
   {
      mtx_unlock (&g_mtxCtlrData);
      DBG (LOG_ERROR, "Controller %u busy with active calls for %lu ms",
           uCtlr, (unsigned long) (TIMEOUT_NO_CALLS * 1000 / hz));
      uRes = CRE_BUSY;
      kcapitrace_appl_reset_ctlr (uCtlr, nNumDataBlocks, paDataBlocks, uRes);
      return (uRes);
   }
   
   pCtlrInfo = &(g_aCtlrInfo [uCtlr]);
   
   /* check for support of the reset operation */
   if (! pCtlrInfo->pRegParams->pfnResetCtlr)
   {
      mtx_unlock (&g_mtxCtlrData);
      DBG (LOG_INFO, "WARNING: Reset operation not supported by controller %u",
           uCtlr);
      uRes = CRE_RESET_NOT_SUPPORTED;
      kcapitrace_appl_reset_ctlr (uCtlr, nNumDataBlocks, paDataBlocks, uRes);
      return (uRes);
   }
   
   /* check for reset operation allowed on this port of the board */
   if ((pCtlrInfo->pRegParams->ulFlags &
        CAPI_CTLRFLAG_RESET_FIRST_PORT) != 0 &&
       pCtlrInfo->pRegParams->uPortIdx > 0)
   {
      DBG (LOG_INFO,
           "WARNING: Reset operation only supported on first port of board %s, controller %u is port no. %u",
           pCtlrInfo->pRegParams->pszCtlrName, uCtlr, pCtlrInfo->pRegParams->uPortIdx);
      mtx_unlock (&g_mtxCtlrData);
      uRes = CRE_RESET_NOT_SUPPORTED;
      kcapitrace_appl_reset_ctlr (uCtlr, nNumDataBlocks, paDataBlocks, uRes);
      return (uRes);
   }
   
   /* check if operation is allowed by owner of the current process / thread */
   if (! curthread ||
       curthread->td_ucred == NOCRED || curthread->td_ucred == FSCRED ||
       suser (curthread) != 0)
   {
      mtx_unlock (&g_mtxCtlrData);
      DBG (LOG_ERROR, "ERROR: Root permission required to reset controller %u",
           uCtlr);
      uRes = CRE_NO_RIGHTS_TO_RESET;
      kcapitrace_appl_reset_ctlr (uCtlr, nNumDataBlocks, paDataBlocks, uRes);
      return (uRes);
   }

   /* So the reset operation is allowed by the calling user and on the
    * addressed controller. To not let any other thread perform another reset
    * operation concurrently and to block other operations on the controller it
    * must be disabled and we must declare our reset call as active. The disable
    * operation must also be performed for the possibly existing other ports of
    * the same board which is done in the following loop.
    */
   pCtlrInfo->uNumActiveCalls++;
   
   /* the controller must be disabled before resetting */
   if (pCtlrInfo->pRegParams->uNumPorts > 0 &&
       (pCtlrInfo->pRegParams->ulFlags & CAPI_CTLRFLAG_RESET_FIRST_PORT) != 0)
   {
      int      i;
      size_t   n;
      unsigned uRes2;
      
      /* reset operation only allowed at the first port of a multi-port board
       * --> disable all ports of the board, assuming all ports are registered
       * in increasing order
       */

      /* Note: The controller mutex will be checked for recursion by the called
       *       function.
       */
      uRes = kcapi_ctlr_disable (uCtlr);
      
      i = 1;
      n = 1;
      while (uCtlr + i < ARRAY_COUNT (g_aCtlrInfo) &&
             n < pCtlrInfo->pRegParams->uNumPorts)
      {
         if (strcmp (pCtlrInfo->pRegParams->pszDriverName,
                     g_aCtlrInfo [uCtlr + i].pRegParams->pszDriverName) == 0)
         {
            uRes2 = kcapi_ctlr_disable (uCtlr + i);
            if (uRes2 == CAPI_OK)
            {
               /* wait until all active calls to the current sub-controller are
                * finished
                */
               ulTimeout = 0;
               while (g_aCtlrInfo [uCtlr + i].uNumActiveCalls > 0 &&
                      ulTimeout <= TIMEOUT_NO_CALLS);
               {
                  msleep (&(g_aCtlrInfo [uCtlr + i]), &g_mtxCtlrData, PZERO,
                          WAIT_MSG_NO_CALLS, SLEEP_VALUE_NO_CALLS);
                  ulTimeout += SLEEP_VALUE_NO_CALLS;
               }
               if (g_aCtlrInfo [uCtlr + i].uNumActiveCalls > 0)
               {
                  DBG (LOG_ERROR, "Controller %u (main port is controller %u) busy with active calls for %lu ms",
                       uCtlr + i, uCtlr,
                       (unsigned long) (TIMEOUT_NO_CALLS * 1000 / hz));
                  uRes2 = CRE_BUSY;
               }
            }
            if (uRes == CAPI_OK)
            {
               uRes = uRes2;
               
            }
            ++n;
         }
         ++i;
      }
   }
   else
   {
      /* Note: The controller mutex will be checked for recursion by the called
       *       function.
       */
      uRes = kcapi_ctlr_disable (uCtlr);
   }
   if (uRes != CAPI_OK)
   {
      pCtlrInfo->uNumActiveCalls--;
      mtx_unlock (&g_mtxCtlrData);
      kcapitrace_appl_reset_ctlr (uCtlr, nNumDataBlocks, paDataBlocks, uRes);
      return (uRes);
   }
   
   /* now finally perform the reset operation */
   mtx_unlock (&g_mtxCtlrData);
   uRes = pCtlrInfo->pRegParams->pfnResetCtlr
             (uCtlr, nNumDataBlocks, paDataBlocks);

   mtx_lock (&g_mtxCtlrData);
   pCtlrInfo->uNumActiveCalls--;
   mtx_unlock (&g_mtxCtlrData);
   
   DBG (LOG_TRACE, "Controller %u reset, result=0x%04X", uCtlr, uRes);
   
   if (e_uNumTracers > 0)
   {
      kcapitrace_drvr_reset_ctlr (uCtlr, nNumDataBlocks, paDataBlocks, uRes);
      kcapitrace_appl_reset_ctlr (uCtlr, nNumDataBlocks, paDataBlocks, uRes);
   }
   return (uRes);
} /* kcapi_reset_ctlr */





/* === implementation of CAPI driver interface functions ================= */





/**
 * Register a new controller at the CAPI manager.
 *
 * @param pCtlrRegParms         I: Registration data for the controller. The
 *                                 memory must be valid until
 *                                 kcapi_ctlr_release() was called. The content
 *                                 may only change until kcapi_ctlr_enable() is
 *                                 called (or between calls to
 *                                 kcapi_ctlr_disable() and kcapi_ctlr_enable()
 *                                 in this order).
 * @param puUniqueCtlrNum       O: The unique controller number (1 based)
 *                                 assigned by the CAPI manager. If controller
 *                                 number mapping is needed, this fact must be
 *                                 declared through the parameters of the
 *                                 function call to kcapi_ctlr_enable().
 *
 * @retval CAPI_OK              Controller registration was successful, a unique
 *                              controller id was assigned.
 * @retval Else                 An error occurred, the result is one of the CAPI
 *                              result values defined in capi_result.h
 */

unsigned kcapi_ctlr_register
   (CAPICtlrRegisterParams_t *pCtlrRegParams,
    unsigned                 *puUniqueCtlrNum)
{
   CapiManCtlrInfo_t *pCtlrInfo;
   int                iCtlrIdx;
   
   /* check for completed initialization */
   if (! g_fDoWorkLoop)
   {
      printf ("kcapimgr: %s: initialization failed\n", __FUNCTION__);
      return (CME_CAPI_NOT_INSTALLED);
   }
   
   /* check for valid parameters */
   /* Note: The reset controller and notify functions are optional. */
   if (! pCtlrRegParams)
   {
      DBG (LOG_ERROR, "No register parameters specified (%p)",
           pCtlrRegParams);
      return (CRE_INVALID_PARAM);
   }
   if (pCtlrRegParams->uIfVersion != CAPI_DRIVER_IF_VERSION)
   {
      DBG (LOG_ERROR, "Unsupported driver version %u, %u expected",
           pCtlrRegParams->uIfVersion, CAPI_DRIVER_IF_VERSION);
      return (CRE_UNSUPPORTED_VERSION);
   }
   if (! pCtlrRegParams->pszCtlrName ||
       ! pCtlrRegParams->pszCtlrTypeName ||
       ! pCtlrRegParams->pszDriverName ||
       ! pCtlrRegParams->pszManufacturer ||
       ! pCtlrRegParams->pfnRegister ||
       ! pCtlrRegParams->pfnRelease ||
       ! pCtlrRegParams->pfnPutMessage ||
       ! puUniqueCtlrNum)
   {
      DBG (LOG_ERROR,
           "Invalid string or function pointer in register parameters");
      return (CRE_INVALID_PARAM);
   }
   
   /* the following operations must not be disturbed by other register
    * operations or by controller release calls
    */
   mtx_lock (&g_mtxCtlrData);
   
   /* get next free unique controller number, starting at 1 */
   for (iCtlrIdx = 1; (size_t) iCtlrIdx < ARRAY_COUNT (g_aCtlrInfo); ++iCtlrIdx)
   {
      if (! g_aCtlrInfo [iCtlrIdx].fCtlrRegistered)
      {
         break;
      }
   }
   if ((size_t) iCtlrIdx >= ARRAY_COUNT (g_aCtlrInfo))
   {
      mtx_unlock (&g_mtxCtlrData);
      DBG (LOG_ERROR, "Too many controllers registered (%zu)",
           ARRAY_COUNT (g_aCtlrInfo) - 1);
      return (CRE_TOO_MANY_CONTROLLERS);
   }
   
   /* initialize and fill the controller entry */
   pCtlrInfo = &(g_aCtlrInfo [iCtlrIdx]);
   bzero (pCtlrInfo, sizeof (*pCtlrInfo));
   pCtlrInfo->uUniqueCtlrNum     = (unsigned) iCtlrIdx;
   pCtlrInfo->uMultiPortMainCtlr = (unsigned) iCtlrIdx;
   pCtlrInfo->uDrvCtlrNum        = (unsigned) iCtlrIdx;
   pCtlrInfo->pRegParams         = pCtlrRegParams;
   
   /* if the controller is part of a multi-port card, find the main (first)
    * controller of the card and save it
    */
   if (pCtlrInfo->pRegParams->uNumPorts > 1)
   {
      int i;
      
      /* search through all controllers back (including the one currently
       * registered, it may be the main port)
       */
      for (i = iCtlrIdx; i >= 0; --i)
      {
         /* the main (first) controller of the current board is the one with the
          * same driver name, the same driver unit number and the port index
          * zero (i.e. the first port of the board)
          */
         if (g_aCtlrInfo [i].fCtlrRegistered &&
             g_aCtlrInfo [i].pRegParams &&
             strcmp (g_aCtlrInfo [i].pRegParams->pszDriverName,
                     pCtlrInfo->pRegParams->pszDriverName) == 0 &&
             g_aCtlrInfo [i].pRegParams->uDrvUnitNumber ==
                pCtlrInfo->pRegParams->uDrvUnitNumber &&
             g_aCtlrInfo [i].pRegParams->uPortIdx == 0)
         {
            break;
         }
      }
      if (i >= 0)
      {
         pCtlrInfo->uMultiPortMainCtlr = (unsigned) i;
      }
      else
      {
         /* Not found? Must be an error in the driver controller data. We will
          * simply assume it is the first port of its board.
          */
         pCtlrInfo->uMultiPortMainCtlr = (unsigned) iCtlrIdx;
      }
   }
   /* else simply use its own controller number */
   
   /* now finally mark the array slot as occupied */
   pCtlrInfo->fCtlrRegistered = 1;
   pCtlrInfo->fCtlrEnabled    = 0;

   /* count the registered controllers */
   atomic_add_rel_int (&e_uNumCtlrs, 1);
   
   /* return the controller number to the driver */
   *puUniqueCtlrNum = pCtlrInfo->uUniqueCtlrNum;
   
   DBG (LOG_INFO, "Controller \"%s\" registered with no. %u",
        pCtlrInfo->pRegParams->pszCtlrName, *puUniqueCtlrNum);

   mtx_unlock (&g_mtxCtlrData);
   
   return (CAPI_OK);
} /* kcapi_ctlr_register */





/**
 * Declare a registered controller as ready for operation.
 *
 * @param uUniqueCtlrNum        I: Unique controller number from registration.
 * @param uDrvCtlrNum           I: If the controller does not need controller
 *                                 number mapping, this must be the same as the
 *                                 unique controller number. Else this value
 *                                 must be set to the driver specific controller
 *                                 number. The CAPI manager will map between the
 *                                 two controller numbers in every CAPI message
 *                                 exchanged between the controller and the CAPI
 *                                 manager in this case.
 *
 * @retval CAPI_OK              Enabling the controller was successful.
 * @retval Else                 An error occurred, the result is one of the CAPI
 *                              result values defined in capi_result.h
 */

unsigned kcapi_ctlr_enable
   (unsigned uUniqueCtlrNum,
    unsigned uDrvCtlrNum)
{
   CapiManCtlrInfo_t *pCtlrInfo;
   int                fRecursive;
   
   /* check for completed initialization */
   if (! g_fDoWorkLoop)
   {
      printf ("kcapimgr: %s: Initialization failed\n", __FUNCTION__);
      return (CME_CAPI_NOT_INSTALLED);
   }
   
   if (mtx_owned (&g_mtxCtlrData))
   {
      fRecursive = 1;
   }
   else
   {
      mtx_lock (&g_mtxCtlrData);
      fRecursive = 0;
   }
   
   /* check for registered controller */
   if (uUniqueCtlrNum <= 0 || uUniqueCtlrNum >= ARRAY_COUNT (g_aCtlrInfo) ||
       ! g_aCtlrInfo [uUniqueCtlrNum].fCtlrRegistered ||
       uDrvCtlrNum > CAPI_CIDMASK_CTLR)
   {
      if (! fRecursive)
      {
         mtx_unlock (&g_mtxCtlrData);
      }
      DBG (LOG_ERROR,
           "Controller %u not registered or driver controller no. %u invalid",
           uUniqueCtlrNum, uDrvCtlrNum);
      return (CRE_INVALID_CONTROLLER);
   }
   pCtlrInfo = &(g_aCtlrInfo [uUniqueCtlrNum]);
   
   pCtlrInfo->uDrvCtlrNum  = uDrvCtlrNum;
   pCtlrInfo->fCtlrEnabled = 1;
   
   if (! fRecursive)
   {
      mtx_unlock (&g_mtxCtlrData);
   }
   
   DBG (LOG_INFO,
        "Controller %u enabled with driver controller no. %u",
        uUniqueCtlrNum, uDrvCtlrNum);
        
   return (CAPI_OK);
} /* kcapi_ctlr_enable */





/**
 * Declare a registered controller as disabled for operation.
 *
 * @param uUniqueCtlrNum        I: Unique controller number from registration.
 *
 * @retval CAPI_OK              Disabling the controller was successful.
 * @retval Else                 An error occurred, the result is one of the CAPI
 *                              result values defined in capi_result.h
 */

unsigned kcapi_ctlr_disable
   (unsigned uUniqueCtlrNum)
{
   CapiManCtlrInfo_t *pCtlrInfo;
   int                fRecursive;
   int                i;
   
   if (mtx_owned (&g_mtxCtlrData))
   {
      fRecursive = 1;
   }
   else
   {
      mtx_lock (&g_mtxCtlrData);
      fRecursive = 0;
   }
   
   /* check for registered controller */
   if (uUniqueCtlrNum <= 0 || uUniqueCtlrNum >= ARRAY_COUNT (g_aCtlrInfo) ||
       ! g_aCtlrInfo [uUniqueCtlrNum].fCtlrRegistered)
   {
      if (! fRecursive)
      {
         mtx_unlock (&g_mtxCtlrData);
      }
      DBG (LOG_ERROR, "Controller %u not registered", uUniqueCtlrNum);
      return (CRE_INVALID_CONTROLLER);
   }
   pCtlrInfo = &(g_aCtlrInfo [uUniqueCtlrNum]);
   
   /* if the controller is still/already disabled, nothing has to be done */
   if (! pCtlrInfo->fCtlrEnabled)
   {
      if (! fRecursive)
      {
         mtx_unlock (&g_mtxCtlrData);
      }
      DBG (LOG_TRACE, "Disable controller %u: Already disabled",
           uUniqueCtlrNum);
      return (CAPI_OK);
   }
   
   /* the first task is to mark the controller as disabled */
   pCtlrInfo->fCtlrEnabled = 0;

   /* block a controller release call by the driver during the following call */
   pCtlrInfo->uNumActiveCalls++;
   
   /* for all applications with open physical connections we must send a
    * Disconnect-Indication to the application. The Disconnect-Response as a
    * reaction will fail with error, but there is no need to handle this in a
    * special manner.
    */
   /* Note: The controller mutex must be released when notifying the
    *       applications. As the application callback function will be called,
    *       the application may e.g. call kcapi_put_message() and thus finally
    *       reach kcapictlr_put_message(). This function will try to acquire the
    *       controller mutex, what would lead to a panic because of an
    *       unsupported recursion. But as the controller is already disabled,
    *       the call will simply return without an attempt to call into the
    *       driver.
    */
   mtx_unlock (&g_mtxCtlrData);
   kcapiappl_notify_ctlr_disable (uUniqueCtlrNum);
   mtx_lock (&g_mtxCtlrData);

   /* undo any application registration at the controller */
   for (i = 0; i < ARRAY_COUNT (pCtlrInfo->auAppMapManToCtlr); ++i)
   {
      if (pCtlrInfo->auAppMapManToCtlr [i] > 0 &&
          pCtlrInfo->auAppMapManToCtlr [i] <
             ARRAY_COUNT (pCtlrInfo->auAppMapManToCtlr))
      {
         printf ("kcapimgr: WARNING: Disabling controller %u with registered application id %u\n",
                 uUniqueCtlrNum, (unsigned) i);
         pCtlrInfo->auAppMapCtlrToMan [pCtlrInfo->auAppMapManToCtlr [i]] = 0;
         pCtlrInfo->auAppMapManToCtlr [i] = 0;
      }
   }
   
   pCtlrInfo->uNumActiveCalls--;
   if (! fRecursive)
   {
      mtx_unlock (&g_mtxCtlrData);
   }
   
   DBG (LOG_INFO, "Controller %u disabled", uUniqueCtlrNum);
   
   return (CAPI_OK);
} /* kcapi_ctlr_disable */
   




/**
 * Releasing a CAPI controller.
 *
 * @param uUniqueCtlrNum        I: Unique controller number to be released.
 *                                 After this call no message exchange or
 *                                 function calls may occur between this
 *                                 controller and the CAPI manager.
 *
 * @retval CAPI_OK              Releasing the controller was successful.
 * @retval Else                 An error occurred, the result is one of the CAPI
 *                              result values defined in capi_result.h
 */

unsigned kcapi_ctlr_release
   (unsigned uUniqueCtlrNum)
{
   CapiManCtlrInfo_t *pCtlrInfo;
   unsigned long      ulTimeout;
   unsigned           uRes;
   
   /* first disable the controller */
   uRes = kcapi_ctlr_disable (uUniqueCtlrNum);
   if (uRes != CAPI_OK)
   {
      return (uRes);
   }
      
   /* Note: The check for a valid controller number was done in
    *       kcapi_ctlr_disable(). So we are sure the controller is registered.
    *       As each controller is itself responsible for en-/disabling and
    *       registering, we need not worry about another call between disabling
    *       and the following operations.
    */
   mtx_lock (&g_mtxCtlrData);
   
   /* we need exclusive controller access to execute the following code */
   pCtlrInfo = &(g_aCtlrInfo [uUniqueCtlrNum]);
   
   /* wait until all active calls are finished */
   ulTimeout = 0;
   while (pCtlrInfo->uNumActiveCalls > 0 &&
          ulTimeout <= TIMEOUT_NO_CALLS)
   {
      msleep (pCtlrInfo, &g_mtxCtlrData, PZERO,
              WAIT_MSG_NO_CALLS, SLEEP_VALUE_NO_CALLS);
   }
   if (pCtlrInfo->uNumActiveCalls > 0)
   {
      DBG (LOG_ERROR,
           "Still %u driver calls active when releasing controller %u",
           pCtlrInfo->uNumActiveCalls, uUniqueCtlrNum);
      /* even if not all active calls could be finished we must proceed to not
       * cause a deadlock
       */
   }
   
   /* Now release the array entry for the controller. We only need to mark the
    * array entry as not occupied. All other data like the application id
    * mapping array was alread set during the disable operation.
    */
   pCtlrInfo->fCtlrRegistered = 0;

   /* count the release of the controller */
   /* Note: The disable operation would have failed if the controller was not
    *       registered. So it is safe to decrement the counter.
    */
   atomic_subtract_int (&e_uNumCtlrs, 1);
   
   mtx_unlock (&g_mtxCtlrData);
   
   DBG (LOG_INFO, "Controller %u released", uUniqueCtlrNum);
   
   return (CAPI_OK);
} /* kcapi_ctlr_release */





/**
 * Receive a CAPI message from a controller.
 *
 * @note Lost CAPI messages may be signalled with an empty mbuf, i.e. an mbuf
 *       with the member m_len set to zero.
 *
 * @important This function must be called with C4B privilege level or even
 *            interrupt context.
 *
 * @param uUniqueCtlrNum        I: The unique controller number this message is
 *                                 from.
 * @param uCtlrApplID           I: The controller specific application id from
 *                                 application registration.
 * @param pmbMsg                I: A pointer to an mbuf for the incoming CAPI
 *                                 message. A Data-B3-Indication contains the
 *                                 address of another mbuf in the pmbMsg->m_next
 *                                 member. The CAPI manager is responsible for
 *                                 releasing the mbuf(s) when they are no longer
 *                                 needed.
 *
 * @return Nothing.
 */

void kcapi_ctlr_receive_capi_message
   (unsigned     uUniqueCtlrNum,
    unsigned     uCtlrApplID,
    struct mbuf *pmbMsg)
{
   /* ensure the mbuf is allocated by kcapi_get_mbuf() */
   if (pmbMsg->m_type != MT_DATA)
   {
      printf ("kcapimgr: ERROR: Got CAPI message to enqueue with invalid mbuf type %d, expected %d\n",
              (int) (pmbMsg->m_type), (int) MT_DATA);
      return;
   }
   
   /* The received message is just stored into the queue for incoming messages.
    * The unique controller number is stored into the csum_data field of the
    * mbuf packet header and the application id into csum_flags. These fields
    * are not used, as the mbuf of the CAPI message itself will never be used
    * in a network interface routine. These two parameters are needed for later
    * message handling, because the mbuf may be empty to signal a lost message.
    * So the controller number and the application id may not be re-read from
    * the CAPI message.
    */
   pmbMsg->m_pkthdr.csum_data = (int) uUniqueCtlrNum;
   pmbMsg->m_pkthdr.csum_flags = (int) uCtlrApplID;
   IF_ENQUEUE_MTX (&g_queueCtlrMsg, pmbMsg);
   
   /* now wakeup the possibly waiting CAPI manager thread for handling incoming
    * messages
    */
   sema_post (&g_semCtlrMsg);
    
} /* kcapi_ctlr_receive_capi_message */





/**
 * Receive a message for passing it throuch protocol stacks from a controller.
 *
 * This function is used by a CAPI driver to pass a notification message to
 * itself. It is usually called in interrupt context, e.g. when a controller
 * signals the reception or transmission of data. The message is stored into the
 * CAPI manager message queue for driver messages. The message thread then
 * passes the message to the driver for handling out of interrupt context. This
 * is used to keep the code executed in interrupt context as short as possible.
 *
 * Notification messages are passed to the driver through the driver provided
 * function of the prototype (*CAPIDrv_PutNotifyMsgFct_t)(). If the driver wants
 * to use notification messages, it must set the corresponding member of the
 * controller registration structure to a valid function address. Otherwise
 * notification messages will be discarded by the CAPI manager because it does
 * not know how to deliver them to the driver.
 *
 * @attention The mbuf for this message must be allocated using
 *            kcapi_get_ctlr_notify_mbuf(). If regular data is to be transfered,
 *            it shall be stored into an additional mbuf allocated with
 *            kcapi_get_mbuf() and attached to the main notification mbuf,
 *            preferably using the member m_next. As the notification mbuf is
 *            not of type MT_DATA, it must not be passed to network layers as
 *            e.g. i4b.
 *
 * @param uUniqueCtlrNum        I: The unique controller number for the
 *                                 controller needing work.
 * @param pmbMsg                I: The address of an mbuf with the message to
 *                                 pass to a handling function of a CAPI driver.
 *                                 After passing the mbuf to this function the
 *                                 CAPI manager is responsible for releasing it
 *                                 until the CAPI manager forwarded it to the
 *                                 handling function of the driver.
 *
 * @return Nothing.
 */

void kcapi_ctlr_receive_notify_message
   (unsigned     uUniqueCtlrNum,
    struct mbuf *pmbMsg)
{
   /* ensure the mbuf is allocated by kcapi_get_ctlr_notify_mbuf() */
   if (pmbMsg->m_type != MT_CONTROL)
   {
      printf ("kcapimgr: ERROR: Got notification message to enqueue with invalid mbuf type %d, expected %d\n",
              (int) (pmbMsg->m_type), (int) MT_CONTROL);
      return;
   }
   
   /* The received message is just stored into the queue for incoming messages.
    * The unique controller number is stored into the csum_data field of the
    * mbuf packet header. This field is not used, as the mbuf of the
    * notification message itself will never be used in a network interface
    * routine. This parameter is needed for later message handling to identify
    * the addressed controller. As the content of the mbuf is not known to the
    * CAPI manager, there is no other way to identify the controller.
    */
   pmbMsg->m_pkthdr.csum_data = (int) uUniqueCtlrNum;
   IF_ENQUEUE_MTX (&g_queueCtlrMsg, pmbMsg);
   
   /* now wakeup the possibly waiting CAPI manager thread for handling incoming
    * messages
    */
   sema_post (&g_semCtlrMsg);
    
} /* kcapi_ctlr_receive_notify_message */





/**
 * Allocate a new mbuf for a controller notification message.
 *
 * @note Mbufs allocated with this function should be released by calling
 *       kcapi_free_mbuf().
 *
 * @attention Mbufs of this type must not be used for normal data! If a data
 *            block is to be transfered through a notification message, the data
 *            mbuf must be attached as a second mbuf to the notification mbuf,
 *            preferably using the member m_next that is released automatically
 *            when releasing the main mbuf.
 *
 * @param nLen                  I: The size of the new mbuf in bytes. If an mbuf
 *                                 is returned by this function, its m_len
 *                                 member is already set to this value.
 *
 * @return NULL                 Unable to allocate an mbuf, out of mbufs.
 * @return Else                 A pointer to the allocated mbuf.
 */

struct mbuf *kcapi_get_ctlr_notify_mbuf
   (size_t nLen)
{
   struct mbuf *pmb;
   
   /* check if length is exceeding maximum length possible (2KB) */
   if (nLen > MCLBYTES)
   {
      return (NULL);
   }
   
   /* allocate mbuf */
   MGETHDR (pmb, M_DONTWAIT, MT_CONTROL);
   if (pmb && nLen > MHLEN)
   {
      MCLGET (pmb, M_DONTWAIT);
      if ((pmb->m_flags & M_EXT) == 0)
      {
         kcapi_free_mbuf (pmb);
         pmb = NULL;
      }
   }
   
   if (pmb)
   {
      pmb->m_len = pmb->m_pkthdr.len = nLen;
   }
   
   return (pmb);
} /* kcapi_get_ctlr_notify_mbuf */





/* === implementation of public functions ================================ */





/**
 * Initialize the controller handling module.
 */

void kcapictlr_init (void)
{
   struct proc *pNewProc;
   int          iRes;
   
   /* initialize the controller data array */
   e_uNumCtlrs = 0;
   bzero (g_aCtlrInfo, sizeof (g_aCtlrInfo));
   
   /* initialize the mutex for exclusive controller array access */
   mtx_init (&g_mtxCtlrData, "kcapictlr-ctlr-data", NULL, MTX_DEF);
   
   /* initialize the semaphore to signal a new message in the controller
    * message queue
    */
   sema_init (&g_semCtlrMsg, 0, "kcapictlr-msg-queue");
   
   /* initialize the semaphore to wait for termination of the message handler
    * thread
    */
   sema_init (&g_semThreadTerm, 0, "kcapictlr-thread-terminate");

   /* initialize the queue for messages from the controllers */
   IF_QINIT (&g_queueCtlrMsg, "kcapimgr-ctlr-msg", 0, 0);
   
   /* start the work thread to handle incoming message from the controllers */
   g_fDoWorkLoop = 1;
   pNewProc = NULL;
   iRes = kthread_create (kcapictlr_message_handler, NULL, &pNewProc,
                          0, 0, "kcapimgr-message-handler");
   if (iRes == 0)
   {
      DBG (LOG_TRACE, "Handler for incoming messages started");
   }
   else
   {
      g_fDoWorkLoop = 0;
      printf ("kcapimgr: ERROR %d starting message handler kernel thread\n",
              iRes);
   }
   
} /* kcapictlr_init */





/**
 * Close the controller handling module.
 */

void kcapictlr_close (void)
{
   struct mbuf *pmb;
   int          i;
   
   /* stop the work thread for handling incoming messages */
   if (g_fDoWorkLoop)
   {
      g_fDoWorkLoop = 0;
      sema_post (&g_semCtlrMsg);
      i = sema_timedwait (&g_semThreadTerm, 5 * hz);
      if (i != 0)
      {
         DBG (LOG_ERROR,
              "ERROR: Unable to terminate message handler thread, waiting error: %d",
              i);
      }
   }
   
   /* clean out the queue for incoming controller messages */
   do
   {
      IF_DEQUEUE_MTX (&g_queueCtlrMsg, pmb);
      if (pmb)
      {
         kcapi_free_mbuf (pmb);
      }
   } while (pmb);
   IF_QDESTROY (&g_queueCtlrMsg);
   
   /* mark all controller entries as not registered */
   mtx_lock (&g_mtxCtlrData);
   for (i = 0; (size_t) i < ARRAY_COUNT (g_aCtlrInfo); i++)
   {
      if (g_aCtlrInfo [i].fCtlrRegistered)
      {
         g_aCtlrInfo [i].fCtlrEnabled    = 0;
         g_aCtlrInfo [i].fCtlrRegistered = 0;
      }
   }
   atomic_store_rel_int (&e_uNumCtlrs, 0);
   mtx_destroy (&g_mtxCtlrData);
   
   sema_destroy (&g_semThreadTerm);
   sema_destroy (&g_semCtlrMsg);
   
} /* kcapictlr_close */





/**
 * Perform application registration at a controller.
 */

unsigned kcapictlr_register_app
   (unsigned           uApplID,
    unsigned           uUniqueCtlrNum,
    unsigned           uMaxLogicalConnections,
    unsigned           uMaxBDataBlocks,
    unsigned           uMaxBDataLen)
{
   CapiManCtlrInfo_t *pCtlrInfo;
   CapiManCtlrInfo_t *pRegCtlrInfo;
   unsigned           uDrvCtlrNum;
   unsigned           uDrvApplID;
   unsigned           uRegCapiCtlrNum;
   unsigned           uRegDrvCtlrNum;
   unsigned           uRes;
   
   mtx_lock (&g_mtxCtlrData);
   
   /* check for valid controller number */
   if (uUniqueCtlrNum >= ARRAY_COUNT (g_aCtlrInfo) ||
       ! g_aCtlrInfo [uUniqueCtlrNum].fCtlrRegistered)
   {
      mtx_unlock (&g_mtxCtlrData);
      DBG (LOG_ERROR, "Controller %u not registered", uUniqueCtlrNum);
      uRes = CRE_CAPI_NOT_INSTALLED;
      if (e_uNumTracers > 0)
      {
         kcapitrace_drvr_register (uUniqueCtlrNum, 0,
                                   uMaxLogicalConnections,
                                   uMaxBDataBlocks, uMaxBDataLen,
                                   uApplID, 0,
                                   uRes);
      }
      return (uRes);
   }
   pCtlrInfo = &(g_aCtlrInfo [uUniqueCtlrNum]);
   uDrvCtlrNum = pCtlrInfo->uDrvCtlrNum;
   if (! pCtlrInfo->fCtlrEnabled)
   {
      mtx_unlock (&g_mtxCtlrData);
      DBG (LOG_ERROR, "Controller %u not enabled", uUniqueCtlrNum);
      uRes = CRE_CAPI_NOT_INSTALLED;
      if (e_uNumTracers > 0)
      {
         kcapitrace_drvr_register (uUniqueCtlrNum, 0,
                                   uMaxLogicalConnections,
                                   uMaxBDataBlocks, uMaxBDataLen,
                                   uApplID, 0,
                                   uRes);
      }
      return (uRes);
   }
   
   /* if the controller is part of a multi-port board, the registration may need
    * to be performed at the main (first) port of the board. For this to be safe
    * we release access to the addressed controller and obtain access to the
    * controller entry for the main port.
    */
   if (pCtlrInfo->pRegParams->uNumPorts > 1 &&
       (pCtlrInfo->pRegParams->ulFlags &
        CAPI_CTLRFLAG_REGISTER_AT_FIRST_PORT) != 0 &&
       pCtlrInfo->uMultiPortMainCtlr < ARRAY_COUNT (g_aCtlrInfo) &&
       g_aCtlrInfo [pCtlrInfo->uMultiPortMainCtlr].fCtlrRegistered)
   {
      uRegCapiCtlrNum = pCtlrInfo->uMultiPortMainCtlr;
      uRegDrvCtlrNum = g_aCtlrInfo [pCtlrInfo->uMultiPortMainCtlr].uDrvCtlrNum;
   }
   else
   {
      uRegCapiCtlrNum = pCtlrInfo->uUniqueCtlrNum;
      uRegDrvCtlrNum = pCtlrInfo->uDrvCtlrNum;
   }
   pRegCtlrInfo = &(g_aCtlrInfo [uRegCapiCtlrNum]);
   if (pRegCtlrInfo != pCtlrInfo)
   {
      if (! pRegCtlrInfo->fCtlrEnabled)
      {
         mtx_unlock (&g_mtxCtlrData);
         DBG (LOG_ERROR,
              "Main port (controller %u) disabled for controller %u, unable to register application id %u",
              uRegCapiCtlrNum, uUniqueCtlrNum, uApplID);
         uRes = CRE_CAPI_NOT_INSTALLED;
         if (e_uNumTracers > 0)
         {
            kcapitrace_drvr_register (uUniqueCtlrNum, 0,
                                      uMaxLogicalConnections,
                                      uMaxBDataBlocks, uMaxBDataLen,
                                      uApplID, 0,
                                      uRes);
         }
         return (uRes);
      }
   }
    
   /* if the application is already registered at the just determined
    * controller, we only need to copy the application mapping
    */
   if (pRegCtlrInfo->auAppMapManToCtlr [uApplID] != 0)
   {
      uDrvApplID = pRegCtlrInfo->auAppMapManToCtlr [uApplID];
      
      pCtlrInfo->auAppMapManToCtlr [uApplID] = uDrvApplID;
      pCtlrInfo->auAppMapCtlrToMan [uDrvApplID] = uApplID;
      
      mtx_unlock (&g_mtxCtlrData);
      
      uRes = CAPI_OK;
      if (e_uNumTracers > 0)
      {
         kcapitrace_drvr_register (uUniqueCtlrNum, uDrvCtlrNum,
                                   uMaxLogicalConnections,
                                   uMaxBDataBlocks, uMaxBDataLen,
                                   uApplID, uDrvApplID,
                                   uRes);
      }
      return (uRes);
   }

   /* if the application is not yet registered at the controller, we must
    * perform a real registration
    */
   uDrvApplID = uApplID;
   pCtlrInfo->uNumActiveCalls++;
   if (pCtlrInfo != pRegCtlrInfo)
   {
      pRegCtlrInfo->uNumActiveCalls++;
   }
   mtx_unlock (&g_mtxCtlrData);
   uRes = pRegCtlrInfo->pRegParams->pfnRegister
             (uRegDrvCtlrNum,
              uMaxLogicalConnections,
              uMaxBDataBlocks,
              uMaxBDataLen,
              &uDrvApplID);
   mtx_lock (&g_mtxCtlrData);
   if (uRes != CAPI_OK)
   {
      pCtlrInfo->uNumActiveCalls--;
      if (pCtlrInfo != pRegCtlrInfo)
      {
         pRegCtlrInfo->uNumActiveCalls--;
      }
      mtx_unlock (&g_mtxCtlrData);
      DBG (LOG_ERROR,
	   "Appl. id %u: Error 0x%04X registering at controller %u (base is %u)",
	   uApplID, uRes, uUniqueCtlrNum, uRegCapiCtlrNum);
      if (e_uNumTracers > 0)
      {
         kcapitrace_drvr_register (uUniqueCtlrNum, uDrvCtlrNum,
                                   uMaxLogicalConnections,
                                   uMaxBDataBlocks, uMaxBDataLen,
                                   uApplID, uDrvApplID,
                                   uRes);
      }
      return (uRes);
   }
   
   /* registration successful, check for valid controller application id */
   if (uDrvApplID >= ARRAY_COUNT (pRegCtlrInfo->auAppMapCtlrToMan))
   {
      if (pRegCtlrInfo->fCtlrRegistered)
      {
         (void) pRegCtlrInfo->pRegParams->pfnRelease
                   (pRegCtlrInfo->uDrvCtlrNum, uDrvApplID);
      }
      pCtlrInfo->uNumActiveCalls--;
      if (pCtlrInfo != pRegCtlrInfo)
      {
         pRegCtlrInfo->uNumActiveCalls--;
      }
      mtx_unlock (&g_mtxCtlrData);
      printf ("kcapimgr: ERROR: Got invalid appl. id %u registering appl. with id %u at controller %u (base is %u)\n",
              uDrvApplID, uApplID, uUniqueCtlrNum, uRegCapiCtlrNum);
      uRes = CRE_CAPI_NOT_INSTALLED;
      if (e_uNumTracers > 0)
      {
         kcapitrace_drvr_register (uUniqueCtlrNum, uDrvCtlrNum,
                                   uMaxLogicalConnections,
                                   uMaxBDataBlocks, uMaxBDataLen,
                                   uApplID, uDrvApplID,
                                   uRes);
      }
      return (uRes);
   }
   
   /* set the application mapping for the controller itself and the base
    * controller if different
    */
   pRegCtlrInfo->auAppMapManToCtlr [uApplID] = uDrvApplID;
   pRegCtlrInfo->auAppMapCtlrToMan [uDrvApplID] = uApplID;
   if (pRegCtlrInfo != pCtlrInfo)
   {
      if (! pCtlrInfo->fCtlrEnabled)
      {
         /* The addressed controller was disabled during this function call, but
          * the registration at the main port controller succeeded. We could try
          * to undo the main registration. But this is not necessary. The main
          * port is likely to be disabled at this point (or very soon). So this
          * will likely fail. A cleanup will be performed when the CAPI
          * application calls kcapi_release(). As kcapi_register() was already
          * successfully called, an unsuccessful result of this function does
          * not mean, calling kcapi_release() is not necessary.
          */
         pCtlrInfo->uNumActiveCalls--;
         if (pCtlrInfo != pRegCtlrInfo)
         {
            pRegCtlrInfo->uNumActiveCalls--;
         }
         mtx_unlock (&g_mtxCtlrData);
         DBG (LOG_ERROR,
              "Controller %u disabled after registration at main port controller %u, unable to register application id %u",
              uUniqueCtlrNum, uRegCapiCtlrNum, uApplID);
         uRes = CRE_CAPI_NOT_INSTALLED;
         if (e_uNumTracers > 0)
         {
            kcapitrace_drvr_register (uUniqueCtlrNum, 0,
                                      uMaxLogicalConnections,
                                      uMaxBDataBlocks, uMaxBDataLen,
                                      uApplID, 0,
                                      uRes);
         }
         return (uRes);
      }
      
      pCtlrInfo->auAppMapManToCtlr [uApplID] = uDrvApplID;
      pCtlrInfo->auAppMapCtlrToMan [uDrvApplID] = uApplID;
   }
   
   pCtlrInfo->uNumActiveCalls--;
   if (pCtlrInfo != pRegCtlrInfo)
   {
      pRegCtlrInfo->uNumActiveCalls--;
   }
   mtx_unlock (&g_mtxCtlrData);
   
   uRes = CAPI_OK;
   if (e_uNumTracers > 0)
   {
      kcapitrace_drvr_register (uUniqueCtlrNum, uDrvCtlrNum,
                                uMaxLogicalConnections,
                                uMaxBDataBlocks, uMaxBDataLen,
                                uApplID, uDrvApplID,
                                uRes);
   }
   return (uRes);
} /* kcapictlr_register_app */





/**
 * Call release of every controller for an application.
 */

unsigned kcapictlr_release_app
   (unsigned uApplID)
{
   CapiManCtlrInfo_t *pCtlrInfo;
   int                i;
   unsigned           uUniqueCtlrNum;
   unsigned           uDrvCtlrNum;
   unsigned           uCtlrApplID;
   unsigned           uRes;
   unsigned           uRes2;
   
   /* traverse the array of CAPI controllers for all registered controllers */
   uRes = CAPI_OK;
   for (i = 0; i < ARRAY_COUNT (g_aCtlrInfo); i++)
   {
      mtx_lock (&g_mtxCtlrData);
      
      pCtlrInfo = &(g_aCtlrInfo [i]);
      if (! pCtlrInfo->fCtlrRegistered ||
          ! pCtlrInfo->fCtlrEnabled)
      {
         mtx_unlock (&g_mtxCtlrData);
         continue;
      }
      if (pCtlrInfo->auAppMapManToCtlr [uApplID] <= 0 ||
          pCtlrInfo->auAppMapManToCtlr [uApplID] >=
             ARRAY_COUNT (pCtlrInfo->auAppMapManToCtlr))
      {
         /* application not registered at this controller */
         mtx_unlock (&g_mtxCtlrData);
         continue;
      }
      uDrvCtlrNum    = pCtlrInfo->uDrvCtlrNum;
      uUniqueCtlrNum = pCtlrInfo->uUniqueCtlrNum;
      uCtlrApplID    = pCtlrInfo->auAppMapManToCtlr [uApplID];
      
      /* perform the release operation at this controller; this may need to be
       * performed at the base port of a multi-port board, i.e. release
       * operations at non-base ports should be skipped
       */
      /* Note: If an application is registered at the non-base port of a
       *       multi-port board, it is also registered at its base port. So we
       *       only need to skip non-base ports here. The release operation at
       *       the base port will always be executed before the other ports,
       *       because the main port is by definition the first port registered.
       */
      if (pCtlrInfo->pRegParams->uNumPorts <= 1 ||
          (pCtlrInfo->pRegParams->ulFlags &
           CAPI_CTLRFLAG_REGISTER_AT_FIRST_PORT) == 0 ||
          pCtlrInfo->pRegParams->uPortIdx <= 0 ||
          pCtlrInfo->uMultiPortMainCtlr >= ARRAY_COUNT (g_aCtlrInfo) ||
          ! g_aCtlrInfo [pCtlrInfo->uMultiPortMainCtlr].fCtlrRegistered)
      {
         pCtlrInfo->uNumActiveCalls++;
         mtx_unlock (&g_mtxCtlrData);
         
         uRes2 = pCtlrInfo->pRegParams->pfnRelease
                   (pCtlrInfo->uDrvCtlrNum, uCtlrApplID);
         mtx_lock (&g_mtxCtlrData);
         pCtlrInfo->uNumActiveCalls--;
         if (uRes2 == CAPI_OK)
         {
            DBG (LOG_TRACE, "Appl. id %u: Released for controller %u",
                 uApplID, uUniqueCtlrNum);
         }
         else
         {
            DBG (LOG_ERROR,
                 "Appl. id %u: Error 0x%04X releasing for controller %u",
                 uApplID, uRes2, uUniqueCtlrNum);
         }
         /* return the first error that occurs, if any */
         if (uRes == CAPI_OK)
         {
            uRes = uRes2;
         }
      }
      else
      {
         DBG (LOG_TRACE,
              "Appl. id %u: Release operation skipped because of multi-port board",
              uApplID);
         uRes2 = CAPI_OK;
      }

      /* the application mapping must be undone in every case */
      pCtlrInfo->auAppMapCtlrToMan [uCtlrApplID] = 0;
      pCtlrInfo->auAppMapManToCtlr [uApplID]     = 0;
      
      mtx_unlock (&g_mtxCtlrData);

      if (e_uNumTracers > 0)
      {
         kcapitrace_drvr_release (uUniqueCtlrNum,
                                  uDrvCtlrNum,
                                  uApplID,
                                  uCtlrApplID,
                                  uRes2);
      }
   }

   return (uRes);
} /* kcapictlr_release_app */





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

unsigned kcapictlr_put_message
   (unsigned     uApplID,
    unsigned     uUniqueCtlrNum,
    struct mbuf *pmbMsg)
{
   CapiManCtlrInfo_t *pCtlrInfo;
   CAPIMsg_t         *pMsg;
   u_int16_t          wCmd;
   unsigned           uDrvCtlrNum;
   unsigned           uCtlrApplID;
   unsigned           uRes;
   
   mtx_lock (&g_mtxCtlrData);
   
   /* check for valid controller number */
   if (uUniqueCtlrNum >= ARRAY_COUNT (g_aCtlrInfo) ||
       ! g_aCtlrInfo [uUniqueCtlrNum].fCtlrRegistered)
   {
      mtx_unlock (&g_mtxCtlrData);
      DBG (LOG_ERROR, "Invalid controller %u", uUniqueCtlrNum);
      uRes = CCE_ILLEGAL_IDENTIFIER;
      kcapitrace_drvr_put_message (uUniqueCtlrNum, 0, uApplID, 0, pmbMsg, uRes);
      return (uRes);
   }
   
   /* get controller info addressed by the controller number */
   pCtlrInfo = &(g_aCtlrInfo [uUniqueCtlrNum]);
   uDrvCtlrNum = pCtlrInfo->uDrvCtlrNum;
   if (! pCtlrInfo->fCtlrEnabled)
   {
      mtx_unlock (&g_mtxCtlrData);
      DBG (LOG_ERROR, "Controller %u not enabled", uUniqueCtlrNum);
      uRes = CME_CAPI_NOT_INSTALLED;
      kcapitrace_drvr_put_message (uUniqueCtlrNum, uDrvCtlrNum, uApplID, 0,
                                   pmbMsg, uRes);
      return (uRes);
   }
   
   /* check for completed application registration at the controller */
   if (pCtlrInfo->auAppMapManToCtlr [uApplID] == 0)
   {
      mtx_unlock (&g_mtxCtlrData);
      DBG (LOG_TRACE, "Appl. id %u: Not registered (yet) at controller %u",
           uApplID, uUniqueCtlrNum);
      uRes = CME_INVALID_APPLICATION_ID;
      kcapitrace_drvr_put_message (uUniqueCtlrNum, uDrvCtlrNum, uApplID, 0,
                                   pmbMsg, uRes);
      return (uRes);
   }
   
   /* Note: The message is already checked. It consists at least of the message
    *       header and must be sent (filtering already done).
    */
   
   /* simpler addressing of the message for following operations */
   pMsg = mtod (pmbMsg, CAPIMsg_t *);
   wCmd = C_GET_WORD (pMsg->head.wCmd);
   
   /* if needed, perform application id mapping */
   if ((pCtlrInfo->pRegParams->ulFlags &
        CAPI_CTLRFLAG_NEED_APPL_ID_MAPPING) != 0)
   {
      uCtlrApplID = pCtlrInfo->auAppMapManToCtlr [uApplID];
      
      C_PUT_WORD (pMsg->head.wApp, uCtlrApplID);
   }
   else
   {
      uCtlrApplID = uApplID;
   }
   pCtlrInfo->uNumActiveCalls++;
   mtx_unlock (&g_mtxCtlrData);
   
   /* if needed, perform controller number mapping */
   if (uUniqueCtlrNum != uDrvCtlrNum)
   {
      kcapictlr_do_ctlr_exchange (pMsg, uDrvCtlrNum);
   }
   
   /* finally send the message to the controller; if the call is successful,
    * the driver will release the mbuf
    */
   if (e_uNumTracers > 0)
   {
      kcapitrace_trace_drvr_msg (uUniqueCtlrNum, pmbMsg);
   }
   uRes = pCtlrInfo->pRegParams->pfnPutMessage
             (pCtlrInfo->uDrvCtlrNum,
              uCtlrApplID,
              pmbMsg);
   mtx_lock (&g_mtxCtlrData);
   pCtlrInfo->uNumActiveCalls--;
   mtx_unlock (&g_mtxCtlrData);
   if (uRes == CAPI_OK)
   {
      DBG (LOG_DEBUG,
           "Appl. id %u: Message 0x%04X successfully sent to controller %u",
           uApplID, (int) wCmd, uUniqueCtlrNum);
      if (e_uNumTracers > 0)
      {
         kcapitrace_drvr_put_message (uUniqueCtlrNum, uDrvCtlrNum,
                                      uApplID, uCtlrApplID,
                                      pmbMsg, uRes);
      }
   }
   else
   {
      /* if an error occurred, the mappings must be undone, for the calling
       * application may queue the mbuf and resend it later
       */
      DBG (LOG_TRACE,
           "Appl. id %u: Error 0x%04X sending message 0x%04X to controller %u",
           uApplID, uRes, (int) wCmd, uUniqueCtlrNum);

      if (e_uNumTracers > 0)
      {
         kcapitrace_drvr_put_message (uUniqueCtlrNum, uDrvCtlrNum,
                                      uApplID, uCtlrApplID,
                                      pmbMsg, uRes);
      }

      /* if needed, undo application id mapping */
      if (uApplID != uCtlrApplID)
      {
         C_PUT_WORD (pMsg->head.wApp, uApplID);
      }

      /* if needed, undo controller mapping */
      if (uUniqueCtlrNum != uDrvCtlrNum)
      {
         kcapictlr_do_ctlr_exchange (pMsg, uUniqueCtlrNum);
      }
   }
   
   return (uRes);
} /* kcapictlr_put_message */





/**
 * Allocate a new mbuf for a trace message.
 *
 * @note Mbufs allocated with this function should be released by calling
 *       kcapi_free_mbuf().
 *
 * @attention Mbufs of this type must not be used for normal data or CAPI
 *            messages! They are only intended for use as trace messages. The
 *            mbufs use a different type than MT_DATA to be distinguishable from
 *            normal CAPI message mbufs.
 *
 * @param nLen                  I: The size of the new mbuf in bytes.
 *
 * @return NULL                 Unable to allocate an mbuf, out of mbufs.
 * @return Else                 A pointer to the allocated mbuf.
 */

struct mbuf *kcapictlr_get_trace_mbuf
   (size_t nLen)
{
   struct mbuf *pmb;
   
   /* check if length is exceeding maximum length possible (2KB) */
   if (nLen > MCLBYTES)
   {
      return (NULL);
   }
   
   /* allocate mbuf */
   MGETHDR (pmb, M_DONTWAIT, MT_OOBDATA);
   if (pmb && nLen > MHLEN)
   {
      MCLGET (pmb, M_DONTWAIT);
      if ((pmb->m_flags & M_EXT) == 0)
      {
         kcapi_free_mbuf (pmb);
         pmb = NULL;
      }
   }
   
   if (pmb)
   {
      pmb->m_len = pmb->m_pkthdr.len = nLen;
   }
   
   return (pmb);
} /* kcapictlr_get_trace_mbuf */





/**
 * Enqueue a trace message for later processing by the CAPI manager thread.
 *
 * This function is called by the driver level CAPI trace functions to enqueue a
 * trace message. As the driver level CAPI functions are normally called when
 * there are locks held for the CAPI application array or the controller array,
 * it is not possible to directly enqueue trace messages into the application
 * queues. So they are first enqueued into the queue for messages from
 * controllers and then passed to the designated application queue by the CAPI
 * manager thread.
 *
 * @attention The trace message mbuf must be allocated by calling
 *            kcapictlr_get_trace_mbuf()! It uses a different type than MT_DATA
 *            to distinguish trace messages from normal CAPI messages.
 *
 * @param uUniqueApplID         I: The unique application id for the application
 *                                 to receive the trace message.
 * @param pmbMsg                I: The address of an mbuf containing the trace
 *                                 message, must be allocated by
 *                                 kcapictlr_get_trace_mbuf().
 *
 * @return Nothing.
 */

void kcapictlr_enqueue_trace_message
   (unsigned     uUniqueApplID,
    struct mbuf *pmbMsg)
{
   /* ensure the mbuf is allocated by kcapictlr_get_trace_mbuf() */
   if (pmbMsg->m_type != MT_OOBDATA)
   {
      printf ("kcapimgr: ERROR: Got trace message to enqueue with invalid mbuf type %d, expected %d\n",
              (int) (pmbMsg->m_type), (int) MT_OOBDATA);
      return;
   }
   
   /* The trace message is just stored into the queue for incoming messages.
    * The application id is stored into the csum_flags member of the mbuf. This
    * field is not used, as the mbuf of the trace message itself will never be
    * used in a network interface routine. The parameters is needed for later
    * message handling.
    */
   pmbMsg->m_pkthdr.csum_flags = (int) uUniqueApplID;
   IF_ENQUEUE_MTX (&g_queueCtlrMsg, pmbMsg);
   
   /* now wakeup the possibly waiting CAPI manager thread for handling incoming
    * messages
    */
   sema_post (&g_semCtlrMsg);
    
} /* kcapictlr_enqueue_trace_message */





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





/**
 * Thread function to handle messages received from controllers.
 */

static void kcapictlr_message_handler
   (void *pUnused)
{
   struct mbuf *pmbMsg;
   int          fMsgFound;
   
   DBG (LOG_TRACE, "Entering message handling loop");
   
   /* loop until the module shall be unloaded */
   while (g_fDoWorkLoop)
   {
      /* loop until all available CAPI and notify messages are handled */
      fMsgFound = 0;
      do
      {
         IF_DEQUEUE_MTX (&g_queueCtlrMsg, pmbMsg);
         
         if (pmbMsg)
         {
            fMsgFound = 1;
            
            DBG (LOG_DEBUG, "Got message, call handling function");
            
            /* distinguish between ready formed CAPI messages and controller
             * notifications that must be filtered through protocol layers
             */
            if (pmbMsg->m_type == MT_DATA)
            {
               /* mbuf is a CAPI message */
               kcapictlr_receive_capi_message (pmbMsg);
            }
            else if (pmbMsg->m_type == MT_OOBDATA)
            {
               /* mbuf is a trace message */
               /* Note: The unique application id is stored in the member
                *       m_pkthdr.csum_flags (see
                *       kcapictlr_enqueue_trace_message()).
                */
               kcapiappl_enqueue_trace_message
                  ((unsigned) (pmbMsg->m_pkthdr.csum_flags), pmbMsg);
            }
            else if (pmbMsg->m_type == MT_CONTROL)
            {
               /* mbuf is a controller notification message */
               kcapictlr_forward_notify_message (pmbMsg);
            }
            else
            {
               /* mbuf is not of a known type */
               DBG (LOG_ERROR, "Got mbuf of unknown type %d, will be discarded",
                    (int) (pmbMsg->m_type));
               kcapi_free_mbuf (pmbMsg);
            }
            /* Note: The mbuf must be released by the message handling function.
             */
            pmbMsg = NULL;
         }
         else
         {
            DBG (LOG_DEBUG, "Message queue empty");
            fMsgFound = 0;
         }
      } while (fMsgFound);

      /* now wait for new incoming messages */
      sema_wait (&g_semCtlrMsg);
   }
   
   DBG (LOG_TRACE, "Left message handling loop");
   
   /* wakeup thread waiting for this thread to terminate */
   sema_post (&g_semThreadTerm);

#if ! defined (__FreeBSD_version) || __FreeBSD_version < 503001
   /* this is needed with current until kthread_exit() does not require it */
   mtx_lock (&Giant);
#endif /* ! __FreeBSD_version || __FreeBSD_version < 503001 */

   kthread_exit (0);
   
   /* suppress warning for unused parameters */
   (void) pUnused;
   
} /* kcapictlr_message_handler */





/**
 * Handle an incoming CAPI message from a controller.
 */

static void kcapictlr_receive_capi_message
   (struct mbuf *pmbMsg)
{
   unsigned           uUniqueCtlrNum;
   unsigned           uDrvCtlrNum;
   unsigned           uCtlrApplID;
   CapiManCtlrInfo_t *pCtlrInfo;
   unsigned           uManApplID;
   
   /* the unique controller number is stored in the field csum_data of the mbuf
    * and the application id is in csum_flags
    */
   uUniqueCtlrNum = (unsigned) (pmbMsg->m_pkthdr.csum_data);
   uCtlrApplID    = (unsigned) (pmbMsg->m_pkthdr.csum_flags);
   
   mtx_lock (&g_mtxCtlrData);
   
   /* check for registered controller */
   if (uUniqueCtlrNum <= 0 || uUniqueCtlrNum >= ARRAY_COUNT (g_aCtlrInfo) ||
       ! g_aCtlrInfo [uUniqueCtlrNum].fCtlrRegistered)
   {
      mtx_unlock (&g_mtxCtlrData);
      printf ("kcapimgr: ERROR: Receiving CAPI message for appl. id %u from invalid controller %u\n",
              uCtlrApplID, uUniqueCtlrNum);
      kcapitrace_drvr_get_message (uUniqueCtlrNum, 0, 0, uCtlrApplID, pmbMsg);
      kcapi_free_mbuf (pmbMsg);
      return;
   }
   pCtlrInfo = &(g_aCtlrInfo [uUniqueCtlrNum]);
   uDrvCtlrNum = pCtlrInfo->uDrvCtlrNum;
   
   /* check for valid controller specific application id */
   if (uCtlrApplID >= ARRAY_COUNT (pCtlrInfo->auAppMapCtlrToMan))
   {
      mtx_unlock (&g_mtxCtlrData);
      printf ("kcapimgr: ERROR: Receiving CAPI message for invalid appl. id %u from controller %u\n",
              uCtlrApplID, uUniqueCtlrNum);
      kcapitrace_drvr_get_message (uUniqueCtlrNum, uDrvCtlrNum, 0, uCtlrApplID,
                                   pmbMsg);
      kcapi_free_mbuf (pmbMsg);
      return;
   }
   
   /* determine the real application id for the controller specific appl. id */
   if ((pCtlrInfo->pRegParams->ulFlags &
        CAPI_CTLRFLAG_NEED_APPL_ID_MAPPING) != 0)
   {
      uManApplID = pCtlrInfo->auAppMapCtlrToMan [uCtlrApplID];
      if (uManApplID <= 0 ||
          uManApplID >= ARRAY_COUNT (pCtlrInfo->auAppMapManToCtlr))
      {
         mtx_unlock (&g_mtxCtlrData);
         printf ("kcapimgr: ERROR: Receiving CAPI message for invalid mapped appl. id %u (%u) from controller %u\n",
                 uManApplID, uCtlrApplID, uUniqueCtlrNum);
         kcapitrace_drvr_get_message (uUniqueCtlrNum, uDrvCtlrNum,
                                      uManApplID, uCtlrApplID, pmbMsg);
         kcapi_free_mbuf (pmbMsg);
         return;
      }
   }
   else
   {
      uManApplID = uCtlrApplID;
   }
   
   mtx_unlock (&g_mtxCtlrData);
   
   if (e_uNumTracers > 0)
   {
      kcapitrace_drvr_get_message (uUniqueCtlrNum, uDrvCtlrNum,
                                   uManApplID, uCtlrApplID, pmbMsg);
      kcapitrace_trace_drvr_msg (uUniqueCtlrNum, pmbMsg);
   }
   
   /* if CAPI message is long enough, mapping may be needed; a shorter message
    * is believed to be an empty mbuf signalling a lost message
    */
   if (pmbMsg && pmbMsg->m_len >= sizeof (CAPIMsgHead_t))
   {
      CAPIMsg_t *pMsg = (CAPIMsg_t *) (pmbMsg->m_data);

      /* if needed, perform application id mapping */
      if (uManApplID != uCtlrApplID)
      {
         C_PUT_WORD (pMsg->head.wApp, uManApplID);
      }
      
      /* if needed, perform controller number mapping */
      if (uUniqueCtlrNum != uDrvCtlrNum)
      {
         kcapictlr_do_ctlr_exchange (pMsg, uUniqueCtlrNum);
      }
   }
   
   /* now the message is enqueued into the application queue */
   kcapiappl_enqueue_capi_message (uManApplID, pmbMsg);
   
} /* kcapictlr_receive_capi_message */




   
/**
 * Perform controller mapping for a CAPI message.
 *
 * @pre The message specified is at least long enough to hold the message
 *      header.
 */

static void kcapictlr_do_ctlr_exchange
   (CAPIMsg_t *pMsg,
    unsigned   uNewCtlr)
{
   unsigned char uc;
   unsigned      uCmd = CAPI_GET_CMD (pMsg);
   size_t        nLen = CAPI_GET_LEN (pMsg);

   /* the controller number is the first byte of the last field dwCid in the
    * message header; but the MSB may specify external equipment
    */
   uc = ((unsigned char *) pMsg) [sizeof (pMsg->head) -
                                  sizeof (pMsg->head.dwCid)];
   ((unsigned char *) pMsg) [sizeof (pMsg->head) -
                             sizeof (pMsg->head.dwCid)] =
      (uNewCtlr & CAPI_CIDMASK_CTLR) | (uc & ~CAPI_CIDMASK_CTLR);

   /* in several Facility-Requests there is also a PLCI in the info part of the
    * message
    */
   if (uCmd == CAPI_REQUEST (C_FACILITY) &&
       nLen >= sizeof (pMsg->head) +
               sizeof (pMsg->info.facility_req) + 1)
   {
      /* Supplementary Services Request */
      if (C_GET_WORD (pMsg->info.facility_req.wSelector) ==
             CAPI_FACILITY_SELECTOR_SUPPL_SERVICES)
      {
         CAPIFacilitySupplReq_t *pSupplReq =
            (CAPIFacilitySupplReq_t *)
               ((unsigned char *) &(pMsg->info.facility_req.wSelector) +
                sizeof (pMsg->info.facility_req.wSelector));

         /* Facility-ECT-Request */
         if (C_GET_BYTE (pSupplReq->bLength) > sizeof (*pSupplReq) &&
             C_GET_WORD (pSupplReq->wFunction) == C_SUPPL_FUNC_ECT)
         {
            CAPISupplParamEctReq_t *pParam =
               (CAPISupplParamEctReq_t *)
                  ((unsigned char *) pSupplReq + sizeof (*pSupplReq));

            if (C_GET_BYTE (pParam->bLength) >= sizeof (*pParam))
            {
               uc = *((unsigned char *) pParam + 1);
               *((unsigned char *) pParam + 1) =
                  (uNewCtlr & CAPI_CIDMASK_CTLR) | (uc & ~CAPI_CIDMASK_CTLR);
            }
         }
         /* Facility-3pty-Begin-Request */
         else if (C_GET_BYTE (pSupplReq->bLength) >
                     sizeof (*pSupplReq) &&
                  C_GET_WORD (pSupplReq->wFunction) ==
                     C_SUPPL_FUNC_3PTY_BEGIN)
         {
            CAPISupplParam3PtyBeginReq_t *pParam =
               (CAPISupplParam3PtyBeginReq_t *)
                  ((unsigned char *) pSupplReq + sizeof (*pSupplReq));

            if (C_GET_BYTE (pParam->bLength) >= sizeof (*pParam))
            {
               uc = *((unsigned char *) pParam + 1);
               *((unsigned char *) pParam + 1) =
                  (uNewCtlr & CAPI_CIDMASK_CTLR) | (uc & ~CAPI_CIDMASK_CTLR);
            }
         }
         /* Facility-3pty-End-Request */
         else if (C_GET_BYTE (pSupplReq->bLength) >
                     sizeof (*pSupplReq) &&
                  C_GET_WORD (pSupplReq->wFunction) ==
                     C_SUPPL_FUNC_3PTY_END)
         {
            CAPISupplParam3PtyEndReq_t *pParam =
               (CAPISupplParam3PtyEndReq_t *)
                  ((unsigned char *) pSupplReq + sizeof (*pSupplReq));

            if (C_GET_BYTE (pParam->bLength) >= sizeof (*pParam))
            {
               uc = *((unsigned char *) pParam + 1);
               *((unsigned char *) pParam + 1) =
                  (uNewCtlr & CAPI_CIDMASK_CTLR) | (uc & ~CAPI_CIDMASK_CTLR);
            }
         }
      }
      /* TODO: When the line interconnect services definitions in capi20.h
       *       are fixed, there may be additional CAPI messages needing
       *       controller mapping in the info part of the message.
       */
   }

} /* kcapictlr_do_ctlr_exchange */





/**
 * Forward a notification message to the originating controller.
 */

static void kcapictlr_forward_notify_message
   (struct mbuf *pmbMsg)
{
   unsigned           uUniqueCtlrNum;
   unsigned           uDrvCtlrNum;
   CapiManCtlrInfo_t *pCtlrInfo;
   unsigned           uRes;
   
   /* the unique controller number is stored in the field csum_data of the mbuf
    */
   uUniqueCtlrNum = (unsigned) (pmbMsg->m_pkthdr.csum_data);
   
   mtx_lock (&g_mtxCtlrData);
   
   /* check for registered controller */
   if (uUniqueCtlrNum <= 0 || uUniqueCtlrNum >= ARRAY_COUNT (g_aCtlrInfo) ||
       ! g_aCtlrInfo [uUniqueCtlrNum].fCtlrRegistered)
   {
      mtx_unlock (&g_mtxCtlrData);
      printf ("kcapimgr: ERROR: Receiving notification message for invalid controller %u\n",
              uUniqueCtlrNum);
      kcapi_free_mbuf (pmbMsg);
      return;
   }
   pCtlrInfo = &(g_aCtlrInfo [uUniqueCtlrNum]);
   uDrvCtlrNum = pCtlrInfo->uDrvCtlrNum;
   
   /* check for a registered notification function for the controller */
   if (pCtlrInfo->pRegParams->pfnPutNotifyMsg == NULL)
   {
      mtx_unlock (&g_mtxCtlrData);
      printf ("kcapimgr: ERROR: Receiving notification message for controller %u without registered handler function\n",
              uUniqueCtlrNum);
      kcapi_free_mbuf (pmbMsg);
      return;
   }
   
   /* now forward the message through the notification function */
   pCtlrInfo->uNumActiveCalls++;
   mtx_unlock (&g_mtxCtlrData);
   uRes = pCtlrInfo->pRegParams->pfnPutNotifyMsg (uDrvCtlrNum, pmbMsg);
   mtx_lock (&g_mtxCtlrData);
   pCtlrInfo->uNumActiveCalls--;
   mtx_unlock (&g_mtxCtlrData);
   if (uRes != CAPI_OK)
   {
      DBG (LOG_ERROR,
           "Error 0x%04X handling notification message by controller %u",
           uRes, uUniqueCtlrNum);
      kcapi_free_mbuf (pmbMsg);
   }
   
} /* kcapictlr_forward_notify_message */
