/**
 * @file i4bcapimgr.c
 *
 * I4BCapiManager - The i4b driver for CAPI controllers.
 *
 * Copyright: 2000-2004 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: i4bcapimgr.c,v 1.1.2.2 2006/03/11 18:55:17 thomas Exp $
 * Project  CAPI for BSD
 * Target   i4bcapimgr - The I4B driver for CAPI manager driven 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/limits.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/conf.h>
#include <sys/mbuf.h>
#include <sys/proc.h>
#include <sys/kthread.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <capi20.h>
#include <capi_bsd.h>
#include <c4b/kcapimgr/capi_drv.h> /* CAPIMAN_VERSION_* */
#include <i4b_cause.h>
#include <i4b_ioctl.h>

/* import includes */
#include <i4b/include/i4b_l3l4.h>
#include <i4b/layer4/i4b_l4.h>

#define __I4BCAPIMGR__

/* local includes */
#include <i4b/i4bcapimgr/i4bcmgr_var.h>
#include <i4b/i4bcapimgr/i4bcmgr_nccism.h>
#include <i4b/i4bcapimgr/i4bcmgr_plcism.h>

#ifndef CTRL_CAPIMGR
#warning I4B kernel part not modified/patched to support C4B.
#define CTRL_CAPIMGR CTRL_NUMTYPES
#endif /* CTRL_CAPIMGR */





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





/* --- registering data for CAPI --- */

int      e_fCapiRegistered     = 0;
unsigned e_uCapiApplID         = 0;
unsigned e_uMaxCapiConnections = 0;



/* --- mapping between i4b and CAPI controller numbers --- */

/* I4b is at maximum capable of handling MAX_CONTROLLERS controllers */
/* 0 means no CAPI controller exists for corresponding i4b controller no. */
unsigned e_auMapI4bToCapiCtlr [MAX_CONTROLLERS] = { 0 };



/* --- data for available CAPI controllers --- */

/* For CAPI a maximum number of 127 controllers is defined, starting at 1. */
I4bCapiCtlrData_t e_aI4bCapiCtlrData [128] = { { 0 } };



/* --- the event queue to communicate with i4bcapimgr from i4b layer 4 --- */

/** The queue of messages to handle. */
struct I4bCEventQueue e_eventQueue = TAILQ_HEAD_INITIALIZER (e_eventQueue);

/** The list of unused message objects. */
struct I4bCEventFreeList e_eventFreeList =
   SLIST_HEAD_INITIALIZER (e_eventFreeList);

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



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

#ifdef I4BCAPIMGR_LOG_LEVEL
int e_iI4bCapiLogLevel = I4BCAPIMGR_LOG_LEVEL;
#else /* I4BCAPIMGR_LOG_LEVEL */
int e_iI4bCapiLogLevel = LOG_ERROR;
#endif /* I4BCAPIMGR_LOG_LEVEL */



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

/** The node for this module under "capi". */
SYSCTL_NODE (_capi, OID_AUTO, i4bcapimgr, CTLFLAG_RW, 0,
             "I4b CAPI manager controller driver");

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

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

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

/** The variable for the current CAPI application id. */
SYSCTL_UINT (_capi_i4bcapimgr, OID_AUTO, appl_id, CTLFLAG_RD,
             &e_uCapiApplID, 0, "Application id from CAPI registration");

/** The variable for the maximum number of possible connections. */
SYSCTL_UINT (_capi_i4bcapimgr, OID_AUTO, max_connections, CTLFLAG_RD,
             &e_uMaxCapiConnections, 0, "Maximum number of connections");



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

TUNABLE_INT (I4BCMGR_TUNABLE_LOGLEVEL, &e_iI4bCapiLogLevel);





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





/* --- some constants --- */

#define WAIT_FOR_CONN_ABORT_MSG         "wait for connection abort"
#define WAIT_FOR_CONN_ABORT_TIMEOUT     30 /* 30 seconds */



/* --- control of the worker thread --- */

/* flag to stop the worker thread */
static volatile int g_fDoWorkLoop = 0;

/* flag to signal available CAPI message */
static volatile int g_fCapiMessageAvailable = 0;

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



/* --- translation table for i4bcapimgr events into strings --- */

/* Important: The array is indexed by the i4bcapimgr event enum values! */
static const char *g_apszEventNames [] =
   {
      "I4B-Connect-Req",
      "I4B-Connect-Resp",
      "I4B-Disconnect-Req",
      "I4B-Alert-Req",
      "I4B-BCh-Config-Up",
      "I4B-BCh-Config-Down",
      "I4B-BCh-Tx-Start",
      "NCCI-Timeout",
      "PLCI-Timeout"
   };





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





/* --- (Un-)Register CAPI controllers at i4b --- */

/* register all CAPI controllers at i4b */
static void RegisterAllControllers (void);

/* unregister all CAPI controllers at i4b */
static void UnregisterAllControllers (void);

/* abort all running connections */
static void AbortAllConnections (void);



/* --- handling of external events and messages --- */

/* start the i4bcapimgr kernel thread to do the work */
static void StartWorkThread (void);

/* stop the i4bcapimgr kernel thread */
static void StopWorkThread (void);

/* the i4bcapimgr kernel thread function to handle events */
static void I4bCapiWorkThread
   (void *pUnused);

/* read and handle all available CAPI messages */
static void ReadCapiMessages (void);

