/**
 * @file i4bcmgr_nccism.c
 *
 * I4BCMgr-NcciSM - NCCI state machine.
 *
 * Copyright: 2000-2004 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: i4bcmgr_nccism.c,v 1.1.2.1 2006/02/28 21:35:28 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/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h> /* for net/if.h */
#include <net/if.h> /* struct ifqueue */
#include <capi20.h>
#include <capi_bsd.h>
#include <i4b_cause.h>
#include <i4b_ioctl.h>

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

#define __I4BCMGR_NCCISM__

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





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





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





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

#define NCCI_MAX_DATA_SEND_RETRIES      (15 * 8)

#define TEL_IDLE_MIN                    (BCH_MAX_DATALEN / 2)



/* --- NCCI timeout values for several situations --- */

#define WAIT_FOR_CONNECT_B3_CONF_TIMEOUT        (5 * hz) /* 5 seconds */
#define WAIT_FOR_CONNECT_B3_IND_TIMEOUT         (30 * hz) /* 30 seconds */
#define WAIT_FOR_CONNECT_B3_ACTIVE_TIMEOUT      (30 * hz) /* 30 seconds */
#define WAIT_FOR_DISCONNECT_B3_IND_TIMEOUT      (30 * hz) /* 30 seconds */
#define WAIT_FOR_PLCI_CONNECT_DOWN_TIMEOUT      (5 * hz) /* 5 seconds */
#define WAIT_FOR_DATA_RESEND_TIMEOUT            (hz / 8) /* 125 ms */



/* --- possible events for the CAPI NCCI state machine --- */

/* Important: These values are used to index the array for translation into
 *            strings. If the enum definition is modified, the mentioned array
 *            must be modified, too.
 */
 
typedef enum
{
   /* events for communication with i4b layer 4 */
   NCCISM_EVENT_I4B_DISC_REQ = 0,
   NCCISM_EVENT_I4B_BCH_CONFIG_UP,
   NCCISM_EVENT_I4B_BCH_CONFIG_DOWN,
   NCCISM_EVENT_I4B_BCH_TX_START,
   NCCISM_EVENT_TIMEOUT,
   /* events from the PLCI layer */
   NCCISM_EVENT_PLCI_CONN_UP,
   NCCISM_EVENT_PLCI_CONN_DOWN,
   /* now the events for incoming CAPI messages */
   NCCISM_EVENT_CAPI_CONN_B3_CONF,
   NCCISM_EVENT_CAPI_CONN_B3_IND,
   NCCISM_EVENT_CAPI_CONN_B3_ACT_IND,
   NCCISM_EVENT_CAPI_DISC_B3_CONF,
   NCCISM_EVENT_CAPI_DISC_B3_IND,
   NCCISM_EVENT_CAPI_DATA_B3_CONF,
   NCCISM_EVENT_CAPI_DATA_B3_IND,
   NCCISM_EVENT_CAPI_FACILITY_CONF,
   NCCISM_EVENT_CAPI_FACILITY_IND,
   NUM_NCCISM_EVENTS
} NcciEventType_t;



/* --- translate CAPI message commands into events --- */

static NcciEventType_t g_aCapiCmdToEvent [] =
   {
      NCCISM_EVENT_CAPI_FACILITY_CONF,  /* Facility-Conf */
      NCCISM_EVENT_CAPI_FACILITY_IND,   /* Facility-Ind */
      NUM_NCCISM_EVENTS,                /* no command 0x81 - confirmation */
      NUM_NCCISM_EVENTS,                /* no command 0x81 - indication */
      NCCISM_EVENT_CAPI_CONN_B3_CONF,   /* Connect-B3-Conf */
      NCCISM_EVENT_CAPI_CONN_B3_IND,    /* Connect-B3-Ind */
      NUM_NCCISM_EVENTS,                /* Connect-B3-Active-Conf, does not
                                         * exist
                                         */
      NCCISM_EVENT_CAPI_CONN_B3_ACT_IND,/* Connect-B3-Active-Ind */
      NCCISM_EVENT_CAPI_DISC_B3_CONF,   /* Disconnect-B3-Conf */
      NCCISM_EVENT_CAPI_DISC_B3_IND,    /* Disconnect-B3-Ind */
      NUM_NCCISM_EVENTS,                /* no command 0x85 - confirmation */
      NUM_NCCISM_EVENTS,                /* no command 0x85 - indication */
      NCCISM_EVENT_CAPI_DATA_B3_CONF,   /* Data-B3-Conf */
      NCCISM_EVENT_CAPI_DATA_B3_IND,    /* Data-B3-Ind */
      NUM_NCCISM_EVENTS,                /* Reset-B3-Conf, not handled in sm */
      NUM_NCCISM_EVENTS,                /* Reset-B3-Ind, not handled in sm */
      NUM_NCCISM_EVENTS,                /* Connect-B3-T90-Active-Conf, does not
                                         * exist
                                         */
      NUM_NCCISM_EVENTS                 /* Connect-B3-T90-Active-Ind, not
                                         * handled in sm
                                         */
   };



/* --- the result of the state machine action functions --- */

typedef enum
{
   SMRES_SUCCESS,
   SMRES_FAILURE,
   SMRES_NO_STATE_TRANSIT,
   SMRES_FATAL
} StateMachineResult_t;



/* --- the definition of an entry in the state table --- */

typedef struct
{
   I4bCapiNcciState_t initialState;
   StateMachineResult_t (*pfnAction)
      (I4bCapiConnectionData_t  *pConnData,
       NcciEventType_t           event,
       CAPIMsg_t                *pCapiMsg,
       const I4bCapiEventData_t *pAddInfo);
   I4bCapiNcciState_t successState;
   I4bCapiNcciState_t failureState;
} StateMachineAction_t;



/* --- translation table for NCCI states into strings --- */

/* Important: The array is indexed by the NCCI state enum values! */
static const char *g_apszStateNames [] =
   {
      "N-0",
      "N-0.1",
      "N-1",
      "N-2",
      "N-ACT",
      "N-3",
      "N-4",
      "N-5"
   };



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

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





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





/* --- functions around the NCCI state machine --- */

/* main event handling function for the NCCI state machine */
static void HandleEvent
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* initialize the state variables for the NCCI layer */
static void InitStateVariables
   (I4bCapiConnectionData_t *pConnData);

/* the timer function */
static void NcciTimeout
   (I4bCapiConnectionData_t *pConnData);

/* create an NcCI state machine event from a CAPI message */
static NcciEventType_t GetEventFromCapiMsg
   (const CAPIMsg_t *pCapiMsg);

/* perform a state transit with log output */
static void DoStateTransit
   (I4bCapiConnectionData_t *pConnData,
    I4bCapiNcciState_t       newState);



/* --- the state machine functions --- */

/* handle i4b request to disable B-channel */
static StateMachineResult_t HandleBChConfigDown
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* handle i4b request to disable B-channel in state N-0.1 */
static StateMachineResult_t HandleBChConfigDownInN0_1
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* handle i4b request to start sending data */
static StateMachineResult_t HandleBChTxStart
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* terminate current connection on PLCI layer */
static StateMachineResult_t SendDisconnectReq
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* handle signal for established PLCI connection */
static StateMachineResult_t HandlePlciConnectUp
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* handle signal for terminated (PLCI) connection */
static StateMachineResult_t ReportConnectDown
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* handle acknowledge for active connection establishment */
static StateMachineResult_t HandleConnectB3Conf
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* handle new incoming B3-level connection */
static StateMachineResult_t HandleConnectB3Ind
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* terminate current connection on NCCI level */
static StateMachineResult_t SendDisconnectB3Req
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* handle acknowledge for active disconnect */
static StateMachineResult_t HandleDisconnectB3Conf
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* handle signal for disconnected B3-level */
static StateMachineResult_t HandleDisconnectB3Ind
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* handle signal for completely established B3-connection */
static StateMachineResult_t HandleConnectB3ActiveInd
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* handle acknowledge for sent data block */
static StateMachineResult_t HandleDataB3Conf
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* handle incoming data block */
static StateMachineResult_t HandleDataB3Ind
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* ignore an incoming event */
static StateMachineResult_t IgnoreEvent
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* timeout occurred waiting for Connect-B3-Ind */
static StateMachineResult_t HandleConnectB3IndTimeout
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* timeout occurred waiting for Connect-B3-Conf */
static StateMachineResult_t HandleConnectB3ConfTimeout
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* timeout occurred waiting for Connect-B3-Active-Ind */
static StateMachineResult_t HandleConnectB3ActiveTimeout
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* timeout occurred waiting to send the next data block */
static StateMachineResult_t HandleSendDataTimeout
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);

/* timeout occurred waiting for Disconnect-B3-Ind */
static StateMachineResult_t HandleActiveDisconnectTimeout
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo);



/* --- miscellaneous --- */

/* send the next available data block */
static StateMachineResult_t SendNextDataBlock
   (I4bCapiConnectionData_t *pConnData);
   
/* send a dummy response for an incoming CAPI message */
static void SendDummyResponse
   (I4bCapiConnectionData_t *pConnData,
    const CAPIMsg_t         *pCapiIndMsg);

/* check for silence in voice data stream */
static int CheckForSilence
   (const unsigned char *pabData,
    size_t               nLenData);

/* clean an mbuf queue */
static void CleanIfQueue
   (struct ifqueue *pIfq);

/* translate NCCI state into string */
static const char *GetNcciStateName
   (I4bCapiNcciState_t state);

/* translate NCCI event into string */
static const char *GetNcciEventName
   (NcciEventType_t event);



/* --- the CAPI NCCI state table --- */

/* must go here after the state machine functions */

