/**
 * @file daic_plci.c
 *
 * Daic-PLCI - The PLCI state machine for the daic device driver.
 *
 * Copyright: 2003 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: daic_plci.c,v 1.1.2.1 2006/02/28 21:35:15 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>

/* Import includes */

#define __DAIC_PLCI__

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





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





/** The type for a byte string of maximum length 6. */
typedef u_int8_t ByteString6_t [6];

/**
 * Table to map CIP values to bearer capability (BC) byte strings, starting with
 * the length byte.
 *
 * @note These strings can only be used with A-Law. For use with american
 *       protocols a second µ-Law table must be provided.
 */
static ByteString6_t g_aabBcValuesByCip [] =
   {
      { 0 },                    /* CIP 0 - No profile */
      { 3, 0x80, 0x90, 0xA3 },  /* CIP 1 - Speech */
      { 2, 0x88, 0x90 },        /* CIP 2 - Unrestricted digital information */
      { 2, 0x89, 0x90 },        /* CIP 3 - Restricted digital information */
      { 3, 0x90, 0x90, 0xA3 },  /* CIP 4 - 3.1 kHz audio */
      { 2, 0x91, 0x90 },        /* CIP 5 - 7 kHz audio */
      { 2, 0x98, 0x90 },        /* CIP 6 - Video */
      { 4, 0x88, 0xC0, 0xC6, 0xE6 },
                                /* CIP 7 - Packet mode */
      { 4, 0x88, 0x90, 0x21, 0x8F },
                                /* CIP 8 - 56 kbit/s rate adaption */
      { 3, 0x91, 0x90, 0xA5 },  /* CIP 9 - Unrestricted digital information
                                 *         with tones and announcements
                                 */
      { 0 },
      { 0 },
      { 0 },
      { 0 },
      { 0 },
      { 0 },
      { 3, 0x80, 0x90, 0xA3 },  /* CIP 16 - Telephony */
      { 3, 0x90, 0x90, 0xA3 },  /* CIP 17 - Group 2/3 facsimile */
      { 2, 0x88, 0x90 },        /* CIP 18 - Group 4 facsimile Class 1 */
      { 2, 0x88, 0x90 },        /* CIP 19 - Teletex service basic and mixed mode
                                 *          and Group 4 facsimile service
                                 *          Classes II and III.
                                 */
      { 2, 0x88, 0x90 },        /* CIP 20 - Teletex service basic and
                                 *          processable mode
                                 */
      { 2, 0x88, 0x90 },        /* CIP 21 - Teletex service basic mode */
      { 2, 0x88, 0x90 },        /* CIP 22 - International interworking for
                                 *          Videotex
                                 */
      { 2, 0x88, 0x90 },        /* CIP 23 - Teletex */
      { 2, 0x88, 0x90 },        /* CIP 24 - Message Handling Systems in
                                 *          accordance with X.400
                                 */
      { 2, 0x88, 0x90 },        /* CIP 25 - OSI application in accordance with
                                 *          X.200
                                 */
      { 3, 0x91, 0x90, 0xA5 },  /* CIP 26 - 7 kHz Telephony */
      { 3, 0x91, 0x90, 0xA5 },  /* CIP 27 - Video telephony, first connection */
      { 2, 0x88, 0x90 }         /* CIP 28 - Video telephony, second connection */
   };

/**
 * Table to map CIP values to high layer capability (HLC) byte strings,
 * starting with the length byte.
 */
static ByteString6_t g_aabHlcValuesByCip [] =
   {
      { 0 },                    /* CIP 0 - No profile */
      { 0 },                    /* CIP 1 - Speech */
      { 0 },                    /* CIP 2 - Unrestricted digital information */
      { 0 },                    /* CIP 3 - Restricted digital information */
      { 0 },                    /* CIP 4 - 3.1 kHz audio */
      { 0 },                    /* CIP 5 - 7 kHz audio */
      { 0 },                    /* CIP 6 - Video */
      { 0 },                    /* CIP 7 - Packet mode */
      { 0 },                    /* CIP 8 - 56 kbit/s rate adaption */
      { 0 },                    /* CIP 9 - Unrestricted digital information
                                 *         with tones and announcements
                                 */
      { 0 },
      { 0 },
      { 0 },
      { 0 },
      { 0 },
      { 0 },
      { 2, 0x91, 0x81 },        /* CIP 16 - Telephony */
      { 3, 0x91, 0x84 },        /* CIP 17 - Group 2/3 facsimile */
      { 2, 0x91, 0xA1 },        /* CIP 18 - Group 4 facsimile Class 1 */
      { 2, 0x91, 0xA4 },        /* CIP 19 - Teletex service basic and mixed mode
                                 *          and Group 4 facsimile service
                                 *          Classes II and III.
                                 */
      { 2, 0x91, 0xA8 },        /* CIP 20 - Teletex service basic and
                                 *          processable mode
                                 */
      { 2, 0x91, 0xB1 },        /* CIP 21 - Teletex service basic mode */
      { 2, 0x91, 0xB2 },        /* CIP 22 - International interworking for
                                 *          Videotex
                                 */
      { 2, 0x91, 0xB5 },        /* CIP 23 - Teletex */
      { 2, 0x91, 0xB8 },        /* CIP 24 - Message Handling Systems in
                                 *          accordance with X.400
                                 */
      { 2, 0x91, 0xC1 },        /* CIP 25 - OSI application in accordance with
                                 *          X.200
                                 */
      { 3, 0x91, 0x81 },        /* CIP 26 - 7 kHz Telephony */
      { 3, 0x91, 0x60, 0x01 },  /* CIP 27 - Video telephony, first connection */
      { 3, 0x91, 0x60, 0x02 }   /* CIP 28 - Video telephony, second connection */
   };



/* --- 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 pPlciData             I/O: The PLCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */
typedef void (DaicRcActionFct_t)
   (DaicPortData_t     *pPortData,
    DaicPlciData_t     *pPlciData,
    const DaicRcData_t *pRcData);

/** Handle the return code for a controller Call-Request. */
static DaicRcActionFct_t daicplci_handle_call_req_rc;

/** Handle the return code for a controller Call-Response-Request. */
static DaicRcActionFct_t daicplci_handle_call_resp_rc;

/** Handle the return code for a controller Alert-Request. */
static DaicRcActionFct_t daicplci_handle_alert_req_rc;

/**
 * Handle the return code for a controller Call-, Info- or
 * User-User-Data-Request.
 *
 * A controller Call-Request in a state other than P-0 and a
 * User-User-Data-Request originates from a CAPI Info-Request with the called
 * party number or only the user-user data part filled respectively. If an
 * Info-Request contains no called party number or additional parts besides
 * user-user data it will result in a controller Info-Request.
 *
 * But in all cases the return code must result in an Info-Confirm CAPI message
 * to the application. So they can all be handled by the same return code action
 * function in the state table.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */
static DaicRcActionFct_t daicplci_handle_info_req_rc;

/** Handle the return code for a controller Hangup-Request. */
static DaicRcActionFct_t daicplci_handle_hangup_req_rc;

/**
 * Handle the return code for a controller Remove-Request for the network id.
 */
static DaicRcActionFct_t daicplci_handle_net_remove_req_rc;

/**
 * Handle the return code for a controller Remove-Request for the signaling id.
 */
static DaicRcActionFct_t daicplci_handle_sig_remove_req_rc;

/** The function table for handling general return codes. */
static DaicRcActionFct_t *g_aRcAction [DAIC_PLCI_NUM_STATES]
                                      [DAIC_SIG_NUM_CMDS + 1] =
   {
      /* DAIC_PLCI_STATE_P0 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call- or Assign-Request (1) */
         NULL,                          /* Not used (2) */
         NULL,                          /* Hangup-Request (3) */
         NULL,                          /* Suspend-Request (4) */
         NULL,                          /* Resume-Request (5) */
         NULL,                          /* Suspend-Reject-Request (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Request (8) */
         NULL,                          /* Not used (9) */
         NULL,                          /* Not used (0x0A) */
         NULL,                          /* Call-Response (0x0B) */
         NULL,                          /* Alert-Request (0x0C) */
         NULL,                          /* Info-Request (0x0D) */
         NULL                           /* Remove-Request (redirected from 0xFF to 0x0E) */
      },
      /* DAIC_PLCI_STATE_P0_1 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call- or Assign-Request (1) */
         NULL,                          /* Not used (2) */
         NULL,                          /* Hangup-Request (3) */
         NULL,                          /* Suspend-Request (4) */
         NULL,                          /* Resume-Request (5) */
         NULL,                          /* Suspend-Reject-Request (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Request (8) */
         NULL,                          /* Not used (9) */
         NULL,                          /* Not used (0x0A) */
         NULL,                          /* Call-Response (0x0B) */
         NULL,                          /* Alert-Request (0x0C) */
         NULL,                          /* Info-Request (0x0D) */
         NULL                           /* Remove-Request (redirected from 0xFF to 0x0E) */
      },
      /* DAIC_PLCI_STATE_P0_2 */
      {
         NULL,                          /* Null command (0) */
         daicplci_handle_call_req_rc,   /* Call- or Assign-Request (1) */
         NULL,                          /* Not used (2) */
         NULL,                          /* Hangup-Request (3) */
         NULL,                          /* Suspend-Request (4) */
         NULL,                          /* Resume-Request (5) */
         NULL,                          /* Suspend-Reject-Request (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Request (8) */
         NULL,                          /* Not used (9) */
         NULL,                          /* Not used (0x0A) */
         NULL,                          /* Call-Response (0x0B) */
         NULL,                          /* Alert-Request (0x0C) */
         NULL,                          /* Info-Request (0x0D) */
         NULL                           /* Remove-Request (redirected from 0xFF to 0x0E) */
      },
      /* DAIC_PLCI_STATE_P1 */
      {
         NULL,                          /* Null command (0) */
         daicplci_handle_info_req_rc,   /* Call- or Assign-Request (1) */
         NULL,                          /* Not used (2) */
         NULL,                          /* Hangup-Request (3) */
         NULL,                          /* Suspend-Request (4) */
         NULL,                          /* Resume-Request (5) */
         NULL,                          /* Suspend-Reject-Request (6) */
         NULL,                          /* Not used (7) */
         daicplci_handle_info_req_rc,   /* User-Data-Request (8) */
         NULL,                          /* Not used (9) */
         NULL,                          /* Not used (0x0A) */
         NULL,                          /* Call-Response (0x0B) */
         NULL,                          /* Alert-Request (0x0C) */
         daicplci_handle_info_req_rc,   /* Info-Request (0x0D) */
         NULL                           /* Remove-Request (redirected from 0xFF to 0x0E) */
      },
      /* DAIC_PLCI_STATE_P1_1 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call- or Assign-Request (1) */
         NULL,                          /* Not used (2) */
         NULL,                          /* Hangup-Request (3) */
         NULL,                          /* Suspend-Request (4) */
         NULL,                          /* Resume-Request (5) */
         NULL,                          /* Suspend-Reject-Request (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Request (8) */
         NULL,                          /* Not used (9) */
         NULL,                          /* Not used (0x0A) */
         NULL,                          /* Call-Response (0x0B) */
         NULL,                          /* Alert-Request (0x0C) */
         NULL,                          /* Info-Request (0x0D) */
         NULL                           /* Remove-Request (redirected from 0xFF to 0x0E) */
      },
      /* DAIC_PLCI_STATE_P2 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call- or Assign-Request (1) */
         NULL,                          /* Not used (2) */
         NULL,                          /* Hangup-Request (3) */
         NULL,                          /* Suspend-Request (4) */
         NULL,                          /* Resume-Request (5) */
         NULL,                          /* Suspend-Reject-Request (6) */
         NULL,                          /* Not used (7) */
         daicplci_handle_info_req_rc,   /* User-Data-Request (8) */
         NULL,                          /* Not used (9) */
         NULL,                          /* Not used (0x0A) */
         NULL,                          /* Call-Response (0x0B) */
         daicplci_handle_alert_req_rc,  /* Alert-Request (0x0C) */
         daicplci_handle_info_req_rc,   /* Info-Request (0x0D) */
         NULL                           /* Remove-Request (redirected from 0xFF to 0x0E) */
      },
      /* DAIC_PLCI_STATE_P4 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call- or Assign-Request (1) */
         NULL,                          /* Not used (2) */
         NULL,                          /* Hangup-Request (3) */
         NULL,                          /* Suspend-Request (4) */
         NULL,                          /* Resume-Request (5) */
         NULL,                          /* Suspend-Reject-Request (6) */
         NULL,                          /* Not used (7) */
         daicplci_handle_info_req_rc,   /* User-Data-Request (8) */
         NULL,                          /* Not used (9) */
         NULL,                          /* Not used (0x0A) */
         daicplci_handle_call_resp_rc,  /* Call-Response (0x0B) */
         NULL,                          /* Alert-Request (0x0C) */
         daicplci_handle_info_req_rc,   /* Info-Request (0x0D) */
         NULL                           /* Remove-Request (redirected from 0xFF to 0x0E) */
      },
      /* DAIC_PLCI_STATE_P4_1 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call- or Assign-Request (1) */
         NULL,                          /* Not used (2) */
         NULL,                          /* Hangup-Request (3) */
         NULL,                          /* Suspend-Request (4) */
         NULL,                          /* Resume-Request (5) */
         NULL,                          /* Suspend-Reject-Request (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Request (8) */
         NULL,                          /* Not used (9) */
         NULL,                          /* Not used (0x0A) */
         NULL,                          /* Call-Response (0x0B) */
         NULL,                          /* Alert-Request (0x0C) */
         NULL,                          /* Info-Request (0x0D) */
         NULL                           /* Remove-Request (redirected from 0xFF to 0x0E) */
      },
      /* DAIC_PLCI_STATE_PACT */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call- or Assign-Request (1) */
         NULL,                          /* Not used (2) */
         NULL,                          /* Hangup-Request (3) */
         NULL,                          /* Suspend-Request (4) */
         NULL,                          /* Resume-Request (5) */
         NULL,                          /* Suspend-Reject-Request (6) */
         NULL,                          /* Not used (7) */
         daicplci_handle_info_req_rc,   /* User-Data-Request (8) */
         NULL,                          /* Not used (9) */
         NULL,                          /* Not used (0x0A) */
         NULL,                          /* Call-Response (0x0B) */
         NULL,                          /* Alert-Request (0x0C) */
         daicplci_handle_info_req_rc,   /* Info-Request (0x0D) */
         daicplci_handle_net_remove_req_rc
                                        /* Remove-Request (redirected from 0xFF to 0x0E) */
      },
      /* DAIC_PLCI_STATE_P5 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call- or Assign-Request (1) */
         NULL,                          /* Not used (2) */
         daicplci_handle_hangup_req_rc, /* Hangup-Request (3) */
         NULL,                          /* Suspend-Request (4) */
         NULL,                          /* Resume-Request (5) */
         NULL,                          /* Suspend-Reject-Request (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Request (8) */
         NULL,                          /* Not used (9) */
         NULL,                          /* Not used (0x0A) */
         NULL,                          /* Call-Response (0x0B) */
         NULL,                          /* Alert-Request (0x0C) */
         NULL,                          /* Info-Request (0x0D) */
         NULL                           /* Remove-Request (redirected from 0xFF to 0x0E) */
      },
      /* DAIC_PLCI_STATE_P6 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call- or Assign-Request (1) */
         NULL,                          /* Not used (2) */
         NULL,                          /* Hangup-Request (3) */
         NULL,                          /* Suspend-Request (4) */
         NULL,                          /* Resume-Request (5) */
         NULL,                          /* Suspend-Reject-Request (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Request (8) */
         NULL,                          /* Not used (9) */
         NULL,                          /* Not used (0x0A) */
         NULL,                          /* Call-Response (0x0B) */
         NULL,                          /* Alert-Request (0x0C) */
         NULL,                          /* Info-Request (0x0D) */
         NULL                           /* Remove-Request (redirected from 0xFF to 0x0E) */
      },
      /* DAIC_PLCI_STATE_P6_1 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call- or Assign-Request (1) */
         NULL,                          /* Not used (2) */
         NULL,                          /* Hangup-Request (3) */
         NULL,                          /* Suspend-Request (4) */
         NULL,                          /* Resume-Request (5) */
         NULL,                          /* Suspend-Reject-Request (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Request (8) */
         NULL,                          /* Not used (9) */
         NULL,                          /* Not used (0x0A) */
         NULL,                          /* Call-Response (0x0B) */
         NULL,                          /* Alert-Request (0x0C) */
         NULL,                          /* Info-Request (0x0D) */
         daicplci_handle_net_remove_req_rc
                                        /* Remove-Request (redirected from 0xFF to 0x0E) */
      },
      /* DAIC_PLCI_STATE_P6_2 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call- or Assign-Request (1) */
         NULL,                          /* Not used (2) */
         NULL,                          /* Hangup-Request (3) */
         NULL,                          /* Suspend-Request (4) */
         NULL,                          /* Resume-Request (5) */
         NULL,                          /* Suspend-Reject-Request (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Request (8) */
         NULL,                          /* Not used (9) */
         NULL,                          /* Not used (0x0A) */
         NULL,                          /* Call-Response (0x0B) */
         NULL,                          /* Alert-Request (0x0C) */
         NULL,                          /* Info-Request (0x0D) */
         NULL                           /* Remove-Request (redirected from 0xFF to 0x0E) */
      },
      /* DAIC_PLCI_STATE_P6_3 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call- or Assign-Request (1) */
         NULL,                          /* Not used (2) */
         NULL,                          /* Hangup-Request (3) */
         NULL,                          /* Suspend-Request (4) */
         NULL,                          /* Resume-Request (5) */
         NULL,                          /* Suspend-Reject-Request (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Request (8) */
         NULL,                          /* Not used (9) */
         NULL,                          /* Not used (0x0A) */
         NULL,                          /* Call-Response (0x0B) */
         NULL,                          /* Alert-Request (0x0C) */
         NULL,                          /* Info-Request (0x0D) */
         daicplci_handle_sig_remove_req_rc
                                        /* Remove-Request (redirected from 0xFF to 0x0E) */
      }
   };



/* --- 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 pPlciData             I/O: The PLCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
typedef void (DaicIndActionFct_t)
   (DaicPortData_t      *pPortData,
    DaicPlciData_t      *pPlciData,
    const DaicIndData_t *pIndData);

/**
 * Handle a controller Call-Connected-Indication.
 *
 * This message signals the connected B-channel for outgoing calls. The
 * application must get a Connect-Active-Indication and maybe some
 * Info-Indications for possibly received information elements and network
 * events.
 *
 * The Connect-Active-Connection will be stored as the "current CAPI message",
 * only Info-Indications are sent to the application now. This is because we
 * must allocate a new network id for accessing the B-channel. As soon as the
 * return code for this Assign-Request is received, we will forward the
 * Connect-Active-Indication to the application.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
static DaicIndActionFct_t daicplci_handle_call_conn_ind;

/**
 * Handle a controller Call-Indication.
 *
 * This message is sent by the controller to signal an established raw B-channel
 * for an incoming call. The CAPI application must get a
 * Connect-Active-Indication and maybe some Info-Indications for possibly
 * received information elements and network events.
 *
 * The Connect-Active-Connection will be stored as the "current CAPI message",
 * only Info-Indications are sent to the application now. This is because we
 * must allocate a new network id for accessing the B-channel. As soon as the
 * return code for this Assign-Request is received, we will forward the
 * Connect-Active-Indication to the application.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
static DaicIndActionFct_t daicplci_handle_call_ind;

/**
 * Handle a controller Alert-Indication.
 *
 * An Alert-Indication from the controller signals the alerting state of the
 * called party for an outgoing call. The only task necessary for handling this
 * message is to send some Info-Indication to the CAPI application.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
static DaicIndActionFct_t daicplci_handle_alert_ind;

/**
 * Handle a user-user data indication.
 *
 * This controller message reports received user-user data through the
 * D-channel. If the application has registered interest in such information, we
 * must send some Info-Indication to it.
 *
 * The situation is a little more complicated for incoming calls, when no
 * application has sent a Connect-Response yet. In this case we must send
 * Info-Indications to all applications that received the Connect-Indication for
 * the incoming call.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
static DaicIndActionFct_t daicplci_handle_uudata_ind;

/**
 * Handle a controller Info-Indication.
 *
 * This controller message signals the receipt some general D-channel message
 * and/or information elements. The application will then receive some
 * Info-Indication for this data.
 *
 * The situation is a little more complicated for incoming calls, when no
 * application has sent a Connect-Response yet. In this case we must sent
 * Info-Indications to all applications that received the Connect-Indication for
 * the incoming call.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
static DaicIndActionFct_t daicplci_handle_info_ind;

/**
 * Handle a controller Hangup-Indication.
 *
 * This message signals the release of the B-channel, i.e. the call is
 * completely disconnected. As a reaction we must release all still existing
 * NCCIs for the corresponding PLCI. When all NCCIs are removed, the CAPI
 * application must receive a Disconnect-Indication. On arrival of the
 * corresponding Disconnect-Response the signaling id will be removed.
 *
 * If there are still NCCIs at this state, we must wait until all of them are
 * released. Only then the application may get the Disconnect-Indication. The
 * PLCI state transit is done in the functions called for the corresponding
 * case.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
static DaicIndActionFct_t daicplci_handle_hangup_ind;

/**
 * Ignore any controller indication message.
 *
 * This function is called when general controller indications do not matter any
 * more. This is the case e.g. for a controller congestion indication (currently
 * not handled within this driver) or for a call indication when the application
 * has already sent a Disconnect-Request (note that message queueing may result
 * in some parallelity). Only the message arrival is logged, no further action
 * is executed.
 * 
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
static DaicIndActionFct_t daicplci_ignore_ind;

/** The function table for handling indications. */
static DaicIndActionFct_t *g_aIndAction [DAIC_PLCI_NUM_STATES]
                                        [DAIC_SIG_NUM_CMDS] =
   {
      /* DAIC_PLCI_STATE_P0 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call-Connected-Indication (1) */
         NULL,                          /* Call-Indication (2) */
         NULL,                          /* Hangup-Indication (3) */
         NULL,                          /* Suspend-Indication (4) */
         NULL,                          /* Resume-Indication (5) */
         NULL,                          /* Suspend-Reject-Indication (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Indication (8) */
         NULL,                          /* Congestion-Indication (9) */
         NULL,                          /* Indicate-Indication (0x0A) */
         NULL,                          /* Not used (0x0B) */
         NULL,                          /* Alert-Indication (0x0C) */
         NULL                           /* Info-Indication (0x0D) */
      },
      /* DAIC_PLCI_STATE_P0_1 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call-Connected-Indication (1) */
         NULL,                          /* Call-Indication (2) */
         NULL,                          /* Hangup-Indication (3) */
         NULL,                          /* Suspend-Indication (4) */
         NULL,                          /* Resume-Indication (5) */
         NULL,                          /* Suspend-Reject-Indication (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Indication (8) */
         NULL,                          /* Congestion-Indication (9) */
         NULL,                          /* Indicate-Indication (0x0A) */
         NULL,                          /* Not used (0x0B) */
         NULL,                          /* Alert-Indication (0x0C) */
         NULL                           /* Info-Indication (0x0D) */
      },
      /* DAIC_PLCI_STATE_P0_2 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call-Connected-Indication (1) */
         NULL,                          /* Call-Indication (2) */
         NULL,                          /* Hangup-Indication (3) */
         NULL,                          /* Suspend-Indication (4) */
         NULL,                          /* Resume-Indication (5) */
         NULL,                          /* Suspend-Reject-Indication (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Indication (8) */
         NULL,                          /* Congestion-Indication (9) */
         NULL,                          /* Indicate-Indication (0x0A) */
         NULL,                          /* Not used (0x0B) */
         NULL,                          /* Alert-Indication (0x0C) */
         NULL                           /* Info-Indication (0x0D) */
      },
      /* DAIC_PLCI_STATE_P1 */
      {
         NULL,                          /* Null command (0) */
         daicplci_handle_call_conn_ind, /* Call-Connected-Indication (1) */
         NULL,                          /* Call-Indication (2) */
         daicplci_handle_hangup_ind,    /* Hangup-Indication (3) */
         NULL,                          /* Suspend-Indication (4) */
         NULL,                          /* Resume-Indication (5) */
         NULL,                          /* Suspend-Reject-Indication (6) */
         NULL,                          /* Not used (7) */
         daicplci_handle_uudata_ind,    /* User-Data-Indication (8) */
         daicplci_ignore_ind,           /* Congestion-Indication (9) */
         NULL,                          /* Indicate-Indication (0x0A) */
         NULL,                          /* Not used (0x0B) */
         daicplci_handle_alert_ind,     /* Alert-Indication (0x0C) */
         daicplci_handle_info_ind       /* Info-Indication (0x0D) */
      },
      /* DAIC_PLCI_STATE_P1_1 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call-Connected-Indication (1) */
         NULL,                          /* Call-Indication (2) */
         NULL,                          /* Hangup-Indication (3) */
         NULL,                          /* Suspend-Indication (4) */
         NULL,                          /* Resume-Indication (5) */
         NULL,                          /* Suspend-Reject-Indication (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Indication (8) */
         NULL,                          /* Congestion-Indication (9) */
         NULL,                          /* Indicate-Indication (0x0A) */
         NULL,                          /* Not used (0x0B) */
         NULL,                          /* Alert-Indication (0x0C) */
         NULL                           /* Info-Indication (0x0D) */
      },
      /* DAIC_PLCI_STATE_P2 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call-Connected-Indication (1) */
         NULL,                          /* Call-Indication (2) */
         daicplci_handle_hangup_ind,    /* Hangup-Indication (3) */
         NULL,                          /* Suspend-Indication (4) */
         NULL,                          /* Resume-Indication (5) */
         NULL,                          /* Suspend-Reject-Indication (6) */
         NULL,                          /* Not used (7) */
         daicplci_handle_uudata_ind,    /* User-Data-Indication (8) */
         daicplci_ignore_ind,           /* Congestion-Indication (9) */
         NULL,                          /* Indicate-Indication (0x0A) */
         NULL,                          /* Not used (0x0B) */
         NULL,                          /* Alert-Indication (0x0C) */
         daicplci_handle_info_ind       /* Info-Indication (0x0D) */
      },
      /* DAIC_PLCI_STATE_P4 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call-Connected-Indication (1) */
         daicplci_handle_call_ind,      /* Call-Indication (2) */
         daicplci_handle_hangup_ind,    /* Hangup-Indication (3) */
         NULL,                          /* Suspend-Indication (4) */
         NULL,                          /* Resume-Indication (5) */
         NULL,                          /* Suspend-Reject-Indication (6) */
         NULL,                          /* Not used (7) */
         daicplci_handle_uudata_ind,    /* User-Data-Indication (8) */
         daicplci_ignore_ind,           /* Congestion-Indication (9) */
         NULL,                          /* Indicate-Indication (0x0A) */
         NULL,                          /* Not used (0x0B) */
         NULL,                          /* Alert-Indication (0x0C) */
         daicplci_handle_info_ind       /* Info-Indication (0x0D) */
      },
      /* DAIC_PLCI_STATE_P4_1 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call-Connected-Indication (1) */
         NULL,                          /* Call-Indication (2) */
         NULL,                          /* Hangup-Indication (3) */
         NULL,                          /* Suspend-Indication (4) */
         NULL,                          /* Resume-Indication (5) */
         NULL,                          /* Suspend-Reject-Indication (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Indication (8) */
         NULL,                          /* Congestion-Indication (9) */
         NULL,                          /* Indicate-Indication (0x0A) */
         NULL,                          /* Not used (0x0B) */
         NULL,                          /* Alert-Indication (0x0C) */
         NULL                           /* Info-Indication (0x0D) */
      },
      /* DAIC_PLCI_STATE_PACT */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call-Connected-Indication (1) */
         NULL,                          /* Call-Indication (2) */
         daicplci_handle_hangup_ind,    /* Hangup-Indication (3) */
         NULL,                          /* Suspend-Indication (4) */
         NULL,                          /* Resume-Indication (5) */
         NULL,                          /* Suspend-Reject-Indication (6) */
         NULL,                          /* Not used (7) */
         daicplci_handle_uudata_ind,    /* User-Data-Indication (8) */
         daicplci_ignore_ind,           /* Congestion-Indication (9) */
         NULL,                          /* Indicate-Indication (0x0A) */
         NULL,                          /* Not used (0x0B) */
         NULL,                          /* Alert-Indication (0x0C) */
         daicplci_handle_info_ind       /* Info-Indication (0x0D) */
      },
      /* DAIC_PLCI_STATE_P5 */
      {
         NULL,                          /* Null command (0) */
         daicplci_ignore_ind,           /* Call-Connected-Indication (1) */
         daicplci_ignore_ind,           /* Call-Indication (2) */
         daicplci_handle_hangup_ind,    /* Hangup-Indication (3) */
         NULL,                          /* Suspend-Indication (4) */
         NULL,                          /* Resume-Indication (5) */
         NULL,                          /* Suspend-Reject-Indication (6) */
         NULL,                          /* Not used (7) */
         daicplci_handle_uudata_ind,    /* User-Data-Indication (8) */
         daicplci_ignore_ind,           /* Congestion-Indication (9) */
         NULL,                          /* Indicate-Indication (0x0A) */
         NULL,                          /* Not used (0x0B) */
         daicplci_ignore_ind,           /* Alert-Indication (0x0C) */
         daicplci_handle_info_ind       /* Info-Indication (0x0D) */
      },
      /* DAIC_PLCI_STATE_P6 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call-Connected-Indication (1) */
         NULL,                          /* Call-Indication (2) */
         NULL,                          /* Hangup-Indication (3) */
         NULL,                          /* Suspend-Indication (4) */
         NULL,                          /* Resume-Indication (5) */
         NULL,                          /* Suspend-Reject-Indication (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Indication (8) */
         NULL,                          /* Congestion-Indication (9) */
         NULL,                          /* Indicate-Indication (0x0A) */
         NULL,                          /* Not used (0x0B) */
         NULL,                          /* Alert-Indication (0x0C) */
         NULL                           /* Info-Indication (0x0D) */
      },
      /* DAIC_PLCI_STATE_P6_1 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call-Connected-Indication (1) */
         NULL,                          /* Call-Indication (2) */
         NULL,                          /* Hangup-Indication (3) */
         NULL,                          /* Suspend-Indication (4) */
         NULL,                          /* Resume-Indication (5) */
         NULL,                          /* Suspend-Reject-Indication (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Indication (8) */
         NULL,                          /* Congestion-Indication (9) */
         NULL,                          /* Indicate-Indication (0x0A) */
         NULL,                          /* Not used (0x0B) */
         NULL,                          /* Alert-Indication (0x0C) */
         NULL                           /* Info-Indication (0x0D) */
      },
      /* DAIC_PLCI_STATE_P6_2 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call-Connected-Indication (1) */
         NULL,                          /* Call-Indication (2) */
         NULL,                          /* Hangup-Indication (3) */
         NULL,                          /* Suspend-Indication (4) */
         NULL,                          /* Resume-Indication (5) */
         NULL,                          /* Suspend-Reject-Indication (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Indication (8) */
         NULL,                          /* Congestion-Indication (9) */
         NULL,                          /* Indicate-Indication (0x0A) */
         NULL,                          /* Not used (0x0B) */
         NULL,                          /* Alert-Indication (0x0C) */
         NULL                           /* Info-Indication (0x0D) */
      },
      /* DAIC_PLCI_STATE_P6_3 */
      {
         NULL,                          /* Null command (0) */
         NULL,                          /* Call-Connected-Indication (1) */
         NULL,                          /* Call-Indication (2) */
         NULL,                          /* Hangup-Indication (3) */
         NULL,                          /* Suspend-Indication (4) */
         NULL,                          /* Resume-Indication (5) */
         NULL,                          /* Suspend-Reject-Indication (6) */
         NULL,                          /* Not used (7) */
         NULL,                          /* User-Data-Indication (8) */
         NULL,                          /* Congestion-Indication (9) */
         NULL,                          /* Indicate-Indication (0x0A) */
         NULL,                          /* Not used (0x0B) */
         NULL,                          /* Alert-Indication (0x0C) */
         NULL                           /* Info-Indication (0x0D) */
      }
   };



/* -- Definitions for handling CAPI messages --- */