/* handle an i4b event */
static void HandleI4bMessage
   (I4bCEventQueueEntry_t *pMsg);



/* --- handling of kernel module specific functionality --- */

/* flag for received first MOD_LOAD event */
static int g_fLoadEventReceived = 0;

/* Handling of module load/unload events */
static int i4bcapimgr_modevent
   (module_t  mod,
    int       iType,
    void     *pUnused);

static moduledata_t i4bcapimgr_mod =
{
   "i4bcapimgr",
   i4bcapimgr_modevent,
   NULL
};

/* Note: i4bcapimgr must be loaded after all CAPI controller drivers. Only this
 *       way it is possible to use all installed controllers for i4b.
 */
DECLARE_MODULE (i4bcapimgr, i4bcapimgr_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);

MODULE_VERSION (i4bcapimgr,
                (I4BCMGR_MODULE_VERSION_MAJOR << 16) |
                   I4BCMGR_MODULE_VERSION_MINOR);

MODULE_DEPEND (i4bcapimgr, kcapimgr,
               (CAPIMAN_VERSION_MAJOR << 16) | CAPIMAN_VERSION_MINOR,
               (CAPIMAN_VERSION_MAJOR << 16) | CAPIMAN_VERSION_MINOR,
               (CAPIMAN_VERSION_MAJOR << 16) | 0x0000FFFF);





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





/*
        handle the signal for an incoming CAPI message
        ----------------------------------------------
        This function is only responsible to forward the signal of a new
        available CAPI message to the worker thread.
*/

#if defined(__alpha__) || defined (__ia64__) || defined (__amd64__) || defined (__sparc64__)
void I4bCmgr_CapiCallback
   (unsigned  uApplID,
    u_int64_t qwParam)
#else /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
void I4bCmgr_CapiCallback
   (unsigned  uApplID,
    u_int32_t dwParam)
#endif /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
{
   g_fCapiMessageAvailable = 1;
   
   sema_post (&e_semMsgAvail);

   // suppress warning
#if defined(__alpha__) || defined (__ia64__) || defined (__amd64__) || defined (__sparc64__)
   (void) qwParam;
#else /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
   (void) dwParam;
#endif /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */

} /* I4bCmgr_CapiCallback */





/*
        get connection data entry from i4b call descriptor id
        -----------------------------------------------------
*/

I4bCapiConnectionData_t *I4bCmgr_GetConnDataByCdId
   (u_int uCdId)
{
   I4bCapiCtlrData_t       *pCtlrData;
   I4bCapiConnectionData_t *pConnData;
   call_desc_t             *pI4bCd;
   unsigned                 uCapiCtlrNum;
   int                      s;
   
   s = SPLI4B ();
   
   /* first get the address of the call descriptor data for the cd id */
   pI4bCd = cd_by_cdid (uCdId);
   if (! pI4bCd)
   {
      splx (s);
      printf ("i4bcapimgr: ERROR: Got i4b call with invalid call descriptor %u\n",
              uCdId);
      return (NULL);
   }
   
   /* get the controller used */
   if ((size_t) (pI4bCd->controller) >= ARRAY_COUNT (e_auMapI4bToCapiCtlr))
   {
      splx (s);
      printf ("i4bcapimgr: ERROR: Got i4b call with invalid i4b controller no. %d in call descriptor %u\n",
              pI4bCd->controller, uCdId);
      return (NULL);
   }
   uCapiCtlrNum = e_auMapI4bToCapiCtlr [pI4bCd->controller];
   if (uCapiCtlrNum == 0 ||
       uCapiCtlrNum >= ARRAY_COUNT (e_aI4bCapiCtlrData))
   {
      splx (s);
      printf ("i4bcapimgr: ERROR: Got i4b call with invalid i4b controller no. %d in call descriptor %u, CAPI controller claimed to be %u\n",
              pI4bCd->controller, uCdId, uCapiCtlrNum);
      return (NULL);
   }
   pCtlrData = &(e_aI4bCapiCtlrData [uCapiCtlrNum]);
   if (! pCtlrData->fEntryUsed ||
       pCtlrData->iI4bCtlrNum < 0 ||
       ! pCtlrData->paConnData)
   {
      splx (s);
      printf ("i4bcapimgr: ERROR: Got i4b call for unusable controller (i4b %d, CAPI %u), call descriptor %u\n",
              pI4bCd->controller, pCtlrData->uCapiCtlrNum, uCdId);
      return (NULL);
   }

   /* now get the B-channel used */
   if ((size_t) (pI4bCd->channelid) >= pCtlrData->nNumBChannels ||
       ! pCtlrData->paConnData [pI4bCd->channelid].pI4bCd)
   {
      splx (s);
      printf ("i4bcapimgr: ERROR: I4b call: B-channel %d invalid or not in use for controller (i4b %d, CAPI %u), call descriptor %u\n",
              pI4bCd->channelid, pI4bCd->controller,
              pCtlrData->uCapiCtlrNum, uCdId);
      return (NULL);
   }
   
   pConnData = &(pCtlrData->paConnData [pI4bCd->channelid]);
   splx (s);

   /* return the address of the connection data found */
   return (pConnData);
} /* I4bCmgr_GetConnDataByCdId */





/*
        get connection data entry from unit and channel number
        ------------------------------------------------------
*/