static StateMachineAction_t g_aStateTable [NUM_NCCI_STATES]
                                          [NUM_NCCISM_EVENTS] =
   {
      /* state N-0 */
      {
         /* event I4B-Disconnect-Req */
         {
            NCCI_STATE_N0, SendDisconnectReq,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event I4B-BCh-Config-Up */
         {
            NCCI_STATE_N0, NULL,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event I4B-BCh-Config-Down */
         {
            NCCI_STATE_N0, IgnoreEvent,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event I4B-BCh-Tx-Start */
         {
            NCCI_STATE_N0, NULL,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event Timeout */
         {
            NCCI_STATE_N0, HandleConnectB3IndTimeout,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event PLCI-Connect-Up */
         {
            NCCI_STATE_N0, HandlePlciConnectUp,
            NCCI_STATE_N0_1, NCCI_STATE_N0
         },
         /* event PLCI-Connect-Down */
         {
            NCCI_STATE_N0, ReportConnectDown,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event C-Connect-B3-Conf */
         {
            NCCI_STATE_N0, IgnoreEvent,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event C-Connect-B3-Ind */
         {
            NCCI_STATE_N1, HandleConnectB3Ind,
            NCCI_STATE_N2, NCCI_STATE_N4
         },
         /* event C-Connect-B3-Active-Ind */
         {
            NCCI_STATE_N0, NULL,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event C-Disconnect-B3-Conf */
         {
            NCCI_STATE_N0, IgnoreEvent,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event C-Disconnect-B3-Ind */
         {
            NCCI_STATE_N0, IgnoreEvent,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event C-Data-B3-Conf */
         {
            NCCI_STATE_N0, IgnoreEvent,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event C-Data-B3-Ind */
         {
            NCCI_STATE_N0, NULL,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event C-Facility-Conf */
         {
            NCCI_STATE_N0, NULL,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event C-Facility-Ind */
         {
            NCCI_STATE_N0, NULL,
            NCCI_STATE_N0, NCCI_STATE_N0
         }
      },
      /* state N-0.1 */
      {
         /* event I4B-Disconnect-Req */
         {
            NCCI_STATE_N0_1, SendDisconnectReq,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event I4B-BCh-Config-Up */
         {
            NCCI_STATE_N0_1, NULL,
            NCCI_STATE_N0_1, NCCI_STATE_N0_1
         },
         /* event I4B-BCh-Config-Down */
         {
            NCCI_STATE_N0_1, HandleBChConfigDownInN0_1,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event I4B-BCh-Tx-Start */
         {
            NCCI_STATE_N0_1, NULL,
            NCCI_STATE_N0_1, NCCI_STATE_N0_1
         },
         /* event Timeout */
         {
            NCCI_STATE_N0_1, HandleConnectB3ConfTimeout,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event PLCI-Connect-Up */
         {
            NCCI_STATE_N0_1, NULL,
            NCCI_STATE_N0_1, NCCI_STATE_N0_1
         },
         /* event PLCI-Connect-Down */
         {
            NCCI_STATE_N0_1, ReportConnectDown,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event C-Connect-B3-Conf */
         {
            NCCI_STATE_N0_1, HandleConnectB3Conf,
            NCCI_STATE_N2, NCCI_STATE_N0
         },
         /* event C-Connect-B3-Ind */
         {
            NCCI_STATE_N0_1, NULL,
            NCCI_STATE_N0_1, NCCI_STATE_N0_1
         },
         /* event C-Connect-B3-Active-Ind */
         {
            NCCI_STATE_N0_1, NULL,
            NCCI_STATE_N0_1, NCCI_STATE_N0_1
         },
         /* event C-Disconnect-B3-Conf */
         {
            NCCI_STATE_N0_1, NULL,
            NCCI_STATE_N0_1, NCCI_STATE_N0_1
         },
         /* event C-Disconnect-B3-Ind */
         {
            NCCI_STATE_N0_1, NULL,
            NCCI_STATE_N0_1, NCCI_STATE_N0_1
         },
         /* event C-Data-B3-Conf */
         {
            NCCI_STATE_N0_1, NULL,
            NCCI_STATE_N0_1, NCCI_STATE_N0_1
         },
         /* event C-Data-B3-Ind */
         {
            NCCI_STATE_N0_1, NULL,
            NCCI_STATE_N0_1, NCCI_STATE_N0_1
         },
         /* event C-Facility-Conf */
         {
            NCCI_STATE_N0_1, NULL,
            NCCI_STATE_N0_1, NCCI_STATE_N0_1
         },
         /* event C-Facility-Ind */
         {
            NCCI_STATE_N0_1, NULL,
            NCCI_STATE_N0_1, NCCI_STATE_N0_1
         }
      },
      /* state N-1 */
      {
         /* event I4B-Disconnect-Req */
         {
            NCCI_STATE_N1, SendDisconnectB3Req,
            NCCI_STATE_N4, NCCI_STATE_N1
         },
         /* event I4B-BCh-Config-Up */
         {
            NCCI_STATE_N1, NULL,
            NCCI_STATE_N1, NCCI_STATE_N1
         },
         /* event I4B-BCh-Config-Down */
         {
            NCCI_STATE_N1, HandleBChConfigDown,
            NCCI_STATE_N4, NCCI_STATE_N1
         },
         /* event I4B-BCh-Tx-Start */
         {
            NCCI_STATE_N1, NULL,
            NCCI_STATE_N1, NCCI_STATE_N1
         },
         /* event Timeout */
         {
            NCCI_STATE_N1, NULL,
            NCCI_STATE_N1, NCCI_STATE_N1
         },
         /* event PLCI-Connect-Up */
         {
            NCCI_STATE_N1, NULL,
            NCCI_STATE_N1, NCCI_STATE_N1
         },
         /* event PLCI-Connect-Down */
         {
            NCCI_STATE_N1, ReportConnectDown,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event C-Connect-B3-Conf */
         {
            NCCI_STATE_N1, NULL,
            NCCI_STATE_N1, NCCI_STATE_N1
         },
         /* event C-Connect-B3-Ind */
         {
            NCCI_STATE_N1, NULL,
            NCCI_STATE_N1, NCCI_STATE_N1
         },
         /* event C-Connect-B3-Active-Ind */
         {
            NCCI_STATE_N1, NULL,
            NCCI_STATE_N1, NCCI_STATE_N1
         },
         /* event C-Disconnect-B3-Conf */
         {
            NCCI_STATE_N1, NULL,
            NCCI_STATE_N1, NCCI_STATE_N1
         },
         /* event C-Disconnect-B3-Ind */
         {
            NCCI_STATE_N5, HandleDisconnectB3Ind,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event C-Data-B3-Conf */
         {
            NCCI_STATE_N1, NULL,
            NCCI_STATE_N1, NCCI_STATE_N1
         },
         /* event C-Data-B3-Ind */
         {
            NCCI_STATE_N1, NULL,
            NCCI_STATE_N1, NCCI_STATE_N1
         },
         /* event C-Facility-Conf */
         {
            NCCI_STATE_N1, NULL,
            NCCI_STATE_N1, NCCI_STATE_N1
         },
         /* event C-Facility-Ind */
         {
            NCCI_STATE_N1, NULL,
            NCCI_STATE_N1, NCCI_STATE_N1
         }
      },
      /* state N-2 */
      {
         /* event I4B-Disconnect-Req */
         {
            NCCI_STATE_N2, SendDisconnectB3Req,
            NCCI_STATE_N4, NCCI_STATE_N2
         },
         /* event I4B-BCh-Config-Up */
         {
            NCCI_STATE_N2, NULL,
            NCCI_STATE_N2, NCCI_STATE_N2
         },
         /* event I4B-BCh-Config-Down */
         {
            NCCI_STATE_N2, HandleBChConfigDown,
            NCCI_STATE_N4, NCCI_STATE_N2
         },
         /* event I4B-BCh-Tx-Start */
         {
            NCCI_STATE_N2, NULL,
            NCCI_STATE_N2, NCCI_STATE_N2
         },
         /* event Timeout */
         {
            NCCI_STATE_N2, HandleConnectB3ActiveTimeout,
            NCCI_STATE_N4, NCCI_STATE_N2
         },
         /* event PLCI-Connect-Up */
         {
            NCCI_STATE_N2, NULL,
            NCCI_STATE_N2, NCCI_STATE_N2
         },
         /* event PLCI-Connect-Down */
         {
            NCCI_STATE_N2, ReportConnectDown,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event C-Connect-B3-Conf */
         {
            NCCI_STATE_N2, NULL,
            NCCI_STATE_N2, NCCI_STATE_N2
         },
         /* event C-Connect-B3-Ind */
         {
            NCCI_STATE_N2, NULL,
            NCCI_STATE_N2, NCCI_STATE_N2
         },
         /* event C-Connect-B3-Active-Ind */
         {
            NCCI_STATE_NACT, HandleConnectB3ActiveInd,
            NCCI_STATE_NACT, NCCI_STATE_N4
         },
         /* event C-Disconnect-B3-Conf */
         {
            NCCI_STATE_N2, NULL,
            NCCI_STATE_N2, NCCI_STATE_N2
         },
         /* event C-Disconnect-B3-Ind */
         {
            NCCI_STATE_N5, HandleDisconnectB3Ind,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event C-Data-B3-Conf */
         {
            NCCI_STATE_N2, NULL,
            NCCI_STATE_N2, NCCI_STATE_N2
         },
         /* event C-Data-B3-Ind */
         {
            NCCI_STATE_N2, NULL,
            NCCI_STATE_N2, NCCI_STATE_N2
         },
         /* event C-Facility-Conf */
         {
            NCCI_STATE_N2, NULL,
            NCCI_STATE_N2, NCCI_STATE_N2
         },
         /* event C-Facility-Ind */
         {
            NCCI_STATE_N2, NULL,
            NCCI_STATE_N2, NCCI_STATE_N2
         }
      },
      /* state N-ACT */
      {
         /* event I4B-Disconnect-Req */
         {
            NCCI_STATE_NACT, SendDisconnectB3Req,
            NCCI_STATE_N4, NCCI_STATE_NACT
         },
         /* event I4B-BCh-Config-Up */
         {
            NCCI_STATE_NACT, IgnoreEvent,
            NCCI_STATE_NACT, NCCI_STATE_NACT
         },
         /* event I4B-BCh-Config-Down */
         {
            NCCI_STATE_NACT, HandleBChConfigDown,
            NCCI_STATE_N4, NCCI_STATE_NACT
         },
         /* event I4B-BCh-Tx-Start */
         {
            NCCI_STATE_NACT, HandleBChTxStart,
            NCCI_STATE_NACT, NCCI_STATE_N4
         },
         /* event Timeout */
         {
            NCCI_STATE_NACT, HandleSendDataTimeout,
            NCCI_STATE_NACT, NCCI_STATE_NACT
         },
         /* event PLCI-Connect-Up */
         {
            NCCI_STATE_NACT, NULL,
            NCCI_STATE_NACT, NCCI_STATE_NACT
         },
         /* event PLCI-Connect-Down */
         {
            NCCI_STATE_NACT, ReportConnectDown,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event C-Connect-B3-Conf */
         {
            NCCI_STATE_NACT, NULL,
            NCCI_STATE_NACT, NCCI_STATE_NACT
         },
         /* event C-Connect-B3-Ind */
         {
            NCCI_STATE_NACT, NULL,
            NCCI_STATE_NACT, NCCI_STATE_NACT
         },
         /* event C-Connect-B3-Active-Ind */
         {
            NCCI_STATE_NACT, NULL,
            NCCI_STATE_NACT, NCCI_STATE_NACT
         },
         /* event C-Disconnect-B3-Conf */
         {
            NCCI_STATE_NACT, HandleDisconnectB3Conf,
            NCCI_STATE_N4, NCCI_STATE_N4
         },
         /* event C-Disconnect-B3-Ind */
         {
            NCCI_STATE_N5, HandleDisconnectB3Ind,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event C-Data-B3-Conf */
         {
            NCCI_STATE_NACT, HandleDataB3Conf,
            NCCI_STATE_NACT, NCCI_STATE_N4
         },
         /* event C-Data-B3-Ind */
         {
            NCCI_STATE_NACT, HandleDataB3Ind,
            NCCI_STATE_NACT, NCCI_STATE_N4
         },
         /* event C-Facility-Conf */
         {
            NCCI_STATE_NACT, NULL,
            NCCI_STATE_NACT, NCCI_STATE_NACT
         },
         /* event C-Facility-Ind */
         {
            NCCI_STATE_NACT, NULL,
            NCCI_STATE_NACT, NCCI_STATE_NACT
         }
      },
      /* state N-3 - Reset-B3 not supported, no entry into this state */
      {
         /* event I4B-Disconnect-Req */
         {
            NCCI_STATE_N3, NULL,
            NCCI_STATE_N3, NCCI_STATE_N3
         },
         /* event I4B-BCh-Config-Up */
         {
            NCCI_STATE_N3, NULL,
            NCCI_STATE_N3, NCCI_STATE_N3
         },
         /* event I4B-BCh-Config-Down */
         {
            NCCI_STATE_N3, NULL,
            NCCI_STATE_N3, NCCI_STATE_N3
         },
         /* event I4B-BCh-Tx-Start */
         {
            NCCI_STATE_N3, NULL,
            NCCI_STATE_N3, NCCI_STATE_N3
         },
         /* event Timeout */
         {
            NCCI_STATE_N3, NULL,
            NCCI_STATE_N3, NCCI_STATE_N3
         },
         /* event PLCI-Connect-Up */
         {
            NCCI_STATE_N3, NULL,
            NCCI_STATE_N3, NCCI_STATE_N3
         },
         /* event PLCI-Connect-Down */
         {
            NCCI_STATE_N3, NULL,
            NCCI_STATE_N3, NCCI_STATE_N3
         },
         /* event C-Connect-B3-Conf */
         {
            NCCI_STATE_N3, NULL,
            NCCI_STATE_N3, NCCI_STATE_N3
         },
         /* event C-Connect-B3-Ind */
         {
            NCCI_STATE_N3, NULL,
            NCCI_STATE_N3, NCCI_STATE_N3
         },
         /* event C-Connect-B3-Active-Ind */
         {
            NCCI_STATE_N3, NULL,
            NCCI_STATE_N3, NCCI_STATE_N3
         },
         /* event C-Disconnect-B3-Conf */
         {
            NCCI_STATE_N3, NULL,
            NCCI_STATE_N3, NCCI_STATE_N3
         },
         /* event C-Disconnect-B3-Ind */
         {
            NCCI_STATE_N3, NULL,
            NCCI_STATE_N3, NCCI_STATE_N3
         },
         /* event C-Data-B3-Conf */
         {
            NCCI_STATE_N3, NULL,
            NCCI_STATE_N3, NCCI_STATE_N3
         },
         /* event C-Data-B3-Ind */
         {
            NCCI_STATE_N3, NULL,
            NCCI_STATE_N3, NCCI_STATE_N3
         },
         /* event C-Facility-Conf */
         {
            NCCI_STATE_N3, NULL,
            NCCI_STATE_N3, NCCI_STATE_N3
         },
         /* event C-Facility-Ind */
         {
            NCCI_STATE_N3, NULL,
            NCCI_STATE_N3, NCCI_STATE_N3
         }
      },
      /* state N-4 */
      {
         /* event I4B-Disconnect-Req */
         {
            NCCI_STATE_N4, SendDisconnectReq,
            NCCI_STATE_N4, NCCI_STATE_N4
         },
         /* event I4B-BCh-Config-Up */
         {
            NCCI_STATE_N4, NULL,
            NCCI_STATE_N4, NCCI_STATE_N4
         },
         /* event I4B-BCh-Config-Down */
         {
            NCCI_STATE_N4, IgnoreEvent,
            NCCI_STATE_N4, NCCI_STATE_N4
         },
         /* event I4B-BCh-Tx-Start */
         {
            NCCI_STATE_N4, IgnoreEvent,
            NCCI_STATE_N4, NCCI_STATE_N4
         },
         /* event Timeout */
         {
            NCCI_STATE_N4, HandleActiveDisconnectTimeout,
            NCCI_STATE_N4, NCCI_STATE_N4
         },
         /* event PLCI-Connect-Up */
         {
            NCCI_STATE_N4, NULL,
            NCCI_STATE_N4, NCCI_STATE_N4
         },
         /* event PLCI-Connect-Down */
         {
            NCCI_STATE_N4, ReportConnectDown,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event C-Connect-B3-Conf */
         {
            NCCI_STATE_N4, HandleDataB3Conf,
            NCCI_STATE_N4, NCCI_STATE_N4
         },
         /* event C-Connect-B3-Ind */
         {
            NCCI_STATE_N4, IgnoreEvent,
            NCCI_STATE_N4, NCCI_STATE_N4
         },
         /* event C-Connect-B3-Active-Ind */
         {
            NCCI_STATE_N4, IgnoreEvent,
            NCCI_STATE_N4, NCCI_STATE_N4
         },
         /* event C-Disconnect-B3-Conf */
         {
            NCCI_STATE_N4, HandleDisconnectB3Conf,
            NCCI_STATE_N4, NCCI_STATE_N4
         },
         /* event C-Disconnect-B3-Ind */
         {
            NCCI_STATE_N5, HandleDisconnectB3Ind,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event C-Data-B3-Conf */
         {
            NCCI_STATE_N4, IgnoreEvent,
            NCCI_STATE_N4, NCCI_STATE_N4
         },
         /* event C-Data-B3-Ind */
         {
            NCCI_STATE_N4, IgnoreEvent,
            NCCI_STATE_N4, NCCI_STATE_N4
         },
         /* event C-Facility-Conf */
         {
            NCCI_STATE_N4, NULL,
            NCCI_STATE_N4, NCCI_STATE_N4
         },
         /* event C-Facility-Ind */
         {
            NCCI_STATE_N4, NULL,
            NCCI_STATE_N4, NCCI_STATE_N4
         }
      },
      /* state N-5 */
      {
         /* event I4B-Disconnect-Req */
         {
            NCCI_STATE_N5, SendDisconnectReq,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event I4B-BCh-Config-Up */
         {
            NCCI_STATE_N5, NULL,
            NCCI_STATE_N5, NCCI_STATE_N5
         },
         /* event I4B-BCh-Config-Down */
         {
            NCCI_STATE_N5, IgnoreEvent,
            NCCI_STATE_N5, NCCI_STATE_N5
         },
         /* event I4B-BCh-Tx-Start */
         {
            NCCI_STATE_N5, NULL,
            NCCI_STATE_N5, NCCI_STATE_N5
         },
         /* event Timeout */
         {
            NCCI_STATE_N5, IgnoreEvent,
            NCCI_STATE_N5, NCCI_STATE_N5
         },
         /* event PLCI-Connect-Up */
         {
            NCCI_STATE_N5, NULL,
            NCCI_STATE_N5, NCCI_STATE_N5
         },
         /* event PLCI-Connect-Down */
         {
            NCCI_STATE_N5, ReportConnectDown,
            NCCI_STATE_N0, NCCI_STATE_N0
         },
         /* event C-Connect-B3-Conf */
         {
            NCCI_STATE_N5, NULL,
            NCCI_STATE_N5, NCCI_STATE_N5
         },
         /* event C-Connect-B3-Ind */
         {
            NCCI_STATE_N5, NULL,
            NCCI_STATE_N5, NCCI_STATE_N5
         },
         /* event C-Connect-B3-Active-Ind */
         {
            NCCI_STATE_N5, NULL,
            NCCI_STATE_N5, NCCI_STATE_N5
         },
         /* event C-Disconnect-B3-Conf */
         {
            NCCI_STATE_N5, NULL,
            NCCI_STATE_N5, NCCI_STATE_N5
         },
         /* event C-Disconnect-B3-Ind */
         {
            NCCI_STATE_N5, NULL,
            NCCI_STATE_N5, NCCI_STATE_N5
         },
         /* event C-Data-B3-Conf */
         {
            NCCI_STATE_N5, NULL,
            NCCI_STATE_N5, NCCI_STATE_N5
         },
         /* event C-Data-B3-Ind */
         {
            NCCI_STATE_N5, NULL,
            NCCI_STATE_N5, NCCI_STATE_N5
         },
         /* event C-Facility-Conf */
         {
            NCCI_STATE_N5, NULL,
            NCCI_STATE_N5, NCCI_STATE_N5
         },
         /* event C-Facility-Ind */
         {
            NCCI_STATE_N5, NULL,
            NCCI_STATE_N5, NCCI_STATE_N5
         }
      }
   };





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





/*
        start a new outgoing connection
        -------------------------------
*/

void I4bCmgr_NcciConnectReq
   (I4bCapiConnectionData_t *pConnData)
{
   /* initialize the connection data for the NCCI layer */
   InitStateVariables (pConnData);
   pConnData->fActiveConnectB3 = 1;

   /* forward the call to the PLCI layer */
   I4bCmgr_PlciConnectReq (pConnData);
   
} /* I4bCmgr_NcciConnectReq */





/*
        acknowledge a new incoming connection
        -------------------------------------
*/

void I4bCmgr_NcciConnectResp
   (I4bCapiConnectionData_t *pConnData,
    I4bCapiEventData_t      *pEventData)
{
   /* just forward the call to the PLCI layer */
   I4bCmgr_PlciConnectResp (pConnData, pEventData);
   
} /* I4bCmgr_NcciConnectResp */





/*
        terminate a connection
        ----------------------
*/

void I4bCmgr_NcciDisconnectReq
   (I4bCapiConnectionData_t *pConnData,
    I4bCapiEventData_t      *pEventData)
{
   /* send the event to the state machine function */
   HandleEvent (pConnData, NCCISM_EVENT_I4B_DISC_REQ, NULL, pEventData);
   
} /* I4bCmgr_NcciDisconnectReq */





/*
        do not let a new incoming connection expire
        -------------------------------------------
*/

void I4bCmgr_NcciAlertReq
   (I4bCapiConnectionData_t *pConnData)
{
   /* just forward the call to the PLCI layer */
   I4bCmgr_PlciAlertReq (pConnData);
   
} /* I4bCmgr_NcciAlertReq */





/*
        set the B-channel protocol configuration
        ----------------------------------------
*/

void I4bCmgr_NcciBChConfigUp
   (I4bCapiConnectionData_t *pConnData,
    I4bCapiEventData_t      *pEventData)
{
   /* send the event to the state machine function */
   HandleEvent (pConnData, NCCISM_EVENT_I4B_BCH_CONFIG_UP, NULL, pEventData);
   
} /* I4bCmgr_NcciBChConfigUp */





/*
        tear down the B-channel connection
        ----------------------------------
*/

void I4bCmgr_NcciBChConfigDown
   (I4bCapiConnectionData_t *pConnData)
{
   /* send the event to the state machine function */
   HandleEvent (pConnData, NCCISM_EVENT_I4B_BCH_CONFIG_DOWN, NULL, NULL);
   
} /* I4bCmgr_NcciBChConfigDown */





/*
        handle timeout for the NCCI layer
        ---------------------------------
*/

void I4bCmgr_NcciTimeout
   (I4bCapiConnectionData_t *pConnData)
{
   /* send the event to the state machine function */
   HandleEvent (pConnData, NCCISM_EVENT_TIMEOUT, NULL, NULL);
   
} /* I4bCmgr_NcciTimeout */





/*
        start sending out data
        ----------------------
*/

void I4bCmgr_NcciTxStart
   (I4bCapiConnectionData_t *pConnData)
{
   /* send the event to the state machine function */
   HandleEvent (pConnData, NCCISM_EVENT_I4B_BCH_TX_START, NULL, NULL);
   
} /* I4bCmgr_NcciTxStart */





/*
        receive signaling of a new incoming connection
        ----------------------------------------------
*/

void I4bCmgr_NcciReceiveConnectInd
   (I4bCapiConnectionData_t *pConnData)
{
   /* initialize the connection data for the NCCI layer */
   InitStateVariables (pConnData);
   pConnData->fActiveConnectB3 = 0;

   /* forward the call to i4b layer 4 */
   i4b_l4_connect_ind (pConnData->pI4bCd);
   
} /* I4bCmgr_NcciReceiveConnectInd */





/*
        receive signalling of connected B-channel
        -----------------------------------------
*/

void I4bCmgr_NcciReceiveConnectionUp
   (I4bCapiConnectionData_t *pConnData)
{
   /* send the event to the state machine function */
   HandleEvent (pConnData, NCCISM_EVENT_PLCI_CONN_UP, NULL, NULL);
   
} /* I4bCmgr_NcciReceiveConnectionUp */





/*
        receive signalling of disconnected B-channel
        --------------------------------------------
*/

void I4bCmgr_NcciReceiveConnectionDown
   (I4bCapiConnectionData_t *pConnData)
{
   /* send the event to the state machine function */
   HandleEvent (pConnData, NCCISM_EVENT_PLCI_CONN_DOWN, NULL, NULL);
   
} /* I4bCmgr_NcciReceiveConnectionDown */





/*
        handle an incoming CAPI message
        -------------------------------
*/

void I4bCmgr_NcciReceiveCapiMessage
   (I4bCapiConnectionData_t *pConnData,
    CAPIMsg_t               *pCapiMsg)
{
   NcciEventType_t event;
   
   /* create NCCI state machine event for this request */
   event = GetEventFromCapiMsg (pCapiMsg);
   if ((size_t) event >= (size_t) NUM_NCCISM_EVENTS)
   {
      /* unknown CAPI message? must be a PLCI layer or CAPI driver error */
      DBG (LOG_ERROR,
           "Cd %u: State %s: Got unknown B3-message 0x%04X, dwCid 0x%08X, wNum 0x%04X",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
           (unsigned) CAPI_GET_CMD (pCapiMsg),
           (unsigned) CAPI_GET_CID (pCapiMsg),
           (unsigned) CAPI_GET_MSGNUM (pCapiMsg));
      SendDummyResponse (pConnData, pCapiMsg);
      return;
   }
   
   /* send the event to the state machine function */
   HandleEvent (pConnData, event, pCapiMsg, NULL);
   
} /* I4bCmgr_NcciReceiveCapiMessage */





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





/*
        main event handling function for the NCCI state machine
        -------------------------------------------------------
*/

static void HandleEvent
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   StateMachineResult_t  res;
   StateMachineAction_t *pAction;
   
   /* test for valid parameters */
   if (! pConnData)
   {
      DBG (LOG_ERROR, "Invalid connection data pointer 0x%p", pConnData);
      return;
   }
   if (pConnData->pI4bCd == NULL)
   {
      DBG (LOG_ERROR, "Controller %u, B-channel %u: no call descriptor linked",
           pConnData->pCtlrData->uCapiCtlrNum, pConnData->uBChn);
      return;
   }
   if ((size_t) (pConnData->ncciState) >= NUM_NCCI_STATES)
   {
      DBG (LOG_ERROR, "Cd %u: Invalid connection state %s",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState));
      return;
   }
   if ((size_t) event >= NUM_NCCISM_EVENTS)
   {
      DBG (LOG_ERROR, "Cd %u: Got invalid event %s",
           pConnData->uCdId, GetNcciEventName (event));
      return;
   }
   
   /* determine action data for current state and event type */
   pAction = &(g_aStateTable [(int) (pConnData->ncciState)] [(int) event]);
   
   /* if there is no action function, the event is not valid in current state
    * --> abort connection; if in state N-0 at the PLCI layer, else on the NCCI
    * layer
    */
   if (pAction->pfnAction == NULL)
   {
      I4bCapiEventData_t eventData;

      DBG (LOG_ERROR, "Cd %u: State %s: Got unexpected event %s",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
           GetNcciEventName (event));

      SET_CAUSE_TV (eventData.discReq.iCause, CAUSET_I4B, CAUSE_I4B_OOO);
      if (pConnData->ncciState == NCCI_STATE_N0 ||
          pConnData->ncciState == NCCI_STATE_N0_1)
      {
         (void) SendDisconnectReq (pConnData, NCCISM_EVENT_I4B_DISC_REQ,
                                   NULL, &eventData);
      }
      else
      {
         (void) SendDisconnectB3Req (pConnData, NCCISM_EVENT_I4B_DISC_REQ,
                                     NULL, &eventData);
      }
      return;
   }
   
   /* first do initial state transit */
   DoStateTransit (pConnData, pAction->initialState);
   
   /* perform action for the event */
   res = (pAction->pfnAction) (pConnData, event, pCapiMsg, pAddInfo);
   
   /* do state transit according to action result */
   switch (res)
   {
      case SMRES_SUCCESS:
         if (pConnData->pI4bCd &&
             pConnData->ncciState == pAction->initialState)
         {
            DoStateTransit (pConnData, pAction->successState);
         }
         break;
         
      case SMRES_FAILURE:
         if (pConnData->pI4bCd &&
             pConnData->ncciState == pAction->initialState)
         {
            DoStateTransit (pConnData, pAction->failureState);
         }
         break;
         
      case SMRES_FATAL:
         if (pConnData->pI4bCd)
         {
            I4bCapiEventData_t eventData;

            SET_CAUSE_TV (eventData.discReq.iCause, CAUSET_I4B, CAUSE_I4B_OOO);
            if (pConnData->ncciState == NCCI_STATE_N0 ||
                pConnData->ncciState == NCCI_STATE_N0_1)
            {
               (void) SendDisconnectReq (pConnData,
                                         NCCISM_EVENT_I4B_DISC_REQ,
                                         NULL, &eventData);
            }
            else
            {
               (void) SendDisconnectB3Req (pConnData,
                                           NCCISM_EVENT_I4B_DISC_REQ,
                                           NULL, &eventData);
            }
         }
         break;
         
      default:
      case SMRES_NO_STATE_TRANSIT:
         break;
   }
   
   DBG (LOG_DEBUG, "Cd %u: State %s: Event %s handled",
        (pConnData->pI4bCd) ? pConnData->uCdId : 0,
        GetNcciStateName (pConnData->ncciState), GetNcciEventName (event));

} /* HandleEvent */





/*
        initialize the state variables for the NCCI layer
        -------------------------------------------------
*/

static void InitStateVariables
   (I4bCapiConnectionData_t *pConnData)
{
   pConnData->ncciState            = NCCI_STATE_N0;
   pConnData->dwNcci               = 0;
   pConnData->fActiveConnectB3     = 0;
   pConnData->fActiveDisconnectB3  = 0;
   
   pConnData->pmbInBuf             = NULL;
   pConnData->rxQueue.ifq_maxlen   = IFQ_MAXLEN;
   CleanIfQueue (&(pConnData->rxQueue));
   pConnData->uRxCount             = 0;
   pConnData->txQueue.ifq_maxlen   = IFQ_MAXLEN;
   CleanIfQueue (&(pConnData->txQueue));
   pConnData->uTxCount             = 0;
   pConnData->pmbOutCapiMsg        = NULL;
   pConnData->uLastDataHandle      = 0;
   pConnData->uConfirmedDataHandle = 0;
   pConnData->uDataBlockRetries    = 0;

   if (! pConnData->fNcciCalloutInitOk)
   {
#ifdef __FreeBSD__
      callout_handle_init (&(pConnData->calloutNcci));
#else /* __FreeBSD__ */
      callout_init (&(pConnData->calloutNcci));
#endif /* __FreeBSD__ */
      pConnData->fNcciCalloutInitOk = 1;
   }
   pConnData->fNcciTimerRunning    = 0;
   
} /* InitStateVariables */





/*
        the timer function
        ------------------
*/

static void NcciTimeout
   (I4bCapiConnectionData_t *pConnData)
{
   I4bCEventQueueEntry_t *pMsg;
   int                    s;
   
   s = SPLI4B ();

   /* check for (still) running timer */
   if (! pConnData->fNcciTimerRunning)
   {
      splx (s);
      DBG (LOG_DEBUG,
           "Cd %u: State %s: Timeout occurred while no timer running",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState));
      return;
   }
   
   /* remember timer not running anymore */
   pConnData->fNcciTimerRunning = 0;
   
   /* send the timeout event to the state machine */
   pMsg = I4bCmgr_GetFreeEventQueueEntry ();
   if (! pMsg)
   {
      splx (s);
      printf ("i4bcapimgr: Cd %u: ERROR: Out of memory for NCCI timeout",
              pConnData->uCdId);
      return;
   }
   pMsg->uCapiCtlrNum = pConnData->pCtlrData->uCapiCtlrNum;
   pMsg->uBChn        = pConnData->uBChn;
   pMsg->uCdId        = pConnData->uCdId;
   pMsg->event        = I4BCMGR_EVENT_NCCI_TIMEOUT;

   TAILQ_INSERT_TAIL (&e_eventQueue, pMsg, links.tqe);
   sema_post (&e_semMsgAvail);
   
   DBG (LOG_DEBUG, "Cd %u: NCCI timeout enqueued", pConnData->uCdId);
   
   splx (s);
   
} /* NcciTimeout */





/*
        create an NCCI state machine event from a CAPI message
        ------------------------------------------------------
        Note: The CAPI message is assumed to be an incoming message, i.e. a
              indication or a confirmation.
*/

static NcciEventType_t GetEventFromCapiMsg
   (const CAPIMsg_t *pCapiMsg)
{
   unsigned uCmd;
   unsigned uRawCmd;
   unsigned uIdx;
   
   uCmd = (unsigned) C_GET_WORD (pCapiMsg->head.wCmd);
   uRawCmd = (uCmd & CAPI_CMDMASK_COMMAND);
   if (uRawCmd >= 0x80 &&
       (uRawCmd - 0x80) < ARRAY_COUNT (g_aCapiCmdToEvent) / 2)
   {
      /* this is a NCCI message, translate it into events for confirmations and
       * indications
       */
      uIdx = (uRawCmd - 0x80) * 2 +
             (((uCmd & CAPI_CMDMASK_SUBCMD) == C_CONF)
              ? 0 : 1);
      return (g_aCapiCmdToEvent [uIdx]);
   }
   
   /* every other message is not a B3-level message */
   return (NUM_NCCISM_EVENTS);
} /* GetEventFromCapiMsg */





/*
        perform a state transit with log output
        ---------------------------------------
*/

static void DoStateTransit
   (I4bCapiConnectionData_t *pConnData,
    I4bCapiNcciState_t       newState)
{
   if (newState == pConnData->ncciState)
   {
      return;
   }
   DBG (LOG_TRACE, "Cd %u: State %s: Moved to state %s",
        pConnData->uCdId,
        GetNcciStateName (pConnData->ncciState),
        GetNcciStateName (newState));
   pConnData->ncciState = newState;
   
} /* DoStateTransit */





/*
        handle i4b request to disable B-channel
        ---------------------------------------
        If disabling of the B-channel is requested, we send a Disconnect-B3-Req
        to terminate the B3-connection. The B-channel itself will not be
        disconnected. We wait for the N_DISCONNECT_REQUEST or the
        PLCI-Connection-Down event.
        
        Maybe support for re-enabling the B3-connection will be supported in
        the future. Then it would also be possible to switch the B-channel
        protocols (whatever this is usefull for in the i4b context).
*/

static StateMachineResult_t HandleBChConfigDown
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   struct mbuf *pmbMsg;
   CAPIMsg_t   *pReqMsg;
   unsigned     uRes;
   
   /* stop possibly running timer */
   if (pConnData->fNcciTimerRunning)
   {
      pConnData->fNcciTimerRunning = 0;
      STOP_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData);
   }
   
   DBG (LOG_TRACE,
        "Cd %u: State %s: Got bch_config_down, send Disconnect-B3-Req",
        pConnData->uCdId, GetNcciStateName (pConnData->ncciState));

   /* allocate an mbuf for the CAPI message */
   pmbMsg = kcapi_get_mbuf (sizeof (CAPIMsgHead_t) + 1);
   if (! pmbMsg)
   {
      DBG (LOG_ERROR,
           "Cd %u: State %s: Out of mbufs for sending Disconnect-B3-Req",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState));
      return (SMRES_FATAL);
   }
   pReqMsg = mtod (pmbMsg, CAPIMsg_t *);
   
   /* declare a passive disconnect to not send a Disconnect-Req to/in the PLCI
    * layer
    */
   pConnData->fActiveDisconnectB3 = 0;
   
   /* fill the Disconnect-B3-Req CAPI message */
   C_PUT_WORD (pReqMsg->head.wApp, e_uCapiApplID);
   C_PUT_WORD (pReqMsg->head.wCmd, CAPI_REQUEST (C_DISCONNECT_B3));
   C_PUT_WORD (pReqMsg->head.wNum, pConnData->uBChn);
   C_PUT_DWORD (pReqMsg->head.dwCid, pConnData->dwNcci);
   C_PUT_BYTE (pReqMsg->info.any.b [0], 0);
                                        /* enter empty NCPI structure */
   pmbMsg->m_len = sizeof (pReqMsg->head) + 1;
   C_PUT_WORD (pReqMsg->head.wLen, pmbMsg->m_len);
   
   /* send the message */
   uRes = I4bCmgr_PlciPutB3Msg (pConnData, pmbMsg);
   if (uRes != CAPI_OK)
   {
      DBG (LOG_ERROR,
           "Cd %u: State %s: Error 0x%04X sending Disconnect-B3-Req, dwCid 0x%08X, wNum 0x%04X",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
           uRes, (unsigned) (pConnData->dwNcci), pConnData->uBChn);
      kcapi_free_mbuf (pmbMsg);
      
      /* Disconnect-B3 not possible, try at PLCI layer */
      return (SMRES_FATAL);
   }
   
   /* start timer to wait for Disconnect-B3-Ind */
   pConnData->fNcciTimerRunning = 1;
   START_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData,
                WAIT_FOR_DISCONNECT_B3_IND_TIMEOUT);

   /* suppress warning about unused parameters */
   (void) pCapiMsg;
   (void) pAddInfo;
   
   return (SMRES_SUCCESS);
} /* HandleBChConfigDown */





/*
        handle i4b request to disable B-channel in state N-0.1
        ------------------------------------------------------
        If disabling of the B-channel is requested in state N-0.1, we only can
        disconnect at the PLCI layer. So we just send a Disconnect-Req to that
        layer.
*/

static StateMachineResult_t HandleBChConfigDownInN0_1
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   I4bCapiEventData_t eventData;
   
   /* stop possibly running timer */
   if (pConnData->fNcciTimerRunning)
   {
      pConnData->fNcciTimerRunning = 0;
      STOP_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData);
   }
   
   DBG (LOG_TRACE,
        "Cd %u: State %s: Got bch_config_down, send PLCI-Disconnect-Req",
        pConnData->uCdId, GetNcciStateName (pConnData->ncciState));

   SET_CAUSE_TV (eventData.discReq.iCause, CAUSET_I4B, CAUSE_I4B_NORMAL);
   I4bCmgr_PlciDisconnectReq (pConnData, &eventData);
   
   /* suppress warning about unused parameters */
   (void) event;
   (void) pCapiMsg;
   (void) pAddInfo;
   
   return (SMRES_SUCCESS);
} /* HandleBChConfigDownInN0_1 */





/*
        handle i4b request to start sending data
        ----------------------------------------
*/

static StateMachineResult_t HandleBChTxStart
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   /* suppress warning about unused parameters */
   (void) pCapiMsg;
   (void) pAddInfo;
   
   DBG (LOG_DATAMSG, "Cd %u: State %s: Got bch_tx_start",
        pConnData->uCdId, GetNcciStateName (pConnData->ncciState));

   /* if there is still an unsent data block, the controller is currently busy
    * and we must wait until it is able to handle the next data block (timer
    * should still be running)
    */
   if (pConnData->pmbOutCapiMsg != NULL)
   {
      DBG (LOG_DATAMSG,
           "Cd %u: State %s: Unsent data block (controller busy), do not send next data block now",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState));
      return (SMRES_NO_STATE_TRANSIT);
   }

   /* paranoid: stop possibly running data-resend timer */
   if (pConnData->fNcciTimerRunning)
   {
      pConnData->fNcciTimerRunning = 0;
      STOP_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData);
   }
   
   /* now finally start sending the first data block in the transmit queue */
   return (SendNextDataBlock (pConnData));
} /* HandleBChTxStart */





/*
        terminate current connection on PLCI layer
        ------------------------------------------
*/

static StateMachineResult_t SendDisconnectReq
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   DBG (LOG_TRACE, "Cd %u: State %s: Will send PLCI-Disconnect-Req",
        pConnData->uCdId, GetNcciStateName (pConnData->ncciState));

   /* first store the cause value into the call descriptor data */
   pConnData->pI4bCd->cause_out = pAddInfo->discReq.iCause;
   
   /* stop possibly running timer */
   if (pConnData->fNcciTimerRunning)
   {
      pConnData->fNcciTimerRunning = 0;
      STOP_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData);
   }
   
   /* forward the call to the PLCI layer */
   I4bCmgr_PlciDisconnectReq (pConnData, pAddInfo);
   
   /* suppress warning about unused parameters */
   (void) pCapiMsg;
   
   return (SMRES_SUCCESS);
} /* SendDisconnectReq */





/*
        handle signal for established PLCI connection
        ---------------------------------------------
        If the connection is an outgoing one, we now must send the
        Connect-B3-Req to proceed in the NCCI layer. For an incoming connection
        we must wait for the Connect-B3-Ind. A timer must be started to ensure
        the connection establishment will not "hang" in this layer.
*/

static StateMachineResult_t HandlePlciConnectUp
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   struct mbuf          *pmbMsg;
   CAPIMsg_t            *pReqMsg;
   unsigned              uRes;
   StateMachineResult_t  smRes;
   
   /* reset flag for active disconnecting; necessary if the B3-connection may
    * be re-established after disconnecting only at the NCCI layer (currently
    * not supported)
    */
   pConnData->fActiveDisconnectB3 = 0;
   
   /* distinguish between active or passive connection establishment */
   if (pConnData->fActiveConnectB3)
   {
      DBG (LOG_TRACE,
           "Cd %u: State %s: PLCI layer established, start active B3-connect",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState));

      /* allocate an mbuf for the CAPI message */
      pmbMsg = kcapi_get_mbuf (sizeof (CAPIMsgHead_t) + 1);
      if (! pmbMsg)
      {
         DBG (LOG_ERROR,
              "Cd %u: State %s: Out of mbufs for sending Connect-B3-Req",
              pConnData->uCdId, GetNcciStateName (pConnData->ncciState));
         return (SMRES_FATAL);
      }
      pReqMsg = mtod (pmbMsg, CAPIMsg_t *);

      /* fill the Connect-B3-Req CAPI message */
      C_PUT_WORD (pReqMsg->head.wApp, e_uCapiApplID);
      C_PUT_WORD (pReqMsg->head.wCmd, CAPI_REQUEST (C_CONNECT_B3));
      C_PUT_WORD (pReqMsg->head.wNum, pConnData->uBChn);
      C_PUT_DWORD (pReqMsg->head.dwCid, pConnData->dwPlci);
                                        /* must use the PLCI to get an NCCI */
      C_PUT_BYTE (pReqMsg->info.any.b [0], 0);
                                           /* enter empty NCPI structure */
      pmbMsg->m_len = sizeof (pReqMsg->head) + 1;
      C_PUT_WORD (pReqMsg->head.wLen, pmbMsg->m_len);

      /* send the message */
      uRes = I4bCmgr_PlciPutB3Msg (pConnData, pmbMsg);
      if (uRes != CAPI_OK)
      {
         DBG (LOG_ERROR,
              "Cd %u: State %s: Error 0x%04X sending Connect-B3-Req, dwCid 0x%08X, wNum 0x%04X",
              pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
              uRes, (unsigned) (pConnData->dwPlci), pConnData->uBChn);
         kcapi_free_mbuf (pmbMsg);

         /* Connect-B3 not possible, abort connection at the PLCI layer */
         return (SMRES_FATAL);
      }
      
      /* start timer to wait for Connect-B3-Active-Ind */
      pConnData->fNcciTimerRunning = 1;
      START_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData,
                   WAIT_FOR_CONNECT_B3_CONF_TIMEOUT);

      smRes = SMRES_SUCCESS;
   }
   else
   {
      DBG (LOG_TRACE,
           "Cd %u: State %s: PLCI layer established, wait for incoming B3-connection",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState));

      /* start timer to wait for Connect-B3-Ind */
      pConnData->fNcciTimerRunning = 1;
      START_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData,
                   WAIT_FOR_CONNECT_B3_IND_TIMEOUT);

      smRes = SMRES_NO_STATE_TRANSIT;
   }
   
   /* suppress warning for unused parameters */
   (void) pCapiMsg;
   (void) pAddInfo;
   
   return (smRes);
} /* HandlePlciConnectUp */





/*
        handle signal for terminated (PLCI) connection
        ----------------------------------------------
        This function is called when the NCCI layer is currently idle or has no
        chance (any more) to disconnect itself. So the state of the NCCI layer
        is not relevant to this function. It just has to forward the disconnect
        signal to the i4b layer 4.
*/

static StateMachineResult_t ReportConnectDown
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   /* stop running timer */
   if (pConnData->fNcciTimerRunning)
   {
      pConnData->fNcciTimerRunning = 0;
      STOP_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData);
   }
   
   DBG (LOG_TRACE, "Cd %u: State %s: Disconnect signaled by PLCI layer",
        pConnData->uCdId, GetNcciStateName (pConnData->ncciState));

   /* cleanup queues and mbufs */
   pConnData->ncciState           = NCCI_STATE_N0;
   pConnData->fActiveConnectB3    = 0;
   pConnData->fActiveDisconnectB3 = 0;
   kcapi_free_mbuf (pConnData->pmbInBuf);
   pConnData->pmbInBuf = NULL;
   CleanIfQueue (&(pConnData->rxQueue));
   CleanIfQueue (&(pConnData->txQueue));
   kcapi_free_mbuf (pConnData->pmbOutCapiMsg);
   pConnData->pmbOutCapiMsg = NULL;
   pConnData->pDrvrLinkTab = NULL;
   
   /* forward the event to i4b layer 4 */
   i4b_l4_disconnect_ind (pConnData->pI4bCd);
   
   /* suppress warning for unused parameters */
   (void) pCapiMsg;
   (void) pAddInfo;
   
   return (SMRES_SUCCESS);
} /* ReportConnectDown */





/*
        handle acknowledge for active connection establishment
        ------------------------------------------------------
*/

static StateMachineResult_t HandleConnectB3Conf
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   /* stop running timer */
   if (pConnData->fNcciTimerRunning)
   {
      pConnData->fNcciTimerRunning = 0;
      STOP_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData);
   }
   
   /* set the NCCI value in the connection data */
   pConnData->dwNcci = CAPI_GET_CID (pCapiMsg);
   
   /* check the info value of the CAPI message */
   if (C_GET_WORD (pCapiMsg->info.connect_b3_conf.wInfo) != CAPI_OK)
   {
      DBG (LOG_ERROR,
           "Cd %u: State %s: Error 0x%04X in Connect-B3-Conf, dwCid 0x%08X, wNum 0x%04X",
           pConnData->uCdId, GetNcciStateName (pConnData->dwNcci),
           (unsigned) C_GET_WORD (pCapiMsg->info.connect_b3_conf.wInfo),
           (unsigned) CAPI_GET_CID (pCapiMsg),
           (unsigned) CAPI_GET_MSGNUM (pCapiMsg));
           
      /* B3-connection not possible, abort connection at PLCI level */
      return (SMRES_FATAL);
   }
   
   /* now start the timer to wait for Connect-B3-Active-Ind */
   pConnData->fNcciTimerRunning = 1;
   START_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData,
                WAIT_FOR_CONNECT_B3_ACTIVE_TIMEOUT);
                
   /* suppress warning for unused parameters */
   (void) pAddInfo;
   
   return (SMRES_SUCCESS);
} /* HandleConnectB3Conf */   





/*
        handle new incoming B3-level connection
        ---------------------------------------
*/

static StateMachineResult_t HandleConnectB3Ind
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   struct mbuf   *pmbMsg;
   CAPIMsg_t     *pRespMsg;
   unsigned char *p;
   unsigned       uRes;
   
   /* stop running timer */
   if (pConnData->fNcciTimerRunning)
   {
      pConnData->fNcciTimerRunning = 0;
      STOP_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData);
   }
   
   DBG (LOG_TRACE, "Cd %u: State %s: Got Connect-B3-Ind",
        pConnData->uCdId, GetNcciStateName (pConnData->ncciState));

   /* set the NCCI value in the connection data */
   pConnData->dwNcci = CAPI_GET_CID (pCapiMsg);
   
   /* allocate an mbuf for the CAPI response message */
   pmbMsg = kcapi_get_mbuf (sizeof (CAPIMsgHead_t) +
                            sizeof (CAPIConnectB3Resp_t) +
                            1);
   if (! pmbMsg)
   {
      DBG (LOG_ERROR,
           "Cd %u: State %s: Out of mbufs for sending Connect-B3-Req",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState));
      return (SMRES_FATAL);
   }
   pRespMsg = mtod (pmbMsg, CAPIMsg_t *);
   
   /* fill the Disconnect-B3-Resp CAPI message */
   pRespMsg->head = pCapiMsg->head;
   C_PUT_WORD (pRespMsg->head.wCmd, CAPI_RESPONSE (C_CONNECT_B3));
   C_PUT_WORD (pRespMsg->info.connect_b3_resp.wReject, CAPI_CALL_ACCEPT);
   p = (unsigned char *) &(pRespMsg->info.connect_b3_resp.wReject) +
       sizeof (pRespMsg->info.connect_b3_resp.wReject);
   p = CapiUt_EnterEmptyStruct (p);
   pmbMsg->m_len = (int) (p - (unsigned char *) pRespMsg);
   C_PUT_WORD (pRespMsg->head.wLen, pmbMsg->m_len);
   
   /* send the message */
   uRes = I4bCmgr_PlciPutB3Msg (pConnData, pmbMsg);
   if (uRes != CAPI_OK)
   {
      DBG (LOG_ERROR,
           "Cd %u: State %s: Error 0x%04X sending Connect-B3-Resp, dwCid 0x%08X, wNum 0x%04X",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
           uRes, (unsigned) (pConnData->dwNcci),
           (unsigned) CAPI_GET_MSGNUM (pCapiMsg));
      kcapi_free_mbuf (pmbMsg);
      
      /* unable to send response, abort connection */
      return (SMRES_FATAL);
   }
   
   /* start the timer to wait for Connect-B3-Active-Ind */
   pConnData->fNcciTimerRunning = 1;
   START_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData,
                WAIT_FOR_CONNECT_B3_ACTIVE_TIMEOUT);
                
   return (SMRES_SUCCESS);
} /* HandleConnectB3Ind */





