/**
 * @file daic_globalid.c
 *
 * Daic-GlobalId - Module to handle the global id (D-channel access) state
 * machine.
 *
 * Copyright: 2003 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: daic_globalid.c,v 1.1.2.1 2006/02/28 21:35:13 thomas Exp $
 * $Project:    CAPI for BSD $
 * $Target:     daic - CAPI manager driver for Diehl active ISDN controllers $
 * @date        12.04.2003
 * @author      "Thomas Wintergerst" <twinterg@gmx.de>
 * <p>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * </p>
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");

/* System includes */
#include <sys/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <capi20.h>

/* Import includes */

#define __DAIC_GLOBALID__

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





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





/** The information elements for a global id Assign-Request. */
static u_int8_t g_abGidAssignReqParams [] =
   {
      DAIC_IE_CAI, 1, 0x80,
      DAIC_IE_KEY, 3, 'C', '4', 'B',
      /*DAIC_IE_CHI, 1, 0x83,*/
      /*DAIC_IE_BC, 3, 0x80, 0x90, 0xA3,*/
      /*DAIC_IE_BC, 2, 0x88, 0x90,*/
      /*DAIC_IE_BC, 3, 0x90, 0x90, 0xA3,*/
      DAIC_IE_SHIFT | 0x08 | 0x06,
      DAIC_IE_SIN, 2, 0, 0,
      0
   };





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





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





/**
 * Send an Assign-Request for the global id state machine.
 *
 * @param pPortData             I/O: The port data for this operation.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicgid_send_assign_req
   (DaicPortData_t *pPortData);

/**
 * Send an Indicate-Request for the global id state machine.
 *
 * @param pPortData             I/O: The port data for this operation.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static unsigned daicgid_send_indicate_req
   (DaicPortData_t *pPortData);

/**
 * Send a Hangup-Request for the global id state machine.
 *
 * This function is normally called in response to a Indicate-Indication that
 * cannot be handled, either because we are out of PLCIs or there is no
 * application registered. So the cause value for the hangup is set to
 * "non-selected user clearing".
 *
 * @param pPortData             I/O: The port data for the operation context.
 *
 * @return Nothing.
 */
static void daicgid_send_hangup_req
   (DaicPortData_t *pPortData);

/**
 * Send a Remove-Request for the global id state machine.
 *
 * @param pPortData             I/O: The port data for this operation.
 *
 * @return CAPI result value, CAPI_OK on success.
 */
static unsigned daicgid_send_remove_req
   (DaicPortData_t *pPortData);

/**
 * Handle a Listen-Request message.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param uApplID               I: The application id the CAPI message is from.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @result CAPI result value, CAPI_OK on success.
 */
static unsigned daicgid_handle_listen_req
   (DaicPortData_t *pPortData,
    unsigned        uApplID,
    struct mbuf    *pmbMsg);





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





/**
 * Reset the state machine for the global id of a port.
 *
 * @param pPortData             I/O: The address for the port data for this
 *                                 operation.
 *
 * @return Nothing.
 */

void daicgid_reset
   (DaicPortData_t *pPortData)
{
   pPortData->globIdData.state = DAIC_GID_STATE_IDLE;
   pPortData->globIdData.uId   = 0;
   
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: GID(%u): Global id data reset, moved to state IDLE",
        pPortData->uPortIdx, pPortData->globIdData.uId);

} /* daicgid_reset */