I4bCapiConnectionData_t *I4bCmgr_GetConnDataByUnitAndChannel
   (int iUnit,
    int iChannel)
{
   I4bCapiCtlrData_t       *pCtlrData;
   I4bCapiConnectionData_t *pConnData;
   int                      s;
   
   s = SPLI4B ();
   
   /* check for valid unit no., i.e. CAPI controller no. */
   if ((size_t) iUnit >= ARRAY_COUNT (e_aI4bCapiCtlrData))
   {
      splx (s);
      printf ("i4bcapimgr: ERROR: Got i4b call with invalid unit no. %d (CAPI controller no.)\n",
              iUnit);
      return (NULL);
   }
   pCtlrData = &(e_aI4bCapiCtlrData [iUnit]);
   if (! pCtlrData->fEntryUsed ||
       pCtlrData->iI4bCtlrNum < 0 ||
       ! pCtlrData->paConnData)
   {
      splx (s);
      printf ("i4bcapimgr: ERROR: Got i4b call with unusable unit no. %d (CAPI controller no.)\n",
              iUnit);
      return (NULL);
   }
   
   /* check for valid channel number */
   if (iChannel >= pCtlrData->nNumBChannels)
   {
      splx (s);
      printf ("i4bcapimgr: ERROR: Got i4b call with invalid channel no. %d for unit %d (CAPI controller no.)\n",
              iChannel, iUnit);
      return (NULL);
   }
   if (! pCtlrData->paConnData [iChannel].pI4bCd)
   {
      splx (s);
      printf ("i4bcapimgr: ERROR: Got i4b call with unused channel no. %d for unit %d (CAPI controller no.)\n",
              iChannel, iUnit);
      return (NULL);
   }
   
   pConnData = &(pCtlrData->paConnData [iChannel]);
   splx (s);

   /* return the address of the connection data found */
   return (pConnData);
} /* I4bCmgr_GetConnDataByUnitAndChannel */





/*
        get memory for a new i4bcapimgr message
        ---------------------------------------
*/

I4bCEventQueueEntry_t *I4bCmgr_GetFreeEventQueueEntry (void)
{
   I4bCEventQueueEntry_t *pMsg;
   int                    s;
   
   /* first check if there is message memory in the free list */
   s = SPLI4B ();
   pMsg = SLIST_FIRST (&e_eventFreeList);
   if (pMsg)
   {
      SLIST_REMOVE_HEAD (&e_eventFreeList, links.sle);
      splx (s);
      return (pMsg);
   }
   splx (s);
   
   /* no unused message memory, allocate new */
   pMsg = (I4bCEventQueueEntry_t *)
             malloc (sizeof (*pMsg), M_DEVBUF, M_WAITOK);

   /* error message is printed by the caller */
   return (pMsg);
} /* I4bCmgr_GetFreeEventQueueEntry */





/*
        translate i4bcapimgr event to string
        ------------------------------------
*/

const char *I4bCmgr_GetI4bCEventName
   (I4bCapiEventType_t event)
{
   static char szBuf [24];
   
   if ((size_t) event >= ARRAY_COUNT (g_apszEventNames))
   {
      snprintf (szBuf, sizeof (szBuf), "<<Unknown: %d>>", (int) event);
      return (szBuf);
   }
   return (g_apszEventNames [(int) event]);
} /* I4bCmgr_GetI4bCEventName */





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





/*
        register all CAPI controllers at i4b
        ------------------------------------
*/