/** The indexes of CAPI messages to be used for action table lookups. */
enum
{
   ALERT_REQ_IDX = 0,
   CONN_REQ_IDX,
   CONN_RSP_IDX,
   CONN_ACT_RSP_IDX,
   DISC_REQ_IDX,
   DISC_RSP_IDX,
   INFO_REQ_IDX,
   INFO_RSP_IDX,
   SEL_B_PROT_REQ_IDX,
   FAC_REQ_IDX,
   FAC_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.
 */
static int g_auCapiMsgCmdIdx [256 * 2] =
   {
      -1, -1,
      ALERT_REQ_IDX,      -1,                           /* Alert-Request (0x01) */
      CONN_REQ_IDX,       CONN_RSP_IDX,                 /* Connect-Request, -Response (0x02) */
      -1,                 CONN_ACT_RSP_IDX,             /* Connect-Active-Response (0x03) */
      DISC_REQ_IDX,       DISC_RSP_IDX,                 /* Disconnect-Request, -Response (0x04) */
      -1,                 -1,                           /* Listen-Request (0x05) not handled in this layer */
                                                      -1, -1, -1, -1,
      INFO_REQ_IDX,       INFO_RSP_IDX,                 /* Info-Request, Info-Response (0x08) */
              -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1,
      SEL_B_PROT_REQ_IDX, -1,                           /* Select-B-Protocol-Request (0x41) */
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -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, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -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 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.
 */
typedef unsigned (DaicCapiMsgActionFct_t)
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    struct mbuf    *pmbMsg);

/**
 * Handle a Connect-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, thecaller 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 daicplci_handle_connect_req;

/**
 * Handle a CAPI Connect-Response.
 *
 * This function is called when one or more CAPI applications have received a
 * Connect-Indication but none of the has responded yet. So the now responding
 * application is either the first to accept the call or it is one to reject /
 * ignore it.
 *
 * If the call shall be accepted, the PLCI will be exclusively assigned to the
 * responding application. All other applications that earlier received the
 * Connect-Indication will now receive a Disconnect-Indication. The next
 * Connect-Response message for another application will simply be ignored by
 * the central CAPI message handling function (see daicplci_handle_capi_msg()).
 * As these applications receive a Disconnect-Indication, everything is fine.
 *
 * If the call shall be rejected or ignored, the responding application must
 * first be removed from the bit mask of applications that received the
 * Connect-Indication. Then it will be sent a Disconnect-Indication.
 *
 * If the negatively responding application was the last application of the bit
 * mask, the call will be rejected against the controller to release the
 * incoming call. The following controller messages will be handled without
 * an assigned application id.
 *
 * @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, thecaller 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 daicplci_handle_conn_rsp;

/**
 * Handle a CAPI Info-Request.
 *
 * This CAPI message can be used for several purposes. The first is for overlap
 * sending, i.e. for dialing a number in several parts or even digit for digit.
 * The Connect-Request in this case will not contain any digits to dial, i.e.
 * the called party number is empty. All digits are dialed through Info-Requests
 * with the called party number of this message set to the (next) string of
 * digits to dial. The additional info part of the Info-Request will then
 * normally be empty, the sending complete part may be filled to signal
 * completeness of the dialed number. The result is to send out a controller
 * Call-Request message. When the return code arrives, the application will be
 * sent an Info-Confirm message.
 *
 * The second purpose is to send out only user-user data. The controller
 * supports a specific User-User-Data-Request that must be sent out in this
 * case. This will be done if only the user-user data part of the Info-Request
 * is filled. The return code of this controller message must also result in an
 * Info-Confirm message sent to the application.
 *
 * The last purpose is to send out some information using the additional info
 * part of the message, where the user-user data part is not or not only filled.
 * The called party number part will then be empty. The result is to send out a
 * controller Info-Request with the content of the additional info part put into
 * this message.
 *
 * @note The controller or the network may not support all combinations of
 *       information elements that may be specified within an Info-Request CAPI
 *       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 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 daicplci_handle_info_req;

/**
 * Handle a CAPI Alert-Request.
 *
 * An Alert-Request will simply result in a controller Alert-Request to be sent
 * out. The Alert-Confirm message will be sent to the application when the
 * return code for the controller Alert-Request arrives.
 *
 * @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 daicplci_handle_alert_req;

/**
 * Handle a CAPI Select-B-Protocol-Request.
 *
 * The B-channel protocol can only be changed if no NCCI is currently active for
 * a PLCI and if the PLCI is in the active state (P-ACT). The later is checked
 * by the state tables for the PLCI state machine, i.e. this function is only
 * called in state P-ACT.
 *
 * In order to change the B-channel protocol we must remove the current
 * signaling id and allocate a new one. After allocating the new network id the
 * Select-B-Protocol-Confirm message must be sent to the application. During
 * this time, no other message may be handled. As all actions in this phase are
 * a Remove-Request and an Assign-Request, we do not need a new state, these
 * two operations cannot be disturbed by other messages (CAPI messages or
 * controller indications).
 *
 * @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 daicplci_handle_sel_b_prot_req;

/**
 * Handle a CAPI Disconnect-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 daicplci_handle_disc_req;

/**
 * Handle a 2nd or more CAPI Disconnect-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 daicplci_handle_2nd_disc_req;

/**
 * Handle a CAPI Disconnect-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 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 daicplci_handle_disc_rsp;

/**
 * Handle any CAPI response besides the ones requiring special handling.
 *
 * @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 daicplci_handle_gen_rsp;

/** The function table for handling CAPI messages. */
static DaicCapiMsgActionFct_t *g_aCapiMsgAction [DAIC_PLCI_NUM_STATES]
                                                [NUM_CAPI_MSG_IDX] =
   {
      /* DAIC_PLCI_STATE_P0 */
      {
         NULL,                          /* ALERT_REQ_IDX */
         daicplci_handle_connect_req,   /* CONN_REQ_IDX */
         NULL,                          /* CONN_RSP_IDX */
         NULL,                          /* CONN_ACT_RSP_IDX */
         NULL,                          /* DISC_REQ_IDX */
         NULL,                          /* DISC_RSP_IDX */
         NULL,                          /* INFO_REQ_IDX */
         NULL,                          /* INFO_RSP_IDX */
         NULL,                          /* SEL_B_PROT_REQ_IDX */
         NULL,                          /* FAC_REQ_IDX */
         NULL                           /* FAC_RSP_IDX */
      },
      /* DAIC_PLCI_STATE_P0_1 */
      {
         NULL,                          /* ALERT_REQ_IDX */
         NULL,                          /* CONN_REQ_IDX */
         NULL,                          /* CONN_RSP_IDX */
         NULL,                          /* CONN_ACT_RSP_IDX */
         NULL,                          /* DISC_REQ_IDX */
         NULL,                          /* DISC_RSP_IDX */
         NULL,                          /* INFO_REQ_IDX */
         NULL,                          /* INFO_RSP_IDX */
         NULL,                          /* SEL_B_PROT_REQ_IDX */
         NULL,                          /* FAC_REQ_IDX */
         NULL                           /* FAC_RSP_IDX */
      },
      /* DAIC_PLCI_STATE_P0_2 */
      {
         NULL,                          /* ALERT_REQ_IDX */
         NULL,                          /* CONN_REQ_IDX */
         NULL,                          /* CONN_RSP_IDX */
         NULL,                          /* CONN_ACT_RSP_IDX */
         NULL,                          /* DISC_REQ_IDX */
         NULL,                          /* DISC_RSP_IDX */
         NULL,                          /* INFO_REQ_IDX */
         NULL,                          /* INFO_RSP_IDX */
         NULL,                          /* SEL_B_PROT_REQ_IDX */
         NULL,                          /* FAC_REQ_IDX */
         NULL                           /* FAC_RSP_IDX */
      },
      /* DAIC_PLCI_STATE_P1 */
      {
         NULL,                          /* ALERT_REQ_IDX */
         NULL,                          /* CONN_REQ_IDX */
         NULL,                          /* CONN_RSP_IDX */
         NULL,                          /* CONN_ACT_RSP_IDX */
         daicplci_handle_disc_req,      /* DISC_REQ_IDX */
         NULL,                          /* DISC_RSP_IDX */
         daicplci_handle_info_req,      /* INFO_REQ_IDX */
         daicplci_handle_gen_rsp,       /* INFO_RSP_IDX */
         NULL,                          /* SEL_B_PROT_REQ_IDX */
         NULL,                          /* FAC_REQ_IDX */
         NULL                           /* FAC_RSP_IDX */
      },
      /* DAIC_PLCI_STATE_P1_1 */
      {
         NULL,                          /* ALERT_REQ_IDX */
         NULL,                          /* CONN_REQ_IDX */
         NULL,                          /* CONN_RSP_IDX */
         NULL,                          /* CONN_ACT_RSP_IDX */
         NULL,                          /* DISC_REQ_IDX */
         NULL,                          /* DISC_RSP_IDX */
         NULL,                          /* INFO_REQ_IDX */
         NULL,                          /* INFO_RSP_IDX */
         NULL,                          /* SEL_B_PROT_REQ_IDX */
         NULL,                          /* FAC_REQ_IDX */
         NULL                           /* FAC_RSP_IDX */
      },
      /* DAIC_PLCI_STATE_P2 */
      {
         daicplci_handle_alert_req,     /* ALERT_REQ_IDX */
         NULL,                          /* CONN_REQ_IDX */
         daicplci_handle_conn_rsp,      /* CONN_RSP_IDX */
         NULL,                          /* CONN_ACT_RSP_IDX */
         daicplci_handle_disc_req,      /* DISC_REQ_IDX */
         NULL,                          /* DISC_RSP_IDX */
         NULL,                          /* INFO_REQ_IDX */
         daicplci_handle_gen_rsp,       /* INFO_RSP_IDX */
         NULL,                          /* SEL_B_PROT_REQ_IDX */
         NULL,                          /* FAC_REQ_IDX */
         NULL                           /* FAC_RSP_IDX */
      },
      /* DAIC_PLCI_STATE_P4 */
      {
         NULL,                          /* ALERT_REQ_IDX */
         NULL,                          /* CONN_REQ_IDX */
         NULL,                          /* CONN_RSP_IDX */
         NULL,                          /* CONN_ACT_RSP_IDX */
         daicplci_handle_disc_req,      /* DISC_REQ_IDX */
         NULL,                          /* DISC_RSP_IDX */
         daicplci_handle_info_req,      /* INFO_REQ_IDX */
         daicplci_handle_gen_rsp,       /* INFO_RSP_IDX */
         NULL,                          /* SEL_B_PROT_REQ_IDX */
         NULL,                          /* FAC_REQ_IDX */
         NULL                           /* FAC_RSP_IDX */
      },
      /* DAIC_PLCI_STATE_P4_1 */
      {
         NULL,                          /* ALERT_REQ_IDX */
         NULL,                          /* CONN_REQ_IDX */
         NULL,                          /* CONN_RSP_IDX */
         NULL,                          /* CONN_ACT_RSP_IDX */
         NULL,                          /* DISC_REQ_IDX */
         NULL,                          /* DISC_RSP_IDX */
         NULL,                          /* INFO_REQ_IDX */
         NULL,                          /* INFO_RSP_IDX */
         NULL,                          /* SEL_B_PROT_REQ_IDX */
         NULL,                          /* FAC_REQ_IDX */
         NULL                           /* FAC_RSP_IDX */
      },
      /* DAIC_PLCI_STATE_PACT */
      {
         NULL,                          /* ALERT_REQ_IDX */
         NULL,                          /* CONN_REQ_IDX */
         NULL,                          /* CONN_RSP_IDX */
         daicplci_handle_gen_rsp,       /* CONN_ACT_RSP_IDX */
         daicplci_handle_disc_req,      /* DISC_REQ_IDX */
         NULL,                          /* DISC_RSP_IDX */
         daicplci_handle_info_req,      /* INFO_REQ_IDX */
         daicplci_handle_gen_rsp,       /* INFO_RSP_IDX */
         daicplci_handle_sel_b_prot_req,/* SEL_B_PROT_REQ_IDX */
         NULL,                          /* FAC_REQ_IDX */
         NULL                           /* FAC_RSP_IDX */
      },
      /* DAIC_PLCI_STATE_P5 */
      {
         NULL,                          /* ALERT_REQ_IDX */
         NULL,                          /* CONN_REQ_IDX */
         daicplci_handle_gen_rsp,       /* CONN_RSP_IDX */
         daicplci_handle_gen_rsp,       /* CONN_ACT_RSP_IDX */
         daicplci_handle_2nd_disc_req,  /* DISC_REQ_IDX */
         NULL,                          /* DISC_RSP_IDX */
         NULL,                          /* INFO_REQ_IDX */
         daicplci_handle_gen_rsp,       /* INFO_RSP_IDX */
         NULL,                          /* SEL_B_PROT_REQ_IDX */
         NULL,                          /* FAC_REQ_IDX */
         NULL                           /* FAC_RSP_IDX */
      },
      /* DAIC_PLCI_STATE_P6 */
      {
         NULL,                          /* ALERT_REQ_IDX */
         NULL,                          /* CONN_REQ_IDX */
         daicplci_handle_gen_rsp,       /* CONN_RSP_IDX */
         daicplci_handle_gen_rsp,       /* CONN_ACT_RSP_IDX */
         daicplci_handle_2nd_disc_req,  /* DISC_REQ_IDX */
         NULL,                          /* DISC_RSP_IDX */
         NULL,                          /* INFO_REQ_IDX */
         daicplci_handle_gen_rsp,       /* INFO_RSP_IDX */
         NULL,                          /* SEL_B_PROT_REQ_IDX */
         NULL,                          /* FAC_REQ_IDX */
         NULL                           /* FAC_RSP_IDX */
      },
      /* DAIC_PLCI_STATE_P6_1 */
      {
         NULL,                          /* ALERT_REQ_IDX */
         NULL,                          /* CONN_REQ_IDX */
         NULL,                          /* CONN_RSP_IDX */
         NULL,                          /* CONN_ACT_RSP_IDX */
         NULL,                          /* DISC_REQ_IDX */
         NULL,                          /* DISC_RSP_IDX */
         NULL,                          /* INFO_REQ_IDX */
         NULL,                          /* INFO_RSP_IDX */
         NULL,                          /* SEL_B_PROT_REQ_IDX */
         NULL,                          /* FAC_REQ_IDX */
         NULL                           /* FAC_RSP_IDX */
      },
      /* DAIC_PLCI_STATE_P6_2 */
      {
         NULL,                          /* ALERT_REQ_IDX */
         NULL,                          /* CONN_REQ_IDX */
         daicplci_handle_gen_rsp,       /* CONN_RSP_IDX */
         daicplci_handle_gen_rsp,       /* CONN_ACT_RSP_IDX */
         daicplci_handle_2nd_disc_req,  /* DISC_REQ_IDX */
         daicplci_handle_disc_rsp,      /* DISC_RSP_IDX */
         NULL,                          /* INFO_REQ_IDX */
         daicplci_handle_gen_rsp,       /* INFO_RSP_IDX */
         NULL,                          /* SEL_B_PROT_REQ_IDX */
         NULL,                          /* FAC_REQ_IDX */
         NULL                           /* FAC_RSP_IDX */
      },
      /* DAIC_PLCI_STATE_P6_3 */
      {
         NULL,                          /* ALERT_REQ_IDX */
         NULL,                          /* CONN_REQ_IDX */
         NULL,                          /* CONN_RSP_IDX */
         NULL,                          /* CONN_ACT_RSP_IDX */
         NULL,                          /* DISC_REQ_IDX */
         NULL,                          /* DISC_RSP_IDX */
         NULL,                          /* INFO_REQ_IDX */
         NULL,                          /* INFO_RSP_IDX */
         NULL,                          /* SEL_B_PROT_REQ_IDX */
         NULL,                          /* FAC_REQ_IDX */
         NULL                           /* FAC_RSP_IDX */
      }
   };





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





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





/**
 * Reset a PLCI state machine.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data to be reset.
 *
 * @return Nothing.
 */
static void daicplci_reset
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData);

/**
 * Handle messages enqueued for a PLCI.
 *
 * This function will handle any message enqueued for a given PLCI, 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 pPlciData             i/O: The PLCI data for this operation.
 *
 * @return Nothing.
 */
static void daicplci_handle_queued_messages
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData);

/**
 * Handle an indication from the controller.
 *
 * @param pPortData             I/O: The port data as the operation context.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */
static void daicplci_handle_ind
   (DaicPortData_t      *pPortData,
    DaicPlciData_t      *pPlciData,
    const DaicIndData_t *pIndData);

/**
 * Handle a CAPI message to a PLCI 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 uPlci                 I: The addressed PLCI value, equal to the
 *                                 signalling 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.
 */
static unsigned daicplci_handle_capi_msg
   (DaicPortData_t  *pPortData,
    unsigned         uPlci,
    unsigned         uApplID,
    struct mbuf     *pmbMsg);

/**
 * Abort a connection in any possible state.
 *
 * A connection may be aborted in all states when no controller request is
 * pending. If this function is called with a controller request running, we can
 * only record this abort request by setting a flag. The handler function for
 * return codes will check this and proceed with aborting the connection as soon
 * as the return code arrives.
 *
 * If no controller request is pending when this function is called, we must
 * perform actions according the current state. If the current state denotes a
 * running controller request and no one is really active, we must assume the
 * request failed with a negative return code.
 *
 * The actions to perform for aborting a connection are removing all NCCIs,
 * sending a Hangup-Request and removing both the network and the signaling id.
 * According to the current state, some actions need not be done because the
 * connection did not make its way completely to active state. The NCCIs are
 * normally removed in the handler for the Hangup-Request return code, so we
 * need not do this here.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param uCauseVal             I: The cause value to signal to the network.
 *
 * @return Nothing.
 */
static void daicplci_abort_connection
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uCauseVal);

/**
 * Continue processing a Connect-Request.
 *
 * This function is called to proceed with a CAPI Connect-Request after a
 * successful Assign-Request. It will parse the CAPI message for necessary call
 * parameters and send out a controller Call-Request.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 *
 * @return Nothing.
 */
static void daicplci_continue_connect_req
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData);

/**
 * Send out a Connect-Confirm and perform follow-up operations.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param uInfoValue            I: The info value to report in the message.
 *
 * @return Nothing.
 */
static void daicplci_send_connect_conf
   (DaicPortData_t       *pPortData,
    DaicPlciData_t       *pPlciData,
    unsigned              uInfoValue);

/**
 * Send out a Disconnect-Confirm.
 *
 * @pre This function must only be called with the pmbCurrCapiMsg member of the
 *      PLCI data structure set to a valid mbuf with an Disconnect-Request.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param uInfoVal              I: The info value to insert into the message.
 *
 * @return Nothing
 */
static void daicplci_send_disc_conf
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uInfoVal);

/**
 * Send out a Select-B-Protocol-Confirm.
 *
 * @pre This function must only be called with the pmbCurrCapiMsg member of the
 *      PLCI data structure set to a valid mbuf with a
 *      Select-B-Protocol-Request.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param uInfoVal              I: The info value to insert into the message.
 *
 * @return Nothing.
 */
static void daicplci_send_sel_b_prot_conf
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uInfoVal);

/**
 * Send out an Info-Confirm.
 *
 * @pre This function must only be called with the pmbCurrCapiMsg member of the
 *      PLCI data structure set to a valid mbuf with an Info-Request.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param uInfoVal              I: The info value to insert into the message.
 *
 * @return Nothing
 */
static void daicplci_send_info_conf
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uInfoVal);

/**
 * Send out an Alert-Confirm.
 *
 * @pre This function must only be called with the pmbCurrCapiMsg member of the
 *      PLCI data structure set to a valid mbuf with an Alert-Request.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param uInfoVal              I: The info value to insert into the message.
 *
 * @return Nothing
 */
static void daicplci_send_alert_conf
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uInfoVal);

/**
 * Send out a Disconnect-Ind and perform a state transit.
 *
 * This function is called to send a Disconnect-Indication to all CAPI
 * applications associated with a PLCI. This may be an application that is
 * exclusively assigned to a PLCI or it may be a bit mask of all applications
 * that earlier received a Connect-Indication.
 *
 * If the PLCI is not already assigned to an application, all applications
 * receiving the Disconnect-Indication are removed from the bit mask. In this
 * case the next state is P-6.3 and a Remove-Request is also sent to the
 * controller.
 *
 * If the PLCI is assigned to an application, the state will be changed to P-6.2
 * and we wait for the Disconnect-Response.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 *
 * @return Nothing.
 */
static void daicplci_send_disc_ind
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData);

/**
 * Send out a Disconnect-Ind to a specific application.
 *
 * This function is called to send out a Disconnect-Indication to an application
 * without regarding the current state of the PLCI. This may e.g. be used to
 * send this message to applications for an incoming call that do not get it.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param uApplID               I: The application id to send the message to.
 * @param uReason               I: The reason value to put into the message.
 *
 * @return Nothing.
 */
static void daicplci_send_disc_ind_to_appl
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uApplID,
    unsigned        uReason);

/**
 * 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 pPlciData             I/O: The PLCI 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 daicplci_send_gen_conf
   (DaicPortData_t  *pPortData,
    DaicPlciData_t  *pPlciData,
    const CAPIMsg_t *pCapiReq,
    unsigned         uInfoVal);

/**
 * Parse a Connect-Request CAPI message.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I: The PLCI data as the context for this
 *                                 operation.
 * @param pCapiMsg              I: The CAPI message to parse.
 * @param ppCedPN               O: The address of the called party number.
 * @param ppCngPN               O: The address of the calling party number.
 * @param ppCedPSa              O: The address of the called party subaddress.
 * @param ppCngPSa              O: The address of the calling party subaddress.
 * @param pBProt                O: The area to store the B-Protocol information.
 * @param ppBC                  O: The address of the bearer capability.
 * @param ppLLC                 O: The address of the low layer compatibility.
 * @param ppHLC                 O: The address of the high layer compatibility.
 * @param ppBChnInfo            O: The address of the B-channel information.
 * @param ppKeypad              O: The address of the keypad facility.
 * @param ppUUData              O: The address of the user-user data.
 * @param ppFacility            O: The address of the facility data array.
 * @param ppSendingComplete     O: The address of the sending complete ie.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static unsigned daicplci_extract_conn_req_params
   (DaicPortData_t       *pPortData,
    DaicPlciData_t       *pPlciData,
    const CAPIMsg_t      *pCapiMsg,
    const u_int8_t      **ppCedPN,
    const u_int8_t      **ppCngPN,
    const u_int8_t      **ppCedPSa,
    const u_int8_t      **ppCngPSa,
    DaicBProtocolData_t  *pBProt,
    const u_int8_t      **ppBC,
    const u_int8_t      **ppLLC,
    const u_int8_t      **ppHLC,
    const u_int8_t      **ppBChnInfo,
    const u_int8_t      **ppKeypad,
    const u_int8_t      **ppUUData,
    const u_int8_t      **ppFacility,
    const u_int8_t      **ppSendingComplete);

/**
 * Parse a Connect-Response CAPI message.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I: The PLCI data as the context for this
 *                                 operation.
 * @param pCapiMsg              I: The CAPI message to parse.
 * @param ppConnN               O: The address of the connected number.
 * @param ppConnSa              O: The address of the connected subaddress.
 * @param pBProt                O: The area to store the B-Protocol information.
 * @param ppLLC                 O: The address of the low layer compatibility.
 * @param ppBChnInfo            O: The address of the B-channel information.
 * @param ppKeypad              O: The address of the keypad facility.
 * @param ppUUData              O: The address of the user-user data.
 * @param ppFacility            O: The address of the facility data array.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static unsigned daicplci_extract_conn_rsp_params
   (DaicPortData_t       *pPortData,
    DaicPlciData_t       *pPlciData,
    const CAPIMsg_t      *pCapiMsg,
    const u_int8_t      **ppConnN,
    const u_int8_t      **ppConnSa,
    DaicBProtocolData_t  *pBProt,
    const u_int8_t      **ppLLC,
    const u_int8_t      **ppBChnInfo,
    const u_int8_t      **ppKeypad,
    const u_int8_t      **ppUUData,
    const u_int8_t      **ppFacility);

/**
 * Parse an Info-Request CAPI message.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I: The PLCI data as the context for this
 *                                 operation.
 * @param pCapiMsg              I: The CAPI message to parse.
 * @param ppCedPN               O: The address of the connected number.
 * @param ppBChnInfo            O: The address of the B-channel information.
 * @param ppKeypad              O: The address of the keypad facility.
 * @param ppUUData              O: The address of the user-user data.
 * @param ppFacility            O: The address of the facility data array.
 * @param ppSendingComplete     O: The address of the sending complete
 *                                 information element.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static unsigned daicplci_extract_info_req_params
   (DaicPortData_t   *pPortData,
    DaicPlciData_t   *pPlciData,
    const CAPIMsg_t  *pCapiMsg,
    const u_int8_t  **ppCedPN,
    const u_int8_t  **ppBChnInfo,
    const u_int8_t  **ppKeypad,
    const u_int8_t  **ppUUData,
    const u_int8_t  **ppFacility,
    const u_int8_t  **ppSendingComplete);

/**
 * Parse a B-protocol settings structure of a CAPI message.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data as the context for this
 *                                 operation.
 * @param pBProtBuf             I: The B-protocol structure to parse.
 * @param uCmdContext           I: The CAPI message command as the context for
 *                                 setting the B-channel operation mode.
 * @param pBProtData            O: The destination for the parsed B-protocol
 *                                 settings.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static unsigned daicplci_extract_bprotocol
   (DaicPortData_t        *pPortData,
    DaicPlciData_t        *pPlciData,
    const CAPIBProtocol_t *pBProtBuf,
    unsigned               uCmdContext,
    DaicBProtocolData_t   *pBProtData);

/**
 * Parse a B2-configuration structure.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data as the context for this
 *                                 operation.
 * @param pB2CfgBuf             I: The B2-configuration structure to parse.
 * @param pBProtData            O: The destination for the parsed B-protocol
 *                                 settings.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static unsigned daicplci_extract_b2config
   (DaicPortData_t      *pPortData,
    DaicPlciData_t      *pPlciData,
    const u_int8_t      *pB2CfgBuf,
    DaicBProtocolData_t *pBProtData);

/**
 * Parse a B3-configuration structure.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data as the context for this
 *                                 operation.
 * @param pB3CfgBuf             I: The B3-configuration structure to parse.
 * @param pBProtData            O: The destination for the parsed B-protocol
 *                                 settings.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static unsigned daicplci_extract_b3config
   (DaicPortData_t      *pPortData,
    DaicPlciData_t      *pPlciData,
    const u_int8_t      *pB3CfgBuf,
    DaicBProtocolData_t *pBProtData);

/**
 * Parse an Additional Info structure of a CAPI message.
 *
 * @pre All output parameters must be initialized to NULL when calling this
 *      function.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data as the context for this
 *                                 operation.
 * @param pAddInfo              I: The additional info structure to parse.
 * @param ppBChnInfo            O: The address of the B-channel information.
 * @param ppKeypad              O: The address of the keypad facility.
 * @param ppUUData              O: The address of the user-user data.
 * @param ppFacility            O: The address of the facility data array.
 * @param ppSendingComplete     O: The address of the sending complete
 *                                 information element.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static unsigned daicplci_extract_add_info_params
   (DaicPortData_t  *pPortData,
    DaicPlciData_t  *pPlciData,
    const u_int8_t  *pAddInfo,
    const u_int8_t **ppBChnInfo,
    const u_int8_t **ppKeypad,
    const u_int8_t **ppUUData,
    const u_int8_t **ppFacility,
    const u_int8_t **ppSendingComplete);

/**
 * Create CAPI Info-Indications for information elements in a controller
 * indication.
 *
 * All information elements in the specified buffer are checked against the CAPI
 * Info-Mask also specified. If the application shall receive an Info-Indication
 * for the information element, a CAPI message is created.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data as a connection context for
 *                                 this operation.
 * @param bMsgType              I: This value reflects the D-channel message
 *                                 type, the information elements in pabIEBuffer
 *                                 belong to. If it is null, the corresponding
 *                                 message type is unknown.
 * @param pabIEBuffer           I: The buffer containing the information
 *                                 elements to examine.
 * @param nLenIEBuffer          I: The length in bytes of the buffer pabIEBuffer.
 * @param uApplID               I: The application id to receive the CAPI
 *                                 Info-Indications.
 * @param dwInfoMask            I: The CAPI Info-Mask to check.
 *
 * @return Nothing.
 */
static void daicplci_create_capi_info_ind
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    u_int8_t        bMsgType,
    const u_int8_t *pabIEBuffer,
    size_t          nLenIEBuffer,
    unsigned        uApplID,
    u_int32_t       dwInfoMask);

/**
 * Create a CAPI Info-Indication for an information element.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data as a connection context for
 *                                 this operation.
 * @param uApplID               I: The application id to receive the CAPI
 *                                 Info-Indications.
 * @param uIE                   I: The information element to report.
 * @param nIEDataLen            I: The length of the data part of the
 *                                 information element. If the element does not
 *                                 contain a data part, this value may be null.
 * @param pbIEData              I: The buffer containing the data part of the
 *                                 information element after the length byte. If
 *                                 the information element does not contain any
 *                                 data (nIEDataLen is null), this pointer is
 *                                 ignored.
 *
 * @return Nothing.
 */
static void daicplci_create_capi_info_ind_for_ie
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uApplID,
    unsigned        uIE,
    size_t          nIEDataLen,
    const u_int8_t *pbIEData);

/**
 * Create a CAPI Info-Indication for a Q.931 message type.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data as a connection context for
 *                                 this operation.
 * @param uApplID               I: The application id to receive the CAPI
 *                                 Info-Indications.
 * @param uMsgType              I: The message type to report.
 *
 * @return Nothing.
 */
static void daicplci_create_capi_info_ind_for_mt
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uApplID,
    unsigned        uMsgType);

/**
 * Parse an information element buffer for Connect-Indication specific data.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data as a connection context for
 *                                 this operation.
 * @param pabIEBuffer           I: The buffer containing the information
 *                                 elements to examine.
 * @param nLenIEBuffer          I: The length in bytes of the buffer pabIEBuffer.
 * @param ppCedPN               O: The address of the called party number.
 * @param ppCngPN               O: The address of the calling party number.
 * @param ppCedPSa              O: The address of the called party subaddress.
 * @param ppCngPSa              O: The address of the calling party subaddress.
 * @param ppBC                  O: The address of the bearer capability.
 * @param ppLLC                 O: The address of the low layer compatibility.
 * @param ppHLC                 O: The address of the high layer compatibility.
 * @param ppBChnInfo            O: The address of the B-channel information.
 * @param ppKeypad              O: The address of the keypad facility.
 * @param ppUUData              O: The address of the user-user data.
 * @param ppFacility            O: The address of the facility data array.
 *
 * @return Nothing.
 */
static void daicplci_extract_conn_ind_info
   (DaicPortData_t  *pPortData,
    DaicPlciData_t  *pPlciData,
    const u_int8_t  *pabIEBuffer,
    size_t           nLenIEBuffer,
    const u_int8_t **ppCedPN,
    const u_int8_t **ppCngPN,
    const u_int8_t **ppCedPSa,
    const u_int8_t **ppCngPSa,
    const u_int8_t **ppBC,
    const u_int8_t **ppLLC,
    const u_int8_t **ppHLC,
    const u_int8_t **ppBChnInfo,
    const u_int8_t **ppKeypad,
    const u_int8_t **ppUUData,
    const u_int8_t **ppFacility);

/**
 * Parse an information element buffer for a call connected indication.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data as a connection context for
 *                                 this operation.
 * @param pabIEBuffer           I: The buffer containing the information
 *                                 elements to examine.
 * @param nLenIEBuffer          I: The length in bytes of the buffer pabIEBuffer.
 * @param ppCedPN               O: The address of the connected party number.
 * @param ppCedPSa              O: The address of the connected party subaddress.
 *
 * @return Nothing.
 */
static void daicplci_extract_call_conn_info
   (DaicPortData_t  *pPortData,
    DaicPlciData_t  *pPlciData,
    const u_int8_t  *pabIEBuffer,
    size_t           nLenIEBuffer,
    const u_int8_t **ppCedPN,
    const u_int8_t **ppCedPSa);

/**
 * Parse an information element buffer for a cause value.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param pabIEBuffer           I: The buffer containing the information
 *                                 elements to examine.
 * @param nLenIEBuffer          I: The length in bytes of the buffer pabIEBuffer.
 * @param pbCauseVal            O: The address to receive the cause byte if
 *                                 successful. If a cause information element
 *                                 is not found in the buffer, the value will
 *                                 not be changed.
 *
 * @return Nothing.
 */
static void daicplci_extract_cause
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    const u_int8_t *pabIEBuffer,
    size_t          nLenIEBuffer,
    u_int8_t       *pbCauseVal);

/**
 * Create a CIP value out of BC and HLC, if available.
 */
static unsigned daicplci_create_cip_value
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    const u_int8_t *pBC,
    const u_int8_t *pHLC);

/**
 * Select a pre-defined BC byte string from a CAPI CIP value.
 */
static u_int8_t *daicplci_get_bc_from_cip
   (unsigned uCipValue);

/**
 * Select a pre-defined HLC byte string from a CAPI CIP value.
 */
static u_int8_t *daicplci_get_hlc_from_cip
   (unsigned uCipValue);

/**
 * Convert a CAPI CIP-Werts into Service Indicator and Additional Information.
 */
static void daicplci_get_sin_sadd_from_cip
   (unsigned  uCipValue,
    u_int8_t *pSin,
    u_int8_t *pSAdd);

/**
 * Send out a controller Hangup-Request with the necessary state transit.
 *
 * @param pPortData             I/O: The port for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param uCauseVal             I: The cause value to report.
 *
 * @return Nothing.
 */
static void daicplci_send_hangup_req
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uCauseVal);

/**
 * Send out a controller Assign-Request for a new network id.
 *
 * This function is called to obtain a new network id for B-channel access for a
 * call in the connection establishment phase and when changing the B-channel
 * protocol with a Select-B-Protocol-Request. This is done when receiving a
 * Call-Connected- or a Call-Indication from the controller or for changing the
 * B-channel protocol after the successful Remove-Request for the network
 * channel. In the call establishment phase the receipt of the assign return
 * code will trigger the forwarding of a stored Connect-Active-Indication
 * resulting from the formerly mentionned controller messages. When changing the
 * B-channel protocol the assign return code will resul in a
 * Select-B-Protocol-Confirm. The two situations are distinguished through the
 * current state (P-ACT for B-channel protocol change, otherwise call
 * establishment is assumed).
 *
 * @param pPortData             I/O: The port for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 *
 * @return Nothing.
 */
static void daicplci_send_net_assign_req
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData);

/**
 * Send out a controller Remove-Request for the network id with the necessary
 * state transit.
 *
 * @param pPortData             I/O: The port for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 *
 * @return Nothing.
 */
static void daicplci_send_net_remove_req
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData);

/**
 * Send out a controller Remove-Request for the signaling id with the necessary
 * state transit.
 *
 * @param pPortData             I/O: The port for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 *
 * @return Nothing.
 */
static void daicplci_send_sig_remove_req
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData);





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





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

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

} /* daicplci_reset_all */





/**
 * Assign a free PLCI entry for a new logical connection.
 *
 * @param pPortData             I/O: the port data as the operation context.
 * @param uSigId                I: The signaling id for the connection if known.
 *                                 This will only be the case for incoming
 *                                 connections, where the signaling id is taken
 *                                 from the global id state machine. For
 *                                 outgoing connections, originating from a
 *                                 CAPI Connect-Request, a signaling id must
 *                                 still be assigned. So in this case it is
 *                                 simply specified as 0.
 *
 * @retval NULL                 No more PLCI available.
 * @retval Else                 The address of the PLCI data for the newly
 *                              assigned PLCI.
 */

DaicPlciData_t *daicplci_alloc_new_plci
   (DaicPortData_t *pPortData,
    unsigned        uSigId)
{
   DaicPlciData_t *pPlciData;
   size_t          n;
   int             i;
   
   /* find a new free entry */
   for (n = ARRAY_COUNT (pPortData->aPlciData),
           i = ((int) (pPortData->uLastAllocatedPlci) + 1) %
               (int) ARRAY_COUNT (pPortData->aPlciData);
        n > 0;
        --n, i = (i + 1) % (int) ARRAY_COUNT (pPortData->aPlciData))
   {
      if (i == 0)
      {
         continue;
      }
      pPlciData = &(pPortData->aPlciData [i]);
      if (pPlciData->uPlci == 0)
      {
         break;
      }
   }
   if (n == 0)
   {
      DBG (LOG_INFO, pPortData->iUnit,
           "Port %u: All PLCIs (%zu) busy, unable to allocate a new one",
           pPortData->uPortIdx, ARRAY_COUNT (pPortData->aPlciData) - 1);
      return (NULL);
   }
   /* i now indexes a free PLCI entry and pPlciData points to it */
   
   /* fill the assigned PLCI with the necessary data */
   pPlciData->state               = DAIC_PLCI_STATE_P0;
   pPlciData->uPlci               = (unsigned) i;
   pPlciData->dwCid               =
      (u_int32_t) ((pPlciData->uPlci << CAPI_CIDSHIFT_PLCI) |
                   pPortData->uUniqueCapiCtlrNum);
   pPlciData->uSigId              = uSigId;
   pPlciData->uNetId              = 0;
   pPlciData->uApplID             = 0;
   pPlciData->iApplDataIdx        = 0;
   pPlciData->ulApplIdxBitField   = 0;
   pPlciData->nNumNcci            = 0;
   pPlciData->ulFlags             = 0;
   pPlciData->uDisconnectCause    = 0;
   pPlciData->uAbortCause         = 0;
   pPlciData->uCurrCapiReqMsgNum  = 0;
   pPlciData->msgQueue.ifq_maxlen = 10;

   pPortData->nNumPlci++;

   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Allocated for new connection, signaling id %u",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        pPlciData->uSigId);

   return (pPlciData);
} /* daicplci_alloc_new_plci */





/**
 * 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.
 * @param iApplIdx              I: The index into the application data array for
 *                                 the application id.
 *
 * @return Nothing.
 */