/**
 * Start the state machine for the global id (D-channel access).
 *
 * The global id state machine must be in state Idle before calling this
 * function. The call will initiate the allocation of a new signalling instance
 * to be informed about new incoming calls. This must be done for each port of
 * an ISDN board. But this function will only work on the specific port given as
 * the argument to this function.
 *
 * @param pPortData             I/O: The port data for this operation.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

unsigned daicgid_start
   (DaicPortData_t *pPortData)
{
   /* If we are in state ASSIGN_OK, it was formerly not possible to send out an
    * Indicate-Request. So now we just try to do this.
    */
   if (pPortData->globIdData.state == DAIC_GID_STATE_ASSIGN_OK)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: GID(%u): Still in state ASSIGN_OK, will send Indicate-Request",
           pPortData->uPortIdx, pPortData->globIdData.uId);
      return (daicgid_send_indicate_req (pPortData));
   }
   
   /* If we are in state IND_FAILED, it was formerly not possible to send out a
    * Remove-Request for an unuseable global id. So now we just try to do this.
    */
   if (pPortData->globIdData.state == DAIC_GID_STATE_IND_FAILED)
   {
      DBG (LOG_DEBUG, pPortData->iUnit,
           "Port %u: GID(%u): Still in state IND_FAILED, will send Remove-Request",
           pPortData->uPortIdx, pPortData->globIdData.uId);
      return (daicgid_send_remove_req (pPortData));
   }
   
   /* else the state machine must be in idle state */
   if (pPortData->globIdData.state != DAIC_GID_STATE_IDLE)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: GID(%u): Unable to start global id state machine in state %d",
           pPortData->uPortIdx, pPortData->globIdData.uId,
           (int) (pPortData->globIdData.state));
      return (CCE_MSG_NOT_ALLOWED_YET);
   }
   
   /* now finally send out the assign request for the new global id */
   return (daicgid_send_assign_req (pPortData));
} /* daicgid_start */





/**
 * Handle the return code for a global id (D-channel access) assign request.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */

void daicgid_handle_assign_rc
   (DaicPortData_t     *pPortData,
    const DaicRcData_t *pRcData)
{
   /* the state machine must be in state "assign request pending" */
   if (pPortData->globIdData.state != DAIC_GID_STATE_ASSIGN_REQ)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: GID(%u): Got unexpected assign return code 0x%02X, id %u in state %d",
           pPortData->uPortIdx, pPortData->globIdData.uId,
           (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId),
           (int) (pPortData->globIdData.state));
      return;
   }
   
   /* check for negative result */
   if (pRcData->bRc != DAIC_RC_ASSIGN_OK)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: GID(%u): State %d --> %d",
           pPortData->uPortIdx, pPortData->globIdData.uId,
           (int) (pPortData->globIdData.state), (int) (DAIC_GID_STATE_IDLE));
      pPortData->globIdData.state = DAIC_GID_STATE_IDLE;
      if (pRcData->bRc == (DAIC_RC_ASSIGN_MASK | DAIC_RC_OUT_OF_RESOURCES) &&
          pPortData->pSc->cardType == DAIC_CARD_TYPE_S2M &&
          pPortData->nNumPlci >= DAIC_ERR_ID - 1)
      {
         /* For S2m boards it is not possible to obtain a new signalling id if
          * there are already 30 connections running. But that should not be
          * reported as an error, because this is a normal situation.
          */
         DBG (LOG_TRACE, pPortData->iUnit,
              "Port %u: GID(%u): All signalling ids in use, unable to obtain new signalling id",
              pPortData->uPortIdx, pPortData->globIdData.uId);
         return;
      }
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: GID(%u): Assign request for global id failed with return code 0x%02X",
           pPortData->uPortIdx, pPortData->globIdData.uId,
           (unsigned) (pRcData->bRc));
      return;
   }
   
   /* take the new global id */
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: GID(%u): State %d --> %d",
        pPortData->uPortIdx, pPortData->globIdData.uId,
        (int) (pPortData->globIdData.state), (int) (DAIC_GID_STATE_ASSIGN_OK));
   pPortData->globIdData.uId = (unsigned) (pRcData->bRcId);
   pPortData->globIdData.state = DAIC_GID_STATE_ASSIGN_OK;
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: GID(%u): New global id assigned",
        pPortData->uPortIdx, pPortData->globIdData.uId);
   
   /* now create the indicate request to be informed about incoming calls */
   (void) daicgid_send_indicate_req (pPortData);
   
} /* daicgid_handle_assign_rc */





/**
 * Handle a return code for a global id (D-channel access) regular request.
 *
 * Only return codes for an Indicate-Request and a Remove-Request are expected.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pRcData               I: The return code data to evaluate.
 *
 * @return Nothing.
 */