static void RegisterAllControllers (void)
{
   CAPIProfileBuffer_t      profile;
   I4bCapiCtlrData_t       *pCtlrData;
   I4bCapiConnectionData_t *pConnData;
   ctrl_desc_t             *pI4bCtlrDesc;
   unsigned                 uRes;
   unsigned                 u;
   int                      j;
   int                      k;
   size_t                   nNumCtlr;
   size_t                   nNumFound;
   size_t                   nNumRegistered;
   int                      s;
   
   /* get number of installed CAPI controllers */
   uRes = kcapi_get_profile (0, &profile);
   if (uRes != CAPI_OK)
   {
      printf ("i4bcapimgr: ERROR 0x%04X getting number of installed CAPI controllers\n",
              uRes);
      return;
   }
   nNumCtlr = (size_t) C_GET_WORD (profile.wCtlr);
   if (nNumCtlr <= 0)
   {
      printf ("i4bcapimgr: No CAPI controllers installed\n");
      return;
   }
   
   /* controller registration must not be disturbed by other actions */
   s = SPLI4B ();
   
   /* get information about each controller and register it at i4b */
   e_uMaxCapiConnections = 0;
   for (u = 1, nNumFound = 0, nNumRegistered = 0;
        u < ARRAY_COUNT (e_aI4bCapiCtlrData) && nNumFound < nNumCtlr;
        u++)
   {
      /* try to get information about current controller */
      uRes = kcapi_get_profile (u, &profile);
      if (uRes == CRE_CAPI_NOT_INSTALLED)
      {
         /* seems to be a hole in the array of CAPI controllers... */
         continue;
      }
      nNumFound++;                      /* count the controller found */
      if (uRes != CAPI_OK)
      {
         printf ("i4bcapimgr: ERROR 0x%04X getting CAPI profile for controller %u\n",
                 uRes, u);
         continue;
      }
      
      /* fill local controller data */
      pCtlrData = &(e_aI4bCapiCtlrData [u]);
      pCtlrData->uCapiCtlrNum = u;
      pCtlrData->iI4bCtlrNum = -1;
      if ((C_GET_DWORD (profile.dwB1ProtocolSupport) &
           (1 << CAPI_B1_HDLC_64)) != 0 &&
          (C_GET_DWORD (profile.dwB2ProtocolSupport) &
           (1 << CAPI_B2_TRANSPARENT)) != 0 &&
          (C_GET_DWORD (profile.dwB3ProtocolSupport) &
           (1 << CAPI_B3_TRANSPARENT)) != 0)
      {
         pCtlrData->uFeatureFlags |= I4BCMGR_FEATURE_FLAG_HDLC;
      }
      if ((C_GET_DWORD (profile.dwB1ProtocolSupport) &
          (1 << CAPI_B1_TRANSPARENT_64)) != 0 &&
          (C_GET_DWORD (profile.dwB2ProtocolSupport) &
           (1 << CAPI_B2_TRANSPARENT)) != 0 &&
          (C_GET_DWORD (profile.dwB3ProtocolSupport) &
           (1 << CAPI_B3_TRANSPARENT)) != 0)
      {
         pCtlrData->uFeatureFlags |= I4BCMGR_FEATURE_FLAG_TRANSPARENT;
      }
      pCtlrData->nNumBChannels = C_GET_WORD (profile.wNumBChannels);
      if (pCtlrData->nNumBChannels <= 0)
      {
         /* Controller has no channels? Must be a driver error, assume 2
          * B-channels
          */
         pCtlrData->nNumBChannels = 2;
      }
      /* i4b can handle a maximum of MAX_BCHAN B-channels for each controller */
      if (pCtlrData->nNumBChannels > ARRAY_COUNT (ctrl_desc [0].bch_state))
      {
         printf ("i4bcapimgr: WARNING: CAPI controller %u supports %zu B-channels, will be limited to %zu for i4b",
                 u, pCtlrData->nNumBChannels,
                 ARRAY_COUNT (ctrl_desc [0].bch_state));
         pCtlrData->nNumBChannels = ARRAY_COUNT (ctrl_desc [0].bch_state);
      }
      MALLOC (pCtlrData->paConnData, I4bCapiConnectionData_t *,
              pCtlrData->nNumBChannels * sizeof (pCtlrData->paConnData [0]),
              M_DEVBUF, M_WAITOK);
      if (! pCtlrData->paConnData)
      {
         printf ("i4bcapimgr: ERROR: Out of memory for data of %zu B-channels for CAPI controller %u\n",
                 pCtlrData->nNumBChannels, u);
         pCtlrData->nNumBChannels = 0;
         continue;
      }
      bzero (pCtlrData->paConnData,
             pCtlrData->nNumBChannels * sizeof (pCtlrData->paConnData [0]));
      for (j = 0; (size_t) j < pCtlrData->nNumBChannels; j++)
      {
         pConnData = &(pCtlrData->paConnData [j]);
         pConnData->pCtlrData = pCtlrData;
         pConnData->uBChn     = (unsigned) j;
         pConnData->rxQueue.ifq_maxlen = IFQ_MAXLEN;
         mtx_init (&(pConnData->rxQueue.ifq_mtx), "i4bcapimgr_rx",
		   NULL, MTX_DEF);
         pConnData->txQueue.ifq_maxlen = IFQ_MAXLEN;
         mtx_init (&(pConnData->txQueue.ifq_mtx), "i4bcapimgr_tx",
		   NULL, MTX_DEF);
         pConnData->isdnLinkTab.unit         = (int) u;
         pConnData->isdnLinkTab.channel      = j;
         pConnData->isdnLinkTab.bch_config   = I4bCmgr_BChConfig;
         pConnData->isdnLinkTab.bch_tx_start = I4bCmgr_BchTxStart;
         pConnData->isdnLinkTab.bch_stat     = I4bCmgr_Stat;
         pConnData->isdnLinkTab.tx_queue     = &(pConnData->txQueue);
         pConnData->isdnLinkTab.rx_queue     = &(pConnData->rxQueue);
         pConnData->isdnLinkTab.rx_mbuf      = &(pConnData->pmbInBuf);
      }
      pCtlrData->fEntryUsed = 1;
      
      /* register the controller at i4b, if (still) possible */
      if ((size_t) nctrl >= MAX_CONTROLLERS)
      {
         printf ("i4bcapimgr: WARNING: Unable to register CAPI controller %u, maximum of %d i4b controllers reached\n",
                 u, nctrl);
         continue;
      }
      pI4bCtlrDesc = &(ctrl_desc [nctrl]);
      pI4bCtlrDesc->unit      = u;
      pI4bCtlrDesc->ctrl_type = CTRL_CAPIMGR;
      pI4bCtlrDesc->card_type = 0;
      pI4bCtlrDesc->protocol  = PROTOCOL_DSS1;
      pI4bCtlrDesc->dl_est    = DL_DOWN;
      pI4bCtlrDesc->nbch      = pCtlrData->nNumBChannels;
      for (k = 0; k < pI4bCtlrDesc->nbch; k++)
      {
         pI4bCtlrDesc->bch_state [k] = BCH_ST_FREE;
      }
      pI4bCtlrDesc->tei                  = 0;
      pI4bCtlrDesc->N_CONNECT_REQUEST    = I4bCmgr_NConnectRequest;
      pI4bCtlrDesc->N_CONNECT_RESPONSE   = I4bCmgr_NConnectResponse;
      pI4bCtlrDesc->N_DISCONNECT_REQUEST = I4bCmgr_NDisconnectRequest;
      pI4bCtlrDesc->N_ALERT_REQUEST      = I4bCmgr_NAlertRequest;
      pI4bCtlrDesc->N_DOWNLOAD           = I4bCmgr_NDownload;
      pI4bCtlrDesc->N_DIAGNOSTICS        = NULL;
      pI4bCtlrDesc->N_MGMT_COMMAND       = I4bCmgr_NMgmtCommand;
      pCtlrData->iI4bCtlrNum = nctrl;
      nctrl++;
      nNumRegistered++;                 /* count the controllers registered */
      
      /* count the no. of B-channels for the maximum no. of connections */
      e_uMaxCapiConnections += pCtlrData->nNumBChannels;
      
      /* set the mapping entry for i4b controller numbers to CAPI */
      e_auMapI4bToCapiCtlr [pCtlrData->iI4bCtlrNum] = pCtlrData->uCapiCtlrNum;
      
      printf ("i4bcapimgr: CAPI controller %u registered at i4b with controller no. %d\n",
              pCtlrData->uCapiCtlrNum, pCtlrData->iI4bCtlrNum);
   }
   
   /* if no controller was registered just leave this function, i4bcapimgr is
    * not going to be used
    */
   if (nNumRegistered == 0)
   {
      e_uMaxCapiConnections = 0;
      
      splx (s);
      
      printf ("i4bcapimgr: No CAPI controller could be registered\n");
      
      return;
   }

   /* register the linktab function addresses for our i4b controller type */
   ctrl_types [CTRL_CAPIMGR].set_linktab = I4bCmgr_SetLinkTab;
   ctrl_types [CTRL_CAPIMGR].get_linktab = I4bCmgr_GetLinkTab;
   
   splx (s);
   
   printf ("i4bcapimgr: %zu CAPI controllers registered at i4b\n",
           nNumRegistered);
   
   /* now finally start the worker thread to do the work */
   StartWorkThread ();
   
} /* RegisterAllControllers */





