/**
 * @file daic_ncci.c
 *
 * Daic-NCCI - The NCCI state machine for the daic device driver.
 *
 * Copyright: 2003 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: daic_ncci.c,v 1.1.2.1 2006/02/28 21:35:14 thomas Exp $
 * $Project:    CAPI for BSD $
 * $Target:     daic - CAPI manager driver for Diehl active ISDN controllers $
 * @date        02.02.2003
 * @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>

/* Import includes */

#define __DAIC_NCCI__

/* Local includes */
#include <c4b/daic/daic_misc.h>
#include <c4b/daic/daic_dispatch.h>
#include <c4b/daic/daic_plci.h>
#include <c4b/daic/daic_ncci.h>





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





/* --- Definitions for handling return codes --- */

/**
 * The prototype for an action function in the return code handling table.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */
typedef void (DaicRcActionFct_t)
   (DaicPortData_t     *pPortData,
    DaicNcciData_t     *pNcciData,
    const DaicRcData_t *pRcData);

/**
 * Handle the return code for a controller Connect-Request.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */
static DaicRcActionFct_t daicncci_handle_connect_req_rc;

/**
 * Handle the return code for a controller Connect-Ack-Request.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */
static DaicRcActionFct_t daicncci_handle_connect_ack_req_rc;

/**
 * Handle the return code for a controller Reset-Request.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */
static DaicRcActionFct_t daicncci_handle_reset_req_rc;

/**
 * Handle the return code for a controller Reset-Ack-Request.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */
static DaicRcActionFct_t daicncci_handle_reset_ack_req_rc;

/**
 * Handle the return code for a controller Data-Request.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */
static DaicRcActionFct_t daicncci_handle_data_req_rc;

/**
 * Handle the return code for a controller Disconnect-Request.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */
static DaicRcActionFct_t daicncci_handle_disconnect_req_rc;

/**
 * Handle the return code for a controller Disconnect-Ack-Request.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */
static DaicRcActionFct_t daicncci_handle_disconnect_ack_req_rc;

/** The function table for handling general return codes. */
static DaicRcActionFct_t *g_aRcAction [DAIC_NCCI_NUM_STATES]
                                      [DAIC_NL_NUM_CMDS + 1] =
   {
      /* DAIC_NCCI_STATE_N0 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* More-Data- or Assign-Request (1) */
         NULL,                          /* Connect-Request (2) */
         NULL,                          /* Connect-Ack-Request (3) */
         NULL,                          /* Disconnect-Request (4) */
         NULL,                          /* Disconnect-Ack-Request (5) */
         NULL,                          /* Reset-Request (6) */
         NULL,                          /* Reset-Ack-Request (7) */
         NULL,                          /* Data-Request (8) */
         NULL,                          /* Expedited-Data-Request (9) */
         NULL,                          /* Unack-Data-Request (0x0A) */
         NULL,                          /* Broadcast-Data-Request (0x0B) */
         NULL                           /* Remove-Request (redirected from 0xFF to 0x0C) */
      },
      /* DAIC_NCCI_STATE_N1 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* More-Data- or Assign-Request (1) */
         daicncci_handle_connect_req_rc,/* Connect-Request (2) */
         NULL,                          /* Connect-Ack-Request (3) */
         NULL,                          /* Disconnect-Request (4) */
         NULL,                          /* Disconnect-Ack-Request (5) */
         NULL,                          /* Reset-Request (6) */
         NULL,                          /* Reset-Ack-Request (7) */
         NULL,                          /* Data-Request (8) */
         NULL,                          /* Expedited-Data-Request (9) */
         NULL,                          /* Unack-Data-Request (0x0A) */
         NULL,                          /* Broadcast-Data-Request (0x0B) */
         NULL                           /* Remove-Request (redirected from 0xFF to 0x0C) */
      },
      /* DAIC_NCCI_STATE_N2 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* More-Data- or Assign-Request (1) */
         NULL,                          /* Connect-Request (2) */
         NULL,                          /* Connect-Ack-Request (3) */
         NULL,                          /* Disconnect-Request (4) */
         NULL,                          /* Disconnect-Ack-Request (5) */
         NULL,                          /* Reset-Request (6) */
         NULL,                          /* Reset-Ack-Request (7) */
         NULL,                          /* Data-Request (8) */
         NULL,                          /* Expedited-Data-Request (9) */
         NULL,                          /* Unack-Data-Request (0x0A) */
         NULL,                          /* Broadcast-Data-Request (0x0B) */
         NULL                           /* Remove-Request (redirected from 0xFF to 0x0C) */
      },
      /* DAIC_NCCI_STATE_N2_1 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* More-Data- or Assign-Request (1) */
         NULL,                          /* Connect-Request (2) */
         daicncci_handle_connect_ack_req_rc,
                                        /* Connect-Ack-Request (3) */
         NULL,                          /* Disconnect-Request (4) */
         NULL,                          /* Disconnect-Ack-Request (5) */
         NULL,                          /* Reset-Request (6) */
         NULL,                          /* Reset-Ack-Request (7) */
         NULL,                          /* Data-Request (8) */
         NULL,                          /* Expedited-Data-Request (9) */
         NULL,                          /* Unack-Data-Request (0x0A) */
         NULL,                          /* Broadcast-Data-Request (0x0B) */
         NULL                           /* Remove-Request (redirected from 0xFF to 0x0C) */
      },
      /* DAIC_NCCI_STATE_NACT */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* More-Data- or Assign-Request (1) */
         NULL,                          /* Connect-Request (2) */
         NULL,                          /* Connect-Ack-Request (3) */
         NULL,                          /* Disconnect-Request (4) */
         NULL,                          /* Disconnect-Ack-Request (5) */
         daicncci_handle_reset_req_rc,  /* Reset-Request (6) */
         daicncci_handle_reset_ack_req_rc,
                                        /* Reset-Ack-Request (7) */
         daicncci_handle_data_req_rc,   /* Data-Request (8) */
         daicncci_handle_data_req_rc,   /* Expedited-Data-Request (9) */
         daicncci_handle_data_req_rc,   /* Unack-Data-Request (0x0A) */
         NULL,                          /* Broadcast-Data-Request (0x0B) */
         NULL                           /* Remove-Request (redirected from 0xFF to 0x0C) */
      },
      /* DAIC_NCCI_STATE_N4 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* More-Data- or Assign-Request (1) */
         NULL,                          /* Connect-Request (2) */
         NULL,                          /* Connect-Ack-Request (3) */
         daicncci_handle_disconnect_req_rc,
                                        /* Disconnect-Request (4) */
         NULL,                          /* Disconnect-Ack-Request (5) */
         NULL,                          /* Reset-Request (6) */
         NULL,                          /* Reset-Ack-Request (7) */
         NULL,                          /* Data-Request (8) */
         NULL,                          /* Expedited-Data-Request (9) */
         NULL,                          /* Unack-Data-Request (0x0A) */
         NULL,                          /* Broadcast-Data-Request (0x0B) */
         NULL                           /* Remove-Request (redirected from 0xFF to 0x0C) */
      },
      /* DAIC_NCCI_STATE_N5 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* More-Data- or Assign-Request (1) */
         NULL,                          /* Connect-Request (2) */
         NULL,                          /* Connect-Ack-Request (3) */
         NULL,                          /* Disconnect-Request (4) */
         daicncci_handle_disconnect_ack_req_rc,
                                        /* Disconnect-Ack-Request (5) */
         NULL,                          /* Reset-Request (6) */
         NULL,                          /* Reset-Ack-Request (7) */
         NULL,                          /* Data-Request (8) */
         NULL,                          /* Expedited-Data-Request (9) */
         NULL,                          /* Unack-Data-Request (0x0A) */
         NULL,                          /* Broadcast-Data-Request (0x0B) */
         NULL                           /* Remove-Request (redirected from 0xFF to 0x0C) */
      }
   };



/* --- Definitions for handling controller indications --- */

/**
 * The prototype for an action function in the indication handling table.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
typedef void (DaicIndActionFct_t)
   (DaicPortData_t      *pPortData,
    DaicNcciData_t      *pNcciData,
    const DaicIndData_t *pIndData);

/**
 * Handle a controller Connect-Indication.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
static DaicIndActionFct_t daicncci_handle_connect_ind;

/**
 * Handle a controller Connect-Ack-Indication.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
static DaicIndActionFct_t daicncci_handle_connect_ack_ind;

/**
 * Handle a controller Disconnect-Indication.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
static DaicIndActionFct_t daicncci_handle_disconnect_ind;

/**
 * Handle a controller Disconnect-Ack-Indication.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
static DaicIndActionFct_t daicncci_handle_disconnect_ack_ind;

/**
 * Handle a controller More-Data-Indication.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
static DaicIndActionFct_t daicncci_handle_mdata_ind;

/**
 * Handle a controller Data-Indication.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
static DaicIndActionFct_t daicncci_handle_data_ind;

/**
 * Handle a controller Reset-Indication.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
static DaicIndActionFct_t daicncci_handle_reset_ind;

/**
 * Handle a controller Reset-Ack-Indication.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
static DaicIndActionFct_t daicncci_handle_reset_ack_ind;

/**
 * Ignore any controller Indication.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
static DaicIndActionFct_t daicncci_ignore_ind;

/** The function table for handling indications. */
static DaicIndActionFct_t *g_aIndAction [DAIC_NCCI_NUM_STATES]
                                        [DAIC_NL_NUM_CMDS] =
   {
      /* DAIC_NCCI_STATE_N0 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* More-Data-Indication (1) */
         daicncci_handle_connect_ind,   /* Connect-Indication (2) */
         NULL,                          /* Connect-Ack-Indication (3) */
         NULL,                          /* Disconnect-Indication (4) */
         NULL,                          /* Disconnect-Ack-Indication (5) */
         NULL,                          /* Reset-Indication (6) */
         NULL,                          /* Reset-Ack-Indication (7) */
         NULL,                          /* Data-Indication (8) */
         NULL,                          /* Expedited-Data-Indication (9) */
         NULL,                          /* Unack-Data-Indication (0x0A) */
         NULL                           /* Broadcast-Data-Indication (0x0B) */
      },
      /* DAIC_NCCI_STATE_N1 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* More-Data-Indication (1) */
         NULL,                          /* Connect-Indication (2) */
         daicncci_handle_connect_ack_ind,
                                        /* Connect-Ack-Indication (3) */
         daicncci_handle_disconnect_ind,/* Disconnect-Indication (4) */
         NULL,                          /* Disconnect-Ack-Indication (5) */
         NULL,                          /* Reset-Indication (6) */
         NULL,                          /* Reset-Ack-Indication (7) */
         NULL,                          /* Data-Indication (8) */
         NULL,                          /* Expedited-Data-Indication (9) */
         NULL,                          /* Unack-Data-Indication (0x0A) */
         NULL                           /* Broadcast-Data-Indication (0x0B) */
      },
      /* DAIC_NCCI_STATE_N2 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* More-Data-Indication (1) */
         NULL,                          /* Connect-Indication (2) */
         NULL,                          /* Connect-Ack-Indication (3) */
         NULL,                          /* Disconnect-Indication (4) */
         NULL,                          /* Disconnect-Ack-Indication (5) */
         NULL,                          /* Reset-Indication (6) */
         NULL,                          /* Reset-Ack-Indication (7) */
         NULL,                          /* Data-Indication (8) */
         NULL,                          /* Expedited-Data-Indication (9) */
         NULL,                          /* Unack-Data-Indication (0x0A) */
         NULL                           /* Broadcast-Data-Indication (0x0B) */
      },
      /* DAIC_NCCI_STATE_N2_1 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* More-Data-Indication (1) */
         NULL,                          /* Connect-Indication (2) */
         NULL,                          /* Connect-Ack-Indication (3) */
         NULL,                          /* Disconnect-Indication (4) */
         NULL,                          /* Disconnect-Ack-Indication (5) */
         NULL,                          /* Reset-Indication (6) */
         NULL,                          /* Reset-Ack-Indication (7) */
         NULL,                          /* Data-Indication (8) */
         NULL,                          /* Expedited-Data-Indication (9) */
         NULL,                          /* Unack-Data-Indication (0x0A) */
         NULL                           /* Broadcast-Data-Indication (0x0B) */
      },
      /* DAIC_NCCI_STATE_NACT */
      {
         NULL,                          /* Null command (0) */
         daicncci_handle_mdata_ind,     /* More-Data-Indication (1) */
         NULL,                          /* Connect-Indication (2) */
         NULL,                          /* Connect-Ack-Indication (3) */
         daicncci_handle_disconnect_ind,/* Disconnect-Indication (4) */
         NULL,                          /* Disconnect-Ack-Indication (5) */
         daicncci_handle_reset_ind,     /* Reset-Indication (6) */
         daicncci_handle_reset_ack_ind, /* Reset-Ack-Indication (7) */
         daicncci_handle_data_ind,      /* Data-Indication (8) */
         daicncci_handle_data_ind,      /* Expedited-Data-Indication (9) */
         daicncci_handle_data_ind,      /* Unack-Data-Indication (0x0A) */
         NULL                           /* Broadcast-Data-Indication (0x0B) */
      },
      /* DAIC_NCCI_STATE_N4 */
      {
         NULL,                          /* Null command (0) */
         daicncci_ignore_ind,           /* More-Data-Indication (1) */
         daicncci_ignore_ind,           /* Connect-Indication (2) */
         daicncci_ignore_ind,           /* Connect-Ack-Indication (3) */
         daicncci_handle_disconnect_ind,/* Disconnect-Indication (4) */
         daicncci_handle_disconnect_ack_ind,
                                        /* Disconnect-Ack-Indication (5) */
         daicncci_ignore_ind,           /* Reset-Indication (6) */
         daicncci_ignore_ind,           /* Reset-Ack-Indication (7) */
         daicncci_ignore_ind,           /* Data-Indication (8) */
         daicncci_ignore_ind,           /* Expedited-Data-Indication (9) */
         daicncci_ignore_ind,           /* Unack-Data-Indication (0x0A) */
         NULL                           /* Broadcast-Data-Indication (0x0B) */
      },
      /* DAIC_NCCI_STATE_N5 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* More-Data-Indication (1) */
         NULL,                          /* Connect-Indication (2) */
         NULL,                          /* Connect-Ack-Indication (3) */
         NULL,                          /* Disconnect-Indication (4) */
         daicncci_ignore_ind,           /* Disconnect-Ack-Indication (5) */
         NULL,                          /* Reset-Indication (6) */
         NULL,                          /* Reset-Ack-Indication (7) */
         NULL,                          /* Data-Indication (8) */
         NULL,                          /* Expedited-Data-Indication (9) */
         NULL,                          /* Unack-Data-Indication (0x0A) */
         NULL                           /* Broadcast-Data-Indication (0x0B) */
      }
   };



/** The indexes of CAPI messages to be used for action table lookups. */
enum
{
   FAC_REQ_IDX = 0,
   FAC_RSP_IDX,
   CONN_B3_REQ_IDX,
   CONN_B3_RSP_IDX,
   CONN_B3_ACT_RSP_IDX,
   DISC_B3_REQ_IDX,
   DISC_B3_RSP_IDX,
   DATA_B3_REQ_IDX,
   DATA_B3_RSP_IDX,
   RESET_B3_REQ_IDX,
   RESET_B3_RSP_IDX,
   CONN_B3_T90_ACT_RSP_IDX,
   NUM_CAPI_MSG_IDX
};

/**
 * The table to translate CAPI message commands into action table indexes.
 *
 * The CAPI message command (excluding the sub-command) will first be multiplied
 * with 2. The result will remain as is for requests and be incremented by 1 for
 * responses. This value will be used as an index into this translation table.
 * The result of this table lookup will be used as an index for the CAPI message
 * action table.
 *
 * Beware that only valid message commands are supported. If an invalid command
 * value ist used for the table lookup, the result will be -1.
 *
 * There is a "little hack" in this table. The messages
 * Connect-Active-Indication and Select-B-Protocol-Confirm must pass the NCCI
 * state machine to trigger some action on this layer. But these messages are
 * neither requests nor responses. But they are inserted into this table on the
 * place of the corresponding request. The handling function will work correctly
 * with this exception.
 */
static int g_auCapiMsgCmdIdx [256 * 2] =
   {
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      FAC_REQ_IDX,        FAC_RSP_IDX,                  /* Facility-Request, -Response (0x80) */
      -1, -1,
      CONN_B3_REQ_IDX,    CONN_B3_RSP_IDX,              /* Connect-B3-Request, -Response (0x82) */
      -1,                 CONN_B3_ACT_RSP_IDX,          /* Connect-B3-Active-Response (0x83) */
      DISC_B3_REQ_IDX,    DISC_B3_RSP_IDX,              /* Disconnect-B3-Request, -Response (0x84) */
      -1, -1,
      DATA_B3_REQ_IDX,    DATA_B3_RSP_IDX,              /* Data-B3-Request, -Response (0x86) */
      RESET_B3_REQ_IDX,   RESET_B3_RSP_IDX,             /* Reset-B3-Request, -Response (0x87) */
      -1,                 CONN_B3_T90_ACT_RSP_IDX,      /* Connect-B3-T90-Active-Response (0x88) */
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
   };