/*
        terminate current connection on NCCI level
        ------------------------------------------
*/

static StateMachineResult_t SendDisconnectB3Req
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   struct mbuf *pmbMsg;
   CAPIMsg_t   *pReqMsg;
   unsigned     uRes;
   
   /* stop running timer */
   if (pConnData->fNcciTimerRunning)
   {
      pConnData->fNcciTimerRunning = 0;
      STOP_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData);
   }
   
   DBG (LOG_TRACE, "Cd %u: State %s: Will disconnect, send Disconnect-B3-Req",
        pConnData->uCdId, GetNcciStateName (pConnData->ncciState));

   /* first store the cause value into the call descriptor data */
   pConnData->pI4bCd->cause_out = pAddInfo->discReq.iCause;
   
   /* allocate an mbuf for the CAPI message */
   pmbMsg = kcapi_get_mbuf (sizeof (CAPIMsgHead_t) + 1);
   if (! pmbMsg)
   {
      DBG (LOG_ERROR,
           "cd %u: state %s: out of mbufs for sending Disconnect-B3-Req",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState));
      return (SMRES_FATAL);
   }
   pReqMsg = mtod (pmbMsg, CAPIMsg_t *);
   
   /* declare an active disconnect to send a Disconnect-Req to/in the PLCI
    * layer after receiving Disconnect-B3-Ind
    */
   pConnData->fActiveDisconnectB3 = 1;
   
   /* fill the Disconnect-B3-Req CAPI message */
   C_PUT_WORD (pReqMsg->head.wApp, e_uCapiApplID);
   C_PUT_WORD (pReqMsg->head.wCmd, CAPI_REQUEST (C_DISCONNECT_B3));
   C_PUT_WORD (pReqMsg->head.wNum, pConnData->uBChn);
   C_PUT_DWORD (pReqMsg->head.dwCid, pConnData->dwNcci);
   C_PUT_BYTE (pReqMsg->info.any.b [0], 0);
                                        /* enter empty NCPI structure */
   pmbMsg->m_len = sizeof (pReqMsg->head) + 1;
   C_PUT_WORD (pReqMsg->head.wLen, pmbMsg->m_len);
   
   /* send the message */
   uRes = I4bCmgr_PlciPutB3Msg (pConnData, pmbMsg);
   if (uRes != CAPI_OK)
   {
      DBG (LOG_ERROR,
           "Cd %u: State %s: Error 0x%04X sending Disconnect-B3-Req, dwCid 0x%08X, wNum 0x%04X",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
           uRes, (unsigned) (pConnData->dwNcci), pConnData->uBChn);
      kcapi_free_mbuf (pmbMsg);
      
      /* Disconnect-B3 not possible, try at PLCI layer */
      return (SMRES_FATAL);
   }
   
   /* start timer to wait for Disconnect-B3-Ind */
   pConnData->fNcciTimerRunning = 1;
   START_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData,
                WAIT_FOR_DISCONNECT_B3_IND_TIMEOUT);

   /* suppress warning about unused parameters */
   (void) pCapiMsg;
   
   return (SMRES_SUCCESS);
} /* SendDisconnectB3Req */