/*
        unregister all CAPI controllers at i4b
        --------------------------------------
*/

static void UnregisterAllControllers (void)
{
   I4bCapiCtlrData_t       *pCtlrData;
   I4bCapiConnectionData_t *pConnData;
   ctrl_desc_t             *pI4bCtlrDesc;
   int                      i;
   int                      j;
   unsigned                 uRes;
   int                      s;
   
   /* first unregister at CAPI to let possibly active connections to be aborted
    * by the CAPI manager (should normally be done when daemon detaches)
    */
   if (e_fCapiRegistered)
   {
      e_fCapiRegistered = 0;
      uRes = kcapi_release (e_uCapiApplID);
      if (uRes != CAPI_OK)
      {
         printf ("i4bcapimgr: ERROR 0x%04X releasing at CAPI\n", uRes);
      }
      DBG (LOG_TRACE, "Appl. id %u released at CAPI manager", e_uCapiApplID);
      e_uCapiApplID = 0;
   }
   
   /* controller unregistration must not be disturbed by other actions */
   s = SPLI4B ();
   
   /* go through the array of possibly installed controllers */
   /* Note: We start at the end to make decrementing the current maximum
    *       controller no. nctrl of i4b possible. If the currently unregistered
    *       controller is the one in the last used i4b controller entry, the
    *       variable nctrl is decremented. Maybe we could register again when
    *       beeing reloaded.
    */
   for (i = (int) ARRAY_COUNT (e_aI4bCapiCtlrData) - 1; i >= 0; i--)
   {
      /* check if controller entry is used */
      pCtlrData = &(e_aI4bCapiCtlrData [i]);
      if (! pCtlrData->fEntryUsed)
      {
         continue;
      }
      
      /* check if controller is currently registered */
      if (pCtlrData->iI4bCtlrNum < 0 ||
          (size_t) (pCtlrData->iI4bCtlrNum) >= MAX_CONTROLLERS)
      {
         continue;
      }
      
      /* undo the mapping for i4b controller numbers to CAPI */
      e_auMapI4bToCapiCtlr [pCtlrData->iI4bCtlrNum] = 0;
      
      /* unregister controller */
      pI4bCtlrDesc = &(ctrl_desc [pCtlrData->iI4bCtlrNum]);
      pI4bCtlrDesc->unit      = 0;
      pI4bCtlrDesc->ctrl_type = CTRL_INVALID;
      pI4bCtlrDesc->card_type = 0;
      pI4bCtlrDesc->dl_est    = DL_DOWN;
      for (j = 0; j < pI4bCtlrDesc->nbch; j++)
      {
         pI4bCtlrDesc->bch_state [j] = BCH_ST_FREE;
      }
      pI4bCtlrDesc->nbch                 = 0;
      pI4bCtlrDesc->N_CONNECT_REQUEST    = NULL;
      pI4bCtlrDesc->N_CONNECT_RESPONSE   = NULL;
      pI4bCtlrDesc->N_DISCONNECT_REQUEST = NULL;
      pI4bCtlrDesc->N_ALERT_REQUEST      = NULL;
      pI4bCtlrDesc->N_DOWNLOAD           = NULL;
      pI4bCtlrDesc->N_DIAGNOSTICS        = NULL;
      pI4bCtlrDesc->N_MGMT_COMMAND       = NULL;
      if (pCtlrData->iI4bCtlrNum == nctrl - 1)
      {
         DBG (LOG_TRACE,
              "CAPI controller %u unregistered at i4b, was last i4b controller no. %d",
              pCtlrData->uCapiCtlrNum, pCtlrData->iI4bCtlrNum);
         nctrl--;
      }
      else
      {
         DBG (LOG_TRACE,
              "CAPI controller %u unregistered at i4b, was i4b controller no. %d of %d",
              pCtlrData->uCapiCtlrNum, pCtlrData->iI4bCtlrNum, nctrl);
      }
      pCtlrData->iI4bCtlrNum = -1;

      /* release the mutexes for queue access for all connection data structures
       */
      for (j = 0; (size_t) j < pCtlrData->nNumBChannels; ++j)
      {
         pConnData = &(pCtlrData->paConnData [j]);
         if (mtx_initialized (&(pConnData->rxQueue.ifq_mtx)))
         {
            mtx_destroy (&(pConnData->rxQueue.ifq_mtx));
         }
         if (mtx_initialized (&(pConnData->txQueue.ifq_mtx)))
         {
            mtx_destroy (&(pConnData->txQueue.ifq_mtx));
         }
      }
      
      /* now finally release all connection data structures */
      if (pCtlrData->paConnData)
      {
         free (pCtlrData->paConnData, M_DEVBUF);
         pCtlrData->paConnData = NULL;
      }
   }
      
   splx (s);
   
} /* UnregisterAllControllers */