/**
 * The prototype for an action function in the CAPI message handling table.
 *
 * If the result value is CAPI_OK, the mbuf is either already released or its
 * address is stored in internal data members. If the result is not CAPI_OK, the
 * caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
typedef unsigned (DaicCapiMsgActionFct_t)
   (DaicPortData_t *pPortData,
    DaicNcciData_t *pNcciData,
    struct mbuf    *pmbMsg);

/**
 * Handle a CAPI Connect-B3-Request.
 *
 * A Connect-B3-Request is always handled in the context of NCCI 0. This is
 * necessary to be able to assign the result for the corresponding controller
 * request to the correct NCCI. Only for Assign-Request we have the possibility
 * to attach some data for identifying the originator of the request. But a
 * Connect-B3-Request results in a controller Connect-Request. And the return
 * code for it consists only of the result value, the network id and a newly
 * assigned network channel id. Because this channel id is not known until this
 * point, the dispatch module cannot forward the result to the right NCCI state
 * machine.
 *
 * The solution is to execute all Connect-B3-Requests through NCCI 0 and to
 * serialize them among all connections on a single controller port. We first
 * allocate a new NCCI to be sure this is possible. The NCCI value is stored
 * into the NCCI 0 state machine, so we always know, what real NCCI we are
 * executing the current request for.
 *
 * After allocating and initializing the new NCCI structure, we send out a
 * controller Connect-Request. The return code will deliver a newly assigned
 * channel id that will later be used to address the real NCCI. Because the
 * dispatch module does not know the new channel id when the return code is
 * dispatched, the return code will be delivered to NCCI 0. Here we are waiting
 * for this result. It is transfered to the formerly allocated real NCCI and
 * processing for the new network connection will be done there.
 *
 * NCCI 0 is then cleaned up, so we can handle the next Connect-B3-Request.
 *
 * @note If the result value is CAPI_OK, the mbuf is either already released or
 *       its address is stored in internal data members. If the result is not
 *       CAPI_OK, the caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static DaicCapiMsgActionFct_t daicncci_handle_connect_b3_req;

/**
 * Handle a CAPI Connect-B3-Response.
 *
 * @note If the result value is CAPI_OK, the mbuf is either already released or
 *       its address is stored in internal data members. If the result is not
 *       CAPI_OK, the caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static DaicCapiMsgActionFct_t daicncci_handle_connect_b3_rsp;

/**
 * Handle a CAPI Disconnect-B3-Request.
 *
 * @note If the result value is CAPI_OK, the mbuf is either already released or
 *       its address is stored in internal data members. If the result is not
 *       CAPI_OK, the caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static DaicCapiMsgActionFct_t daicncci_handle_disconnect_b3_req;

/**
 * Handle a CAPI Disconnect-B3-Response.
 *
 * @note If the result value is CAPI_OK, the mbuf is either already released or
 *       its address is stored in internal data members. If the result is not
 *       CAPI_OK, the caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static DaicCapiMsgActionFct_t daicncci_handle_disconnect_b3_rsp;

/**
 * Handle a CAPI Data-B3-Request.
 *
 * @note If the result value is CAPI_OK, the mbuf is either already released or
 *       its address is stored in internal data members. If the result is not
 *       CAPI_OK, the caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static DaicCapiMsgActionFct_t daicncci_handle_data_b3_req;

/**
 * Handle a CAPI Reset-B3-Request.
 *
 * @note If the result value is CAPI_OK, the mbuf is either already released or
 *       its address is stored in internal data members. If the result is not
 *       CAPI_OK, the caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static DaicCapiMsgActionFct_t daicncci_handle_reset_b3_req;

/**
 * Handle a CAPI Facility-Request.
 *
 * @note If the result value is CAPI_OK, the mbuf is either already released or
 *       its address is stored in internal data members. If the result is not
 *       CAPI_OK, the caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static DaicCapiMsgActionFct_t daicncci_handle_facility_req;

/**
 * Ignore any CAPI request message.
 *
 * @note If the result value is CAPI_OK, the mbuf is either already released or
 *       its address is stored in internal data members. If the result is not
 *       CAPI_OK, the caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static DaicCapiMsgActionFct_t daicncci_ignore_capi_req;

/**
 * Handle any CAPI response message.
 *
 * @note If the result value is CAPI_OK, the mbuf is either already released or
 *       its address is stored in internal data members. If the result is not
 *       CAPI_OK, the caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static DaicCapiMsgActionFct_t daicncci_handle_gen_rsp;

/** The function table for handling CAPI messages. */
static DaicCapiMsgActionFct_t *g_aCapiMsgAction [DAIC_NCCI_NUM_STATES]
                                                [NUM_CAPI_MSG_IDX] =
   {
      /* DAIC_NCCI_STATE_N0 */
      {
         NULL,                          /* FAC_REQ_IDX */
         NULL,                          /* FAC_RSP_IDX */
         daicncci_handle_connect_b3_req,/* CONN_B3_REQ_IDX */
         NULL,                          /* CONN_B3_RSP_IDX */
         NULL,                          /* CONN_B3_ACT_RSP_IDX */
         NULL,                          /* DISC_B3_REQ_IDX */
         NULL,                          /* DISC_B3_RSP_IDX */
         NULL,                          /* DATA_B3_REQ_IDX */
         NULL,                          /* DATA_B3_RSP_IDX */
         NULL,                          /* RESET_B3_REQ_IDX */
         NULL,                          /* RESET_B3_RSP_IDX */
         NULL                           /* CONN_B3_T90_ACT_RSP_IDX */
      },
      /* DAIC_NCCI_STATE_N1 */
      {
         NULL,                          /* FAC_REQ_IDX */
         NULL,                          /* FAC_RSP_IDX */
         NULL,                          /* CONN_B3_REQ_IDX */
         NULL,                          /* CONN_B3_RSP_IDX */
         NULL,                          /* CONN_B3_ACT_RSP_IDX */
         daicncci_handle_disconnect_b3_req,
                                        /* DISC_B3_REQ_IDX */
         NULL,                          /* DISC_B3_RSP_IDX */
         NULL,                          /* DATA_B3_REQ_IDX */
         NULL,                          /* DATA_B3_RSP_IDX */
         NULL,                          /* RESET_B3_REQ_IDX */
         NULL,                          /* RESET_B3_RSP_IDX */
         NULL                           /* CONN_B3_T90_ACT_RSP_IDX */
      },
      /* DAIC_NCCI_STATE_N2 */
      {
         NULL,                          /* FAC_REQ_IDX */
         NULL,                          /* FAC_RSP_IDX */
         NULL,                          /* CONN_B3_REQ_IDX */
         daicncci_handle_connect_b3_rsp,/* CONN_B3_RSP_IDX */
         NULL,                          /* CONN_B3_ACT_RSP_IDX */
         daicncci_handle_disconnect_b3_req,
                                        /* DISC_B3_REQ_IDX */
         NULL,                          /* DISC_B3_RSP_IDX */
         NULL,                          /* DATA_B3_REQ_IDX */
         NULL,                          /* DATA_B3_RSP_IDX */
         NULL,                          /* RESET_B3_REQ_IDX */
         NULL,                          /* RESET_B3_RSP_IDX */
         NULL                           /* CONN_B3_T90_ACT_RSP_IDX */
      },
      /* DAIC_NCCI_STATE_N2_1 */
      {
         NULL,                          /* FAC_REQ_IDX */
         NULL,                          /* FAC_RSP_IDX */
         NULL,                          /* CONN_B3_REQ_IDX */
         NULL,                          /* CONN_B3_RSP_IDX */
         NULL,                          /* CONN_B3_ACT_RSP_IDX */
         NULL,                          /* DISC_B3_REQ_IDX */
         NULL,                          /* DISC_B3_RSP_IDX */
         NULL,                          /* DATA_B3_REQ_IDX */
         NULL,                          /* DATA_B3_RSP_IDX */
         NULL,                          /* RESET_B3_REQ_IDX */
         NULL,                          /* RESET_B3_RSP_IDX */
         NULL                           /* CONN_B3_T90_ACT_RSP_IDX */
      },
      /* DAIC_NCCI_STATE_NACT */
      {
         daicncci_handle_facility_req,  /* FAC_REQ_IDX */
         NULL,                          /* FAC_RSP_IDX */
         NULL,                          /* CONN_B3_REQ_IDX */
         NULL,                          /* CONN_B3_RSP_IDX */
         daicncci_handle_gen_rsp,       /* CONN_B3_ACT_RSP_IDX */
         daicncci_handle_disconnect_b3_req,
                                        /* DISC_B3_REQ_IDX */
         NULL,                          /* DISC_B3_RSP_IDX */
         daicncci_handle_data_b3_req,   /* DATA_B3_REQ_IDX */
         daicncci_handle_gen_rsp,       /* DATA_B3_RSP_IDX */
         daicncci_handle_reset_b3_req,  /* RESET_B3_REQ_IDX */
         daicncci_handle_gen_rsp,       /* RESET_B3_RSP_IDX */
         daicncci_handle_gen_rsp        /* CONN_B3_T90_ACT_RSP_IDX */
      },
      /* DAIC_NCCI_STATE_N4 */
      {
         NULL,                          /* FAC_REQ_IDX */
         NULL,                          /* FAC_RSP_IDX */
         NULL,                          /* CONN_B3_REQ_IDX */
         NULL,                          /* CONN_B3_RSP_IDX */
         daicncci_handle_gen_rsp,       /* CONN_B3_ACT_RSP_IDX */
         daicncci_ignore_capi_req,      /* DISC_B3_REQ_IDX */
         NULL,                          /* DISC_B3_RSP_IDX */
         NULL,                          /* DATA_B3_REQ_IDX */
         daicncci_handle_gen_rsp,       /* DATA_B3_RSP_IDX */
         NULL,                          /* RESET_B3_REQ_IDX */
         daicncci_handle_gen_rsp,       /* RESET_B3_RSP_IDX */
         daicncci_handle_gen_rsp        /* CONN_B3_T90_ACT_RSP_IDX */
      },
      /* DAIC_NCCI_STATE_N5 */
      {
         daicncci_handle_facility_req,  /* FAC_REQ_IDX */
         NULL,                          /* FAC_RSP_IDX */
         NULL,                          /* CONN_B3_REQ_IDX */
         daicncci_handle_gen_rsp,       /* CONN_B3_RSP_IDX */
         daicncci_handle_gen_rsp,       /* CONN_B3_ACT_RSP_IDX */
         daicncci_ignore_capi_req,      /* DISC_B3_REQ_IDX */
         daicncci_handle_disconnect_b3_rsp,
                                        /* DISC_B3_RSP_IDX */
         daicncci_ignore_capi_req,      /* DATA_B3_REQ_IDX */
         daicncci_handle_gen_rsp,       /* DATA_B3_RSP_IDX */
         daicncci_ignore_capi_req,      /* RESET_B3_REQ_IDX */
         daicncci_handle_gen_rsp,       /* RESET_B3_RSP_IDX */
         daicncci_handle_gen_rsp        /* CONN_B3_T90_ACT_RSP_IDX */
      }
   };





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





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





/**
 * Reset a NCCI state machine.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pNcciData             I/O: The NCCI data to be reset.
 *
 * @return Nothing.
 */
static void daicncci_reset
   (DaicPortData_t *pPortData,
    DaicNcciData_t *pNcciData);

/**
 * Handle messages enqueued for a NCCI.
 *
 * This function will handle any message enqueued for a given NCCI, until the
 * queue is empty or the operation results in a pending hardware requeust for
 * the controller.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pNcciData             i/O: The NCCI data for this operation.
 *
 * @return Nothing.
 */
static void daicncci_handle_queued_messages
   (DaicPortData_t *pPortData,
    DaicNcciData_t *pNcciData);

/**
 * Handle an indication from the controller.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pNcciData             I/O: The NCCI data for this operation.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
static void daicncci_handle_ind
   (DaicPortData_t      *pPortData,
    DaicNcciData_t      *pNcciData,
    const DaicIndData_t *pIndData);

/**
 * Handle a CAPI message to a NCCI instance.
 *
 * If the result value is CAPI_OK, the mbuf is either already released or its
 * address is stored in internal data members. If the result is not CAPI_OK, the
 * caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pNcciData             I/O: The NCCI data for this operation.
 * @param uApplID               I: The application id the CAPI message is from.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static unsigned daicncci_handle_capi_msg
   (DaicPortData_t  *pPortData,
    DaicNcciData_t  *pNcciData,
    unsigned         uApplID,
    struct mbuf     *pmbMsg);

/**
 * Abort a connection in any possible state.
 *
 * A connection may be aborted in any state, if there is no request pending. In
 * this case we send a controller Disconnect-Request. If a request is pending,
 * we can only set the flag for a pending Abort-Request, so when the return code
 * for the pending request arrives, the Abort-Request can be executed.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pNcciData             I/O: The NCCI data for this operation.
 *
 * @return Nothing.
 */
static void daicncci_abort_connection
   (DaicPortData_t *pPortData,
    DaicNcciData_t *pNcciData);

/**
 * Send out a controller Connect-Request for network connection establishment.
 *
 * This function is called after the CAPI Connect-B3-Request message is
 * successfully validated and a new NCCI for a network connection is allocated.
 *
 * Here we send an NL_CONNECT request over the assigned network id. Then we must
 * wait for the NL_CONNECT_ACK indication to report the complete connection
 * establishment to the application. The return code for the NL_CONNECT request
 * will deliver a newly allocated channel it in the context of the network id
 * used. A Connect-B3-Confirm is sent when receiving the return code for the
 * NL_CONNECT request.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pNcciData             I/O: The NCCI data entry as the context for
 *                                 sending the NL_CONNECT request.
 * @param uSigId                I: The signalling id of the associated PLCI.
 *
 * @retval CAPI_OK              The NL_CONNECT request is sent out successfully.
 * @retval Else                 Error occurred.
 */
static unsigned daicncci_send_nl_connect_req
   (DaicPortData_t  *pPortData,
    DaicNcciData_t  *pNcciData,
    unsigned         uSigId);

/**
 * Send a Connect-B3-Confirm CAPI message to the application.
 *
 * This function is used by the PLCI and the NCCI layer to signal a
 * Connect-B3-Confirm to the CAPI application. The info value for the message is
 * specified as a function argument.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pNcciData             I/O: The NCCI data for this operation.
 * @param pReqMsg               I: The Connect-B3-Request CAPI message this
 *                                 confirmation belongs to.
 * @param uInfoValue            I: The info value to put into the message.
 *
 * @return Nothing.
 */
static void daicncci_send_connect_b3_conf
   (DaicPortData_t  *pPortData,
    DaicNcciData_t  *pNcciData,
    const CAPIMsg_t *pReqMsg,
    unsigned         uInfoValue);

/**
 * Send a Data-B3-Confirm and cleanup the corresponding Data-B3-Request.
 *
 * @pre This function must be called with an mbuf for a Data-B3-Request stored
 *      in the NCCI data structure.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pNcciData             I/O: The NCCI data for this operation.
 * @param uInfoVal              I: The info value to report in the confirmation.
 *
 * @return Nothing.
 */
static void daicncci_send_data_b3_conf
   (DaicPortData_t  *pPortData,
    DaicNcciData_t  *pNcciData,
    unsigned         uInfoVal);

/**
 * Send a generic confirmation message for any CAPI message.
 *
 * This function is called for a CAPI message that cannot be handled in the
 * current context. If a CAPI response message is passed, nothing will be done.
 * Only request messages are handled.
 *
 * In most cases only a confirmation message with simply an info value in the
 * info part of the CAPI message needs to be sent. But some messages must be
 * handled specially. These are Facility-Requests and Data-B3-Requests.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pNcciData             I/O: The NCCI data for this operation.
 * @param pCapiReq              I: The CAPI message to send a confirmation for.
 * @param uInfoVal              I: The info value to report in the confirmation.
 *
 * @return Nothing.
 */
static void daicncci_send_gen_conf
   (DaicPortData_t  *pPortData,
    DaicNcciData_t  *pNcciData,
    const CAPIMsg_t *pCapiReq,
    unsigned         uInfoVal);





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





/**
 * Reset the NCCI state machines of all NCCIs for a port.
 *
 * @param pPortData             I/O: The address for the port data for this
 *                                 operation.
 *
 * @return Nothing.
 */

void daicncci_reset_all
   (DaicPortData_t *pPortData)
{
   int i;
   
   /* loop through all NCCI entries */
   for (i = 0; (size_t) i < ARRAY_COUNT (pPortData->aNcciData); ++i)
   {
      daicncci_reset (pPortData, &(pPortData->aNcciData [i]));
   }
   
   DBG (LOG_DEBUG, pPortData->iUnit, "Port %u: All NCCIs released",
        pPortData->uPortIdx);

} /* daicncci_reset_all */





/**
 * Assign a free NCCI entry for a new logical connection.
 *
 * @param pPortData             I/O: the port data as the operation context.
 * @param pPlciData             I/O: The data for the PLCI, the new NCCI will be
 *                                 assigned for.
 * @param uChId                 I: The newly assigned channel id for the new
 *                                 NCCI.
 *
 * @retval NULL                 No more NCCI available.
 * @retval Else                 The address of the NCCI data for the newly
 *                              assigned NCCI.
 */

DaicNcciData_t *daicncci_alloc_new_ncci
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uChId)
{
   DaicNcciData_t *pNcciData;
   size_t          n;
   int             i;
   
   /* find a new free entry */
   for (n = ARRAY_COUNT (pPortData->aNcciData),
           i = ((int) (pPortData->uLastAllocatedNcci) + 1) %
               (int) ARRAY_COUNT (pPortData->aNcciData);
        n > 0;
        --n, i = (i + 1) % (int) ARRAY_COUNT (pPortData->aNcciData))
   {
      if (i == 0)
      {
         continue;
      }
      pNcciData = &(pPortData->aNcciData [i]);
      if (pNcciData->uNcci == 0)
      {
         break;
      }
   }
   if (n == 0)
   {
      DBG (LOG_INFO, pPortData->iUnit,
           "Port %u: All NCCIs (%zu) busy, unable to allocate a new one",
           pPortData->uPortIdx, ARRAY_COUNT (pPortData->aNcciData) - 1);
      return (NULL);
   }
   /* i now indexes a free NCCI entry and pNcciData points to it */
   
   /* fill the assigned NCCI with the necessary data */
   pNcciData->state       = DAIC_NCCI_STATE_N0;
   pNcciData->uNcci       = (unsigned) i;
   pNcciData->uPlci       = pPlciData->uPlci;
   pNcciData->dwCid       = (u_int32_t)
                               ((pNcciData->uNcci << CAPI_CIDSHIFT_NCCI) |
                                pPlciData->dwCid);
   pNcciData->uNetId      = pPlciData->uNetId;
   pNcciData->uNetCh      = uChId;
   pNcciData->uApplID     = pPlciData->uApplID;
   pNcciData->uMaxDataLen =
      pPortData->aApplData [pPlciData->iApplDataIdx].uMaxBDataLen;
   if (pPlciData->bprot.wB3Prot == CAPI_B3_ISO8208)
   {
      pNcciData->ulFlags |= DAIC_NCCI_FLAG_USE_NCPI;
   }
   if (pPlciData->bprot.wB3Prot == CAPI_B3_T90NL)
   {
      pNcciData->ulFlags |= DAIC_NCCI_FLAG_T90_ENABLE;
   }
   pNcciData->uDisconnectReason  = 0;
   pNcciData->uCurrCapiReqMsgNum = 0;
   pNcciData->uDataIndHandle     = 0;

   /* determine if this NCCI is used for a voice connection */
   if (pPlciData->bprot.wB1Prot == CAPI_B1_TRANSPARENT_64 &&
       pPlciData->bprot.wB2Prot == CAPI_B2_TRANSPARENT &&
       pPlciData->bprot.wB3Prot == CAPI_B3_TRANSPARENT)
   {
      pNcciData->ulFlags |= DAIC_NCCI_FLAG_TRANSPARENT;
      
      /* the data block size must be a multiple of 256 (the controller delivers
       * voice data in 256 byte blocks) and so is rounded up to the next step
       * above the registration value, but limited to 2048 (MCLBYTES) bytes
       */
      if (pNcciData->uMaxDataLen > MCLBYTES)
      {
         pNcciData->uMaxDataLen = MCLBYTES;
      }
      pNcciData->uMaxDataLen = (pNcciData->uMaxDataLen + 255) & 0xFFFFFF00;
      
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Voice connection, use data block size %u",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           pNcciData->uMaxDataLen);
   }
   
   /* increment the number of active NCCIs for the corresponding PLCI */
   ++(pPlciData->nNumNcci);
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Assigned to PLCI %u (now %zu NCCIs), Network id %u, channel id %u",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        pNcciData->uPlci, pPlciData->nNumNcci,
        pNcciData->uNetId, pNcciData->uNetCh);

   return (pNcciData);
} /* daicncci_alloc_new_ncci */