/*
        handle acknowledge for active disconnect
        ----------------------------------------
*/

static StateMachineResult_t HandleDisconnectB3Conf
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   /* check the info parameter of the CAPI message */
   if (C_GET_WORD (pCapiMsg->info.disconnect_b3_conf.wInfo) != CAPI_OK)
   {
      DBG (LOG_ERROR,
           "Cd %u: State %s: Error 0x%04X in Disconnect-B3-Conf, dwCid 0x%08X, wNum 0x%04X",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
           (unsigned) C_GET_WORD (pCapiMsg->info.disconnect_b3_conf.wInfo),
           (unsigned) CAPI_GET_CID (pCapiMsg),
           (unsigned) CAPI_GET_MSGNUM (pCapiMsg));
           
      /* Disconnect at this layer not possible, try at the PLCI layer */
      return (SMRES_FATAL);
   }
   
   /* go on waiting for Disconnect-B3-Ind */
   return (SMRES_SUCCESS);
} /* HandleDisconnectB3Conf */





/*
        handle signal for disconnected B3-level
        ---------------------------------------
*/

static StateMachineResult_t HandleDisconnectB3Ind
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   struct mbuf *pmbMsg;
   CAPIMsg_t   *pRespMsg;
   unsigned     uRes;
   
   /* stop running timer */
   if (pConnData->fNcciTimerRunning)
   {
      pConnData->fNcciTimerRunning = 0;
      STOP_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData);
   }
   
   /* evaluate the reason parameter of the CAPI message */
   /* Note: Some controllers (notably the active ones from AVM) signal a layer
    *       1 error when normally disconnecting for transparent or raw HDLC
    *       connections. The manufacturers interpret this as removing the
    *       physical layer from the non-existing upper layers. I think if there
    *       is no "real" protocol, it can't deliver errors. But we must take
    *       what the manufacturers give us...
    */
   if (C_GET_WORD (pCapiMsg->info.disconnect_b3_ind.wReason) == CAPI_OK ||
       C_GET_WORD (pCapiMsg->info.disconnect_b3_ind.wReason) ==
          CDISC_PROTOCOL_LAYER_1)
   {
      DBG (LOG_TRACE, "Cd %u: Wtate %s: Got Disconnect-B3-Ind, reason O.K.",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState));
   }
   else
   {
      printf ("i4bcapimgr: %s: Cd %u: State %s: Error 0x%04X in Disconnect-B3-Ind, dwCid 0x%08X, wNum 0x%04X",
              __FUNCTION__, pConnData->uCdId,
              GetNcciStateName (pConnData->ncciState),
              (unsigned) C_GET_WORD (pCapiMsg->info.disconnect_b3_ind.wReason),
              (unsigned) CAPI_GET_CID (pCapiMsg),
              (unsigned) CAPI_GET_MSGNUM (pCapiMsg));
   }
   
   /* allocate an mbuf for the CAPI response message */
   pmbMsg = kcapi_get_mbuf (sizeof (CAPIMsgHead_t));
   if (! pmbMsg)
   {
      DBG (LOG_ERROR,
           "Cd %u: State %s: Out of mbufs for sending Disconnect-B3-Resp",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState));
      return (SMRES_FATAL);
   }
   pRespMsg = mtod (pmbMsg, CAPIMsg_t *);
   
   /* fill the Disconnect-B3-Resp CAPI message */
   pRespMsg->head = pCapiMsg->head;
   C_PUT_WORD (pRespMsg->head.wCmd, CAPI_RESPONSE (C_DISCONNECT_B3));
   pmbMsg->m_len = sizeof (pRespMsg->head);
   C_PUT_WORD (pRespMsg->head.wLen, pmbMsg->m_len);
   
   /* send the message */
   uRes = I4bCmgr_PlciPutB3Msg (pConnData, pmbMsg);
   if (uRes != CAPI_OK)
   {
      DBG (LOG_ERROR,
           "Cd %u: State %s: Error 0x%04X sending Disconnect-B3-Resp, dwCid 0x%08X, wNum 0x%04X",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
           uRes, (unsigned) (pConnData->dwNcci),
           (unsigned) CAPI_GET_MSGNUM (pCapiMsg));
      kcapi_free_mbuf (pmbMsg);
      
      /* unable to send response, abort connection */
      return (SMRES_FATAL);
   }
   
   /* for active disconnecting we now must send the Disconnect-Req at the PLCI
    * layer
    */
   if (pConnData->fActiveDisconnectB3)
   {
      I4bCapiEventData_t eventData;
      
      eventData.discReq.iCause = pConnData->pI4bCd->cause_out;
      I4bCmgr_PlciDisconnectReq (pConnData, &eventData);
   }
   /* for passive disconnecting a timer must be started to actively disconnect
    * if Disconnect-Ind is not received
    */
   else
   {
      pConnData->fNcciTimerRunning = 1;
      START_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData,
                   WAIT_FOR_PLCI_CONNECT_DOWN_TIMEOUT);
   }
   
   return (SMRES_SUCCESS);
} /* HandleDisconnectB3Ind */





