/**
 * @file daic_misc.c
 *
 * Daic-Misc - Miscellaneous helper functions for the daic device driver.
 *
 * Copyright: 2003 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: daic_misc.c,v 1.1.2.1 2006/02/28 21:35:14 thomas Exp $
 * $Project:    CAPI for BSD $
 * $Target:     daic - CAPI manager driver for Diehl active ISDN controllers $
 * @date        07.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>          /* size_t */
#include <sys/systm.h>
#include <sys/kernel.h>

/* Import includes */

#define __DAIC_MISC__

/* Local includes */
#include <c4b/daic/daic_hw.h>
#include <c4b/daic/daic_misc.h>





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





/** The timeout in hz units for obtaining controller access. */
#define DAICMISC_WAIT_FOR_CTLR_ACCESS_TIMEOUT   (5 * hz)

/** The table of strings for the translation of board types into strings. */
static const char *g_apszCardTypeNames [] =
   {
      "Unknown",        /**< DAIC_CARD_TYPE_UNKNOWN. */
      "Diehl S",        /**< DAIC_CARD_TYPE_S. */
      "Diehl Sx",       /**< DAIC_CARD_TYPE_SX. */
      "Diehl SCOM",     /**< DAIC_CARD_TYPE_SCOM. */
      "Diehl Quadro",   /**< DAIC_CARD_TYPE_QUADRO. */
      "Diehl S2m"       /**< DAIC_CARD_TYPE_S2M. */
   };





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





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





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





/**
 * Get access to a controller for an operation.
 *
 * @note This function is called by all CAPI manager entry functions. The
 *       interrupt routine must not call it, but must directly obtain the board
 *       access mutex.
 *
 * @param pSc                   I/O: The softc structure for the board.
 *
 * @retval 0                    Unable to obtain board access, timeout.
 * @retval 1                    Exclusive board access gained.
 */

int daicmisc_get_ctlr_access
   (DaicSc_t *pSc)
{
   /* The mutex might not be initialized when calling this function. But in this
    * case no real operation will be pending, but the controller device will be
    * detached from its bus. So it does not matter, but a lock operation must
    * not be performed.
    */
   if (! mtx_initialized (&(pSc->mtxAccess)))
   {
      DBG (LOG_DEBUG, pSc->iUnit,
           "Allow direct controller access because of uninitialized mutex");
      pSc->fOpInProgress = 1;
      return (1);
   }
   
   DBG (LOG_IRQ, pSc->iUnit, "Gain controller access");
   mtx_lock (&(pSc->mtxAccess));
   DBG (LOG_IRQ, pSc->iUnit,
        "Got preliminary controller access, wait for no other operation in progress");
   while (pSc->fOpInProgress || pSc->fIntrActive)
   {
      if (cv_timedwait (&(pSc->cvNotify), &(pSc->mtxAccess),
                        DAICMISC_WAIT_FOR_CTLR_ACCESS_TIMEOUT) != 0)
      {
         if (mtx_owned (&(pSc->mtxAccess)) &&
             ! pSc->fOpInProgress && ! pSc->fIntrActive)
         {
            /* the wait operation failed but we got mutex access and no other
             * operation is pending, so just proceed and return success
             */
            DBG (LOG_ERROR, pSc->iUnit,
                 "Timeout waiting for controller access (%lums) but no operation in progess",
                 (unsigned long)
                    DAICMISC_WAIT_FOR_CTLR_ACCESS_TIMEOUT * 1000 / hz);
            break;
         }
         
         DBG (LOG_ERROR, pSc->iUnit,
              "Timeout waiting for controller access (%lums), unable to obtain controller access",
              (unsigned long) DAICMISC_WAIT_FOR_CTLR_ACCESS_TIMEOUT * 1000 / hz);
         if (pSc->fOpInProgress != 0)
         {
            DBG (LOG_ERROR, pSc->iUnit, "Still operation in progress");
         }
         if (pSc->fIntrActive != 0)
         {
            DBG (LOG_ERROR, pSc->iUnit, "Interrupt handler still active");
         }
         if (mtx_owned (&(pSc->mtxAccess)))
         {
            mtx_unlock (&(pSc->mtxAccess));
         }
         return (0);
      }
   }
   
   pSc->fOpInProgress = 1;
   
   DBG (LOG_DEBUG, pSc->iUnit, "Got controller access");
   
   return (1);
} /* daicmisc_get_ctlr_access */





/**
 * Release access to a controller after an operation.
 */