/**
 * Release all connections assigned to an application id.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param uApplID               I: The appl. id for the release operation.
 *
 * @return Nothing.
 */

void daicncci_release_appl
   (DaicPortData_t *pPortData,
    unsigned        uApplID)
{
   int             i;
   DaicNcciData_t *pNcciData;
   size_t          n;
   struct mbuf    *pmb;
   
   /* loop through all NCCI entries to find a connection for the application
    * specified
    */
   /* Note: NCCI 0 is reserved to handle Connect-B3-Requests for all
    *       applications and need not be checked. Before handling a
    *       Connect-B3-Request a check is made if the application is really
    *       registered. So we can leave the queue for NCCI 0 as is. Even if an
    *       NL_CONNECT-Request is pending for the releasing application, we
    *       need not do anything, because the return code will simply lead to
    *       throwing it away if the application is not registered any more.
    */
   for (i = 0; (size_t) i < ARRAY_COUNT (pPortData->aNcciData); ++i)
   {
      /* check for entry in use */
      pNcciData = &(pPortData->aNcciData [i]);
      if (i == 0 || pNcciData->uNcci == 0)
      {
         continue;
      }
      
      /* check for entry assigned to the application id specified */
      if (pNcciData->uApplID == uApplID)
      {
         /* release the application from this NCCI */
         pNcciData->uApplID = 0;
         
         /* the connection will be terminated, so all pending CAPI messages must
          * be discarded (by clearing the CAPI message queue); controller
          * indications must remain intact
          */
         n = 0;
         do
         {
            _IF_DEQUEUE (&(pNcciData->capiMsgQueue), pmb);
            if (pmb != NULL)
            {
               kcapi_free_mbuf (pmb);
               ++n;
            }
         } while (pmb != NULL);
         DBG (LOG_DEBUG, pPortData->iUnit,
              "Port %u: NCCI %u: CAPI message queue cleaned up, %zu messages discarded",
              pPortData->uPortIdx, pNcciData->uNcci, n);

         /* now finally start aborting the connection */
         daicncci_abort_connection (pPortData, pNcciData);
         continue;
      }
   }
   
} /* daicncci_release_appl */





/**
 * Handle the result of a regular request (no assign).
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param uNcci                 I: The NCCI value the return code shall be
 *                                 dispatched to.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */

void daicncci_handle_rc
   (DaicPortData_t     *pPortData,
    unsigned            uNcci,
    const DaicRcData_t *pRcData)
{
   DaicNcciData_t    *pNcciData = &(pPortData->aNcciData [uNcci]);
   DaicRcActionFct_t *pfnAction;
   unsigned           uCurrRequest;
   
   /* if the NCCI is not allocated, simply ignore the return code */
   if (uNcci != 0 && pNcciData->uNcci == 0)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: Got return code 0x%02X for unallocated NCCI",
           pPortData->uPortIdx, uNcci, (unsigned) (pRcData->bRc));
      return;
   }
      
   /* if the abort flag is set, just reset the flag for a pending request and
    * continue with the connection abort operation.
    */
   if ((pNcciData->ulFlags & DAIC_NCCI_FLAG_GOT_ABORT_REQ) != 0)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Ignoring return code 0x%02X because of abort request",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (unsigned) (pRcData->bRc));
      pNcciData->ulFlags      &= ~DAIC_NCCI_FLAG_REQUEST_PENDING;
      pNcciData->uCurrRequest  = 0;
      daicncci_abort_connection (pPortData, pNcciData);
      return;
   }
   
   /* check if there is a request pending and memorize the request locally */
   if ((pNcciData->ulFlags & DAIC_NCCI_FLAG_REQUEST_PENDING) == 0)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Got return code 0x%02X without request pending",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (unsigned) (pRcData->bRc));
      return;
   }
   uCurrRequest = pNcciData->uCurrRequest;
   if (uCurrRequest == DAIC_NL_REMOVE)
   {
      uCurrRequest = DAIC_NL_NUM_CMDS;
   }

   /* now there is no request pending any more */
   pNcciData->ulFlags &= ~DAIC_NCCI_FLAG_REQUEST_PENDING;
   
   /* perform a lookup into the table for action functions according to the
    * current state and the current pending request
    */
   pfnAction = g_aRcAction [(int) (pNcciData->state)] [uCurrRequest];
   if (pfnAction != NULL)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Calling action function for return code 0x%02X to request 0x%02X",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (unsigned) (pRcData->bRc), uCurrRequest);
      pfnAction (pPortData, pNcciData, pRcData);
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Action function completed",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
   }
   else
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Return code 0x%02X for request 0x%02X not expected in current state",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (unsigned) (pRcData->bRc), uCurrRequest);
   }

   /* handle all enqueued messages until the queue is empty or a return code for
    * a hardware request is pending
    */
   daicncci_handle_queued_messages (pPortData, pNcciData);

} /* daicncci_handle_rc */





/**
 * Enqueue an indication from the controller for a NCCI.
 *
 * If there is currently no hardware request pending and the message queue for
 * the NCCI is empty, the indication will be handled at once.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param uNcci                 I: The NCCI value the return code shall be
 *                                 dispatched to.
 * @param pmbIndData            I/O: The indication data to evaluate. The
 *                                 ownership of the mbuf changes to the called
 *                                 function.
 *
 * @return Nothing.
 */

void daicncci_enqueue_ind
   (DaicPortData_t *pPortData,
    unsigned        uNcci,
    struct mbuf    *pmbIndData)
{
   DaicIndData_t  *pIndData = mtod (pmbIndData, DaicIndData_t *);
   DaicNcciData_t *pNcciData = &(pPortData->aNcciData [uNcci]);
   
   /* if the NCCI is not allocated, simply ignore the indication */
   if (pNcciData->uNcci == 0)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: Got indication 0x%02X for unallocated NCCI",
           pPortData->uPortIdx, uNcci, (unsigned) (pIndData->bInd));
      return;
   }

   /* if the indication is a Reset-Indication, we must set the corresponding
    * flag, so that all Data-B3-Requests pending in the message queue will be
    * ignored, until a Reset-B3-Response arrives
    */
   if ((pIndData->bInd & 0x0F) == DAIC_NL_RESET)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Got controller Reset-Indication to enqueue, set flag for received reset",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
      pNcciData->ulFlags |= DAIC_NCCI_FLAG_GOT_RESET;
   }
   
   /* If there is at least one entry in the local CAPI message queue or if the
    * current state denotes a pending controller request, just put the message
    * into the queue. The message will be handled later after receiving the
    * return code. Note that there is no checking of a filled up queue for
    * controller indications.
    */
   if (_IF_QLEN (&(pNcciData->indMsgQueue)) > 0 ||
       (pNcciData->ulFlags & DAIC_NCCI_FLAG_REQUEST_PENDING) != 0)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Busy, enqueue indication 0x%02X",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (unsigned) (pIndData->bInd));
      _IF_ENQUEUE (&(pNcciData->indMsgQueue), pmbIndData);
      return;
   }

   /* so there is currently no other task pending, just execute the indication
    * now
    */
   daicncci_handle_ind (pPortData, pNcciData,
                        mtod (pmbIndData, DaicIndData_t *));
   kcapi_free_mbuf (pmbIndData);
   
} /* daicncci_enqueue_ind */





/**
 * Enqueue a CAPI message to a NCCI instance.
 *
 * If there is currently no hardware request pending and the message queue for
 * the NCCI is empty, the message will be handled at once.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param uNcci                 I: The addressed NCCI value, equal to the
 *                                 network id subtracted by the network global
 *                                 id.
 * @param uApplID               I: The application id the CAPI message is from.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

unsigned daicncci_enqueue_capi_msg
   (DaicPortData_t  *pPortData,
    unsigned         uNcci,
    unsigned         uApplID,
    struct mbuf     *pmbMsg)
{
   CAPIMsg_t      *pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
   DaicNcciData_t *pNcciData;
   unsigned        uCmd;
   int             iCmdIdx;
   u_int32_t       dwCid = (u_int32_t) CAPI_GET_CID (pCapiMsg);
   
   /* a Connect-B3-Request is always directed to NCCI 0 */
   uCmd = CAPI_GET_CMD (pCapiMsg);
   if (uCmd == CAPI_REQUEST (C_CONNECT_B3))
   {
      if (uNcci != 0)
      {
         DBG (LOG_ERROR, pPortData->iUnit,
              "Port %u: PLCI %u: Got CAPI message 0x%04X with invalid NCCI in CID 0x%08lX, must be null",
              pPortData->uPortIdx,
              (unsigned) (CAPI_GET_PLCI_FROM_CID (dwCid)),
              uCmd,
              (unsigned long) dwCid);
         return (CCE_ILLEGAL_IDENTIFIER);
      }
      pNcciData = &(pPortData->aNcciData [0]);
   }
   else
   {
      /* If the NCCI is invalid or not allocated, simply ignore the message. The
       * check for the correct application id is done when handling the message.
       */
      if (uNcci >= ARRAY_COUNT (pPortData->aNcciData) ||
          pPortData->aNcciData [uNcci].uNcci == 0 ||
          pPortData->aNcciData [uNcci].dwCid != dwCid)
      {
         DBG (LOG_ERROR, pPortData->iUnit,
              "Port %u: Got CAPI message 0x%04X with invalid NCCI in CID 0x%08lX",
              pPortData->uPortIdx, uCmd, (unsigned long) dwCid);
         return (CCE_ILLEGAL_IDENTIFIER);
      }
      pNcciData = &(pPortData->aNcciData [uNcci]);
   }

   /* check if the NCCI is assigned to an application and the application id of
    * the current CAPI message is the same (no check is done for NCCI 0 as it
    * handles the first messages before allocating the "real" NCCI)
    */
   if (uNcci != 0 &&
       (pNcciData->uApplID == 0 || pNcciData->uApplID != uApplID))
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Reject CAPI message 0x%04X because NCCI is not assigned to application id %u",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           uCmd, uApplID);
      return (CME_INVALID_APPLICATION_ID);
   }

   /* if the message is a Reset-B3-Request, we must set the corresponding flag
    * so that all pending Data-B3-Requests in the queue are discarded, until the
    * controller Reset-Ack-Indication arrives
    */
   if (uCmd == CAPI_REQUEST (C_RESET_B3_20))
   {
      pNcciData->ulFlags |= DAIC_NCCI_FLAG_GOT_RESET;
   }
   
   /* translate the CAPI message command into an action table index to check if
    * the message command is valid for the NCCI state machine
    */
   iCmdIdx = (uCmd & CAPI_CMDMASK_COMMAND) * 2;
   if ((uCmd & CAPI_CMDMASK_SUBCMD) == C_RESP)
   {
      ++iCmdIdx;
   }
   iCmdIdx = g_auCapiMsgCmdIdx [iCmdIdx];
   if (iCmdIdx < 0)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: CAPI message 0x%04X not valid for NCCI context",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           uCmd);
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   
   /* If there is at least one entry in the local CAPI message queue or if the
    * current state denotes a pending controller request, just put the message
    * into the queue. The message will be handled later after receiving the
    * return code.
    */
   if (_IF_QLEN (&(pNcciData->capiMsgQueue)) > 0 ||
       _IF_QLEN (&(pNcciData->indMsgQueue)) > 0 ||
       (pNcciData->ulFlags & DAIC_NCCI_FLAG_REQUEST_PENDING) != 0)
   {
      if (_IF_QFULL (&(pNcciData->capiMsgQueue)))
      {
         DBG (LOG_DEBUG, pPortData->iUnit,
              "Port %u: NCCI %u: State %d: Busy and message queue full, reject CAPI message 0x%04X",
              pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
              CAPI_GET_CMD (pCapiMsg));
         return (CME_PUT_QUEUE_FULL);
      }
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Busy, enqueue CAPI message 0x%04X",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           CAPI_GET_CMD (pCapiMsg));
      _IF_ENQUEUE (&(pNcciData->capiMsgQueue), pmbMsg);
      return (CAPI_OK);
   }

   /* so there is currently no task pending, execute the message now */
   if (daicncci_handle_capi_msg
          (pPortData, pNcciData, uApplID, pmbMsg) != CAPI_OK)
   {
      kcapi_free_mbuf (pmbMsg);
   }
   
   return (CAPI_OK);
} /* daicncci_enqueue_capi_msg */





/**
 * Start removing all NCCIs for a disconnected PLCI.
 *
 * For each removed NCCI the PLCI layer is informed.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param uPlci                 I: The disconnected PLCI for removing all
 *                                 associated NCCIs.
 * @param uReason               I: The reason to signal for each NCCI to be
 *                                 removed.
 *
 * @return Nothing.
 */

void daicncci_remove_all_ncci_for_plci
   (DaicPortData_t *pPortData,
    unsigned        uPlci,
    unsigned        uReason)
{
   int             i;
   DaicNcciData_t *pNcciData;
   
   /* loop through all NCCI entries to find all entries with the signaling id
    * specified
    */
   for (i = 1; (size_t) i < ARRAY_COUNT (pPortData->aNcciData); ++i)
   {
      /* check for entry in use */
      pNcciData = &(pPortData->aNcciData [i]);
      if (pNcciData->uNcci == 0)
      {
         continue;
      }
      
      /* check for entry linked to the PLCI specified */
      if (pNcciData->uPlci == uPlci)
      {
         /* store the disconnect reason to report */
         pNcciData->uDisconnectReason = uReason;
         
         /* start aborting the network connection */
         daicncci_abort_connection (pPortData, pNcciData);
      }
   }
   
} /* daicncci_remove_all_ncci_for_plci */





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





/**
 * Handle the return code for a controller Connect-Request.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */

static void daicncci_handle_connect_req_rc
   (DaicPortData_t     *pPortData,
    DaicNcciData_t     *pNcciData,
    const DaicRcData_t *pRcData)
{
   DaicNcciData_t *pNewNcciData;
   unsigned        uInfoValue;
   
   if (pRcData->bRc == DAIC_RC_OK)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Connect-Request successful",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
   }
   else
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Connect-Request failed with rc 0x%02X",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (unsigned) (pRcData->bRc));
   }
   
   /* determine the CAPI result value from the return code value */
   uInfoValue = daicmisc_get_capi_info_from_rc
                   (pRcData->bRc, DAIC_ID_QUALIFY_NCCI);
   
   /* send out the Connect-B3-Confirm message */
   if (pNcciData->pmbCurrCapiMsg != NULL)
   {
      daicncci_send_connect_b3_conf
         (pPortData, pNcciData,
          mtod (pNcciData->pmbCurrCapiMsg, CAPIMsg_t *), uInfoValue);
      kcapi_free_mbuf (pNcciData->pmbCurrCapiMsg);
      pNcciData->pmbCurrCapiMsg = NULL;
   }
   else if (uInfoValue == CAPI_OK)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Got controller Connect-Request return code without stored CAPI message",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
      uInfoValue = CCE_MSG_NOT_ALLOWED_YET;
   }

   /* If the Connect-Request (only done on NCCI 0 that is never assigned to an
    * application) failed, we only need to release the newly assigned NCCI.
    * Otherwise we must now hand the network connection over to the new NCCI.
    * Note that the NCCI was already assigned when handling the
    * Connect-B3-Request and that its NCCI value is stored into uNcci of NCCI 0.
    */
   pNewNcciData = &(pPortData->aNcciData [pNcciData->uNcci]);
   if (uInfoValue == CAPI_OK)
   {
      pNewNcciData->state  = DAIC_NCCI_STATE_N1;
      pNewNcciData->uNetCh = (unsigned) (pRcData->bRcCh);
      (void) daicdisp_idtrans_enter_channel_and_ncci
                (pPortData, pNewNcciData->uNetId,
                 pNewNcciData->uNetCh, pNewNcciData->uNcci);

      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Got positive Connect-Request return code, wait for Connect-Ack-Indication",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));

      /* if the Abort-Request flag is set, perform an Abort-Request on the newly
       * assigned NCCI
       */
      if ((pNcciData->ulFlags & DAIC_NCCI_FLAG_GOT_ABORT_REQ) != 0)
      {
         daicncci_abort_connection (pPortData, pNewNcciData);
      }
   }
   else
   {
      pNewNcciData->uNcci = 0;
      daicplci_ncci_removed (pPortData, pNcciData->uPlci);
   }
   
   /* now prepare the NCCI entry 0 for the next Connect-B3-Request */
   pNcciData->state   = DAIC_NCCI_STATE_N0;
   pNcciData->uNcci   = 0;
   pNcciData->uPlci   = 0;
   pNcciData->dwCid   = 0;
   pNcciData->uNetId  = 0;
   pNcciData->uNetCh  = 0;
   pNcciData->uApplID = 0;
   pNcciData->ulFlags = 0;
   
} /* daicncci_handle_connect_req_rc */





/**
 * Handle the return code for a controller Connect-Ack-Request.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */

static void daicncci_handle_connect_ack_req_rc
   (DaicPortData_t     *pPortData,
    DaicNcciData_t     *pNcciData,
    const DaicRcData_t *pRcData)
{
   unsigned uInfoValue;
   
   if (pRcData->bRc == DAIC_RC_OK)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Connect-Ack-Request successful",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
   }
   else
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Connect-Ack-Request failed with rc 0x%02X",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (unsigned) (pRcData->bRc));
   }
   
   /* determine the CAPI result value from the return code value */
   uInfoValue = daicmisc_get_capi_info_from_rc
                   (pRcData->bRc, DAIC_ID_QUALIFY_NCCI);
   
   /* if the Connect-Ack-Request failed, we must abort the connection and send
    * a Disconnect-B3-Indication
    */
   if (uInfoValue != CAPI_OK)
   {
      pNcciData->uDisconnectReason = uInfoValue;
      daicncci_abort_connection (pPortData, pNcciData);
   }
   /* else we must send a Connect-B3-Active-Indication and go to active state */
   else
   {
      struct mbuf *pmb;
      CAPIMsg_t   *pCapiMsg;
      
      pmb = kcapi_get_mbuf (sizeof (pCapiMsg->head) + 1);
      if (pmb == NULL)
      {
         DBG (LOG_ERROR, pPortData->iUnit,
              "Port %u: NCCI %u: State %d: Out of mbufs for Connect-B3-Active-Indication",
              pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
         pNcciData->uDisconnectReason = CME_OS_RESOURCE_ERROR;
         daicncci_abort_connection (pPortData, pNcciData);
         return;
      }
      
      pCapiMsg = mtod (pmb, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pNcciData->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_CONNECT_B3_ACTIVE));
      C_PUT_WORD (pCapiMsg->head.wNum, pPortData->uCapiIndMsgNum);
      ++(pPortData->uCapiIndMsgNum);
      C_PUT_DWORD (pCapiMsg->head.dwCid, pNcciData->dwCid);
      C_PUT_BYTE (pCapiMsg->info.any.b [0], 0);
      C_PUT_WORD (pCapiMsg->head.wLen, sizeof (pCapiMsg->head) + 1);
      pmb->m_len = sizeof (pCapiMsg->head) + 1;
      
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Send Connect-B3-Active-Indication",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));

      mtx_unlock (&(pPortData->pSc->mtxAccess));
      kcapi_ctlr_receive_capi_message
         (pPortData->uUniqueCapiCtlrNum, pNcciData->uApplID, pmb);
      mtx_lock (&(pPortData->pSc->mtxAccess));

      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: NCCI %u: State %d --> %d",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (int) DAIC_NCCI_STATE_NACT);
      pNcciData->state = DAIC_NCCI_STATE_NACT;
   }
   
} /* daicncci_handle_connect_ack_req_rc */





/**
 * Handle the return code for a controller Reset-Request.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */

static void daicncci_handle_reset_req_rc
   (DaicPortData_t     *pPortData,
    DaicNcciData_t     *pNcciData,
    const DaicRcData_t *pRcData)
{
   struct mbuf *pmb;
   CAPIMsg_t   *pCapiMsg;
   unsigned     uInfoValue;
   
   if (pRcData->bRc == DAIC_RC_OK)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Reset-Request successful",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
   }
   else
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Reset-Request failed with rc 0x%02X",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (unsigned) (pRcData->bRc));
   }
   
   /* determine the CAPI result value from the return code value */
   uInfoValue = daicmisc_get_capi_info_from_rc
                   (pRcData->bRc, DAIC_ID_QUALIFY_NCCI);
   
   /* send out the Reset-B3-Confirm message */

   pmb = kcapi_get_mbuf (sizeof (pCapiMsg->head) +
                         sizeof (pCapiMsg->info.reset_b3_conf));
   if (pmb == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of mbufs for Reset-B3-Confirm",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
      return;
   }

   pCapiMsg = mtod (pmb, CAPIMsg_t *);
   C_PUT_WORD (pCapiMsg->head.wApp, pNcciData->uApplID);
   C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_CONFIRM (C_RESET_B3_20));
   C_PUT_WORD (pCapiMsg->head.wNum, pNcciData->uCurrCapiReqMsgNum);
   C_PUT_DWORD (pCapiMsg->head.dwCid, pNcciData->dwCid);
   C_PUT_WORD (pCapiMsg->info.reset_b3_conf.wInfo, uInfoValue);
   C_PUT_WORD (pCapiMsg->head.wLen,
               sizeof (pCapiMsg->head) + sizeof (pCapiMsg->info.reset_b3_conf));
   pmb->m_len = sizeof (pCapiMsg->head) + sizeof (pCapiMsg->info.reset_b3_conf);

   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Send Reset-B3-Confirm with info value 0x%04X",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        uInfoValue);

   mtx_unlock (&(pPortData->pSc->mtxAccess));
   kcapi_ctlr_receive_capi_message
      (pPortData->uUniqueCapiCtlrNum, pNcciData->uApplID, pmb);
   mtx_lock (&(pPortData->pSc->mtxAccess));

} /* daicncci_handle_reset_req_rc */





/**
 * Handle the return code for a controller Reset-Ack-Request.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */

static void daicncci_handle_reset_ack_req_rc
   (DaicPortData_t     *pPortData,
    DaicNcciData_t     *pNcciData,
    const DaicRcData_t *pRcData)
{
   if (pRcData->bRc == DAIC_RC_OK)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Reset-Request successful",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
   }
   else
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Reset-Request failed with rc 0x%02X",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (unsigned) (pRcData->bRc));
   }
   
   /* reset the flag for a pending B3-layer reset */
   pNcciData->ulFlags &= ~DAIC_NCCI_FLAG_GOT_RESET;
   
} /* daicncci_handle_reset_ack_req_rc */





/**
 * Handle the return code for a controller Data-Request.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */

static void daicncci_handle_data_req_rc
   (DaicPortData_t     *pPortData,
    DaicNcciData_t     *pNcciData,
    const DaicRcData_t *pRcData)
{
   unsigned uInfoValue;
   
   /* count the the newly received return code */
   if (pRcData->bReqSuppressed != 0)
   {
      pNcciData->nNumPendingDataRc = 0;
   }
   else if (pNcciData->nNumPendingDataRc > 0)
   {
      --(pNcciData->nNumPendingDataRc);
   }
   
   /* determine the CAPI result value from the return code value */
   uInfoValue = daicmisc_get_capi_info_from_rc
                   (pRcData->bRc, DAIC_ID_QUALIFY_NCCI);
   if (uInfoValue != CAPI_OK && pNcciData->uDataB3ReqResult == CAPI_OK)
   {
      pNcciData->uDataB3ReqResult = uInfoValue;
   }
   
   /* check if all expected return codes have arrived */
   if (pNcciData->nNumPendingDataRc > 0)
   {
      /* as we are still waiting for some return code, we must declare this fact
       * in the flags of this NCCI (the request itself is still there)
       */
      pNcciData->ulFlags |= DAIC_NCCI_FLAG_REQUEST_PENDING;
      
      DBG (LOG_DATAMSG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Got return code 0x%02X (CAPI info 0x%04X) for Data-Request portion, still %zu remaining",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (unsigned) (pRcData->bRc), uInfoValue, pNcciData->nNumPendingDataRc);
      return;
   }
   DBG (LOG_DATAMSG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got final return code 0x%02X (CAPI info 0x%04X) for Data-Request",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        (unsigned) (pRcData->bRc), uInfoValue);
   
   /* so the Data-B3-Request is finished */
   if (pNcciData->uDataB3ReqResult == CAPI_OK)
   {
      DBG (LOG_DATAMSG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Data-Request successful",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
   }
   else
   {
      DBG (LOG_DATAMSG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Data-Request failed with info value 0x%04X",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           pNcciData->uDataB3ReqResult);
   }
   
   /* a Data-B3-Request message must be pending */
   if (pNcciData->pmbCurrCapiMsg == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Got Data-Request return code with no Data-B3-Request pending",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
      return;
   }
   
   /* send out a Data-B3-Confirm and clean up for sending the current data block
    */
   daicncci_send_data_b3_conf
      (pPortData, pNcciData, pNcciData->uDataB3ReqResult);

} /* daicncci_handle_data_req_rc */





/**
 * Handle the return code for a controller Disconnect-Request.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */

static void daicncci_handle_disconnect_req_rc
   (DaicPortData_t     *pPortData,
    DaicNcciData_t     *pNcciData,
    const DaicRcData_t *pRcData)
{
   struct mbuf *pmb;
   CAPIMsg_t   *pCapiMsg;
   unsigned     uInfoValue;
   
   if (pRcData->bRc == DAIC_RC_OK)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Disconnect-Request successful",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
   }
   else
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Disconnect-Request failed with rc 0x%02X",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (unsigned) (pRcData->bRc));
   }
   
   /* determine the CAPI result value from the return code value */
   uInfoValue = daicmisc_get_capi_info_from_rc
                   (pRcData->bRc, DAIC_ID_QUALIFY_NCCI);
   
   /* send out the Disconnect-B3-Confirm message if a Disconnect-B3-Request is
    * in fact pending
    */

   if (pNcciData->pmbCurrCapiMsg == NULL)
   {
      return;
   }
   
   pmb = kcapi_get_mbuf (sizeof (pCapiMsg->head) +
                         sizeof (pCapiMsg->info.disconnect_b3_conf));
   if (pmb == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of mbufs for Disconnect-B3-Confirm",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
      kcapi_free_mbuf (pNcciData->pmbCurrCapiMsg);
      pNcciData->pmbCurrCapiMsg = NULL;
      return;
   }

   pCapiMsg = mtod (pmb, CAPIMsg_t *);
   C_PUT_WORD (pCapiMsg->head.wApp, pNcciData->uApplID);
   C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_CONFIRM (C_DISCONNECT_B3));
   C_PUT_WORD (pCapiMsg->head.wNum, pNcciData->uCurrCapiReqMsgNum);
   C_PUT_DWORD (pCapiMsg->head.dwCid, pNcciData->dwCid);
   C_PUT_WORD (pCapiMsg->info.disconnect_b3_conf.wInfo, uInfoValue);
   C_PUT_WORD (pCapiMsg->head.wLen,
               sizeof (pCapiMsg->head) +
                  sizeof (pCapiMsg->info.disconnect_b3_conf));
   pmb->m_len = sizeof (pCapiMsg->head) +
                sizeof (pCapiMsg->info.disconnect_b3_conf);

   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Send Disconnect-B3-Confirm with info value 0x%04X",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        uInfoValue);

   mtx_unlock (&(pPortData->pSc->mtxAccess));
   kcapi_ctlr_receive_capi_message
      (pPortData->uUniqueCapiCtlrNum, pNcciData->uApplID, pmb);
   mtx_lock (&(pPortData->pSc->mtxAccess));

   kcapi_free_mbuf (pNcciData->pmbCurrCapiMsg);
   pNcciData->pmbCurrCapiMsg = NULL;
   
} /* daicncci_handle_disconnect_req_rc */





/**
 * Handle the return code for a controller Disconnect-Ack-Request.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */

static void daicncci_handle_disconnect_ack_req_rc
   (DaicPortData_t     *pPortData,
    DaicNcciData_t     *pNcciData,
    const DaicRcData_t *pRcData)
{
   if (pRcData->bRc == DAIC_RC_OK)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Disconnect-Ack-Request successful",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
   }
   else
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Disconnect-Ack-Request failed with rc 0x%02X",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (unsigned) (pRcData->bRc));
   }
   
   /* we still wait for the Disconnect-B3-Response, that will trigger the
    * release of this NCCI
    */

} /* daicncci_handle_disconnect_ack_req_rc */





/**
 * Handle a controller Connect-Indication.
 *
 * This is the first message for an incoming connection, that will arrive at the
 * NCCI layer. The dispatch module has allocated this NCCI and the internal
 * state machine is already initialized.
 *
 * So now a Connect-B3-Indication is sent to the application. Then we wait for
 * the Connect-B3-Response to continue the connection establishment with a
 * controll Connect-Ack-Request.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */

static void daicncci_handle_connect_ind
   (DaicPortData_t      *pPortData,
    DaicNcciData_t      *pNcciData,
    const DaicIndData_t *pIndData)
{
   struct mbuf   *pmb;
   CAPIMsg_t     *pCapiMsg;
   CAPIStdNCPI_t *pNcpi;
   size_t         nLen;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got controller Connect-Indication",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: NCCI %u: State %d --> %d",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        (int) DAIC_NCCI_STATE_N2);
   pNcciData->state = DAIC_NCCI_STATE_N2;
   
   
   /* get a new mbuf for a Connect-B3-Indication; if there are some bytes in the
    * data part of the indication, they must be reported within an NCPI that is
    * the only info part of the message
    */
   nLen = (size_t) (pIndData->wDataLength);
   if (nLen > 0 && (pNcciData->ulFlags & DAIC_NCCI_FLAG_USE_NCPI) != 0)
   {
      nLen += sizeof (CAPIStdNCPI_t) - 1;
   }
   else
   {
      nLen = 0;
   }
   pmb = kcapi_get_mbuf (sizeof (pCapiMsg->head) + nLen + 1);
   if (pmb == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of mbufs for Connect-B3-Indication",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
      return;
   }
   
   /* fill the message and the NCPI if necessary */
   pCapiMsg = mtod (pmb, CAPIMsg_t *);
   C_PUT_WORD (pCapiMsg->head.wApp, pNcciData->uApplID);
   C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_CONNECT_B3));
   C_PUT_WORD (pCapiMsg->head.wNum, pPortData->uCapiIndMsgNum);
   ++(pPortData->uCapiIndMsgNum);
   C_PUT_DWORD (pCapiMsg->head.dwCid, pNcciData->dwCid);
   pNcpi = (CAPIStdNCPI_t *) &(pCapiMsg->info.any.b [0]);
   C_PUT_BYTE (pNcpi->bLength, nLen);
   if (nLen != 0)
   {
      C_PUT_BYTE (pNcpi->b0, 0);
      C_PUT_BYTE (pNcpi->b1, 0);
      C_PUT_BYTE (pNcpi->b2, 0);
      bcopy ((const u_int8_t *) pIndData + sizeof (*pIndData),
             (u_int8_t *) pNcpi + sizeof (*pNcpi),
             pIndData->wDataLength);
   }
   C_PUT_WORD (pCapiMsg->head.wLen, sizeof (pCapiMsg->head) + nLen + 1);
   pmb->m_len = sizeof (pCapiMsg->head) + nLen + 1;

   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Send Connect-B3-Indication",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));

   mtx_unlock (&(pPortData->pSc->mtxAccess));
   kcapi_ctlr_receive_capi_message
      (pPortData->uUniqueCapiCtlrNum, pNcciData->uApplID, pmb);
   mtx_lock (&(pPortData->pSc->mtxAccess));

} /* daicncci_handle_connect_ind */





/**
 * Handle a controller Connect-Ack-Indication.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */

static void daicncci_handle_connect_ack_ind
   (DaicPortData_t      *pPortData,
    DaicNcciData_t      *pNcciData,
    const DaicIndData_t *pIndData)
{
   struct mbuf   *pmb;
   CAPIMsg_t     *pCapiMsg;
   CAPIStdNCPI_t *pNcpi;
   size_t         nLen;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got controller Connect-Ack-Indication",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: NCCI %u: State %d --> %d",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        (int) DAIC_NCCI_STATE_NACT);
   pNcciData->state = DAIC_NCCI_STATE_NACT;
   
   /* get a new mbuf for a Connect-B3-Active-Indication; if there are some bytes
    * in the data part of the indication, they must be reported within an NCPI
    * that is the only info part of the message
    */
   nLen = (size_t) (pIndData->wDataLength);
   if (nLen > 0 && (pNcciData->ulFlags & DAIC_NCCI_FLAG_USE_NCPI) != 0)
   {
      nLen += sizeof (CAPIStdNCPI_t) - 1;
   }
   else
   {
      nLen = 0;
   }
   pmb = kcapi_get_mbuf (sizeof (pCapiMsg->head) + nLen + 1);
   if (pmb == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of mbufs for Connect-B3-Active-Indication",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
      return;
   }
   
   /* fill the message and the NCPI if necessary */
   pCapiMsg = mtod (pmb, CAPIMsg_t *);
   C_PUT_WORD (pCapiMsg->head.wApp, pNcciData->uApplID);
   C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_CONNECT_B3_ACTIVE));
   C_PUT_WORD (pCapiMsg->head.wNum, pPortData->uCapiIndMsgNum);
   ++(pPortData->uCapiIndMsgNum);
   C_PUT_DWORD (pCapiMsg->head.dwCid, pNcciData->dwCid);
   pNcpi = (CAPIStdNCPI_t *) &(pCapiMsg->info.any.b [0]);
   C_PUT_BYTE (pNcpi->bLength, nLen);
   if (nLen != 0)
   {
      C_PUT_BYTE (pNcpi->b0, 0);
      C_PUT_BYTE (pNcpi->b1, 0);
      C_PUT_BYTE (pNcpi->b2, 0);
      bcopy ((const u_int8_t *) pIndData + sizeof (*pIndData),
             (u_int8_t *) pNcpi + sizeof (*pNcpi),
             pIndData->wDataLength);
   }
   C_PUT_WORD (pCapiMsg->head.wLen, sizeof (pCapiMsg->head) + nLen + 1);
   pmb->m_len = sizeof (pCapiMsg->head) + nLen + 1;

   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Send Connect-B3-Active-Indication",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));

   mtx_unlock (&(pPortData->pSc->mtxAccess));
   kcapi_ctlr_receive_capi_message
      (pPortData->uUniqueCapiCtlrNum, pNcciData->uApplID, pmb);
   mtx_lock (&(pPortData->pSc->mtxAccess));

} /* daicncci_handle_connect_ack_ind */





/**
 * Handle a controller Disconnect-Indication.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */

static void daicncci_handle_disconnect_ind
   (DaicPortData_t      *pPortData,
    DaicNcciData_t      *pNcciData,
    const DaicIndData_t *pIndData)
{
   struct mbuf   *pmb;
   CAPIMsg_t     *pCapiMsg;
   CAPIStdNCPI_t *pNcpi;
   DaicReqData_t *pReqData;
   size_t         nLen;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got controller Disconnect-Indication",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: NCCI %u: State %d --> %d",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        (int) DAIC_NCCI_STATE_N5);
   pNcciData->state = DAIC_NCCI_STATE_N5;
   
   /* get a new mbuf for a Disconnect-B3-Indication; if there are some bytes in
    * the data part of the indication, they must be reported within an NCPI
    */
   nLen = (size_t) (pIndData->wDataLength);
   if (nLen > 0 && (pNcciData->ulFlags & DAIC_NCCI_FLAG_USE_NCPI) != 0)
   {
      nLen += sizeof (CAPIStdNCPI_t) - 1;
   }
   else
   {
      nLen = 0;
   }
   pmb = kcapi_get_mbuf (sizeof (pCapiMsg->head) +
                            sizeof (pCapiMsg->info.disconnect_b3_ind) +
                            nLen + 1);
   if (pmb == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of mbufs for Disconnect-B3-Indication",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
      return;
   }
   
   /* fill the message and the NCPI if necessary */
   pCapiMsg = mtod (pmb, CAPIMsg_t *);
   C_PUT_WORD (pCapiMsg->head.wApp, pNcciData->uApplID);
   C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_DISCONNECT_B3));
   C_PUT_WORD (pCapiMsg->head.wNum, pPortData->uCapiIndMsgNum);
   ++(pPortData->uCapiIndMsgNum);
   C_PUT_DWORD (pCapiMsg->head.dwCid, pNcciData->dwCid);
   C_PUT_WORD (pCapiMsg->info.disconnect_b3_ind.wReason,
               pNcciData->uDisconnectReason);
   pNcpi = (CAPIStdNCPI_t *)
              &(pCapiMsg->info.any.b
                   [sizeof (pCapiMsg->info.disconnect_b3_ind)]);
   C_PUT_BYTE (pNcpi->bLength, nLen);
   if (nLen != 0)
   {
      C_PUT_BYTE (pNcpi->b0, 0);
      C_PUT_BYTE (pNcpi->b1, 0);
      C_PUT_BYTE (pNcpi->b2, 0);
      bcopy ((const u_int8_t *) pIndData + sizeof (*pIndData),
             (u_int8_t *) pNcpi + sizeof (*pNcpi),
             pIndData->wDataLength);
   }
   C_PUT_WORD (pCapiMsg->head.wLen,
               sizeof (pCapiMsg->head) +
                  sizeof (pCapiMsg->info.disconnect_b3_ind) + nLen + 1);
   pmb->m_len = sizeof (pCapiMsg->head) +
                sizeof (pCapiMsg->info.disconnect_b3_ind) + nLen + 1;

   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Send Disconnect-B3-Indication with reason 0x%04X",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        pNcciData->uDisconnectReason);

   mtx_unlock (&(pPortData->pSc->mtxAccess));
   kcapi_ctlr_receive_capi_message
      (pPortData->uUniqueCapiCtlrNum, pNcciData->uApplID, pmb);
   mtx_lock (&(pPortData->pSc->mtxAccess));

   /* Acknowledge the disconnect against the controller now. When the
    * Disconnect-B3-Response is handled, the network id will completely be in
    * the idle state.
    */
   pmb = kcapi_get_mbuf (sizeof (*pReqData));
   if (pmb == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of mbufs for Disconnect-Ack-Request",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));

      /* If the Disconnect-Ack-Request cannot be sent, we can only hope that the
       * Remove-Request will be successful.
       */
      return;
   }
   pReqData = mtod (pmb, DaicReqData_t *);
   pReqData->bReq                    = DAIC_NL_DISC_ACK;
   pReqData->bReqId                  = (u_int8_t) (pNcciData->uNetId);
   pReqData->bReqCh                  = (u_int8_t) (pNcciData->uNetCh);
   pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_UNUSED;
   pReqData->wAssignReqPlci          = 0;
   pReqData->wDataLength             = 0;
   pmb->m_len = sizeof (*pReqData);
   
   /* register the pending request for this NCCI */
   pNcciData->uCurrRequest  = (unsigned) (pReqData->bReq);
   pNcciData->ulFlags      |= DAIC_NCCI_FLAG_REQUEST_PENDING;
   _IF_ENQUEUE (&(pPortData->reqQueue), pmb);
   
} /* daicncci_handle_disconnect_ind */





/**
 * Handle a controller Disconnect-Ack-Indication.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */

static void daicncci_handle_disconnect_ack_ind
   (DaicPortData_t      *pPortData,
    DaicNcciData_t      *pNcciData,
    const DaicIndData_t *pIndData)
{
   struct mbuf   *pmb;
   CAPIMsg_t     *pCapiMsg;
   CAPIStdNCPI_t *pNcpi;
   size_t         nLen;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got controller Disconnect-Ack-Indication",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: NCCI %u: State %d --> %d",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        (int) DAIC_NCCI_STATE_N5);
   pNcciData->state = DAIC_NCCI_STATE_N5;
   
   /* get a new mbuf for a Disconnect-B3-Indication; if there are some bytes in
    * the data part of the indication, they must be reported within an NCPI
    */
   nLen = (size_t) (pIndData->wDataLength);
   if (nLen > 0 && (pNcciData->ulFlags & DAIC_NCCI_FLAG_USE_NCPI) != 0)
   {
      nLen += sizeof (CAPIStdNCPI_t) - 1;
   }
   else
   {
      nLen = 0;
   }
   pmb = kcapi_get_mbuf (sizeof (pCapiMsg->head) +
                            sizeof (pCapiMsg->info.disconnect_b3_ind) +
                            nLen + 1);
   if (pmb == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of mbufs for Disconnect-B3-Indication",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
      return;
   }
   
   /* fill the message and the NCPI if necessary */
   pCapiMsg = mtod (pmb, CAPIMsg_t *);
   C_PUT_WORD (pCapiMsg->head.wApp, pNcciData->uApplID);
   C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_DISCONNECT_B3));
   C_PUT_WORD (pCapiMsg->head.wNum, pPortData->uCapiIndMsgNum);
   ++(pPortData->uCapiIndMsgNum);
   C_PUT_DWORD (pCapiMsg->head.dwCid, pNcciData->dwCid);
   C_PUT_WORD (pCapiMsg->info.disconnect_b3_ind.wReason,
               pNcciData->uDisconnectReason);
   pNcpi = (CAPIStdNCPI_t *)
              &(pCapiMsg->info.any.b
                   [sizeof (pCapiMsg->info.disconnect_b3_ind)]);
   C_PUT_BYTE (pNcpi->bLength, nLen);
   if (nLen != 0)
   {
      C_PUT_BYTE (pNcpi->b0, 0);
      C_PUT_BYTE (pNcpi->b1, 0);
      C_PUT_BYTE (pNcpi->b2, 0);
      bcopy ((const u_int8_t *) pIndData + sizeof (*pIndData),
             (u_int8_t *) pNcpi + sizeof (*pNcpi),
             pIndData->wDataLength);
   }
   C_PUT_WORD (pCapiMsg->head.wLen,
               sizeof (pCapiMsg->head) +
                  sizeof (pCapiMsg->info.disconnect_b3_ind) + nLen + 1);
   pmb->m_len = sizeof (pCapiMsg->head) +
                sizeof (pCapiMsg->info.disconnect_b3_ind) + nLen + 1;

   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Send Disconnect-B3-Indication with reason 0x%04X",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        pNcciData->uDisconnectReason);

   mtx_unlock (&(pPortData->pSc->mtxAccess));
   kcapi_ctlr_receive_capi_message
      (pPortData->uUniqueCapiCtlrNum, pNcciData->uApplID, pmb);
   mtx_lock (&(pPortData->pSc->mtxAccess));

   /* now wait for the Disconnect-B3-Response to remove the network id */
   
} /* daicncci_handle_disconnect_ack_ind */





/**
 * Handle a controller More-Data-Indication.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */

static void daicncci_handle_mdata_ind
   (DaicPortData_t      *pPortData,
    DaicNcciData_t      *pNcciData,
    const DaicIndData_t *pIndData)
{
   DBG (LOG_DATAMSG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got controller More-Data-Indication, length %u, total length %u, final indication 0x%02X",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        (unsigned) (pIndData->wDataLength), (unsigned) (pIndData->wMLength),
        (unsigned) (pIndData->bMInd));

   /* if there is currently no data mbuf to store the data, allocate one of the
    * total data block length
    */
   if (pNcciData->pmbReceiveData == NULL)
   {
      pNcciData->pmbReceiveData = kcapi_get_mbuf (pIndData->wMLength);
      if (pNcciData->pmbReceiveData == NULL)
      {
         DBG (LOG_ERROR, pPortData->iUnit,
              "Port %u: NCCI %u: State %d: Out of mbufs for More-Data-Indication of length %u",
              pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
              (unsigned) (pIndData->wMLength));
         return;
      }
      /* Note: The maximum length is in the member m_len of the mbuf, the
       *       current fill index is pNcciData->uCurrReceiveData.
       */
      pNcciData->uCurrReceiveData = 0;
   }

   /* check for enough room to store the indicated data */
   if (pNcciData->uCurrReceiveData + pIndData->wDataLength >
          pNcciData->pmbReceiveData->m_len)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Received data block (%u+%u) longer than declared previously (%u)",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           pNcciData->uCurrReceiveData, (unsigned) (pIndData->wDataLength),
           pNcciData->pmbReceiveData->m_len);
      /* Note: The current fill state above the limit is a sign to reject the
       *       inconsistent data block later.
       */
      pNcciData->uCurrReceiveData = pNcciData->pmbReceiveData->m_len + 1;
      return;
   }
   
   /* copy the indicated data to the collection buffer */
   bcopy ((const u_int8_t *) pIndData + sizeof (*pIndData),
          &(mtod (pNcciData->pmbReceiveData, u_int8_t *)
               [pNcciData->uCurrReceiveData]),
          pIndData->wDataLength);
   pNcciData->uCurrReceiveData += pIndData->wDataLength;
   
} /* daicncci_handle_mdata_ind */





/**
 * Handle a controller Data-Indication.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */

static void daicncci_handle_data_ind
   (DaicPortData_t      *pPortData,
    DaicNcciData_t      *pNcciData,
    const DaicIndData_t *pIndData)
{
   DBG (LOG_DATAMSG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got controller Data-Indication 0x%02X, length %u, total length %u",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        (unsigned) (pIndData->bInd),
        (unsigned) (pIndData->wDataLength), (unsigned) (pIndData->wMLength));

   /* if there is currently no data mbuf to store the data, allocate one of the
    * signaled data block length or one of the maximum length for voice
    * connections
    */
   if (pNcciData->pmbReceiveData == NULL)
   {
      if ((pNcciData->ulFlags & DAIC_NCCI_FLAG_TRANSPARENT) == 0)
      {
         pNcciData->pmbReceiveData = kcapi_get_mbuf (pIndData->wDataLength);
      }
      else
      {
         pNcciData->pmbReceiveData = kcapi_get_mbuf (pNcciData->uMaxDataLen);
      }
      if (pNcciData->pmbReceiveData == NULL)
      {
         DBG (LOG_ERROR, pPortData->iUnit,
              "Port %u: NCCI %u: State %d: Out of mbufs for Data-Indication 0x%02X of length %u",
              pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
              (unsigned) (pIndData->bInd),
              ((pNcciData->ulFlags & DAIC_NCCI_FLAG_TRANSPARENT) == 0)
                 ? (unsigned) (pIndData->wDataLength)
                 : pNcciData->uMaxDataLen);
         return;
      }
      /* Note: The maximum length is in the member m_len of the mbuf, the
       *       current fill index is pNcciData->uCurrReceiveData.
       */
      pNcciData->uCurrReceiveData = 0;
   }

   /* check for enough room to store the indicated data */
   if (pNcciData->uCurrReceiveData + (unsigned) (pIndData->wDataLength) >
          pNcciData->pmbReceiveData->m_len)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Received data block (%u+%u) longer than declared previously (%u) for Data-Indication 0x%02X",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           pNcciData->uCurrReceiveData, (unsigned) (pIndData->wDataLength),
           pNcciData->pmbReceiveData->m_len, (unsigned) (pIndData->bInd));
      /* Note: The current fill state above the limit is a sign to reject the
       *       inconsistent data block later.
       */
      pNcciData->uCurrReceiveData = pNcciData->pmbReceiveData->m_len + 1;
   }
   /* copy the indicated data to the collection buffer */
   else
   {
      bcopy ((const u_int8_t *) pIndData + sizeof (*pIndData),
             &(mtod (pNcciData->pmbReceiveData, u_int8_t *)
                  [pNcciData->uCurrReceiveData]),
             pIndData->wDataLength);
      pNcciData->uCurrReceiveData += (unsigned) (pIndData->wDataLength);
      /* for non-voice connections the block _must_ now be filled up, for voice
       * connections we may need to collect several Data-Indications before a
       * Data-B3-Indication is created
       */
      if ((pNcciData->ulFlags & DAIC_NCCI_FLAG_TRANSPARENT) == 0)
      {
         pNcciData->pmbReceiveData->m_len = pNcciData->uCurrReceiveData;
      }
   }
   
   /* only data blocks of valid length are forwarded */
   if (pNcciData->pmbReceiveData->m_len == pNcciData->uCurrReceiveData)
   {
      struct mbuf *pmbCapiMsg;
      CAPIMsg_t   *pCapiMsg;
      u_int16_t    wFlags;
      
      pmbCapiMsg = kcapi_get_mbuf (sizeof (pCapiMsg->head) +
                                      sizeof (pCapiMsg->info.data_b3_ind));
      if (pmbCapiMsg == NULL)
      {
         DBG (LOG_ERROR, pPortData->iUnit,
              "Port %u: NCCI %u: State %d: Out of mbufs for Data-B3-Indication",
              pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
         kcapi_free_mbuf (pNcciData->pmbReceiveData);
         pNcciData->pmbReceiveData = NULL;
         return;
      }
      
      pCapiMsg = mtod (pmbCapiMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pNcciData->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_DATA_B3));
      C_PUT_WORD (pCapiMsg->head.wNum, pPortData->uCapiIndMsgNum);
      ++(pPortData->uCapiIndMsgNum);
      C_PUT_DWORD (pCapiMsg->head.dwCid, pNcciData->dwCid);
      C_PUT_DWORD (pCapiMsg->info.data_b3_ind.dwData, 0);
      C_PUT_WORD (pCapiMsg->info.data_b3_ind.wLen,
                  pNcciData->pmbReceiveData->m_len);
      C_PUT_WORD (pCapiMsg->info.data_b3_ind.wHandle,
                  pNcciData->uDataIndHandle);
      C_PUT_QWORD (pCapiMsg->info.data_b3_ind.qwData64, 0);
      ++(pNcciData->uDataIndHandle);
      wFlags = 0;
      switch (pIndData->bInd & 0x0F)
      {
         default:
         case DAIC_NL_DATA:
            break;
            
         case DAIC_NL_EDATA:
            wFlags |= CAPI_DATA_B3_FLAG_EXPEDITED;
            break;
            
         case DAIC_NL_UDATA:
            wFlags |= CAPI_DATA_B3_FLAG_UI_FRAME;
            break;
      }
      if ((pIndData->bInd & DAIC_NL_CMDBIT_DEL_CONF) != 0)
      {
         wFlags |= CAPI_DATA_B3_FLAG_DELIVERY_CONF;
      }
      if ((pIndData->bInd & DAIC_NL_CMDBIT_MORE_DATA) != 0)
      {
         wFlags |= CAPI_DATA_B3_FLAG_MORE_DATA;
      }
      if ((pIndData->bInd & DAIC_NL_CMDBIT_QUALIFIER) != 0)
      {
         wFlags |= CAPI_DATA_B3_FLAG_QUALIFIER;
      }
      C_PUT_WORD (pCapiMsg->info.data_b3_ind.wFlags, wFlags);
      C_PUT_WORD (pCapiMsg->head.wLen,
                  sizeof (pCapiMsg->head) +
                     sizeof (pCapiMsg->info.data_b3_ind));
      
      /* transfer the data mbuf to the CAPI message mbuf */
      pmbCapiMsg->m_next = pNcciData->pmbReceiveData;
      pNcciData->pmbReceiveData = NULL;
      
      DBG (LOG_DATAMSG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Send Data-B3-Indication, handle %u, length %u",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (unsigned) C_GET_WORD (pCapiMsg->info.data_b3_ind.wHandle),
           pmbCapiMsg->m_next->m_len);

      mtx_unlock (&(pPortData->pSc->mtxAccess));
      kcapi_ctlr_receive_capi_message
         (pPortData->uUniqueCapiCtlrNum, pNcciData->uApplID, pmbCapiMsg);
      mtx_lock (&(pPortData->pSc->mtxAccess));
   }
   /* for non-voice connections an incomplete or "overfilled" buffer will be
    * discarded (should only happen in the case of software errors)
    */
   else if ((pNcciData->ulFlags & DAIC_NCCI_FLAG_TRANSPARENT) == 0)
   {
      kcapi_free_mbuf (pNcciData->pmbReceiveData);
      pNcciData->pmbReceiveData = NULL;
   }
   /* for voice connections the data buffer may still be incomplete, wait for
    * the next data block
    */
   
} /* daicncci_handle_data_ind */





/**
 * Handle a controller Reset-Indication.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */

static void daicncci_handle_reset_ind
   (DaicPortData_t      *pPortData,
    DaicNcciData_t      *pNcciData,
    const DaicIndData_t *pIndData)
{
   struct mbuf   *pmb;
   CAPIMsg_t     *pCapiMsg;
   CAPIStdNCPI_t *pNcpi;
   size_t         nLen;
   DaicReqData_t *pReqData;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got controller Reset-Indication",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));

   /* get a new mbuf for a Reset-B3-Indication; if there are some bytes in the
    * data part of the indication, they must be reported within an NCPI
    */
   nLen = (size_t) (pIndData->wDataLength);
   if (nLen > 0 && (pNcciData->ulFlags & DAIC_NCCI_FLAG_USE_NCPI) != 0)
   {
      nLen += sizeof (CAPIStdNCPI_t) - 1;
   }
   else
   {
      nLen = 0;
   }
   pmb = kcapi_get_mbuf (sizeof (pCapiMsg->head) + nLen + 1);
   if (pmb == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of mbufs for Reset-B3-Indication because of controller Reset-Indication",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
      pNcciData->ulFlags &= ~DAIC_NCCI_FLAG_GOT_RESET;
      return;
   }
   
   /* fill the message and the NCPI if necessary */
   pCapiMsg = mtod (pmb, CAPIMsg_t *);
   C_PUT_WORD (pCapiMsg->head.wApp, pNcciData->uApplID);
   C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_RESET_B3_20));
   C_PUT_WORD (pCapiMsg->head.wNum, pPortData->uCapiIndMsgNum);
   ++(pPortData->uCapiIndMsgNum);
   C_PUT_DWORD (pCapiMsg->head.dwCid, pNcciData->dwCid);
   pNcpi = (CAPIStdNCPI_t *) &(pCapiMsg->info.any.b [0]);
   C_PUT_BYTE (pNcpi->bLength, nLen);
   if (nLen != 0)
   {
      C_PUT_BYTE (pNcpi->b0, 0);
      C_PUT_BYTE (pNcpi->b1, 0);
      C_PUT_BYTE (pNcpi->b2, 0);
      bcopy ((const u_int8_t *) pIndData + sizeof (*pIndData),
             (u_int8_t *) pNcpi + sizeof (*pNcpi),
             pIndData->wDataLength);
   }
   C_PUT_WORD (pCapiMsg->head.wLen, sizeof (pCapiMsg->head) + nLen + 1);
   pmb->m_len = sizeof (pCapiMsg->head) + nLen + 1;

   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Send Reset-B3-Indication",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));

   mtx_unlock (&(pPortData->pSc->mtxAccess));
   kcapi_ctlr_receive_capi_message
      (pPortData->uUniqueCapiCtlrNum, pNcciData->uApplID, pmb);
   mtx_lock (&(pPortData->pSc->mtxAccess));

   /* Acknowledge the reset against the controller now. When the
    * Reset-B3-Response is handled, the reset operation will already be
    * complete, the Reset-Ack-Request return code will have been received.
    */
   pmb = kcapi_get_mbuf (sizeof (*pReqData));
   if (pmb == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of mbufs for controller Reset-Ack-Request",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
      pNcciData->ulFlags &= ~DAIC_NCCI_FLAG_GOT_RESET;
      return;
   }
   pReqData = mtod (pmb, DaicReqData_t *);
   pReqData->bReq                    = DAIC_NL_RESET_ACK;
   pReqData->bReqId                  = (u_int8_t) (pNcciData->uNetId);
   pReqData->bReqCh                  = (u_int8_t) (pNcciData->uNetCh);
   pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_UNUSED;
   pReqData->wAssignReqPlci          = 0;
   pReqData->wDataLength             = 0;
   pmb->m_len = sizeof (*pReqData);
   
   /* register the pending request for this NCCI */
   pNcciData->uCurrRequest  = (unsigned) (pReqData->bReq);
   pNcciData->ulFlags      |= DAIC_NCCI_FLAG_REQUEST_PENDING;
   _IF_ENQUEUE (&(pPortData->reqQueue), pmb);
   
   /* now the reset phase is over */
   pNcciData->ulFlags &= ~DAIC_NCCI_FLAG_GOT_RESET;
   
} /* daicncci_handle_reset_ind */





/**
 * Handle a controller Reset-Ack-Indication.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */

static void daicncci_handle_reset_ack_ind
   (DaicPortData_t      *pPortData,
    DaicNcciData_t      *pNcciData,
    const DaicIndData_t *pIndData)
{
   struct mbuf   *pmb;
   CAPIMsg_t     *pCapiMsg;
   CAPIStdNCPI_t *pNcpi;
   size_t         nLen;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got controller Reset-Ack-Indication",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));

   /* get a new mbuf for a Reset-B3-Indication; if there are some bytes in the
    * data part of the indication, they must be reported within an NCPI
    */
   nLen = (size_t) (pIndData->wDataLength);
   if (nLen > 0 && (pNcciData->ulFlags & DAIC_NCCI_FLAG_USE_NCPI) != 0)
   {
      nLen += sizeof (CAPIStdNCPI_t) - 1;
   }
   else
   {
      nLen = 0;
   }
   pmb = kcapi_get_mbuf (sizeof (pCapiMsg->head) + nLen + 1);
   if (pmb == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of mbufs for Reset-B3-Indication because of controller Reset-Ack-Indication",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
      pNcciData->ulFlags &= ~DAIC_NCCI_FLAG_GOT_RESET;
      return;
   }
   
   /* fill the message and the NCPI if necessary */
   pCapiMsg = mtod (pmb, CAPIMsg_t *);
   C_PUT_WORD (pCapiMsg->head.wApp, pNcciData->uApplID);
   C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_RESET_B3_20));
   C_PUT_WORD (pCapiMsg->head.wNum, pPortData->uCapiIndMsgNum);
   ++(pPortData->uCapiIndMsgNum);
   C_PUT_DWORD (pCapiMsg->head.dwCid, pNcciData->dwCid);
   pNcpi = (CAPIStdNCPI_t *) &(pCapiMsg->info.any.b [0]);
   C_PUT_BYTE (pNcpi->bLength, nLen);
   if (nLen != 0)
   {
      C_PUT_BYTE (pNcpi->b0, 0);
      C_PUT_BYTE (pNcpi->b1, 0);
      C_PUT_BYTE (pNcpi->b2, 0);
      bcopy ((const u_int8_t *) pIndData + sizeof (*pIndData),
             (u_int8_t *) pNcpi + sizeof (*pNcpi),
             pIndData->wDataLength);
   }
   C_PUT_WORD (pCapiMsg->head.wLen, sizeof (pCapiMsg->head) + nLen + 1);
   pmb->m_len = sizeof (pCapiMsg->head) + nLen + 1;

   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Send Reset-B3-Indication",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));

   mtx_unlock (&(pPortData->pSc->mtxAccess));
   kcapi_ctlr_receive_capi_message
      (pPortData->uUniqueCapiCtlrNum, pNcciData->uApplID, pmb);
   mtx_lock (&(pPortData->pSc->mtxAccess));

   /* now the reset phase is over */
   pNcciData->ulFlags &= ~DAIC_NCCI_FLAG_GOT_RESET;
   
} /* daicncci_handle_reset_ack_ind */





/**
 * Ignore any controller Indication.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */

static void daicncci_ignore_ind
   (DaicPortData_t      *pPortData,
    DaicNcciData_t      *pNcciData,
    const DaicIndData_t *pIndData)
{
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got controller Indication 0x%02X, will be ignored",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        (unsigned) (pIndData->bInd));
   
} /* daicncci_ignore_ind */