void daicplci_release_appl
   (DaicPortData_t *pPortData,
    unsigned        uApplID,
    int             iApplIdx)
{
   int             i;
   DaicPlciData_t *pPlciData;
   size_t          n;
   struct mbuf    *pmb;
   unsigned long   ulApplBit;
   
   /* loop through all PLCI entries to find a connection for the application
    * specified
    */
   /* Note: PLCI 0 is never used and need not be checked. */
   for (i = 1; (size_t) i < ARRAY_COUNT (pPortData->aPlciData); ++i)
   {
      /* check for entry in use */
      pPlciData = &(pPortData->aPlciData [i]);
      if (pPlciData->uPlci == 0)
      {
         continue;
      }
      
      /* check for entry exclusively assigned to the application id specified */
      if (pPlciData->uApplID == uApplID)
      {
         /* release the application from this PLCI */
         pPlciData->uApplID = 0;
         DBG (LOG_TRACE, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: Application %u unassigned",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              uApplID);
         
         /* the connection will be terminated, so all pending CAPI messages must
          * be marked as invalid (by setting their message length to 0);
          * controller indications must remain intact
          */
         for (n = 0, pmb = pPlciData->msgQueue.ifq_head;
              pmb != NULL;
              pmb = pmb->m_nextpkt)
         {
            if (pmb->m_type != MT_DATA)
            {
               continue;
            }
            C_PUT_WORD (mtod (pmb, CAPIMsg_t *)->head.wLen, 0);
            ++n;
         }
         DBG (LOG_DEBUG, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: CAPI message queue cleaned up, %zu messages invalidated",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              n);

         /* now finally start aborting the connection */
         daicplci_abort_connection (pPortData, pPlciData, DAIC_CAUSE_NORMAL);
         continue;
      }
      /* if the current entry is still not assigned to any application, check
       * if the application id is part of the bitmask of application indices
       */
      else if (pPlciData->uApplID == 0 &&
               pPlciData->state == DAIC_PLCI_STATE_P2)
      {
         ulApplBit = (1 << iApplIdx);
         pPlciData->ulApplIdxBitField &= ~ulApplBit;
         DBG (LOG_DEBUG, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: Appl. id %u, index %d removed from application bitmask",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              uApplID, iApplIdx);
         
         if (pPlciData->ulApplIdxBitField == 0)
         {
            DBG (LOG_DEBUG, pPortData->iUnit,
                 "Port %u: PLCI %u: State %d: Last application id removed from application bitmask, reject call",
                 pPortData->uPortIdx, pPlciData->uPlci,
                 (int) (pPlciData->state));
            daicplci_abort_connection (pPortData, pPlciData, DAIC_CAUSE_NORMAL);
         }
      }
   }
   
} /* daicplci_release_appl */





/**
 * Handle a new incoming call.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @retval CAPI_OK              The message was handled successfully, the PLCI
 *                              state machine for the addressed PLCI is running.
 * @retval CDISC_CALL_TO_ANOTHER_APPLICATION
 *                              There is no application registered that can
 *                              handle the new call.
 * @retval Else                 Error occurred.
 */

unsigned daicplci_handle_indicate_ind
   (DaicPortData_t      *pPortData,
    DaicPlciData_t      *pPlciData,
    const DaicIndData_t *pIndData)
{
   DaicApplData_t *pApplData;
   const u_int8_t *pCedPN;
   const u_int8_t *pCngPN;
   const u_int8_t *pCedPSa;
   const u_int8_t *pCngPSa;
   const u_int8_t *pBC;
   const u_int8_t *pLLC;
   const u_int8_t *pHLC;
   const u_int8_t *pBChInfo;
   const u_int8_t *pKeypad;
   const u_int8_t *pUUData;
   const u_int8_t *pFacility;
   unsigned        uCipValue;
   int             i;
   struct mbuf    *pmbCapiMsg;
   CAPIMsg_t      *pCapiMsg;
   u_int8_t       *p;
   u_int8_t       *q;

   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: PLCI %u: State %d --> %d",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (DAIC_PLCI_STATE_P0),
        (int) DAIC_PLCI_STATE_P2);
   pPlciData->state = DAIC_PLCI_STATE_P2;
   pPlciData->uSigId = (unsigned) (pIndData->bIndId);
   
   /* extract all signalling information from the receive buffer (CPN, OAD, SIN,
    * etc.) that is relevant for a Connect-Indication CAPI message
    */
   pCedPN           = NULL;
   pCngPN           = NULL;
   pCedPSa          = NULL;
   pCngPSa          = NULL;
   pBC              = NULL;
   pLLC             = NULL;
   pHLC             = NULL;
   pBChInfo         = NULL;
   pKeypad          = NULL;
   pUUData          = NULL;
   pFacility        = NULL;
   daicplci_extract_conn_ind_info
      (pPortData, pPlciData,
       (const u_int8_t *) pIndData + sizeof (*pIndData),
       (size_t) (pIndData->wDataLength),
       &pCedPN, &pCngPN, &pCedPSa, &pCngPSa,
       &pBC, &pLLC, &pHLC, &pBChInfo, &pKeypad, &pUUData, &pFacility);
   
   /* create a CIP value out of BC and HLC, if available */
   uCipValue = daicplci_create_cip_value (pPortData, pPlciData, pBC, pHLC);
   
   /* send a Connect-Indication to all compatible registered applications */
   for (i = 0; (size_t) i < ARRAY_COUNT (pPortData->aApplData); ++i)
   {
      /* check for active application data entry */
      pApplData = &(pPortData->aApplData [i]);
      if (pApplData->uApplID == 0)
      {
         continue;
      }
      
      /* check for compatible application through CIP value and CIP mask */
      if ((pApplData->dwListenCipMask & CAPI_CIP_MASK_ANY) != 0 ||
          (pApplData->dwListenCipMask & (1 << uCipValue)) != 0)
      {
         /* application is compatible, send a Connect-Indication */
         pmbCapiMsg = kcapi_get_mbuf (sizeof (CAPIMsg_t));
         if (pmbCapiMsg == NULL)
         {
            DBG (LOG_ERROR, pPortData->iUnit,
                 "Port %u: PLCI %u: State %d: Out of mbufs for Connect-Indication",
                 pPortData->uPortIdx, pPlciData->uPlci,
                 (int) (pPlciData->state));
            /* nevertheless we proceed with the next application */
            continue;
         }
         pCapiMsg = mtod (pmbCapiMsg, CAPIMsg_t *);
         C_PUT_WORD (pCapiMsg->head.wApp, pApplData->uApplID);
         C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_CONNECT));
         C_PUT_WORD (pCapiMsg->head.wNum, pPortData->uCapiIndMsgNum);
         ++(pPortData->uCapiIndMsgNum);
         C_PUT_DWORD (pCapiMsg->head.dwCid, pPlciData->dwCid);
         C_PUT_WORD (pCapiMsg->info.connect_ind.wCip, uCipValue);
         p = (u_int8_t *) &(pCapiMsg->info.connect_ind.wCip) +
             sizeof (pCapiMsg->info.connect_ind.wCip);
         if (pCedPN != NULL && pCedPN [0] > 0)
         {
            bcopy (pCedPN, p, pCedPN [0] + 1);
            p += pCedPN [0] + 1;
         }
         else
         {
            *(p++) = 0;
         }
         if (pCngPN != NULL && pCngPN [0] > 0)
         {
            bcopy (pCngPN, p, pCngPN [0] + 1);
            p += pCngPN [0] + 1;
         }
         else
         {
            *(p++) = 0;
         }
         if (pCedPSa != NULL && pCedPSa [0] > 0)
         {
            bcopy (pCedPSa, p, pCedPSa [0] + 1);
            p += pCedPSa [0] + 1;
         }
         else
         {
            *(p++) = 0;
         }
         if (pCngPSa != NULL && pCngPSa [0] > 0)
         {
            bcopy (pCngPSa, p, pCngPSa [0] + 1);
            p += pCngPSa [0] + 1;
         }
         else
         {
            *(p++) = 0;
         }
         if (pBC != NULL && pBC [0] > 0)
         {
            bcopy (pBC, p, pBC [0] + 1);
            p += pBC [0] + 1;
         }
         else
         {
            *(p++) = 0;
         }
         if (pLLC != NULL && pLLC [0] > 0)
         {
            bcopy (pLLC, p, pLLC [0] + 1);
            p += pLLC [0] + 1;
         }
         else
         {
            *(p++) = 0;
         }
         if (pHLC != NULL && pHLC [0] > 0)
         {
            bcopy (pHLC, p, pHLC [0] + 1);
            p += pHLC [0] + 1;
         }
         else
         {
            *(p++) = 0;
         }
         if (pBChInfo != NULL || pKeypad != NULL ||
             pUUData != NULL || pFacility != NULL)
         {
            q = p + 1;
            if (pBChInfo != NULL && pBChInfo [0] > 0)
            {
               CAPIBChannelInfo_t *pTmp = (CAPIBChannelInfo_t *) q;
               
               C_PUT_BYTE (pTmp->bLength, sizeof (cWORD) + 1 + pBChInfo [0]);
               C_PUT_WORD (pTmp->wChannel, CAPI_BCHAN_MODE_CHANNEL_ID);
               bcopy (pBChInfo, &(pTmp->info), pBChInfo [0] + 1);
               q += q [0] + 1;
            }
            else
            {
               *(q++) = 0;
            }
            if (pKeypad != NULL && pKeypad [0] > 0)
            {
               bcopy (pKeypad, q, pKeypad [0] + 1);
               q += pKeypad [0] + 1;
            }
            else
            {
               *(q++) = 0;
            }
            if (pUUData != NULL && pUUData [0] > 0)
            {
               bcopy (pUUData, q, pUUData [0] + 1);
               q += pUUData [0] + 1;
            }
            else
            {
               *(q++) = 0;
            }
            if (pFacility != NULL && pFacility [0] > 0)
            {
               bcopy (pFacility, q, pFacility [0] + 1);
               q += pFacility [0] + 1;
            }
            else
            {
               *(q++) = 0;
            }
            *p = (u_int8_t) (q - p) - 1;
            p = q;
         }
         else
         {
            *(p++) = 0;
         }
         pmbCapiMsg->m_len = p - (u_int8_t *) pCapiMsg;
         C_PUT_WORD (pCapiMsg->head.wLen, pmbCapiMsg->m_len);
                     
         /* insert the current application into the bitfield of notified
          * applications
          */
         pPlciData->ulApplIdxBitField |= (1 << i);
         
         mtx_unlock (&(pPortData->pSc->mtxAccess));
         kcapi_ctlr_receive_capi_message (pPortData->uUniqueCapiCtlrNum,
                                          pApplData->uApplID,
                                          pmbCapiMsg);
         mtx_lock (&(pPortData->pSc->mtxAccess));
         
         DBG (LOG_DEBUG, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: Connect-Indication sent to application id %u",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              pApplData->uApplID);

         /* finally create all necessary info indications for the indication */
         daicplci_create_capi_info_ind
            (pPortData, pPlciData,
             DAIC_MT_SETUP,
             (const u_int8_t *) pIndData + sizeof (*pIndData),
             (size_t) (pIndData->wDataLength),
             pApplData->uApplID, pApplData->dwListenInfoMask);
      }
   }
   
   /* if no compatible application is registered: simply remove the signalling
    * id
    */
   if (pPlciData->ulApplIdxBitField == 0)
   {
      DBG (LOG_INFO, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got Indicate-Indication, but no application is registered or compatible (CIP value %u)",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           uCipValue);
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d --> %d",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (int) DAIC_PLCI_STATE_P0);
      daicplci_reset (pPortData, pPlciData);
      if (pPortData->nNumPlci > 0)
      {
         --(pPortData->nNumPlci);
      }
      return (CDISC_CALL_TO_OTHER_APPLICATION);
   }

   /* now wait for any CAPI Connect-Response */
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: All applications notified about incoming call, wait for Connect-Response",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));

   return (CAPI_OK);
} /* daicplci_handle_indicate_ind */





/**
 * Handle the result of an assign request to obtain a new signaling id.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param uPlci                 I: The PLCI value the return code shall be
 *                                 dispatched to.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */

void daicplci_handle_sig_assign_rc
   (DaicPortData_t     *pPortData,
    unsigned            uPlci,
    const DaicRcData_t *pRcData)
{
   DaicPlciData_t *pPlciData = &(pPortData->aPlciData [uPlci]);
   unsigned        uInfoValue;

   /* check if there is a request pending and if this request is really an
    * Assign-Request as expected
    */
   if ((pPlciData->ulFlags & DAIC_PLCI_FLAG_REQUEST_PENDING) == 0 ||
       pPlciData->uCurrRequest != DAIC_SIG_ASSIGN)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got return code 0x%02X, id 0x%02X without signaling Assign-Request pending",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId));
      return;
   }

   /* no request is pending anymore */
   pPlciData->ulFlags      &= ~DAIC_PLCI_FLAG_REQUEST_PENDING;
   pPlciData->uCurrRequest  = 0;

   /* if there is an Assign-Request for a signaling id pending there must be a
    * Connect-Request stored as the current CAPI message
    */
   if (pPlciData->pmbCurrCapiMsg == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got signaling Assign-Request return code 0x%02X, id 0x%02X without CAPI Connect-Request stored",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId));
      return;
   }
   
   /* translate the rc value into a CAPI result value for later evaulation and
    * for signaling it to the CAPI application
    */
   uInfoValue = daicmisc_get_capi_info_from_rc
                   (pRcData->bRc, DAIC_ID_QUALIFY_PLCI);

   /* handle a failed assign request */
   if (uInfoValue != CAPI_OK)
   {
      /* active connection establishment --> return a negative Connect-Confirm
       */
      DBG (LOG_INFO, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Unable to obtain signaling id for Connect-Request",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      daicplci_send_connect_conf (pPortData, pPlciData, uInfoValue);
      daicplci_reset (pPortData, pPlciData);
      if (pPortData->nNumPlci > 0)
      {
         --(pPortData->nNumPlci);
      }
   }
   /* handle a successful Assign-Request after getting an abort request */
   else if ((pPlciData->ulFlags & DAIC_PLCI_FLAG_GOT_ABORT_REQ) != 0)
   {
      /* active connection establishment --> return a negative Connect-Confirm
       */
      /* Note: We must perform a state transit here to let
       *       daicplci_send_connect_conf() do the right thing, i.e. send a
       *       Remove-Request. Otherwise it would "think" that no signaling id
       *       is assigned.
       */
      pPlciData->uSigId = (unsigned) (pRcData->bRcId);
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d --> %d",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (int) DAIC_PLCI_STATE_P0_2);
      pPlciData->state = DAIC_PLCI_STATE_P0_2;
      DBG (LOG_INFO, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got abort request during assign, id 0x%02X will be removed again",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           pPlciData->uSigId);
      /* Note: An Abort-Request during the signaling id allocation phase
       *       normally results from an application release. A simple
       *       Disconnect-Request is not possible, because the application
       *       only receives the PLCI within the Connect-Confirm, that is sent
       *       just now. So the confirmation info value is set to "invalid
       *       application id".
       */
      daicplci_send_connect_conf
         (pPortData, pPlciData, CME_INVALID_APPLICATION_ID);
      /* Note: Remove-Request is already sent by daicplci_send_connect_conf().
       */
      
      /* as we have a controller request pending, we must not start handling the
       * next message in the queue
       */
      return;
   }
   /* handle a successful assign request */
   else
   {
      pPlciData->uSigId = (unsigned) (pRcData->bRcId);
      
      daicplci_continue_connect_req (pPortData, pPlciData);
   }
   
   /* handle all enqueued messages until the queue is empty or a return code for
    * a hardware request is pending
    */
   daicplci_handle_queued_messages (pPortData, pPlciData);

} /* daicplci_handle_sig_assign_rc */





/**
 * Handle the result of an assign request to obtain a new network id.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param uPlci                 I: The PLCI value the return code shall be
 *                                 dispatched to.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */

void daicplci_handle_net_assign_rc
   (DaicPortData_t     *pPortData,
    unsigned            uPlci,
    const DaicRcData_t *pRcData)
{
   DaicPlciData_t *pPlciData = &(pPortData->aPlciData [uPlci]);
   unsigned        uInfoValue;

   /* check if there is a request pending and if this request is really an
    * Assign-Request as expected
    */
   if ((pPlciData->ulFlags & DAIC_PLCI_FLAG_REQUEST_PENDING) == 0 ||
       pPlciData->uCurrRequest != DAIC_SIG_ASSIGN)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got return code 0x%02X, id 0x%02X without network Assign-Request pending",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId));
      return;
   }

   /* no request is pending anymore */
   pPlciData->ulFlags      &= ~DAIC_PLCI_FLAG_REQUEST_PENDING;
   pPlciData->uCurrRequest  = 0;

   /* if we are in the call setup phase: */
   if (pPlciData->state == DAIC_PLCI_STATE_P1_1 ||
       pPlciData->state == DAIC_PLCI_STATE_P4_1)
   {
      /* No matter if the Assign-Request succeeded, we first send out a stored
       * Connect-Active-Indication to the application. If the request failed, the
       * application will soon receive a Disconnect-Indication because of an
       * aborted connection. Note that we do not go to state P-ACT, because we do
       * not have a network id. Otherwise the abort request function would try to
       * release a non-existing network id. And this will also prevent a
       * Connect-B3-Request from beeing executed (will be rejected in states other
       * than P-ACT).
       */
      if (pPlciData->pmbCurrCapiMsg != NULL)
      {
         DBG (LOG_DEBUG, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: Send Connect-Active-Indication to application %u",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              pPlciData->uApplID);

         mtx_unlock (&(pPortData->pSc->mtxAccess));
         kcapi_ctlr_receive_capi_message
            (pPortData->uUniqueCapiCtlrNum, pPlciData->uApplID,
             pPlciData->pmbCurrCapiMsg);
         mtx_lock (&(pPortData->pSc->mtxAccess));

         pPlciData->pmbCurrCapiMsg = NULL;
      }
      else
      {
         /* No Connect-Active-Indication stored??? Abort the connection, soon. */
         DBG (LOG_ERROR, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: No Connect-Active-Indication message stored, abort connection",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
         if ((pPlciData->ulFlags & DAIC_PLCI_FLAG_GOT_ABORT_REQ) == 0)
         {
            pPlciData->ulFlags |= DAIC_PLCI_FLAG_GOT_ABORT_REQ;
            pPlciData->uAbortCause = DAIC_CAUSE_DEST_OUT_OF_ORDER;
            pPlciData->uDisconnectCause = CME_OS_RESOURCE_ERROR;
         }
      }
   }
    
   /* translate the rc value into a CAPI result value for later evaulation and
    * for signaling it to the CAPI application
    */
   uInfoValue = daicmisc_get_capi_info_from_rc
                   (pRcData->bRc, DAIC_ID_QUALIFY_NCCI);

   /* handle a failed assign request */
   if (uInfoValue != CAPI_OK)
   {
      /* We have an established B-channel, but we cannot access it because we
       * cannot get a network id. In the call setup phase we will abort the
       * connection, the application will receive a Disconnect-Indication, soon.
       * When in changing the B-channel protocol, we simply send a negative
       * Select-B-Protocol-Confirm.
       */
      DBG (LOG_INFO, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Unable to obtain network id for B-channel access (return code 0x%02X, CAPI reason 0x%04X)",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRc), uInfoValue);
      if (pPlciData->state == DAIC_PLCI_STATE_PACT)
      {
         daicplci_send_sel_b_prot_conf (pPortData, pPlciData, uInfoValue);
      }
      else
      {
         pPlciData->uDisconnectCause = uInfoValue;
         daicplci_abort_connection
            (pPortData, pPlciData, DAIC_CAUSE_RESOURCE_UNAVAIL_UNSPEC);

         /* as there should be a request pending now (hopefully a Hangup-Request),
          * we cannot handle any messages in this PLCI's queue
          */
         return;
      }
   }
   /* Else the network Assign-Request was successful, the B-channel is now fully
    * active. So in the call setup phase go to state P-ACT. We must check later
    * if the flag for a pending Abort-Request is set. When executing a change of
    * the B-channel protocol, only the Select-B-Protocol-Confirm message must be
    * sent.
    */
   else
   {
      pPlciData->uNetId = (unsigned) (pRcData->bRcId);
      if (pPlciData->state == DAIC_PLCI_STATE_PACT)
      {
         DBG (LOG_TRACE, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: Network id %u allocated for B-channel access (B-protocol switch)",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              pPlciData->uNetId);
         daicplci_send_sel_b_prot_conf (pPortData, pPlciData, CAPI_OK);
      }
      else
      {
         DBG (LOG_TRACE, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: Network id %u allocated for B-channel access",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              pPlciData->uNetId);
         DBG (LOG_TRACE, pPortData->iUnit,
              "Port %u: PLCI %u: State %d --> %d",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              (int) DAIC_PLCI_STATE_PACT);
         pPlciData->state = DAIC_PLCI_STATE_PACT;
      }
   }
   
   /* check if the flag for a received abort request is set */
   if ((pPlciData->ulFlags & DAIC_PLCI_FLAG_GOT_ABORT_REQ) != 0)
   {
      daicplci_abort_connection (pPortData, pPlciData, pPlciData->uAbortCause);
      
      /* as we have a controller request pending, we must not start handling the
       * next message in the queue
       */
      return;
   }
   
   /* handle all enqueued messages until the queue is empty or a return code for
    * a hardware request is pending
    */
   daicplci_handle_queued_messages (pPortData, pPlciData);

} /* daicplci_handle_net_assign_rc */





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

void daicplci_handle_rc
   (DaicPortData_t     *pPortData,
    unsigned            uPlci,
    const DaicRcData_t *pRcData)
{
   DaicPlciData_t    *pPlciData = &(pPortData->aPlciData [uPlci]);
   DaicRcActionFct_t *pfnAction;
   unsigned           uCurrRequest;
   
   /* if the PLCI is not allocated, simply ignore the return code */
   if (pPlciData->uPlci == 0)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: Got return code 0x%02X, id 0x%02X for unallocated PLCI",
           pPortData->uPortIdx, uPlci,
           (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId));
      return;
   }
      
   /* If the abort flag is set, just reset the flag for a pending request and
    * continue with the connection abort operation. But if the pending request
    * is a remove request (both network or signaling), we must always handle the
    * return code. Note that the request codes for both Remove-Requests are
    * identical.
    */
   if ((pPlciData->ulFlags & DAIC_PLCI_FLAG_GOT_ABORT_REQ) != 0 &&
       ((pPlciData->ulFlags & DAIC_PLCI_FLAG_REQUEST_PENDING) == 0 ||
        pPlciData->uCurrRequest != DAIC_SIG_REMOVE))
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Ignoring return code 0x%02X, id 0x%02X because of pending abort request",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId));
      pPlciData->ulFlags      &= ~DAIC_PLCI_FLAG_REQUEST_PENDING;
      pPlciData->uCurrRequest  = 0;
      daicplci_abort_connection (pPortData, pPlciData, pPlciData->uAbortCause);
      return;
   }
   
   /* check if there is a request pending and memorize the request locally */
   if ((pPlciData->ulFlags & DAIC_PLCI_FLAG_REQUEST_PENDING) == 0)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got return code 0x%02X, id 0x%02X without request pending",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId));
      daicplci_abort_connection (pPortData, pPlciData, DAIC_CAUSE_TEMP_FAILURE);
      return;
   }
   uCurrRequest = pPlciData->uCurrRequest;
   if (uCurrRequest == DAIC_SIG_REMOVE)
   {
      uCurrRequest = DAIC_SIG_NUM_CMDS;
   }

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

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

} /* daicplci_handle_rc */





/**
 * Enqueue an indication from the controller for a PLCI.
 *
 * If there is currently no hardware request pending and the message queue for
 * the PLCI is empty, the indication will be handled at once.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param uPlci                 I: The PLCI value the indication 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 daicplci_enqueue_ind
   (DaicPortData_t *pPortData,
    unsigned        uPlci,
    struct mbuf    *pmbIndData)
{
   DaicPlciData_t *pPlciData = &(pPortData->aPlciData [uPlci]);
   DaicIndData_t  *pIndData  = mtod (pmbIndData, DaicIndData_t *);
   
   /* if the PLCI is not allocated, simply ignore the indication */
   if (pPlciData->uPlci == 0)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: Got indication 0x%02X, id 0x%02X for unallocated PLCI",
           pPortData->uPortIdx, uPlci,
           (unsigned) (pIndData->bInd), (unsigned) (pIndData->bIndId));
      return;
   }
      
   /* 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 (&(pPlciData->msgQueue)) > 0 ||
       (pPlciData->ulFlags & DAIC_PLCI_FLAG_REQUEST_PENDING) != 0)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Busy, enqueue indication 0x%02X, id 0x%02X",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pIndData->bInd), (unsigned) (pIndData->bIndId));
      _IF_ENQUEUE (&(pPlciData->msgQueue), pmbIndData);
      return;
   }

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





/**
 * Enqueue a CAPI message to a PLCI instance.
 *
 * If there is currently no hardware request pending and the message queue for
 * the PLCI is empty, the message will be handled at once.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param uPlci                 I: The addressed PLCI value, equal to the
 *                                 signalling 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 daicplci_enqueue_capi_msg
   (DaicPortData_t  *pPortData,
    unsigned         uPlci,
    unsigned         uApplID,
    struct mbuf     *pmbMsg)
{
   CAPIMsg_t      *pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
   unsigned        uCmd = CAPI_GET_CMD (pCapiMsg);
   int             iCmdIdx;
   u_int32_t       dwCid = CAPI_GET_CID (pCapiMsg);
   DaicPlciData_t *pPlciData;
   
   /* check for valid and allocated PLCI (note that PLCI 0 is never allocated)
    */
   if (uPlci < 1 || uPlci >= ARRAY_COUNT (pPortData->aPlciData) ||
       pPortData->aPlciData [uPlci].uPlci == 0)
   {
      /* If the message is a Disconnect-Response, the PLCI may just have gone
       * to P-0 before this message arrived. This is also true for responses to
       * Info-Indications preceeding the Disconnect-Indication. For tolerance
       * we simply ignore all responses without an error message.
       */
      if (uPlci < ARRAY_COUNT (pPortData->aPlciData) &&
          (uCmd & CAPI_CMDMASK_SUBCMD) == C_RESP)
      {
         DBG (LOG_DEBUG, pPortData->iUnit,
              "Port %u: PLCI %u: Ignore CAPI message 0x%04X because of already removed signaling id",
              pPortData->uPortIdx, uPlci, uCmd);
         kcapi_free_mbuf (pmbMsg);
         return (CAPI_OK);
      }
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: Got CAPI message 0x%04X with invalid PLCI in CID 0x%08lX",
           pPortData->uPortIdx, uCmd, (unsigned long) dwCid);
      return (CCE_ILLEGAL_IDENTIFIER);
   }
   pPlciData = &(pPortData->aPlciData [uPlci]);

   /* Here we should check if the application id specified is really associated
    * with the PLCI. This can be done by checking the application id field or
    * for a still unassigned PLCI by checking the application index bit field.
    * But unfortunately we also get valid messages from applications that are
    * already removed from the bit mask, because the PLCI is already assigned to
    * another application. This is especially true for Connect- and
    * Disconnect-Responses. So if the application id is not the same, we reject
    * only messages not of some specific kind. All others are handled and
    * checked later.
    */
   if (pPlciData->uApplID != 0 && pPlciData->uApplID != uApplID)
   {
      if (uCmd != CAPI_REQUEST (C_ALERT) &&
          uCmd != CAPI_RESPONSE (C_CONNECT) &&
          uCmd != CAPI_REQUEST (C_DISCONNECT) &&
          uCmd != CAPI_RESPONSE (C_DISCONNECT) &&
          uCmd != CAPI_RESPONSE (C_INFO_20))
      {
         DBG (LOG_INFO, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: Reject CAPI message 0x%04X because PLCI is assigned to application id %u, not %u",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              uCmd, pPlciData->uApplID, uApplID);
         return (CME_INVALID_APPLICATION_ID);
      }
   }

   /* translate the CAPI message command into an action table index to check if
    * the message command is valid for the PLCI 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: PLCI %u: State %d: CAPI message 0x%04X not valid for PLCI context",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->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 (&(pPlciData->msgQueue)) > 0 ||
       (pPlciData->ulFlags & DAIC_PLCI_FLAG_REQUEST_PENDING) != 0)
   {
      if (_IF_QFULL (&(pPlciData->msgQueue)))
      {
         DBG (LOG_DEBUG, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: Busy and message queue full, reject CAPI message 0x%04X",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              CAPI_GET_CMD (pCapiMsg));
         return (CME_PUT_QUEUE_FULL);
      }
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Busy, enqueue CAPI message 0x%04X",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           CAPI_GET_CMD (pCapiMsg));
      _IF_ENQUEUE (&(pPlciData->msgQueue), pmbMsg);
      return (CAPI_OK);
   }

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





/**
 * Count the release of an NCCI associated with a PLCI.
 *
 * @param pPortData             I/O: The port data as the operation context.
 * @param uPlci                 I: The PLCI, the NCCI is released for.
 *
 * @return Nothing.
 */

void daicplci_ncci_removed
   (DaicPortData_t *pPortData,
    unsigned        uPlci)
{
   DaicPlciData_t *pPlciData;
   
   if (uPlci >= ARRAY_COUNT (pPortData->aPlciData))
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: Invalid PLCI %u for removed NCCI",
           pPortData->uPortIdx, uPlci);
      return;
   }
   
   pPlciData = &(pPortData->aPlciData [uPlci]);
   if (pPlciData->uPlci == 0)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u not allocated for removed NCCI",
           pPortData->uPortIdx, uPlci);
      return;
   }
   
   if (pPlciData->nNumNcci == 0)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: No. NCCIs is already null, unable to remove NCCI",
           pPortData->uPortIdx, pPlciData->uPlci);
      return;
   }
   
   --(pPlciData->nNumNcci);
   
   /* If we are not in state P-6, we must only count the removed NCCI. In state
    * P-6 we are waiting for the removal of all NCCIs to proceed with a hangup.
    */
   if (pPlciData->nNumNcci == 0 && pPlciData->state == DAIC_PLCI_STATE_P6)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: No more NCCI active, remove network id 0x%02X",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           pPlciData->uNetId);

      daicplci_send_net_remove_req (pPortData, pPlciData);

      /* Note: If there is no network id allocated, the process goes on with
       *       the next step, sending Disconnect-Indication to the application.
       *       If the PLCI is not assigned to an application (any more), again
       *       the next step is executed, removing the signaling id.
       */
   }
   else
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: NCCI removed, now %zu active",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           pPlciData->nNumNcci);
   }
   
} /* daicplci_ncci_removed */





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





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

static void daicplci_handle_call_req_rc
   (DaicPortData_t     *pPortData,
    DaicPlciData_t     *pPlciData,
    const DaicRcData_t *pRcData)
{
   unsigned uInfoValue;
   
   /* if there is no CAPI request pending or it is no Connect-Request, just
    * ignore this rc
    */
   if (pPlciData->pmbCurrCapiMsg == NULL ||
       CAPI_GET_CMD (mtod (pPlciData->pmbCurrCapiMsg, CAPIMsg_t *)) !=
          CAPI_REQUEST (C_CONNECT))
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got return code 0x%02X, id 0x%02X with no Connect-Request pending",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId));
      return;
   }
   
   /* determine the CAPI result value from the return code value */
   uInfoValue = daicmisc_get_capi_info_from_rc
                   (pRcData->bRc, DAIC_ID_QUALIFY_PLCI);
   
   if (pRcData->bRc == DAIC_RC_OK)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Call-Request for id 0x%02X successful",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRcId));
   }
   else
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Call-Request for id 0x%02X failed with rc 0x%02X, CAPI info value 0x%04X",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRcId), (unsigned) (pRcData->bRc), uInfoValue);
   }
   
   /* send out the Connect-Confirm message and proceed with the follow-up task
    */
   daicplci_send_connect_conf (pPortData, pPlciData, uInfoValue);
   /* Note: Either we are now in P-1 and wait for a Call-Connected-Indication or
    *       we are in P-6.3 and a Remove-Request is now pending.
    */

} /* daicplci_handle_call_req_rc */





/**
 * Handle the return code for a controller Call-Response-Request.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */

static void daicplci_handle_call_resp_rc
   (DaicPortData_t     *pPortData,
    DaicPlciData_t     *pPlciData,
    const DaicRcData_t *pRcData)
{
   if (pRcData->bRc == DAIC_RC_OK)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Call-Response for id 0x%02X successful",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRcId));
   }
   else
   {
      pPlciData->uDisconnectCause =
         daicmisc_get_capi_info_from_rc (pRcData->bRc, DAIC_ID_QUALIFY_PLCI);
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Call-Response for id 0x%02X failed with rc 0x%02X, disconnect cause 0x%02X",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRcId), (unsigned) (pRcData->bRc),
           pPlciData->uDisconnectCause);
      daicplci_abort_connection (pPortData, pPlciData, DAIC_CAUSE_TEMP_FAILURE);
   }
   
} /* daicplci_handle_call_resp_rc */





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

static void daicplci_handle_alert_req_rc
   (DaicPortData_t     *pPortData,
    DaicPlciData_t     *pPlciData,
    const DaicRcData_t *pRcData)
{
   unsigned uInfoValue;

   /* if there is no CAPI request pending or it is no Alert-Request, just
    * ignore this rc
    */
   if (pPlciData->pmbCurrCapiMsg == NULL ||
       CAPI_GET_CMD (mtod (pPlciData->pmbCurrCapiMsg, CAPIMsg_t *)) !=
          CAPI_REQUEST (C_ALERT))
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got return code 0x%02X, id 0x%02X with no Alert-Request pending",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId));
      return;
   }
   
   /* translate the return code to a CAPI info value */
   uInfoValue = daicmisc_get_capi_info_from_rc
                   (pRcData->bRc, DAIC_ID_QUALIFY_PLCI);
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Got return code 0x%02X, id 0x%02X, CAPI info value 0x%04X for Alert-Request",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId), uInfoValue);

   daicplci_send_alert_conf (pPortData, pPlciData, uInfoValue);
   
   return;
} /* daicplci_handle_alert_req_rc */





/**
 * Handle the return code for a controller Call-, Info- or
 * User-User-Data-Request.
 *
 * A controller Call-Request in a state other than P-0 and a
 * User-User-Data-Request originates from a CAPI Info-Request with the called
 * party number or only the user-user data part filled respectively. If an
 * Info-Request contains no called party number or additional parts besides
 * user-user data it will result in a controller Info-Request.
 *
 * But in all cases the return code must result in an Info-Confirm CAPI message
 * to the application. So they can all be handled by the same return code action
 * function in the state table.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */

static void daicplci_handle_info_req_rc
   (DaicPortData_t     *pPortData,
    DaicPlciData_t     *pPlciData,
    const DaicRcData_t *pRcData)
{
   unsigned uInfoValue;

   /* if there is no CAPI request pending or it is no Info-Request, just ignore
    * this rc
    */
   if (pPlciData->pmbCurrCapiMsg == NULL ||
       CAPI_GET_CMD (mtod (pPlciData->pmbCurrCapiMsg, CAPIMsg_t *)) !=
          CAPI_REQUEST (C_INFO_20))
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got return code 0x%02X, id 0x%02X with no Info-Request pending",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId));
      return;
   }
   
   /* translate the return code to a CAPI info value */
   uInfoValue = daicmisc_get_capi_info_from_rc
                   (pRcData->bRc, DAIC_ID_QUALIFY_PLCI);
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Got return code 0x%02X, id 0x%02X, CAPI info value 0x%04X for Info-Request",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId), uInfoValue);

   /* send out an Info-Confirm with the computed info value */
   daicplci_send_info_conf (pPortData, pPlciData, uInfoValue);

} /* daicplci_handle_info_req_rc */





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

static void daicplci_handle_hangup_req_rc
   (DaicPortData_t     *pPortData,
    DaicPlciData_t     *pPlciData,
    const DaicRcData_t *pRcData)
{
   if (pRcData->bRc != DAIC_RC_OK)
   {
      unsigned uRes;
      
      /* If a hangup request cannot be executed, we can only send a negative
       * Disconnect-Confirm to the application. Error recovery must be left to
       * the application. We must not hide this situation against the
       * application. The only real recovery from this error is to re-load the
       * board firmware.
       */
      uRes = daicmisc_get_capi_info_from_rc
                (pRcData->bRc, DAIC_ID_QUALIFY_PLCI);

      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got Hangup-Request return code 0x%02X, id 0x%02X, CAPI info value 0x%04X, unable to disconnect",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId), uRes);

      daicplci_send_disc_conf (pPortData, pPlciData, uRes);
   }
   else
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got Hangup-Request return code 0x%02X, id 0x%02X wait for hangup indication",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId));

      daicplci_send_disc_conf (pPortData, pPlciData, CAPI_OK);
      
      /* now wait for the hangup indication */
   }
   
} /* daicplci_handle_hangup_req_rc */