void daicgid_handle_rc
   (DaicPortData_t     *pPortData,
    const DaicRcData_t *pRcData)
{
   /* distinguish between the allowed states */
   if (pPortData->globIdData.state == DAIC_GID_STATE_INDICATE_REQ)
   {
      /* check for failed Indicate-Request */
      if (pRcData->bRc != DAIC_RC_OK)
      {
         DBG (LOG_ERROR, pPortData->iUnit,
              "Port %u: GID(%u): Indicate-Request failed with return code 0x%02X",
              pPortData->uPortIdx, pPortData->globIdData.uId,
              (unsigned) (pRcData->bRc));
         DBG (LOG_TRACE, pPortData->iUnit,
              "Port %u: GID(%u): State %d --> %d",
              pPortData->uPortIdx, pPortData->globIdData.uId,
              (int) (pPortData->globIdData.state),
              (int) DAIC_GID_STATE_IND_FAILED);
         pPortData->globIdData.state = DAIC_GID_STATE_IND_FAILED;
         (void) daicgid_send_remove_req (pPortData);
         return;
      }

      /* now the global id is fully operable */
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: GID(%u): State %d --> %d",
           pPortData->uPortIdx, pPortData->globIdData.uId,
           (int) (pPortData->globIdData.state),
           (int) DAIC_GID_STATE_ACTIVE);
      pPortData->globIdData.state = DAIC_GID_STATE_ACTIVE;

      return;
   }
   else if (pPortData->globIdData.state == DAIC_GID_STATE_HANGUP_REQ)
   {
      if (pRcData->bRc == DAIC_RC_OK)
      {
         DBG (LOG_TRACE, pPortData->iUnit,
              "Port%u: GID(%u): Hangup-Request succeeded, wait for Hangup-Indication",
              pPortData->uPortIdx, pPortData->globIdData.uId);
         /* now wait for the Hangup-Indication to arrive */
      }
      else
      {
         DBG (LOG_ERROR, pPortData->iUnit,
              "Port %u: GID(%u): Hangup-Request failed with return code 0x%02X, proceed with Remove-Request",
              pPortData->uPortIdx, pPortData->globIdData.uId,
              (unsigned) (pRcData->bRc));
         /* even if the Hangup-Request failed, we cannot do anything else than
          * perform a Remove-Request to got to a stable state with a new global
          * id
          */
         (void) daicgid_send_remove_req (pPortData);
      }
   }
   else if (pPortData->globIdData.state == DAIC_GID_STATE_REMOVE_REQ)
   {
      /* check for failed remove request; only log error, no recovery possible */
      if (pRcData->bRc != DAIC_RC_OK)
      {
         DBG (LOG_ERROR, pPortData->iUnit,
              "Port %u: GID(%u): Remove-Request for id %u failed with return code 0x%02X",
              pPortData->uPortIdx, pPortData->globIdData.uId,
              (unsigned) (pRcData->bRcId), (unsigned) (pRcData->bRc));
      }
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: GID(%u): State %d --> %d",
           pPortData->uPortIdx, pPortData->globIdData.uId,
           (int) (pPortData->globIdData.state),
           (int) DAIC_GID_STATE_IDLE);
      daicdisp_id_removed (pPortData, pPortData->globIdData.uId);
      pPortData->globIdData.state = DAIC_GID_STATE_IDLE;
      pPortData->globIdData.uId   = 0;
      
      /* now re-start the global id state machine */
      (void) daicgid_start (pPortData);
      return;
   }
   else
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: GID(%u): Got unexpected return code 0x%02X, id %u in state %d",
           pPortData->uPortIdx, pPortData->globIdData.uId,
           (unsigned) (pRcData->bRc), (unsigned) (pRcData->bRcId),
           (int) (pPortData->globIdData.state));
      return;
   }
   
} /* daicgid_handle_rc */





/**
 * Handle an indication for the global id (D-channel access).
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param pIndData              I: The indication data to evaluate.
 *
 * @return Nothing.
 */