/**
 * Handle a CAPI Connect-B3-Request.
 *
 * A Connect-B3-Request is always handled in the context of NCCI 0. This is
 * necessary to be able to assign the result for the corresponding controller
 * request to the correct NCCI. Only for Assign-Request we have the possibility
 * to attach some data for identifying the originator of the request. But a
 * Connect-B3-Request results in a controller Connect-Request. And the return
 * code for it consists only of the result value, the network id and a newly
 * assigned network channel id. Because this channel id is not known until this
 * point, the dispatch module cannot forward the result to the right NCCI state
 * machine.
 *
 * The solution is to execute all Connect-B3-Requests through NCCI 0 and to
 * serialize them among all connections on a single controller port. We first
 * allocate a new NCCI to be sure this is possible. The NCCI value is stored
 * into the NCCI 0 state machine, so we always know, what real NCCI we are
 * executing the current request for.
 *
 * After allocating and initializing the new NCCI structure, we send out a
 * controller Connect-Request. The return code will deliver a newly assigned
 * channel id that will later be used to address the real NCCI. Because the
 * dispatch module does not know the new channel id when the return code is
 * dispatched, the return code will be delivered to NCCI 0. Here we are waiting
 * for this result. It is transfered to the formerly allocated real NCCI and
 * processing for the new network connection will be done there.
 *
 * @note If the result value is CAPI_OK, the mbuf is either already released or
 *       its address is stored in internal data members. If the result is not
 *       CAPI_OK, the caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicncci_handle_connect_b3_req
   (DaicPortData_t *pPortData,
    DaicNcciData_t *pNcciData,
    struct mbuf    *pmbMsg)
{
   CAPIMsg_t      *pCapiReq = mtod (pmbMsg, CAPIMsg_t *);
   u_int32_t       dwCid;
   unsigned        uPlci;
   DaicPlciData_t *pPlciData;
   DaicNcciData_t *pNewNcciData;
   unsigned        uRes;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got Connect-B3-Request",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));

   /* if this is not NCCI 0 we must reject the message */
   if (pNcciData->uNcci != 0)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: CAPI Connect-B3-Request is only handled over NCCI 0 (internal driver error)",
           pPortData->uPortIdx, pNcciData->uNcci);
      return (CME_ILLEGAL_COMMAND);
   }
   
   /* first store the mbuf for later sending a matching confirmation to the
    * application; the handler function for the return code will rely on this
    */
   pNcciData->pmbCurrCapiMsg     = pmbMsg;
   pNcciData->uCurrCapiReqMsgNum = CAPI_GET_MSGNUM (pCapiReq);
   
   /* check for a valid PLCI value in the CAPI message */
   dwCid = CAPI_GET_CID (pCapiReq);
   uPlci = CAPI_GET_PLCI_FROM_CID (dwCid);
   if (uPlci < 1 || uPlci >= ARRAY_COUNT (pPortData->aPlciData) ||
       pPortData->aPlciData [uPlci].uPlci == 0)
   {
      DBG (LOG_INFO, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Got Connect-B3-Request with invalid CID 0x%04X, PLCI not active",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (unsigned) dwCid);
      daicncci_send_connect_b3_conf
         (pPortData, pNcciData, pCapiReq, CCE_ILLEGAL_IDENTIFIER);
      kcapi_free_mbuf (pNcciData->pmbCurrCapiMsg);
      pNcciData->pmbCurrCapiMsg = NULL;
      return (CAPI_OK);
   }
   pPlciData = &(pPortData->aPlciData [uPlci]);
   
   /* check if the related PLCI is assigned to the same application id as in
    * the CAPI message
    */
   if (pPlciData->uApplID != CAPI_GET_APPL (pCapiReq))
   {
      DBG (LOG_INFO, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Connect-B3-Request invalid, has application id %u, PLCI %u has application id %u",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (unsigned) CAPI_GET_APPL (pCapiReq), uPlci, pPlciData->uApplID);
      daicncci_send_connect_b3_conf
         (pPortData, pNcciData, pCapiReq, CME_INVALID_APPLICATION_ID);
      kcapi_free_mbuf (pNcciData->pmbCurrCapiMsg);
      pNcciData->pmbCurrCapiMsg = NULL;
      return (CAPI_OK);
   }
   
   /* check if the related PLCI is really in active state; if it is not we must
    * reject the Connect-B3-Request because of an invalid state
    */
   if (pPlciData->state != DAIC_PLCI_STATE_PACT)
   {
      DBG (LOG_INFO, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Connect-B3-Request not allowed, because related PLCI %u is not in active state (%d)",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           uPlci, (int) (pPlciData->state));
      daicncci_send_connect_b3_conf
         (pPortData, pNcciData, pCapiReq, CCE_MSG_NOT_ALLOWED_NOW);
      kcapi_free_mbuf (pNcciData->pmbCurrCapiMsg);
      pNcciData->pmbCurrCapiMsg = NULL;
      return (CAPI_OK);
   }
   
   /* check if the related PLCI has a valid network id assigned; a
    * Select-B-Protocol-Request may fail and leave the network id unallocated
    * although P-ACT is set
    */
   if (pPlciData->uNetId == 0)
   {
      DBG (LOG_INFO, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Connect-B3-Request not allowed, because PLCI %u has not network id allocated",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           uPlci);
      daicncci_send_connect_b3_conf
         (pPortData, pNcciData, pCapiReq, CCE_MSG_NOT_ALLOWED_NOW);
      kcapi_free_mbuf (pNcciData->pmbCurrCapiMsg);
      pNcciData->pmbCurrCapiMsg = NULL;
      return (CAPI_OK);
   }
   
   /* so the message is valid, allocate a new NCCI for later proceeding with the
    * network connection (currently with channelid 0)
    */
   if (pPlciData->nNumNcci >= DAIC_MAX_NCCI_PER_PLCI)
   {
      DBG (LOG_INFO, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of NCCIs for PLCI %u, already maximum of %zu allocated",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           pPlciData->uPlci, pPlciData->nNumNcci);
      daicncci_send_connect_b3_conf
         (pPortData, pNcciData, pCapiReq, CCE_NO_NCCI_AVAILABLE);
      kcapi_free_mbuf (pNcciData->pmbCurrCapiMsg);
      pNcciData->pmbCurrCapiMsg = NULL;
      return (CAPI_OK);
   }
   pNewNcciData = daicncci_alloc_new_ncci
                     (pPortData, pPlciData, 0);
   if (pNewNcciData == NULL)
   {
      DBG (LOG_INFO, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of NCCIs for PLCI %u, all available NCCIs busy",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           pPlciData->uPlci);
      daicncci_send_connect_b3_conf
         (pPortData, pNcciData, pCapiReq, CCE_NO_NCCI_AVAILABLE);
      kcapi_free_mbuf (pNcciData->pmbCurrCapiMsg);
      pNcciData->pmbCurrCapiMsg = NULL;
      return (CAPI_OK);
   }
                     
   /* everything is fine, initialize _this_ NCCI with some necessary data */
   pNcciData->uNcci       = pNewNcciData->uNcci;
   pNcciData->uPlci       = uPlci;
   pNcciData->dwCid       = pNewNcciData->dwCid;
   pNcciData->uNetId      = pNewNcciData->uNetId;
   pNcciData->uNetCh      = pNewNcciData->uNetCh;
   pNcciData->uApplID     = pNewNcciData->uApplID;
   pNcciData->uMaxDataLen = pNewNcciData->uMaxDataLen;
   pNcciData->ulFlags     = 0;
   
   /* and finally send out the NL_CONNECT request to the controller */
   uRes = daicncci_send_nl_connect_req
             (pPortData, pNcciData, pPlciData->uSigId);
   if (uRes != CAPI_OK)
   {
      /* error log message is already out, we must release the just allocated
       * NCCI
       */
      daicncci_reset (pPortData, pNewNcciData);
      daicplci_ncci_removed (pPortData, uPlci);
      daicncci_send_connect_b3_conf (pPortData, pNcciData, pCapiReq, uRes);
      kcapi_free_mbuf (pNcciData->pmbCurrCapiMsg);
      pNcciData->pmbCurrCapiMsg = NULL;
      pNcciData->uNcci = 0;
      return (CAPI_OK);
   }
   
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: NCCI %u: State %d --> %d",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        DAIC_NCCI_STATE_N1);
   pNcciData->state = DAIC_NCCI_STATE_N1;
   
   return (CAPI_OK);
} /* daicncci_handle_connect_b3_req */





/**
 * Handle a CAPI Connect-B3-Response.
 *
 * @note If the result value is CAPI_OK, the mbuf is either already released or
 *       its address is stored in internal data members. If the result is not
 *       CAPI_OK, the caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicncci_handle_connect_b3_rsp
   (DaicPortData_t *pPortData,
    DaicNcciData_t *pNcciData,
    struct mbuf    *pmbMsg)
{
   CAPIMsg_t     *pCapiRsp = mtod (pmbMsg, CAPIMsg_t *);
   CAPIStdNCPI_t *pNcpi;
   struct mbuf   *pmbReqData;
   DaicReqData_t *pReqData;
   size_t         nLen;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got Connect-B3-Response, reject value %u",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        (unsigned) C_GET_WORD (pCapiRsp->info.connect_b3_resp.wReject));
   
   /* determine if some bytes must be sent within the Connect-Ack-Request to the
    * controller
    */
   pNcpi = (CAPIStdNCPI_t *)
              &(pCapiRsp->info.any.b [sizeof (pCapiRsp->info.connect_b3_resp)]);
   if ((pNcciData->ulFlags & DAIC_NCCI_FLAG_USE_NCPI) != 0 &&
       C_GET_BYTE (pNcpi->bLength) > sizeof (*pNcpi) - 1)
   {
      nLen = C_GET_BYTE (pNcpi->bLength) - sizeof (*pNcpi) + 1;
   }
   else
   {
      nLen = 0;
   }
   
   /* get a new mbuf for a controller Connect-Ack-Request */
   pmbReqData = kcapi_get_mbuf (sizeof (*pReqData) + nLen);
   if (pmbReqData == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of mbufs for Connect-Ack-Request",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
      return (CME_OS_RESOURCE_ERROR);
   }
   pReqData = mtod (pmbReqData, DaicReqData_t *);
   pReqData->bReq                    = DAIC_NL_CONNECT_ACK;
   pReqData->bReqId                  = (u_int8_t) (pNcciData->uNetId);
   pReqData->bReqCh                  = (u_int8_t) (pNcciData->uNetCh);
   pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_NCCI;
   pReqData->wAssignReqPlci          = (u_int16_t) (pNcciData->uPlci);
   pReqData->wDataLength             = (u_int16_t) nLen;
   if (nLen > 0)
   {
      bcopy ((u_int8_t *) pNcpi + sizeof (*pNcpi),
             (u_int8_t *) pReqData + sizeof (*pReqData),
             nLen);
   }
   pmbReqData->m_len = sizeof (*pReqData) + nLen;
   
   /* send the request to the controller and register the pending request */
   pNcciData->uCurrRequest  = (unsigned) (pReqData->bReq);
   pNcciData->ulFlags      |= DAIC_NCCI_FLAG_REQUEST_PENDING;
   _IF_ENQUEUE (&(pPortData->reqQueue), pmbReqData);
   
   kcapi_free_mbuf (pmbMsg);
   
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: NCCI %u: State %d --> %d",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        (int) DAIC_NCCI_STATE_N2_1);
   pNcciData->state = DAIC_NCCI_STATE_N2_1;

   return (CAPI_OK);
} /* daicncci_handle_connect_b3_rsp */





/**
 * Handle a CAPI Disconnect-B3-Request.
 *
 * @note If the result value is CAPI_OK, the mbuf is either already released or
 *       its address is stored in internal data members. If the result is not
 *       CAPI_OK, the caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicncci_handle_disconnect_b3_req
   (DaicPortData_t *pPortData,
    DaicNcciData_t *pNcciData,
    struct mbuf    *pmbMsg)
{
   CAPIMsg_t     *pCapiReq = mtod (pmbMsg, CAPIMsg_t *);
   CAPIStdNCPI_t *pNcpi;
   struct mbuf   *pmbReqData;
   DaicReqData_t *pReqData;
   size_t         nLen;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got Disconnect-B3-Request",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
   
   /* determine if some bytes must be sent within the Disconnect-Request to the
    * controller
    */
   pNcpi = (CAPIStdNCPI_t *) &(pCapiReq->info.any.b [0]);
   if ((pNcciData->ulFlags & DAIC_NCCI_FLAG_USE_NCPI) != 0 &&
       C_GET_BYTE (pNcpi->bLength) > sizeof (*pNcpi) - 1)
   {
      nLen = C_GET_BYTE (pNcpi->bLength) - sizeof (*pNcpi) + 1;
   }
   else
   {
      nLen = 0;
   }
   
   /* get a new mbuf for a controller Disconnect-Request */
   pmbReqData = kcapi_get_mbuf (sizeof (*pReqData) + nLen);
   if (pmbReqData == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of mbufs for controller Disconnect-Request",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
      return (CME_OS_RESOURCE_ERROR);
   }
   pReqData = mtod (pmbReqData, DaicReqData_t *);
   pReqData->bReq                    = DAIC_NL_DISC;
   pReqData->bReqId                  = (u_int8_t) (pNcciData->uNetId);
   pReqData->bReqCh                  = (u_int8_t) (pNcciData->uNetCh);
   pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_NCCI;
   pReqData->wAssignReqPlci          = (u_int16_t) (pNcciData->uPlci);
   pReqData->wDataLength             = (u_int16_t) nLen;
   if (nLen > 0)
   {
      bcopy ((u_int8_t *) pNcpi + sizeof (*pNcpi),
             (u_int8_t *) pReqData + sizeof (*pReqData),
             nLen);
   }
   pmbReqData->m_len = sizeof (*pReqData) + nLen;
   
   /* send the request to the controller and register the pending request */
   pNcciData->uCurrRequest  = (unsigned) (pReqData->bReq);
   pNcciData->ulFlags      |= DAIC_NCCI_FLAG_REQUEST_PENDING;
   _IF_ENQUEUE (&(pPortData->reqQueue), pmbReqData);
   
   pNcciData->pmbCurrCapiMsg     = pmbMsg;
   pNcciData->uCurrCapiReqMsgNum = CAPI_GET_MSGNUM (pCapiReq);
   
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: NCCI %u: State %d --> %d",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        (int) DAIC_NCCI_STATE_N4);
   pNcciData->state = DAIC_NCCI_STATE_N4;

   return (CAPI_OK);
} /* daicncci_handle_disconnect_b3_req */





/**
 * Handle a CAPI Disconnect-B3-Response.
 *
 * @note If the result value is CAPI_OK, the mbuf is either already released or
 *       its address is stored in internal data members. If the result is not
 *       CAPI_OK, the caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicncci_handle_disconnect_b3_rsp
   (DaicPortData_t *pPortData,
    DaicNcciData_t *pNcciData,
    struct mbuf    *pmbMsg)
{
   unsigned uPlci;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got Disconnect-B3-Response",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));

   kcapi_free_mbuf (pmbMsg);
   
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: NCCI %u: State %d --> %d",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        (int) DAIC_NCCI_STATE_N0);
   pNcciData->state = DAIC_NCCI_STATE_N0;

   /* Note: A reset may only be done for NCCIs not equal to null. NCCI null is
    *       used for allocating new network ids and there may be messages in the
    *       queue that must not be thrown away.
    */
   uPlci = pNcciData->uPlci;
   if (pNcciData->uNcci != 0)
   {
      daicncci_reset (pPortData, pNcciData);
   }
   
   /* notify the PLCI layer of the now released NCCI */
   daicplci_ncci_removed (pPortData, uPlci);
   
   return (CAPI_OK);
} /* daicncci_handle_disconnect_b3_rsp */





/**
 * Handle a CAPI Data-B3-Request.
 *
 * @note If the result value is CAPI_OK, the mbuf is either already released or
 *       its address is stored in internal data members. If the result is not
 *       CAPI_OK, the caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicncci_handle_data_b3_req
   (DaicPortData_t *pPortData,
    DaicNcciData_t *pNcciData,
    struct mbuf    *pmbMsg)
{
   CAPIMsg_t     *pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
   struct mbuf   *pmbData;
   struct mbuf   *pmbReq;
   DaicReqData_t *pReqData;
   u_int16_t      wFlags;
   
   DBG (LOG_DATAMSG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got Data-B3-Request, handle %u, length %u (%u)",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        C_GET_WORD (pCapiMsg->info.data_b3_req.wHandle),
        C_GET_WORD (pCapiMsg->info.data_b3_req.wLen),
        (pmbMsg->m_next != NULL) ? (pmbMsg->m_next->m_len) : 0);

   /* if we just got a Reset-B3-Request or Reset-Indication, just throw away
    * this data block
    */
   if ((pNcciData->ulFlags & DAIC_NCCI_FLAG_GOT_RESET) != 0)
   {
      DBG (LOG_DATAMSG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Ignore Data-B3-Request because of B3-layer reset",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));

      kcapi_free_mbuf (pmbMsg);
      return (CAPI_OK);
   }
   
   /* now allocate a new mbuf for the controller request */
   pmbReq = kcapi_get_mbuf (sizeof (*pReqData));
   if (pmbReq == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of mbufs for Data-Request",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
      return (CME_OS_RESOURCE_ERROR);
   }
   pReqData = mtod (pmbReq, DaicReqData_t *);
   
   /* store the Data-B3-Request for later processing */
   pNcciData->pmbCurrCapiMsg     = pmbMsg;
   pNcciData->uCurrCapiReqMsgNum = CAPI_GET_MSGNUM (pCapiMsg);
   
   /* determine the number of portions needed for the data block */
   pmbData = pmbMsg->m_next;
   if (pmbData != NULL && pmbData->m_len > 0)
   {
      pNcciData->nNumPendingDataRc =
         pmbData->m_len / DAIC_SHMEM_LEN_RXBUFFER + 1;
   }
   else
   {
      pNcciData->nNumPendingDataRc = 0;
   }
   
   /* fill the request with all necessary information and transfer the data
    * block mbuf to the mbuf for the controller request
    */
   wFlags = C_GET_WORD (pCapiMsg->info.data_b3_req.wFlags);
   if ((wFlags & CAPI_DATA_B3_FLAG_EXPEDITED) != 0)
   {
      pReqData->bReq = DAIC_NL_EDATA;
   }
   else if ((wFlags & CAPI_DATA_B3_FLAG_UI_FRAME) != 0)
   {
      pReqData->bReq = DAIC_NL_UDATA;
   }
   else
   {
      pReqData->bReq = DAIC_NL_DATA;
   }
   if ((wFlags & CAPI_DATA_B3_FLAG_QUALIFIER) != 0)
   {
      pReqData->bReq |= DAIC_NL_CMDBIT_QUALIFIER;
   }
   if ((wFlags & CAPI_DATA_B3_FLAG_MORE_DATA) != 0)
   {
      pReqData->bReq |= DAIC_NL_CMDBIT_MORE_DATA;
   }
   if ((wFlags & CAPI_DATA_B3_FLAG_DELIVERY_CONF) != 0)
   {
      pReqData->bReq |= DAIC_NL_CMDBIT_DEL_CONF;
   }
   pReqData->bReqId                  = pNcciData->uNetId;
   pReqData->bReqCh                  = pNcciData->uNetCh;
   pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_NCCI;
   pReqData->wAssignReqPlci          = pNcciData->uPlci;
   pReqData->wDataLength             = 0;
   pmbReq->m_len = sizeof (*pReqData);
   pmbMsg->m_next = NULL;
   pmbReq->m_next = pmbData;
   
   DBG (LOG_DATAMSG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Send Data-Request 0x%02X to controller, length %u",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        (unsigned) (pReqData->bReq),
        (pmbData == NULL) ? 0 : pmbData->m_len);

   /* send the request to the controller */
   pNcciData->uCurrRequest  =
      (unsigned) (pReqData->bReq) & ~DAIC_NL_CMDBIT_MASK;
   pNcciData->ulFlags      |= DAIC_NCCI_FLAG_REQUEST_PENDING;
   _IF_ENQUEUE (&(pPortData->reqQueue), pmbReq);
   
   return (CAPI_OK);
} /* daicncci_handle_data_b3_req */





/**
 * Handle a CAPI Reset-B3-Request.
 *
 * @note If the result value is CAPI_OK, the mbuf is either already released or
 *       its address is stored in internal data members. If the result is not
 *       CAPI_OK, the caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicncci_handle_reset_b3_req
   (DaicPortData_t *pPortData,
    DaicNcciData_t *pNcciData,
    struct mbuf    *pmbMsg)
{
   CAPIMsg_t     *pCapiReq = mtod (pmbMsg, CAPIMsg_t *);
   CAPIStdNCPI_t *pNcpi;
   struct mbuf   *pmbReqData;
   DaicReqData_t *pReqData;
   size_t         nLen;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got Reset-B3-Request",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
   
   /* determine if some bytes must be sent within the Reset-Request to the
    * controller
    */
   pNcpi = (CAPIStdNCPI_t *) &(pCapiReq->info.any.b [0]);
   if ((pNcciData->ulFlags & DAIC_NCCI_FLAG_USE_NCPI) != 0 &&
       C_GET_BYTE (pNcpi->bLength) > sizeof (*pNcpi) - 1)
   {
      nLen = C_GET_BYTE (pNcpi->bLength) - sizeof (*pNcpi) + 1;
   }
   else
   {
      nLen = 0;
   }
   
   /* get a new mbuf for a controller Reset-Request */
   pmbReqData = kcapi_get_mbuf (sizeof (*pReqData) + nLen);
   if (pmbReqData == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of mbufs for controller Reset-Request",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
      pNcciData->ulFlags &= ~DAIC_NCCI_FLAG_GOT_RESET;
      return (CME_OS_RESOURCE_ERROR);
   }
   pReqData = mtod (pmbReqData, DaicReqData_t *);
   pReqData->bReq                    = DAIC_NL_RESET;
   pReqData->bReqId                  = (u_int8_t) (pNcciData->uNetId);
   pReqData->bReqCh                  = (u_int8_t) (pNcciData->uNetCh);
   pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_NCCI;
   pReqData->wAssignReqPlci          = (u_int16_t) (pNcciData->uPlci);
   pReqData->wDataLength             = (u_int16_t) nLen;
   if (nLen > 0)
   {
      bcopy ((u_int8_t *) pNcpi + sizeof (*pNcpi),
             (u_int8_t *) pReqData + sizeof (*pReqData),
             nLen);
   }
   pmbReqData->m_len = sizeof (*pReqData) + nLen;
   
   /* send the request to the controller and register the pending request */
   pNcciData->uCurrRequest  = (unsigned) (pReqData->bReq);
   pNcciData->ulFlags      |= DAIC_NCCI_FLAG_REQUEST_PENDING;
   _IF_ENQUEUE (&(pPortData->reqQueue), pmbReqData);
   
   pNcciData->uCurrCapiReqMsgNum = CAPI_GET_MSGNUM (pCapiReq);
   kcapi_free_mbuf (pmbMsg);
   
   return (CAPI_OK);
} /* daicncci_handle_reset_b3_req */





/**
 * Handle a CAPI Facility-Request.
 *
 * @note If the result value is CAPI_OK, the mbuf is either already released or
 *       its address is stored in internal data members. If the result is not
 *       CAPI_OK, the caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicncci_handle_facility_req
   (DaicPortData_t *pPortData,
    DaicNcciData_t *pNcciData,
    struct mbuf    *pmbMsg)
{
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got Facility-Request, currently not supported",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));

   daicncci_send_gen_conf (pPortData, pNcciData, mtod (pmbMsg, CAPIMsg_t *),
                           CSE_FACILITY_NOT_SUPPORTED);

   kcapi_free_mbuf (pmbMsg);
   
   return (CAPI_OK);
} /* daicncci_handle_facility_req */





/**
 * Ignore any CAPI request message.
 *
 * @note If the result value is CAPI_OK, the mbuf is either already released or
 *       its address is stored in internal data members. If the result is not
 *       CAPI_OK, the caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicncci_ignore_capi_req
   (DaicPortData_t *pPortData,
    DaicNcciData_t *pNcciData,
    struct mbuf    *pmbMsg)
{
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got CAPI request 0x%04X, will be ignored",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        (unsigned) CAPI_GET_CMD (mtod (pmbMsg, CAPIMsg_t *)));

   kcapi_free_mbuf (pmbMsg);
   
   return (CAPI_OK);
} /* daicncci_ignore_capi_req */





/**
 * Handle any CAPI response message.
 *
 * @note If the result value is CAPI_OK, the mbuf is either already released or
 *       its address is stored in internal data members. If the result is not
 *       CAPI_OK, the caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pNcciData             I/O: The NCCI data for the operation context.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicncci_handle_gen_rsp
   (DaicPortData_t *pPortData,
    DaicNcciData_t *pNcciData,
    struct mbuf    *pmbMsg)
{
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Got CAPI response 0x%04X",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        (unsigned) CAPI_GET_CMD (mtod (pmbMsg, CAPIMsg_t *)));

   kcapi_free_mbuf (pmbMsg);
   
   return (CAPI_OK);
} /* daicncci_handle_gen_rsp */





/**
 * Reset a NCCI state machine.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pNcciData             I/O: The NCCI data to be reset.
 *
 * @return Nothing.
 */

static void daicncci_reset
   (DaicPortData_t *pPortData,
    DaicNcciData_t *pNcciData)
{
   struct mbuf *pmb;
   
   /* if there is still a CAPI message in use, release it */
   if (pNcciData->pmbCurrCapiMsg != NULL)
   {
      kcapi_free_mbuf (pNcciData->pmbCurrCapiMsg);
   }

   /* if there is still a data block to be filled, release it */
   if (pNcciData->pmbReceiveData != NULL)
   {
      kcapi_free_mbuf (pNcciData->pmbReceiveData);
   }
   
   /* clear the message queues */
   do
   {
      _IF_DEQUEUE (&(pNcciData->capiMsgQueue), pmb);
      if (pmb != NULL)
      {
         kcapi_free_mbuf (pmb);
      }
   } while (pmb != NULL);
   do
   {
      _IF_DEQUEUE (&(pNcciData->indMsgQueue), pmb);
      if (pmb != NULL)
      {
         kcapi_free_mbuf (pmb);
      }
   } while (pmb != NULL);

   /* reset all member variables */
   bzero (pNcciData, sizeof (*pNcciData));
   pNcciData->capiMsgQueue.ifq_maxlen = 16;
   pNcciData->indMsgQueue.ifq_maxlen = 64;

} /* daicncci_reset */





/**
 * Handle messages enqueued for a NCCI.
 *
 * This function will handle any message enqueued for a given NCCI, until the
 * queue is empty or the operation results in a pending hardware requeust for
 * the controller.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pNcciData             i/O: The NCCI data for this operation.
 *
 * @return Nothing.
 */

static void daicncci_handle_queued_messages
   (DaicPortData_t *pPortData,
    DaicNcciData_t *pNcciData)
{
   struct mbuf *pmb;
   
   /* If there is at least one message in one of the local queues and the state
    * after the handler function does not denote any pending controller request,
    * dequeue the next message and handle it through the message handler.
    * Indications from a controller are handled before CAPI messages (requests
    * or responses).
    */
   while ((pNcciData->ulFlags & DAIC_NCCI_FLAG_REQUEST_PENDING) == 0 &&
          _IF_QLEN (&(pNcciData->indMsgQueue)) > 0)
   {
      _IF_DEQUEUE (&(pNcciData->indMsgQueue), pmb);
      daicncci_handle_ind
         (pPortData, pNcciData, mtod (pmb, DaicIndData_t *));
      kcapi_free_mbuf (pmb);
   }
   while ((pNcciData->ulFlags & DAIC_NCCI_FLAG_REQUEST_PENDING) == 0 &&
          _IF_QLEN (&(pNcciData->capiMsgQueue)) > 0)
   {
      _IF_DEQUEUE (&(pNcciData->capiMsgQueue), pmb);
      if (daicncci_handle_capi_msg
             (pPortData, pNcciData,
              CAPI_GET_APPL (mtod (pmb, CAPIMsg_t *)), pmb) != CAPI_OK)
      {
         kcapi_free_mbuf (pmb);
      }
   }

} /* daicncci_handle_queued_messages */





/**
 * Handle an indication from the controller.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pNcciData             I/O: The NCCI data for this operation.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */

static void daicncci_handle_ind
   (DaicPortData_t      *pPortData,
    DaicNcciData_t      *pNcciData,
    const DaicIndData_t *pIndData)
{
   DaicIndActionFct_t *pfnAction;
   
   /* perform a lookup into the table for action functions according to the
    * current state and the indication received
    */
   pfnAction = g_aIndAction [(int) (pNcciData->state)]
                            [pIndData->bInd & ~DAIC_NL_CMDBIT_MASK];

   /* if there is a valid handler function: call it */
   if (pfnAction != NULL)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Calling action function for indication 0x%02X",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (unsigned) (pIndData->bInd));
      pfnAction (pPortData, pNcciData, pIndData);
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Action function completed",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
   }
   /* if there is no valid handler function, the indication is not expected and
    * the connection must be aborted
    */
   else
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Indication 0x%02X not expected in current state",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (unsigned) (pIndData->bInd));
   }

} /* daicncci_handle_ind */





/**
 * Handle a CAPI message to a NCCI instance.
 *
 * If the result value is CAPI_OK, the mbuf is either already released or its
 * address is stored in internal data members. If the result is not CAPI_OK, the
 * caller must release the mbuf himself.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pNcciData             I/O: The NCCI data for this operation.
 * @param uApplID               I: The application id the CAPI message is from.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicncci_handle_capi_msg
   (DaicPortData_t  *pPortData,
    DaicNcciData_t  *pNcciData,
    unsigned         uApplID,
    struct mbuf     *pmbMsg)
{
   CAPIMsg_t              *pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
   DaicCapiMsgActionFct_t *pfnAction;
   unsigned                uCmd = CAPI_GET_CMD (pCapiMsg);
   int                     iCmdIdx;
   unsigned                uRes;
   
   /* if the message length is set to null, the message was canceled (e.g.
    * through a call to CAPI_RELEASE)
    */
   if (CAPI_GET_LEN (pCapiMsg) == 0)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: CAPI message 0x%04X, application %u invalidated, will be ignored",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           (unsigned) CAPI_GET_CMD (pCapiMsg), uApplID);
      kcapi_free_mbuf (pmbMsg);
      return (CAPI_OK);
   }
   
   /* First check if the NCCI is assigned exclusively to an application and the
    * application id of the current CAPI message is not the same. An unassigned
    * NCCI can only happen for NCCI 0 that is exclusively used to process
    * Connect-B3-Requests. All other NCCIs are exclusively assigned when a CAPI
    * message arrives. If this application id and the id of the message are not
    * the same, there is something wrong and we simple ignore the message. The
    * CAPI manager should normally filter out such messages.
    */
   if (pNcciData->uApplID != 0 &&
       pNcciData->uApplID != uApplID)
   {
      DBG (LOG_INFO, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Reject CAPI message 0x%04X because NCCI is assigned to application id %u, not %u",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           uCmd, pNcciData->uApplID, uApplID);
      daicncci_send_gen_conf
         (pPortData, pNcciData, pCapiMsg, CME_INVALID_APPLICATION_ID);
      kcapi_free_mbuf (pmbMsg);
      return (CAPI_OK);
   }

   /* translate the CAPI message command into an action table index */
   iCmdIdx = (uCmd & CAPI_CMDMASK_COMMAND) * 2;
   if ((uCmd & CAPI_CMDMASK_SUBCMD) == C_RESP)
   {
      ++iCmdIdx;
   }
   iCmdIdx = g_auCapiMsgCmdIdx [iCmdIdx];
   if (iCmdIdx < 0)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: CAPI message 0x%04X not valid for NCCI context",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state), uCmd);
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   
   /* perform a lookup into the table for action functions according to the
    * current state and the message command index just computed
    */
   pfnAction = g_aCapiMsgAction [(int) (pNcciData->state)] [iCmdIdx];
   
   /* if there is a valid handler function: call it */
   if (pfnAction != NULL)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Calling action function for CAPI message 0x%04X, index %d",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           uCmd, iCmdIdx);
      uRes = pfnAction (pPortData, pNcciData, pmbMsg);
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Action function returned 0x%04X",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state), uRes);
   }
   /* If there is no valid handler function, the CAPI message is not expected.
    * For a request send out a confirmation with an error code. For a response
    * there is in general no action required.
    */
   else
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: CAPI message 0x%04X, index %d not expected in current state",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           uCmd, iCmdIdx);
      uRes = CCE_MSG_NOT_ALLOWED_YET;
      daicncci_send_gen_conf (pPortData, pNcciData, pCapiMsg, uRes);
   }

   return (uRes);
} /* daicncci_handle_capi_msg */