/*
        abort all running connections
        -----------------------------
        It is assumed that the global activation flag is already reset when
        calling this function. So no more connections will be started.
        It is also necessary to not call UnregisterAllControllers() during
        lifetime of a call to this function.
*/

static void AbortAllConnections (void)
{
   I4bCapiCtlrData_t       *pCtlrData;
   I4bCapiConnectionData_t *pConnData;
   cause_t                  cause;
   int                      s;
   int                      i;
   int                      j;
   
   /* search for open connections */
   SET_CAUSE_TV (cause, CAUSET_I4B, CAUSE_I4B_NORMAL);
   s = SPLI4B ();
   for (i = 0; (size_t) i < ARRAY_COUNT (e_aI4bCapiCtlrData); i++)
   {
      pCtlrData = &(e_aI4bCapiCtlrData [i]);
      if (! pCtlrData->fEntryUsed ||
          pCtlrData->iI4bCtlrNum < 0 ||
          ! pCtlrData->paConnData)
      {
         continue;
      }
      for (j = 0; (size_t) j < pCtlrData->nNumBChannels; j++)
      {
         pConnData = &(pCtlrData->paConnData [j]);
         if (! pConnData->pI4bCd)
         {
            continue;
         }
         printf ("i4bcapimgr: Connection (Cdid %u, B-channel %u, CAPI ctlr. %u) still active, will be aborted\n",
                 pConnData->pI4bCd->cdid, pConnData->uBChn,
                 pConnData->pCtlrData->uCapiCtlrNum);

         pConnData->fDisconnectSleeping = 1;
         I4bCmgr_NDisconnectRequest (pConnData->pI4bCd->cdid, (int) cause);
         tsleep (pConnData, PZERO,
                 WAIT_FOR_CONN_ABORT_MSG, WAIT_FOR_CONN_ABORT_TIMEOUT * hz);
         pConnData->fDisconnectSleeping = 0;

         if (pConnData->pI4bCd)
         {
            printf ("i4bcapimgr: WARNING: Unable to abort connection (Cdid %u, B-channel %u, CAPI ctlr. %u)\n",
                    pConnData->pI4bCd->cdid, pConnData->uBChn,
                    pConnData->pCtlrData->uCapiCtlrNum);
         }
      }
   }
   splx (s);
   
} /* AbortAllConnections */





/*
        start the i4bcapimgr kernel thread to do the work
        -------------------------------------------------
*/

static void StartWorkThread (void)
{
   struct proc *pNewProc;
   int          iRes;
   
   if (! g_fDoWorkLoop)
   {
      g_fDoWorkLoop = 1;
      pNewProc = NULL;

      /* initialize the semaphore to signal a new message in the controller
       * message queue
       */
      sema_init (&e_semMsgAvail, 0, "i4bcapimgr-msg-queue");
   
      /* initialize the semaphore to wait for termination of the message handler
       * thread
       */
      sema_init (&g_semThreadTerm, 0, "i4bcapimgr-thread-terminate");

      iRes = kthread_create (I4bCapiWorkThread, NULL, &pNewProc,
                             0, 0, "i4bcapimgr");
      if (iRes == 0)
      {
         DBG (LOG_TRACE, "Kernel thread started");
      }
      else
      {
         g_fDoWorkLoop = 0;
         printf ("i4bcapimgr: ERROR %d starting kernel thread\n", iRes);
      }
   }
   
} /* StartWorkThread */





/*
        stop the i4bcapimgr kernel thread
        ---------------------------------
*/

