/**
 * @file kcapi_trace.c
 *
 * KCapiTrace - Implementation of CAPI tracing.
 *
 * Copyright: 2000-2003 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: kcapi_trace.c,v 1.1.2.1 2006/02/28 21:35:26 thomas Exp $
 * Project  CAPI for BSD
 * Target   kcapimgr - The CAPI manager for *BSD
 * @date    01.01.2000
 * @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 <stddef.h>*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/sx.h>
#include <sys/queue.h> /* list macros */
#include <sys/endian.h> /* le*toh() */
#include <capi20.h>
#include <capi_bsd.h>
#include <capi_bsdtrc.h>

/* import includes */

#define __KCAPI_TRACE__

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





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





/* --- some global counters available to all modules --- */

/** The current number of registered tracers. */
unsigned e_uNumTracers = 0;





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





/* --- definitions for the list of registered tracers --- */

/** The type of a tracer list entry. */
typedef struct TracerListEntry
{
   LIST_ENTRY (TracerListEntry) le;
                                /**< Members for chaining the tracers. */
   
   unsigned       uTracerID;    /**< The id of the current tracer. */
   unsigned       uApplID;      /**< The application id for the tracer; this
                                 *   application receives the trace messages.
                                 */

   unsigned long  ulTraceMask;  /**< The bit mask of enabled tracing features,
                                 *   a combination of C4BTRC_TRCMSK_*.
                                 */
   unsigned long  ulDataBlockLength;
                                /**< If the trace mask specifies to log Data-B3
                                 *   messages and the data blocks themselves,
                                 *   this parameter specifies the maximum number
                                 *   of bytes to trace for one data block.
                                 */
   int            fIsApplTracer;/**< 1: The tracer is an application tracer,
                                 *      afTraceApplAllow is set to valid values,
                                 *   0: The tracer is a controller tracer,
                                 *      uCtlrNum is set to the controller number
                                 *      to trace.
                                 */
   
   int            afTraceApplAllow [CAPI_MAX_APPLICATIONS];
                                /**< If the tracer is an application tracer,
                                 *   this array contains a flag for each
                                 *   possible application id. If an entry is
                                 *   TRUE, the application with the id equal to
                                 *   the entry index may be traced by the
                                 *   current tracer.
                                 */
   
   unsigned       uCtlrNum;     /**< If the tracer is a controller tracer, this
                                 *   field contains the controller number.
                                 */

   int            fIsRoot;      /**< 1: The owner of the tracer's process has
                                 *      root privileges,
                                 *   0: The owner of the tracer's process is a
                                 *      normal user, pCred is set to its
                                 *      credentials.
                                 */
   struct ucred  *pCred;        /**< If the process' owner is not root (fIsRoot
                                 *   is 0), this field points to the process'
                                 *   owners credentials for checking access
                                 *   rights to applications.
                                 */
} TracerListEntry_t;

/** The type definition for the tracer list type. */
LIST_HEAD (TracerList, TracerListEntry);

/** The list of registered tracers. */
struct TracerList g_tracerList = LIST_HEAD_INITIALIZER (g_tracerList);

/** The lock for access to the tracer list. */
static struct sx g_sxTracerList;



/** The current tracer id, increment on each tracer registration. */
static unsigned g_uCurrTracerID = 0;





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





/* === implementation of CAPI interface functions ======================== */





/* === implementation of public functions ================================ */





/**
 * Initialize the trace support module.
 */
 
void kcapitrace_init (void)
{
   /* initialize the list of the active tracers */
   e_uNumApps = 0;
   
   /* initialize the mutex for access to the tracer list */
   sx_init (&g_sxTracerList, "kcapimgr-tracer-list");
   
} /* kcapitrace_init */





/**
 * Close the trace support module.
 *
 * @note Locking the tracer list is not necessary here, because when closing
 *       no CAPI messages are exchanged and all actions in the CAPI manager are
 *       stopped.
 */

void kcapitrace_close (void)
{
   TracerListEntry_t *pEntry;
   
   /* be sure all tracers are released */
   while (LIST_FIRST (&g_tracerList))
   {
      /* get the list entry at the head */
      pEntry = LIST_FIRST (&g_tracerList);
      
      /* remove the entry from the list */
      LIST_REMOVE (pEntry, le);
      
      DBG (LOG_ERROR, "Appl. id %u: Tracer with id %u still registered",
           pEntry->uApplID, pEntry->uTracerID);
           
      /* release the old list entry as it is not needed any more */
      if (pEntry->pCred)
      {
         crfree (pEntry->pCred);
      }
      FREE (pEntry, M_KCAPIMGRBUF);
      
      /* count the removed tracer */
      atomic_subtract_int (&e_uNumTracers, 1);
   }
   
   sx_destroy (&g_sxTracerList);
   
} /* kcapitrace_close */





/**
 * Add a new tracer.
 *
 * @attention The caller must ensure that no call to register or release another
 *            application or tracer can interrupt the update process. This can
 *            only be accomplished by letting the CAPI application array access
 *            be blocked during this call. The implementation of this function
 *            will never call back into the application handling module, so this
 *            locking will not leed to a deadlock.
 *
 * @param uApplID               I: The application id to register the new tracer
 *                                 for.
 * @param ulTraceMask           I: The bit mask of enabled tracing features, a
 *                                 combination of C4BTRC_TRCMSK_*.
 * @param ulDataBlockLength     I: If the trace mask specifies to log Data-B3
 *                                 messages and the data blocks themselves, this
 *                                 parameter specifies the maximum number of
 *                                 bytes to trace for one data block.
 * @param fIsApplTracer         I: TRUE: The tracer is an application tracer,
 *                                    the array pafMayTraceAppl contains a flag
 *                                    for each currently registered application
 *                                    that this tracer receives messages for
 *                                    (i.e. this tracer has the right to trace),
 *                                 FALSE: The tracer is a controller tracer, the
 *                                    controller number to trace is in uCtlrNum.
 * @param pafMayTraceAppl       I: If the tracer to add is an application
 *                                 tracer, this array contains a flag for each
 *                                 registered application this tracer may
 *                                 receive trace messages from. The application
 *                                 id is used as an index into this array. The
 *                                 array should be created by checking for each
 *                                 currently registered application that the
 *                                 process owner of new tracer is either the
 *                                 owner of the application process or he is a
 *                                 member of the applications effective group.
 * @param uCtlrNum              I: If the tracer is a controller tracer, this
 *                                 parameter determines the controller to trace.
 * @param puTracerID            O: The id of the new tracer if the result is
 *                                 CAPI_OK.
 *
 * @return CAPI_OK              The new tracer was registered successfully.
 * @return Else                 An error occurred, the tracer id is not valid.
 */

unsigned kcapitrace_add_tracer
   (unsigned                  uApplID,
    unsigned long             ulTraceMask,
    unsigned long             ulDataBlockLength,
    int                       fIsApplTracer,
    int                       pafMayTraceAppl [CAPI_MAX_APPLICATIONS],
    unsigned                  uCtlrNum,
    unsigned                 *puTracerID)
{
   TracerListEntry_t *pEntry;
   unsigned           u;
   
   /* create a new tracer list entry */
   MALLOC (pEntry, TracerListEntry_t *, sizeof (*pEntry),
           M_KCAPIMGRBUF, M_WAITOK);
   if (! pEntry)
   {
      DBG (LOG_ERROR,
           "Out of memory allocating tracer list entry (%zu bytes)",
           sizeof (*pEntry));
      return (CRE_OS_RESOURCE_ERROR);
   }
   bzero (pEntry, sizeof (*pEntry));
   pEntry->uApplID           = uApplID;
   pEntry->ulTraceMask       = ulTraceMask;
   pEntry->ulDataBlockLength = ulDataBlockLength;
   pEntry->fIsApplTracer     = fIsApplTracer;
   if (pEntry->fIsApplTracer)
   {
      bcopy (pafMayTraceAppl, pEntry->afTraceApplAllow,
             sizeof (pEntry->afTraceApplAllow));
   }
   else
   {
      pEntry->uCtlrNum = uCtlrNum;
   }
   /* duplicate the user credentials of the current process, will be needed
    * when checking tracer rights for new registered applications
    */
   if (curthread->td_ucred != NULL)
   {
      /* check if the current privileges declare a root user */
      if (suser (curthread) == 0)
      {
         pEntry->fIsRoot = 1;
      }
      else
      {
         /* no root user --> add the reference count of the credentials for
          * later use
          */
         pEntry->pCred = crhold (curthread->td_ucred);
         if (pEntry->pCred == NULL)
         {
            FREE (pEntry, M_KCAPIMGRBUF);
            return (CRE_OS_RESOURCE_ERROR);
         }
      }
   }
   else
   {
      /* if there are no credentials the current process is assumed to run under
       * root privilege
       */
      pEntry->fIsRoot = 1;
   }
   if (pEntry->fIsRoot)
   {
      DBG (LOG_DEBUG, "Appl. id %u is driven by root", uApplID);
   }
   /* the tracer id will be assigned soon */
   
   /* do some consistency checks */
   if (! pEntry->fIsApplTracer && ! pEntry->fIsRoot)
   {
      /* only root may start controller tracers */
      if (pEntry->pCred != NULL)
      {
         crfree (pEntry->pCred);
      }
      FREE (pEntry, M_KCAPIMGRBUF);
      DBG (LOG_ERROR,
           "Appl. id %u: Attempt to start a controller tracer while not beeing root",
           uApplID);
      return (CRE_NO_RIGHTS_TO_TRACE);
   }
   
   /* lock the list of registered tracers (write lock) */
   sx_xlock (&g_sxTracerList);
   
   /* assign a new tracer id to the new tracer */
   pEntry->uTracerID = ++g_uCurrTracerID;
   *puTracerID = pEntry->uTracerID;

   /* add the new tracer to the front of the list */
   LIST_INSERT_HEAD (&g_tracerList, pEntry, le);
   
   /* count the new tracer */
   atomic_add_int (&e_uNumTracers, 1);
   u = atomic_load_acq_int (&e_uNumTracers);

   DBG (LOG_DEBUG,
        "appl. id %u: New tracer started with id %u, currently %u tracers",
        uApplID, pEntry->uTracerID, u);

   /* unlock the tracer list */
   sx_xunlock (&g_sxTracerList);
   
   return (CAPI_OK);
} /* kcapitrace_add_tracer */





/**
 * Remove a tracer.
 *
 + @param uTracerID             I: The id of the tracer to remove.
 *
 * @return CAPI_OK              The tracer was removed successfully.
 * @return Else                 An error occurred, no tracer was removed.
 */

unsigned kcapitrace_remove_tracer
   (unsigned uTracerID)
{
   TracerListEntry_t *pEntry;
   unsigned           uRes = CRE_UNKNOWN_TRACER_ID;
   
   /* lock the list of registered tracers (write lock) */
   sx_xlock (&g_sxTracerList);
   
   /* traverse the tracer list to find the tracer with the id specified */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      if (pEntry->uTracerID == uTracerID)
      {
         break;
      }
   }
   
   /* if the tracer was found: */
   if (pEntry && pEntry->uTracerID == uTracerID)
   {
      unsigned u;

      /* remove the entry from the list */
      LIST_REMOVE (pEntry, le);
      
      /* release the old list entry as it is not needed any more */
      if (pEntry->pCred != NULL)
      {
         crfree (pEntry->pCred);
      }
      FREE (pEntry, M_KCAPIMGRBUF);
      
      /* count the removed tracer */
      atomic_subtract_int (&e_uNumTracers, 1);
      u = atomic_load_acq_int (&e_uNumTracers);
      
      DBG (LOG_DEBUG, "Tracer %u removed from list, currently %u tracers",
           uTracerID, u);
      uRes = CAPI_OK;
   }
   else
   {
      DBG (LOG_ERROR, "Unable to find tracer with id %u, not registered",
           uTracerID);
   }
   
   /* unlock the tracer list */
   sx_xunlock (&g_sxTracerList);
   
   return (uRes);
} /* kcapitrace_remove_tracer */