/**
 * Handle the return code for a controller Remove-Request for the network id.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */

static void daicplci_handle_net_remove_req_rc
   (DaicPortData_t     *pPortData,
    DaicPlciData_t     *pPlciData,
    const DaicRcData_t *pRcData)
{
   if (pRcData->bRc != DAIC_RC_OK)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got Remove-Request return code 0x%02X, unable to remove network id 0x%02X",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId));

      /* If a remove request failed, we can normally only declare the network
       * id as removed. This will produce a resource hole that can only be
       * filled by reloading the board with its firmware. So in the hangup phase
       * we proceed like the request was successful. Only if we are about to
       * change the B-channel protocol we can react by sending a negative
       * Select-B-Protocol-Confirm. In this case the application is informed
       * about a controller problem.
       */
   }
   else
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got Remove-Request return code 0x%02X, id 0x%02X released",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId));
   }
   daicdisp_id_removed (pPortData, pRcData->bRcId);
   pPlciData->uNetId = 0;
   
   /* if the current state is P-ACT, we are about to change the B-channel
    * protocol --> continue by sending an Assign-Request for a new network id
    */
   if (pPlciData->state == DAIC_PLCI_STATE_PACT)
   {
      if ((pPlciData->ulFlags & DAIC_PLCI_FLAG_GOT_ABORT_REQ) != 0)
      {
         daicplci_abort_connection 
            (pPortData, pPlciData, pPlciData->uAbortCause);
      }
      else if (pRcData->bRc == DAIC_RC_OK)
      {
         daicplci_send_net_assign_req (pPortData, pPlciData);
      }
      else
      {
         daicplci_send_sel_b_prot_conf
            (pPortData, pPlciData, CME_OS_RESOURCE_ERROR);
      }
      return;
   }
   
   /* so we are in the hangup phase, so the next step is to send a
    * Disconnect-Indication to the application
    */
   daicplci_send_disc_ind (pPortData, pPlciData);
   
} /* daicplci_handle_net_remove_req_rc */





/**
 * Handle the return code for a controller Remove-Request for the signaling id.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */

static void daicplci_handle_sig_remove_req_rc
   (DaicPortData_t     *pPortData,
    DaicPlciData_t     *pPlciData,
    const DaicRcData_t *pRcData)
{
   if (pRcData->bRc != DAIC_RC_OK)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got Remove-Request return code 0x%02X, unable to remove signaling id 0x%02X",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId));

      /* If a remove request failed, we can only declare the signalind id as 
       * removed. This will produce a resource hole that can only be filled by
       * reloading the board with its firmware. So we proceed like the request
       * was successful.
       */
   }
   else
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got Remove-Request return code 0x%02X, id 0x%02X released",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId));
   }
   daicdisp_id_removed (pPortData, pRcData->bRcId);
   pPlciData->uSigId = 0;
   
   daicplci_reset (pPortData, pPlciData);
   if (pPortData->nNumPlci > 0)
   {
      pPortData->nNumPlci--;
   }
   
} /* daicplci_handle_sig_remove_req_rc */





/**
 * Handle a controller Call-Connected-Indication.
 *
 * This message signals the connected B-channel for outgoing calls. The
 * application must get a Connect-Active-Indication and maybe some
 * Info-Indications for possibly received information elements and network
 * events.
 *
 * The Connect-Active-Connection will be stored as the "current CAPI message",
 * only Info-Indications are sent to the application now. This is because we
 * must allocate a new network id for accessing the B-channel. As soon as the
 * return code for this Assign-Request is received, we will forward the
 * Connect-Active-Indication to the application.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */

static void daicplci_handle_call_conn_ind
   (DaicPortData_t      *pPortData,
    DaicPlciData_t      *pPlciData,
    const DaicIndData_t *pIndData)
{
   const u_int8_t *pCpn;
   const u_int8_t *pCpsa;
   struct mbuf    *pmbCapiMsg;
   CAPIMsg_t      *pCapiMsg;
   u_int8_t       *pLastPos;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Got Call-Connected-Indication for id 0x%02X, data buffer length %u",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (unsigned) (pIndData->bIndId), (unsigned) (pIndData->wDataLength));

   /* send Info-Indications for the info elements in the message */
   if (pIndData->wDataLength > 0)
   {
      DaicApplData_t *pApplData;
      
      pApplData = &(pPortData->aApplData [pPlciData->iApplDataIdx]);
      daicplci_create_capi_info_ind
         (pPortData, pPlciData,
          DAIC_MT_CONNECT,
          (const u_int8_t *) pIndData + sizeof (*pIndData),
          (size_t) (pIndData->wDataLength),
          pPlciData->uApplID, pApplData->dwListenInfoMask);
   }
   
   /* extract the connected party address information from the message */
   pCpn = NULL;
   pCpsa = NULL;
   daicplci_extract_call_conn_info
      (pPortData, pPlciData,
       (const u_int8_t *) pIndData + sizeof (*pIndData),
       (size_t) (pIndData->wDataLength),
       &pCpn, &pCpsa);
   
   /* create a Connect-Active-Indication for the CAPI application */
   pmbCapiMsg = kcapi_get_mbuf (sizeof (pCapiMsg->head) +
                                1 + ((pCpn != NULL) ? pCpn [0] : 0) +
                                1 + ((pCpsa != NULL) ? pCpsa [0] : 0) +
                                1);
   if (pmbCapiMsg == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for Connect-Active-Indication, abort connection",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      daicplci_abort_connection (pPortData, pPlciData,
                                 DAIC_CAUSE_RESOURCE_UNAVAIL_UNSPEC);
      return;
   }
   pCapiMsg = mtod (pmbCapiMsg, CAPIMsg_t *);
   C_PUT_WORD (pCapiMsg->head.wApp, pPlciData->uApplID);
   C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_CONNECT_ACTIVE));
   C_PUT_WORD (pCapiMsg->head.wNum, pPortData->uCapiIndMsgNum);
   ++pPortData->uCapiIndMsgNum;
   C_PUT_DWORD (pCapiMsg->head.dwCid, pPlciData->dwCid);
   pLastPos = (u_int8_t *) pCapiMsg + sizeof (pCapiMsg->head);
   if (pCpn != NULL)
   {
      bcopy (pCpn, pLastPos, *pCpn + 1);
      pLastPos += *pCpn + 1;
   }
   else
   {
      *(pLastPos++) = 0;
   }
   if (pCpsa != NULL)
   {
      bcopy (pCpsa, pLastPos, *pCpsa + 1);
      pLastPos += *pCpsa + 1;
   }
   else
   {
      *(pLastPos++) = 0;
   }
   *(pLastPos++) = 0;
   pmbCapiMsg->m_len = (u_int16_t) (pLastPos - (u_int8_t *) pCapiMsg);
   C_PUT_WORD (pCapiMsg->head.wLen, pmbCapiMsg->m_len);
   
   /* store the CAPI message for later forwarding to the application */
   pPlciData->pmbCurrCapiMsg = pmbCapiMsg;
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Connect-Active-Indication created and stored for later forwarding",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state))

   /* start allocating a new network id for B-channel access */
   daicplci_send_net_assign_req (pPortData, pPlciData);
   
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: PLCI %u: State %d --> %d",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (int) DAIC_PLCI_STATE_P1_1);
   pPlciData->state = DAIC_PLCI_STATE_P1_1;
   
} /* daicplci_handle_call_conn_ind */





/**
 * Handle a controller Call-Indication.
 *
 * This message is sent by the controller to signal an established raw B-channel
 * for an incoming call. The CAPI application must get a
 * Connect-Active-Indication and maybe some Info-Indications for possibly
 * received information elements and network events.
 *
 * The Connect-Active-Connection will be stored as the "current CAPI message",
 * only Info-Indications are sent to the application now. This is because we
 * must allocate a new network id for accessing the B-channel. As soon as the
 * return code for this Assign-Request is received, we will forward the
 * Connect-Active-Indication to the application.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */

static void daicplci_handle_call_ind
   (DaicPortData_t      *pPortData,
    DaicPlciData_t      *pPlciData,
    const DaicIndData_t *pIndData)
{
   const u_int8_t *pCpn;
   const u_int8_t *pCpsa;
   struct mbuf    *pmbCapiMsg;
   CAPIMsg_t      *pCapiMsg;
   u_int8_t       *pLastPos;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Got Call-Indication for id 0x%02X, data buffer length %u",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (unsigned) (pIndData->bIndId), (unsigned) (pIndData->wDataLength));
   
   /* send Info-Indications for the info elements in the message */
   if (pIndData->wDataLength > 0)
   {
      DaicApplData_t *pApplData;
      
      pApplData = &(pPortData->aApplData [pPlciData->iApplDataIdx]);
      daicplci_create_capi_info_ind
         (pPortData, pPlciData,
          0,
          (const u_int8_t *) pIndData + sizeof (*pIndData),
          (size_t) (pIndData->wDataLength),
          pPlciData->uApplID, pApplData->dwListenInfoMask);
   }
   
   /* extract the connected party address information from the message (is the
    * same as call connected indication)
    */
   pCpn = NULL;
   pCpsa = NULL;
   daicplci_extract_call_conn_info
      (pPortData, pPlciData,
       (const u_int8_t *) pIndData + sizeof (*pIndData),
       (size_t) (pIndData->wDataLength),
       &pCpn, &pCpsa);
   
   /* create a Connect-Active-Indication for the CAPI application */
   pmbCapiMsg = kcapi_get_mbuf (sizeof (pCapiMsg->head) +
                                1 + ((pCpn != NULL) ? pCpn [0] : 0) +
                                1 + ((pCpsa != NULL) ? pCpsa [0] : 0) +
                                1);
   if (pmbCapiMsg == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for Connect-Active-Indication, abort connection",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      daicplci_abort_connection (pPortData, pPlciData,
                                 DAIC_CAUSE_RESOURCE_UNAVAIL_UNSPEC);
      return;
   }
   pCapiMsg = mtod (pmbCapiMsg, CAPIMsg_t *);
   C_PUT_WORD (pCapiMsg->head.wApp, pPlciData->uApplID);
   C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_CONNECT_ACTIVE));
   C_PUT_WORD (pCapiMsg->head.wNum, pPortData->uCapiIndMsgNum);
   ++pPortData->uCapiIndMsgNum;
   C_PUT_DWORD (pCapiMsg->head.dwCid, pPlciData->dwCid);
   pLastPos = (u_int8_t *) pCapiMsg + sizeof (pCapiMsg->head);
   if (pCpn != NULL)
   {
      bcopy (pCpn, pLastPos, *pCpn + 1);
      pLastPos += *pCpn + 1;
   }
   else
   {
      *(pLastPos++) = 0;
   }
   if (pCpsa != NULL)
   {
      bcopy (pCpsa, pLastPos, *pCpsa + 1);
      pLastPos += *pCpsa + 1;
   }
   else
   {
      *(pLastPos++) = 0;
   }
   *(pLastPos++) = 0;
   pmbCapiMsg->m_len = (u_int16_t) (pLastPos - (u_int8_t *) pCapiMsg);
   C_PUT_WORD (pCapiMsg->head.wLen, pmbCapiMsg->m_len);
   
   /* store the CAPI message for later forwarding to the application */
   pPlciData->pmbCurrCapiMsg = pmbCapiMsg;
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Connect-Active-Indication created and stored for later forwarding",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state))

   /* start allocating a new network id for B-channel access */
   daicplci_send_net_assign_req (pPortData, pPlciData);
   
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: PLCI %u: State %d --> %d",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (int) DAIC_PLCI_STATE_P4_1);
   pPlciData->state = DAIC_PLCI_STATE_P4_1;
   
} /* daicplci_handle_call_ind */





/**
 * Handle a controller Alert-Indication.
 *
 * An Alert-Indication from the controller signals the alerting state of the
 * called party for an outgoing call. The only task necessary for handling this
 * message is to send some Info-Indication to the CAPI application.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */

static void daicplci_handle_alert_ind
   (DaicPortData_t      *pPortData,
    DaicPlciData_t      *pPlciData,
    const DaicIndData_t *pIndData)
{
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Got Alerting-Indication from id 0x%02X, data buffer length %u",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (unsigned) (pIndData->bIndId), (unsigned) (pIndData->wDataLength));

   /* an Alerting-Indication only results in one or more Info-Indications if the
    * application has requested this information.
    */
   if (pIndData->wDataLength > 0)
   {
      DaicApplData_t *pApplData;
      
      pApplData = &(pPortData->aApplData [pPlciData->iApplDataIdx]);
      daicplci_create_capi_info_ind
         (pPortData, pPlciData,
          DAIC_MT_ALERTING,
          (const u_int8_t *) pIndData + sizeof (*pIndData),
          (size_t) (pIndData->wDataLength),
          pPlciData->uApplID, pApplData->dwListenInfoMask);
   }
   
} /* daicplci_handle_alert_ind */





/**
 * Handle a user-user data indication.
 *
 * This controller message reports received user-user data through the
 * D-channel. If the application has registered interest in such information, we
 * must send some Info-Indication to it.
 *
 * The situation is a little more complicated for incoming calls, when no
 * application has sent a Connect-Response yet. In this case we must send
 * Info-Indications to all applications that received the Connect-Indication for
 * the incoming call.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */

static void daicplci_handle_uudata_ind
   (DaicPortData_t      *pPortData,
    DaicPlciData_t      *pPlciData,
    const DaicIndData_t *pIndData)
{
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Got User-User-Data-Indication for id 0x%02X, data buffer length %u",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (unsigned) (pIndData->bIndId), (unsigned) (pIndData->wDataLength));

   /* a user-user data indication only results in one or more Info-Indications
    * if the application has requested this information.
    */
   if (pIndData->wDataLength > 0)
   {
      DaicApplData_t *pApplData;
      int             i;
      
      /* if the call is exclusively assigned to one application, only this
       * application will receive Info-Indications for the user-user data
       */
      if (pPlciData->uApplID != 0)
      {
         pApplData = &(pPortData->aApplData [pPlciData->iApplDataIdx]);
         daicplci_create_capi_info_ind
            (pPortData, pPlciData,
             0,
             (const u_int8_t *) pIndData + sizeof (*pIndData),
             (size_t) (pIndData->wDataLength),
             pPlciData->uApplID, pApplData->dwListenInfoMask);
      }
      /* if the call is not yet assigned to a specific application (i.e. none
       * has sent a Connect-Response) for an incoming call, all applications
       * that received the Connect-Indication must also receive Info-Indications
       * for the user-user data
       */
      else if (pPlciData->ulApplIdxBitField != 0)
      {
         for (i = 0; i < ARRAY_COUNT (pPortData->aApplData); ++i)
         {
            if ((pPlciData->ulApplIdxBitField & (1 << i)) == 0)
            {
               continue;
            }
            pApplData = &(pPortData->aApplData [i]);
            if (pApplData->uApplID == 0)
            {
               continue;
            }
            daicplci_create_capi_info_ind
               (pPortData, pPlciData,
                0,
                (const u_int8_t *) pIndData + sizeof (*pIndData),
                (size_t) (pIndData->wDataLength),
                pApplData->uApplID, pApplData->dwListenInfoMask);
         }
      }
   }
   
} /* daicplci_handle_uudata_ind */





/**
 * Handle a controller Info-Indication.
 *
 * This controller message signals the receipt some general D-channel message
 * and/or information elements. The application will then receive some
 * Info-Indication for this data.
 *
 * The situation is a little more complicated for incoming calls, when no
 * application has sent a Connect-Response yet. In this case we must sent
 * Info-Indications to all applications that received the Connect-Indication for
 * the incoming call.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */

static void daicplci_handle_info_ind
   (DaicPortData_t      *pPortData,
    DaicPlciData_t      *pPlciData,
    const DaicIndData_t *pIndData)
{
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Got Info-Indication for id 0x%02X, data buffer length %u",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (unsigned) (pIndData->bIndId), (unsigned) (pIndData->wDataLength));

   /* an info indication only results in one or more Info-Indications if the
    * application has requested this information.
    */
   if (pIndData->wDataLength > 0)
   {
      DaicApplData_t *pApplData;
      int             i;
      
      /* if the call is exclusively assigned to one application, only this
       * application will receive Info-Indications
       */
      if (pPlciData->uApplID != 0)
      {
         pApplData = &(pPortData->aApplData [pPlciData->iApplDataIdx]);
         daicplci_create_capi_info_ind
            (pPortData, pPlciData,
             0,
             (const u_int8_t *) pIndData + sizeof (*pIndData),
             (size_t) (pIndData->wDataLength),
             pPlciData->uApplID, pApplData->dwListenInfoMask);
      }
      /* if the call is not yet assigned to a specific application (i.e. none
       * has sent a Connect-Response) for an incoming call, all applications
       * that received the Connect-Indication must also receive Info-Indications
       */
      else if (pPlciData->ulApplIdxBitField != 0)
      {
         for (i = 0; i < ARRAY_COUNT (pPortData->aApplData); ++i)
         {
            if ((pPlciData->ulApplIdxBitField & (1 << i)) == 0)
            {
               continue;
            }
            pApplData = &(pPortData->aApplData [i]);
            if (pApplData->uApplID == 0)
            {
               continue;
            }
            daicplci_create_capi_info_ind
               (pPortData, pPlciData,
                0,
                (const u_int8_t *) pIndData + sizeof (*pIndData),
                (size_t) (pIndData->wDataLength),
                pApplData->uApplID, pApplData->dwListenInfoMask);
         }
      }
   }
   
} /* daicplci_handle_info_ind */





/**
 * Handle a controller Hangup-Indication.
 *
 * This message signals the release of the B-channel, i.e. the call is
 * completely disconnected. As a reaction we must release all still existing
 * NCCIs for the corresponding PLCI and remove the network id if still
 * allocated. When all NCCIs and the network id are removed, the CAPI
 * application must receive a Disconnect-Indication. On arrival of the
 * corresponding Disconnect-Response the signaling id will also be removed.
 *
 * If there are still NCCIs in this state, we must wait until all of them are
 * released. Only then the application may get the Disconnect-Indication. The
 * PLCI state transit is done in the functions called for the corresponding
 * case.
 *
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */

static void daicplci_handle_hangup_ind
   (DaicPortData_t      *pPortData,
    DaicPlciData_t      *pPlciData,
    const DaicIndData_t *pIndData)
{
   u_int8_t bCauseVal;
   
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Got Hangup-Indication for id 0x%02X, data buffer length %u, wait for removal of all NCCIs (%zu)",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (unsigned) (pIndData->bIndId), (unsigned) (pIndData->wDataLength),
        pPlciData->nNumNcci);
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: PLCI %u: State %d --> %d",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (int) DAIC_PLCI_STATE_P6);
   pPlciData->state = DAIC_PLCI_STATE_P6;
   
   /* extract the cause value from the message */
   bCauseVal = 0;
   daicplci_extract_cause (pPortData, pPlciData,
                           (const u_int8_t *) pIndData + sizeof (*pIndData),
                           (size_t) (pIndData->wDataLength),
                           &bCauseVal);
   pPlciData->uDisconnectCause = CDISC_NETWORK_CAUSE_MASK + bCauseVal;
   
   /* send Info-Indications for all information elements received with the
    * Hangup-Indication if the application has registered interest
    */
   if (pIndData->wDataLength > 0)
   {
      DaicApplData_t *pApplData;
      int             i;
      
      /* if the call is exclusively assigned to one application, only this
       * application will receive Info-Indications
       */
      if (pPlciData->uApplID != 0)
      {
         pApplData = &(pPortData->aApplData [pPlciData->iApplDataIdx]);
         daicplci_create_capi_info_ind
            (pPortData, pPlciData,
             DAIC_MT_DISCONNECT,
             (const u_int8_t *) pIndData + sizeof (*pIndData),
             (size_t) (pIndData->wDataLength),
             pPlciData->uApplID, pApplData->dwListenInfoMask);
      }
      /* if the call is not yet assigned to a specific application (i.e. none
       * has sent a Connect-Response) for an incoming call, all applications
       * that received the Connect-Indication must also receive Info-Indications
       */
      else if (pPlciData->ulApplIdxBitField != 0)
      {
         for (i = 0; i < ARRAY_COUNT (pPortData->aApplData); ++i)
         {
            if ((pPlciData->ulApplIdxBitField & (1 << i)) == 0)
            {
               continue;
            }
            pApplData = &(pPortData->aApplData [i]);
            if (pApplData->uApplID == 0)
            {
               continue;
            }
            daicplci_create_capi_info_ind
               (pPortData, pPlciData,
                DAIC_MT_DISCONNECT,
                (const u_int8_t *) pIndData + sizeof (*pIndData),
                (size_t) (pIndData->wDataLength),
                pApplData->uApplID, pApplData->dwListenInfoMask);
         }
      }
   }
   
   /* if there are still NCCI active for this PLCI, we must trigger the
    * removal of all NCCIs and wait until all of them are released
    */
   if (pPlciData->nNumNcci > 0)
   {
      daicncci_remove_all_ncci_for_plci
         (pPortData, pPlciData->uPlci,
          (pPlciData->bprot.wB2Prot == CAPI_B2_TRANSPARENT)
             ? CAPI_OK : CDISC_B3_PROTOCOL_LAYER_1);
   }
   /* else we must now remove the network id if it is allocated; note that the
    * called function will automatically proceed with sending the
    * Disconnect-Indication to the application, if no network id is allocated
    */
   else
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: No NCCI active, remove network id 0x%02X",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           pPlciData->uNetId);

      daicplci_send_net_remove_req (pPortData, pPlciData);
   }
   
} /* daicplci_handle_hangup_ind */





/**
 * Ignore any controller indication message.
 *
 * This function is called when general controller indications do not matter any
 * more. This is the case e.g. for a controller congestion indication (currently
 * not handled within this driver) or for a call indication when the application
 * has already sent a Disconnect-Request (note that message queueing may result
 * in some parallelity). Only the message arrival is logged, no further action
 * is executed.
 * 
 * @param pPortData             I/O: The port data for the operation.
 * @param pPlciData             I/O: The PLCI data for the operation context.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */

static void daicplci_ignore_ind
   (DaicPortData_t      *pPortData,
    DaicPlciData_t      *pPlciData,
    const DaicIndData_t *pIndData)
{
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Ignoring indication 0x%02X for id 0x%02X, data buffer length %u",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (unsigned) (pIndData->bInd), (unsigned) (pIndData->bIndId),
        (unsigned) (pIndData->wDataLength));

} /* daicplci_ignore_ind */





/**
 * Handle a Connect-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, thecaller 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 daicplci_handle_connect_req
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    struct mbuf    *pmbMsg)
{
   DaicApplData_t *pApplData;
   CAPIMsg_t      *pCapiReq;
   struct mbuf    *pmbReq;
   DaicReqData_t  *pReqData;
   u_int8_t        bSin;
   u_int8_t        bSAdd;
   const u_int8_t *pBChInfo;
   u_int8_t       *p;
   int             i;
   unsigned        uRes;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Got Connect-Request",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));

   /* find the application data entry */
   pCapiReq = mtod (pmbMsg, CAPIMsg_t *);
   pPlciData->uApplID = CAPI_GET_APPL (pCapiReq);
   for (i = 0; (size_t) i < ARRAY_COUNT (pPortData->aApplData); ++i)
   {
      if (pPortData->aApplData [i].uApplID == pPlciData->uApplID)
      {
         break;
      }
   }
   if (i >= ARRAY_COUNT (pPortData->aApplData))
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: Got Connect-Request for unregistered application id %u, must have released already",
           pPortData->uPortIdx, pPlciData->uPlci, pPlciData->uApplID);
      daicplci_reset (pPortData, pPlciData);
      if (pPortData->nNumPlci > 0)
      {
         --(pPortData->nNumPlci);
      }
      return (CME_INVALID_APPLICATION_ID);
   }
   pApplData = &(pPortData->aApplData [i]);
   pPlciData->iApplDataIdx = i;

   /* fill the PLCI with some necessary data */
   pPlciData->state = DAIC_PLCI_STATE_P0;
   pPlciData->pmbCurrCapiMsg = pmbMsg;
   pPlciData->uCurrCapiReqMsgNum = CAPI_GET_MSGNUM (pCapiReq);

   /* parse the Connect-Request message for Assign-Request parameters (B-channel
    * protocol data, B-channel specification)
    */
   bzero (&(pPlciData->bprot), sizeof (pPlciData->bprot));
   pBChInfo = NULL;
   /* Note: This call will also check all parameters for valid maximum length
    *       and translate any CIP value into BC and HLC, if they are not
    *       explicitly specified in the message.
    */
   uRes = daicplci_extract_conn_req_params
             (pPortData, pPlciData, pCapiReq,
              NULL, NULL, NULL, NULL, &(pPlciData->bprot),
              NULL, NULL, NULL, &pBChInfo, NULL, NULL, NULL, NULL);
   if (uRes != CAPI_OK)
   {
      daicplci_send_connect_conf (pPortData, pPlciData, uRes);
      daicplci_reset (pPortData, pPlciData);
      if (pPortData->nNumPlci > 0)
      {
         --(pPortData->nNumPlci);
      }
      return (CAPI_OK);
   }

   /* send out an Assign-Request */
   pmbReq = kcapi_get_mbuf (sizeof (DaicReqData_t) + DAIC_SHMEM_LEN_RXBUFFER);
   if (pmbReq == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for Assign-Request",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      daicplci_send_connect_conf (pPortData, pPlciData, CME_OS_RESOURCE_ERROR);
      daicplci_reset (pPortData, pPlciData);
      if (pPortData->nNumPlci > 0)
      {
         --(pPortData->nNumPlci);
      }
      return (CAPI_OK);
   }
   pReqData = mtod (pmbReq, DaicReqData_t *);
   pReqData->bReq                    = DAIC_SIG_ASSIGN;
   pReqData->bReqId                  = DAIC_SIG_GLOBAL_ID;
   pReqData->bReqCh                  = 0;
   pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_PLCI;
   pReqData->wAssignReqPlci          = pPlciData->uPlci;
   i = 0;
   p = (u_int8_t *) pReqData + sizeof (*pReqData);
   p [i++] = DAIC_IE_CAI;
   p [i++] = 1;
#if 1
   p [i++] = 0x80;
#else /* 1 */
   if (pPlciData->bprot.wB1Prot == CAPI_B1_TRANSPARENT_64)
   {
      p [i++] = 0x89;
   }
   else
   {
      p [i++] = 0x85;
   }
#endif /* 1 */
   daicplci_get_sin_sadd_from_cip
      ((unsigned) C_GET_WORD (pCapiReq->info.connect_req.wCip),
       &bSin, &bSAdd);
   p [i++] = DAIC_IE_KEY;
   p [i++] = 3;
   p [i++] = 'C';
   p [i++] = '4';
   p [i++] = 'B';
   p [i++] = DAIC_IE_SHIFT | 0x08 | 0x06;
   p [i++] = DAIC_IE_SIN;
   p [i++] = 2;
   p [i++] = bSin;
   p [i++] = bSAdd;
   if (pBChInfo != NULL && pBChInfo [0] > sizeof (cWORD) + 1)
   {
      const CAPIBChannelInfo_t *pTmp = (const CAPIBChannelInfo_t *) pBChInfo;
      
      if (C_GET_WORD (pTmp->wChannel) == CAPI_BCHAN_MODE_CHANNEL_ID)
      {
         p [i++] = DAIC_IE_CHI;
         bcopy (&(pTmp->info), &(p [i]),
                *((const u_int8_t *) &(pTmp->info)) + 1);
         i += *((const u_int8_t *) &(pTmp->info)) + 1;
      }
   }
#if 0
   else
   {
      p [i++] = DAIC_IE_CHI;
      p [i++] = 1;
      p [i++] = 0x83;
   }
#endif /* 0 */
   p [i++] = 0;
   pReqData->wDataLength = (u_int16_t) i;
   pmbReq->m_len = sizeof (*pReqData) + pReqData->wDataLength;
   pPlciData->uCurrRequest = (unsigned) (pReqData->bReq);
   pPlciData->ulFlags |= DAIC_PLCI_FLAG_REQUEST_PENDING;
   _IF_ENQUEUE (&(pPortData->reqQueue), pmbReq);

   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: PLCI %u: State %d --> %d",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (int) DAIC_PLCI_STATE_P0_1);
   pPlciData->state = DAIC_PLCI_STATE_P0_1;

   return (CAPI_OK);
} /* daicplci_handle_connect_req */





/**
 * Handle a CAPI Connect-Response.
 *
 * This function is called when one or more CAPI applications have received a
 * Connect-Indication but none of the has responded yet. So the now responding
 * application is either the first to accept the call or it is one to reject /
 * ignore it.
 *
 * If the call shall be accepted, the PLCI will be exclusively assigned to the
 * responding application. All other applications that earlier received the
 * Connect-Indication will now receive a Disconnect-Indication. The next
 * Connect-Response message for another application will simply be ignored by
 * the central CAPI message handling function (see daicplci_handle_capi_msg()).
 * As these applications receive a Disconnect-Indication, everything is fine.
 *
 * If the call shall be rejected or ignored, the responding application must
 * first be removed from the bit mask of applications that received the
 * Connect-Indication. Then it will be sent a Disconnect-Indication.
 *
 * If the negatively responding application was the last application of the bit
 * mask, the call will be rejected against the controller to release the
 * incoming call. The following controller messages will be handled without
 * an assigned application id.
 *
 * @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, thecaller 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 daicplci_handle_conn_rsp
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    struct mbuf    *pmbMsg)
{
   const CAPIMsg_t *pCapiMsg;
   unsigned         uApplID;
   unsigned         uRejectVal;
   int              i;
   const u_int8_t  *pConnN;
   const u_int8_t  *pConnSa;
   const u_int8_t  *pLLC;
   const u_int8_t  *pBChInfo;
   const u_int8_t  *pKeypad;
   const u_int8_t  *pUUData;
   const u_int8_t  *pFacility;
   u_int8_t        *p;
   struct mbuf     *pmbReq;
   DaicReqData_t   *pReqData;
   unsigned         uRes;
   
   /* extract some necessary data from the CAPI message */
   pCapiMsg = mtod (pmbMsg, const CAPIMsg_t *);
   uApplID = CAPI_GET_APPL (pCapiMsg);
   uRejectVal = (unsigned) C_GET_WORD (pCapiMsg->info.connect_resp.wReject);

   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Got Connect-Response, appl.id %u, reject value %u",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        uApplID, uRejectVal);

   /* if the application is not registered or in fact not associated with this
    * PLCI, reject the message
    */
   for (i = 0; i < ARRAY_COUNT (pPortData->aApplData); ++i)
   {
      if (pPortData->aApplData [i].uApplID != 0 &&
          pPortData->aApplData [i].uApplID == uApplID)
      {
         break;
      }
   }
   if (i >= ARRAY_COUNT (pPortData->aApplData))
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got Connect-Response from unregistered appl.id %u",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           uApplID);
      return (CME_INVALID_APPLICATION_ID);
   }
   if (((1 << i) & pPlciData->ulApplIdxBitField) == 0)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got Connect-Response from appl.id %u that did not receive the Connect-Indication",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           uApplID);
      return (CME_INVALID_APPLICATION_ID);
   }

   /* if the connection shall not be accepted, remove the current application
    * from this PLCI, etc.
    */
   if (uRejectVal != CAPI_CALL_ACCEPT)
   {
      /* remove the application from the PLCI */
      pPlciData->ulApplIdxBitField &= ~(1 << i);
      
      /* the Connect-Response CAPI message mbuf is not needed any more, from
       * this point on we will only return CAPI_OK
       */
      kcapi_free_mbuf (pmbMsg);
      
      /* send a Disconnect-Indication to the application */
      daicplci_send_disc_ind_to_appl
         (pPortData, pPlciData, uApplID, CAPI_OK);
      
      /* if this was the last application in the application mask, reject the
       * new connection and start removing the PLCI
       */
      if (pPlciData->ulApplIdxBitField == 0)
      {
         daicplci_abort_connection
            (pPortData, pPlciData,
             daicmisc_get_cause_from_capi_reject (uRejectVal));
      }
      
      return (CAPI_OK);
   }
   
   /* the connection shall be accepted --> assign the responding application to
    * this PLCI
    */
   pPlciData->uApplID      = uApplID;
   pPlciData->iApplDataIdx = i;
   pPlciData->ulApplIdxBitField &= ~(1 << i);
   
   /* remove all other applications from this PLCI and send them a
    * Disconnect-Indication
    */
   for (i = 0; i < ARRAY_COUNT (pPortData->aApplData); ++i)
   {
      if (pPortData->aApplData [i].uApplID == 0)
      {
         continue;
      }
      if ((pPlciData->ulApplIdxBitField & (1 << i)) == 0)
      {
         continue;
      }
      daicplci_send_disc_ind_to_appl
         (pPortData, pPlciData, pPortData->aApplData [i].uApplID,
          CDISC_CALL_TO_OTHER_APPLICATION);
   }
   pPlciData->ulApplIdxBitField = 0;

   /* parse the Connect-Response message for call parameters (CPN, B-channel
    * protocol data, etc.)
    */
   pConnN    = NULL;
   pConnSa   = NULL;
   pLLC      = NULL;
   pBChInfo  = NULL;
   pKeypad   = NULL;
   pUUData   = NULL;
   pFacility = NULL;
   /* Note: This call will also check all parameters for valid maximum length.
    */
   uRes = daicplci_extract_conn_rsp_params
             (pPortData, pPlciData, pCapiMsg,
              &pConnN, &pConnSa, &(pPlciData->bprot),
              &pLLC, &pBChInfo, &pKeypad, &pUUData, &pFacility);
   if (uRes != CAPI_OK)
   {
      pPlciData->uDisconnectCause = uRes;
      daicplci_abort_connection (pPortData, pPlciData,
                                 DAIC_CAUSE_DEST_OUT_OF_ORDER);
      return (uRes);
   }

   /* fill a controller message for a call response */
   pmbReq = kcapi_get_mbuf (sizeof (DaicReqData_t) + DAIC_SHMEM_LEN_RXBUFFER);
   if (pmbReq == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for Call-Response",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      pPlciData->uDisconnectCause = CME_OS_RESOURCE_ERROR;
      daicplci_abort_connection (pPortData, pPlciData,
                                 DAIC_CAUSE_RESOURCE_UNAVAIL_UNSPEC);
      return (CME_OS_RESOURCE_ERROR);
   }
   pReqData = mtod (pmbReq, DaicReqData_t *);
   pReqData->bReq                    = DAIC_SIG_CALL_RES;
   pReqData->bReqId                  = pPlciData->uSigId;
   pReqData->bReqCh                  = 0;
   pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_PLCI;
   pReqData->wAssignReqPlci          = pPlciData->uPlci;
   i = 0;
   p = (u_int8_t *) pReqData + sizeof (*pReqData);
   p [i++] = DAIC_IE_CAI;
   if (pPlciData->bprot.wB1Prot == CAPI_B1_TRANSPARENT_64)
   {
      p [i++] = 6;
      p [i++] = 0x89;
      p [i++] = 0;
      p [i++] = 0;
      p [i++] = 0;
      p [i++] = 32;
      p [i++] = 0;
   }
   else
   {
      p [i++] = 1;
      p [i++] = 0x85;
   }
   if (pBChInfo != NULL && pBChInfo [0] > sizeof (cWORD) + 1)
   {
      const CAPIBChannelInfo_t *pTmp = (const CAPIBChannelInfo_t *) pBChInfo;
      
      if (C_GET_WORD (pTmp->wChannel) == CAPI_BCHAN_MODE_CHANNEL_ID)
      {
         p [i++] = DAIC_IE_CHI;
         bcopy (&(pTmp->info), &(p [i]),
                *((const u_int8_t *) &(pTmp->info)) + 1);
         i += *((const u_int8_t *) &(pTmp->info)) + 1;
      }
   }
   if (pFacility != NULL && pFacility [0] > 0)
   {
      p [i++] = DAIC_IE_FAC;
      bcopy (pFacility, &(p [i]), pFacility [0] + 1);
      i += pFacility [0] + 1;
   }
   if (pKeypad != NULL && pKeypad [0] > 0)
   {
      p [i++] = DAIC_IE_KEY;
      bcopy (pKeypad, &(p [i]), pKeypad [0] + 1);
      i += pKeypad [0] + 1;
   }
   if (pConnN != NULL && pConnN [0] > 0)
   {
      p [i++] = DAIC_IE_CNO;
      bcopy (pConnN, &(p [i]), pConnN [0] + 1);
      i += pConnN [0] + 1;
   }
   if (pConnSa != NULL && pConnSa [0] > 0)
   {
      p [i++] = DAIC_IE_CSA;
      bcopy (pConnSa, &(p [i]), pConnSa [0] + 1);
      i += pConnSa [0] + 1;
   }
   if (pLLC != NULL && pLLC [0] > 0)
   {
      p [i++] = DAIC_IE_LLC;
      bcopy (pLLC, &(p [i]), pLLC [0] + 1);
      i += pLLC [0] + 1;
   }
   if (pUUData != NULL && pUUData [0] > 0)
   {
      p [i++] = DAIC_IE_UUI;
      bcopy (pUUData, &(p [i]), pUUData [0] + 1);
      i += pUUData [0] + 1;
   }
   p [i++] = 0;
   pReqData->wDataLength = i;
   pmbReq->m_len = sizeof (*pReqData) + pReqData->wDataLength;

   /* now the CAPI message from the function argument is not needed any more; as
    * from this point on we only return CAPI_OK we must release its mbuf
    */
   kcapi_free_mbuf (pmbMsg);

   /* send out a call response request */
   pPlciData->uCurrRequest = (unsigned) (pReqData->bReq);
   pPlciData->ulFlags |= DAIC_PLCI_FLAG_REQUEST_PENDING;
   _IF_ENQUEUE (&(pPortData->reqQueue), pmbReq);
   
   /* now perform the state transit to P-4, i.e. Connect-Response sent out */
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: PLCI %u: State %d --> %d",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (int) DAIC_PLCI_STATE_P4);
   pPlciData->state = DAIC_PLCI_STATE_P4;
   
   return (CAPI_OK);
} /* daicplci_handle_conn_rsp */