static void StopWorkThread (void)
{
   I4bCEventQueueEntry_t *pMsg;
   
   if (g_fDoWorkLoop)
   {
      g_fDoWorkLoop = 0;
      sema_post (&e_semMsgAvail);
      if (sema_timedwait (&g_semThreadTerm, 5 * hz) == 0)
      {
         DBG (LOG_ERROR, "ERROR: Unable to terminate message handler thread");
      }

      sema_destroy (&g_semThreadTerm);
      sema_destroy (&e_semMsgAvail);
   }
   
   /* clean the event queue and the free list */
   for (pMsg = TAILQ_FIRST (&e_eventQueue);
        pMsg;
        pMsg = TAILQ_FIRST (&e_eventQueue))
   {
      TAILQ_REMOVE (&e_eventQueue, pMsg, links.tqe);
      free (pMsg, M_DEVBUF);
   }
   for (pMsg = SLIST_FIRST (&e_eventFreeList);
        pMsg;
        pMsg = SLIST_FIRST (&e_eventFreeList))
   {
      SLIST_REMOVE_HEAD (&e_eventFreeList, links.sle);
      free (pMsg, M_DEVBUF);
   }
   
} /* StopWorkThread */





/*
        the i4bcapimgr kernel thread function to handle events
        ------------------------------------------------------
        This function starts the thread doing most of the work in i4bcapimgr. This
        is necessary, because only one thread of execution is allowed to step
        through the CAPI state machines. As it is not possible to do all the
        work on privilege level "splimp()", there is a single thread handling
        the CAPI stack.
        All communication with this thread is performed by message queues
        (direction from i4b layer 4 to i4bcapimgr) and a single flag signalling
        available CAPI messages.
        CAPI messages have priority over i4b messages, for we must not overload
        the ISDN adapters.
*/

static void I4bCapiWorkThread
   (void *pUnused)
{
   I4bCEventQueueEntry_t *pMsg;
   int                    s;
   
   /* loop until the module shall be unloaded */
   while (g_fDoWorkLoop)
   {
      /* loop until all available CAPI and i4b messages are handled */
      do
      {
         /* check for available CAPI message */
         if (g_fCapiMessageAvailable)
         {
            g_fCapiMessageAvailable = 0;

            /* get and handle all available CAPI messages */
            ReadCapiMessages ();
         }

         /* check for available i4b message */
         s = SPLI4B ();
         pMsg = TAILQ_FIRST (&e_eventQueue);
         if (pMsg)
         {
            TAILQ_REMOVE (&e_eventQueue, pMsg, links.tqe);
            splx (s);
            
            HandleI4bMessage (pMsg);
            
            s = SPLI4B ();
            
            SLIST_INSERT_HEAD (&e_eventFreeList, pMsg, links.sle);
         }
         splx (s);

      } while (pMsg);

      /* now wait for new incoming messages */
      sema_wait (&e_semMsgAvail);
   }
   
   /* wakeup thread waiting for this thread to terminate */
   sema_post (&g_semThreadTerm);

   /* this is needed with current until kthread_exit() itself grabs it */
   mtx_lock (&Giant);
   
   kthread_exit (0);
   
   /* suppress warning for unused parameters */
   (void) pUnused;
   
} /* I4bCapiWorkThread */





/*
        read and handle all available CAPI messages
        -------------------------------------------
*/

static void ReadCapiMessages (void)
{
   unsigned     uRes;
   struct mbuf *pmbMsg;
   CAPIMsg_t   *pCapiMsg;
   unsigned     uCmd;

   /* loop until no CAPI message is available any more */
   do
   {
      /* try to get next message */
      uRes = kcapi_get_message (e_uCapiApplID, &pmbMsg);
      if (uRes == CAPI_OK)
      {
         /* if the message is a Listen-Conf, just ignore it; there is no event
          * for this message and there is no corresponding connection data
          * entry
          */
         pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
         uCmd = CAPI_GET_CMD (pCapiMsg);
         if (uCmd == CAPI_CONFIRM (C_LISTEN))
         {
            DBG (LOG_TRACE, "Got Listen-Conf from controller %u, wInfo 0x%04X",
                 (unsigned) CAPI_GET_CID (pCapiMsg),
                 (unsigned) C_GET_WORD (pCapiMsg->info.listen_conf.wInfo));
         }
         /* if the message is a Data-B3-Indication the dwData member must be set
          * to the address of the data mbuf, that is delivered in
          * pmbMsg->m_next; as this mbuf will possibly be delivered to the
          * upper i4b layers, it may not necessarily be released with the
          * message mbuf
          */
         else if (uCmd == CAPI_INDICAT (C_DATA_B3))
         {
#if defined(__alpha__) || defined (__ia64__) || defined (__amd64__) || defined (__sparc64__)
            C_PUT_QWORD (pCapiMsg->info.data_b3_ind.qwData64,
                         (u_int64_t) (pmbMsg->m_next));
#else /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
            C_PUT_DWORD (pCapiMsg->info.data_b3_ind.dwData,
                         (u_int32_t) (pmbMsg->m_next));
#endif /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
            I4bCmgr_PlciReceiveCapiMessage (pCapiMsg);
#if defined(__alpha__) || defined (__ia64__) || defined (__amd64__) || defined (__sparc64__)
            if (C_GET_QWORD (pCapiMsg->info.data_b3_ind.qwData64) == 0)
#else /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
            if (C_GET_DWORD (pCapiMsg->info.data_b3_ind.dwData) == 0)
#endif /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
            {
               pmbMsg->m_next = NULL;
            }
         }
         else
         {
            /* just forward the CAPI message to the PLCI layer */
            I4bCmgr_PlciReceiveCapiMessage (pCapiMsg);
         }
         
         /* the CAPI message is not needed any more and must be freed */
         kcapi_free_mbuf (pmbMsg);
      }
      else if (uRes == CME_GET_QUEUE_EMPTY)
      {
         DBG (LOG_DEBUG, "kcapi_get_message returned CME_GET_QUEUE_EMPTY");
      }
      else
      {
         printf ("i4bcapimgr: Error 0x%04X getting next CAPI message\n", uRes);
      }
   } while (uRes == CAPI_OK || uRes == CME_GET_QUEUE_OVERFLOW);
   
} /* ReadCapiMessages */