/*
        handle signal for completely established B3-connection
        ------------------------------------------------------
*/

static StateMachineResult_t HandleConnectB3ActiveInd
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   struct mbuf *pmbMsg;
   CAPIMsg_t   *pRespMsg;
   unsigned     uRes;
   
   /* stop running timer */
   if (pConnData->fNcciTimerRunning)
   {
      pConnData->fNcciTimerRunning = 0;
      STOP_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData);
   }
   
   DBG (LOG_INFO, "Cd %u: State %s: Connection completely established",
        pConnData->uCdId, GetNcciStateName (pConnData->ncciState));
        
   /* reset flags for active connection establishment and disconnecting; maybe
    * connection re-establishment will be supported, so the flags must be reset
    * here
    */
   pConnData->fActiveConnectB3 = 0;
   pConnData->fActiveDisconnectB3 = 0;
   
   /* allocate an mbuf for the CAPI response message */
   pmbMsg = kcapi_get_mbuf (sizeof (CAPIMsgHead_t));
   if (! pmbMsg)
   {
      DBG (LOG_ERROR,
           "Cd %u: State %s: Out of mbufs for sending Connect-B3-Active-Resp",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState));
      return (SMRES_FATAL);
   }
   pRespMsg = mtod (pmbMsg, CAPIMsg_t *);
   
   /* fill the Connect-B3-Active-Resp CAPI message */
   pRespMsg->head = pCapiMsg->head;
   C_PUT_WORD (pRespMsg->head.wCmd, CAPI_RESPONSE (C_CONNECT_B3_ACTIVE));
   pmbMsg->m_len = sizeof (pRespMsg->head);
   C_PUT_WORD (pRespMsg->head.wLen, pmbMsg->m_len);
   
   /* send the message */
   uRes = I4bCmgr_PlciPutB3Msg (pConnData, pmbMsg);
   if (uRes != CAPI_OK)
   {
      DBG (LOG_ERROR,
           "Cd %u: State %s: Error 0x%04X sending Connect-B3-Active-Resp, dwCid 0x%08X, wNum 0x%04X",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
           uRes, (unsigned) (pConnData->dwNcci),
           (unsigned) CAPI_GET_MSGNUM (pCapiMsg));
      kcapi_free_mbuf (pmbMsg);
      
      /* unable to send response, abort connection */
      return (SMRES_FATAL);
   }
   
   /* tell i4b layer 4 the connection is established */
   i4b_l4_connect_active_ind (pConnData->pI4bCd);
   
   return (SMRES_SUCCESS);
} /* HandleConnectB3ActiveInd */