/**
 * Handle a CAPI Info-Request.
 *
 * This CAPI message can be used for several purposes. The first is for overlap
 * sending, i.e. for dialing a number in several parts or even digit for digit.
 * The Connect-Request in this case will not contain any digits to dial, i.e.
 * the called party number is empty. All digits are dialed through Info-Requests
 * with the called party number of this message set to the (next) string of
 * digits to dial. The additional info part of the Info-Request will then
 * normally be empty, the sending complete part may be filled to signal
 * completeness of the dialed number. The result is to send out a controller
 * Call-Request message. When the return code arrives, the application will be
 * sent an Info-Confirm message.
 *
 * The second purpose is to send out only user-user data. The controller
 * supports a specific User-User-Data-Request that must be sent out in this
 * case. This will be done if only the user-user data part of the Info-Request
 * is filled. The return code of this controller message must also result in an
 * Info-Confirm message sent to the application.
 *
 * The last purpose is to send out some information using the additional info
 * part of the message, where the user-user data part is not or not only filled.
 * The called party number part will then be empty. The result is to send out a
 * controller Info-Request with the content of the additional info part put into
 * this message.
 *
 * @note The controller or the network may not support all combinations of
 *       information elements that may be specified within an Info-Request CAPI
 *       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 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 daicplci_handle_info_req
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    struct mbuf    *pmbMsg)
{
   CAPIMsg_t      *pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
   const u_int8_t *pCedPN;
   const u_int8_t *pBChInfo;
   const u_int8_t *pKeypad;
   const u_int8_t *pUUData;
   const u_int8_t *pFacility;
   const u_int8_t *pSendingComplete;
   struct mbuf    *pmbReq;
   DaicReqData_t  *pReqData;
   u_int8_t       *p;
   int             i;
   unsigned        uRes;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Got Info-Request",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));

   /* first store the mbuf for later sending a matching confirmation to the
    * application; the handler function for the return code and the function to
    * send the Info-Confirm CAPI message will rely on this
    */
   pPlciData->pmbCurrCapiMsg     = pmbMsg;
   pPlciData->uCurrCapiReqMsgNum = CAPI_GET_MSGNUM (pCapiMsg);
   
   /* parse the Info-Request message for D-channel information elements to
    * send out
    */
   pCedPN           = NULL;
   pBChInfo         = NULL;
   pKeypad          = NULL;
   pUUData          = NULL;
   pFacility        = NULL;
   pSendingComplete = NULL;
   /* Note: This call will also check all parameters for valid maximum length.
    */
   uRes = daicplci_extract_info_req_params
             (pPortData, pPlciData, pCapiMsg,
              &pCedPN,
              &pBChInfo, &pKeypad, &pUUData, &pFacility, &pSendingComplete);
   if (uRes != CAPI_OK)
   {
      daicplci_send_info_conf (pPortData, pPlciData, uRes);
      return (CAPI_OK);
   }

   /* fill a controller message for a call request or an info request (depending
    * on the existence of a called party number in the Info-Request message)
    */
   pmbReq = kcapi_get_mbuf (sizeof (DaicReqData_t) + DAIC_SHMEM_LEN_RXBUFFER);
   if (pmbReq == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for Info-Request, Call-Request for overlap sending or User-User-Data-Request",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      daicplci_send_info_conf (pPortData, pPlciData, CME_OS_RESOURCE_ERROR);
      return (CAPI_OK);
   }
   pReqData = mtod (pmbReq, DaicReqData_t *);
   if (pCedPN == NULL || *pCedPN == 0)
   {
      if (pUUData != NULL && *pUUData != 0 &&
          (pBChInfo == NULL || *pBChInfo == 0) &&
          (pKeypad == NULL || *pKeypad == 0) &&
          (pFacility == NULL || *pFacility == 0) &&
          (pSendingComplete == NULL || *pSendingComplete == 0))
      {
         pReqData->bReq = DAIC_SIG_USER_DATA;
      }
      else
      {
         pReqData->bReq = DAIC_SIG_INFO_REQ;
      }
   }
   else
   {
      pReqData->bReq = DAIC_SIG_CALL_REQ;
   }
   pReqData->bReqId                  = pPlciData->uSigId;
   pReqData->bReqCh                  = 0;
   pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_PLCI;
   pReqData->wAssignReqPlci          = (u_int16_t) (pPlciData->uPlci);
   i = 0;
   p = (u_int8_t *) pReqData + sizeof (*pReqData);
   if (pBChInfo != NULL && pBChInfo [0] > 0)
   {
      const CAPIBChannelInfo_t *pTmp = (const CAPIBChannelInfo_t *) pBChInfo;
      
      if (C_GET_WORD (pTmp->wChannel) == CAPI_BCHAN_MODE_CHANNEL_ID)
      {
         p [i++] = DAIC_IE_CHI;
         bcopy (&(pTmp->info), &(p [i]),
                *((const u_int8_t *) &(pTmp->info)) + 1);
         i += *((const u_int8_t *) &(pTmp->info)) + 1;
      }
   }
   if (pFacility != NULL && pFacility [0] > 0)
   {
      p [i++] = DAIC_IE_FAC;
      bcopy (pFacility, &(p [i]), pFacility [0] + 1);
      i += pFacility [0] + 1;
   }
   if (pKeypad != NULL && pKeypad [0] > 0)
   {
      p [i++] = DAIC_IE_KEY;
      bcopy (pKeypad, &(p [i]), pKeypad [0] + 1);
      i += pKeypad [0] + 1;
   }
   if (pCedPN != NULL && pCedPN [0] > 0)
   {
      p [i++] = DAIC_IE_CPN;
      bcopy (pCedPN, &(p [i]), pCedPN [0] + 1);
      i += pCedPN [0] + 1;
   }
   if (pUUData != NULL && pUUData [0] > 0)
   {
      p [i++] = DAIC_IE_UUI;
      bcopy (pUUData, &(p [i]), pUUData [0] + 1);
      i += pUUData [0] + 1;
   }
   if (pSendingComplete != NULL && pSendingComplete [0] >= 2 &&
       pSendingComplete [1] + (pSendingComplete [2] << 8) != 0)
   {
      p [i++] = DAIC_IE_SCP;
   }
   p [i++] = 0;
   pReqData->wDataLength = i;
   pmbReq->m_len = sizeof (*pReqData) + pReqData->wDataLength;

   /* send out the request */
   pPlciData->uCurrRequest = (unsigned) (pReqData->bReq);
   pPlciData->ulFlags |= DAIC_PLCI_FLAG_REQUEST_PENDING;
   _IF_ENQUEUE (&(pPortData->reqQueue), pmbReq);
   
   return (CAPI_OK);
} /* daicplci_handle_info_req */





/**
 * Handle a CAPI Alert-Request.
 *
 * An Alert-Request will simply result in a controller Alert-Request to be sent
 * out. The Alert-Confirm message will be sent to the application when the
 * return code for the controller Alert-Request arrives.
 *
 * @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 daicplci_handle_alert_req
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    struct mbuf    *pmbMsg)
{
   CAPIMsg_t      *pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
   const u_int8_t *pBChInfo;
   const u_int8_t *pKeypad;
   const u_int8_t *pUUData;
   const u_int8_t *pFacility;
   const u_int8_t *pSendingComplete;
   struct mbuf    *pmbReq;
   DaicReqData_t  *pReqData;
   u_int8_t       *p;
   int             i;
   unsigned        uRes;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Got Alert-Request",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));

   /* first store the mbuf for later sending a matching confirmation to the
    * application; the handler function for the return code and the function to
    * send the Alert-Confirm CAPI message will rely on this
    */
   pPlciData->pmbCurrCapiMsg     = pmbMsg;
   pPlciData->uCurrCapiReqMsgNum = CAPI_GET_MSGNUM (pCapiMsg);
   
   /* check if an Alert-Request was already handled */
   if ((pPlciData->ulFlags & DAIC_PLCI_FLAG_ALERTING_SENT) != 0)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Alerting already sent, ignore Alert-Request",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      daicplci_send_alert_conf (pPortData, pPlciData, CIV_ALERT_ALREADY_SENT);
      return (CAPI_OK);
   }
   
   /* parse the Alert-Request message for D-channel information elements to
    * send out
    */
   pBChInfo         = NULL;
   pKeypad          = NULL;
   pUUData          = NULL;
   pFacility        = NULL;
   pSendingComplete = NULL;
   /* Note: This call will also check all parameters for valid maximum length.
    */
   uRes = daicplci_extract_add_info_params
             (pPortData, pPlciData, (u_int8_t *) &(pCapiMsg->info.any.b [0]),
              &pBChInfo, &pKeypad, &pUUData, &pFacility, &pSendingComplete);
   if (uRes != CAPI_OK)
   {
      daicplci_send_alert_conf (pPortData, pPlciData, uRes);
      return (CAPI_OK);
   }
   
   /* we cannot explicitly send out a CALL_PROCEEDING message as defined in the
    * CAPI specification, so we must reply with an error confirmation in this
    * case
    */
   if (pSendingComplete != NULL && pSendingComplete [0] >= 2 &&
       pSendingComplete [1] + (pSendingComplete [2] << 8) != 0)
   {
      DBG (LOG_INFO, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Explicitly sending of CALL_PROCEEDING not supported by hardware",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
           
      daicplci_send_alert_conf
         (pPortData, pPlciData, CCE_ILLEGAL_MSG_PARAMETER);
      return (CAPI_OK);
   }

   /* fill a controller message for a controller Alert-Request */
   pmbReq = kcapi_get_mbuf (sizeof (DaicReqData_t) + DAIC_SHMEM_LEN_RXBUFFER);
   if (pmbReq == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for Alert-Request",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));

      daicplci_send_alert_conf (pPortData, pPlciData, CME_OS_RESOURCE_ERROR);
      return (CAPI_OK);
   }
   pReqData = mtod (pmbReq, DaicReqData_t *);
   pReqData->bReq                    = DAIC_SIG_ALERT;
   pReqData->bReqId                  = pPlciData->uSigId;
   pReqData->bReqCh                  = 0;
   pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_PLCI;
   pReqData->wAssignReqPlci          = (u_int16_t) (pPlciData->uPlci);
   i = 0;
   p = (u_int8_t *) pReqData + sizeof (*pReqData);
   if (pBChInfo != NULL && pBChInfo [0] > 0)
   {
      const CAPIBChannelInfo_t *pTmp = (const CAPIBChannelInfo_t *) pBChInfo;
      
      if (C_GET_WORD (pTmp->wChannel) == CAPI_BCHAN_MODE_CHANNEL_ID)
      {
         p [i++] = DAIC_IE_CHI;
         bcopy (&(pTmp->info), &(p [i]),
                *((const u_int8_t *) &(pTmp->info)) + 1);
         i += *((const u_int8_t *) &(pTmp->info)) + 1;
      }
   }
   if (pFacility != NULL && pFacility [0] > 0)
   {
      p [i++] = DAIC_IE_FAC;
      bcopy (pFacility, &(p [i]), pFacility [0] + 1);
      i += pFacility [0] + 1;
   }
   if (pKeypad != NULL && pKeypad [0] > 0)
   {
      p [i++] = DAIC_IE_KEY;
      bcopy (pKeypad, &(p [i]), pKeypad [0] + 1);
      i += pKeypad [0] + 1;
   }
   if (pUUData != NULL && pUUData [0] > 0)
   {
      p [i++] = DAIC_IE_UUI;
      bcopy (pUUData, &(p [i]), pUUData [0] + 1);
      i += pUUData [0] + 1;
   }
   p [i++] = 0;
   pReqData->wDataLength = i;
   pmbReq->m_len = sizeof (*pReqData) + pReqData->wDataLength;

   /* send out an Alert-Request */
   pPlciData->uCurrRequest = (unsigned) (pReqData->bReq);
   pPlciData->ulFlags |= DAIC_PLCI_FLAG_REQUEST_PENDING |
                         DAIC_PLCI_FLAG_ALERTING_SENT;
   _IF_ENQUEUE (&(pPortData->reqQueue), pmbReq);
   
   return (CAPI_OK);
} /* daicplci_handle_alert_req */





/**
 * Handle a CAPI Select-B-Protocol-Request.
 *
 * The B-channel protocol can only be changed if no NCCI is currently active for
 * a PLCI and if the PLCI is in the active state (P-ACT). The later is checked
 * by the state tables for the PLCI state machine, i.e. this function is only
 * called in state P-ACT.
 *
 * In order to change the B-channel protocol we must remove the current
 * signaling id and allocate a new one. After allocating the new network id the
 * Select-B-Protocol-Confirm message must be sent to the application. During
 * this time, no other message may be handled. As all actions in this phase are
 * a Remove-Request and an Assign-Request, we do not need a new state, these
 * two operations cannot be disturbed by other messages (CAPI messages or
 * controller indications).
 *
 * @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 daicplci_handle_sel_b_prot_req
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    struct mbuf    *pmbMsg)
{
   CAPIMsg_t *pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
   unsigned   uRes;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Got Select-B-Protocol-Request",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));

   /* first store the mbuf for later sending a matching confirmation to the
    * application; the handler function for the return code and the function to
    * send the Select-B-Protocol-Confirm CAPI message will rely on this
    */
   pPlciData->pmbCurrCapiMsg     = pmbMsg;
   pPlciData->uCurrCapiReqMsgNum = CAPI_GET_MSGNUM (pCapiMsg);
   
   /* check that no NCCI is currently active for this PLCI */
   if (pPlciData->nNumNcci > 0)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Message Select-B-Protocol-Request not allowed with active NCCIs (%zu)",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           pPlciData->nNumNcci);

      daicplci_send_sel_b_prot_conf
         (pPortData, pPlciData, CCE_MSG_NOT_ALLOWED_NOW);
      return (CAPI_OK);
   }
   
   /* now evaluate the new B-protocol settings and check them for validity */
   uRes = daicplci_extract_bprotocol
             (pPortData, pPlciData,
              (CAPIBProtocol_t *) &(pCapiMsg->info.any.b [0]),
              CAPI_REQUEST (C_SELECT_B_PROTOCOL),
              &(pPlciData->bprot));
   if (uRes != CAPI_OK)
   {
      daicplci_send_sel_b_prot_conf (pPortData, pPlciData, uRes);
      return (CAPI_OK);
   }
   
   /* the B-protocol settings are valid and supported --> proceed with removing
    * and re-assigning the network id
    */
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Select-B-Protocol-Request and B-protocol data valid, start re-assigning network id",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
   daicplci_send_net_remove_req (pPortData, pPlciData);
   
   return (CAPI_OK);
} /* daicplci_handle_sel_b_prot_req */





/**
 * Handle a CAPI Disconnect-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 unsigned daicplci_handle_disc_req
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    struct mbuf    *pmbMsg)
{
   CAPIMsg_t      *pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
   const u_int8_t *pBChInfo;
   const u_int8_t *pKeypad;
   const u_int8_t *pUUData;
   const u_int8_t *pFacility;
   const u_int8_t *pSendingComplete;
   struct mbuf    *pmbReq;
   DaicReqData_t  *pReqData;
   u_int8_t       *p;
   int             i;
   unsigned        uRes;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Got Disconnect-Request",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));

   /* first store the mbuf for later sending a matching confirmation to the
    * application; the handler function for the return code and the function to
    * send the Alert-Confirm CAPI message will rely on this
    */
   pPlciData->pmbCurrCapiMsg     = pmbMsg;
   pPlciData->uCurrCapiReqMsgNum = CAPI_GET_MSGNUM (pCapiMsg);
   
   /* if we are still in state P-2 we must treat the message like a negative
    * Connect-Response, i.e. remove the current application from this PLCI, etc.
    */
   if (pPlciData->state == DAIC_PLCI_STATE_P2)
   {
      /* Note: The central message handling function must ensure, we only get
       *       here if the current PLCI is still not assigned to an application.
       */
      unsigned uApplID = CAPI_GET_APPL (pCapiMsg);

      /* if the application is in fact not associated with this PLCI, reject the
       * message
       */
      for (i = 0; i < ARRAY_COUNT (pPortData->aApplData); ++i)
      {
         if (pPortData->aApplData [i].uApplID != 0 &&
             pPortData->aApplData [i].uApplID == uApplID)
         {
            break;
         }
      }
      if (i >= ARRAY_COUNT (pPortData->aApplData))
      {
         DBG (LOG_ERROR, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: Got Disconnect-Request from unregistered appl.id %u",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              uApplID);
         daicplci_send_disc_conf
            (pPortData, pPlciData, CME_INVALID_APPLICATION_ID);
         return (CAPI_OK);
      }
      if (((1 << i) & pPlciData->ulApplIdxBitField) == 0)
      {
         DBG (LOG_ERROR, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: Got Disconnect-Response from appl.id %u that did not receive the Connect-Indication",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              uApplID);
         daicplci_send_disc_conf
            (pPortData, pPlciData, CME_INVALID_APPLICATION_ID);
         return (CAPI_OK);
      }

      /* remove the application from the PLCI */
      pPlciData->ulApplIdxBitField &= ~(1 << i);
      
      /* confirm the Disconnect-Request */
      daicplci_send_disc_conf (pPortData, pPlciData, CAPI_OK);

      /* send a Disconnect-Indication to the application */
      daicplci_send_disc_ind_to_appl
         (pPortData, pPlciData, uApplID, CAPI_OK);
      
      /* if this was the last application in the application mask, reject the
       * new connection and start removing the PLCI
       */
      if (pPlciData->ulApplIdxBitField == 0)
      {
         daicplci_abort_connection
            (pPortData, pPlciData, DAIC_CAUSE_NO_USER_RESPONDING);
      }
      
      return (CAPI_OK);
   }
   
   /* parse the Disconnect-Request message for D-channel information elements to
    * send out
    */
   pBChInfo         = NULL;
   pKeypad          = NULL;
   pUUData          = NULL;
   pFacility        = NULL;
   pSendingComplete = NULL;
   /* Note: This call will also check all parameters for valid maximum length.
    */
   uRes = daicplci_extract_add_info_params
             (pPortData, pPlciData, (u_int8_t *) &(pCapiMsg->info.any.b [0]),
              &pBChInfo, &pKeypad, &pUUData, &pFacility, &pSendingComplete);
   if (uRes != CAPI_OK)
   {
      daicplci_send_disc_conf (pPortData, pPlciData, uRes);
      return (CAPI_OK);
   }
   
   /* fill a controller message for a controller Hangup-Request */
   pmbReq = kcapi_get_mbuf (sizeof (DaicReqData_t) + DAIC_SHMEM_LEN_RXBUFFER);
   if (pmbReq == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for Hangup-Request",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));

      daicplci_send_disc_conf (pPortData, pPlciData, CME_OS_RESOURCE_ERROR);
      return (CAPI_OK);
   }
   pReqData = mtod (pmbReq, DaicReqData_t *);
   pReqData->bReq                    = DAIC_SIG_HANGUP;
   pReqData->bReqId                  = pPlciData->uSigId;
   pReqData->bReqCh                  = 0;
   pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_PLCI;
   pReqData->wAssignReqPlci          = (u_int16_t) (pPlciData->uPlci);
   i = 0;
   p = (u_int8_t *) pReqData + sizeof (*pReqData);
   if (pBChInfo != NULL && pBChInfo [0] > 0)
   {
      const CAPIBChannelInfo_t *pTmp = (const CAPIBChannelInfo_t *) pBChInfo;
      
      if (C_GET_WORD (pTmp->wChannel) == CAPI_BCHAN_MODE_CHANNEL_ID)
      {
         p [i++] = DAIC_IE_CHI;
         bcopy (&(pTmp->info), &(p [i]),
                *((const u_int8_t *) &(pTmp->info)) + 1);
         i += *((const u_int8_t *) &(pTmp->info)) + 1;
      }
   }
   if (pFacility != NULL && pFacility [0] > 0)
   {
      p [i++] = DAIC_IE_FAC;
      bcopy (pFacility, &(p [i]), pFacility [0] + 1);
      i += pFacility [0] + 1;
   }
   if (pKeypad != NULL && pKeypad [0] > 0)
   {
      p [i++] = DAIC_IE_KEY;
      bcopy (pKeypad, &(p [i]), pKeypad [0] + 1);
      i += pKeypad [0] + 1;
   }
   if (pUUData != NULL && pUUData [0] > 0)
   {
      p [i++] = DAIC_IE_UUI;
      bcopy (pUUData, &(p [i]), pUUData [0] + 1);
      i += pUUData [0] + 1;
   }
   if (pSendingComplete != NULL && pSendingComplete [0] >= 2 &&
       pSendingComplete [1] + (pSendingComplete [2] << 8) != 0)
   {
      p [i++] = DAIC_IE_SCP;
   }
   p [i++] = 0;
   pReqData->wDataLength = i;
   pmbReq->m_len = sizeof (*pReqData) + pReqData->wDataLength;

   /* send out a Hangup-Request */
   pPlciData->uCurrRequest = (unsigned) (pReqData->bReq);
   pPlciData->ulFlags |= DAIC_PLCI_FLAG_REQUEST_PENDING;
   _IF_ENQUEUE (&(pPortData->reqQueue), pmbReq);
   
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: PLCI %u: State %d --> %d",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (int) DAIC_PLCI_STATE_P5);
   pPlciData->state = DAIC_PLCI_STATE_P5;
   
   return (CAPI_OK);
} /* daicplci_handle_disc_req */





/**
 * Handle a 2nd or more CAPI Disconnect-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 unsigned daicplci_handle_2nd_disc_req
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    struct mbuf    *pmbMsg)
{
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Got 2nd Disconnect-Request, try to abort the connection",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));

   pPlciData->pmbCurrCapiMsg     = pmbMsg;
   pPlciData->uCurrCapiReqMsgNum = CAPI_GET_MSGNUM (mtod (pmbMsg, CAPIMsg_t *));
   daicplci_send_disc_conf (pPortData, pPlciData, CAPI_OK);
   daicplci_abort_connection (pPortData, pPlciData, DAIC_CAUSE_NORMAL_UNSPEC);
   
   return (CAPI_OK);
} /* daicplci_handle_2nd_disc_req */





/**
 * Handle a CAPI Disconnect-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 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 daicplci_handle_disc_rsp
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    struct mbuf    *pmbMsg)
{
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Got Disconnect-Response, will remove signaling id",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));

   kcapi_free_mbuf (pmbMsg);
   
   daicplci_send_sig_remove_req (pPortData, pPlciData);
   
   return (CAPI_OK);
} /* daicplci_handle_disc_rsp */





/**
 * Handle any CAPI response besides the ones requiring special handling.
 *
 * @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 daicplci_handle_gen_rsp
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    struct mbuf    *pmbMsg)
{
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Got CAPI response 0x%04X",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (unsigned) CAPI_GET_CMD (mtod (pmbMsg, CAPIMsg_t *)));

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





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

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

   /* clear the message queue */
   do
   {
      _IF_DEQUEUE (&(pPlciData->msgQueue), pmb);
      if (pmb != NULL)
      {
         kcapi_free_mbuf (pmb);
      }
   } while (pmb != NULL);

   /* reset all member variables */
   bzero (pPlciData, sizeof (*pPlciData));
   pPlciData->msgQueue.ifq_maxlen = 10;

} /* daicplci_reset */





/**
 * Handle messages enqueued for a PLCI.
 *
 * This function will handle any message enqueued for a given PLCI, 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 pPlciData             i/O: The PLCI data for this operation.
 *
 * @return Nothing.
 */

static void daicplci_handle_queued_messages
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData)
{
   struct mbuf *pmb;
   
   /* if there is at least one CAPI message in the local queue and the state
    * after the handler function does not denote any pending controller request,
    * dequeue the next message and handle it through the CAPI message handler.
    */
   while ((pPlciData->ulFlags & DAIC_PLCI_FLAG_REQUEST_PENDING) == 0 &&
          _IF_QLEN (&(pPlciData->msgQueue)) > 0)
   {
      _IF_DEQUEUE (&(pPlciData->msgQueue), pmb);
      if (pmb->m_type == MT_DATA)
      {
         if (daicplci_handle_capi_msg
                (pPortData, pPlciData->uPlci,
                 CAPI_GET_APPL (mtod (pmb, CAPIMsg_t *)), pmb) != CAPI_OK)
         {
            kcapi_free_mbuf (pmb);
         }
      }
      else
      {
         daicplci_handle_ind
            (pPortData, pPlciData, mtod (pmb, DaicIndData_t *));
         kcapi_free_mbuf (pmb);
      }
   }

} /* daicplci_handle_queued_messages */





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

static void daicplci_handle_ind
   (DaicPortData_t      *pPortData,
    DaicPlciData_t      *pPlciData,
    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) (pPlciData->state)] [pIndData->bInd];

   /* if there is a valid handler function: call it */
   if (pfnAction != NULL)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Calling action function for indication 0x%02X, id 0x%02X",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pIndData->bInd), (unsigned) (pIndData->bIndId));
      pfnAction (pPortData, pPlciData, pIndData);
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Action function completed",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->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: PLCI %u: State %d: Indication 0x%02X, id 0x%02X not expected in current state",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pIndData->bInd), (unsigned) (pIndData->bIndId));
   }

} /* daicplci_handle_ind */





/**
 * Handle a CAPI message to a PLCI 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 uPlci                 I: The addressed PLCI value, equal to the
 *                                 signalling 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.
 */

static unsigned daicplci_handle_capi_msg
   (DaicPortData_t  *pPortData,
    unsigned         uPlci,
    unsigned         uApplID,
    struct mbuf     *pmbMsg)
{
   DaicPlciData_t         *pPlciData = &(pPortData->aPlciData [uPlci]);
   CAPIMsg_t              *pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
   DaicCapiMsgActionFct_t *pfnAction;
   unsigned                uCmd;
   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: PLCI %u: State %d: CAPI message 0x%04X, application %u invalidated, will be ignored",
           pPortData->uPortIdx, uPlci, (int) (pPlciData->state),
           (unsigned) CAPI_GET_CMD (pCapiMsg), uApplID);
      kcapi_free_mbuf (pmbMsg);
      return (CAPI_OK);
   }
   
   /* translate the CAPI message command into an action table index */
   uCmd = CAPI_GET_CMD (pCapiMsg);
   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: PLCI %u: State %d: CAPI message 0x%04X, CID 0x%08lX not valid for PLCI context",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           uCmd, (unsigned long) (CAPI_GET_CID (pCapiMsg)));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   
   /* Check if the PLCI is assigned to the same application id as the currently
    * handled CAPI message. If the PLCI is currently not assigned exclusively,
    * all messages are passed to the state machine. As the state machine will
    * itself reject all messages that are not allowed in the current state, this
    * is safe. And all message handling functions in these states check the
    * application id themselves for validity. If the PLCI is exclusively
    * assigned and the current CAPI message originates from a different
    * application, we must reject the message but check for some exceptions.
    */
   if (pPlciData->uApplID != 0 && pPlciData->uApplID != uApplID)
   {
      /* The PLCI is assigned exclusively and the current CAPI message
       * originates from a different application id. We must normally reject the
       * message. But for incoming calls multiple applications may receive the
       * Connect-Indication and must respond to it. Only the first responding
       * application gets the call and has the PLCI exclusively assigned to it.
       * The other applications will receive a Disconnect-Indication. So later
       * Connect-Responses, Disconnect-Requests or Alert-Requests must be simply
       * ignored (i.e. not answered), as well as a Disconnect-Response.
       */
      if (uCmd == CAPI_RESPONSE (C_CONNECT) ||
          uCmd == CAPI_REQUEST (C_ALERT) ||
          uCmd == CAPI_REQUEST (C_DISCONNECT) ||
          uCmd == CAPI_RESPONSE (C_DISCONNECT))
      {
         DBG (LOG_TRACE, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: Ignore CAPI message 0x%04X because PLCI is already assigned to application id %u, not %u",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              uCmd, pPlciData->uApplID, uApplID);
      }
      else
      {
         DBG (LOG_TRACE, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: Reject CAPI message 0x%04X because PLCI is assigned to application id %u, not %u",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              uCmd, pPlciData->uApplID, uApplID);
         daicplci_send_gen_conf
            (pPortData, pPlciData, pCapiMsg, CME_INVALID_APPLICATION_ID);
      }
      kcapi_free_mbuf (pmbMsg);
      return (CAPI_OK);
   }

   /* 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) (pPlciData->state)] [iCmdIdx];
   
   /* if there is a valid handler function: call it */
   if (pfnAction != NULL)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Calling action function for CAPI message 0x%04X, index %d",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           uCmd, iCmdIdx);
      uRes = pfnAction (pPortData, pPlciData, pmbMsg);
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Action function returned 0x%04X",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->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, pPlciData->uPlci, (int) (pPlciData->state),
           uCmd, iCmdIdx);
      uRes = CCE_MSG_NOT_ALLOWED_NOW;
      daicplci_send_gen_conf (pPortData, pPlciData, pCapiMsg, uRes);
   }

   return (uRes);
} /* daicplci_handle_capi_msg */





/**
 * Abort a connection in any possible state.
 *
 * A connection may be aborted in all states when no controller request is
 * pending. If this function is called with a controller request running, we can
 * only record this abort request by setting a flag. The handler function for
 * return codes will check this and proceed with aborting the connection as soon
 * as the return code arrives.
 *
 * If no controller request is pending when this function is called, we must
 * perform actions according the current state. If the current state denotes a
 * running controller request and no one is really active, we must assume the
 * request failed with a negative return code.
 *
 * The actions to perform for aborting a connection are removing all NCCIs,
 * sending a Hangup-Request and removing both the network and the signaling id.
 * According to the current state, some actions need not be done because the
 * connection did not make its way completely to active state. The NCCIs are
 * normally removed in the handler for the Hangup-Request return code, so we
 * need not do this here.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param uCauseVal             I: The cause value to signal to the network.
 *
 * @return Nothing.
 */

static void daicplci_abort_connection
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uCauseVal)
{
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Will abort connection with cause 0x%02X",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        uCauseVal);

   /* if there is currently a controller request pending, we can only register
    * the abort request and wait for the return code
    */
   if ((pPlciData->ulFlags & DAIC_PLCI_FLAG_REQUEST_PENDING) != 0)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Register abort request because of pending controller request 0x%02X",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           pPlciData->uCurrRequest);
      pPlciData->ulFlags     |= DAIC_PLCI_FLAG_GOT_ABORT_REQ;
      pPlciData->uAbortCause  = uCauseVal;
      return;
   }
   
   /* distinguish between the different PLCI states */
   switch (pPlciData->state)
   {
      case DAIC_PLCI_STATE_P0:
         DBG (LOG_DEBUG, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: PLCI released by resetting data",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
         daicplci_reset (pPortData, pPlciData);
         if (pPortData->nNumPlci > 0)
         {
            --(pPortData->nNumPlci);
         }
         break;

      case DAIC_PLCI_STATE_P0_2:
         /* the call is in the on-hook state --> start removing the signaling id
          */
         daicplci_send_sig_remove_req (pPortData, pPlciData);
         break;
         
      case DAIC_PLCI_STATE_P5:
      case DAIC_PLCI_STATE_P6_1:
      case DAIC_PLCI_STATE_P6_3:
         /* A Hangup-Request or a Remove-Request is already sent, just ignore
          * this request
          */
         break;

      case DAIC_PLCI_STATE_P6:
         /* it seems that at least one active NCCI cannot be released -->
          * proceed with removing the network id
          */
         daicplci_send_net_remove_req (pPortData, pPlciData);
         break;

      case DAIC_PLCI_STATE_P6_2:
         /* in this case we cannot assume the pending Disconnect-Response to
          * arrive --> perform the Remove-Request as the last operation for a
          * call
          */
         daicplci_send_sig_remove_req (pPortData, pPlciData);
         break;

      default:
         /* in the other states there is an active call or at least a call not
          * in the on-hook state --> a Hangup-Request is needed
          */
         daicplci_send_hangup_req (pPortData, pPlciData, uCauseVal);
         break;
   }
   
} /* daicplci_abort_connection */





/**
 * Continue processing a Connect-Request.
 *
 * This function is called to proceed with a CAPI Connect-Request after a
 * successful Assign-Request. It will parse the CAPI message for necessary call
 * parameters and send out a controller Call-Request.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 *
 * @return Nothing.
 */