/*
        handle an i4b event
        -------------------
*/

static void HandleI4bMessage
   (I4bCEventQueueEntry_t *pMsg)
{
   I4bCapiConnectionData_t *pConnData;
   
   /* If the event is one above the defined elements, this event shall be
    * ignored. This is used in the PLCI layer when a connection is finished but
    * there is still a message in the event queue for exactly this connection.
    */
   if (pMsg->event == NUM_I4BCMGR_EVENTS)
   {
      if (pMsg->uCapiCtlrNum == UINT_MAX || pMsg->uBChn == UINT_MAX)
      {
         DBG (LOG_DEBUG,
              "Message with invalidated event ignored for cd %u", pMsg->uCdId);
      }
      else
      {
         DBG (LOG_DEBUG,
              "Message with invalidated event ignored for CAPI ctlr %u, B-channel %u",
              pMsg->uCapiCtlrNum, pMsg->uBChn);
      }
      return;
   }
   
   /* determine the connection data entry for the message */
   if (pMsg->uCapiCtlrNum == UINT_MAX ||
       pMsg->uBChn == UINT_MAX)
   {
      pConnData = I4bCmgr_GetConnDataByCdId (pMsg->uCdId);
      if (! pConnData)
      {
         DBG (LOG_ERROR,
              "Unable to determine connection data entry for call descriptor %u, event %s",
              pMsg->uCdId, I4bCmgr_GetI4bCEventName (pMsg->event));
         return;
      }
   }
   else
   {
      pConnData = I4bCmgr_GetConnDataByUnitAndChannel
                     (pMsg->uCapiCtlrNum, pMsg->uBChn);
      if (! pConnData)
      {
         DBG (LOG_ERROR,
              "Unable to determine connection data entry for unit %u and channel %u, event %s",
              pMsg->uCapiCtlrNum, pMsg->uBChn,
              I4bCmgr_GetI4bCEventName (pMsg->event));
         return;
      }
   }
   
   /* this will be the most often received event, check and handle it first */
   if (pMsg->event == I4BCMGR_EVENT_BCH_TX_START)
   {
      I4bCmgr_NcciTxStart (pConnData);
      return;
   }
   
   /* evaluate the event type for the other events */
   switch (pMsg->event)
   {
      case I4BCMGR_EVENT_CONN_REQ:
         I4bCmgr_NcciConnectReq (pConnData);
         break;
         
      case I4BCMGR_EVENT_CONN_RESP:
         I4bCmgr_NcciConnectResp (pConnData, &(pMsg->data));
         break;
         
      case I4BCMGR_EVENT_DISC_REQ:
         I4bCmgr_NcciDisconnectReq (pConnData, &(pMsg->data));
         break;
         
      case I4BCMGR_EVENT_ALERT_REQ:
         I4bCmgr_NcciAlertReq (pConnData);
         break;
         
      case I4BCMGR_EVENT_BCH_CONFIG_UP:
         I4bCmgr_NcciBChConfigUp (pConnData, &(pMsg->data));
         break;
         
      case I4BCMGR_EVENT_BCH_CONFIG_DOWN:
         I4bCmgr_NcciBChConfigDown (pConnData);
         break;
         
      case I4BCMGR_EVENT_NCCI_TIMEOUT:
         I4bCmgr_NcciTimeout (pConnData);
         break;
         
      case I4BCMGR_EVENT_PLCI_TIMEOUT:
         I4bCmgr_PlciTimeout (pConnData);
         break;
         
      default:
         DBG (LOG_ERROR, "Got invalid event %d", pMsg->event);
         break;
   }
   
} /* HandleI4bMessage */





/*
        Handling of module load/unload events
        -------------------------------------
*/

static int i4bcapimgr_modevent
   (module_t  mod,
    int       iType,
    void     *pUnused)
{
   switch (iType)
   {
      case MOD_LOAD:
         DBG (LOG_DEBUG, "Got modevent \"MOD_LOAD\"");
         if (! g_fLoadEventReceived)
         {
            snprintf (g_szVersion, sizeof (g_szVersion), "%d.%d",
                      I4BCMGR_MODULE_VERSION_MAJOR,
                      I4BCMGR_MODULE_VERSION_MINOR);
            RegisterAllControllers ();
         }
         g_fLoadEventReceived = 1;
         break;
         
      case MOD_UNLOAD:
         DBG (LOG_DEBUG, "Got modevent \"MOD_UNLOAD\"");
         if (g_fLoadEventReceived)
         {
            g_fLoadEventReceived = 0;
            AbortAllConnections ();
            StopWorkThread ();
            UnregisterAllControllers ();
         }
         break;
         
      case MOD_SHUTDOWN:
         DBG (LOG_DEBUG, "Got modevent \"MOD_SHUTDOWN\"");
         AbortAllConnections ();
         break;
         
      default:
         /* event not handled or unknown */
         DBG (LOG_DEBUG, "Got unknown modevent %d", iType);
         break;
   }
   
   return (0);
} /* i4bcapimgr_modevent */