void daicgid_handle_ind
   (DaicPortData_t      *pPortData,
    const DaicIndData_t *pIndData)
{
   unsigned uRes;

   /* one of the two possible indications for the global id is a
    * Hangup-Indication for a rejected incoming call
    */
   if (pPortData->globIdData.state == DAIC_GID_STATE_HANGUP_REQ &&
       pIndData->bInd == DAIC_SIG_HANGUP)
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: GID(%u): Got Hangup-Indication for id %u, proceed with removing the current global id",
           pPortData->uPortIdx, pPortData->globIdData.uId,
           (unsigned) (pIndData->bIndId));
      (void) daicgid_send_remove_req (pPortData);
      return;
   }
   
   /* to handle an Indicate-Indication we must be in the ACTIVE state */
   if (pPortData->globIdData.state != DAIC_GID_STATE_ACTIVE)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: GID(%u): Got unexpected indication 0x%02X, id %u in state %u (not ACTIVE)",
           pPortData->uPortIdx, pPortData->globIdData.uId,
           (unsigned) (pIndData->bInd), (unsigned) (pIndData->bIndId),
           (int) (pPortData->globIdData.state));
      return;
   }
   
   /* now the only indication allowed is an Indicate-Indication */
   if (pIndData->bInd != DAIC_SIG_INDICATE_IND)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: GID(%u): Got unexpected indication 0x%02X, id %u, only INDICATE_IND expected",
           pPortData->uPortIdx, pPortData->globIdData.uId,
           (unsigned) (pIndData->bInd), (unsigned) (pIndData->bIndId));
      return;
   }
   
   /* A new incoming call is pending, the current global id must be used as a
    * new PLCI signaling id for this call. The message is sent back to the
    * dispatch module with our former global id as the signaling id. The
    * dispatch module will allocate a new PLCI, associate it with the signaling
    * id (marked as belonging to a PLCI) and finally forward the message to the
    * PLCI layer to proceed with the call.
    */
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: GID(%u): Got Indicate-Indication for id %u, send message back to dispatch to forward it to a newly allocated PLCI",
        pPortData->uPortIdx, pPortData->globIdData.uId,
        (unsigned) (pIndData->bIndId));
   uRes = daicdisp_handle_indicate_ind
             (pPortData, pPortData->globIdData.uId, pIndData);
   if (uRes != CAPI_OK)
   {
      /* Something went wrong, maybe we are out of PLCIs or there are no
       * applications registered. In this case we must send a Hangup-Request in
       * this state machine. In response to the corresponding return code the
       * global id will be removed and a new one will be allocated to be safe.
       */
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: GID(%u): Unable to handle incoming call, reject with Hangup-Request",
           pPortData->uPortIdx, pPortData->globIdData.uId);
      daicgid_send_hangup_req (pPortData);
      
      return;
   }
   
   /* The Indicate-Indication is now handled by the PLCI layer, so the global id
    * does not exist any more. We now must allocate a new one.
    */
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: GID(%u): State %d --> %d",
        pPortData->uPortIdx, pPortData->globIdData.uId,
        (int) (pPortData->globIdData.state),
        (int) DAIC_GID_STATE_IDLE);
   pPortData->globIdData.state = DAIC_GID_STATE_IDLE;
   pPortData->globIdData.uId = 0;
   (void) daicgid_start (pPortData);
   
} /* daicgid_handle_ind */