static void daicplci_continue_connect_req
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData)
{
   CAPIMsg_t      *pCapiReq = mtod (pPlciData->pmbCurrCapiMsg, CAPIMsg_t *);
   struct mbuf    *pmbReq;
   DaicReqData_t  *pReqData;
   const u_int8_t *pCedPN;
   const u_int8_t *pCngPN;
   const u_int8_t *pCedPSa;
   const u_int8_t *pCngPSa;
   const u_int8_t *pBC;
   const u_int8_t *pLLC;
   const u_int8_t *pHLC;
   const u_int8_t *pBChInfo;
   const u_int8_t *pKeypad;
   const u_int8_t *pUUData;
   const u_int8_t *pFacility;
   const u_int8_t *pSendingComplete;
   u_int8_t       *p;
   int             i;
   unsigned        uRes;
   
   /* we must perform the state transit now and not only after sending out the
    * Call-Request, because if an error occurs a Remove-Request must be sent and
    * not only a state transfer to P-0 (see daicplci_send_connect_conf())
    */
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: PLCI %u: State %d --> %d",
        pPortData->uPortIdx, pPlciData->uPlci,
        (int) (pPlciData->state),
        (int) DAIC_PLCI_STATE_P0_2);
   pPlciData->state = DAIC_PLCI_STATE_P0_2;

   /* parse the Connect-Request message for Call-Request parameters (Numbers,
    * BC, HLC, etc.)
    */
   pCedPN = NULL;
   pCngPN = NULL;
   pCedPSa = NULL;
   pCngPSa = NULL;
   pBC = NULL;
   pLLC = NULL;
   pHLC = NULL;
   pBChInfo = NULL;
   pKeypad = NULL;
   pUUData = NULL;
   pFacility = NULL;
   pSendingComplete = NULL;
   /* Note: This call will also check all parameters for valid maximum length
    *       and translate any CIP value into BC and HLC, if they are not
    *       explicitly specified in the message.
    */
   uRes = daicplci_extract_conn_req_params
             (pPortData, pPlciData, pCapiReq,
              &pCedPN, &pCngPN, &pCedPSa, &pCngPSa, &(pPlciData->bprot),
              &pBC, &pLLC, &pHLC, &pBChInfo, &pKeypad, &pUUData, &pFacility,
              &pSendingComplete);
   if (uRes != CAPI_OK)
   {
      daicplci_send_connect_conf (pPortData, pPlciData, uRes);
      daicplci_send_sig_remove_req (pPortData, pPlciData);
      return;
   }

   /* send out a Call-Request */
   pmbReq = kcapi_get_mbuf (sizeof (DaicReqData_t) + DAIC_SHMEM_LEN_RXBUFFER);
   if (pmbReq == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for Call-Request",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      pPlciData->uApplID = 0;
      daicplci_send_connect_conf (pPortData, pPlciData, CME_OS_RESOURCE_ERROR);
      daicplci_send_sig_remove_req (pPortData, pPlciData);
      return;
   }
   pReqData = mtod (pmbReq, DaicReqData_t *);
   pReqData->bReq                    = DAIC_SIG_CALL_REQ;
   pReqData->bReqId                  = pPlciData->uSigId;
   pReqData->bReqCh                  = 0;
   pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_PLCI;
   pReqData->wAssignReqPlci          = (u_int16_t) (pPlciData->uPlci);
   i = 0;
   p = (u_int8_t *) pReqData + sizeof (*pReqData);
   if (pBC != NULL && pBC [0] > 0)
   {
      p [i++] = DAIC_IE_BC;
      bcopy (pBC, &(p [i]), pBC [0] + 1);
      i += pBC [0] + 1;
   }
   p [i++] = DAIC_IE_CAI;
   if (pPlciData->bprot.wB1Prot == CAPI_B1_TRANSPARENT_64)
   {
      p [i++] = 6;
      p [i++] = 0x89;
      p [i++] = 0;
      p [i++] = 0;
      p [i++] = 0;
      p [i++] = 32;
      p [i++] = 0;
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: CAI for Call-Request length 6, 1st byte 0x%02X",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (p [i - 6]));
   }
   else
   {
      p [i++] = 1;
      p [i++] = 0x85;
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: CAI for Call-Request 0x%02X",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (p [i - 1]));
   }
   if (pBChInfo != NULL && pBChInfo [0] > sizeof (cWORD) + 1)
   {
      const CAPIBChannelInfo_t *pTmp = (const CAPIBChannelInfo_t *) pBChInfo;
      
      if (C_GET_WORD (pTmp->wChannel) == CAPI_BCHAN_MODE_CHANNEL_ID)
      {
         p [i++] = DAIC_IE_CHI;
         bcopy (&(pTmp->info), &(p [i]),
                *((const u_int8_t *) &(pTmp->info)) + 1);
         i += *((const u_int8_t *) &(pTmp->info)) + 1;
      }
   }
   if (pFacility != NULL && pFacility [0] > 0)
   {
      p [i++] = DAIC_IE_FAC;
      bcopy (pFacility, &(p [i]), pFacility [0] + 1);
      i += pFacility [0] + 1;
   }
   if (pKeypad != NULL && pKeypad [0] > 0)
   {
      p [i++] = DAIC_IE_KEY;
      bcopy (pKeypad, &(p [i]), pKeypad [0] + 1);
      i += pKeypad [0] + 1;
   }
   if (pCngPN != NULL && pCngPN [0] > 0)
   {
      p [i++] = DAIC_IE_OAD;
      bcopy (pCngPN, &(p [i]), pCngPN [0] + 1);
      i += pCngPN [0] + 1;
   }
   if (pCngPSa != NULL && pCngPSa [0] > 0)
   {
      p [i++] = DAIC_IE_OSA;
      bcopy (pCngPSa, &(p [i]), pCngPSa [0] + 1);
      i += pCngPSa [0] + 1;
   }
   if (pCedPN != NULL && pCedPN [0] > 0)
   {
      p [i++] = DAIC_IE_CPN;
      bcopy (pCedPN, &(p [i]), pCedPN [0] + 1);
      i += pCedPN [0] + 1;
   }
   if (pCedPSa != NULL && pCedPSa [0] > 0)
   {
      p [i++] = DAIC_IE_DSA;
      bcopy (pCedPSa, &(p [i]), pCedPSa [0] + 1);
      i += pCedPSa [0] + 1;
   }
   if (pLLC != NULL && pLLC [0] > 0)
   {
      p [i++] = DAIC_IE_LLC;
      bcopy (pLLC, &(p [i]), pLLC [0] + 1);
      i += pLLC [0] + 1;
   }
   if (pHLC != NULL && pHLC [0] > 0)
   {
      p [i++] = DAIC_IE_HLC;
      bcopy (pHLC, &(p [i]), pHLC [0] + 1);
      i += pHLC [0] + 1;
   }
   if (pUUData != NULL && pUUData [0] > 0)
   {
      p [i++] = DAIC_IE_UUI;
      bcopy (pUUData, &(p [i]), pUUData [0] + 1);
      i += pUUData [0] + 1;
   }
   if (pSendingComplete != NULL && pSendingComplete [0] >= 2 &&
       pSendingComplete [1] + (pSendingComplete [2] << 8) != 0)
   {
      p [i++] = DAIC_IE_SCP;
   }
   p [i++] = 0;
   pReqData->wDataLength = (u_int16_t) i;
   pmbReq->m_len = sizeof (*pReqData) + pReqData->wDataLength;
   pPlciData->uCurrRequest = (unsigned) (pReqData->bReq);
   pPlciData->ulFlags |= DAIC_PLCI_FLAG_REQUEST_PENDING;
   _IF_ENQUEUE (&(pPortData->reqQueue), pmbReq);

   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %u: Call-Request created, wait for Call-Connected-Indication",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));

} /* daicplci_continue_connect_req */





/**
 * Send out a Connect-Confirm and perform follow-up operations.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param uInfoValue            I: The info value to report in the message.
 *
 * @return Nothing.
 */

static void daicplci_send_connect_conf
   (DaicPortData_t       *pPortData,
    DaicPlciData_t       *pPlciData,
    unsigned              uInfoValue)
{
   CAPIMsg_t      *pCapiConf;
   struct mbuf    *pmbConf;

   /* if this PLCI is not assigned to an application or there is no stored
    * request CAPI message, we need- and cannot do anything, simply return
    */
   if (pPlciData->uApplID == 0 ||
       pPlciData->pmbCurrCapiMsg == NULL)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Appl.id not assigned or no request message stored, do not send Connect-Confirm",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
   }
   else
   {
      /* prepare a Connect-Confirm mbuf */
      pmbConf = kcapi_get_mbuf (sizeof (CAPIMsgHead_t) +
                                sizeof (CAPIConnectConf_t));
      if (pmbConf == NULL)
      {
         DBG (LOG_ERROR, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: Out of mbufs for Connect-Confirm",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
         kcapi_free_mbuf (pPlciData->pmbCurrCapiMsg);
         pPlciData->pmbCurrCapiMsg = NULL;
         daicplci_abort_connection
            (pPortData, pPlciData, DAIC_CAUSE_RESOURCE_UNAVAIL_UNSPEC);
         return;
      }
      pCapiConf = mtod (pmbConf, CAPIMsg_t *);
      C_PUT_WORD (pCapiConf->head.wApp, pPlciData->uApplID);
      C_PUT_WORD (pCapiConf->head.wCmd, CAPI_CONFIRM (C_CONNECT));
      C_PUT_WORD (pCapiConf->head.wNum, pPlciData->uCurrCapiReqMsgNum);
      if (uInfoValue == CAPI_OK)
      {
         C_PUT_DWORD (pCapiConf->head.dwCid, pPlciData->dwCid);
      }
      else
      {
         u_int32_t dwCid = (pPlciData->dwCid & CAPI_CIDMASK_CTLR);
         C_PUT_DWORD (pCapiConf->head.dwCid, dwCid);
      }
      C_PUT_WORD (pCapiConf->info.connect_conf.wInfo, uInfoValue);
      C_PUT_WORD (pCapiConf->head.wLen,
                  sizeof (CAPIMsgHead_t) + sizeof (CAPIConnectConf_t));

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

      mtx_unlock (&(pPortData->pSc->mtxAccess));
      kcapi_ctlr_receive_capi_message (pPortData->uUniqueCapiCtlrNum,
                                       pPlciData->uApplID, pmbConf);
      mtx_lock (&(pPortData->pSc->mtxAccess));

      /* if it was a negative confirmation, the PLCI is now not assigned to an
       * application any more
       */
      if (uInfoValue != CAPI_OK)
      {
         pPlciData->uApplID = 0;
         pPlciData->ulApplIdxBitField = 0;
      }
   }
   
   /* the stored Connect-Request message is not needed any more and must be
    * released
    */
   if (pPlciData->pmbCurrCapiMsg != NULL)
   {
      kcapi_free_mbuf (pPlciData->pmbCurrCapiMsg);
      pPlciData->pmbCurrCapiMsg = NULL;
   }
   
   if (uInfoValue == CAPI_OK)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d --> %d",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (int) DAIC_PLCI_STATE_P1);
      pPlciData->state = DAIC_PLCI_STATE_P1;
   }
   else if (pPlciData->state == DAIC_PLCI_STATE_P0_1)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d --> %d",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (int) DAIC_PLCI_STATE_P0);
      pPlciData->state = DAIC_PLCI_STATE_P0;
   }
   else if (pPlciData->state != DAIC_PLCI_STATE_P0)
   {
      /* function will perform state transit and corresponding log message */
      daicplci_send_sig_remove_req (pPortData, pPlciData);
   }
   
} /* daicplci_send_connect_conf */





/**
 * Send out a Disconnect-Confirm.
 *
 * @pre This function must only be called with the pmbCurrCapiMsg member of the
 *      PLCI data structure set to a valid mbuf with an Disconnect-Request.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param uInfoVal              I: The info value to insert into the message.
 *
 * @return Nothing
 */

static void daicplci_send_disc_conf
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uInfoVal)
{
   CAPIMsg_t      *pCapiConf;
   struct mbuf    *pmbConf;

   /* if there is no stored request CAPI message, we need- and cannot do
    * anything, simply return
    */
   if (pPlciData->pmbCurrCapiMsg == NULL)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: No request message stored, do not send Disconnect-Confirm",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return;
   }
   
   /* prepare an Disconnect-Confirm mbuf */
   pmbConf = kcapi_get_mbuf (sizeof (CAPIMsgHead_t) +
                             sizeof (CAPIDisconnectConf_t));
   if (pmbConf == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for Disconnect-Confirm",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      kcapi_free_mbuf (pPlciData->pmbCurrCapiMsg);
      pPlciData->pmbCurrCapiMsg = NULL;
      return;
   }
   pCapiConf = mtod (pmbConf, CAPIMsg_t *);
   pCapiConf->head = mtod (pPlciData->pmbCurrCapiMsg, CAPIMsg_t *)->head;
   C_PUT_WORD (pCapiConf->head.wCmd, CAPI_CONFIRM (C_DISCONNECT));
   C_PUT_DWORD (pCapiConf->head.dwCid, pPlciData->dwCid);
   C_PUT_WORD (pCapiConf->info.disconnect_conf.wInfo, uInfoVal);
   C_PUT_WORD (pCapiConf->head.wLen,
               sizeof (CAPIMsgHead_t) + sizeof (CAPIDisconnectConf_t));

   kcapi_free_mbuf (pPlciData->pmbCurrCapiMsg);
   pPlciData->pmbCurrCapiMsg = NULL;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Send Disconnect-Confirm with wInfo 0x%04X",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        uInfoVal);

   /* Note: The Disconnect-Confirm can also be sent to an application that is
    *       not exclusively assigned this PLCI.
    */
   mtx_unlock (&(pPortData->pSc->mtxAccess));
   kcapi_ctlr_receive_capi_message (pPortData->uUniqueCapiCtlrNum,
                                    CAPI_GET_APPL (pCapiConf), pmbConf);
   mtx_lock (&(pPortData->pSc->mtxAccess));

   /* no state transit */
   
} /* daicplci_send_disc_conf */





/**
 * Send out a Select-B-Protocol-Confirm.
 *
 * @pre This function must only be called with the pmbCurrCapiMsg member of the
 *      PLCI data structure set to a valid mbuf with a
 *      Select-B-Protocol-Request.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param uInfoVal              I: The info value to insert into the message.
 *
 * @return Nothing.
 */

static void daicplci_send_sel_b_prot_conf
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uInfoVal)
{
   CAPIMsg_t      *pCapiConf;
   struct mbuf    *pmbConf;

   /* if there is no stored request CAPI message, we need- and cannot do
    * anything, simply return
    */
   if (pPlciData->pmbCurrCapiMsg == NULL)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: No request message stored, do not send Select-B-Protocol-Confirm",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return;
   }
   
   /* prepare an Select-B-Protocol-Confirm mbuf */
   pmbConf = kcapi_get_mbuf (sizeof (CAPIMsgHead_t) +
                             sizeof (CAPISelectBProtocolConf_t));
   if (pmbConf == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for Select-B-Protocol-Confirm",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      kcapi_free_mbuf (pPlciData->pmbCurrCapiMsg);
      pPlciData->pmbCurrCapiMsg = NULL;
      return;
   }
   pCapiConf = mtod (pmbConf, CAPIMsg_t *);
   pCapiConf->head = mtod (pPlciData->pmbCurrCapiMsg, CAPIMsg_t *)->head;
   C_PUT_WORD (pCapiConf->head.wCmd, CAPI_CONFIRM (C_SELECT_B_PROTOCOL));
   C_PUT_DWORD (pCapiConf->head.dwCid, pPlciData->dwCid);
   C_PUT_WORD (pCapiConf->info.select_b_protocol_conf.wInfo, uInfoVal);
   C_PUT_WORD (pCapiConf->head.wLen,
               sizeof (CAPIMsgHead_t) + sizeof (CAPISelectBProtocolConf_t));

   kcapi_free_mbuf (pPlciData->pmbCurrCapiMsg);
   pPlciData->pmbCurrCapiMsg = NULL;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Send Select-B-Protocol-Confirm with wInfo 0x%04X",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        uInfoVal);

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

   /* no state transit */
   
} /* daicplci_send_sel_b_prot_conf */





/**
 * Send out an Info-Confirm.
 *
 * @pre This function must only be called with the pmbCurrCapiMsg member of the
 *      PLCI data structure set to a valid mbuf with an Info-Request.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param uInfoVal              I: The info value to insert into the message.
 *
 * @return Nothing
 */

static void daicplci_send_info_conf
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uInfoVal)
{
   CAPIMsg_t      *pCapiConf;
   struct mbuf    *pmbConf;

   /* if there is no stored request CAPI message, we need- and cannot do
    * anything, simply return
    */
   if (pPlciData->pmbCurrCapiMsg == NULL)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: No request message stored, do not send Info-Confirm",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return;
   }
   
   /* prepare an Info-Confirm mbuf */
   pmbConf = kcapi_get_mbuf (sizeof (CAPIMsgHead_t) +
                             sizeof (CAPIInfoConf_t));
   if (pmbConf == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for Info-Confirm",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      kcapi_free_mbuf (pPlciData->pmbCurrCapiMsg);
      pPlciData->pmbCurrCapiMsg = NULL;
      return;
   }
   pCapiConf = mtod (pmbConf, CAPIMsg_t *);
   pCapiConf->head = mtod (pPlciData->pmbCurrCapiMsg, CAPIMsg_t *)->head;
   C_PUT_WORD (pCapiConf->head.wCmd, CAPI_CONFIRM (C_INFO_20));
   C_PUT_DWORD (pCapiConf->head.dwCid, pPlciData->dwCid);
   C_PUT_WORD (pCapiConf->info.info_conf.wInfo, uInfoVal);
   C_PUT_WORD (pCapiConf->head.wLen,
               sizeof (CAPIMsgHead_t) + sizeof (CAPIInfoConf_t));

   kcapi_free_mbuf (pPlciData->pmbCurrCapiMsg);
   pPlciData->pmbCurrCapiMsg = NULL;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Send Info-Confirm with wInfo 0x%04X",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        uInfoVal);

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

   /* no state transit */
   
} /* daicplci_send_info_conf */





/**
 * Send out an Alert-Confirm.
 *
 * @pre This function must only be called with the pmbCurrCapiMsg member of the
 *      PLCI data structure set to a valid mbuf with an Alert-Request.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param uInfoVal              I: The info value to insert into the message.
 *
 * @return Nothing.
 */

static void daicplci_send_alert_conf
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uInfoVal)
{
   CAPIMsg_t      *pCapiConf;
   struct mbuf    *pmbConf;

   /* if there is no stored request CAPI message, we need- and cannot do
    * anything, simply return
    */
   if (pPlciData->pmbCurrCapiMsg == NULL)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: No request message stored, do not send Alert-Confirm",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return;
   }
   
   /* prepare an Alert-Confirm mbuf */
   pmbConf = kcapi_get_mbuf (sizeof (CAPIMsgHead_t) +
                             sizeof (CAPIAlertConf_t));
   if (pmbConf == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for Alert-Confirm",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      kcapi_free_mbuf (pPlciData->pmbCurrCapiMsg);
      pPlciData->pmbCurrCapiMsg = NULL;
      return;
   }
   pCapiConf = mtod (pmbConf, CAPIMsg_t *);
   pCapiConf->head = mtod (pPlciData->pmbCurrCapiMsg, CAPIMsg_t *)->head;
   C_PUT_WORD (pCapiConf->head.wCmd, CAPI_CONFIRM (C_ALERT));
   C_PUT_DWORD (pCapiConf->head.dwCid, pPlciData->dwCid);
   C_PUT_WORD (pCapiConf->info.alert_conf.wInfo, uInfoVal);
   C_PUT_WORD (pCapiConf->head.wLen,
               sizeof (CAPIMsgHead_t) + sizeof (CAPIAlertConf_t));

   kcapi_free_mbuf (pPlciData->pmbCurrCapiMsg);
   pPlciData->pmbCurrCapiMsg = NULL;
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Send Alert-Confirm with wInfo 0x%04X",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        uInfoVal);

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

   /* no state transit */
   
} /* daicplci_send_alert_conf */





/**
 * Send out a Disconnect-Ind and perform a state transit.
 *
 * This function is called to send a Disconnect-Indication to all CAPI
 * applications associated with a PLCI. This may be an application that is
 * exclusively assigned to a PLCI or it may be a bit mask of all applications
 * that earlier received a Connect-Indication.
 *
 * If the PLCI is not already assigned to an application, all applications
 * receiving the Disconnect-Indication are removed from the bit mask. In this
 * case the next state is P-6.3 and a Remove-Request is also sent to the
 * controller.
 *
 * If the PLCI is assigned to an application, the state will be changed to P-6.2
 * and we wait for the Disconnect-Response.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 *
 * @return Nothing.
 */

static void daicplci_send_disc_ind
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData)
{
   /* if the PLCI is exclusively assigned to an application, send out a
    * Disconnect-Indication only to this application
    */
   if (pPlciData->uApplID != 0)
   {
      daicplci_send_disc_ind_to_appl
         (pPortData, pPlciData,
          pPlciData->uApplID, pPlciData->uDisconnectCause);
         
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d --> %d",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (int) DAIC_PLCI_STATE_P6_2);
      pPlciData->state = DAIC_PLCI_STATE_P6_2;
   }
   /* if the PLCI is not exclusively assigned to a single application, sent out
    * a Disconnect-Indication to all applications that received the
    * Connect-Indication (i.e. members of the application bit mask). Every
    * application is removed from the bit mask and so a Disconnect-Response
    * cannot be handled for this PLCI. Thus we now must send the Remove-Request
    * for the signaling id and not wait for a Disconnect-Response.
    */
   else
   {
      int iIdx;
      int iMaskBit;
      
      for (iIdx = 0, iMaskBit = 1;
           iIdx < ARRAY_COUNT (pPortData->aApplData);
           ++iIdx, iMaskBit <<= 1)
      {
         if ((pPlciData->ulApplIdxBitField & (iMaskBit)) == 0)
         {
            continue;
         }
         pPlciData->ulApplIdxBitField &= ~iMaskBit;
         daicplci_send_disc_ind_to_appl
            (pPortData, pPlciData,
             pPortData->aApplData [iIdx].uApplID, pPlciData->uDisconnectCause);
      }
      
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: All applications removed from application mask, remove signaling id 0x%02X now",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           pPlciData->uSigId);
      daicplci_send_sig_remove_req (pPortData, pPlciData);
   }
   
} /* daicplci_send_disc_ind */





/**
 * Send out a Disconnect-Ind to a specific application.
 *
 * This function is called to send out a Disconnect-Indication to an application
 * without regarding the current state of the PLCI. This may e.g. be used to
 * send this message to applications for an incoming call that do not get it.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param uApplID               I: The application id to send the message to.
 * @param uReason               I: The reason value to put into the message.
 *
 * @return Nothing.
 */

static void daicplci_send_disc_ind_to_appl
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uApplID,
    unsigned        uReason)
{
   CAPIMsg_t      *pCapiInd;
   struct mbuf    *pmbInd;

   /* prepare a Disconnect-Indication mbuf */
   pmbInd = kcapi_get_mbuf (sizeof (CAPIMsgHead_t) +
                            sizeof (CAPIDisconnectInd_t));
   if (pmbInd == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for Disconnect-Indication for application id %u",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           uApplID);
      return;
   }
   pCapiInd = mtod (pmbInd, CAPIMsg_t *);
   C_PUT_WORD (pCapiInd->head.wApp, uApplID);
   C_PUT_WORD (pCapiInd->head.wCmd, CAPI_INDICAT (C_DISCONNECT));
   C_PUT_WORD (pCapiInd->head.wNum, pPortData->uCapiIndMsgNum);
   ++(pPortData->uCapiIndMsgNum);
   C_PUT_DWORD (pCapiInd->head.dwCid, pPlciData->dwCid);
   if ((uReason & 0xFF00) == 0)
   {
      uReason |= CDISC_NETWORK_CAUSE_MASK;
   }
   C_PUT_WORD (pCapiInd->info.disconnect_ind.wReason, uReason);
   C_PUT_WORD (pCapiInd->head.wLen,
               sizeof (CAPIMsgHead_t) + sizeof (CAPIDisconnectInd_t));

   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Send Disconnect-Indication with wReason 0x%04X to application %u",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        uReason, uApplID);

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

   /* no state transit here */
   
} /* daicplci_send_disc_ind_to_appl */





/**
 * 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 pPlciData             I/O: The PLCI 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 daicplci_send_gen_conf
   (DaicPortData_t  *pPortData,
    DaicPlciData_t  *pPlciData,
    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: PLCI %u: State %d: Out of mbufs for generic CAPI confirmation message for 0x%04X",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->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: PLCI %u: State %d: Send generic CAPI confirmation 0x%04X with info value 0x%04X to application %u",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->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));

} /* daicplci_send_gen_conf */





/**
 * Parse a Connect-Request CAPI message.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I: The PLCI data as the context for this
 *                                 operation.
 * @param pCapiMsg              I: The CAPI message to parse.
 * @param ppCedPN               O: The address of the called party number.
 * @param ppCngPN               O: The address of the calling party number.
 * @param ppCedPSa              O: The address of the called party subaddress.
 * @param ppCngPSa              O: The address of the calling party subaddress.
 * @param pBProt                O: The area to store the B-Protocol information.
 * @param ppBC                  O: The address of the bearer capability.
 * @param ppLLC                 O: The address of the low layer compatibility.
 * @param ppHLC                 O: The address of the high layer compatibility.
 * @param ppBChnInfo            O: The address of the B-channel information.
 * @param ppKeypad              O: The address of the keypad facility.
 * @param ppUUData              O: The address of the user-user data.
 * @param ppFacility            O: The address of the facility data array.
 * @param ppSendingComplete     O: The address of the sending complete ie.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicplci_extract_conn_req_params
   (DaicPortData_t       *pPortData,
    DaicPlciData_t       *pPlciData,
    const CAPIMsg_t      *pCapiMsg,
    const u_int8_t      **ppCedPN,
    const u_int8_t      **ppCngPN,
    const u_int8_t      **ppCedPSa,
    const u_int8_t      **ppCngPSa,
    DaicBProtocolData_t  *pBProt,
    const u_int8_t      **ppBC,
    const u_int8_t      **ppLLC,
    const u_int8_t      **ppHLC,
    const u_int8_t      **ppBChnInfo,
    const u_int8_t      **ppKeypad,
    const u_int8_t      **ppUUData,
    const u_int8_t      **ppFacility,
    const u_int8_t      **ppSendingComplete)
{
   const u_int8_t *p;
   size_t          nMaxPos;
   size_t          nCurrPos;
   unsigned        uRes;
   
   p = (const u_int8_t *) &(pCapiMsg->info.connect_req.wCip) +
       sizeof (pCapiMsg->info.connect_req.wCip);
   nMaxPos = (size_t) C_GET_WORD (pCapiMsg->head.wLen);
   nCurrPos = (size_t) (p - (const u_int8_t *) pCapiMsg);

   /* check for called party number */
   if (nCurrPos >= nMaxPos ||
       nCurrPos + 1 + p [0] > nMaxPos ||
       p [0] > 21)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Connect-Request: Called Party Number not found or exceeds message length",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   if (ppCedPN != NULL)
   {
      *ppCedPN = p;
   }
   nCurrPos += 1 + p [0];
   p += p [0] + 1;
   
   /* check for calling party number */
   if (nCurrPos >= nMaxPos ||
       nCurrPos + 1 + p [0] > nMaxPos ||
       p [0] > 22)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Connect-Request: Calling Party Number not found or exceeds message length",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   if (ppCngPN != NULL)
   {
      *ppCngPN = p;
   }
   nCurrPos += 1 + p [0];
   p += p [0] + 1;
   
   /* check for called party subaddress */
   if (nCurrPos >= nMaxPos ||
       nCurrPos + 1 + p [0] > nMaxPos ||
       p [0] > 21)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Connect-Request: Called Party Subaddress not found or exceeds message length",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   if (ppCedPSa != NULL)
   {
      *ppCedPSa = p;
   }
   nCurrPos += 1 + p [0];
   p += p [0] + 1;
   
   /* check for calling party subaddress */
   if (nCurrPos >= nMaxPos ||
       nCurrPos + 1 + p [0] > nMaxPos ||
       p [0] > 21)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Connect-Request: Calling Party Subaddress not found or exceeds message length",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   if (ppCngPSa != NULL)
   {
      *ppCngPSa = p;
   }
   nCurrPos += 1 + p [0];
   p += p [0] + 1;
   
   /* check for B-protocol */
   if (nCurrPos >= nMaxPos ||
       nCurrPos + 1 + p [0] > nMaxPos)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Connect-Request: B-protocol not found or exceeds message length",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   if (pBProt != NULL)
   {
      uRes = daicplci_extract_bprotocol
                (pPortData, pPlciData,
                 (const CAPIBProtocol_t *) p, CAPI_REQUEST (C_CONNECT), pBProt);
      if (uRes != CAPI_OK)
      {
         return (uRes);
      }
   }
   nCurrPos += 1 + p [0];
   p += p [0] + 1;
   
   /* check for bearer capability */
   if (nCurrPos >= nMaxPos ||
       nCurrPos + 1 + p [0] > nMaxPos ||
       p [0] > 11)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Connect-Request: Bearer Capability not found or exceeds message length",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   if (ppBC != NULL)
   {
      if (p [0] > 0)
      {
         *ppBC = p;
      }
      else
      {
         *ppBC = daicplci_get_bc_from_cip
                    ((unsigned) C_GET_WORD (pCapiMsg->info.connect_req.wCip));
      }
   }
   nCurrPos += 1 + p [0];
   p += p [0] + 1;
   
   /* check for low layer compatibility */
   if (nCurrPos >= nMaxPos ||
       nCurrPos + 1 + p [0] > nMaxPos ||
       p [0] > 14)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Connect-Request: Low Layer Compatibility not found or exceeds message length",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   if (ppLLC != NULL)
   {
      *ppLLC = p;
   }
   nCurrPos += 1 + p [0];
   p += p [0] + 1;
   
   /* check for high layer compatibility */
   if (nCurrPos >= nMaxPos ||
       nCurrPos + 1 + p [0] > nMaxPos ||
       p [0] > 3)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Connect-Request: High Layer Compatibility not found or exceeds message length",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   if (ppHLC != NULL)
   {
      if (p [0] > 0)
      {
         *ppHLC = p;
      }
      else
      {
         *ppHLC = daicplci_get_hlc_from_cip
                     ((unsigned) C_GET_WORD (pCapiMsg->info.connect_req.wCip));
      }
   }
   nCurrPos += 1 + p [0];
   p += p [0] + 1;
   
   /* check for additional info */
   if (nCurrPos >= nMaxPos ||
       nCurrPos + 1 + p [0] > nMaxPos)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Connect-Request: Additional Info not found or exceeds message length",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   uRes = daicplci_extract_add_info_params
             (pPortData, pPlciData, p,
              ppBChnInfo, ppKeypad, ppUUData, ppFacility, ppSendingComplete);
   
   return (uRes);
} /* daicplci_extract_conn_req_params */





/**
 * Parse a Connect-Response CAPI message.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I: The PLCI data as the context for this
 *                                 operation.
 * @param pCapiMsg              I: The CAPI message to parse.
 * @param ppConnN               O: The address of the connected number.
 * @param ppConnSa              O: The address of the connected subaddress.
 * @param pBProt                O: The area to store the B-Protocol information.
 * @param ppLLC                 O: The address of the low layer compatibility.
 * @param ppBChnInfo            O: The address of the B-channel information.
 * @param ppKeypad              O: The address of the keypad facility.
 * @param ppUUData              O: The address of the user-user data.
 * @param ppFacility            O: The address of the facility data array.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicplci_extract_conn_rsp_params
   (DaicPortData_t       *pPortData,
    DaicPlciData_t       *pPlciData,
    const CAPIMsg_t      *pCapiMsg,
    const u_int8_t      **ppConnN,
    const u_int8_t      **ppConnSa,
    DaicBProtocolData_t  *pBProt,
    const u_int8_t      **ppLLC,
    const u_int8_t      **ppBChnInfo,
    const u_int8_t      **ppKeypad,
    const u_int8_t      **ppUUData,
    const u_int8_t      **ppFacility)
{
   const u_int8_t *p;
   size_t          nMaxPos;
   size_t          nCurrPos;
   unsigned        uRes;
   
   p = (const u_int8_t *) &(pCapiMsg->info.connect_resp.wReject) +
       sizeof (pCapiMsg->info.connect_resp.wReject);
   nMaxPos = (size_t) C_GET_WORD (pCapiMsg->head.wLen);
   nCurrPos = (size_t) (p - (const u_int8_t *) pCapiMsg);

   /* check for B-protocol */
   if (nCurrPos >= nMaxPos ||
       nCurrPos + 1 + p [0] > nMaxPos)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Connect-Response: B-protocol not found or exceeds message length",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   if (pBProt != NULL)
   {
      uRes = daicplci_extract_bprotocol
                (pPortData, pPlciData,
                 (const CAPIBProtocol_t *) p,
                 CAPI_RESPONSE (C_CONNECT), pBProt);
      if (uRes != CAPI_OK)
      {
         return (uRes);
      }
   }
   nCurrPos += 1 + p [0];
   p += p [0] + 1;
   
   /* check for connected number */
   if (nCurrPos >= nMaxPos ||
       nCurrPos + 1 + p [0] > nMaxPos ||
       p [0] > 22)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Connect-Response: Connected Number not found or exceeds message length",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   if (ppConnN != NULL)
   {
      *ppConnN = p;
   }
   nCurrPos += 1 + p [0];
   p += p [0] + 1;
   
   /* check for connected subaddress */
   if (nCurrPos >= nMaxPos ||
       nCurrPos + 1 + p [0] > nMaxPos ||
       p [0] > 21)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Connect-Response: Connected Subaddress not found or exceeds message length",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   if (ppConnSa != NULL)
   {
      *ppConnSa = p;
   }
   nCurrPos += 1 + p [0];
   p += p [0] + 1;
   
   /* check for low layer compatibility */
   if (nCurrPos >= nMaxPos ||
       nCurrPos + 1 + p [0] > nMaxPos ||
       p [0] > 14)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Connect-Response: Low Layer Compatibility not found or exceeds message length",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   if (ppLLC != NULL)
   {
      *ppLLC = p;
   }
   nCurrPos += 1 + p [0];
   p += p [0] + 1;
   
   /* check for additional info */
   if (nCurrPos >= nMaxPos ||
       nCurrPos + 1 + p [0] > nMaxPos)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Connect-Response: Additional Info not found or exceeds message length",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   uRes = daicplci_extract_add_info_params
             (pPortData, pPlciData, p,
              ppBChnInfo, ppKeypad, ppUUData, ppFacility, NULL);
   
   return (uRes);
} /* daicplci_extract_conn_rsp_params */





/**
 * Parse an Info-Request CAPI message.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I: The PLCI data as the context for this
 *                                 operation.
 * @param pCapiMsg              I: The CAPI message to parse.
 * @param ppCedPN               O: The address of the connected number.
 * @param ppBChnInfo            O: The address of the B-channel information.
 * @param ppKeypad              O: The address of the keypad facility.
 * @param ppUUData              O: The address of the user-user data.
 * @param ppFacility            O: The address of the facility data array.
 * @param ppSendingComplete     O: The address of the sending complete
 *                                 information element.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicplci_extract_info_req_params
   (DaicPortData_t   *pPortData,
    DaicPlciData_t   *pPlciData,
    const CAPIMsg_t  *pCapiMsg,
    const u_int8_t  **ppCedPN,
    const u_int8_t  **ppBChnInfo,
    const u_int8_t  **ppKeypad,
    const u_int8_t  **ppUUData,
    const u_int8_t  **ppFacility,
    const u_int8_t  **ppSendingComplete)
{
   const u_int8_t *p;
   size_t          nMaxPos;
   size_t          nCurrPos;
   unsigned        uRes;
   
   p = (const u_int8_t *) &(pCapiMsg->info.connect_resp.wReject) +
       sizeof (pCapiMsg->info.connect_resp.wReject);
   nMaxPos = (size_t) C_GET_WORD (pCapiMsg->head.wLen);
   nCurrPos = (size_t) (p - (const u_int8_t *) pCapiMsg);

   /* check for called party number */
   if (nCurrPos >= nMaxPos ||
       nCurrPos + 1 + p [0] > nMaxPos ||
       p [0] > 21)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Info-Request: Called Party Number not found or exceeds message length",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   if (ppCedPN != NULL)
   {
      *ppCedPN = p;
   }
   nCurrPos += 1 + p [0];
   p += p [0] + 1;
   
   /* check for additional info */
   if (nCurrPos >= nMaxPos ||
       nCurrPos + 1 + p [0] > nMaxPos)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Info-Request: Additional Info not found or exceeds message length",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   uRes = daicplci_extract_add_info_params
             (pPortData, pPlciData, p,
              ppBChnInfo, ppKeypad, ppUUData, ppFacility, ppSendingComplete);
   
   return (uRes);
} /* daicplci_extract_info_req_params */