/**
 * Update the application tracer configurations for a new CAPI application.
 *
 * This function must be called when a new CAPI application is registered and
 * there is at least one tracer active (e_nNumTraces greater than 0). During
 * this call all application tracer's configuration is updated with respect to
 * the right to receive trace messages according to the new application. For
 * each tracer the user id and group ids are checked against the effective
 * user and group id of the new application's process.
 *
 * @attention The caller must ensure that no call to register or release another
 *            application or tracer can interrupt the update process. This can
 *            only be accomplished by letting the CAPI application array access
 *            be blocked during this call. The implementation of this function
 *            will never call back into the application handling module, so this
 *            locking will not leed to a deadlock.
 *
 * @param uNewApplID            I: The application id of the new application.
 * @param effUserID             I: The effective user id of the new applications
 *                                 process owner.
 * @param effGroupID            I: The effective group id of the new
 *                                 applications process owner.
 *
 * @return Nothing.
 */

void kcapitrace_update_tracer_for_registered_appl
   (unsigned uNewApplID,
    uid_t    effUserID,
    gid_t    effGroupID)
{
   TracerListEntry_t *pEntry;
   
   /* check for valid application id */
   if (uNewApplID >= CAPI_MAX_APPLICATIONS)
   {
      DBG (LOG_ERROR, "got invalid appl. id %u", uNewApplID);
      return;
   }
   
   /* lock the list of registered tracers (write lock) */
   sx_xlock (&g_sxTracerList);
   
   /* traverse the list of registered tracers */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check if the current tracer is an application tracer */
      if (! pEntry->fIsApplTracer)
      {
         continue;
      }
      
      /* check if the current tracer is allowed to receive trace messages for
       * the application specified (i.e. check the access rights of the process
       * owner)
       */
      if (pEntry->fIsRoot)
      {
         pEntry->afTraceApplAllow [uNewApplID] = 1;
         DBG (LOG_DEBUG,
              "Tracer %u, appl. id %u is allowed to trace appl. id %u (is root)",
              pEntry->uTracerID, pEntry->uApplID, uNewApplID);
         continue;
      }
      if (pEntry->pCred == NULL)
      {
         continue;
      }
      if (pEntry->pCred->cr_uid == effUserID ||
          groupmember (effGroupID, pEntry->pCred) != 0)
      {
         pEntry->afTraceApplAllow [uNewApplID] = 1;
         DBG (LOG_DEBUG,
              "Tracer %u, appl. id %u is allowed to trace appl. id %u",
              pEntry->uTracerID, pEntry->uApplID, uNewApplID);
      }
   }
       
   /* unlock the tracer list */
   sx_xunlock (&g_sxTracerList);
   
} /* kcapitrace_update_tracer_for_registered_appl */





/**
 * Update the application tracer configurations for a released CAPI application.
 *
 * This function must be called when a CAPI application is released and there is
 * at least one tracer active (e_nNumTraces greater than 0). During this call
 * all application tracer's configuration is updated to not receive any trace
 * messages according to the released application id.
 *
 * @attention The caller must ensure that no call to register or release another
 *            application or tracer can interrupt the update process. This can
 *            only be accomplished by letting the CAPI application array access
 *            be blocked during this call. The implementation of this function
 *            will never call back into the application handling module, so this
 *            locking will not leed to a deadlock.
 *
 * @param uOldApplID            I: The application id of the removed
 *                                 application.
 *
 * @return Nothing.
 */

void kcapitrace_update_tracer_for_released_appl
   (unsigned uOldApplID)
{
   TracerListEntry_t *pEntry;
   
   /* check for valid application id */
   if (uOldApplID >= CAPI_MAX_APPLICATIONS)
   {
      DBG (LOG_ERROR, "got invalid appl. id %u", uOldApplID);
      return;
   }
   
   /* lock the list of registered tracers (write lock) */
   sx_xlock (&g_sxTracerList);
   
   /* traverse the list of registered tracers */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check if the current tracer is an application tracer */
      if (! pEntry->fIsApplTracer)
      {
         continue;
      }
      
      /* reset the flag of the current tracer for the application specified */
      pEntry->afTraceApplAllow [uOldApplID] = 0;
      DBG (LOG_DEBUG,
           "Tracer %u, appl. id %u will not receive trace messages for appl. id %u any more",
           pEntry->uTracerID, pEntry->uApplID, uOldApplID);
   }
      
   /* unlock the tracer list */
   sx_xunlock (&g_sxTracerList);
   
} /* kcapitrace_update_tracer_for_released_appl */





/**
 * Trace call to kcapi_register.
 *
 * @note This function must be called after the new application is registered by
 *       calling kcapitrace_update_tracer_for_registered_appl(). Otherwise the
 *       register call would not create any trace message.
 */

void kcapitrace_appl_register
   (unsigned  uMaxLogicalConnections,
    unsigned  uMaxBDataBlocks,
    unsigned  uMaxBDataLen,
    unsigned *puApplID,
    unsigned  uResult)
{
   struct timeval     tv;
   TracerListEntry_t *pEntry;
   struct mbuf       *pmbMsg;
   CAPIMsg_t         *pCapiMsg;
   CAPITraceMsg_t    *pTrcMsg;
   u_int32_t          dwLow;
   u_int32_t          dwHigh;
   
   /* prepare the timestamp for a possible trace message */
   getmicrotime (&tv);
   
   /* lock the list of registered tracers (read lock) */
   sx_slock (&g_sxTracerList);
   
   /* traverse the tracer list and deliver a trace message to every application
    * tracer with enough rights
    */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check for application tracer */
      if (! pEntry->fIsApplTracer)
      {
         continue;
      }
      
      /* check for the right to receive a trace message */
      if (puApplID && *puApplID < CAPI_MAX_APPLICATIONS &&
          pEntry->afTraceApplAllow [*puApplID] == 0)
      {
         continue;
      }
      
      /* create a Manufacturer-Indication with the tracing information */
      pmbMsg = kcapictlr_get_trace_mbuf
                  (sizeof (pCapiMsg->head) +
                   sizeof (pCapiMsg->info.manufacturer_ind) +
                   sizeof (pTrcMsg->head) +
                   sizeof (pTrcMsg->info.appl_register));
      if (! pmbMsg)
      {
         DBG (LOG_ERROR, "Tracer %u: out of mbufs", pEntry->uTracerID);
         continue;
      }
      pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pEntry->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_MANUFACTURER));
      C_PUT_WORD (pCapiMsg->head.wNum, 0);
      C_PUT_DWORD (pCapiMsg->head.dwCid, 0);
      C_PUT_DWORD (pCapiMsg->info.manufacturer_ind.dwManuID, C4B_TRACE_MANU_ID);
      pTrcMsg = (CAPITraceMsg_t *) ((u_int8_t *) &(pCapiMsg->info) +
                                    sizeof (pCapiMsg->info.manufacturer_ind));
      C_PUT_DWORD (pTrcMsg->head.dwTracerID, pEntry->uTracerID);
      C_PUT_DWORD (pTrcMsg->head.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (pTrcMsg->head.dwTimeMicroSec, tv.tv_usec);
      C_PUT_WORD (pTrcMsg->head.wMsgType, C4BTRC_MSGTYPE_APPL_REGISTER);
      C_PUT_DWORD (pTrcMsg->info.appl_register.dwMaxLogicalConnections,
                   uMaxLogicalConnections);
      C_PUT_DWORD (pTrcMsg->info.appl_register.dwMaxBDataBlocks,
                   uMaxBDataBlocks);
      C_PUT_DWORD (pTrcMsg->info.appl_register.dwMaxBDataLen, uMaxBDataLen);
      dwLow  = C_LOW_PTR_DWORD (puApplID);
      dwHigh = C_HIGH_PTR_DWORD (puApplID);
      C_PUT_DWORD (pTrcMsg->info.appl_register.dwApplIdPointerLow, dwLow);
      C_PUT_DWORD (pTrcMsg->info.appl_register.dwApplIdPointerHigh, dwHigh);
      if (puApplID)
      {
         C_PUT_DWORD (pTrcMsg->info.appl_register.dwApplID, *puApplID);
      }
      else
      {
         C_PUT_DWORD (pTrcMsg->info.appl_register.dwApplID, 0);
      }
      C_PUT_WORD (pTrcMsg->info.appl_register.wResult, uResult);
      C_PUT_WORD (pTrcMsg->head.wLength,
                  sizeof (pTrcMsg->head) +
                     sizeof (pTrcMsg->info.appl_register));
      C_PUT_WORD (pCapiMsg->head.wLen, pmbMsg->m_len);
      
      /* deliver the Manufacturer-Indication to the tracing application */
      kcapictlr_enqueue_trace_message (pEntry->uApplID, pmbMsg);
   }
   
   /* unlock the tracer list */
   sx_sunlock (&g_sxTracerList);
   
} /* kcapitrace_appl_register */





/**
 * Trace call to kcapi_release.
 */

void kcapitrace_appl_release
   (unsigned uApplID,
    unsigned uResult)
{
   struct timeval     tv;
   TracerListEntry_t *pEntry;
   struct mbuf       *pmbMsg;
   CAPIMsg_t         *pCapiMsg;
   CAPITraceMsg_t    *pTrcMsg;
   
   /* prepare the timestamp for a possible trace message */
   getmicrotime (&tv);
   
   /* lock the list of registered tracers (read lock) */
   sx_slock (&g_sxTracerList);
   
   /* traverse the tracer list and deliver a trace message to every application
    * tracer with enough rights
    */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check for application tracer */
      if (! pEntry->fIsApplTracer)
      {
         continue;
      }
      
      /* check for the right to receive a trace message */
      if (pEntry->afTraceApplAllow [uApplID] == 0)
      {
         continue;
      }
      
      /* create a Manufacturer-Indication with the tracing information */
      pmbMsg = kcapictlr_get_trace_mbuf
                  (sizeof (pCapiMsg->head) +
                   sizeof (pCapiMsg->info.manufacturer_ind) +
                   sizeof (pTrcMsg->head) +
                   sizeof (pTrcMsg->info.appl_release));
      if (! pmbMsg)
      {
         DBG (LOG_ERROR, "Tracer %u: Out of mbufs", pEntry->uTracerID);
         continue;
      }
      pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pEntry->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_MANUFACTURER));
      C_PUT_WORD (pCapiMsg->head.wNum, 0);
      C_PUT_DWORD (pCapiMsg->head.dwCid, 0);
      C_PUT_DWORD (pCapiMsg->info.manufacturer_ind.dwManuID, C4B_TRACE_MANU_ID);
      pTrcMsg = (CAPITraceMsg_t *) ((u_int8_t *) &(pCapiMsg->info) +
                                    sizeof (pCapiMsg->info.manufacturer_ind));
      C_PUT_DWORD (pTrcMsg->head.dwTracerID, pEntry->uTracerID);
      C_PUT_DWORD (pTrcMsg->head.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (pTrcMsg->head.dwTimeMicroSec, tv.tv_usec);
      C_PUT_WORD (pTrcMsg->head.wMsgType, C4BTRC_MSGTYPE_APPL_RELEASE);
      C_PUT_DWORD (pTrcMsg->info.appl_release.dwApplID, uApplID);
      C_PUT_WORD (pTrcMsg->info.appl_release.wResult, uResult);
      C_PUT_WORD (pTrcMsg->head.wLength,
                  sizeof (pTrcMsg->head) + sizeof (pTrcMsg->info.appl_release));
      C_PUT_WORD (pCapiMsg->head.wLen, pmbMsg->m_len);
      
      /* deliver the Manufacturer-Indication to the tracing application */
      kcapictlr_enqueue_trace_message (pEntry->uApplID, pmbMsg);
   }
   
   /* unlock the tracer list */
   sx_sunlock (&g_sxTracerList);
   
} /* kcapitrace_appl_release */