/**
 * Handle a CAPI message for the global id (D-channel access).
 *
 * The only messages, that are directly addressed to a controller are a
 * Listen-Request and a Connect-Request. An Info-Request to a controller is not
 * supported by this driver. All other messages will be rejected.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param uApplID               I: The application id the CAPI message is from.
 * @param iApplIdx              I: The index into the application data array for
 *                                 the application id.
 * @param pCapiMsg              I: The CAPI message to evaluate.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

unsigned daicgid_handle_capi_msg
   (DaicPortData_t  *pPortData,
    unsigned         uApplID,
    struct mbuf     *pmbMsg)
{
   CAPIMsg_t *pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
   unsigned   uRes;
   unsigned   uCmd;
   
   /* handle a Listen-Request */
   uCmd = CAPI_GET_CMD (pCapiMsg);
   if (uCmd == CAPI_REQUEST (C_LISTEN))
   {
      uRes = daicgid_handle_listen_req (pPortData, uApplID, pmbMsg);
   }
   /* all other messages are not supported (Info-Request) */
   else
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: GID(%u): CAPI message 0x%04X not supported with CID 0x%08lX",
           pPortData->uPortIdx, pPortData->globIdData.uId,
           uCmd, (unsigned long) CAPI_GET_CID (pCapiMsg));
      uRes = CCE_MSG_NOT_ALLOWED_YET;
   }
   
   return (uRes);
} /* daicgid_handle_capi_msg */





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





   
/**
 * Send an Assign-Request for the global id state machine.
 *
 * @param pPortData             I/O: The port data for this operation.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

static unsigned daicgid_send_assign_req
   (DaicPortData_t *pPortData)
{
   struct mbuf   *pmb;
   DaicReqData_t *pReqData;
   u_int8_t      *p;
   
   /* check if the request queue is not filled up */
   if (_IF_QFULL (&(pPortData->reqQueue)))
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: GID(%u): Request queue filled up, unable to send Assign-Request",
           pPortData->uPortIdx, pPortData->globIdData.uId);
      return (CME_PUT_QUEUE_FULL);
   }
   
   /* allocate an mbuf for the desired operation */
   pmb = kcapi_get_mbuf (sizeof (DaicReqData_t) +
                         sizeof (g_abGidAssignReqParams));
   if (pmb == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: GID(%u): Out of mbufs for Assign-Request",
           pPortData->uPortIdx, pPortData->globIdData.uId);
      return (CME_OS_RESOURCE_ERROR);
   }

   /* fill the mbuf with the request data */
   pReqData = mtod (pmb, DaicReqData_t *);
   pReqData->bReq                    = DAIC_SIG_ASSIGN;
   pReqData->bReqId                  = DAIC_SIG_GLOBAL_ID;
   pReqData->bReqCh                  = 0;
   pReqData->bAssignReqQualification = DAIC_ID_QUALIFY_GID;
   pReqData->wAssignReqPlci          = 0;
   p = (u_int8_t *) pReqData + sizeof (*pReqData);
   pReqData->wDataLength = sizeof (g_abGidAssignReqParams);
   bcopy (g_abGidAssignReqParams, p, sizeof (g_abGidAssignReqParams));
   
   /* send the mbuf to the request queue */
   _IF_ENQUEUE (&(pPortData->reqQueue), pmb);
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: GID(%u): State %d --> %d",
        pPortData->uPortIdx, pPortData->globIdData.uId,
        (int) (pPortData->globIdData.state),
        (int) DAIC_GID_STATE_ASSIGN_REQ);
   pPortData->globIdData.state = DAIC_GID_STATE_ASSIGN_REQ;
   
   return (CAPI_OK);
} /* daicgid_send_assign_req */





/**
 * Send an Indicate-Request for the global id state machine.
 *
 * @param pPortData             I/O: The port data for this operation.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

unsigned daicgid_send_indicate_req
   (DaicPortData_t *pPortData)
{
   struct mbuf   *pmb;
   DaicReqData_t *pReqData;
   
   /* check for filled up request queue */
   if (_IF_QFULL (&(pPortData->reqQueue)))
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: GID(%u): Request queue filled up, unable to send Indicate-Request",
           pPortData->uPortIdx, pPortData->globIdData.uId);
      return (CME_PUT_QUEUE_FULL);
   }
   
   /* allocate an mbuf for the desired operation */
   pmb = kcapi_get_mbuf (sizeof (DaicReqData_t) + 0);
   if (pmb == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: GID(%u): Out of mbufs for Indicate-Request",
           pPortData->uPortIdx, pPortData->globIdData.uId);
      return (CME_OS_RESOURCE_ERROR);
   }
   
   /* fill the mbuf with the request data */
   pReqData = mtod (pmb, DaicReqData_t *);
   pReqData->bReq        = DAIC_SIG_INDICATE_REQ;
   pReqData->bReqId      = (u_int8_t) (pPortData->globIdData.uId);
   pReqData->bReqCh      = 0;
   pReqData->wDataLength = 0;
   
   /* send the mbuf to the request queue */
   _IF_ENQUEUE (&(pPortData->reqQueue), pmb);
   
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: GID(%u): State %d --> %d",
        pPortData->uPortIdx, pPortData->globIdData.uId,
        (int) (pPortData->globIdData.state),
        (int) DAIC_GID_STATE_INDICATE_REQ);
   pPortData->globIdData.state = DAIC_GID_STATE_INDICATE_REQ;
   
   return (CAPI_OK);
} /* daicgid_send_indicate_req */