/*
        handle acknowledge for sent data block
        --------------------------------------
*/

static StateMachineResult_t HandleDataB3Conf
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   /* remember the last data block confirmed */
   pConnData->uConfirmedDataHandle =
      C_GET_WORD (pCapiMsg->info.data_b3_conf.wHandle);

   /* stop possibly running data-resend timer */
   if (pConnData->fNcciTimerRunning)
   {
      pConnData->fNcciTimerRunning = 0;
      STOP_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData);
   }
   
   DBG (LOG_DATAMSG, "Cd %u: State %s: Got Data-B3-Confirm, wHandle %u",
        pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
        pConnData->uConfirmedDataHandle);

   /* send next data block if one is ready to send */
   (void) SendNextDataBlock (pConnData);
   
   /* if all data blocks are confirmed, tell the layer 4 driver the send queue
    * is empty
    */
   if (pConnData->uConfirmedDataHandle == pConnData->uLastDataHandle &&
       pConnData->pDrvrLinkTab != NULL &&
       pConnData->pDrvrLinkTab->bch_tx_queue_empty)
   {
      pConnData->pDrvrLinkTab->bch_tx_queue_empty
         (pConnData->pDrvrLinkTab->unit);
   }
   
   return (SMRES_NO_STATE_TRANSIT);
} /* HandleDataB3Conf */





/*
        handle incoming data block
        --------------------------
*/