/**
 * Abort a connection in any possible state.
 *
 * A connection may be aborted in any state but N-0 and N-5, if there is no
 * request pending. In these cases we send a controller Disconnect-Request. If
 * a request is pending, we can only set the flag for a pending Abort-Request,
 * so when the return code for the pending request arrives, the Abort-Request
 * can be executed.
 *
 * In state N-0 we are already in the idle state, so nothing can be done (and in
 * fact this function should have never been called in this situation). In state
 * N-5 the connection is already completely down, but we wait for a
 * Disconnect-B3-Response. If we are to abort a connection in this state, we
 * must assume that the Disconnect-B3-Response will never arrive. So in this
 * case we simply release the current NCCI.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pNcciData             I/O: The NCCI data for this operation.
 *
 * @return Nothing.
 */

static void daicncci_abort_connection
   (DaicPortData_t *pPortData,
    DaicNcciData_t *pNcciData)
{
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Will abort connection",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));

   /* if there is currently a controller request pending, we can only register
    * the abort request and wait for the return code
    */
   if ((pNcciData->ulFlags & DAIC_NCCI_FLAG_REQUEST_PENDING) != 0)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Register abort request because of pending controller request 0x%02X",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           pNcciData->uCurrRequest);
      pNcciData->ulFlags |= DAIC_NCCI_FLAG_GOT_ABORT_REQ;
      return;
   }
   
   /* distinguish between the different NCCI states */
   switch (pNcciData->state)
   {
      default:
      case DAIC_NCCI_STATE_N0:
         /* Nothing to be done, already in idle state */
         break;
      
      case DAIC_NCCI_STATE_N1:
      case DAIC_NCCI_STATE_N2:
      case DAIC_NCCI_STATE_N2_1:
      case DAIC_NCCI_STATE_NACT:
         {
            struct mbuf   *pmbReqData;
            DaicReqData_t *pReqData;
            
            /* get a new mbuf for a controller Disconnect-Request */
            pmbReqData = kcapi_get_mbuf (sizeof (*pReqData));
            if (pmbReqData == NULL)
            {
               DBG (LOG_ERROR, pPortData->iUnit,
                    "Port %u: NCCI %u: State %d: Out of mbufs for controller Disconnect-Request during connection abort",
                    pPortData->uPortIdx, pNcciData->uNcci,
                    (int) (pNcciData->state));
               return;
            }
            pReqData = mtod (pmbReqData, DaicReqData_t *);
            pReqData->bReq                    = DAIC_NL_DISC;
            pReqData->bReqId                  = (u_int8_t) (pNcciData->uNetId);
            pReqData->bReqCh                  = (u_int8_t) (pNcciData->uNetCh);
            pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_NCCI;
            pReqData->wAssignReqPlci          = (u_int16_t) (pNcciData->uPlci);
            pReqData->wDataLength             = 0;
            pmbReqData->m_len = sizeof (*pReqData);

            /* send the request to the controller and register the pending
             * request
             */
            pNcciData->uCurrRequest  = (unsigned) (pReqData->bReq);
            pNcciData->ulFlags      |= DAIC_NCCI_FLAG_REQUEST_PENDING;
            _IF_ENQUEUE (&(pPortData->reqQueue), pmbReqData);

            DBG (LOG_TRACE, pPortData->iUnit,
                 "Port %u: NCCI %u: State %d --> %d",
                 pPortData->uPortIdx, pNcciData->uNcci,
                 (int) (pNcciData->state), (int) DAIC_NCCI_STATE_N4);
            pNcciData->state = DAIC_NCCI_STATE_N4;
         }
         break;

      case DAIC_NCCI_STATE_N4:
      case DAIC_NCCI_STATE_N5:
         /* in this case we cannot assume the pending Disconnect-Response to
          * arrive --> release the NCCI
          */
         if (pNcciData->uNcci != 0)
         {
            unsigned uPlci = pNcciData->uPlci;
            daicncci_reset (pPortData, pNcciData);
            daicplci_ncci_removed (pPortData, uPlci);
         }
         break;
   }
   
} /* daicncci_abort_connection */





/**
 * Send out a controller Connect-Request for network connection establishment.
 *
 * This function is called after the CAPI Connect-B3-Request message is
 * successfully validated and a new NCCI for a network connection is allocated.
 *
 * Here we send an NL_CONNECT request over the assigned network id. Then we must
 * wait for the NL_CONNECT_ACK indication to report the complete connection
 * establishment to the application. The return code for the NL_CONNECT request
 * will deliver a newly allocated channel it in the context of the network id
 * used. A Connect-B3-Confirm is sent when receiving the return code for the
 * NL_CONNECT request.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pNcciData             I/O: The NCCI data entry as the context for
 *                                 sending the NL_CONNECT request.
 * @param uSigId                I: The signalling id of the associated PLCI.
 *
 * @retval CAPI_OK              The NL_CONNECT request is sent out successfully.
 * @retval Else                 Error occurred.
 */

static unsigned daicncci_send_nl_connect_req
   (DaicPortData_t  *pPortData,
    DaicNcciData_t  *pNcciData,
    unsigned         uSigId)
{
   CAPIMsg_t      *pCapiReq = mtod (pNcciData->pmbCurrCapiMsg, CAPIMsg_t *);
   CAPINCPI_t     *pNcpi;
   struct mbuf    *pmbReq;
   DaicReqData_t  *pReqData;
   size_t          nLenData;
   u_int8_t       *p;
   
   /* check if the message contains an NCPI and if this NCPI contains a byte
    * array to be inserted into a controller Connect-Request
    */
   pNcpi = (CAPINCPI_t *) &(pCapiReq->info.any.b [0]);
   if ((pNcciData->ulFlags & DAIC_NCCI_FLAG_USE_NCPI) != 0 &&
       (size_t) ((u_int8_t *) pNcpi - (u_int8_t *) pCapiReq) <
          (size_t) CAPI_GET_LEN (pCapiReq) &&
       C_GET_BYTE (pNcpi->std.bLength) > 0 &&
       sizeof (pCapiReq->head) + C_GET_BYTE (pNcpi->std.bLength) + 1 <=
          CAPI_GET_LEN (pCapiReq) &&
       C_GET_BYTE (pNcpi->std.bLength) >= sizeof (pNcpi->std))
   {
      nLenData = C_GET_BYTE (pNcpi->std.bLength) - sizeof (pNcpi->std) + 1;
   }
   else
   {
      nLenData = 0;
   }

   /* send out a controller Connect-Request */
   pmbReq = kcapi_get_mbuf (sizeof (DaicReqData_t) + nLenData);
   if (pmbReq == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of mbufs for Connect-Request",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
      return (CME_OS_RESOURCE_ERROR);
   }
   pReqData = mtod (pmbReq, DaicReqData_t *);
   pReqData->bReq                    = DAIC_NL_CONNECT;
   pReqData->bReqId                  = pNcciData->uNetId;
   pReqData->bReqCh                  = pNcciData->uNetCh;
   pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_NCCI;
   pReqData->wAssignReqPlci          = pNcciData->uPlci;
   if (nLenData > 0)
   {
      p = (u_int8_t *) pReqData + sizeof (*pReqData);
      bcopy ((u_int8_t *) pNcpi + sizeof (pNcpi->std), p, nLenData);
   }
   pReqData->wDataLength = (u_int16_t) nLenData;
   pmbReq->m_len = sizeof (*pReqData) + pReqData->wDataLength;
   pNcciData->uCurrRequest  = (unsigned) (pReqData->bReq);
   pNcciData->ulFlags      |= DAIC_NCCI_FLAG_REQUEST_PENDING;
   _IF_ENQUEUE (&(pPortData->reqQueue), pmbReq);

   return (CAPI_OK);
} /* daicncci_send_nl_connect_req */





/**
 * Send a Connect-B3-Confirm CAPI message to the application.
 *
 * This function is used to signal a Connect-B3-Confirm to the CAPI application.
 * The info value for the message is specified as a function argument.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pNcciData             I/O: The NCCI data for this operation.
 * @param pReqMsg               I: The Connect-B3-Request CAPI message this
 *                                 confirmation belongs to.
 * @param uInfoValue            I: The info value to put into the message.
 *
 * @return Nothing.
 */

static void daicncci_send_connect_b3_conf
   (DaicPortData_t  *pPortData,
    DaicNcciData_t  *pNcciData,
    const CAPIMsg_t *pReqMsg,
    unsigned         uInfoValue)
{
   struct mbuf *pmbConf;
   CAPIMsg_t   *pCapiConf;

   /* prepare a Connect-B3-Confirm mbuf */
   pmbConf = kcapi_get_mbuf (sizeof (CAPIMsgHead_t) +
                             sizeof (CAPIConnectB3Conf_t));
   if (pmbConf == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of mbufs for Connect-B3-Confirm",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));
      return;
   }
   pCapiConf = mtod (pmbConf, CAPIMsg_t *);
   pCapiConf->head = pReqMsg->head;
   C_PUT_WORD (pCapiConf->head.wCmd, CAPI_CONFIRM (C_CONNECT_B3));
   if (uInfoValue == CAPI_OK)
   {
      C_PUT_DWORD (pCapiConf->head.dwCid, pNcciData->dwCid);
   }
   else
   {
      u_int32_t dwCid = (pNcciData->dwCid & ~CAPI_CIDMASK_NCCI);
      C_PUT_DWORD (pCapiConf->head.dwCid, dwCid);
   }
   C_PUT_WORD (pCapiConf->info.connect_b3_conf.wInfo, uInfoValue);
   C_PUT_WORD (pCapiConf->head.wLen,
               sizeof (CAPIMsgHead_t) + sizeof (CAPIConnectB3Conf_t));

   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Send Connect-B3-Confirm with wInfo 0x%04X",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        uInfoValue);

   mtx_unlock (&(pPortData->pSc->mtxAccess));
   kcapi_ctlr_receive_capi_message (pPortData->uUniqueCapiCtlrNum,
                                    CAPI_GET_APPL (pReqMsg), pmbConf);
   mtx_lock (&(pPortData->pSc->mtxAccess));

} /* daicncci_send_connect_b3_conf */





/**
 * Send a Data-B3-Confirm and cleanup the corresponding Data-B3-Request.
 *
 * @pre This function must be called with an mbuf for a Data-B3-Request stored
 *      in the NCCI data structure.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pNcciData             I/O: The NCCI data for this operation.
 * @param uInfoVal              I: The info value to report in the confirmation.
 *
 * @return Nothing.
 */

static void daicncci_send_data_b3_conf
   (DaicPortData_t  *pPortData,
    DaicNcciData_t  *pNcciData,
    unsigned         uInfoVal)
{
   struct mbuf *pmb;
   CAPIMsg_t   *pCapiReq;
   CAPIMsg_t   *pCapiConf;

   pmb = kcapi_get_mbuf (sizeof (pCapiConf->head) +
                         sizeof (pCapiConf->info.data_b3_conf));
   if (pmb == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of mbufs for Data-B3-Confirm",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state));

      kcapi_free_mbuf (pNcciData->pmbCurrCapiMsg);
      pNcciData->pmbCurrCapiMsg = NULL;
      return;
   }

   pCapiConf = mtod (pmb, CAPIMsg_t *);
   pCapiReq  = mtod (pNcciData->pmbCurrCapiMsg, CAPIMsg_t *);
   pCapiConf->head = (pCapiReq->head);
   C_PUT_WORD (pCapiConf->head.wCmd, CAPI_CONFIRM (C_DATA_B3));
   C_PUT_WORD (pCapiConf->info.data_b3_conf.wHandle,
               C_GET_WORD (pCapiReq->info.data_b3_req.wHandle));
   C_PUT_WORD (pCapiConf->info.data_b3_conf.wInfo, uInfoVal);
   C_PUT_WORD (pCapiConf->head.wLen,
               sizeof (pCapiConf->head) +
                  sizeof (pCapiConf->info.data_b3_conf));
   pmb->m_len = sizeof (pCapiConf->head) +
                sizeof (pCapiConf->info.data_b3_conf);

   DBG (LOG_DATAMSG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Send Data-B3-Confirm, info value 0x%04X, handle %u",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        uInfoVal, (unsigned) C_GET_WORD (pCapiConf->info.data_b3_conf.wHandle));

   mtx_unlock (&(pPortData->pSc->mtxAccess));
   kcapi_ctlr_receive_capi_message
      (pPortData->uUniqueCapiCtlrNum, pNcciData->uApplID, pmb);
   mtx_lock (&(pPortData->pSc->mtxAccess));

   kcapi_free_mbuf (pNcciData->pmbCurrCapiMsg);
   pNcciData->pmbCurrCapiMsg = NULL;
   
} /* daicncci_send_data_b3_conf */





/**
 * Send a generic confirmation message for any CAPI message.
 *
 * This function is called for a CAPI message that cannot be handled in the
 * current context. If a CAPI response message is passed, nothing will be done.
 * Only request messages are handled.
 *
 * In most cases only a confirmation message with simply an info value in the
 * info part of the CAPI message needs to be sent. But some messages must be
 * handled specially. These are Facility-Requests and Data-B3-Requests.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pNcciData             I/O: The NCCI data for this operation.
 * @param pCapiReq              I: The CAPI message to send a confirmation for.
 * @param uInfoVal              I: The info value to report in the confirmation.
 *
 * @return Nothing.
 */

static void daicncci_send_gen_conf
   (DaicPortData_t  *pPortData,
    DaicNcciData_t  *pNcciData,
    const CAPIMsg_t *pCapiReq,
    unsigned         uInfoVal)
{
   unsigned     uApplId = CAPI_GET_APPL (pCapiReq);
   unsigned     uCmd    = CAPI_GET_CMD (pCapiReq);
   size_t       nLen;
   struct mbuf *pmbCapiConf;
   CAPIMsg_t   *pCapiConf;
   
   /* only request messages must be handled */
   if ((uCmd & CAPI_CMDMASK_SUBCMD) != C_REQ)
   {
      return;
   }
   
   /* determine message length */
   if (uCmd == CAPI_REQUEST (C_FACILITY))
   {
      nLen =
         sizeof (pCapiConf->head) + sizeof (pCapiConf->info.facility_conf) + 1;
   }
   else if (uCmd == CAPI_REQUEST (C_DATA_B3))
   {
      nLen = sizeof (pCapiConf->head) + sizeof (pCapiConf->info.data_b3_conf);
   }
   else
   {
      nLen = sizeof (pCapiConf->head) + sizeof (pCapiConf->info.any.w [0]);
   }
   
   /* get a new mbuf for the CAPI message */
   pmbCapiConf = kcapi_get_mbuf (nLen);
   if (pmbCapiConf == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: NCCI %u: State %d: Out of mbufs for generic CAPI confirmation message for 0x%04X",
           pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
           uCmd);
      return;
   }
   pCapiConf = mtod (pmbCapiConf, CAPIMsg_t *);
   
   /* fill the CAPI confirmation message */
   pCapiConf = mtod (pmbCapiConf, CAPIMsg_t *);
   pCapiConf->head = pCapiReq->head;
   C_PUT_WORD (pCapiConf->head.wCmd,
               CAPI_CONFIRM ((uCmd & CAPI_CMDMASK_COMMAND)));
   if (uCmd == CAPI_REQUEST (C_FACILITY))
   {
      C_PUT_WORD (pCapiConf->info.facility_conf.wInfo, uInfoVal);
      C_PUT_WORD (pCapiConf->info.facility_conf.wSelector,
                  C_GET_WORD (pCapiReq->info.facility_req.wSelector));
      C_PUT_BYTE (pCapiConf->info.any.b
                     [sizeof (pCapiConf->info.facility_conf)], 0);
   }
   else if (uCmd == CAPI_REQUEST (C_DATA_B3))
   {
      C_PUT_WORD (pCapiConf->info.data_b3_conf.wHandle,
                  C_GET_WORD (pCapiReq->info.data_b3_req.wHandle));
      C_PUT_WORD (pCapiConf->info.data_b3_conf.wInfo, uInfoVal);
   }
   else
   {
      C_PUT_WORD (pCapiConf->info.any.w [0], uInfoVal);
   }
   C_PUT_WORD (pCapiConf->head.wLen, nLen);
   pmbCapiConf->m_len = nLen;

   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: NCCI %u: State %d: Send generic CAPI confirmation 0x%04X with info value 0x%04X to application %u",
        pPortData->uPortIdx, pNcciData->uNcci, (int) (pNcciData->state),
        (unsigned) CAPI_GET_CMD (pCapiConf), uInfoVal, uApplId);

   mtx_unlock (&(pPortData->pSc->mtxAccess));
   kcapi_ctlr_receive_capi_message
      (pPortData->uUniqueCapiCtlrNum, uApplId, pmbCapiConf);
   mtx_lock (&(pPortData->pSc->mtxAccess));

} /* daicncci_send_gen_conf */