void daicmisc_release_ctlr_access
   (DaicSc_t *pSc)
{
   /* The mutex might not be initialized when calling this function. But in this
    * case no real operation will be pending, but the controller device will be
    * detached from its bus. So it does not matter, but an unlock operation must
    * not be performed.
    */
   if (! mtx_initialized (&(pSc->mtxAccess)) ||
       ! mtx_owned (&(pSc->mtxAccess)))
   {
      DBG (LOG_DEBUG, pSc->iUnit,
           "Directly release controller access because of uninitialized mutex");
      pSc->fOpInProgress = 0;
      return;
   }
   
   pSc->fOpInProgress = 0;
   cv_broadcast (&(pSc->cvNotify));
   mtx_unlock (&(pSc->mtxAccess));
   
   DBG (LOG_DEBUG, pSc->iUnit, "Released controller access");

} /* daicmisc_release_ctlr_access */





/**
 * Translate a card type into a string.
 */

const char *daicmisc_get_card_type_name
   (DaicCardType_t cardType)
{
   if ((size_t) cardType >= ARRAY_COUNT (g_apszCardTypeNames))
   {
      return ("Invalid");
   }
   return (g_apszCardTypeNames [(size_t) cardType]);
} /* daicmisc_get_card_type_name */





/**
 * Translate a controller return code into a CAPI result value.
 *
 * @param bRc                   I: Controller return code to translate.
 * @param iQualify              I: The originating layer that the return code
 *                                 occurred in. One of DAIC_ID_QUALIFY_*.
 *
 * @return The translated CAPI result value.
 */

unsigned daicmisc_get_capi_info_from_rc
   (u_int8_t bRc,
    int      iQualify)
{
   switch (bRc)
   {
      case DAIC_RC_OK:
      case DAIC_RC_ASSIGN_OK:
         return (CAPI_OK);

      case DAIC_RC_UNKNOWN_COMMAND:
      case DAIC_RC_WRONG_COMMAND:
         return (CME_ILLEGAL_COMMAND);

      case DAIC_RC_WRONG_ID:
      case DAIC_RC_WRONG_CH:
      case DAIC_RC_ADAPTER_DEAD:
         return (CCE_ILLEGAL_IDENTIFIER);
         
      case DAIC_RC_OUT_OF_RESOURCES:
         return (CCE_MSG_NOT_ALLOWED_YET);

      case DAIC_RC_UNKNOWN_IE:
      case DAIC_RC_WRONG_IE:
         return (CCE_ILLEGAL_MSG_PARAMETER);

      default:
         if ((bRc & 0xF0) == DAIC_RC_ASSIGN_MASK)
         {
            switch (iQualify)
            {
               case DAIC_ID_QUALIFY_GID:
                  return (CCE_NO_LISTEN_AVAILABLE);
                  
               case DAIC_ID_QUALIFY_PLCI:
                  return (CCE_NO_PLCI_AVAILABLE);
                  
               case DAIC_ID_QUALIFY_NCCI:
                  return (CCE_NO_NCCI_AVAILABLE);
                  
               default:
                  break;
            }
         }
         return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   
} /* daicmisc_get_capi_info_from_rc */





/**
 * Translate a CAPI reject value into a Q.850 cause value.
 */

unsigned daicmisc_get_cause_from_capi_reject
   (unsigned uRejectVal)
{
   switch (uRejectVal)
   {
      case CAPI_CALL_ACCEPT:
         return (0);
         
      case CAPI_CALL_IGNORE:
         return (DAIC_CAUSE_NO_ANSWER);
         
      case CAPI_CALL_REJECT_NORMAL_CLEARING:
         return (DAIC_CAUSE_NORMAL);
         
      case CAPI_CALL_REJECT_USER_BUSY:
         return (DAIC_CAUSE_USER_BUSY);
         
      case CAPI_CALL_REJECT_NO_CHANNEL:
         return (DAIC_CAUSE_NO_CHANNEL_AVAIL);
         
      case CAPI_CALL_REJECT_FACILITY:
         return (DAIC_CAUSE_FACILITY_REJECTED);
         
      case CAPI_CALL_REJECT_CHANNEL_UNACCEPTABLE:
         return (DAIC_CAUSE_CHANNEL_UNACCEPTABLE);
         
      case CAPI_CALL_REJECT_INCOMPAT_DESTINATION:
         return (DAIC_CAUSE_INCOMPAT_DESTINATION);
         
      case CAPI_CALL_REJECT_OUT_OF_ORDER:
         return (DAIC_CAUSE_DEST_OUT_OF_ORDER);
      
      default:
         if ((uRejectVal & 0xFF00) == CDISC_NETWORK_CAUSE_MASK)
         {
            return (uRejectVal & 0x007F);
         }
         /* Note: Null is no regular value, but will normally be accepted. */
         return (0);
   }
   
} /* daicmisc_get_cause_from_capi_reject */





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