/**
 * Trace call to kcapi_put_message.
 */

void kcapitrace_appl_put_message
   (unsigned           uApplID,
    const struct mbuf *pmbOrgMsg,
    unsigned           uResult,
    int                fIsDataB3Msg)
{
   struct timeval     tv;
   TracerListEntry_t *pEntry;
   struct mbuf       *pmbMsg;
   CAPIMsg_t         *pCapiMsg;
   CAPITraceMsg_t    *pTrcMsg;
   u_int32_t          dwLow;
   u_int32_t          dwHigh;
   
   /* prepare the timestamp for a possible trace message */
   getmicrotime (&tv);
   
   /* lock the list of registered tracers (read lock) */
   sx_slock (&g_sxTracerList);
   
   /* traverse the tracer list and deliver a trace message to every application
    * tracer with enough rights
    */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check for application tracer */
      if (! pEntry->fIsApplTracer)
      {
         continue;
      }
      
      /* check for the right to receive a trace message */
      if (pEntry->afTraceApplAllow [uApplID] == 0)
      {
         continue;
      }
      
      /* if the message is a Data-B3 message and these messages shall not be
       * traced, the function calls also shall not be traced
       */
      if ((pEntry->ulTraceMask & C4BTRC_TRCMSK_DATA_B3_MSGS) == 0 &&
          fIsDataB3Msg)
      {
         continue;
      }
      
      /* create a Manufacturer-Indication with the tracing information */
      pmbMsg = kcapictlr_get_trace_mbuf
                  (sizeof (pCapiMsg->head) +
                   sizeof (pCapiMsg->info.manufacturer_ind) +
                   sizeof (pTrcMsg->head) +
                   sizeof (pTrcMsg->info.appl_put_message));
      if (! pmbMsg)
      {
         DBG (LOG_ERROR, "Tracer %u: Out of mbufs", pEntry->uTracerID);
         continue;
      }
      pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pEntry->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_MANUFACTURER));
      C_PUT_WORD (pCapiMsg->head.wNum, 0);
      C_PUT_DWORD (pCapiMsg->head.dwCid, 0);
      C_PUT_DWORD (pCapiMsg->info.manufacturer_ind.dwManuID, C4B_TRACE_MANU_ID);
      pTrcMsg = (CAPITraceMsg_t *) ((u_int8_t *) &(pCapiMsg->info) +
                                    sizeof (pCapiMsg->info.manufacturer_ind));
      C_PUT_DWORD (pTrcMsg->head.dwTracerID, pEntry->uTracerID);
      C_PUT_DWORD (pTrcMsg->head.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (pTrcMsg->head.dwTimeMicroSec, tv.tv_usec);
      C_PUT_WORD (pTrcMsg->head.wMsgType, C4BTRC_MSGTYPE_APPL_PUT_MESSAGE);
      C_PUT_DWORD (pTrcMsg->info.appl_put_message.dwApplID, uApplID);
      dwLow  = C_LOW_PTR_DWORD (pmbOrgMsg);
      dwHigh = C_HIGH_PTR_DWORD (pmbOrgMsg);
      C_PUT_DWORD (pTrcMsg->info.appl_put_message.dwMsgPointerLow, dwLow);
      C_PUT_DWORD (pTrcMsg->info.appl_put_message.dwMsgPointerHigh, dwHigh);
      C_PUT_WORD (pTrcMsg->info.appl_put_message.wResult, uResult);
      C_PUT_WORD (pTrcMsg->head.wLength,
                  sizeof (pTrcMsg->head) +
                     sizeof (pTrcMsg->info.appl_put_message));
      C_PUT_WORD (pCapiMsg->head.wLen, pmbMsg->m_len);
      
      /* deliver the Manufacturer-Indication to the tracing application */
      kcapictlr_enqueue_trace_message (pEntry->uApplID, pmbMsg);
   }
   
   /* unlock the tracer list */
   sx_sunlock (&g_sxTracerList);
   
} /* kcapitrace_appl_put_message */





/**
 * Trace call to kcapi_get_message.
 */

void kcapitrace_appl_get_message
   (unsigned      uApplID,
    struct mbuf **ppmbOrgMsg,
    unsigned      uResult)
{
   struct timeval     tv;
   TracerListEntry_t *pEntry;
   struct mbuf       *pmbMsg;
   CAPIMsg_t         *pCapiMsg;
   CAPITraceMsg_t    *pTrcMsg;
   u_int32_t          dwLow;
   u_int32_t          dwHigh;
   
   /* prepare the timestamp for a possible trace message */
   getmicrotime (&tv);
   
   /* lock the list of registered tracers (read lock) */
   sx_slock (&g_sxTracerList);
   
   /* traverse the tracer list and deliver a trace message to every application
    * tracer with enough rights
    */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check for application tracer */
      if (! pEntry->fIsApplTracer)
      {
         continue;
      }
      
      /* check for the right to receive a trace message */
      if (pEntry->afTraceApplAllow [uApplID] == 0)
      {
         continue;
      }
      
      /* if the message is a Data-B3 message and these messages shall not be
       * traced, the function calls also shall not be traced
       */
      if ((pEntry->ulTraceMask & C4BTRC_TRCMSK_DATA_B3_MSGS) == 0 &&
          ppmbOrgMsg && *ppmbOrgMsg &&
          (CAPI_GET_CMD (mtod (*ppmbOrgMsg, CAPIMsg_t *)) &
           CAPI_CMDMASK_COMMAND) == C_DATA_B3)
      {
         continue;
      }
      
      /* create a Manufacturer-Indication with the tracing information */
      pmbMsg = kcapictlr_get_trace_mbuf
                  (sizeof (pCapiMsg->head) +
                   sizeof (pCapiMsg->info.manufacturer_ind) +
                   sizeof (pTrcMsg->head) +
                   sizeof (pTrcMsg->info.appl_get_message));
      if (! pmbMsg)
      {
         DBG (LOG_ERROR, "Tracer %u: Out of mbufs", pEntry->uTracerID);
         continue;
      }
      pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pEntry->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_MANUFACTURER));
      C_PUT_WORD (pCapiMsg->head.wNum, 0);
      C_PUT_DWORD (pCapiMsg->head.dwCid, 0);
      C_PUT_DWORD (pCapiMsg->info.manufacturer_ind.dwManuID, C4B_TRACE_MANU_ID);
      pTrcMsg = (CAPITraceMsg_t *) ((u_int8_t *) &(pCapiMsg->info) +
                                    sizeof (pCapiMsg->info.manufacturer_ind));
      C_PUT_DWORD (pTrcMsg->head.dwTracerID, pEntry->uTracerID);
      C_PUT_DWORD (pTrcMsg->head.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (pTrcMsg->head.dwTimeMicroSec, tv.tv_usec);
      C_PUT_WORD (pTrcMsg->head.wMsgType, C4BTRC_MSGTYPE_APPL_GET_MESSAGE);
      C_PUT_DWORD (pTrcMsg->info.appl_get_message.dwApplID, uApplID);
      dwLow  = C_LOW_PTR_DWORD (ppmbOrgMsg);
      dwHigh = C_HIGH_PTR_DWORD (ppmbOrgMsg);
      C_PUT_DWORD (pTrcMsg->info.appl_get_message.dwPointerArgLow, dwLow);
      C_PUT_DWORD (pTrcMsg->info.appl_get_message.dwPointerArgHigh, dwHigh);
      if (ppmbOrgMsg != NULL)
      {
         dwLow  = C_LOW_PTR_DWORD (*ppmbOrgMsg);
         dwHigh = C_HIGH_PTR_DWORD (*ppmbOrgMsg);
      }
      else
      {
         dwLow  = 0;
         dwHigh = 0;
      }
      C_PUT_DWORD (pTrcMsg->info.appl_get_message.dwMsgPointerLow, dwLow);
      C_PUT_DWORD (pTrcMsg->info.appl_get_message.dwMsgPointerHigh, dwHigh);
      C_PUT_WORD (pTrcMsg->info.appl_get_message.wResult, uResult);
      C_PUT_WORD (pTrcMsg->head.wLength,
                  sizeof (pTrcMsg->head) +
                     sizeof (pTrcMsg->info.appl_get_message));
      C_PUT_WORD (pCapiMsg->head.wLen, pmbMsg->m_len);
      
      /* deliver the Manufacturer-Indication to the tracing application */
      kcapictlr_enqueue_trace_message (pEntry->uApplID, pmbMsg);
   }
   
   /* unlock the tracer list */
   sx_sunlock (&g_sxTracerList);
   
} /* kcapitrace_appl_get_message */





/**
 * Trace call to kcapi_set_signal.
 */

#if defined (__alpha__) || defined (__ia64__) || defined (__amd64__) || defined (__sparc64__)
void kcapitrace_appl_set_signal
   (unsigned  uApplID,
    void (*pfnCallback) (unsigned uApplID, u_int64_t qwParam),
    u_int64_t qwParam,
    unsigned  uResult)
#else /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
void kcapitrace_appl_set_signal
   (unsigned  uApplID,
    void (*pfnCallback) (unsigned uApplID, u_int32_t dwParam),
    u_int32_t dwParam,
    unsigned  uResult)
#endif /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
{
   struct timeval     tv;
   TracerListEntry_t *pEntry;
   struct mbuf       *pmbMsg;
   CAPIMsg_t         *pCapiMsg;
   CAPITraceMsg_t    *pTrcMsg;
   u_int32_t          dwLow;
   u_int32_t          dwHigh;
   size_t             nLenMsg;
   
   /* prepare the timestamp for a possible trace message */
   getmicrotime (&tv);
   
   /* lock the list of registered tracers (read lock) */
   sx_slock (&g_sxTracerList);
   
   /* traverse the tracer list and deliver a trace message to every application
    * tracer with enough rights
    */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check for application tracer */
      if (! pEntry->fIsApplTracer)
      {
         continue;
      }
      
      /* check for the right to receive a trace message */
      if (pEntry->afTraceApplAllow [uApplID] == 0)
      {
         continue;
      }
      
      /* create a Manufacturer-Indication with the tracing information */
      pmbMsg = kcapictlr_get_trace_mbuf
                  (sizeof (pCapiMsg->head) +
                   sizeof (pCapiMsg->info.manufacturer_ind) +
                   sizeof (pTrcMsg->head) +
                   sizeof (pTrcMsg->info.appl_set_signal));
      if (! pmbMsg)
      {
         DBG (LOG_ERROR, "Tracer %u: Out of mbufs", pEntry->uTracerID);
         continue;
      }
      pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pEntry->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_MANUFACTURER));
      C_PUT_WORD (pCapiMsg->head.wNum, 0);
      C_PUT_DWORD (pCapiMsg->head.dwCid, 0);
      C_PUT_DWORD (pCapiMsg->info.manufacturer_ind.dwManuID, C4B_TRACE_MANU_ID);
      pTrcMsg = (CAPITraceMsg_t *) ((u_int8_t *) &(pCapiMsg->info) +
                                    sizeof (pCapiMsg->info.manufacturer_ind));
      C_PUT_DWORD (pTrcMsg->head.dwTracerID, pEntry->uTracerID);
      C_PUT_DWORD (pTrcMsg->head.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (pTrcMsg->head.dwTimeMicroSec, tv.tv_usec);
      C_PUT_WORD (pTrcMsg->head.wMsgType, C4BTRC_MSGTYPE_APPL_SET_SIGNAL);
      C_PUT_DWORD (pTrcMsg->info.appl_set_signal.dwApplID, uApplID);
      dwLow  = C_LOW_PTR_DWORD (pfnCallback);
      dwHigh = C_HIGH_PTR_DWORD (pfnCallback);
      C_PUT_DWORD (pTrcMsg->info.appl_set_signal.dwCallbackLow, dwLow);
      C_PUT_DWORD (pTrcMsg->info.appl_set_signal.dwCallbackHigh, dwHigh);
#if defined (__alpha__) || defined (__ia64__) || defined (__amd64__) || defined (__sparc64__)
      C_PUT_DWORD (pTrcMsg->info.appl_set_signal.dwParam, 0);
      C_PUT_QWORD (pTrcMsg->info.appl_set_signal.qwParam64, qwParam);
      nLenMsg = sizeof (pTrcMsg->head) + sizeof (pTrcMsg->info.appl_set_signal);
#else /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
      /* Note: To mark the message as originating from a 32bit application, the
       *       qword at the end of the message is left out.
       */
      C_PUT_DWORD (pTrcMsg->info.appl_set_signal.dwParam, dwParam);
      nLenMsg = sizeof (pTrcMsg->head) +
                sizeof (pTrcMsg->info.appl_set_signal) -
                sizeof (pTrcMsg->info.appl_set_signal.qwParam64);
#endif /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
      C_PUT_WORD (pTrcMsg->info.appl_set_signal.wResult, uResult);
      C_PUT_WORD (pTrcMsg->head.wLength, nLenMsg);
      C_PUT_WORD (pCapiMsg->head.wLen, pmbMsg->m_len);
      
      /* deliver the Manufacturer-Indication to the tracing application */
      kcapictlr_enqueue_trace_message (pEntry->uApplID, pmbMsg);
   }
   
   /* unlock the tracer list */
   sx_sunlock (&g_sxTracerList);
   
} /* kcapitrace_appl_set_signal */