/**
 * Send a Hangup-Request for the global id state machine.
 *
 * This function is normally called in response to a Indicate-Indication that
 * cannot be handled, either because we are out of PLCIs or there is no
 * application registered. So the cause value for the hangup is set to
 * "non-selected user clearing".
 *
 * @param pPortData             I/O: The port data for the operation context.
 *
 * @return Nothing.
 */

static void daicgid_send_hangup_req
   (DaicPortData_t *pPortData)
{
   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: GID(%u): Out of mbufs for Hangup-Request",
           pPortData->uPortIdx, pPortData->globIdData.uId);
      return;
   }
   pReqData = mtod (pmbReq, DaicReqData_t *);
   pReqData->bReq = DAIC_SIG_HANGUP;
   pReqData->bReqId = pPortData->globIdData.uId;
   pReqData->bReqCh = 0;
   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 | DAIC_CAUSE_NON_SELECTED_USER_CLEARING);
   p [i++] = 0;
   pReqData->wDataLength = i;
   pmbReq->m_len = sizeof (*pReqData) + pReqData->wDataLength;

   _IF_ENQUEUE (&(pPortData->reqQueue), pmbReq);
   
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: GID(%u): State %d --> %d",
        pPortData->uPortIdx, pPortData->globIdData.uId,
        (int) (pPortData->globIdData.state),
        (int) DAIC_GID_STATE_HANGUP_REQ);
   pPortData->globIdData.state = DAIC_GID_STATE_HANGUP_REQ;
   
} /* daicgid_send_hangup_req */





/**
 * Send a Remove-Request for the global id state machine.
 *
 * @param pPortData             I/O: The port data for this operation.
 *
 * @return CAPI result value, CAPI_OK on success.
 */

unsigned daicgid_send_remove_req
   (DaicPortData_t *pPortData)
{
   struct mbuf   *pmb;
   DaicReqData_t *pReqData;
   
   /* check for filled up request queue */
   if (_IF_QFULL (&(pPortData->reqQueue)))
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: GID(%u): Request queue filled up, unable to send Remove-Request",
           pPortData->uPortIdx, pPortData->globIdData.uId);
      return (CME_PUT_QUEUE_FULL);
   }
   
   /* allocate an mbuf for the desired operation */
   pmb = kcapi_get_mbuf (sizeof (DaicReqData_t) + 9);
   if (pmb == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: GID(%u): Out of mbufs for Remove-Request",
           pPortData->uPortIdx, pPortData->globIdData.uId);
      return (CME_OS_RESOURCE_ERROR);
   }
   
   /* fill the mbuf with the request data */
   pReqData = mtod (pmb, DaicReqData_t *);
   pReqData->bReq        = DAIC_SIG_REMOVE;
   pReqData->bReqId      = (u_int8_t) (pPortData->globIdData.uId);
   pReqData->bReqCh      = 0;
   pReqData->wDataLength = 0;
   
   /* send the mbuf to the request queue */
   _IF_ENQUEUE (&(pPortData->reqQueue), pmb);
   
   DBG (LOG_TRACE, pPortData->iUnit,
        "Port %u: GID(%u): State %d --> %d",
        pPortData->uPortIdx, pPortData->globIdData.uId,
        (int) (pPortData->globIdData.state),
        (int) DAIC_GID_STATE_REMOVE_REQ);
   pPortData->globIdData.state = DAIC_GID_STATE_REMOVE_REQ;
   
   return (CAPI_OK);
} /* daicgid_send_remove_req */