static StateMachineResult_t HandleDataB3Ind
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   struct mbuf          *pmbMsg;
   struct mbuf          *pmbData;
   CAPIMsg_t            *pRespMsg;
   int                   fSilenceData;
   unsigned              uRes;
   StateMachineResult_t  smRes = SMRES_NO_STATE_TRANSIT;
   
   DBG (LOG_DATAMSG,
        "Cd %u: State %s: Got Data-B3-Ind, wHandle %u, wLen %u",
        pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
        (unsigned) C_GET_WORD (pCapiMsg->info.data_b3_ind.wHandle),
        (unsigned) C_GET_WORD (pCapiMsg->info.data_b3_ind.wLen));

   /* allocate an mbuf for the CAPI response message */
   pmbMsg = kcapi_get_mbuf (sizeof (CAPIMsgHead_t) +
                            sizeof (CAPIDataB3Resp_t));
   if (pmbMsg)
   {
      pRespMsg = mtod (pmbMsg, CAPIMsg_t *);

      /* fill the Data-B3-Resp CAPI message */
      pRespMsg->head = pCapiMsg->head;
      C_PUT_WORD (pRespMsg->head.wCmd, CAPI_RESPONSE (C_DATA_B3));
      C_PUT_WORD (pRespMsg->info.data_b3_resp.wHandle,
                  C_GET_WORD (pCapiMsg->info.data_b3_ind.wHandle));
      pmbMsg->m_len = sizeof (pRespMsg->head) +
                      sizeof (pRespMsg->info.data_b3_resp);
      C_PUT_WORD (pRespMsg->head.wLen, pmbMsg->m_len);

      /* send the message */
      uRes = I4bCmgr_PlciPutB3Msg (pConnData, pmbMsg);
      if (uRes != CAPI_OK)
      {
         DBG (LOG_ERROR,
              "Cd %u: State %s: Error 0x%04X sending Data-B3-Resp, dwCid 0x%08X, wNum 0x%04X",
              pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
              uRes, (unsigned) (pConnData->dwNcci),
              (unsigned) CAPI_GET_MSGNUM (pCapiMsg));
         kcapi_free_mbuf (pmbMsg);

         /* unable to send response, abort connection */
         smRes = SMRES_FATAL;
      }
   }
   else
   {
      DBG (LOG_ERROR,
           "Cd %u: State %s: Out of mbufs for sending Data-B3-Resp",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState));
      smRes = SMRES_FATAL;
   }
   
   /* determine the address of the data block mbuf within the message */
#if defined(__alpha__) || defined (__ia64__) || defined (__amd64__) || defined (__sparc64__)
   pmbData =
      (struct mbuf *) C_GET_QWORD (pCapiMsg->info.data_b3_ind.qwData64);
#else /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
   pmbData = (struct mbuf *) C_GET_DWORD (pCapiMsg->info.data_b3_ind.dwData);
#endif /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */

   /* forward the data block to the layer 4 driver */
   if (pConnData->pDrvrLinkTab != NULL &&
       pConnData->pDrvrLinkTab->bch_rx_data_ready)
   {
      /* extract the data block out of the CAPI message */
#if defined (I4BCAPIMGR_ALLOC_MAX_BDATA_SIZE)

      struct mbuf *pmbTmp;
      
      /* to ensure the maximum data block size assumed in the layer 4 drivers
       * the received data block must be copied into a new mbuf of maximum size
       */
      pmbTmp = pmbData;
      pmbData = kcapi_get_mbuf (BCH_MAX_DATALEN);
      if (! pmbData)
      {
         DBG (LOG_ERROR,
              "Cd %u: State %u: Out of mbufs for copying incoming data block",
              pConnData->uCdId, GetNcciStateName (pConnData->ncciState));
         return (SMRES_FATAL);
      }
      if (pmbTmp != NULL)
      {
         bcopy (pmbTmp->m_data, pmbData->m_data, pmbTmp->m_len);
         pmbData->m_len = pmbData->m_pkthdr.len = pmbTmp->m_len;
      }
      else
      {
         pmbData->m_len = pmbData->m_pkthdr.len = 0;
      }

#else /* defined (I4BCAPIMGR_ALLOC_MAX_BDATA_SIZE) */

      /* we use the received data mbuf directly, therefore the CAPI message
       * must be modified to no let the PLCI layer free the mbuf for the data
       */
      if (! pmbData)
      {
         return (SMRES_NO_STATE_TRANSIT);
      }
      /* don't let the data mbuf be released by the PLCI layer, as it is
       * forwarded to the upper i4b layers
       */
#if defined(__alpha__) || defined (__ia64__) || defined (__amd64__) || defined (__sparc64__)
      C_PUT_QWORD (pCapiMsg->info.data_b3_ind.qwData64, 0);
#else /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
      C_PUT_DWORD (pCapiMsg->info.data_b3_ind.dwData, 0);
#endif /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */

#endif /* defined (I4BCAPIMGR_ALLOC_MAX_BDATA_SIZE) */
      pConnData->uRxCount += pmbData->m_len;

      /* for raw HDLC connections the buffer must be stored as one mbuf */
      if (pConnData->pI4bCd->bprot == BPROT_RHDLC)
      {
         pConnData->pmbInBuf = pmbData;
         fSilenceData = 0;
      }
      /* for telephony connections (no B-channel protocol) the data block must
       * be entered into the receive queue
       */
      else
      {
         IF_VAR;
         
         fSilenceData = CheckForSilence (pmbData->m_data, pmbData->m_len);
         IF_LOCK (&(pConnData->rxQueue));
         if (! _IF_QFULL (&(pConnData->rxQueue)))
         {
            _IF_ENQUEUE (&(pConnData->rxQueue), pmbData);
         }
         else
         {
            DBG (LOG_ERROR,
                 "Cd %u: State %s: Rx queue full, unable to store data block",
                 pConnData->uCdId, GetNcciStateName (pConnData->ncciState));
            kcapi_free_mbuf (pmbData);
         }
         IF_UNLOCK (&(pConnData->rxQueue));
      }
      
      /* tell the layer 4 driver there is data to fetch */
      pConnData->pDrvrLinkTab->bch_rx_data_ready
         (pConnData->pDrvrLinkTab->unit);
         
      /* cleanup for raw HDLC connections: must declare the input buffer as
       * non-existing
       */
      if (pConnData->pI4bCd->bprot == BPROT_RHDLC)
      {
         pConnData->pmbInBuf = NULL;
      }
      
      /* signal B-channel activity to the layer 4 driver */
      if (pConnData->pI4bCd->bprot != BPROT_NONE || ! fSilenceData)
      {
         if (pConnData->pDrvrLinkTab->bch_activity)
         {
            pConnData->pDrvrLinkTab->bch_activity
               (pConnData->pDrvrLinkTab->unit, ACT_RX);
         }
      }
   }
   else
   {
      DBG (LOG_ERROR,
           "Cd %u: State %s: Unable to handle data block, no driver link tab",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState));
      smRes = SMRES_FATAL;
   }
   
   return (smRes);
} /* HandleDataB3Ind */





/*
        ignore an incoming event
        ------------------------
*/

static StateMachineResult_t IgnoreEvent
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   /* if the event is a CAPI message, we must send a dummy response message if
    * it is an indication
    */
   if (event >= NCCISM_EVENT_CAPI_CONN_B3_CONF)
   {
      DBG (LOG_DEBUG, "Cd %u: State %s: Ignoring CAPI message 0x%04X",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
           CAPI_GET_CMD (pCapiMsg));

      SendDummyResponse (pConnData, pCapiMsg);
   }
   else
   {
      DBG (LOG_DEBUG, "Cd %u: State %s: Ignoring event %s",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
           GetNcciEventName (event));
   }
   
   /* suppress warning about unused parameters */
   (void) pAddInfo;
   
   return (SMRES_SUCCESS);
} /* IgnoreEvent */





/*
        timeout occurred waiting for Connect-B3-Ind
        -------------------------------------------
*/

static StateMachineResult_t HandleConnectB3IndTimeout
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   DBG (LOG_ERROR,
        "Cd %u: State %s: Timeout waiting for Connect-B3-Ind, dwCid 0x%08X",
        pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
        pConnData->dwPlci);

   /* suppress warning about unused parameters */
   (void) pCapiMsg;
   (void) pAddInfo;
   
   /* abort the current connection */
   return (SMRES_FATAL);
} /* HandleConnectB3IndTimeout */





/*
        timeout occurred waiting for Connect-B3-Conf
        --------------------------------------------
*/

static StateMachineResult_t HandleConnectB3ConfTimeout
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   DBG (LOG_ERROR,
        "Cd %u: State %s: Timeout waiting for Connect-B3-Conf, dwCid 0x%08X",
        pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
        pConnData->dwPlci);

   /* suppress warning about unused parameters */
   (void) pCapiMsg;
   (void) pAddInfo;
   
   /* abort the current connection */
   return (SMRES_FATAL);
} /* HandleConnectB3ConfTimeout */





/*
        timeout occurred waiting for Connect-B3-Active-Ind
        --------------------------------------------------
*/

static StateMachineResult_t HandleConnectB3ActiveTimeout
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   DBG (LOG_ERROR,
        "Cd %u: State %s: Timeout waiting for Connect-B3-Active-Ind, dwCid 0x%08X",
        pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
        (unsigned) (pConnData->dwNcci));

   /* suppress warning about unused parameters */
   (void) pCapiMsg;
   (void) pAddInfo;
   
   /* abort the current connection */
   return (SMRES_FATAL);
} /* HandleConnectB3ActiveTimeout */





/*
        timeout occurred waiting to send the next data block
        ----------------------------------------------------
*/

static StateMachineResult_t HandleSendDataTimeout
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   DBG (LOG_DATAMSG,
        "Cd %u: State %s: Timer expired, do Data-B3-Req attempt %u of %u, dwCid 0x%08X",
        pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
        pConnData->uDataBlockRetries, NCCI_MAX_DATA_SEND_RETRIES,
        (unsigned) (pConnData->dwNcci));

   /* suppress warning about unused parameters */
   (void) pCapiMsg;
   (void) pAddInfo;
   
   /* next send attempt for the current data block */
   return (SendNextDataBlock (pConnData));
} /* HandleSendDataTimeout */





/*
        timeout occurred waiting for Disconnect-B3-Ind
        ----------------------------------------------
*/

static StateMachineResult_t HandleActiveDisconnectTimeout
   (I4bCapiConnectionData_t  *pConnData,
    NcciEventType_t           event,
    CAPIMsg_t                *pCapiMsg,
    const I4bCapiEventData_t *pAddInfo)
{
   DBG (LOG_ERROR,
        "Cd %u: State %s: Timeout waiting for Disonnect-B3-Ind, dwCid 0x%08X",
        pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
        (unsigned) (pConnData->dwNcci));

   /* suppress warning about unused parameters */
   (void) pCapiMsg;
   (void) pAddInfo;
   
   /* abort the current connection (at the PLCI layer) */
   return (SMRES_FATAL);
} /* HandleAciveDisconnectTimeout */





/*
        send the next available data block
        ----------------------------------
*/