/**
 * Trace call to kcapi_get_manufacturer.
 */

void kcapitrace_appl_get_manufacturer
   (unsigned       uCtlrNum,
    unsigned char *pszBuffer,
    unsigned       uResult)
{
   struct timeval     tv;
   TracerListEntry_t *pEntry;
   struct mbuf       *pmbMsg;
   CAPIMsg_t         *pCapiMsg;
   CAPITraceMsg_t    *pTrcMsg;
   u_int32_t          dwLow;
   u_int32_t          dwHigh;
   
   /* prepare the timestamp for a possible trace message */
   getmicrotime (&tv);
   
   /* lock the list of registered tracers (read lock) */
   sx_slock (&g_sxTracerList);
   
   /* traverse the tracer list and deliver a trace message to every application
    * tracer with enough rights
    */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check for application tracer */
      if (! pEntry->fIsApplTracer)
      {
         continue;
      }
      
      /* Note: This CAPI function without an application context, so every
       *       tracer may get informed about the call.
       */
      
      /* create a Manufacturer-Indication with the tracing information */
      pmbMsg = kcapictlr_get_trace_mbuf
                  (sizeof (pCapiMsg->head) +
                   sizeof (pCapiMsg->info.manufacturer_ind) +
                   sizeof (pTrcMsg->head) +
                   sizeof (pTrcMsg->info.appl_get_manufacturer));
      if (! pmbMsg)
      {
         DBG (LOG_ERROR, "Tracer %u: Out of mbufs", pEntry->uTracerID);
         continue;
      }
      pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pEntry->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_MANUFACTURER));
      C_PUT_WORD (pCapiMsg->head.wNum, 0);
      C_PUT_DWORD (pCapiMsg->head.dwCid, 0);
      C_PUT_DWORD (pCapiMsg->info.manufacturer_ind.dwManuID, C4B_TRACE_MANU_ID);
      pTrcMsg = (CAPITraceMsg_t *) ((u_int8_t *) &(pCapiMsg->info) +
                                    sizeof (pCapiMsg->info.manufacturer_ind));
      C_PUT_DWORD (pTrcMsg->head.dwTracerID, pEntry->uTracerID);
      C_PUT_DWORD (pTrcMsg->head.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (pTrcMsg->head.dwTimeMicroSec, tv.tv_usec);
      C_PUT_WORD (pTrcMsg->head.wMsgType, C4BTRC_MSGTYPE_APPL_GET_MANUFACTURER);
      C_PUT_DWORD (pTrcMsg->info.appl_get_manufacturer.dwCtlrNum, uCtlrNum);
      dwLow  = C_LOW_PTR_DWORD (pszBuffer);
      dwHigh = C_HIGH_PTR_DWORD (pszBuffer);
      C_PUT_DWORD (pTrcMsg->info.appl_get_manufacturer.dwBufferPointerLow,
                   dwLow);
      C_PUT_DWORD (pTrcMsg->info.appl_get_manufacturer.dwBufferPointerHigh,
                   dwHigh);
      bzero (pTrcMsg->info.appl_get_manufacturer.abBuffer,
             sizeof (pTrcMsg->info.appl_get_manufacturer.abBuffer));
      if (pszBuffer != NULL)
      {
         strncpy (pTrcMsg->info.appl_get_manufacturer.abBuffer, pszBuffer,
                  sizeof (pTrcMsg->info.appl_get_manufacturer.abBuffer) - 1);
      }
      C_PUT_WORD (pTrcMsg->info.appl_get_manufacturer.wResult, uResult);
      C_PUT_WORD (pTrcMsg->head.wLength,
                  sizeof (pTrcMsg->head) +
                     sizeof (pTrcMsg->info.appl_get_manufacturer));
      C_PUT_WORD (pCapiMsg->head.wLen, pmbMsg->m_len);
      
      /* deliver the Manufacturer-Indication to the tracing application */
      kcapictlr_enqueue_trace_message (pEntry->uApplID, pmbMsg);
   }
   
   /* unlock the tracer list */
   sx_sunlock (&g_sxTracerList);
   
} /* kcapitrace_appl_get_manufacturer */





/**
 * Trace call to kcapi_get_version.
 */

void kcapitrace_appl_get_version
   (unsigned uCtlrNum,
    unsigned uCAPIMajor,
    unsigned uCAPIMinor,
    unsigned uManufacturerMajor,
    unsigned uManufacturerMinor,
    unsigned uBSDMajor,
    unsigned uBSDMinor,
    unsigned uResult)
{
   struct timeval     tv;
   TracerListEntry_t *pEntry;
   struct mbuf       *pmbMsg;
   CAPIMsg_t         *pCapiMsg;
   CAPITraceMsg_t    *pTrcMsg;
   
   /* prepare the timestamp for a possible trace message */
   getmicrotime (&tv);
   
   /* lock the list of registered tracers (read lock) */
   sx_slock (&g_sxTracerList);
   
   /* traverse the tracer list and deliver a trace message to every application
    * tracer with enough rights
    */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check for application tracer */
      if (! pEntry->fIsApplTracer)
      {
         continue;
      }
      
      /* Note: This CAPI function without an application context, so every
       *       tracer may get informed about the call.
       */
      
      /* create a Manufacturer-Indication with the tracing information */
      pmbMsg = kcapictlr_get_trace_mbuf
                  (sizeof (pCapiMsg->head) +
                   sizeof (pCapiMsg->info.manufacturer_ind) +
                   sizeof (pTrcMsg->head) +
                   sizeof (pTrcMsg->info.appl_get_version));
      if (! pmbMsg)
      {
         DBG (LOG_ERROR, "Tracer %u: Out of mbufs", pEntry->uTracerID);
         continue;
      }
      pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pEntry->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_MANUFACTURER));
      C_PUT_WORD (pCapiMsg->head.wNum, 0);
      C_PUT_DWORD (pCapiMsg->head.dwCid, 0);
      C_PUT_DWORD (pCapiMsg->info.manufacturer_ind.dwManuID, C4B_TRACE_MANU_ID);
      pTrcMsg = (CAPITraceMsg_t *) ((u_int8_t *) &(pCapiMsg->info) +
                                    sizeof (pCapiMsg->info.manufacturer_ind));
      C_PUT_DWORD (pTrcMsg->head.dwTracerID, pEntry->uTracerID);
      C_PUT_DWORD (pTrcMsg->head.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (pTrcMsg->head.dwTimeMicroSec, tv.tv_usec);
      C_PUT_WORD (pTrcMsg->head.wMsgType, C4BTRC_MSGTYPE_APPL_GET_VERSION);
      C_PUT_DWORD (pTrcMsg->info.appl_get_version.dwCtlrNum, uCtlrNum);
      C_PUT_DWORD (pTrcMsg->info.appl_get_version.dwCAPIMajor, uCAPIMajor);
      C_PUT_DWORD (pTrcMsg->info.appl_get_version.dwCAPIMinor, uCAPIMinor);
      C_PUT_DWORD (pTrcMsg->info.appl_get_version.dwManufacturerMajor,
                   uManufacturerMajor);
      C_PUT_DWORD (pTrcMsg->info.appl_get_version.dwManufacturerMinor,
                   uManufacturerMinor);
      C_PUT_DWORD (pTrcMsg->info.appl_get_version.dwBSDMajor, uBSDMajor);
      C_PUT_DWORD (pTrcMsg->info.appl_get_version.dwBSDMinor, uBSDMinor);
      C_PUT_WORD (pTrcMsg->info.appl_get_version.wResult, uResult);
      C_PUT_WORD (pTrcMsg->head.wLength,
                  sizeof (pTrcMsg->head) +
                     sizeof (pTrcMsg->info.appl_get_version));
      C_PUT_WORD (pCapiMsg->head.wLen, pmbMsg->m_len);
      
      /* deliver the Manufacturer-Indication to the tracing application */
      kcapictlr_enqueue_trace_message (pEntry->uApplID, pmbMsg);
   }
   
   /* unlock the tracer list */
   sx_sunlock (&g_sxTracerList);
   
} /* kcapitrace_appl_get_version */





/**
 * Trace call to kcapi_get_serial_number.
 */

void kcapitrace_appl_get_serial_number
   (unsigned       uCtlrNum,
    unsigned char *pszBuffer,
    unsigned       uResult)
{
   struct timeval     tv;
   TracerListEntry_t *pEntry;
   struct mbuf       *pmbMsg;
   CAPIMsg_t         *pCapiMsg;
   CAPITraceMsg_t    *pTrcMsg;
   u_int32_t          dwLow;
   u_int32_t          dwHigh;
   
   /* prepare the timestamp for a possible trace message */
   getmicrotime (&tv);
   
   /* lock the list of registered tracers (read lock) */
   sx_slock (&g_sxTracerList);
   
   /* traverse the tracer list and deliver a trace message to every application
    * tracer with enough rights
    */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check for application tracer */
      if (! pEntry->fIsApplTracer)
      {
         continue;
      }
      
      /* Note: This CAPI function without an application context, so every
       *       tracer may get informed about the call.
       */
      
      /* create a Manufacturer-Indication with the tracing information */
      pmbMsg = kcapictlr_get_trace_mbuf
                  (sizeof (pCapiMsg->head) +
                   sizeof (pCapiMsg->info.manufacturer_ind) +
                   sizeof (pTrcMsg->head) +
                   sizeof (pTrcMsg->info.appl_get_serial_number));
      if (! pmbMsg)
      {
         DBG (LOG_ERROR, "Tracer %u: Out of mbufs", pEntry->uTracerID);
         continue;
      }
      pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pEntry->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_MANUFACTURER));
      C_PUT_WORD (pCapiMsg->head.wNum, 0);
      C_PUT_DWORD (pCapiMsg->head.dwCid, 0);
      C_PUT_DWORD (pCapiMsg->info.manufacturer_ind.dwManuID, C4B_TRACE_MANU_ID);
      pTrcMsg = (CAPITraceMsg_t *) ((u_int8_t *) &(pCapiMsg->info) +
                                    sizeof (pCapiMsg->info.manufacturer_ind));
      C_PUT_DWORD (pTrcMsg->head.dwTracerID, pEntry->uTracerID);
      C_PUT_DWORD (pTrcMsg->head.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (pTrcMsg->head.dwTimeMicroSec, tv.tv_usec);
      C_PUT_WORD (pTrcMsg->head.wMsgType,
                  C4BTRC_MSGTYPE_APPL_GET_SERIAL_NUMBER);
      C_PUT_DWORD (pTrcMsg->info.appl_get_serial_number.dwCtlrNum, uCtlrNum);
      dwLow  = C_LOW_PTR_DWORD (pszBuffer);
      dwHigh = C_HIGH_PTR_DWORD (pszBuffer);
      C_PUT_DWORD (pTrcMsg->info.appl_get_serial_number.dwBufferPointerLow,
                   dwLow);
      C_PUT_DWORD (pTrcMsg->info.appl_get_serial_number.dwBufferPointerHigh,
                   dwHigh);
      bzero (pTrcMsg->info.appl_get_serial_number.abBuffer,
             sizeof (pTrcMsg->info.appl_get_serial_number.abBuffer));
      if (pszBuffer)
      {
         strncpy (pTrcMsg->info.appl_get_serial_number.abBuffer, pszBuffer,
                  sizeof (pTrcMsg->info.appl_get_serial_number.abBuffer) - 1);
      }
      C_PUT_WORD (pTrcMsg->info.appl_get_serial_number.wResult, uResult);
      C_PUT_WORD (pTrcMsg->head.wLength,
                  sizeof (pTrcMsg->head) +
                     sizeof (pTrcMsg->info.appl_get_serial_number));
      C_PUT_WORD (pCapiMsg->head.wLen, pmbMsg->m_len);
      
      /* deliver the Manufacturer-Indication to the tracing application */
      kcapictlr_enqueue_trace_message (pEntry->uApplID, pmbMsg);
   }
   
   /* unlock the tracer list */
   sx_sunlock (&g_sxTracerList);
   
} /* kcapitrace_appl_get_serial_number */