/**
 * Handle a Listen-Request message.
 *
 * @param pPortData             I/O: The port data for this operation.
 * @param uApplID               I: The application id the CAPI message is from.
 * @param pmbMsg                I: The CAPI message to evaluate.
 *
 * @result CAPI result value, CAPI_OK on success.
 */

static unsigned daicgid_handle_listen_req
   (DaicPortData_t *pPortData,
    unsigned        uApplID,
    struct mbuf    *pmbMsg)
{
   CAPIMsg_t      *pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
   CAPIMsg_t      *pMsgConf;
   DaicApplData_t *pApplData;
   struct mbuf    *pmbConf;
   int             i;
   
   /* check for valid message length */
   if (CAPI_GET_LEN (pCapiMsg) <
          sizeof (pCapiMsg->head) + sizeof (pCapiMsg->info.listen_req))
   {
      DBG (LOG_TRACE, pPortData->iUnit,
           "Port %u: GID(%u): Length (%u) of CAPI message Listen-Request is too small",
           pPortData->uPortIdx, pPortData->globIdData.uId,
           (unsigned) CAPI_GET_LEN (pCapiMsg));
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   
   /* address the application data for the application id */
   for (i = 0; (size_t) i < ARRAY_COUNT (pPortData->aApplData); ++i)
   {
      if (pPortData->aApplData [i].uApplID == uApplID)
      {
         break;
      }
   }
   if (i >= ARRAY_COUNT (pPortData->aApplData))
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: GID(%u): Got Listen-Request for unregistered application id %u",
           pPortData->uPortIdx, pPortData->globIdData.uId, uApplID);
      return (CME_INVALID_APPLICATION_ID);
   }
   pApplData = &(pPortData->aApplData [i]);
   
   /* take the needed message data */
   pApplData->dwListenCipMask =
      C_GET_DWORD (pCapiMsg->info.listen_req.dwCipMask);
   pApplData->dwListenInfoMask =
      C_GET_DWORD (pCapiMsg->info.listen_req.dwInfoMask);

   /* create the confirmation message */
   pmbConf = kcapi_get_mbuf
                (sizeof (CAPIMsgHead_t) + sizeof (CAPIListenConf_t));
   if (pmbConf == NULL)
   {
      DBG (LOG_ERROR, pPortData->iUnit,
           "Port %u: GID(%u): Out of mbufs for Listen-Confirm CAPI message",
           pPortData->uPortIdx, pPortData->globIdData.uId);
      return (CME_OS_RESOURCE_ERROR);
   }
   pMsgConf = mtod (pmbConf, CAPIMsg_t *);
   pMsgConf->head = pCapiMsg->head;
   C_PUT_WORD (pMsgConf->head.wCmd, CAPI_CONFIRM (C_LISTEN));
   C_PUT_WORD (pMsgConf->info.listen_conf.wInfo, CAPI_OK);
   C_PUT_WORD (pMsgConf->head.wLen, pmbConf->m_len);
   
   mtx_unlock (&(pPortData->pSc->mtxAccess));
   kcapi_ctlr_receive_capi_message (pPortData->uUniqueCapiCtlrNum,
                                    uApplID, pmbConf);
   mtx_lock (&(pPortData->pSc->mtxAccess));

   kcapi_free_mbuf (pmbMsg);
   
   DBG (LOG_DEBUG, pPortData->iUnit,
        "Port %u: GID(%u): Listen-Request for appl.id. %u handled successfully, CIP mask 0x%08lX, info mask 0x%08lX",
        pPortData->uPortIdx, pPortData->globIdData.uId, uApplID,
        (unsigned long) (pApplData->dwListenCipMask),
        (unsigned long) (pApplData->dwListenInfoMask));

   return (CAPI_OK);
} /* daicgid_handle_listen_req */
