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

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

/* system includes */
#include <sys/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/conf.h>
#include <sys/mbuf.h>
#include <sys/proc.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <capi20.h>
#include <capi_bsd.h>

/* import includes */

#define __KCAPIMGR__

/* local includes */
#include <opt_kcapimgr.h>
#include <c4b/kcapimgr/capi_drv.h>
#include <c4b/kcapimgr/kcapimgr_var.h>
#include <c4b/kcapimgr/kcapi_appl.h>
#include <c4b/kcapimgr/kcapi_trace.h>
#include <c4b/kcapimgr/kcapi_ctlr.h>





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





/* --- definitions for dynamic memory allocation --- */

/* the following tag will be used when allocating memory for use in the kernel
 * CAPI manager
 */
MALLOC_DEFINE (M_KCAPIMGRBUF, "kcapimgrbuffer",
               "dynamic memory for the kernel CAPI manager");



/* --- the logging functionality --- */

#ifdef KCAPIMGR_LOG_LEVEL
int e_iKCapiMgrLogLevel = KCAPIMGR_LOG_LEVEL;
#else /* KCAPIMGR_LOG_LEVEL */
int e_iKCapiMgrLogLevel = LOG_ERROR;
#endif /* KCAPIMGR_LOG_LEVEL */





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





/* --- support for sysctl variables --- */

/** The basic "capi" node. */
SYSCTL_NODE (, OID_AUTO, capi, CTLFLAG_RW, 0, "CAPI subsystem");

/** The node for the CAPI manager under "capi". */
SYSCTL_NODE (_capi, OID_AUTO, kcapimgr, CTLFLAG_RW, 0, "kernel CAPI manager");

/** String needed for the version variable of the CAPI manager. */
static char g_szVersion [16] = "";

/** The variable for the version number of the CAPI manager. */
SYSCTL_STRING (_capi_kcapimgr, OID_AUTO, version, CTLFLAG_RD,
               g_szVersion, 0, "Version number");

/** The variable for the logging mask. */
SYSCTL_INT (_capi_kcapimgr, OID_AUTO, loglevel, CTLFLAG_RW,
            &e_iKCapiMgrLogLevel, 0, "Logging level");

/** The variable for the number of registered applications. */
SYSCTL_UINT (_capi_kcapimgr, OID_AUTO, apps_registered, CTLFLAG_RD,
             &e_uNumApps, 0, "Number of registered applications");

/** The variable for the number of registered controllers. */
SYSCTL_UINT (_capi_kcapimgr, OID_AUTO, ctlrs_registered, CTLFLAG_RD,
             &e_uNumCtlrs, 0, "Number of registered controllers");

/** The variable for the number of registered tracers. */
SYSCTL_UINT (_capi_kcapimgr, OID_AUTO, tracers_registered, CTLFLAG_RD,
             &e_uNumTracers, 0, "Number of registered tracers");



/* --- support for tunable 'constants' in the kernel environment --- */

TUNABLE_INT (CAPIMAN_TUNABLE_LOGLEVEL, &e_iKCapiMgrLogLevel);





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





/* --- handling of kernel module specific functionality --- */

/* flag for received first MOD_LOAD event */
static unsigned g_fLoadEventReceived = 0;

/* Handling of module load/unload events */
static int kcapimgr_modevent
   (module_t  mod,
    int       iType,
    void     *pUnused);

static moduledata_t kcapi_mod =
{
   "kcapimgr",
   kcapimgr_modevent,
   NULL
};

DECLARE_MODULE (kcapimgr, kcapi_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE - 1);

#if defined (__FreeBSD_version) && __FreeBSD_version >= 500000
MODULE_VERSION (kcapimgr, (CAPIMAN_VERSION_MAJOR << 16) | CAPIMAN_VERSION_MINOR);
#endif /* defined (__FreeBSD_version) && __FreeBSD_version >= 500000 */





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





/*
        allocate a new mbuf of the specified length
        -------------------------------------------
*/

struct mbuf *kcapi_get_mbuf
   (size_t nLen)
{
   struct mbuf *pmb;
   
   /* check if length is exceeding maximum length possible (2KB) */
   if (nLen > MCLBYTES)
   {
      return (NULL);
   }
   
   /* allocate mbuf */
   MGETHDR (pmb, M_DONTWAIT, MT_DATA);
   if (pmb && nLen > MHLEN)
   {
      MCLGET (pmb, M_DONTWAIT);
      if ((pmb->m_flags & M_EXT) == 0)
      {
         kcapi_free_mbuf (pmb);
         pmb = NULL;
      }
   }
   
   if (pmb)
   {
      pmb->m_len = pmb->m_pkthdr.len = nLen;
   }
   
   return (pmb);
} /* kcapi_get_mbuf */





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





/*
        Handling of module load/unload events
        -------------------------------------
*/

static int kcapimgr_modevent
   (module_t  mod,
    int       iType,
    void     *pUnused)
{
   switch (iType)
   {
      case MOD_LOAD:
         DBG (LOG_TRACE, "got modevent \"%s\"", "MOD_LOAD");
         if (! g_fLoadEventReceived)
         {
            snprintf (g_szVersion, sizeof (g_szVersion), "%d.%d",
                      CAPIMAN_VERSION_MAJOR, CAPIMAN_VERSION_MINOR);
            kcapictlr_init ();
            kcapitrace_init ();
            kcapiappl_init ();
         }
         g_fLoadEventReceived = 1;
         break;
         
      case MOD_UNLOAD:
         DBG (LOG_TRACE, "got modevent \"%s\"", "MOD_UNLOAD");
         /* the CAPI manager may only be unloaded, if there are no (more)
          * registered applications or controllers
          */
         if (e_uNumApps > 0)
         {
            printf ("WARNING: kcapimgr: Unable to unload module, still %u applications registered\n",
                    e_uNumApps);
            return (1);
         }
         if (e_uNumCtlrs > 0)
         {
            printf ("WARNING: kcapimgr: Unable to unload module, still %u controllers registered\n",
                    e_uNumCtlrs);
            return (1);
         }
         if (g_fLoadEventReceived)
         {
            kcapiappl_close ();
            kcapitrace_close ();
            kcapictlr_close ();
            g_fLoadEventReceived = 0;
         }
         break;
         
      case MOD_SHUTDOWN:
         DBG (LOG_TRACE, "got modevent \"%s\"", "MOD_SHUTDOWN");
      default:
         /* event not handled or unknown */
         break;
   }
   
   return (0);
} /* kcapimgr_modevent */