/**
 * Trace call to kcapi_get_profile.
 */

void kcapitrace_appl_get_profile
   (unsigned             uCtlrNum,
    CAPIProfileBuffer_t *pProfile,
    unsigned             uResult)
{
   struct timeval     tv;
   TracerListEntry_t *pEntry;
   struct mbuf       *pmbMsg;
   CAPIMsg_t         *pCapiMsg;
   CAPITraceMsg_t    *pTrcMsg;
   u_int32_t          dwLow;
   u_int32_t          dwHigh;
   
   /* prepare the timestamp for a possible trace message */
   getmicrotime (&tv);
   
   /* lock the list of registered tracers (read lock) */
   sx_slock (&g_sxTracerList);
   
   /* traverse the tracer list and deliver a trace message to every application
    * tracer with enough rights
    */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check for application tracer */
      if (! pEntry->fIsApplTracer)
      {
         continue;
      }
      
      /* Note: This CAPI function without an application context, so every
       *       tracer may get informed about the call.
       */
      
      /* create a Manufacturer-Indication with the tracing information */
      pmbMsg = kcapictlr_get_trace_mbuf
                  (sizeof (pCapiMsg->head) +
                   sizeof (pCapiMsg->info.manufacturer_ind) +
                   sizeof (pTrcMsg->head) +
                   sizeof (pTrcMsg->info.appl_get_profile));
      if (! pmbMsg)
      {
         DBG (LOG_ERROR, "Tracer %u: Out of mbufs", pEntry->uTracerID);
         continue;
      }
      pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pEntry->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_MANUFACTURER));
      C_PUT_WORD (pCapiMsg->head.wNum, 0);
      C_PUT_DWORD (pCapiMsg->head.dwCid, 0);
      C_PUT_DWORD (pCapiMsg->info.manufacturer_ind.dwManuID, C4B_TRACE_MANU_ID);
      pTrcMsg = (CAPITraceMsg_t *) ((u_int8_t *) &(pCapiMsg->info) +
                                    sizeof (pCapiMsg->info.manufacturer_ind));
      C_PUT_DWORD (pTrcMsg->head.dwTracerID, pEntry->uTracerID);
      C_PUT_DWORD (pTrcMsg->head.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (pTrcMsg->head.dwTimeMicroSec, tv.tv_usec);
      C_PUT_WORD (pTrcMsg->head.wMsgType, C4BTRC_MSGTYPE_APPL_GET_PROFILE);
      C_PUT_DWORD (pTrcMsg->info.appl_get_profile.dwCtlrNum, uCtlrNum);
      dwLow  = C_LOW_PTR_DWORD (pProfile);
      dwHigh = C_HIGH_PTR_DWORD (pProfile);
      C_PUT_DWORD (pTrcMsg->info.appl_get_profile.dwBufferPointerLow, dwLow);
      C_PUT_DWORD (pTrcMsg->info.appl_get_profile.dwBufferPointerHigh, dwHigh);
      if (pProfile != NULL)
      {
         bcopy (pProfile, &(pTrcMsg->info.appl_get_profile.profile),
                sizeof (pTrcMsg->info.appl_get_profile.profile));
      }
      else
      {
         bzero (&(pTrcMsg->info.appl_get_profile.profile),
                sizeof (pTrcMsg->info.appl_get_profile.profile));
      }
      C_PUT_WORD (pTrcMsg->info.appl_get_profile.wResult, uResult);
      C_PUT_WORD (pTrcMsg->head.wLength,
                  sizeof (pTrcMsg->head) +
                     sizeof (pTrcMsg->info.appl_get_profile));
      C_PUT_WORD (pCapiMsg->head.wLen, pmbMsg->m_len);
      
      /* deliver the Manufacturer-Indication to the tracing application */
      kcapictlr_enqueue_trace_message (pEntry->uApplID, pmbMsg);
   }
   
   /* unlock the tracer list */
   sx_sunlock (&g_sxTracerList);
   
} /* kcapitrace_appl_get_profile */





/**
 * Trace call to kcapi_get_ctlr_driver_info.
 */

void kcapitrace_appl_get_ctlr_driver_info
   (unsigned              uCtlrNum,
    CAPICtlrDriverInfo_t *pDrvInfo,
    unsigned              uResult)
{
   struct timeval     tv;
   TracerListEntry_t *pEntry;
   struct mbuf       *pmbMsg;
   CAPIMsg_t         *pCapiMsg;
   CAPITraceMsg_t    *pTrcMsg;
   u_int32_t          dwLow;
   u_int32_t          dwHigh;
   
   /* prepare the timestamp for a possible trace message */
   getmicrotime (&tv);
   
   /* lock the list of registered tracers (read lock) */
   sx_slock (&g_sxTracerList);
   
   /* traverse the tracer list and deliver a trace message to every application
    * tracer with enough rights
    */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check for application tracer */
      if (! pEntry->fIsApplTracer)
      {
         continue;
      }
      
      /* Note: This CAPI function without an application context, so every
       *       tracer may get informed about the call.
       */
      
      /* create a Manufacturer-Indication with the tracing information */
      pmbMsg = kcapictlr_get_trace_mbuf
                  (sizeof (pCapiMsg->head) +
                   sizeof (pCapiMsg->info.manufacturer_ind) +
                   sizeof (pTrcMsg->head) +
                   sizeof (pTrcMsg->info.appl_get_ctlr_driver_info));
      if (! pmbMsg)
      {
         DBG (LOG_ERROR, "Tracer %u: Out of mbufs", pEntry->uTracerID);
         continue;
      }
      pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pEntry->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_MANUFACTURER));
      C_PUT_WORD (pCapiMsg->head.wNum, 0);
      C_PUT_DWORD (pCapiMsg->head.dwCid, 0);
      C_PUT_DWORD (pCapiMsg->info.manufacturer_ind.dwManuID, C4B_TRACE_MANU_ID);
      pTrcMsg = (CAPITraceMsg_t *) ((u_int8_t *) &(pCapiMsg->info) +
                                    sizeof (pCapiMsg->info.manufacturer_ind));
      C_PUT_DWORD (pTrcMsg->head.dwTracerID, pEntry->uTracerID);
      C_PUT_DWORD (pTrcMsg->head.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (pTrcMsg->head.dwTimeMicroSec, tv.tv_usec);
      C_PUT_WORD (pTrcMsg->head.wMsgType,
                  C4BTRC_MSGTYPE_APPL_GET_CTLR_DRIVER_INFO);
      C_PUT_DWORD (pTrcMsg->info.appl_get_ctlr_driver_info.dwCtlrNum, uCtlrNum);
      dwLow  = C_LOW_PTR_DWORD (pDrvInfo);
      dwHigh = C_HIGH_PTR_DWORD (pDrvInfo);
      C_PUT_DWORD (pTrcMsg->info.appl_get_ctlr_driver_info.dwBufferPointerLow,
                   dwLow);
      C_PUT_DWORD (pTrcMsg->info.appl_get_ctlr_driver_info.dwBufferPointerHigh,
                   dwHigh);
      if (pDrvInfo != NULL)
      {
         bcopy (pDrvInfo, &(pTrcMsg->info.appl_get_ctlr_driver_info.driverInfo),
                sizeof (pTrcMsg->info.appl_get_ctlr_driver_info.driverInfo));
      }
      else
      {
         bzero (&(pTrcMsg->info.appl_get_ctlr_driver_info.driverInfo),
                sizeof (pTrcMsg->info.appl_get_ctlr_driver_info.driverInfo));
      }
      C_PUT_WORD (pTrcMsg->info.appl_get_ctlr_driver_info.wResult, uResult);
      C_PUT_WORD (pTrcMsg->head.wLength,
                  sizeof (pTrcMsg->head) +
                     sizeof (pTrcMsg->info.appl_get_ctlr_driver_info));
      C_PUT_WORD (pCapiMsg->head.wLen, pmbMsg->m_len);
      
      /* deliver the Manufacturer-Indication to the tracing application */
      kcapictlr_enqueue_trace_message (pEntry->uApplID, pmbMsg);
   }
   
   /* unlock the tracer list */
   sx_sunlock (&g_sxTracerList);
   
} /* kcapitrace_appl_get_ctlr_driver_info */





/**
 * Trace call to kcapi_reset_ctlr.
 */