/**
 * Parse a B-protocol settings structure of a CAPI message.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data as the context for this
 *                                 operation.
 * @param pBProtBuf             I: The B-protocol structure to parse.
 * @param uCmdContext           I: The CAPI message command as the context for
 *                                 setting the B-channel operation mode.
 * @param pBProtData            O: The destination for the parsed B-protocol
 *                                 settings.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicplci_extract_bprotocol
   (DaicPortData_t        *pPortData,
    DaicPlciData_t        *pPlciData,
    const CAPIBProtocol_t *pBProtBuf,
    unsigned               uCmdContext,
    DaicBProtocolData_t   *pBProtData)
{
   const u_int8_t *p;
   const u_int8_t *pEnd;
   unsigned        uRes;
   
   /* check for valid length of B-protocol structure */
   p = (const u_int8_t *) pBProtBuf;
   pEnd = p + C_GET_BYTE (pBProtBuf->bLength) + 1;
   if (C_GET_BYTE (pBProtBuf->bLength) == 0)
   {
      /* B-protocol structure of length 0 --> default protocols and
       * configuration
       */
      pBProtData->wB1Prot = CAPI_B1_HDLC_64;
      pBProtData->wB2Prot = CAPI_B2_ISO7776_X75_SLP;
      pBProtData->wB3Prot = CAPI_B3_TRANSPARENT;
      (void) daicplci_extract_b2config (pPortData, pPlciData, p, pBProtData);
      (void) daicplci_extract_b3config (pPortData, pPlciData, p, pBProtData);
      return (CAPI_OK);
   }
   if (C_GET_BYTE (pBProtBuf->bLength) <= 3 * sizeof (cWORD))
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: B-protocol settings shorter than 3 words",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }

   /* check B1 protocol */
   pBProtData->wB1Prot = C_GET_WORD (pBProtBuf->wB1protocol);
   switch (pBProtData->wB1Prot)
   {
      case CAPI_B1_HDLC_64:
      case CAPI_B1_TRANSPARENT_64:
         break;
         
      default:
         return (CSE_B1_PROTOCOL_NOT_SUPPORTED);
   }
   
   /* check B2 protocol */
   pBProtData->wB2Prot = C_GET_WORD (pBProtBuf->wB2protocol);
   switch (pBProtData->wB2Prot)
   {
      case CAPI_B2_ISO7776_X75_SLP:
      case CAPI_B2_TRANSPARENT:
      case CAPI_B2_SDLC:
         break;
         
      default:
         return (CSE_B2_PROTOCOL_NOT_SUPPORTED);
   }
   
   /* check B3 protocol */
   pBProtData->wB3Prot = C_GET_WORD (pBProtBuf->wB3protocol);
   switch (pBProtData->wB3Prot)
   {
      case CAPI_B3_TRANSPARENT:
      case CAPI_B3_T90NL:
      case CAPI_B3_ISO8208:
         break;
         
      default:
         return (CSE_B3_PROTOCOL_NOT_SUPPORTED);
   }
   
   /* check for valid protocol combinations */
   if (pBProtData->wB3Prot != CAPI_B3_TRANSPARENT)
   {
      if (pBProtData->wB1Prot != CAPI_B1_HDLC_64 ||
          pBProtData->wB2Prot != CAPI_B2_ISO7776_X75_SLP)
      {
         return (CSE_INVALID_B_PROT_COMBINATION);
      }
   }
   if (pBProtData->wB2Prot != CAPI_B2_TRANSPARENT)
   {
      if (pBProtData->wB1Prot != CAPI_B1_HDLC_64)
      {
         return (CSE_INVALID_B_PROT_COMBINATION);
      }
   }
   
   p = (const u_int8_t *) &(pBProtBuf->wB3protocol) +
       sizeof (pBProtBuf->wB3protocol);
   
   /* B1 configuration */
   if (p >= pEnd || p [0] == 0)
   {
      /* default configuration, no settings needed */
   }
   else if (p [0] > 0)
   {
      /* B1-configuration block specified, not supported for transparent or HDLC
       * protocol
       */
      return (CSE_INVALID_B1_PARAMETER);
   }
   if (p < pEnd)
   {
      p += 1 + p [0];
   }
   
   /* B2 configuration */
   if (p >= pEnd || p [0] == 0)
   {
      u_int8_t b = 0;
      (void) daicplci_extract_b2config (pPortData, pPlciData, &b, pBProtData);
   }
   else if (p + 1 + p [0] > pEnd)
   {
      return (CSE_INVALID_B2_PARAMETER);
   }
   else
   {
      uRes = daicplci_extract_b2config (pPortData, pPlciData, p, pBProtData);
      if (uRes != CAPI_OK)
      {
         return (uRes);
      }
   }

   /* B3 configuration */
   if (p >= pEnd || p [0] == 0)
   {
      u_int8_t b = 0;
      (void) daicplci_extract_b3config (pPortData, pPlciData, &b, pBProtData);
   }
   else if (p + 1 + p [0] > pEnd)
   {
      return (CSE_INVALID_B3_PARAMETER);
   }
   else
   {
      uRes = daicplci_extract_b3config (pPortData, pPlciData, p, pBProtData);
      if (uRes != CAPI_OK)
      {
         return (uRes);
      }
   }
   
   /* B-channel operation */
   if (p >= pEnd || p [0] == 0)
   {
      /* default B-channel operation --> determine from CAPI message context */
      if (uCmdContext == CAPI_REQUEST (C_CONNECT))
      {
         pBProtData->wBChnOp = CAPI_BPROT_GLOBOPT_BCHNOP_DTE;
      }
      else if (uCmdContext == CAPI_RESPONSE (C_CONNECT))
      {
         pBProtData->wBChnOp = CAPI_BPROT_GLOBOPT_BCHNOP_DCE;
      }
      /* else it must be a Select-B-Protocol-Request --> leave the setting from
       * the original Connect-Request or -Response
       */
   }
   else if (p + 1 + p [0] > pEnd ||
            p [0] < sizeof (cWORD))
   {
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   else
   {
      const CAPIBProtocolGlobalOptions_t *pBChnOp;
      
      pBChnOp = (const CAPIBProtocolGlobalOptions_t *) p;
      pBProtData->wBChnOp = C_GET_WORD (pBChnOp->wBChannelOperation);
      if (pBProtData->wBChnOp == CAPI_BPROT_GLOBOPT_BCHNOP_DEFAULT)
      {
         if (uCmdContext == CAPI_REQUEST (C_CONNECT))
         {
            pBProtData->wBChnOp = CAPI_BPROT_GLOBOPT_BCHNOP_DTE;
         }
         else if (uCmdContext == CAPI_RESPONSE (C_CONNECT))
         {
            pBProtData->wBChnOp = CAPI_BPROT_GLOBOPT_BCHNOP_DCE;
         }
         /* else it must be a Select-B-Protocol-Request --> leave the setting
          * from the original Connect-Request or -Response
          */
      }
      else if (pBProtData->wBChnOp != CAPI_BPROT_GLOBOPT_BCHNOP_DTE &&
               pBProtData->wBChnOp != CAPI_BPROT_GLOBOPT_BCHNOP_DCE)
      {
         return (CCE_ILLEGAL_MSG_PARAMETER);
      }
   }
   
   return (CAPI_OK);
} /* daicplci_extract_bprotocol */





/**
 * Parse a B2-configuration structure.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data as the context for this
 *                                 operation.
 * @param pB2CfgBuf             I: The B2-configuration structure to parse.
 * @param pBProtData            O: The destination for the parsed B-protocol
 *                                 settings.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicplci_extract_b2config
   (DaicPortData_t      *pPortData,
    DaicPlciData_t      *pPlciData,
    const u_int8_t      *pB2CfgBuf,
    DaicBProtocolData_t *pBProtData)
{
   switch (pBProtData->wB2Prot)
   {
      case CAPI_B2_ISO7776_X75_SLP:
         {
            const CAPIB2ConfigX75Slp_t *p =
               (const CAPIB2ConfigX75Slp_t *) pB2CfgBuf;
            
            if (C_GET_BYTE (p->bLength) == 0)
            {
               pBProtData->b2Cfg.bAddrA      = 3;
               pBProtData->b2Cfg.bAddrB      = 1;
               pBProtData->b2Cfg.bModuloMode = 8;
               pBProtData->b2Cfg.bWindowSize = 7;
            }
            else if (C_GET_BYTE (p->bLength) <
                        sizeof (*p) - sizeof (p->bLength))
            {
               return (CSE_INVALID_B2_PARAMETER);
            }
            else
            {
               pBProtData->b2Cfg.bAddrA      = C_GET_BYTE (p->bAddressA);
               pBProtData->b2Cfg.bAddrB      = C_GET_BYTE (p->bAddressB);
               pBProtData->b2Cfg.bModuloMode = C_GET_BYTE (p->bModuloMode);
               pBProtData->b2Cfg.bWindowSize = C_GET_BYTE (p->bWindowSize);
            }
         }
         break;

      case CAPI_B2_TRANSPARENT:
         /* no settings allowed / supported for transparent protocol */
         if (pB2CfgBuf [0] != 0)
         {
            return (CSE_INVALID_B2_PARAMETER);
         }
         break;
      
      case CAPI_B2_SDLC:
         {
            const CAPIB2ConfigSdlc_t *p =
               (const CAPIB2ConfigSdlc_t *) pB2CfgBuf;
            
            if (C_GET_BYTE (p->bLength) == 0)
            {
               pBProtData->b2Cfg.bAddrA      = 0xC1;
               pBProtData->b2Cfg.bAddrB      = 0;
               pBProtData->b2Cfg.bModuloMode = 8;
               pBProtData->b2Cfg.bWindowSize = 7;
            }
            else if (C_GET_BYTE (p->bLength) <
                        sizeof (*p) - sizeof (p->bLength))
            {
               return (CSE_INVALID_B2_PARAMETER);
            }
            else
            {
               pBProtData->b2Cfg.bAddrA      = C_GET_BYTE (p->bAddress);
               pBProtData->b2Cfg.bAddrB      = C_GET_BYTE (p->bReserved);
               pBProtData->b2Cfg.bModuloMode = C_GET_BYTE (p->bModuloMode);
               pBProtData->b2Cfg.bWindowSize = C_GET_BYTE (p->bWindowSize);
            }
         }
         break;
      
      default:
         return (CSE_B2_PROTOCOL_NOT_SUPPORTED);
   }

   return (CAPI_OK);
} /* daicplci_extract_b2config */





/**
 * Parse a B3-configuration structure.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data as the context for this
 *                                 operation.
 * @param pB3CfgBuf             I: The B3-configuration structure to parse.
 * @param pBProtData            O: The destination for the parsed B-protocol
 *                                 settings.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicplci_extract_b3config
   (DaicPortData_t      *pPortData,
    DaicPlciData_t      *pPlciData,
    const u_int8_t      *pB3CfgBuf,
    DaicBProtocolData_t *pBProtData)
{
   switch (pBProtData->wB3Prot)
   {
      case CAPI_B3_TRANSPARENT:
         /* no settings allowed / supported for transparent protocol */
         if (pB3CfgBuf [0] != 0)
         {
            return (CSE_INVALID_B3_PARAMETER);
         }
         break;
      
      case CAPI_B3_T90NL:
      case CAPI_B3_ISO8208:
         {
            const CAPIB3ConfigT90NL_t *p =
               (const CAPIB3ConfigT90NL_t *) pB3CfgBuf;
            
            if (C_GET_BYTE (p->bLength) == 0)
            {
               pBProtData->b3Cfg.wLIC        = 0;
               pBProtData->b3Cfg.wHIC        = 0;
               pBProtData->b3Cfg.wLTC        = 1;
               pBProtData->b3Cfg.wHTC        = 1;
               pBProtData->b3Cfg.wLOC        = 0;
               pBProtData->b3Cfg.wHOC        = 0;
               pBProtData->b3Cfg.wModuloMode = 8;
               pBProtData->b3Cfg.wWindowSize = 2;
            }
            else if (C_GET_BYTE (p->bLength) <
                        sizeof (*p) - sizeof (p->bLength))
            {
               return (CSE_INVALID_B3_PARAMETER);
            }
            else
            {
               pBProtData->b3Cfg.wLIC        = C_GET_WORD (p->wLIC);
               pBProtData->b3Cfg.wHIC        = C_GET_WORD (p->wHIC);
               pBProtData->b3Cfg.wLTC        = C_GET_WORD (p->wLTC);
               pBProtData->b3Cfg.wHTC        = C_GET_WORD (p->wHTC);
               pBProtData->b3Cfg.wLOC        = C_GET_WORD (p->wLOC);
               pBProtData->b3Cfg.wHOC        = C_GET_WORD (p->wHOC);
               pBProtData->b3Cfg.wModuloMode = C_GET_WORD (p->wModuloMode);
               pBProtData->b3Cfg.wWindowSize = C_GET_WORD (p->wWindowSize);
            }
         }
         break;

      default:
         return (CSE_B3_PROTOCOL_NOT_SUPPORTED);
   }

   return (CAPI_OK);
} /* daicplci_extract_b3config */





/**
 * Parse an Additional Info structure of a CAPI message.
 *
 * @pre All output parameters must be initialized to NULL when calling this
 *      function.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I: The PLCI data as the context for this
 *                                 operation.
 * @param pAddInfo              I: The additional info structure to parse.
 * @param ppBChnInfo            O: The address of the B-channel information.
 * @param ppKeypad              O: The address of the keypad facility.
 * @param ppUUData              O: The address of the user-user data.
 * @param ppFacility            O: The address of the facility data array.
 * @param ppSendingComplete     O: The address of the sending complete
 *                                 information element.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicplci_extract_add_info_params
   (DaicPortData_t  *pPortData,
    DaicPlciData_t  *pPlciData,
    const u_int8_t  *pAddInfo,
    const u_int8_t **ppBChnInfo,
    const u_int8_t **ppKeypad,
    const u_int8_t **ppUUData,
    const u_int8_t **ppFacility,
    const u_int8_t **ppSendingComplete)
{
   const u_int8_t *p;
   const u_int8_t *pEnd;
   
   p = pAddInfo;
   pEnd = p + 1 + p [0];
   if (p + 1 >= pEnd)
   {
      /* empty structure, leave all parameters initialized to NULL */
      return (CAPI_OK);
   }
   ++p;

   /* check for B-channel information */
   if (p < pEnd && p [0] > 0)
   {
      const CAPIBChannelInfo_t *pTmp = (const CAPIBChannelInfo_t *) p;

      if (p + 1 + p [0] > pEnd)
      {
         return (CCE_ILLEGAL_MSG_PARAMETER);
      }
      if (C_GET_BYTE (pTmp->bLength) <= sizeof (cWORD) + 1)
      {
         return (CCE_ILLEGAL_MSG_PARAMETER);
      }
      /* Note: Only the B-channel identification element is supported. */
      if (C_GET_WORD (pTmp->wChannel) == CAPI_BCHAN_MODE_CHANNEL_ID)
      {
         if (ppBChnInfo != NULL)
         {
            *ppBChnInfo = p;
         }
      }
      p += 1 + p [0];
   }
   
   /* check for keypad facility */
   if (p < pEnd && p [0] > 0)
   {
      if (p + 1 + p [0] > pEnd)
      {
         return (CCE_ILLEGAL_MSG_PARAMETER);
      }
      if (p [0] > 32)
      {
         return (CCE_ILLEGAL_MSG_PARAMETER);
      }
      if (ppKeypad != NULL)
      {
         *ppKeypad = p;
      }
      p += 1 + p [0];
   }

   /* check for user-user data */
   if (p < pEnd && p [0] > 0)
   {
      if (p + 1 + p [0] > pEnd)
      {
         return (CCE_ILLEGAL_MSG_PARAMETER);
      }
      if (p [0] > 129)
      {
         return (CCE_ILLEGAL_MSG_PARAMETER);
      }
      if (ppUUData != NULL)
      {
         *ppUUData = p;
      }
      p += 1 + p [0];
   }

   /* check for facility data array */
   if (p < pEnd && p [0] > 0)
   {
      if (p + 1 + p [0] > pEnd)
      {
         return (CCE_ILLEGAL_MSG_PARAMETER);
      }
      if (ppFacility != NULL)
      {
         *ppFacility = p;
      }
      p += 1 + p [0];
   }

   /* check for sending complete */
   if (p < pEnd && p [0] > 0)
   {
      if (p + 1 + p [0] > pEnd)
      {
         return (CCE_ILLEGAL_MSG_PARAMETER);
      }
      if (ppSendingComplete != NULL)
      {
         *ppSendingComplete = p;
      }
      p += 1 + p [0];
   }

   return (CAPI_OK);
} /* daicplci_extract_add_info_params */





/**
 * Create CAPI Info-Indications for information elements in a controller
 * indication.
 *
 * All information elements in the specified buffer are checked against the CAPI
 * Info-Mask also specified. If the application shall receive an Info-Indication
 * for the information element, a CAPI message is created.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data as a connection context for
 *                                 this operation.
 * @param bMsgType              I: This value reflects the D-channel message
 *                                 type, the information elements in pabIEBuffer
 *                                 belong to. If it is null, the corresponding
 *                                 message type is unknown.
 * @param pabIEBuffer           I: The buffer containing the information
 *                                 elements to examine.
 * @param nLenIEBuffer          I: The length in bytes of the buffer pabIEBuffer.
 * @param uApplID               I: The application id to receive the CAPI
 *                                 Info-Indications.
 * @param dwInfoMask            I: The CAPI Info-Mask to check.
 *
 * @return Nothing.
 */

static void daicplci_create_capi_info_ind
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    u_int8_t        bMsgType,
    const u_int8_t *pabIEBuffer,
    size_t          nLenIEBuffer,
    unsigned        uApplID,
    u_int32_t       dwInfoMask)
{
   const u_int8_t *p;
   const u_int8_t *pEnd;
   unsigned        uIE;
   size_t          nLen;
   unsigned        uCurrCS;
   unsigned        uNextCS;
   
   p = pabIEBuffer;
   pEnd = pabIEBuffer + nLenIEBuffer;
   uCurrCS = 0;
   uNextCS = 0;
   while (p < pEnd)
   {
      /* determine the element and check for valid length */
      uIE = (unsigned) *p;
      if (uIE == 0)
      {
         /* mark for end-of-list */
         break;
      }
      if ((uIE & 0x80) == 0)
      {
         /* variable length information element, length byte should follow */
         if (p + 1 >= pEnd)
         {
            DBG (LOG_ERROR, pPortData->iUnit,
                 "Port %u: PLCI %u: State %d: Multibyte IE 0x%02X not followed by length byte",
                 pPortData->uPortIdx, pPlciData->uPlci,
                 (int) (pPlciData->state), uIE);
            break;
         }
         nLen = p [1];
         if (p + 2 + nLen >= pEnd)
         {
            DBG (LOG_ERROR, pPortData->iUnit,
                 "Port %u: PLCI %u: State %d: Length %zu of multibyte IE 0x%02X exceeds buffer limit",
                 pPortData->uPortIdx, pPlciData->uPlci,
                 (int) (pPlciData->state), nLen, uIE);
            break;
         }
         p += 2;
      }
      else
      {
         /* single octet information element */
         nLen = 0;
         ++p;
      }
      
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got IE 0x%02X, length %zu, current code set %u",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           uIE, nLen, uCurrCS);

      /* if the information element is "escape for extension", the real
       * information element follows the length byte and the length must be
       * updated
       */
      if (uCurrCS == 0 && uIE == DAIC_IE_ESC && nLen > 0)
      {
         uIE = *(p++);
         --nLen;
         
         DBG (LOG_DEBUG, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: Escape for extension contains IE 0x%02X, length now %zu",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              uIE, nLen);
      }
      
      if ((uIE & 0xF0) == 0x90)
      {
         /* code set shift */
         uCurrCS = (uIE & 0x07);
         if ((uIE & 0x08) == 0)
         {
            /* locking code set shift, do not switch back after evaluating next
             * IE
             */
            uNextCS = uCurrCS;
         }
      }
      else if (uCurrCS == 0)
      {
         switch (uIE)
         {
            case DAIC_IE_CAU:
               if ((dwInfoMask & CAPI_INFO_MASK_CAUSE) != 0)
               {
                  daicplci_create_capi_info_ind_for_ie
                     (pPortData, pPlciData, uApplID, uIE, nLen, p);
               }
               break;
               
            case DAIC_IE_CHI:
               if (bMsgType != DAIC_MT_SETUP &&
                   (dwInfoMask & CAPI_INFO_MASK_CHANNEL_ID) != 0)
               {
                  daicplci_create_capi_info_ind_for_ie
                     (pPortData, pPlciData, uApplID, uIE, nLen, p);
               }
               break;

            case DAIC_IE_FAC:
            case DAIC_IE_NFA:
               if (bMsgType != DAIC_MT_SETUP &&
                   (dwInfoMask & CAPI_INFO_MASK_FACILITY) != 0)
               {
                  daicplci_create_capi_info_ind_for_ie
                     (pPortData, pPlciData, uApplID, uIE, nLen, p);
               }
               break;
               
            case DAIC_IE_PRO:
            case DAIC_IE_NOI:
               if ((dwInfoMask & CAPI_INFO_MASK_CALL_PROGRESSION) != 0)
               {
                  daicplci_create_capi_info_ind_for_ie
                     (pPortData, pPlciData, uApplID, uIE, nLen, p);
               }
               break;
            
            case DAIC_IE_DIS:
               if ((dwInfoMask & CAPI_INFO_MASK_DISPLAY) != 0)
               {
                  daicplci_create_capi_info_ind_for_ie
                     (pPortData, pPlciData, uApplID, uIE, nLen, p);
               }
               break;
            
            case DAIC_IE_DT:
               if ((dwInfoMask & CAPI_INFO_MASK_DATE_TIME) != 0)
               {
                  daicplci_create_capi_info_ind_for_ie
                     (pPortData, pPlciData, uApplID, uIE, nLen, p);
               }
               break;
            
            case DAIC_IE_KEY:
               if (bMsgType != DAIC_MT_SETUP &&
                   (dwInfoMask &
                    (CAPI_INFO_MASK_USER_USER | CAPI_INFO_MASK_FACILITY)) != 0)
               {
                  daicplci_create_capi_info_ind_for_ie
                     (pPortData, pPlciData, uApplID, uIE, nLen, p);
               }
               break;
            
            case DAIC_IE_CPN:
            case DAIC_IE_DSA:
               if ((dwInfoMask & CAPI_INFO_MASK_CALLED_PARTY_NUMBER) != 0)
               {
                  daicplci_create_capi_info_ind_for_ie
                     (pPortData, pPlciData, uApplID, uIE, nLen, p);
               }
               break;
            
            case DAIC_IE_RDNG:
            case DAIC_IE_RDON:
               if ((dwInfoMask & CAPI_INFO_MASK_REDIRECTION_INFO) != 0)
               {
                  daicplci_create_capi_info_ind_for_ie
                     (pPortData, pPlciData, uApplID, uIE, nLen, p);
               }
               break;
            
            case DAIC_IE_MT:
               if (nLen >= 1 &&
                   (dwInfoMask & CAPI_INFO_MASK_CALL_PROGRESSION) != 0)
               {
                  if (nLen >= 2 && p [0] >= 1)
                  {
                     daicplci_create_capi_info_ind_for_mt
                        (pPortData, pPlciData, uApplID, (unsigned) (p [1]));
                  }
                  else
                  {
                     daicplci_create_capi_info_ind_for_mt
                        (pPortData, pPlciData, uApplID, (unsigned) (p [0]));
                  }
               }
               break;

            case DAIC_IE_UUI:
               if (bMsgType != DAIC_MT_SETUP &&
                   (dwInfoMask & CAPI_INFO_MASK_USER_USER) != 0)
               {
                  daicplci_create_capi_info_ind_for_ie
                     (pPortData, pPlciData, uApplID, uIE, nLen, p);
               }
               break;
            
            case DAIC_IE_ESC:
               if (nLen >= 3)
               {
                  DBG (LOG_DEBUG, pPortData->iUnit,
                       "Port %u: PLCI %u: State %d: Got Escape for extension, IE 0x%02X, 1st byte 0x%02X, 2nd byte 0x%02X",
                       pPortData->uPortIdx, pPlciData->uPlci,
                       (int) (pPlciData->state),
                       (unsigned) p [0], (unsigned) p [1], (unsigned) p [2]);
               }
               else if (nLen >= 2)
               {
                  DBG (LOG_DEBUG, pPortData->iUnit,
                       "Port %u: PLCI %u: State %d: Got Escape for extension, IE 0x%02X, 1st byte 0x%02X",
                       pPortData->uPortIdx, pPlciData->uPlci,
                       (int) (pPlciData->state),
                       (unsigned) p [0], (unsigned) p [1]);
               }
               else if (nLen >= 1)
               {
                  DBG (LOG_DEBUG, pPortData->iUnit,
                       "Port %u: PLCI %u: State %d: Got Escape for extension, IE 0x%02X",
                       pPortData->uPortIdx, pPlciData->uPlci,
                       (int) (pPlciData->state), (unsigned) p [0]);
               }
               break;
               
            case DAIC_IE_SCP:
               if ((dwInfoMask & CAPI_INFO_MASK_SENDING_COMPLETE) != 0)
               {
                  daicplci_create_capi_info_ind_for_ie
                     (pPortData, pPlciData, uApplID, uIE, nLen, p);
               }
               break;
            
            default:
               if (nLen >= 3)
               {
                  DBG (LOG_DEBUG, pPortData->iUnit,
                       "Port %u: PLCI %u: State %d: Got unhandled IE 0x%02X, data 0x%02X, 0x%02X, 0x%02X",
                       pPortData->uPortIdx, pPlciData->uPlci,
                       (int) (pPlciData->state), uIE,
                       (unsigned) p [0], (unsigned) p [1], (unsigned) p [2]);
               }
               else if (nLen >= 2)
               {
                  DBG (LOG_DEBUG, pPortData->iUnit,
                       "Port %u: PLCI %u: State %d: Got unhandled IE 0x%02X, data 0x%02X, 0x%02X",
                       pPortData->uPortIdx, pPlciData->uPlci,
                       (int) (pPlciData->state), uIE,
                       (unsigned) p [0], (unsigned) p [1]);
               }
               else if (nLen >= 1)
               {
                  DBG (LOG_DEBUG, pPortData->iUnit,
                       "Port %u: PLCI %u: State %d: Got unhandled IE 0x%02X, data 0x%02X",
                       pPortData->uPortIdx, pPlciData->uPlci,
                       (int) (pPlciData->state), uIE, (unsigned) p [0]);
               }
               else
               {
                  DBG (LOG_DEBUG, pPortData->iUnit,
                       "Port %u: PLCI %u: State %d: Got unhandled IE 0x%02X, no data",
                       pPortData->uPortIdx, pPlciData->uPlci,
                       (int) (pPlciData->state), uIE);
               }
               break;
         }
         uCurrCS = uNextCS;
      }
      else if (uCurrCS == 6)
      {
         switch (uIE)
         {
            case DAIC_IE_CIF:
               /* must translate charging information from decimal ASCII
                * representation to a little endian binary dword
                */
               {
                  CAPIMsg_t      *pCapiInd;
                  struct mbuf    *pmbInd;
                  int             i;
                  u_int32_t       dw;
                  u_int8_t       *q;
                  
                  /* prepare an Info-Indication mbuf */
                  pmbInd = kcapi_get_mbuf (sizeof (CAPIMsgHead_t) +
                                           sizeof (CAPIInfoInd_t) +
                                           sizeof (u_int8_t) +
                                           sizeof (cDWORD));
                  if (pmbInd == NULL)
                  {
                     DBG (LOG_ERROR, pPortData->iUnit,
                          "Port %u: PLCI %u: State %d: Out of mbufs for Info-Indication for charging information",
                          pPortData->uPortIdx, pPlciData->uPlci,
                          (int) (pPlciData->state));
                     break;
                  }

                  for (i = 0, dw = 0; (size_t) i < (size_t) (p [1]); ++i)
                  {
                     dw = (dw * 10) + (u_int32_t) (p [i + 2]);
                  }

                  pCapiInd = mtod (pmbInd, CAPIMsg_t *);
                  C_PUT_WORD (pCapiInd->head.wApp, uApplID);
                  C_PUT_WORD (pCapiInd->head.wCmd, CAPI_INDICAT (C_INFO_20));
                  C_PUT_WORD (pCapiInd->head.wNum, pPortData->uCapiIndMsgNum);
                  ++(pPortData->uCapiIndMsgNum);
                  C_PUT_DWORD (pCapiInd->head.dwCid, pPlciData->dwCid);
                  C_PUT_WORD (pCapiInd->info.info_ind.wInfoNum, 0x4000);
                  q = (u_int8_t *) &(pCapiInd->info.info_ind.wInfoNum) +
                      sizeof (pCapiInd->info.info_ind.wInfoNum);
                  *(q++) = 4;
                  *(q++) = (dw & 0xFF);
                  *(q++) = (dw & 0xFF00) >> 8;
                  *(q++) = (dw & 0xFF0000) >> 16;
                  *(q++) = (dw & 0xFF000000) >> 24;
                  C_PUT_WORD (pCapiInd->head.wLen,
                              (size_t) (q - (u_int8_t *) pCapiInd));

                  DBG (LOG_DEBUG, pPortData->iUnit,
                       "Port %u: PLCI %u: State %d: Send Info-Indication with charging units %lu to application %u",
                       pPortData->uPortIdx, pPlciData->uPlci,
                       (int) (pPlciData->state), (unsigned long) dw, uApplID);

                  mtx_unlock (&(pPortData->pSc->mtxAccess));
                  kcapi_ctlr_receive_capi_message
                     (pPortData->uUniqueCapiCtlrNum, uApplID, pmbInd);
                  mtx_lock (&(pPortData->pSc->mtxAccess));
               }
               break;
               
            default:
               break;
         }
         uCurrCS = uNextCS;
      }
      
      p += nLen;
   }
   
   if (bMsgType != 0)
   {
      daicplci_create_capi_info_ind_for_mt
         (pPortData, pPlciData, uApplID, bMsgType);
   }
   
} /* daicplci_create_capi_info_ind */





/**
 * Create a CAPI Info-Indication for an information element.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data as a connection context for
 *                                 this operation.
 * @param uApplID               I: The application id to receive the CAPI
 *                                 Info-Indications.
 * @param uIE                   I: The information element to report.
 * @param nIEDataLen            I: The length of the data part of the
 *                                 information element. If the element does not
 *                                 contain a data part, this value may be null.
 * @param pbIEData              I: The buffer containing the data part of the
 *                                 information element after the length byte. If
 *                                 the information element does not contain any
 *                                 data (nIEDataLen is null), this pointer is
 *                                 ignored.
 *
 * @return Nothing.
 */

static void daicplci_create_capi_info_ind_for_ie
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uApplID,
    unsigned        uIE,
    size_t          nIEDataLen,
    const u_int8_t *pbIEData)
{
   CAPIMsg_t   *pCapiInd;
   struct mbuf *pmbInd;
   u_int8_t    *q;

   /* determine the length of the info element structure */
   if ((uIE & 0x80) != 0 || pbIEData == NULL)
   {
      nIEDataLen = 0;
   }
   
   /* prepare an Info-Indication mbuf */
   pmbInd = kcapi_get_mbuf (sizeof (CAPIMsgHead_t) +
                            sizeof (CAPIInfoInd_t) +
                            nIEDataLen + 1);
   if (pmbInd == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for Info-Indication for IE 0x%02X",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           uIE);
      return;
   }

   pCapiInd = mtod (pmbInd, CAPIMsg_t *);
   C_PUT_WORD (pCapiInd->head.wApp, uApplID);
   C_PUT_WORD (pCapiInd->head.wCmd, CAPI_INDICAT (C_INFO_20));
   C_PUT_WORD (pCapiInd->head.wNum, pPortData->uCapiIndMsgNum);
   ++(pPortData->uCapiIndMsgNum);
   C_PUT_DWORD (pCapiInd->head.dwCid, pPlciData->dwCid);
   C_PUT_WORD (pCapiInd->info.info_ind.wInfoNum, uIE);
   q = (u_int8_t *) &(pCapiInd->info.info_ind.wInfoNum) +
       sizeof (pCapiInd->info.info_ind.wInfoNum);
   if (nIEDataLen > 0)
   {
      *(q++) = (u_int8_t) nIEDataLen;
      bcopy (pbIEData, q, nIEDataLen);
      q += nIEDataLen;
   }
   else
   {
      *(q++) = 0;
   }
   C_PUT_WORD (pCapiInd->head.wLen, (size_t) (q - (u_int8_t *) pCapiInd));

   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Send Info-Indication with IE 0x%02X, data length %zu to application %u",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        uIE, nIEDataLen, uApplID);

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

} /* daicplci_create_capi_info_ind_for_ie */





/**
 * Create a CAPI Info-Indication for a Q.931 message type.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data as a connection context for
 *                                 this operation.
 * @param uApplID               I: The application id to receive the CAPI
 *                                 Info-Indications.
 * @param uMsgType              I: The message type to report.
 *
 * @return Nothing.
 */

static void daicplci_create_capi_info_ind_for_mt
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uApplID,
    unsigned        uMsgType)
{
   CAPIMsg_t   *pCapiInd;
   struct mbuf *pmbInd;
   u_int8_t    *q;

   /* prepare an Info-Indication mbuf */
   pmbInd = kcapi_get_mbuf (sizeof (CAPIMsgHead_t) +
                            sizeof (CAPIInfoInd_t) + 1);
   if (pmbInd == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for Info-Indication for network event 0x%02X",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           uMsgType);
      return;
   }

   pCapiInd = mtod (pmbInd, CAPIMsg_t *);
   C_PUT_WORD (pCapiInd->head.wApp, uApplID);
   C_PUT_WORD (pCapiInd->head.wCmd, CAPI_INDICAT (C_INFO_20));
   C_PUT_WORD (pCapiInd->head.wNum, pPortData->uCapiIndMsgNum);
   ++(pPortData->uCapiIndMsgNum);
   C_PUT_DWORD (pCapiInd->head.dwCid, pPlciData->dwCid);
   C_PUT_WORD (pCapiInd->info.info_ind.wInfoNum, 0x8000 | uMsgType);
   q = (u_int8_t *) &(pCapiInd->info.info_ind.wInfoNum) +
       sizeof (pCapiInd->info.info_ind.wInfoNum);
   *(q++) = 0;
   C_PUT_WORD (pCapiInd->head.wLen, (size_t) (q - (u_int8_t *) pCapiInd));

   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Send Info-Indication with message type 0x%02X to application %u",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        uMsgType, uApplID);

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

} /* daicplci_create_capi_info_ind_for_mt */