static StateMachineResult_t SendNextDataBlock
   (I4bCapiConnectionData_t *pConnData)
{
   struct mbuf *pmbData;
   CAPIMsg_t   *pCapiMsg;
   int          fQueueEmpty;
   size_t       nLen;
   unsigned     uRes;
   IF_VAR;
   
   /* empty the transfer queue */
   IF_LOCK (&(pConnData->txQueue));
   fQueueEmpty = (_IF_QLEN (&(pConnData->txQueue)) == 0);
   IF_UNLOCK (&(pConnData->txQueue));
   while (pConnData->pmbOutCapiMsg || ! fQueueEmpty)
   {
      /* check if there is already a block prepared for sending (i.e. not the
       * first send attempt now)
       */
      if (pConnData->pmbOutCapiMsg)
      {
         /* data block present --> declare next send attempt */
         pConnData->uDataBlockRetries++;
         
         DBG (LOG_DATAMSG, "Cd %u: State %s: Will resend data block %u",
              pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
              (unsigned) C_GET_WORD (((CAPIMsg_t *)
                                         (pConnData->pmbOutCapiMsg->m_data))
                                        ->info.data_b3_req.wHandle));
      }
      else
      {
         /* no current output buffer --> allocate a new mbuf for the CAPI
          * message Data-B3-Request itself
          */
         pConnData->pmbOutCapiMsg = kcapi_get_mbuf
                                       (sizeof (CAPIMsgHead_t) +
                                        sizeof (CAPIDataB3Req_t));
         if (! pConnData->pmbOutCapiMsg)
         {
            DBG (LOG_ERROR,
                 "Cd %u: State %s: Out of mbufs for sending Data-B3-Req, dwCid 0x%08X",
                 pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
                 (unsigned) (pConnData->dwNcci));
            return (SMRES_FATAL);
         }

         /* extract the next data block out of the transmit queue; if there is
          * an mbuf chain, only take the head and put the rest back to the front
          * of the queue
          */
         IF_LOCK (&(pConnData->txQueue));
         _IF_DEQUEUE (&(pConnData->txQueue), pmbData);
         if (pmbData != NULL && pmbData->m_next != NULL)
         {
            struct mbuf *pmbTmp;
            
            DBG (LOG_DEBUG,
                 "Cd %u: State %s: Mbuf chain detected",
                 pConnData->uCdId, GetNcciStateName (pConnData->ncciState));
                 
            pmbTmp = pmbData->m_next;
            pmbData->m_next = NULL;
            _IF_PREPEND (&(pConnData->txQueue), pmbTmp);
         }
         IF_UNLOCK (&(pConnData->txQueue));
         if (pmbData == NULL)
         {
            /* queue is not empty but got no data mbuf??? */
            kcapi_free_mbuf (pConnData->pmbOutCapiMsg);
            pConnData->pmbOutCapiMsg = NULL;
            break;
         }

         /* increment the data handle to use for the current data block */
         pConnData->uLastDataHandle++;
         
         /* for the current data block it is the first send attempt */
         pConnData->uDataBlockRetries = 0;
         
         /* create the CAPI message to send the data */
         pCapiMsg = mtod (pConnData->pmbOutCapiMsg, CAPIMsg_t *);
         C_PUT_WORD (pCapiMsg->head.wApp, e_uCapiApplID);
         C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_REQUEST (C_DATA_B3));
         C_PUT_WORD (pCapiMsg->head.wNum, pConnData->uBChn);
         C_PUT_DWORD (pCapiMsg->head.dwCid, pConnData->dwNcci);
         C_PUT_WORD (pCapiMsg->info.data_b3_req.wLen, pmbData->m_len);
         C_PUT_WORD (pCapiMsg->info.data_b3_req.wHandle,
                     pConnData->uLastDataHandle);
         C_PUT_WORD (pCapiMsg->info.data_b3_req.wFlags, 0);
         /* the data block will be transfered by the m_next member of the
          * message mbuf
          */
         C_PUT_DWORD (pCapiMsg->info.data_b3_req.dwData, 0);
         C_PUT_QWORD (pCapiMsg->info.data_b3_req.qwData64, 0);
         pConnData->pmbOutCapiMsg->m_next = pmbData;
         pConnData->pmbOutCapiMsg->m_len =
            sizeof (pCapiMsg->head) + sizeof (pCapiMsg->info.data_b3_req);
         C_PUT_WORD (pCapiMsg->head.wLen, pConnData->pmbOutCapiMsg->m_len);
      }

      /* send the Data-B3-Req */
      nLen = (pConnData->pmbOutCapiMsg == NULL)
                ? 0
                : C_GET_WORD
                     (mtod (pConnData->pmbOutCapiMsg, CAPIMsg_t *)
                         ->info.data_b3_req.wLen);
      uRes = I4bCmgr_PlciPutB3Msg (pConnData, pConnData->pmbOutCapiMsg);
      if (uRes == CME_PUT_QUEUE_FULL || uRes == CME_BUSY)
      {
         /* controller currently busy */
         
         /* check if send attempts are exhausted */
         if (pConnData->uDataBlockRetries >= NCCI_MAX_DATA_SEND_RETRIES)
         {
            DBG (LOG_ERROR,
                 "Cd %u: State %s: Error 0x%04X doing Data-B3-Req send attempt %u, wHandle %u, wLen %u, dwCid 0x%08X",
                 pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
                 uRes,
                 pConnData->uDataBlockRetries,
                 pConnData->uLastDataHandle,
                 (unsigned) nLen,
                 (unsigned) (pConnData->dwNcci));
            kcapi_free_mbuf (pConnData->pmbOutCapiMsg);
            pConnData->pmbOutCapiMsg = NULL;
            return (SMRES_FATAL);
         }
         
         /* start timer for next send attempt */
         DBG (LOG_DATAMSG,
              "Cd %u: State %s: Result 0x%04X doing Data-B3-Req sent attempt %u, wHandle %u, wLen %u, dwCid 0x%08X, start timeout for next send attempt",
              pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
              uRes,
              pConnData->uDataBlockRetries,
              pConnData->uLastDataHandle,
              (unsigned) nLen,
              (unsigned) (pConnData->dwNcci));
         START_TIMER (pConnData->calloutNcci, NcciTimeout, pConnData,
                      WAIT_FOR_DATA_RESEND_TIMEOUT);
         return (SMRES_NO_STATE_TRANSIT);
      }
      else if (uRes != CAPI_OK)
      {
         DBG (LOG_ERROR,
              "Cd %u: State %s: Error 0x%04X doing Data-B3-Req send attempt %u, wHandle %u, wLen %u, dwCid 0x%08X",
              pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
              uRes,
              pConnData->uDataBlockRetries,
              pConnData->uLastDataHandle,
              (unsigned) nLen,
              (unsigned) (pConnData->dwNcci));
         kcapi_free_mbuf (pConnData->pmbOutCapiMsg);
         pConnData->pmbOutCapiMsg = NULL;
         return (SMRES_FATAL);
      }
      else
      {
         /* data block sent, inform layer 4 driver of B-channel activity */

         DBG (LOG_DATAMSG,
              "Cd %u: State %s: Data-B3-Req sent, wHandle %u, wLen %u",
              pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
              pConnData->uLastDataHandle, (unsigned) nLen);

         if (pConnData->pDrvrLinkTab != NULL &&
             pConnData->pDrvrLinkTab->bch_activity)
         {
            pConnData->pDrvrLinkTab->bch_activity
               (pConnData->pDrvrLinkTab->unit, ACT_TX);
         }
         pConnData->pmbOutCapiMsg = NULL;
         pConnData->uTxCount += nLen;
      }
   }
   
   DBG (LOG_DATAMSG, "Cd %u: State %s: All queued data blocks sent",
        pConnData->uCdId, GetNcciStateName (pConnData->ncciState));

   return (SMRES_NO_STATE_TRANSIT);
} /* SendNextDataBlock */





/*
        send a dummy response for an incoming CAPI message
        --------------------------------------------------
*/

static void SendDummyResponse
   (I4bCapiConnectionData_t *pConnData,
    const CAPIMsg_t         *pCapiIndMsg)
{
   struct mbuf   *pmbMsg;
   CAPIMsg_t     *pRespMsg;
   unsigned char *p;
   unsigned       uCmd;
   unsigned       uRes;
   
   /* check if the message is really an indication */
   uCmd = CAPI_GET_CMD (pCapiIndMsg);
   if ((uCmd & CAPI_CMDMASK_SUBCMD) != C_IND)
   {
      return;
   }
   
   /* get an mbuf for the CAPI message; as the real length is unknown yet, use
    * the maximum length of a CAPI message
    */
   pmbMsg = kcapi_get_mbuf (sizeof (CAPIMsg_t));
   if (! pmbMsg)
   {
      DBG (LOG_ERROR,
           "Cd %u: State %s: Out of mbufs sending dummy response for CAPI message 0x%04X, dwCid 0x%08X, wNum 0x%04X",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
           uCmd, (unsigned) CAPI_GET_CID (pCapiIndMsg),
           (unsigned) CAPI_GET_MSGNUM (pCapiIndMsg));
      return;
   }
   pRespMsg = mtod (pmbMsg, CAPIMsg_t *);
   
   /* some messages must be handled specially */
   if (uCmd == CAPI_INDICAT (C_CONNECT_B3))
   {
      /* fill CAPI message Connect-Resp */
      pRespMsg->head = pCapiIndMsg->head;
      C_PUT_WORD (pRespMsg->head.wCmd, CAPI_RESPONSE (C_CONNECT_B3));
      C_PUT_WORD (pRespMsg->info.connect_b3_resp.wReject,
                  CAPI_CALL_REJECT_NORMAL_CLEARING);
      p = (unsigned char *) &(pRespMsg->info.connect_b3_resp.wReject) +
          sizeof (pRespMsg->info.connect_b3_resp.wReject);
      p = CapiUt_EnterEmptyStruct (p);  /* no NCPI */
   
      /* compute the message length */
      pmbMsg->m_len = (int) (p - (unsigned char *) pRespMsg);
      C_PUT_WORD (pRespMsg->head.wLen, pmbMsg->m_len);
   }
   else if (uCmd == CAPI_INDICAT (C_FACILITY))
   {
      pRespMsg->head = pCapiIndMsg->head;
      C_PUT_WORD (pRespMsg->head.wCmd, CAPI_RESPONSE (C_FACILITY));
      C_PUT_WORD (pRespMsg->info.facility_resp.wSelector,
                  C_GET_WORD (pCapiIndMsg->info.facility_ind.wSelector));
      p = (unsigned char *) &(pRespMsg->info.facility_resp.wSelector) +
          sizeof (pRespMsg->info.facility_resp.wSelector);
      p = CapiUt_EnterEmptyStruct (p);  /* no facility parameters */
      pmbMsg->m_len = (int) (p - (unsigned char *) pRespMsg);
      C_PUT_WORD (pRespMsg->head.wLen, pmbMsg->m_len);
   }
   else if (uCmd == CAPI_INDICAT (C_DATA_B3))
   {
      pRespMsg->head = pCapiIndMsg->head;
      C_PUT_WORD (pRespMsg->head.wCmd, CAPI_RESPONSE (C_DATA_B3));
      C_PUT_WORD (pRespMsg->info.data_b3_resp.wHandle,
                  C_GET_WORD (pCapiIndMsg->info.data_b3_ind.wHandle));
      pmbMsg->m_len = sizeof (pRespMsg->head) +
                      sizeof (pRespMsg->info.data_b3_resp);
      C_PUT_WORD (pRespMsg->head.wLen, pmbMsg->m_len);
   }
   else
   {
      /* for a dummy response only the message header is needed */
      pRespMsg->head = pCapiIndMsg->head;
      C_PUT_WORD (pRespMsg->head.wCmd,
                  CAPI_RESPONSE (uCmd & CAPI_CMDMASK_COMMAND));
      pmbMsg->m_len = sizeof (pRespMsg->head);
      C_PUT_WORD (pRespMsg->head.wLen, pmbMsg->m_len);
   }
   uRes = I4bCmgr_PlciPutB3Msg (pConnData, pmbMsg);
   if (uRes != CAPI_OK)
   {
      DBG (LOG_ERROR,
           "Cd %u: State %s: Error 0x%04X responding to message 0x%04X, dwCid 0x%08X, wNum 0x%04X",
           pConnData->uCdId, GetNcciStateName (pConnData->ncciState),
           uRes, uCmd, (unsigned) CAPI_GET_CID (pCapiIndMsg),
           (unsigned) CAPI_GET_MSGNUM (pCapiIndMsg));

      kcapi_free_mbuf (pmbMsg);
   }
   
} /* SendDummyResponse */





/*
        check for silence in voice data stream
        --------------------------------------
        Nearly verbatim copy of i4b_l1_bchan_tel_silence.
*/

static int CheckForSilence
   (const unsigned char *pabData,
    size_t               nLenData)
{
   register int i;
   register int j;
   
   for (i = 0, j = 0; (size_t) i < nLenData; i++, pabData++)
   {
      if (*pabData >= 0xAA && *pabData <= 0xAC)
      {
         j++;
      }
   }
   
   /* if not enough silence bytes are found: no silence */
   if (j < TEL_IDLE_MIN)
   {
      return (0);
   }
   
   /* at least half of the buffer contains silence: silence */
   return (1);
} /* CheckForSilence */





/*
        clean an mbuf queue
        -------------------
*/

static void CleanIfQueue
   (struct ifqueue *pIfq)
{
   struct mbuf *pmb;
   
   do
   {
      IF_DEQUEUE (pIfq, pmb);
      if (pmb != NULL)
      {
         kcapi_free_mbuf (pmb);
      }
   } while (pmb != NULL);
   
} /* CleanIfQueue */





/*
        translate NCCI state into string
        --------------------------------
*/

static const char *GetNcciStateName
   (I4bCapiNcciState_t state)
{
   static char szBuf [24];
   
   if ((size_t) state >= ARRAY_COUNT (g_apszStateNames))
   {
      snprintf (szBuf, sizeof (szBuf), "<<Unknown: %d>>", (int) state);
      return (szBuf);
   }
   return (g_apszStateNames [(int) state]);
} /* GetNcciStateName */





/*
        translate NCCI event into string
        --------------------------------
*/

static const char *GetNcciEventName
   (NcciEventType_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]);
} /* GetNcciEventName */