void kcapitrace_appl_reset_ctlr
   (unsigned             uCtlrNum,
    size_t               nNumDataBlocks,
    CAPICtlrDataBlock_t *paDataBlocks,
    unsigned             uResult)
{
   struct timeval            tv;
   TracerListEntry_t        *pEntry;
   struct mbuf              *pmbMsg;
   CAPIMsg_t                *pCapiMsg;
   CAPITraceMsg_t           *pTrcMsg;
   CAPITraceCtlrDataBlock_t *p;
   u_int32_t                 dwLow;
   u_int32_t                 dwHigh;
   int                       i;
   
   /* prepare the timestamp for a possible trace message */
   getmicrotime (&tv);
   
   /* lock the list of registered tracers (read lock) */
   sx_slock (&g_sxTracerList);
   
   /* traverse the tracer list and deliver a trace message to every application
    * tracer with enough rights
    */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check for application tracer */
      if (! pEntry->fIsApplTracer)
      {
         continue;
      }
      
      /* check for the right to receive a trace message */
      /* Note: The kcapi_reset_ctlr() call can only be executed as root, so only
       *       root tracers may get informed about this call.
       */
      if (! pEntry->fIsRoot)
      {
         continue;
      }
      
      /* create a Manufacturer-Indication with the tracing information */
      pmbMsg = kcapictlr_get_trace_mbuf
                  (sizeof (pCapiMsg->head) +
                   sizeof (pCapiMsg->info.manufacturer_ind) +
                   sizeof (pTrcMsg->head) +
                   sizeof (pTrcMsg->info.appl_reset_ctlr) +
                   nNumDataBlocks * sizeof (CAPITraceCtlrDataBlock_t));
      if (! pmbMsg)
      {
         DBG (LOG_ERROR, "Tracer %u: Out of mbufs", pEntry->uTracerID);
         continue;
      }
      pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pEntry->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_MANUFACTURER));
      C_PUT_WORD (pCapiMsg->head.wNum, 0);
      C_PUT_DWORD (pCapiMsg->head.dwCid, 0);
      C_PUT_DWORD (pCapiMsg->info.manufacturer_ind.dwManuID, C4B_TRACE_MANU_ID);
      pTrcMsg = (CAPITraceMsg_t *) ((u_int8_t *) &(pCapiMsg->info) +
                                    sizeof (pCapiMsg->info.manufacturer_ind));
      C_PUT_DWORD (pTrcMsg->head.dwTracerID, pEntry->uTracerID);
      C_PUT_DWORD (pTrcMsg->head.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (pTrcMsg->head.dwTimeMicroSec, tv.tv_usec);
      C_PUT_WORD (pTrcMsg->head.wMsgType, C4BTRC_MSGTYPE_APPL_RESET_CTLR);
      C_PUT_DWORD (pTrcMsg->info.appl_reset_ctlr.dwCtlrNum, uCtlrNum);
      C_PUT_WORD (pTrcMsg->info.appl_reset_ctlr.wResult, uResult);
      C_PUT_DWORD (pTrcMsg->info.appl_reset_ctlr.dwNumDataBlocks,
                   nNumDataBlocks);
      for (i = 0,
              p = (CAPITraceCtlrDataBlock_t *)
                     ((u_int8_t *) &(pTrcMsg->info.appl_reset_ctlr) +
                      sizeof (pTrcMsg->info.appl_reset_ctlr));
           (size_t) i < nNumDataBlocks;
           ++i, ++p)
      {
         C_PUT_DWORD (p->dwLenDataBlock, paDataBlocks  [i].nLenDataBlock);
         dwLow  = C_LOW_PTR_DWORD (paDataBlocks [i].paucDataBlock);
         dwHigh = C_HIGH_PTR_DWORD (paDataBlocks [i].paucDataBlock);
         C_PUT_DWORD (p->dwDataBlockPointerLow, dwLow);
         C_PUT_DWORD (p->dwDataBlockPointerHigh, dwHigh);
      }
      C_PUT_WORD (pTrcMsg->head.wLength,
                  sizeof (pTrcMsg->head) +
                     sizeof (pTrcMsg->info.appl_reset_ctlr) +
                     nNumDataBlocks * sizeof (CAPITraceCtlrDataBlock_t));
      C_PUT_WORD (pCapiMsg->head.wLen, pmbMsg->m_len);
      
      /* deliver the Manufacturer-Indication to the tracing application */
      kcapictlr_enqueue_trace_message (pEntry->uApplID, pmbMsg);
   }
   
   /* unlock the tracer list */
   sx_sunlock (&g_sxTracerList);
   
} /* kcapitrace_appl_reset_ctlr */





/**
 * Trace call to CAPI_REGISTER in CAPI driver.
 */

void kcapitrace_drvr_register
   (unsigned uUniqueCtlrNum,
    unsigned uDrvCtlrNum,
    unsigned uMaxLogicalConnections,
    unsigned uMaxBDataBlocks,
    unsigned uMaxBDataLen,
    unsigned uUniqueApplID,
    unsigned uCtlrApplID,
    unsigned uResult)
{
   struct timeval     tv;
   TracerListEntry_t *pEntry;
   struct mbuf       *pmbMsg;
   CAPIMsg_t         *pCapiMsg;
   CAPITraceMsg_t    *pTrcMsg;
   
   /* prepare the timestamp for a possible trace message */
   getmicrotime (&tv);
   
   /* lock the list of registered tracers (read lock) */
   sx_slock (&g_sxTracerList);
   
   /* traverse the tracer list and deliver a trace message to every application
    * tracer with enough rights
    */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check for controller tracer for controller specified */
      if (pEntry->fIsApplTracer || pEntry->uCtlrNum != uUniqueCtlrNum)
      {
         continue;
      }
      
      /* check for the right to receive a trace message */
      if (! pEntry->fIsRoot)
      {
         continue;
      }
      
      /* create a Manufacturer-Indication with the tracing information */
      pmbMsg = kcapictlr_get_trace_mbuf
                  (sizeof (pCapiMsg->head) +
                   sizeof (pCapiMsg->info.manufacturer_ind) +
                   sizeof (pTrcMsg->head) +
                   sizeof (pTrcMsg->info.drvr_register));
      if (! pmbMsg)
      {
         DBG (LOG_ERROR, "Tracer %u: Out of mbufs", pEntry->uTracerID);
         continue;
      }
      pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pEntry->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_MANUFACTURER));
      C_PUT_WORD (pCapiMsg->head.wNum, 0);
      C_PUT_DWORD (pCapiMsg->head.dwCid, uUniqueCtlrNum);
      C_PUT_DWORD (pCapiMsg->info.manufacturer_ind.dwManuID, C4B_TRACE_MANU_ID);
      pTrcMsg = (CAPITraceMsg_t *) ((u_int8_t *) &(pCapiMsg->info) +
                                    sizeof (pCapiMsg->info.manufacturer_ind));
      C_PUT_DWORD (pTrcMsg->head.dwTracerID, pEntry->uTracerID);
      C_PUT_DWORD (pTrcMsg->head.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (pTrcMsg->head.dwTimeMicroSec, tv.tv_usec);
      C_PUT_WORD (pTrcMsg->head.wMsgType, C4BTRC_MSGTYPE_DRVR_REGISTER);
      C_PUT_DWORD (pTrcMsg->info.drvr_register.dwUniqueCtlrNum, uUniqueCtlrNum);
      C_PUT_DWORD (pTrcMsg->info.drvr_register.dwDrvCtlrNum, uDrvCtlrNum);
      C_PUT_DWORD (pTrcMsg->info.drvr_register.dwMaxLogicalConnections,
                   uMaxLogicalConnections);
      C_PUT_DWORD (pTrcMsg->info.drvr_register.dwMaxBDataBlocks,
                   uMaxBDataBlocks);
      C_PUT_DWORD (pTrcMsg->info.drvr_register.dwMaxBDataLen, uMaxBDataLen);
      C_PUT_DWORD (pTrcMsg->info.drvr_register.dwUniqueApplID, uUniqueApplID);
      C_PUT_DWORD (pTrcMsg->info.drvr_register.dwCtlrApplID, uCtlrApplID);
      C_PUT_WORD (pTrcMsg->info.drvr_register.wResult, uResult);
      C_PUT_WORD (pTrcMsg->head.wLength,
                  sizeof (pTrcMsg->head) +
                     sizeof (pTrcMsg->info.drvr_register));
      C_PUT_WORD (pCapiMsg->head.wLen, pmbMsg->m_len);
      
      /* deliver the Manufacturer-Indication to the tracing application */
      kcapictlr_enqueue_trace_message (pEntry->uApplID, pmbMsg);
   }
   
   /* unlock the tracer list */
   sx_sunlock (&g_sxTracerList);
   
} /* kcapitrace_drvr_register */





/**
 * Trace call to CAPI_RELEASE in CAPI driver.
 */

void kcapitrace_drvr_release
   (unsigned uUniqueCtlrNum,
    unsigned uDrvCtlrNum,
    unsigned uUniqueApplID,
    unsigned uCtlrApplID,
    unsigned uResult)
{
   struct timeval     tv;
   TracerListEntry_t *pEntry;
   struct mbuf       *pmbMsg;
   CAPIMsg_t         *pCapiMsg;
   CAPITraceMsg_t    *pTrcMsg;
   
   /* prepare the timestamp for a possible trace message */
   getmicrotime (&tv);
   
   /* lock the list of registered tracers (read lock) */
   sx_slock (&g_sxTracerList);
   
   /* traverse the tracer list and deliver a trace message to every application
    * tracer with enough rights
    */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check for controller tracer for controller specified */
      if (pEntry->fIsApplTracer || pEntry->uCtlrNum != uUniqueCtlrNum)
      {
         continue;
      }
      
      /* check for the right to receive a trace message */
      if (! pEntry->fIsRoot)
      {
         continue;
      }
      
      /* create a Manufacturer-Indication with the tracing information */
      pmbMsg = kcapictlr_get_trace_mbuf
                  (sizeof (pCapiMsg->head) +
                   sizeof (pCapiMsg->info.manufacturer_ind) +
                   sizeof (pTrcMsg->head) +
                   sizeof (pTrcMsg->info.drvr_release));
      if (! pmbMsg)
      {
         DBG (LOG_ERROR, "Tracer %u: Out of mbufs", pEntry->uTracerID);
         continue;
      }
      pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pEntry->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_MANUFACTURER));
      C_PUT_WORD (pCapiMsg->head.wNum, 0);
      C_PUT_DWORD (pCapiMsg->head.dwCid, uUniqueCtlrNum);
      C_PUT_DWORD (pCapiMsg->info.manufacturer_ind.dwManuID, C4B_TRACE_MANU_ID);
      pTrcMsg = (CAPITraceMsg_t *) ((u_int8_t *) &(pCapiMsg->info) +
                                    sizeof (pCapiMsg->info.manufacturer_ind));
      C_PUT_DWORD (pTrcMsg->head.dwTracerID, pEntry->uTracerID);
      C_PUT_DWORD (pTrcMsg->head.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (pTrcMsg->head.dwTimeMicroSec, tv.tv_usec);
      C_PUT_WORD (pTrcMsg->head.wMsgType, C4BTRC_MSGTYPE_DRVR_RELEASE);
      C_PUT_DWORD (pTrcMsg->info.drvr_release.dwUniqueCtlrNum, uUniqueCtlrNum);
      C_PUT_DWORD (pTrcMsg->info.drvr_release.dwDrvCtlrNum, uDrvCtlrNum);
      C_PUT_DWORD (pTrcMsg->info.drvr_release.dwUniqueApplID, uUniqueApplID);
      C_PUT_DWORD (pTrcMsg->info.drvr_release.dwCtlrApplID, uCtlrApplID);
      C_PUT_WORD (pTrcMsg->info.drvr_release.wResult, uResult);
      C_PUT_WORD (pTrcMsg->head.wLength,
                  sizeof (pTrcMsg->head) + sizeof (pTrcMsg->info.drvr_release));
      C_PUT_WORD (pCapiMsg->head.wLen, pmbMsg->m_len);
      
      /* deliver the Manufacturer-Indication to the tracing application */
      kcapictlr_enqueue_trace_message (pEntry->uApplID, pmbMsg);
   }
   
   /* unlock the tracer list */
   sx_sunlock (&g_sxTracerList);
   
} /* kcapitrace_drvr_release */





/**
 * Trace call to CAPI_PUT_MESSAGE in CAPI driver.
 */