/**
 * Parse an information element buffer for Connect-Indication specific data.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data as a connection context for
 *                                 this operation.
 * @param pabIEBuffer           I: The buffer containing the information
 *                                 elements to examine.
 * @param nLenIEBuffer          I: The length in bytes of the buffer pabIEBuffer.
 * @param ppCedPN               O: The address of the called party number.
 * @param ppCngPN               O: The address of the calling party number.
 * @param ppCedPSa              O: The address of the called party subaddress.
 * @param ppCngPSa              O: The address of the calling party subaddress.
 * @param ppBC                  O: The address of the bearer capability.
 * @param ppLLC                 O: The address of the low layer compatibility.
 * @param ppHLC                 O: The address of the high layer compatibility.
 * @param ppBChnInfo            O: The address of the B-channel information.
 * @param ppKeypad              O: The address of the keypad facility.
 * @param ppUUData              O: The address of the user-user data.
 * @param ppFacility            O: The address of the facility data array.
 *
 * @return Nothing.
 */

static void daicplci_extract_conn_ind_info
   (DaicPortData_t  *pPortData,
    DaicPlciData_t  *pPlciData,
    const u_int8_t  *pabIEBuffer,
    size_t           nLenIEBuffer,
    const u_int8_t **ppCedPN,
    const u_int8_t **ppCngPN,
    const u_int8_t **ppCedPSa,
    const u_int8_t **ppCngPSa,
    const u_int8_t **ppBC,
    const u_int8_t **ppLLC,
    const u_int8_t **ppHLC,
    const u_int8_t **ppBChnInfo,
    const u_int8_t **ppKeypad,
    const u_int8_t **ppUUData,
    const u_int8_t **ppFacility)
{
   const u_int8_t *p;
   const u_int8_t *pEnd;
   unsigned        uIE;
   size_t          nLen;
   unsigned        uCurrCS;
   unsigned        uNextCS;
   
   p = pabIEBuffer;
   pEnd = pabIEBuffer + nLenIEBuffer;
   uCurrCS = 0;
   uNextCS = 0;
   while (p < pEnd)
   {
      /* determine and check for valid length */
      uIE = (unsigned) *p;
      if (uIE == 0)
      {
         /* mark for end-of-list */
         break;
      }
      if ((uIE & 0x80) == 0)
      {
         /* variable length information element, length byte should follow */
         if (p + 1 >= pEnd)
         {
            DBG (LOG_ERROR, pPortData->iUnit,
                 "Port %u: PLCI %u: State %d: Multibyte IE 0x%02X not followed by length byte",
                 pPortData->uPortIdx, pPlciData->uPlci,
                 (int) (pPlciData->state), uIE);
            break;
         }
         nLen = p [1];
         if (p + 2 + nLen >= pEnd)
         {
            DBG (LOG_ERROR, pPortData->iUnit,
                 "Port %u: PLCI %u: State %d: Length %zu of multibyte IE 0x%02X exceeds buffer limit",
                 pPortData->uPortIdx, pPlciData->uPlci,
                 (int) (pPlciData->state), nLen, uIE);
            break;
         }
      }
      else
      {
         /* single octet information element */
         nLen = 0;
      }
      ++p;
      
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got IE 0x%02X, length %zu, current code set %u",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           uIE, nLen, uCurrCS);

      if ((uIE & 0xF0) == 0x90)
      {
         /* code set shift */
         uCurrCS = (uIE & 0x07);
         if ((uIE & 0x08) == 0)
         {
            /* locking code set shift, do not switch back after evaluating next
             * IE
             */
            uNextCS = uCurrCS;
         }
      }
      else if (uCurrCS == 0)
      {
         switch (uIE)
         {
            case DAIC_IE_BC:
               if (ppBC != NULL)
               {
                  *ppBC = p;
               }
               break;
               
            case DAIC_IE_CHI:
               if (ppBChnInfo != NULL)
               {
                  *ppBChnInfo = p;
               }
               break;

            case DAIC_IE_FAC:
               if (ppFacility != NULL)
               {
                  *ppFacility = p;
               }
               break;
               
            case DAIC_IE_KEY:
               if (ppKeypad != NULL)
               {
                  *ppKeypad = p;
               }
               break;
            
            case DAIC_IE_OAD:
               if (ppCngPN != NULL)
               {
                  *ppCngPN = p;
               }
               break;
               
            case DAIC_IE_OSA:
               if (ppCngPSa != NULL)
               {
                  *ppCngPSa = p;
               }
               break;
               
            case DAIC_IE_CPN:
               if (ppCedPN != NULL)
               {
                  *ppCedPN = p;
               }
               break;
               
            case DAIC_IE_DSA:
               if (ppCedPSa != NULL)
               {
                  *ppCedPSa = p;
               }
               break;
            
            case DAIC_IE_LLC:
               if (ppLLC != NULL)
               {
                  *ppLLC = p;
               }
               break;
               
            case DAIC_IE_HLC:
               if (ppHLC != NULL)
               {
                  *ppHLC = p;
               }
               break;
               
            case DAIC_IE_UUI:
               if (ppUUData != NULL)
               {
                  *ppUUData = p;
               }
               break;
            
            default:
               break;
         }
         uCurrCS = uNextCS;
      }
      
      if ((uIE & 0x80) == 0)
      {
         p += 1 + nLen;
      }
   }
   
} /* daicplci_extract_conn_ind_info */





/**
 * Parse an information element buffer for a call connected indication.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data as a connection context for
 *                                 this operation.
 * @param pabIEBuffer           I: The buffer containing the information
 *                                 elements to examine.
 * @param nLenIEBuffer          I: The length in bytes of the buffer pabIEBuffer.
 * @param ppCedPN               O: The address of the connected party number.
 * @param ppCedPSa              O: The address of the connected party subaddress.
 *
 * @return Nothing.
 */

static void daicplci_extract_call_conn_info
   (DaicPortData_t  *pPortData,
    DaicPlciData_t  *pPlciData,
    const u_int8_t  *pabIEBuffer,
    size_t           nLenIEBuffer,
    const u_int8_t **ppCedPN,
    const u_int8_t **ppCedPSa)
{
   const u_int8_t *p;
   const u_int8_t *pEnd;
   unsigned        uIE;
   size_t          nLen;
   unsigned        uCurrCS;
   unsigned        uNextCS;
   
   p = pabIEBuffer;
   pEnd = pabIEBuffer + nLenIEBuffer;
   uCurrCS = 0;
   uNextCS = 0;
   while (p < pEnd)
   {
      /* determine and check for valid length */
      uIE = (unsigned) *p;
      if (uIE == 0)
      {
         /* mark for end-of-list */
         break;
      }
      if ((uIE & 0x80) == 0)
      {
         /* variable length information element, length byte should follow */
         if (p + 1 >= pEnd)
         {
            DBG (LOG_ERROR, pPortData->iUnit,
                 "Port %u: PLCI %u: State %d: Multibyte IE 0x%02X not followed by length byte",
                 pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
                 uIE);
            break;
         }
         nLen = p [1];
         if (p + 2 + nLen >= pEnd)
         {
            DBG (LOG_ERROR, pPortData->iUnit,
                 "Port %u: PLCI %u: State %d: Length %zu of multibyte IE 0x%02X exceeds buffer limit",
                 pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
                 nLen, uIE);
            break;
         }
      }
      else
      {
         /* single octet information element */
         nLen = 0;
      }
      ++p;
      
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got IE 0x%02X, length %zu, current code set %u",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           uIE, nLen, uCurrCS);

      if ((uIE & 0xF0) == 0x90)
      {
         /* code set shift */
         uCurrCS = (uIE & 0x07);
         if ((uIE & 0x08) == 0)
         {
            /* locking code set shift, do not switch back after evaluating next
             * IE
             */
            uNextCS = uCurrCS;
         }
      }
      else if (uCurrCS == 0)
      {
         switch (uIE)
         {
            case DAIC_IE_CAD:
            case DAIC_IE_CNO:
            case DAIC_IE_OAD:
               if (ppCedPN != NULL)
               {
                  *ppCedPN = p;
               }
               break;
               
            case DAIC_IE_CSA:
            case DAIC_IE_OSA:
               if (ppCedPSa != NULL)
               {
                  *ppCedPSa = p;
               }
               break;

            default:
               break;
         }
         uCurrCS = uNextCS;
      }
      
      if ((uIE & 0x80) == 0)
      {
         p += 1 + nLen;
      }
   }
   
} /* daicplci_extract_call_conn_info */





/**
 * Parse an information element buffer for a cause value.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param pabIEBuffer           I: The buffer containing the information
 *                                 elements to examine.
 * @param nLenIEBuffer          I: The length in bytes of the buffer pabIEBuffer.
 * @param pbCauseVal            O: The address to receive the cause byte if
 *                                 successful. If a cause information element
 *                                 is not found in the buffer, the value will
 *                                 not be changed.
 *
 * @return Nothing.
 */

static void daicplci_extract_cause
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    const u_int8_t *pabIEBuffer,
    size_t          nLenIEBuffer,
    u_int8_t       *pbCauseVal)
{
   const u_int8_t *p;
   const u_int8_t *pEnd;
   unsigned        uIE;
   size_t          nLen;
   unsigned        uCurrCS;
   unsigned        uNextCS;
   
   if (pbCauseVal == NULL)
   {
      return;
   }
   
   p = pabIEBuffer;
   pEnd = pabIEBuffer + nLenIEBuffer;
   uCurrCS = 0;
   uNextCS = 0;
   while (p < pEnd)
   {
      /* determine and check for valid length */
      uIE = (unsigned) *p;
      if (uIE == 0)
      {
         /* mark for end-of-list */
         break;
      }
      if ((uIE & 0x80) == 0)
      {
         /* variable length information element, length byte should follow */
         if (p + 1 >= pEnd)
         {
            DBG (LOG_ERROR, pPortData->iUnit,
                 "Port %u: PLCI %u: State %d: Multibyte IE 0x%02X not followed by length byte",
                 pPortData->uPortIdx, pPlciData->uPlci,
                 (int) (pPlciData->state), uIE);
            break;
         }
         nLen = p [1];
         if (p + 2 + nLen >= pEnd)
         {
            DBG (LOG_ERROR, pPortData->iUnit,
                 "Port %u: PLCI %u: State %d: Length %zu of multibyte IE 0x%02X exceeds buffer limit",
                 pPortData->uPortIdx, pPlciData->uPlci,
                 (int) (pPlciData->state), nLen, uIE);
            break;
         }
         p += 2;
      }
      else
      {
         /* single octet information element */
         nLen = 0;
         ++p;
      }
      
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got IE 0x%02X, length %zu, current code set %u",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           uIE, nLen, uCurrCS);

      /* if the information element is "escape for extension", the real
       * information element follows the length byte and the length must be
       * updated
       */
      if (uCurrCS == 0 && uIE == DAIC_IE_ESC && nLen > 0)
      {
         uIE = *(p++);
         --nLen;
         
         DBG (LOG_DEBUG, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: Escape for extension contains IE 0x%02X, length now %zu",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              uIE, nLen);
      }
      
      if ((uIE & 0xF0) == 0x90)
      {
         /* code set shift */
         uCurrCS = (uIE & 0x07);
         if ((uIE & 0x08) == 0)
         {
            /* locking code set shift, do not switch back after evaluating next
             * IE
             */
            uNextCS = uCurrCS;
         }
      }
      else if (uCurrCS == 0)
      {
         switch (uIE)
         {
            case DAIC_IE_CAU:
               if (nLen < 2)
               {
                  break;
               }
               if ((p [0] & 0x80) == 0)
               {
                  if (nLen < 3)
                  {
                     break;
                  }
                  *pbCauseVal = p [2];
               }
               else
               {
                  *pbCauseVal = p [1];
               }
               /* the first cause value found is returned, others should be sent
                * as Info-Indications
                */
               return;
               
            default:
               break;
         }
         uCurrCS = uNextCS;
      }
      
      p += nLen;
   }
   
} /* daicplci_extract_cause */





/**
 * Create a CIP value out of BC and HLC, if available.
 */

static unsigned daicplci_create_cip_value
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    const u_int8_t *pBC,
    const u_int8_t *pHLC)
{
   unsigned uCip = CAPI_CIP_NO;
   
   /* if no BC is specified, we cannot deliver any CIP value */
   if (pBC == NULL || pBC [0] == 0)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got no or empty BC, return CIP value %u",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           uCip);
      return (uCip);
   }
   
   /* first distinguish between the length of BC */
   if (pBC [0] == 2)
   {
      if (pBC [1] == 0x88 && pBC [2] == 0x90)
      {
         uCip = CAPI_CIP_UNRESTRICTED_DATA;
         if (pHLC != NULL && pHLC [0] == 2 &&
             pHLC [1] == 0x91 && pHLC [2] == 0xA1)
         {
            uCip = CAPI_CIP_FAX_G4_CLASS1;
         }
         else if (pHLC != NULL && pHLC [0] == 2 &&
                  pHLC [1] == 0x91 && pHLC [2] == 0xA4)
         {
            uCip = CAPI_CIP_TELETEX_BASIC_MIXED;
         }
         else if (pHLC != NULL && pHLC [0] == 2 &&
                  pHLC [1] == 0x91 && pHLC [2] == 0xA8)
         {
            uCip = CAPI_CIP_TELETEX_BASIC_PROCESSABLE;
         }
         else if (pHLC != NULL && pHLC [0] == 2 &&
                  pHLC [1] == 0x91 && pHLC [2] == 0xB1)
         {
            uCip = CAPI_CIP_TELETEX_BASIC;
         }
         else if (pHLC != NULL && pHLC [0] == 2 &&
                  pHLC [1] == 0x91 && pHLC [2] == 0xB2)
         {
            uCip = CAPI_CIP_VIDEOTEX;
         }
         else if (pHLC != NULL && pHLC [0] == 2 &&
                  pHLC [1] == 0x91 && pHLC [2] == 0xB5)
         {
            uCip = CAPI_CIP_TELEX;
         }
         else if (pHLC != NULL && pHLC [0] == 2 &&
                  pHLC [1] == 0x91 && pHLC [2] == 0xB8)
         {
            uCip = CAPI_CIP_X400;
         }
         else if (pHLC != NULL && pHLC [0] == 2 &&
                  pHLC [1] == 0x91 && pHLC [2] == 0xC1)
         {
            uCip = CAPI_CIP_X200;
         }
         else if (pHLC != NULL && pHLC [0] == 3 &&
                  pHLC [1] == 0x91 && pHLC [2] == 0x60 && pHLC [3] == 0x02)
         {
            uCip = CAPI_CIP_VIDEO_TELEPHONY_2ND;
         }
      }
      else if (pBC [1] == 0x89 && pBC [2] == 0x90)
      {
         uCip = CAPI_CIP_RESTRICTED_DATA;
      }
      else if (pBC [1] == 0x91 && pBC [2] == 0x90)
      {
         uCip = CAPI_CIP_7kHz_AUDIO;
      }
      else if (pBC [1] == 0x98 && pBC [2] == 0x90)
      {
         uCip = CAPI_CIP_VIDEO;
      }

      if (uCip == CAPI_CIP_NO)
      {
         DBG (LOG_TRACE, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: Got unknown BC, length 2: 0x%02X, 0x%02X",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              (unsigned) (pBC [1]), (unsigned) (pBC [2]));
      }
   }
   else if (pBC [0] == 3)
   {
      if (pBC [1] == 0x80 && pBC [2] == 0x90 &&
          (pBC [3] == 0xA3 || pBC [3] == 0xA2))
      {
         uCip = CAPI_CIP_SPEECH;
         if (pHLC != NULL && pHLC [0] == 2 &&
             pHLC [1] == 0x91 && pHLC [2] == 0x81)
         {
            uCip = CAPI_CIP_TELEPHONY;
         }
      }
      else if (pBC [1] == 0x90 && pBC [2] == 0x90 &&
               (pBC [3] == 0xA3 || pBC [3] == 0xA2))
      {
         uCip = CAPI_CIP_3100Hz_AUDIO;
         if (pHLC != NULL && pHLC [0] == 2 &&
             pHLC [1] == 0x91 && pHLC [2] == 0x84)
         {
            uCip = CAPI_CIP_FAX_G2_G3;
         }
      }
      else if (pBC [1] == 0x91 && pBC [2] == 0x90 && pBC [3] == 0xA5)
      {
         uCip = CAPI_CIP_UNRESTRICTED_DATA_TONES;
         if (pHLC != NULL && pHLC [0] == 2 &&
             pHLC [1] == 0x91 && pHLC [2] == 0x81)
         {
            uCip = CAPI_CIP_7kHz_TELEPHONY;
         }
         else if (pHLC != NULL && pHLC [0] == 3 &&
                  pHLC [1] == 0x91 && pHLC [2] == 0x60 && pHLC [3] == 0x01)
         {
            uCip = CAPI_CIP_VIDEO_TELEPHONY_1ST;
         }
      }

      if (uCip == CAPI_CIP_NO)
      {
         DBG (LOG_TRACE, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: Got unknown BC, length 3: 0x%02X, 0x%02X, 0x%02X",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              (unsigned) (pBC [1]), (unsigned) (pBC [2]), (unsigned) (pBC [3]));
      }
   }
   else if (pBC [0] == 4)
   {
      if (pBC [1] == 0x88 && pBC [2] == 0xC0 &&
          pBC [3] == 0xC6 && pBC [4] == 0xE6)
      {
         uCip = CAPI_CIP_PACKET_MODE;
      }
      else if (pBC [1] == 0x88 && pBC [2] == 0x90 &&
               pBC [3] == 0x21 && pBC [4] == 0x8F)
      {
         uCip = CAPI_CIP_DATA_56;
      }

      if (uCip == CAPI_CIP_NO)
      {
         DBG (LOG_TRACE, pPortData->iUnit,
              "Port %u: PLCI %u: State %d: Got unknown BC, length 4: 0x%02X, 0x%02X, 0x%02X, 0x%02X",
              pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
              (unsigned) (pBC [1]), (unsigned) (pBC [2]),
              (unsigned) (pBC [3]), (unsigned) (pBC [4]));
      }
   }
   /* all other length values are not compatible with any CIP value */
   else
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Got unknown BC of length %u",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pBC [0]));
   }
   
   return (uCip);
} /* daicplci_create_cip_value */





/**
 * Select a pre-defined BC byte string from a CAPI CIP value.
 */

static u_int8_t *daicplci_get_bc_from_cip
   (unsigned uCipValue)
{
   if (uCipValue >= ARRAY_COUNT (g_aabBcValuesByCip) ||
       g_aabBcValuesByCip [uCipValue] [0] == 0)
   {
      return (NULL);
   }
   return (g_aabBcValuesByCip [uCipValue]);
} /* daicplci_get_bc_from_cip */





/**
 * Select a pre-defined HLC byte string from a CAPI CIP value.
 */

static u_int8_t *daicplci_get_hlc_from_cip
   (unsigned uCipValue)
{
   if (uCipValue >= ARRAY_COUNT (g_aabHlcValuesByCip) ||
       g_aabHlcValuesByCip [uCipValue] [0] == 0)
   {
      return (NULL);
   }
   return (g_aabHlcValuesByCip [uCipValue]);
} /* daicplci_get_hlc_from_cip */





/**
 * Convert a CAPI CIP-Werts into Service Indicator and Additional Information.
 */

static void daicplci_get_sin_sadd_from_cip
   (unsigned  uCipValue,
    u_int8_t *pSin,
    u_int8_t *pSAdd)
{
   switch (uCipValue)
   {
      case CAPI_CIP_SPEECH:
      case CAPI_CIP_TELEPHONY:
      case CAPI_CIP_7kHz_AUDIO:
      case CAPI_CIP_7kHz_TELEPHONY:
         *pSin = 1;
         *pSAdd = 1;
         break;
         
      case CAPI_CIP_3100Hz_AUDIO:
         *pSin = 1;
         *pSAdd = 2;
         break;
         
      case CAPI_CIP_PACKET_MODE:
         *pSin = 8;
         *pSAdd = 0;
         break;
         
      case CAPI_CIP_FAX_G2_G3:
         *pSin = 2;
         *pSAdd = 2;
         break;
         
      case CAPI_CIP_FAX_G4_CLASS1:
         *pSin = 4;
         *pSin = 0;
         break;
         
      case CAPI_CIP_TELETEX_BASIC_MIXED:
         *pSin = 10;
         *pSAdd = 0;
         break;
         
      case CAPI_CIP_TELETEX_BASIC_PROCESSABLE:
      case CAPI_CIP_TELETEX_BASIC:
         *pSin = 9;
         *pSAdd = 0;
         break;
         
      case CAPI_CIP_VIDEOTEX:
         *pSin = 15;
         *pSAdd = 0;
         break;
         
      case CAPI_CIP_VIDEO_TELEPHONY_1ST:
      case CAPI_CIP_VIDEO_TELEPHONY_2ND:
         *pSin = 14;
         *pSAdd = 0;
         break;
      
      default:
         *pSin = 7;
         *pSAdd = 0;
         break;
   }
   
} /* daicplci_get_sin_sadd_from_cip */





/**
 * Send out a controller Hangup-Request with the necessary state transit.
 *
 * @param pPortData             I/O: The port for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 * @param uCauseVal             I: The cause value to report.
 *
 * @return Nothing.
 */

static void daicplci_send_hangup_req
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData,
    unsigned        uCauseVal)
{
   struct mbuf   *pmbReq;
   DaicReqData_t *pReqData;
   u_int8_t      *p;
   int            i;

   /* fill a controller message for a controller Hangup-Request */
   pmbReq = kcapi_get_mbuf (sizeof (DaicReqData_t) + 6);
   if (pmbReq == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for Hangup-Request",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return;
   }
   pReqData = mtod (pmbReq, DaicReqData_t *);
   pReqData->bReq                    = DAIC_SIG_HANGUP;
   pReqData->bReqId                  = pPlciData->uSigId;
   pReqData->bReqCh                  = 0;
   pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_PLCI;
   pReqData->wAssignReqPlci          = (u_int16_t) (pPlciData->uPlci);
   i = 0;
   p = (u_int8_t *) pReqData + sizeof (*pReqData);
   p [i++] = DAIC_IE_CAU;
   p [i++] = 3;
   p [i++] = 0x00;
   p [i++] = 0x80;
   p [i++] = (u_int8_t) (0x80 | uCauseVal);
   p [i++] = 0;
   pReqData->wDataLength = i;
   pmbReq->m_len = sizeof (*pReqData) + pReqData->wDataLength;

   /* register the pending request for this PLCI */
   pPlciData->uCurrRequest = (unsigned) (pReqData->bReq);
   pPlciData->ulFlags |= DAIC_PLCI_FLAG_REQUEST_PENDING;
   _IF_ENQUEUE (&(pPortData->reqQueue), pmbReq);
   
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: PLCI %u: State %d --> %d",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (int) DAIC_PLCI_STATE_P5);
   pPlciData->state = DAIC_PLCI_STATE_P5;
   
} /* daicplci_send_hangup_req */





/**
 * Send out a controller Assign-Request for a new network id.
 *
 * This function is called to obtain a new network id for B-channel access for a
 * call in the connection establishment phase and when changing the B-channel
 * protocol with a Select-B-Protocol-Request. This is done when receiving a
 * Call-Connected- or a Call-Indication from the controller or for changing the
 * B-channel protocol after the successful Remove-Request for the network
 * channel. In the call establishment phase the receipt of the assign return
 * code will trigger the forwarding of a stored Connect-Active-Indication
 * resulting from the formerly mentionned controller messages. When changing the
 * B-channel protocol the assign return code will resul in a
 * Select-B-Protocol-Confirm. The two situations are distinguished through the
 * current state (P-ACT for B-channel protocol change, otherwise call
 * establishment is assumed).
 *
 * @param pPortData             I/O: The port for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 *
 * @return Nothing.
 */

static void daicplci_send_net_assign_req
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData)
{
   struct mbuf    *pmbReq;
   DaicReqData_t  *pReqData;
   u_int8_t       *p;
   int             i;
   u_int16_t       wMaxDataLen;

   /* fill a controller message for a controller Assign-Request for a network id
    */
   pmbReq = kcapi_get_mbuf (sizeof (DaicReqData_t) + DAIC_SHMEM_LEN_RXBUFFER);
   if (pmbReq == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for network id Assign-Request",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return;
   }
   pReqData = mtod (pmbReq, DaicReqData_t *);
   pReqData->bReq                    = DAIC_NL_ASSIGN;
   pReqData->bReqId                  = DAIC_NL_GLOBAL_ID;
   pReqData->bReqCh                  = 0;
   pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_NCCI;
   pReqData->wAssignReqPlci          = (u_int16_t) (pPlciData->uPlci);
   i = 0;
   p = (u_int8_t *) pReqData + sizeof (*pReqData);
   p [i++] = DAIC_NIE_CAI;
   p [i++] = 1;
   p [i++] = (u_int8_t) (pPlciData->uSigId);
   p [i++] = DAIC_NIE_LLC;
   p [i++] = 2;
   switch (pPlciData->bprot.wB2Prot)
   {
      default:
      case CAPI_B2_ISO7776_X75_SLP:
         p [i++] = 1/*5*/;
         break;
         
      case CAPI_B2_TRANSPARENT:
         /* For a transparent layer 2 there are two possibilities for
          * establishing the B-channel protocols: the application sends a
          * Connect-B3-Request or it expects a Connect-B3-Indication. The
          * controller supports these two cases through two different layer 2
          * protocols 2 and 3, respectively. So for outgoing connections we must
          * normally use protocol 2, for incoming calls we use 3. But if the
          * application requested a change of the data direction (see global
          * options of the B-protocol settings in the CAPI spec.), this must be
          * inverted. Besides the connection establishment phase a
          * Select-B-Protocol-Request is sent in state P-ACT. And there we also
          * must distinguish between the two directions of logical connection
          * establishment. All these different cases are represented by the
          * current setting of the field wBChnOp of the parsed B-protocol data
          * structure for the current PLCI.
          */
         /* Note: For other protocols this is not relevant, because the
          *       application at the other side starts the connection
          *       establishment and the ISDN adapters simply pass these messages
          *       without change.
          */
         if (pPlciData->bprot.wBChnOp == CAPI_BPROT_GLOBOPT_BCHNOP_DTE)
         {
            /* treat it like an outgoing connection */
            p [i++] = 2;
         }
         else
         {
            /* treat it like an incoming connection with automatic receipt of a
             * Connect-Indication
             */
            p [i++] = 3;
         }
         break;
         
      case CAPI_B2_SDLC:
         p [i++] = 4;
         break;
   }
   switch (pPlciData->bprot.wB3Prot)
   {
      default:
      case CAPI_B3_TRANSPARENT:
         p [i++] = 4;
         break;
         
      case CAPI_B3_T90NL:
         p [i++] = 3;
         break;
         
      case CAPI_B3_ISO8208:
         p [i++] = 2;
         break;
   }
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: LLC for NL-Assign-Request: 0x%02X, 0x%02X",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (unsigned) (p [i - 2]), (unsigned) (p [i - 1]));
   wMaxDataLen =
      (u_int16_t) (pPortData->aApplData [pPlciData->iApplDataIdx].uMaxBDataLen);
   /* Note: For B3-protocols T.90NL or ISO-8208 the data block on layer 2 is
    *       a little bit larger than 2048 bytes on layer 3 because of
    *       protocol overhead. For T.70NL this is 2 bytes, for X.25 it is 3
    *       or 4 bytes according the modulo mode (only 8 and 128 supported).
    *       As T.70NL can only be set through T.90NL with compatibility, we
    *       must assume X.25 as a "worst case".
    */
   if (pPlciData->bprot.wB3Prot == CAPI_B3_T90NL ||
       pPlciData->bprot.wB3Prot == CAPI_B3_ISO8208)
   {
      if (pPlciData->bprot.b3Cfg.wModuloMode == 8)
      {
         wMaxDataLen += 3;
      }
      else
      {
         wMaxDataLen += 4;
      }
   }
   p [i++] = DAIC_NIE_DLC;
   p [i++] = 8;
   p [i++] = (wMaxDataLen & 0xFF);
   p [i++] = (wMaxDataLen & 0xFF00) >> 8;
   if (pPlciData->bprot.wB2Prot == CAPI_B2_TRANSPARENT)
   {
      p [i++] = 0x03;
      p [i++] = 0x01;
      p [i++] = 0x07;
      p [i++] = 7;
      p [i++] = 0;
      p [i++] = 0;
   }
   else
   {
      p [i++] = pPlciData->bprot.b2Cfg.bAddrA;
      p [i++] = pPlciData->bprot.b2Cfg.bAddrB;
      p [i++] = pPlciData->bprot.b2Cfg.bModuloMode - 1;
      p [i++] = pPlciData->bprot.b2Cfg.bWindowSize;
      p [i++] = 0;
      p [i++] = 0;
   }
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: DLC for NL-Assign-Request: Max. data length %u, mode %u, window size %u, addrA %u, addrB %u",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        wMaxDataLen, (unsigned) (p [i - 4]), (unsigned) (p [i - 3]),
        (unsigned) (p [i - 6]), (unsigned) (p [i - 5]));
   if (pPlciData->bprot.wB3Prot != CAPI_B3_TRANSPARENT)
   {
      p [i++] = DAIC_NIE_NLC;
      p [i++] = 13;
      p [i++] = (pPlciData->bprot.b3Cfg.wLIC & 0xFF);
      p [i++] = (pPlciData->bprot.b3Cfg.wLIC & 0xFF00) >> 8;
      p [i++] = (pPlciData->bprot.b3Cfg.wHIC & 0xFF);
      p [i++] = (pPlciData->bprot.b3Cfg.wHIC & 0xFF00) >> 8;
      p [i++] = (pPlciData->bprot.b3Cfg.wLTC & 0xFF);
      p [i++] = (pPlciData->bprot.b3Cfg.wLTC & 0xFF00) >> 8;
      p [i++] = (pPlciData->bprot.b3Cfg.wHTC & 0xFF);
      p [i++] = (pPlciData->bprot.b3Cfg.wHTC & 0xFF00) >> 8;
      p [i++] = (pPlciData->bprot.b3Cfg.wLOC & 0xFF);
      p [i++] = (pPlciData->bprot.b3Cfg.wLOC & 0xFF00) >> 8;
      p [i++] = (pPlciData->bprot.b3Cfg.wHOC & 0xFF);
      p [i++] = (pPlciData->bprot.b3Cfg.wHOC & 0xFF00) >> 8;
      p [i++] = (pPlciData->bprot.b3Cfg.wModuloMode & 0xFF);
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: NLC for NL-Assign-Request: LIC %u, HIC %u, LTC %u, HTC %u, LOC %u, HOC %u, modulo mode %u",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
           (unsigned) (pPlciData->bprot.b3Cfg.wLIC),
           (unsigned) (pPlciData->bprot.b3Cfg.wHIC),
           (unsigned) (pPlciData->bprot.b3Cfg.wLTC),
           (unsigned) (pPlciData->bprot.b3Cfg.wHTC),
           (unsigned) (pPlciData->bprot.b3Cfg.wLOC),
           (unsigned) (pPlciData->bprot.b3Cfg.wHOC),
           (unsigned) (pPlciData->bprot.b3Cfg.wModuloMode));
   }
   p [i++] = 0;
   pReqData->wDataLength = i;
   pmbReq->m_len = sizeof (*pReqData) + pReqData->wDataLength;

   /* register the pending request for this PLCI */
   pPlciData->uCurrRequest  = (unsigned) (pReqData->bReq);
   pPlciData->ulFlags      |= DAIC_PLCI_FLAG_REQUEST_PENDING;
   _IF_ENQUEUE (&(pPortData->reqQueue), pmbReq);
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: PLCI %u: State %d: Network id Assign-Request sent for signaling id 0x%02X",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        pPlciData->uSigId);

} /* daicplci_send_net_assign_req */





/**
 * Send out a controller Remove-Request for the network id with the necessary
 * state transit.
 *
 * @param pPortData             I/O: The port for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 *
 * @return Nothing.
 */

static void daicplci_send_net_remove_req
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData)
{
   struct mbuf   *pmbReq;
   DaicReqData_t *pReqData;

   /* if in the hangup phase and there is no network id allocated, proceed with
    * sending the Disconnect-Indication to the application
    */
   if (pPlciData->state != DAIC_PLCI_STATE_PACT &&
       pPlciData->uNetId == 0)
   {
      daicplci_send_disc_ind (pPortData, pPlciData);
      return;
   }
   
   /* fill a controller message for a controller Remove-Request */
   pmbReq = kcapi_get_mbuf (sizeof (DaicReqData_t));
   if (pmbReq == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for network id Remove-Request",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return;
   }
   pReqData = mtod (pmbReq, DaicReqData_t *);
   pReqData->bReq                    = DAIC_NL_REMOVE;
   pReqData->bReqId                  = pPlciData->uNetId;
   pReqData->bReqCh                  = 0;
   pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_NCCI;
   pReqData->wAssignReqPlci          = (u_int16_t) (pPlciData->uPlci);
   pReqData->wDataLength             = 0;
   pmbReq->m_len = sizeof (*pReqData);

   /* register the pending request for this PLCI */
   pPlciData->uCurrRequest  = (unsigned) (pReqData->bReq);
   pPlciData->ulFlags      |= DAIC_PLCI_FLAG_REQUEST_PENDING;
   _IF_ENQUEUE (&(pPortData->reqQueue), pmbReq);
   
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: PLCI %u: State %d --> %d",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (int) DAIC_PLCI_STATE_P6_1);
   pPlciData->state = DAIC_PLCI_STATE_P6_1;
   
} /* daicplci_send_net_remove_req */





/**
 * Send out a controller Remove-Request with the necessary state transit.
 *
 * @param pPortData             I/O: The port for this operation.
 * @param pPlciData             I/O: The PLCI data for this operation.
 *
 * @return Nothing.
 */

static void daicplci_send_sig_remove_req
   (DaicPortData_t *pPortData,
    DaicPlciData_t *pPlciData)
{
   struct mbuf   *pmbReq;
   DaicReqData_t *pReqData;
   u_int8_t      *p;

   /* fill a controller message for a controller Remove-Request */
   pmbReq = kcapi_get_mbuf (sizeof (DaicReqData_t) + 1);
   if (pmbReq == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: PLCI %u: State %d: Out of mbufs for signaling id Remove-Request",
           pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state));
      return;
   }
   pReqData = mtod (pmbReq, DaicReqData_t *);
   pReqData->bReq                    = DAIC_SIG_REMOVE;
   pReqData->bReqId                  = pPlciData->uSigId;
   pReqData->bReqCh                  = 0;
   pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_PLCI;
   pReqData->wAssignReqPlci          = (u_int16_t) (pPlciData->uPlci);
   pReqData->wDataLength             = 1;
   p = (u_int8_t *) pReqData + sizeof (*pReqData);
   p [0] = 0;
   pmbReq->m_len = sizeof (*pReqData) + 1;

   /* register the pending request for this PLCI */
   pPlciData->uCurrRequest = (unsigned) (pReqData->bReq);
   pPlciData->ulFlags |= DAIC_PLCI_FLAG_REQUEST_PENDING;
   _IF_ENQUEUE (&(pPortData->reqQueue), pmbReq);
   
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: PLCI %u: State %d --> %d",
        pPortData->uPortIdx, pPlciData->uPlci, (int) (pPlciData->state),
        (int) DAIC_PLCI_STATE_P6_3);
   pPlciData->state = DAIC_PLCI_STATE_P6_3;
   
} /* daicplci_send_sig_remove_req */