void kcapitrace_drvr_put_message
   (unsigned           uUniqueCtlrNum,
    unsigned           uDrvCtlrNum,
    unsigned           uUniqueApplID,
    unsigned           uCtlrApplID,
    const struct mbuf *pmbOrgMsg,
    unsigned           uResult)
{
   struct timeval     tv;
   TracerListEntry_t *pEntry;
   struct mbuf       *pmbMsg;
   CAPIMsg_t         *pCapiMsg;
   CAPITraceMsg_t    *pTrcMsg;
   u_int32_t          dwLow;
   u_int32_t          dwHigh;
   
   /* prepare the timestamp for a possible trace message */
   getmicrotime (&tv);
   
   /* lock the list of registered tracers (read lock) */
   sx_slock (&g_sxTracerList);
   
   /* traverse the tracer list and deliver a trace message to every application
    * tracer with enough rights
    */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check for controller tracer for controller specified */
      if (pEntry->fIsApplTracer || pEntry->uCtlrNum != uUniqueCtlrNum)
      {
         continue;
      }
      
      /* check for the right to receive a trace message */
      if (! pEntry->fIsRoot)
      {
         continue;
      }
      
      /* if the message is a Data-B3 message and these messages shall not be
       * traced, the function calls also shall not be traced
       */
      if ((pEntry->ulTraceMask & C4BTRC_TRCMSK_DATA_B3_MSGS) == 0 &&
          pmbOrgMsg &&
          (CAPI_GET_CMD (mtod (pmbOrgMsg, CAPIMsg_t *)) &
           CAPI_CMDMASK_COMMAND) == C_DATA_B3)
      {
         continue;
      }
      
      /* create a Manufacturer-Indication with the tracing information */
      pmbMsg = kcapictlr_get_trace_mbuf
                  (sizeof (pCapiMsg->head) +
                   sizeof (pCapiMsg->info.manufacturer_ind) +
                   sizeof (pTrcMsg->head) +
                   sizeof (pTrcMsg->info.drvr_put_message));
      if (! pmbMsg)
      {
         DBG (LOG_ERROR, "Tracer %u: Out of mbufs", pEntry->uTracerID);
         continue;
      }
      pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pEntry->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_MANUFACTURER));
      C_PUT_WORD (pCapiMsg->head.wNum, 0);
      C_PUT_DWORD (pCapiMsg->head.dwCid, uUniqueCtlrNum);
      C_PUT_DWORD (pCapiMsg->info.manufacturer_ind.dwManuID, C4B_TRACE_MANU_ID);
      pTrcMsg = (CAPITraceMsg_t *) ((u_int8_t *) &(pCapiMsg->info) +
                                    sizeof (pCapiMsg->info.manufacturer_ind));
      C_PUT_DWORD (pTrcMsg->head.dwTracerID, pEntry->uTracerID);
      C_PUT_DWORD (pTrcMsg->head.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (pTrcMsg->head.dwTimeMicroSec, tv.tv_usec);
      C_PUT_WORD (pTrcMsg->head.wMsgType, C4BTRC_MSGTYPE_DRVR_PUT_MESSAGE);
      C_PUT_DWORD (pTrcMsg->info.drvr_put_message.dwUniqueCtlrNum,
                   uUniqueCtlrNum);
      C_PUT_DWORD (pTrcMsg->info.drvr_put_message.dwDrvCtlrNum, uDrvCtlrNum);
      C_PUT_DWORD (pTrcMsg->info.drvr_put_message.dwUniqueApplID,
                   uUniqueApplID);
      C_PUT_DWORD (pTrcMsg->info.drvr_put_message.dwCtlrApplID, uCtlrApplID);
      dwLow  = C_LOW_PTR_DWORD (pmbOrgMsg);
      dwHigh = C_HIGH_PTR_DWORD (pmbOrgMsg);
      C_PUT_DWORD (pTrcMsg->info.drvr_put_message.dwMsgPointerLow, dwLow);
      C_PUT_DWORD (pTrcMsg->info.drvr_put_message.dwMsgPointerHigh, dwHigh);
      C_PUT_WORD (pTrcMsg->info.drvr_put_message.wResult, uResult);
      C_PUT_WORD (pTrcMsg->head.wLength,
                  sizeof (pTrcMsg->head) +
                     sizeof (pTrcMsg->info.drvr_put_message));
      C_PUT_WORD (pCapiMsg->head.wLen, pmbMsg->m_len);
      
      /* deliver the Manufacturer-Indication to the tracing application */
      kcapictlr_enqueue_trace_message (pEntry->uApplID, pmbMsg);
   }
   
   /* unlock the tracer list */
   sx_sunlock (&g_sxTracerList);
   
} /* kcapitrace_drvr_put_message */





/**
 * Trace call to kcapictlr_receive_capi_message as the driver interface variant
 * of CAPI_GET_MESSAGE.
 */

void kcapitrace_drvr_get_message
   (unsigned           uUniqueCtlrNum, 
    unsigned           uDrvCtlrNum,
    unsigned           uUniqueApplID,
    unsigned           uCtlrApplID,
    const struct mbuf *pmbOrgMsg)
{
   struct timeval     tv;
   TracerListEntry_t *pEntry;
   struct mbuf       *pmbMsg;
   CAPIMsg_t         *pCapiMsg;
   CAPITraceMsg_t    *pTrcMsg;
   u_int32_t          dwLow;
   u_int32_t          dwHigh;
   
   /* prepare the timestamp for a possible trace message */
   getmicrotime (&tv);
   
   /* lock the list of registered tracers (read lock) */
   sx_slock (&g_sxTracerList);
   
   /* traverse the tracer list and deliver a trace message to every application
    * tracer with enough rights
    */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check for controller tracer for controller specified */
      if (pEntry->fIsApplTracer || pEntry->uCtlrNum != uUniqueCtlrNum)
      {
         continue;
      }
      
      /* check for the right to receive a trace message */
      if (! pEntry->fIsRoot)
      {
         continue;
      }
      
      /* if the message is a Data-B3 message and these messages shall not be
       * traced, the function calls also shall not be traced
       */
      if ((pEntry->ulTraceMask & C4BTRC_TRCMSK_DATA_B3_MSGS) == 0 &&
          pmbOrgMsg &&
          (CAPI_GET_CMD (mtod (pmbOrgMsg, CAPIMsg_t *)) &
           CAPI_CMDMASK_COMMAND) == C_DATA_B3)
      {
         continue;
      }
      
      /* create a Manufacturer-Indication with the tracing information */
      pmbMsg = kcapictlr_get_trace_mbuf
                  (sizeof (pCapiMsg->head) +
                   sizeof (pCapiMsg->info.manufacturer_ind) +
                   sizeof (pTrcMsg->head) +
                   sizeof (pTrcMsg->info.drvr_get_message));
      if (! pmbMsg)
      {
         DBG (LOG_ERROR, "Tracer %u: Out of mbufs", pEntry->uTracerID);
         continue;
      }
      pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pEntry->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_MANUFACTURER));
      C_PUT_WORD (pCapiMsg->head.wNum, 0);
      C_PUT_DWORD (pCapiMsg->head.dwCid, uUniqueCtlrNum);
      C_PUT_DWORD (pCapiMsg->info.manufacturer_ind.dwManuID, C4B_TRACE_MANU_ID);
      pTrcMsg = (CAPITraceMsg_t *) ((u_int8_t *) &(pCapiMsg->info) +
                                    sizeof (pCapiMsg->info.manufacturer_ind));
      C_PUT_DWORD (pTrcMsg->head.dwTracerID, pEntry->uTracerID);
      C_PUT_DWORD (pTrcMsg->head.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (pTrcMsg->head.dwTimeMicroSec, tv.tv_usec);
      C_PUT_WORD (pTrcMsg->head.wMsgType, C4BTRC_MSGTYPE_DRVR_GET_MESSAGE);
      C_PUT_DWORD (pTrcMsg->info.drvr_get_message.dwUniqueCtlrNum,
                   uUniqueCtlrNum);
      C_PUT_DWORD (pTrcMsg->info.drvr_get_message.dwDrvCtlrNum, uDrvCtlrNum);
      C_PUT_DWORD (pTrcMsg->info.drvr_get_message.dwUniqueApplID,
                   uUniqueApplID);
      C_PUT_DWORD (pTrcMsg->info.drvr_get_message.dwCtlrApplID, uCtlrApplID);
      dwLow  = C_LOW_PTR_DWORD (pmbOrgMsg);
      dwHigh = C_HIGH_PTR_DWORD (pmbOrgMsg);
      C_PUT_DWORD (pTrcMsg->info.drvr_get_message.dwMsgPointerLow, dwLow);
      C_PUT_DWORD (pTrcMsg->info.drvr_get_message.dwMsgPointerHigh, dwHigh);
      C_PUT_WORD (pTrcMsg->head.wLength,
                  sizeof (pTrcMsg->head) +
                     sizeof (pTrcMsg->info.drvr_get_message));
      C_PUT_WORD (pCapiMsg->head.wLen, pmbMsg->m_len);
      
      /* deliver the Manufacturer-Indication to the tracing application */
      kcapictlr_enqueue_trace_message (pEntry->uApplID, pmbMsg);
   }
   
   /* unlock the tracer list */
   sx_sunlock (&g_sxTracerList);
   
} /* kcapitrace_drvr_get_message */





/**
 * Trace call to CAPI_RESET_CTLR in CAPI driver.
 */

void kcapitrace_drvr_reset_ctlr
   (unsigned             uUniqueCtlrNum,
    size_t               nNumDataBlocks,
    CAPICtlrDataBlock_t *paDataBlocks,
    unsigned             uResult)
{
   struct timeval            tv;
   TracerListEntry_t        *pEntry;
   struct mbuf              *pmbMsg;
   CAPIMsg_t                *pCapiMsg;
   CAPITraceMsg_t           *pTrcMsg;
   CAPITraceCtlrDataBlock_t *p;
   u_int32_t                 dwLow;
   u_int32_t                 dwHigh;
   int                       i;
   
   /* prepare the timestamp for a possible trace message */
   getmicrotime (&tv);
   
   /* lock the list of registered tracers (read lock) */
   sx_slock (&g_sxTracerList);
   
   /* traverse the tracer list and deliver a trace message to every application
    * tracer with enough rights
    */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check for controller tracer for controller specified */
      if (pEntry->fIsApplTracer || pEntry->uCtlrNum != uUniqueCtlrNum)
      {
         continue;
      }
      
      /* check for the right to receive a trace message */
      if (! pEntry->fIsRoot)
      {
         continue;
      }
      
      /* create a Manufacturer-Indication with the tracing information */
      pmbMsg = kcapictlr_get_trace_mbuf
                  (sizeof (pCapiMsg->head) +
                   sizeof (pCapiMsg->info.manufacturer_ind) +
                   sizeof (pTrcMsg->head) +
                   sizeof (pTrcMsg->info.drvr_reset_ctlr) +
                   nNumDataBlocks * sizeof (CAPITraceCtlrDataBlock_t));
      if (! pmbMsg)
      {
         DBG (LOG_ERROR, "Tracer %u: Out of mbufs", pEntry->uTracerID);
         continue;
      }
      pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pEntry->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_MANUFACTURER));
      C_PUT_WORD (pCapiMsg->head.wNum, 0);
      C_PUT_DWORD (pCapiMsg->head.dwCid, uUniqueCtlrNum);
      C_PUT_DWORD (pCapiMsg->info.manufacturer_ind.dwManuID, C4B_TRACE_MANU_ID);
      pTrcMsg = (CAPITraceMsg_t *) ((u_int8_t *) &(pCapiMsg->info) +
                                    sizeof (pCapiMsg->info.manufacturer_ind));
      C_PUT_DWORD (pTrcMsg->head.dwTracerID, pEntry->uTracerID);
      C_PUT_DWORD (pTrcMsg->head.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (pTrcMsg->head.dwTimeMicroSec, tv.tv_usec);
      C_PUT_WORD (pTrcMsg->head.wMsgType, C4BTRC_MSGTYPE_DRVR_RESET_CTLR);
      C_PUT_DWORD (pTrcMsg->info.drvr_reset_ctlr.dwUniqueCtlrNum,
                   uUniqueCtlrNum);
      C_PUT_WORD (pTrcMsg->info.drvr_reset_ctlr.wResult, uResult);
      C_PUT_DWORD (pTrcMsg->info.drvr_reset_ctlr.dwNumDataBlocks,
                   nNumDataBlocks);
      for (i = 0,
              p = (CAPITraceCtlrDataBlock_t *)
                     ((u_int8_t *) &(pTrcMsg->info.drvr_reset_ctlr) +
                      sizeof (pTrcMsg->info.drvr_reset_ctlr));
           (size_t) i < nNumDataBlocks;
           ++i, ++p)
      {
         C_PUT_DWORD (p->dwLenDataBlock, paDataBlocks  [i].nLenDataBlock);
         dwLow  = C_LOW_PTR_DWORD (paDataBlocks [i].paucDataBlock);
         dwHigh = C_HIGH_PTR_DWORD (paDataBlocks [i].paucDataBlock);
         C_PUT_DWORD (p->dwDataBlockPointerLow, dwLow);
         C_PUT_DWORD (p->dwDataBlockPointerHigh, dwHigh);
      }
      C_PUT_WORD (pTrcMsg->head.wLength,
                  sizeof (pTrcMsg->head) +
                     sizeof (pTrcMsg->info.drvr_reset_ctlr) +
                     nNumDataBlocks * sizeof (CAPITraceCtlrDataBlock_t));
      C_PUT_WORD (pCapiMsg->head.wLen, pmbMsg->m_len);
      
      /* deliver the Manufacturer-Indication to the tracing application */
      kcapictlr_enqueue_trace_message (pEntry->uApplID, pmbMsg);
   }
   
   /* unlock the tracer list */
   sx_sunlock (&g_sxTracerList);
   
} /* kcapitrace_drvr_reset_ctlr */





/**
 * Trace a CAPI message on the application interface level.
 *
 * Note: The effective user and group ids of the current process is used to
 *       check if the tracer is authorized to get this message.
 *
 * @param uApplID               I: The application id to receive or send the
 *                                 CAPI message.
 * @param pmbOrgMsg             I: The CAPI message to trace.
 * @param fIsDataB3Msg          I: 0 - The message is no Data-B3 message.
 *                                 1 - The message is a Data-B3 message.
 *
 * @return Nothing.
 */

void kcapitrace_trace_appl_msg
   (unsigned           uApplID,
    const struct mbuf *pmbOrgMsg,
    int                fIsDataB3Msg)
{
   struct timeval     tv;
   TracerListEntry_t *pEntry;
   struct mbuf       *pmbMsg;
   struct mbuf       *pmbData;
   CAPIMsg_t         *pCapiMsg;
   CAPITraceMsg_t    *pTrcMsg;
   u_int8_t          *p;
   size_t             nDataLen;
   
   /* prepare the timestamp for a possible trace message */
   getmicrotime (&tv);
   
   /* lock the list of registered tracers (read lock) */
   sx_slock (&g_sxTracerList);
   
   /* traverse the tracer list and deliver a trace message to every application
    * tracer with enough rights
    */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check for application tracer */
      if (! pEntry->fIsApplTracer)
      {
         continue;
      }
      
      /* check for the right to receive a trace message */
      if (pEntry->afTraceApplAllow [uApplID] == 0)
      {
         continue;
      }
      
      /* if the message is a Data-B3 message and these messages shall not be
       * traced: further to next tracer
       */
      if ((pEntry->ulTraceMask & C4BTRC_TRCMSK_DATA_B3_MSGS) == 0 &&
          fIsDataB3Msg)
      {
         continue;
      }
      
      /* create a Manufacturer-Indication with the tracing information */
      pmbMsg = kcapictlr_get_trace_mbuf
                  (sizeof (pCapiMsg->head) +
                   sizeof (pCapiMsg->info.manufacturer_ind) +
                   sizeof (pTrcMsg->head) +
                   sizeof (pTrcMsg->info.appl_msg) + pmbOrgMsg->m_len);
      if (! pmbMsg)
      {
         DBG (LOG_ERROR, "Tracer %u: Out of mbufs for message",
              pEntry->uTracerID);
         continue;
      }
      pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pEntry->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_MANUFACTURER));
      C_PUT_WORD (pCapiMsg->head.wNum, 0);
      C_PUT_DWORD (pCapiMsg->head.dwCid, 0);
      C_PUT_DWORD (pCapiMsg->info.manufacturer_ind.dwManuID, C4B_TRACE_MANU_ID);
      pTrcMsg = (CAPITraceMsg_t *) ((u_int8_t *) &(pCapiMsg->info) +
                                    sizeof (pCapiMsg->info.manufacturer_ind));
      C_PUT_DWORD (pTrcMsg->head.dwTracerID, pEntry->uTracerID);
      C_PUT_DWORD (pTrcMsg->head.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (pTrcMsg->head.dwTimeMicroSec, tv.tv_usec);
      C_PUT_WORD (pTrcMsg->head.wMsgType, C4BTRC_MSGTYPE_APPL_MSG);
      if (pmbOrgMsg->m_next != NULL &&
          pmbOrgMsg->m_next->m_len > 0 &&
          (pEntry->ulTraceMask & C4BTRC_TRCMSK_DATA_BLOCKS) != 0 &&
          pEntry->ulDataBlockLength > 0)
      {
         nDataLen = pmbOrgMsg->m_next->m_len;
         if (nDataLen > pEntry->ulDataBlockLength)
         {
            nDataLen = pEntry->ulDataBlockLength;
         }
         pmbData = kcapi_get_mbuf (nDataLen);
         if (! pmbData)
         {
            kcapi_free_mbuf (pmbMsg);
            DBG (LOG_ERROR, "Tracer %u: Out of mbufs for data block (%zu bytes)",
                 pEntry->uTracerID, nDataLen);
            continue;
         }
         bcopy (pmbOrgMsg->m_next->m_data, pmbData->m_data, nDataLen);
         C_PUT_DWORD (pTrcMsg->info.appl_msg.dwDataLength, nDataLen);
         pmbMsg->m_next = pmbData;
      }
      else
      {
         C_PUT_DWORD (pTrcMsg->info.appl_msg.dwDataLength, 0);
      }
      /* Note: The data pointer is only valid at the user process space level.
       *       Here the data block mbuf is attached to the trace message as any
       *       other data block to a Data-B3 message.
       */
      C_PUT_DWORD (pTrcMsg->info.appl_msg.dwDataPointerLow, 0);
      C_PUT_DWORD (pTrcMsg->info.appl_msg.dwDataPointerHigh, 0);
      p = (u_int8_t *) &(pTrcMsg->info.appl_msg) +
          sizeof (pTrcMsg->info.appl_msg);
      bcopy (pmbOrgMsg->m_data, p, pmbOrgMsg->m_len);
      C_PUT_WORD (pTrcMsg->head.wLength,
                  sizeof (pTrcMsg->head) +
                     sizeof (pTrcMsg->info.appl_msg) +
                     pmbOrgMsg->m_len);
      C_PUT_WORD (pCapiMsg->head.wLen, pmbMsg->m_len);
      
      /* deliver the Manufacturer-Indication to the tracing application */
      kcapictlr_enqueue_trace_message (pEntry->uApplID, pmbMsg);
   }
   
   /* unlock the tracer list */
   sx_sunlock (&g_sxTracerList);
   
} /* kcapitrace_trace_appl_msg */





/**
 * Trace a CAPI message on the driver interface level.
 *
 + @param uUniqueCtlrNum        I: Unique controller number the message belongs
 *                                 to.
 * @param pmbOrgMsg             I: The CAPI message to trace.
 *
 * @return Nothing.
 */

void kcapitrace_trace_drvr_msg
   (unsigned           uUniqueCtlrNum,
    const struct mbuf *pmbOrgMsg)
{
   struct timeval     tv;
   TracerListEntry_t *pEntry;
   struct mbuf       *pmbMsg;
   struct mbuf       *pmbData;
   CAPIMsg_t         *pCapiMsg;
   CAPITraceMsg_t    *pTrcMsg;
   u_int8_t          *p;
   size_t             nDataLen;
   
   /* prepare the timestamp for a possible trace message */
   getmicrotime (&tv);
   
   /* lock the list of registered tracers (read lock) */
   sx_slock (&g_sxTracerList);
   
   /* traverse the tracer list and deliver a trace message to every application
    * tracer with enough rights
    */
   LIST_FOREACH (pEntry, &g_tracerList, le)
   {
      /* check for controller tracer for controller specified */
      if (pEntry->fIsApplTracer || pEntry->uCtlrNum != uUniqueCtlrNum)
      {
         continue;
      }
      
      /* check for the right to receive a trace message */
      if (! pEntry->fIsRoot)
      {
         continue;
      }
      
      /* if the message is a Data-B3 message and these messages shall not be
       * traced: continue with next tracer
       */
      if ((pEntry->ulTraceMask & C4BTRC_TRCMSK_DATA_B3_MSGS) == 0 &&
          pmbOrgMsg &&
          (CAPI_GET_CMD (mtod (pmbOrgMsg, CAPIMsg_t *)) &
           CAPI_CMDMASK_COMMAND) == C_DATA_B3)
      {
         continue;
      }
      
      /* create a Manufacturer-Indication with the tracing information */
      pmbMsg = kcapictlr_get_trace_mbuf
                  (sizeof (pCapiMsg->head) +
                   sizeof (pCapiMsg->info.manufacturer_ind) +
                   sizeof (pTrcMsg->head) +
                   sizeof (pTrcMsg->info.drvr_msg) +
                   pmbOrgMsg->m_len);
      if (! pmbMsg)
      {
         DBG (LOG_ERROR, "Tracer %u: Out of mbufs for message",
              pEntry->uTracerID);
         continue;
      }
      pCapiMsg = mtod (pmbMsg, CAPIMsg_t *);
      C_PUT_WORD (pCapiMsg->head.wApp, pEntry->uApplID);
      C_PUT_WORD (pCapiMsg->head.wCmd, CAPI_INDICAT (C_MANUFACTURER));
      C_PUT_WORD (pCapiMsg->head.wNum, 0);
      C_PUT_DWORD (pCapiMsg->head.dwCid, uUniqueCtlrNum);
      C_PUT_DWORD (pCapiMsg->info.manufacturer_ind.dwManuID, C4B_TRACE_MANU_ID);
      pTrcMsg = (CAPITraceMsg_t *) ((u_int8_t *) &(pCapiMsg->info) +
                                    sizeof (pCapiMsg->info.manufacturer_ind));
      C_PUT_DWORD (pTrcMsg->head.dwTracerID, pEntry->uTracerID);
      C_PUT_DWORD (pTrcMsg->head.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (pTrcMsg->head.dwTimeMicroSec, tv.tv_usec);
      C_PUT_WORD (pTrcMsg->head.wMsgType, C4BTRC_MSGTYPE_DRVR_MSG);
      if (pmbOrgMsg->m_next != NULL &&
          pmbOrgMsg->m_next->m_len > 0 &&
          (pEntry->ulTraceMask & C4BTRC_TRCMSK_DATA_BLOCKS) != 0 &&
          pEntry->ulDataBlockLength > 0)
      {
         nDataLen = pmbOrgMsg->m_next->m_len;
         if (nDataLen > pEntry->ulDataBlockLength)
         {
            nDataLen = pEntry->ulDataBlockLength;
         }
         pmbData = kcapi_get_mbuf (nDataLen);
         if (! pmbData)
         {
            kcapi_free_mbuf (pmbMsg);
            DBG (LOG_ERROR, "Tracer %u: Out of mbufs for data block (%zu bytes)",
                 pEntry->uTracerID, nDataLen);
            continue;
         }
         bcopy (pmbOrgMsg->m_next->m_data, pmbData->m_data, nDataLen);
         C_PUT_DWORD (pTrcMsg->info.drvr_msg.dwDataLength, nDataLen);
         pmbMsg->m_next = pmbData;
      }
      else
      {
         C_PUT_DWORD (pTrcMsg->info.drvr_msg.dwDataLength, 0);
      }
      /* Note: The data pointer is only valid at the user process space level.
       *       Here the data block mbuf is attached to the trace message as any
       *       other data block to a Data-B3 message.
       */
      C_PUT_DWORD (pTrcMsg->info.drvr_msg.dwDataPointerLow, 0);
      C_PUT_DWORD (pTrcMsg->info.drvr_msg.dwDataPointerHigh, 0);
      p = (u_int8_t *) &(pTrcMsg->info.drvr_msg) +
          sizeof (pTrcMsg->info.drvr_msg);
      bcopy (pmbOrgMsg->m_data, p, pmbOrgMsg->m_len);
      C_PUT_WORD (pTrcMsg->head.wLength,
                  sizeof (pTrcMsg->head) +
                     sizeof (pTrcMsg->info.drvr_msg) +
                     pmbOrgMsg->m_len);
      C_PUT_WORD (pCapiMsg->head.wLen, pmbMsg->m_len);
      
      /* deliver the Manufacturer-Indication to the tracing application */
      kcapictlr_enqueue_trace_message (pEntry->uApplID, pmbMsg);
   }
   
   /* unlock the tracer list */
   sx_sunlock (&g_sxTracerList);
   
} /* kcapitrace_trace_ctlr_msg */





/* === implementation of private functions =============================== */





