/**
 * @file capidev.c
 *
 * capidev - The interface device between userland and kernel CAPI.
 *
 * Copyright: Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: capidev.c,v 1.1.2.1 2006/02/28 21:35:11 thomas Exp $
 * $Project     CAPI for BSD $
 * $Target      CAPI userland device $
 * @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/filio.h>
#include <sys/malloc.h>
#include <sys/file.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <sys/filedesc.h>
#include <sys/selinfo.h>
#include <sys/mutex.h>
#include <sys/sysctl.h>
#include <capi20.h>
#include <capi_bsd.h>
#include <c4b/kcapimgr/capi_drv.h>

/* Import includes */

#define __CAPIDEV__

/* Local includes */
#include <opt_capidev.h>





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





/** The device driver name. */
#define CAPIDEV_DEVICE_DRIVER_NAME      "capidev"

/** The major version number of the CAPI device. */
#define CAPIDEV_VERSION_MAJOR           1

/** The minor version number of the CAPI device. */
#define CAPIDEV_VERSION_MINOR           0

/** The name of the CAPI device. */
#define CAPIDEV_DEVICE_NAME             "capi20"

/** The major device number for CAPI 2.0. */
#if defined (__FreeBSD_version) && __FreeBSD_version >= 501000
#  define CDEV_MAJOR    MAJOR_AUTO
#else /* defined (__FreeBSD_version) && __FreeBSD_version >= 501000 */
#  define CDEV_MAJOR    226
#endif /* defined (__FreeBSD_version) && __FreeBSD_version >= 501000 */

/** New file descriptor type for the CAPI device.
 *
 * In order to let the kernel not think he knows what kind of data is
 * associated with an open CAPI device file descriptor we need to define a new
 * descriptor type. It would be nice to use a new type number above the
 * currently defined values. But at least one part of the kernel (something
 * about "kqueues") uses the descriptor type as an index into an array,
 * unchecked. So we just use the 0 as the CAPI descriptor type.
 */
#define DTYPE_CAPI      0



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

/* the following tag will be used when allocating memory for use in the CAPI
 * device driver
 */
MALLOC_DECLARE (M_CAPIDEVBUF);
MALLOC_DEFINE (M_CAPIDEVBUF,
               "capidevicebuffer", "dynamic memory for the CAPI device");



/* --- information to store for one registered application --- */

/** Magic value to recognize a app-info structure (representation for "CAPI"). */
#define APP_INFO_MAGIC  0x43415049

/** The data maintained for an open CAPI device file descriptor. */
typedef struct
{
   unsigned               uMagic;       /**<
                                         * Magic value to be sure we address the
                                         * correct structure.
                                         */
   int                    fIsClosing;   /**<
                                         * - 0
                                         *    Normal operation, calls to read,
                                         *    write and ioctl are allowed.
                                         * - 1
                                         *    Close is called for this
                                         *    application, no other calls to
                                         *    read, write or ioctl are allowed
                                         *    any more.
                                         */
   int                    fAppRegistered;
                                        /**<
                                         * Flag for validity of the rest (1:
                                         * valid, 0: invalid).
                                         */
   unsigned               uApplID;      /**<
                                         * Application id obtained from the CAPI
                                         * manager.
                                         */
   size_t                 nNumActiveCalls;
                                        /**<
                                         * The current number of active calls to
                                         * read, write or ioctl. If this number
                                         * is greater than null, a call to
                                         * close must wait until all calls are
                                         * finished.
                                         */
   size_t                 nMinMsgBufSize;
                                        /**<
                                         * Maximum number of bytes needed to
                                         * store a CAPI message and a data
                                         * block ( = sizeof (CAPIMsg_t) +
                                         * regData.uMaxDBlockLen ).
                                         */
   struct mtx             mtxSelect;    /**<
                                         * The mutex for accessing the three
                                         * next structure members. Unfortunately
                                         * we cannot use FILE_LOCK() for this,
                                         * because the CAPI callback function
                                         * has not access to the file descriptor
                                         * for a registered application. Even
                                         * the open() routine does not know the
                                         * real file descriptor associated with
                                         * the application registration.
                                         */
   char                   szMtxName [16];
                                        /**< The unique name of the mutex. */
   unsigned               uMsgsWaiting; /**<
                                         * Number of times the callback function
                                         * was called; is decremented in each
                                         * read call.
                                         */
   int                    fSelFlag;     /**<
                                         * - 0
                                         *    No one is waiting on poll.
                                         * - 1
                                         *    Poll system call pending.
                                         */
   struct selinfo         selRdInfo;    /**<
                                         * Structure needed to implement the
                                         * poll system call.
                                         */
} CapiAppInfo_t;



/* --- several variables --- */

/** The CAPI device structure created at initialization. */
static struct cdev *g_pCapiDev = NULL;

/**
 * The number of open file descriptors to the CAPI device.
 *
 * @note This value is only modified through the atomic functions.
 */
static unsigned g_uNumOpenFds = 0;

/**
 * The number of registered CAPI applications.
 *
 * @note This value is only modified through the atomic functions.
 */
static unsigned g_uNumApps = 0;

/** The owner to specify when creating the CAPI device. */
static int g_iDevOwner = UID_ROOT;

/** The group to specify when creating the CAPI device. */
static int g_iDevGroup = GID_WHEEL;

/** The file mode to set when creating the CAPI device. */
static int g_iDevMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;



/* --- Compatibility macros --- */

#define GET_PID( p )    ((p)->td_proc->p_pid)



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

/** The logging levels supported. */
#define LOG_ALWAYS      0
#define LOG_ERROR       1
#define LOG_INFO        2
#define LOG_TRACE       3
#define LOG_DEBUG       4

/**
 * Characters to mark log messages with their severity.
 *
 * @note The array-string is indexed by the log level.
 */
#define LOG_CHARS       "AEMTD"

/** The current logging level. */
#ifdef CAPIDEV_LOG_LEVEL
static int g_iLogLevel = CAPIDEV_LOG_LEVEL;
#else /* CAPIDEV_LOG_LEVEL */
static int g_iLogLevel = LOG_ERROR;
#endif /* CAPIDEV_LOG_LEVEL */

/** The macro for log output with driver unit number. */
#define DBG( level, fmt, args... )                        \
   if ((level) <= g_iLogLevel)                            \
   {                                                      \
      printf ("capidev: %c %s: " fmt "\n",                \
              LOG_CHARS [level], __FUNCTION__ , ## args); \
   }



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

/** The node for the CAPI userland device driver under "capi". */
SYSCTL_NODE (_capi, OID_AUTO, capidev, CTLFLAG_RW, 0,
             "Device for user space CAPI support");

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

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

#if ! defined (__FreeBSD_version) || (__FreeBSD_version < 501000)
/**
 * The variable for the character device major number.
 * This is only relevant for FreeBSD before v5.1.
 */
SYSCTL_INT (_capi_capidev, OID_AUTO, cdev_major, CTLFLAG_RD,
            NULL, CDEV_MAJOR, "Major character device number");
#endif /* ! defined (__FreeBSD_version) || (__FreeBSD_version < 501000) */

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

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

/** The variable for the number of open file descriptors. */
SYSCTL_UINT (_capi_capidev, OID_AUTO, open_file_descriptors, CTLFLAG_RD,
             &g_uNumOpenFds, 0, "Number of open file descriptors");



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

#define CAPIDEV_TUNABLE_LOGLEVEL \
   "capi." CAPIDEV_DEVICE_DRIVER_NAME ".loglevel"

TUNABLE_INT (CAPIDEV_TUNABLE_LOGLEVEL, &g_iLogLevel);

#define CAPIDEV_TUNABLE_DEVOWNER \
   "capi." CAPIDEV_DEVICE_DRIVER_NAME ".devowner"

TUNABLE_INT (CAPIDEV_TUNABLE_DEVOWNER, &g_iDevOwner);

#define CAPIDEV_TUNABLE_DEVGROUP \
   "capi." CAPIDEV_DEVICE_DRIVER_NAME ".devgroup"

TUNABLE_INT (CAPIDEV_TUNABLE_DEVGROUP, &g_iDevGroup);

#define CAPIDEV_TUNABLE_DEVMODE \
   "capi." CAPIDEV_DEVICE_DRIVER_NAME ".devmode"

TUNABLE_INT (CAPIDEV_TUNABLE_DEVMODE, &g_iDevMode);





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





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





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

/**
 * Callback function to signal new CAPI messages by the CAPI manager.
 *
 * @note When this function is called by the CAPI manager, kcapi_release() is
 *       blocked. So in this call we can safely access the application data
 *       structure (at least its static members).
 */
#if defined(__alpha__) || defined (__ia64__) || defined (__amd64__) || defined (__sparc64__)
static void capidev_kcapi_callback
   (unsigned  uApplID,
    u_int64_t qwParam);
#else /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
static void capidev_kcapi_callback
   (unsigned  uApplID,
    u_int32_t dwParam);
#endif /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */

/**
 * Handling the request to open the CAPI device.
 *
 * @param dev                   I: Device to be opened.
 * @param iOFlags               I: Open flags, unused.
 * @param iDevType              I: Device type, unused.
 * @param pThread               I: The description of the process / thread that
 *                                 is going to open the CAPI device.
 *
 * @retval 0                    Success.
 * @retval Else                 Errno value to indicate an error.
 */
static int capidev_open
   (struct cdev *pDev,
    int          iOFlags,
    int          iDevType,
    d_thread_t  *pThread);



/* --- the CAPI related file descriptor operations --- */

/**
 * The read system call.
 *
 * Here we will read the next CAPI message if available. The message and a
 * possible data block are obtained in mbufs. The contents of these are copied
 * to the buffer provided. After this the mbuf(s) must be released.
 */
static int capidev_read
   (struct file  *fp,
    struct uio   *pUio,
    struct ucred *pCred,
    int           iFlags,
    d_thread_t   *pThread);
    
/**
 * The write system call.
 */
static int capidev_write
   (struct file  *fp,
    struct uio   *pUio,
    struct ucred *pCred,
    int           iFlags,
    d_thread_t   *pThread);

/**
 * The ioctl system call.
 */
static int capidev_ioctl
   (struct file  *fp,
    u_long        ulCmd,
    void         *pData,
    struct ucred *pCred,
    d_thread_t   *pThread);

/**
 * The poll system call.
 */
static int capidev_poll
   (struct file  *fp,
    int           iEvents,
    struct ucred *pCred,
    d_thread_t   *pThread);

/**
 * The kqfilter system call.
 */
static int capidev_kqfilter
   (struct file  *fp,
    struct knote *pkn);

/**
 * The stat system call.
 */
static int capidev_stat
   (struct file  *fp,
    struct stat  *psb,
    struct ucred *pCred,
    d_thread_t   *pThread);

/**
 * The close system call.
 */
static int capidev_close
   (struct file *fp,
    d_thread_t  *pThread);





/* --- the interface definition for the CAPI device as a kernel module --- */

/**
 * Flag for the completed initialization of the module to prevent multiple
 * initialization.
 */
static int g_fLoadEventReceived = 0;

/** The description structure for this kernel module. */
static moduledata_t capidev_mod =
{
   CAPIDEV_DEVICE_DRIVER_NAME,
   capidev_modevent,
   NULL
};

/** The effective declaration for this kernel module. */
DECLARE_MODULE (capidev, capidev_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);

/** The version number for this kernel module. */
MODULE_VERSION (capidev, (CAPIDEV_VERSION_MAJOR << 16) | CAPIDEV_VERSION_MINOR);

/** 
 * Dependency declaration for the CAPI manager.
 *
 * @note The CAPI manager application interface shall not change within a major
 *       version. So any minor version will do, provided the major version is
 *       as expected.
 */
MODULE_DEPEND (capidev, kcapimgr,
               (CAPIMAN_VERSION_MAJOR << 16) | CAPIMAN_VERSION_MINOR,
               (CAPIMAN_VERSION_MAJOR << 16) | CAPIMAN_VERSION_MINOR,
               (CAPIMAN_VERSION_MAJOR << 16) | 0x0000FFFF);



/** The interface definition for the CAPI device.
 *
 * @note We need only the open function. All other functions are handled by
 *       specific vnode functions, that are assigned to the file descriptor on
 *       the open call.
 */

#if defined (__FreeBSD_version) && __FreeBSD_version >= 502103

static struct cdevsw g_capi_cdevsw =
{
   .d_version = D_VERSION,
   .d_flags   = 0,
   .d_name    = CAPIDEV_DEVICE_NAME,
   .d_open    = capidev_open,
};

#elif defined (__FreeBSD_version) && __FreeBSD_version >= 501110

static struct cdevsw g_capi_cdevsw =
{
   .d_flags = 0,
   .d_name  = CAPIDEV_DEVICE_NAME,
   .d_open  = capidev_open,
   .d_maj   = CDEV_MAJOR
};

#elif defined (__FreeBSD_version) && __FreeBSD_version >= 501000

static struct cdevsw g_capi_cdevsw =
{
   CDEV_MAJOR,          /* d_maj */
   0,                   /* d_flags */
   CAPIDEV_DEVICE_NAME, /* d_name */
   capidev_open,        /* d_open */
   noclose,             /* d_close */
   noread,              /* d_read */
   nowrite,             /* d_write */
   noioctl,             /* d_ioctl */
   nopoll,              /* d_poll */
   nommap,              /* d_mmap */
   nostrategy,          /* d_strategy */
   nodump,              /* d_dump */
   nokqfilter,          /* d_kqfilter */
};

#else /* defined (__FreeBSD_version) && __FreeBSD_version >= 501000 */

static struct cdevsw g_capi_cdevsw =
{
   capidev_open,        /* open */
   noclose,             /* close */
   noread,              /* read */
   nowrite,             /* write */
   noioctl,             /* ioctl */
   nopoll,              /* poll */
   nommap,              /* mmap */
   nostrategy,          /* strategy */
   CAPIDEV_DEVICE_NAME, /* name */
   CDEV_MAJOR,          /* major device number */
   nodump,              /* dump */
   nopsize,             /* psize */
   0                    /* flags */
   ,
   nokqfilter           /* kqfilter */
};

#endif /* defined (__FreeBSD_version) && __FreeBSD_version >= 501000 */



/** The structure of CAPI related descriptor functions. */
static struct fileops g_capiFdOps =
{
   capidev_read,        /* fo_read */
   capidev_write,       /* fo_write */
   capidev_ioctl,       /* fo_ioctl */
   capidev_poll,        /* fo_poll */
   capidev_kqfilter,    /* fo_kqfilter */
   capidev_stat,        /* fo_stat */
   capidev_close        /* fo_close */
#if defined (__FreeBSD_version) && __FreeBSD_version >= 501000
   ,
   0                    /* fo_flags */
#endif /* defined (__FreeBSD_version) && __FreeBSD_version >= 501000 */
};





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





/* There are actually no functions to be called directly. All functions are
 * called by reference in structures or function lists.
 */





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





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

static int capidev_modevent
   (module_t  mod,
    int       iType,
    void     *pUnused)
{
   switch (iType)
   {
      case MOD_LOAD:
         DBG (LOG_TRACE, "got modevent \"%s\"", "MOD_LOAD");
         /* create the CAPI device */
         if (! g_fLoadEventReceived)
         {
            snprintf (g_szVersion, sizeof (g_szVersion), "%d.%d",
                      CAPIDEV_VERSION_MAJOR,
                      CAPIDEV_VERSION_MINOR);
            g_pCapiDev = make_dev (&g_capi_cdevsw, 0,
                                   g_iDevOwner, g_iDevGroup, g_iDevMode,
                                   CAPIDEV_DEVICE_NAME);
            if (g_pCapiDev != NULL)
            {
               DBG (LOG_TRACE, "capidev: CAPI device created");
            }
            else
            {
               printf ("capidev: WARNING: Creation of device %s failed\n",
                       CAPIDEV_DEVICE_NAME);
               return (1);
            }
         }
         g_fLoadEventReceived = 1;
         break;
         
      case MOD_UNLOAD:
         DBG (LOG_TRACE, "Got modevent \"%s\"", "MOD_UNLOAD");
         /* be sure everything is cleaned up */
         if (g_uNumApps > 0)
         {
            printf ("capidev: WARNING: Still %d CAPI applications registered, unable to unload capidev module\n",
                    g_uNumApps);
            return (1);
         }
         if (g_uNumOpenFds > 0)
         {
            printf ("capidev: WARNING: Still %d open file descriptors to \"%s\", unable to unload capidev module\n",
                    g_uNumOpenFds, CAPI_DEVICE_NAME);
            return (1);
         }
         
         /* destroy the CAPI device */
         if (g_pCapiDev != NULL)
         {
            destroy_dev (g_pCapiDev);
            g_pCapiDev = NULL;
            DBG (LOG_TRACE, "capidev: CAPI device destroyed");
         }
         g_fLoadEventReceived = 0;
         break;
         
      case MOD_SHUTDOWN:
      default:
         /* event not handled or unknown */
         break;
   }
   
   return (0);
} /* capidev_modevent */





/**
 * Callback function to signal new CAPI messages by the CAPI manager.
 *
 * @note When this function is called by the CAPI manager, kcapi_release() is
 *       blocked. So in this call we can safely access the application data
 *       structure (at least its static members).
 */

#if defined(__alpha__) || defined (__ia64__) || defined (__amd64__) || defined (__sparc64__)
static void capidev_kcapi_callback
   (unsigned  uApplID,
    u_int64_t qwParam)
#else /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
static void capidev_kcapi_callback
   (unsigned  uApplID,
    u_int32_t dwParam)
#endif /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
{
   CapiAppInfo_t *pAppInfo;
   
   /* check for valid callback parameter, treat the dwParam/qwParam as a pointer
    * to an app-info structure
    */
#if defined(__alpha__) || defined (__ia64__) || defined (__amd64__) || defined (__sparc64__)
   pAppInfo = (CapiAppInfo_t *) qwParam;
   if (pAppInfo == NULL)
   {
      DBG (LOG_ERROR,
           "Got invalid callback parameter 0x%08llX, must be a valid pointer",
           (unsigned long long) qwParam);
      return;
   }
#else /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
   pAppInfo = (CapiAppInfo_t *) dwParam;
   if (pAppInfo == NULL)
   {
      DBG (LOG_ERROR,
           "Got invalid callback parameter 0x%08lX, must be a valid pointer",
           (unsigned long) dwParam);
      return;
   }
#endif /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
   
   /* lock access to the relevant members of the structure; note that the
    * appl.info structure cannot be released, because kcapi_release() is blocked
    * until this function returns
    */
   mtx_lock (&(pAppInfo->mtxSelect));
   
   /* check for valid app-info structure */
   if (pAppInfo->uMagic != APP_INFO_MAGIC)
   {
      mtx_unlock (&(pAppInfo->mtxSelect));
#if defined(__alpha__) || defined (__ia64__) || defined (__amd64__) || defined (__sparc64__)
      DBG (LOG_ERROR,
           "Got invalid qwParam 0x%08llX, appl.info structure does not contain magic no.",
           (unsigned long long) qwParam);
#else /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
      DBG (LOG_ERROR,
           "Got invalid dwParam 0x%08lX, appl.info structure does not contain magic no.",
           (unsigned long) dwParam);
#endif /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
      return;
   }
   
   /* for valid applications wakeup any sleeping poll call */
   pAppInfo->uMsgsWaiting++;
   if (pAppInfo->fSelFlag)
   {
      pAppInfo->fSelFlag = 0;
      selwakeup (&(pAppInfo->selRdInfo));
      DBG (LOG_DEBUG, "Appl. id %u woken up", pAppInfo->uApplID);
   }
   else
   {
      DBG (LOG_DEBUG, "Appl. id %u not sleeping", pAppInfo->uApplID);
   }

   mtx_unlock (&(pAppInfo->mtxSelect));
   
} /* capidev_kcapi_callback */





/**
 * Handling the request to open the CAPI device.
 *
 * @param dev                   I: Device to be opened.
 * @param iOFlags               I: Open flags, unused.
 * @param iDevType              I: Device type, unused.
 * @param pThread               I: The description of the process / thread that
 *                                 is going to open the CAPI device.
 *
 * @retval 0                    Success.
 * @retval Else                 Errno value to indicate an error.
 */

static int capidev_open
   (struct cdev *pDev,
    int          iOFlags,
    int          iDevType,
    d_thread_t  *pThread)
{
   CapiAppInfo_t *pAppInfo;
   int            fd;
   struct file   *fp;
   int            iRes;

   /* check if the caller wants to do a real dup */
   PROC_LOCK (pThread->td_proc);
   if (pThread->td_dupfd >= 0)
   {
      /* let him do it (magic in kernel code) */
      PROC_UNLOCK (pThread->td_proc);
      DBG (LOG_INFO, "Open called for dupfd");
      return (ENODEV);
   }
   PROC_UNLOCK (pThread->td_proc);

   DBG (LOG_TRACE, "CAPI device is beeing opened");
   
   /* first we need some memory to store CAPI application specific data with
    * the file descriptor
    */
   MALLOC (pAppInfo, CapiAppInfo_t *, sizeof (*pAppInfo),
           M_CAPIDEVBUF, M_WAITOK);
   if (! pAppInfo)
   {
      DBG (LOG_ERROR, "Out of memory for appl. info data (%zu bytes)",
           sizeof (*pAppInfo));
      return (CRE_OS_RESOURCE_ERROR);
   }
   bzero (pAppInfo, sizeof (*pAppInfo));
   pAppInfo->uMagic = APP_INFO_MAGIC;
   mtx_init (&(pAppInfo->mtxSelect), pAppInfo->szMtxName, NULL, MTX_DEF);
   
   /* we need a temporarily new file descriptor for this process */
   iRes = falloc (pThread, &fp, &fd);
   if (iRes != 0)
   {
      /* unable to open new descriptor */
      mtx_destroy (&(pAppInfo->mtxSelect));
      FREE (pAppInfo, M_CAPIDEVBUF);
      DBG (LOG_ERROR, "Error allocating new temp fd: %d", iRes);
      return (CRE_OS_RESOURCE_ERROR);
   }
   DBG (LOG_DEBUG, "new temp fd %d allocated", fd);
   
   /* now we can assign the new functions and data to the allocated descriptor
    */
   FILEDESC_LOCK (pThread->td_proc->p_fd);
   fp->f_data = (caddr_t) pAppInfo;
   fp->f_flag = FREAD | FWRITE;
   fp->f_ops  = &g_capiFdOps;
   fp->f_type = DTYPE_CAPI;
   FILEDESC_UNLOCK (pThread->td_proc->p_fd);

   /* falloc() holds an extra reference (sys/kern/kern_descrip.c rev. 1.215 */
   fdrop (fp, pThread);

   /* count the new open descriptor */
   atomic_add_int (&g_uNumOpenFds, 1);

   /* finally let the kernel assign the newly allocated descriptor data to the
    * original descriptor (that we do not know), this is why ENXIO is returned
    */
   PROC_LOCK (pThread->td_proc);
   pThread->td_dupfd = fd;
   PROC_UNLOCK (pThread->td_proc);
   return (ENXIO);
} /* capidev_open */





/**
 * The read system call.
 *
 * Here we will read the next CAPI message if available. The message and a
 * possible data block are obtained in mbufs. The contents of these are copied
 * to the buffer provided. After this the mbuf(s) must be released.
 */

static int capidev_read
   (struct file  *fp,
    struct uio   *pUio,
    struct ucred *pCred,
    int           iFlags,
    d_thread_t   *pThread)
{
   CapiAppInfo_t *pAppInfo;
   struct mbuf   *pmbMsg;
   struct mbuf   *pmbData;
   CAPIMsg_t     *pMsg;
   unsigned       uRes;
   int            iRes;
   
   /* first test for valid CAPI descriptor */
   FILE_LOCK (fp);
   if (fp->f_type != DTYPE_CAPI ||
       fp->f_data == NULL ||
       ((CapiAppInfo_t *) (fp->f_data))->uMagic != APP_INFO_MAGIC)
   {
      uprintf ("capidev: WARNING: Got read system call for invalid file descriptor, process id %u, fd type %i\n",
               (unsigned) GET_PID (pThread), (int) (fp->f_type));
      FILE_UNLOCK (fp);
      return (CME_INVALID_APPLICATION_ID);
   }
   /* now the descriptor data must point to a app-info structure */
   pAppInfo = (CapiAppInfo_t *) (fp->f_data);

   /* check for already pending close operation */
   if (pAppInfo->fIsClosing)
   {
      FILE_UNLOCK (fp);
      DBG (LOG_ERROR, "close() already called");
      return (CME_INVALID_APPLICATION_ID);
   }
   
   /* check for registered application */
   if (! pAppInfo->fAppRegistered)
   {
      FILE_UNLOCK (fp);
      DBG (LOG_ERROR, "Application not registered");
      return (CME_INVALID_APPLICATION_ID);
   }

   /* test for enough buffer space to hold a CAPI message and a data block */
   if (pUio->uio_resid < pAppInfo->nMinMsgBufSize)
   {
      /* it is no message exchange error, but it seems to clarify the situation
       */
      DBG (LOG_ERROR,
           "Appl. id %u: File pointer buffer too small (%u bytes) to hold a complete CAPI message and data block (up to %zu bytes)",
           pAppInfo->uApplID, (unsigned) (pUio->uio_resid),
           pAppInfo->nMinMsgBufSize);
      FILE_UNLOCK (fp);
      return (CRE_INVALID_MSG_BUFFER);
   }

   /* Now declare a new call into the CAPI manager and release the file lock
    * until the CAPI call is completed. The app-info structure will remain valid
    * because the only operation to destroy it is close(). And the close()
    * operation will wait until all active calls are finished.
    */
   pAppInfo->nNumActiveCalls++;
   FILE_UNLOCK (fp);
   
   DBG (LOG_DEBUG, "Appl. id %u: Trying to get message", pAppInfo->uApplID);
   
   /* try to get the next CAPI message */
   uRes = kcapi_get_message (pAppInfo->uApplID, &pmbMsg);
   mtx_lock (&(pAppInfo->mtxSelect));
   if (pAppInfo->uMsgsWaiting > 0)
   {
      pAppInfo->uMsgsWaiting--;
   }
   mtx_unlock (&(pAppInfo->mtxSelect));
   if (uRes != CAPI_OK)
   {
      DBG (LOG_DEBUG, "Appl. id %u: Error getting message: 0x%04X",
           pAppInfo->uApplID, uRes);
      FILE_LOCK (fp);
      pAppInfo->nNumActiveCalls--;
      FILE_UNLOCK (fp);
      return (uRes);
   }
   pMsg = (CAPIMsg_t *) (pmbMsg->m_data);
   DBG (LOG_DEBUG, "Appl. id %u: Got message 0x%04X",
        pAppInfo->uApplID, CAPI_GET_CMD (pMsg));

   /* transfer the raw message to the file descriptor */
   uRes = CAPI_OK;
   iRes = uiomove (pmbMsg->m_data, pmbMsg->m_len, pUio);
   if (iRes != 0)
   {
      DBG (LOG_ERROR,
           "Appl. id %u: Error writing message (%u bytes) to fd: %d",
           pAppInfo->uApplID, pmbMsg->m_len, iRes);
      uRes = CME_OS_RESOURCE_ERROR;
   }

   /* if the message is a Data-B3-Indication or a trace message with a data
    * block, the data block must now be transferred (pmbMsg->m_next is non-NULL
    * exactly in this case)
    */
   pmbData = pmbMsg->m_next;
   if (pmbData)
   {
      iRes = uiomove (pmbData->m_data, pmbData->m_len, pUio);
      if (iRes != 0)
      {
         DBG (LOG_ERROR,
              "Appl. id %u: Error writing data block (%u bytes) to fd: %d",
              pAppInfo->uApplID, pmbData->m_len, iRes);
         uRes = CME_OS_RESOURCE_ERROR;
      }
   }
   
   /* now release all mbufs */
   kcapi_free_mbuf (pmbMsg);

   if (uRes == CAPI_OK)
   {
      DBG (LOG_DEBUG,
           "Appl. id %u: Message (and data block?) successfully written to fd",
           pAppInfo->uApplID);
   }
   
   FILE_LOCK (fp);
   pAppInfo->nNumActiveCalls--;
   FILE_UNLOCK (fp);
   
   return (uRes);
} /* capidev_read */
    




/**
 * The write system call.
 */

static int capidev_write
   (struct file  *fp,
    struct uio   *pUio,
    struct ucred *pCred,
    int           iFlags,
    d_thread_t   *pThread)
{
   CapiAppInfo_t *pAppInfo;
   struct mbuf   *pmbMsg;
   struct mbuf   *pmbData;
   CAPIMsg_t     *pMsg;
   unsigned char  auc [2];
   size_t         nMsgLen;
   size_t         nDataLen;
   unsigned       uRes;
   int            iRes;
   
   /* first test for valid CAPI descriptor */
   FILE_LOCK (fp);
   if (fp->f_type != DTYPE_CAPI ||
       fp->f_data == NULL ||
       ((CapiAppInfo_t *) (fp->f_data))->uMagic != APP_INFO_MAGIC)
   {
      uprintf ("capidev: WARNING: Got write system call for invalid file descriptor, process id %u, fd type %i\n",
               (unsigned) GET_PID (pThread), (int) (fp->f_type));
      FILE_UNLOCK (fp);
      return (CME_INVALID_APPLICATION_ID);
   }
   /* now the descriptor data must point to a app-info structure */
   pAppInfo = (CapiAppInfo_t *) (fp->f_data);

   /* check for already pending close operation */
   if (pAppInfo->fIsClosing)
   {
      FILE_UNLOCK (fp);
      DBG (LOG_ERROR, "close() already called");
      return (CME_INVALID_APPLICATION_ID);
   }
   
   /* check for registered application */
   if (! pAppInfo->fAppRegistered)
   {
      FILE_UNLOCK (fp);
      DBG (LOG_ERROR, "Application not registered");
      return (CME_INVALID_APPLICATION_ID);
   }

   /* Now declare a new call into the CAPI manager and release the file lock
    * until the CAPI call is completed. The app-info structure will remain valid
    * because the only operation to destroy it is close(). And the close()
    * operation will wait until all active calls are finished.
    */
   pAppInfo->nNumActiveCalls++;
   FILE_UNLOCK (fp);
   
   /* first we need the message size; is in the first two bytes in Intel order
    */
   if (pUio->uio_resid < sizeof (auc) ||
       uiomove ((caddr_t) auc, sizeof (auc), pUio) != 0)
   {
      FILE_LOCK (fp);
      pAppInfo->nNumActiveCalls--;
      FILE_UNLOCK (fp);
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   nMsgLen = auc [0] + (auc [1] << 8);
   if (nMsgLen <= 2 || pUio->uio_resid < nMsgLen - 2)
   {
      /* message length too short or greater than bytes in the uio buffer */
      DBG (LOG_ERROR,
           "Appl. id %u: Message length %zu shorter than 2 bytes or longer than bytes in the file buffer (%u bytes total)",
           pAppInfo->uApplID, nMsgLen, (unsigned) (pUio->uio_resid + 2));
      FILE_LOCK (fp);
      pAppInfo->nNumActiveCalls--;
      FILE_UNLOCK (fp);
      return (CCE_ILLEGAL_MSG_PARAMETER);
   }
   
   /* get mbuf for the CAPI message */
   pmbMsg = kcapi_get_mbuf (nMsgLen);
   if (! pmbMsg)
   {
      /* out of mbufs */
      DBG (LOG_ERROR, "Appl. id %u: Out of mbufs for message, length %zu",
           pAppInfo->uApplID, nMsgLen);
      FILE_LOCK (fp);
      pAppInfo->nNumActiveCalls--;
      FILE_UNLOCK (fp);
      return ((nMsgLen <= sizeof (CAPIMsg_t))
              ? CME_OS_RESOURCE_ERROR
              : CCE_ILLEGAL_MSG_PARAMETER);
   }
   
   /* fill the mbuf with the CAPI message */
   pmbMsg->m_data [0] = auc [0];
   pmbMsg->m_data [1] = auc [1];
   iRes = uiomove (&(pmbMsg->m_data [2]), nMsgLen - 2, pUio);
   if (iRes != 0)
   {
      DBG (LOG_ERROR,
           "Appl. id %u: Error reading message (%zu-2 bytes) from fd: %d",
           pAppInfo->uApplID, nMsgLen, iRes);
      FILE_LOCK (fp);
      pAppInfo->nNumActiveCalls--;
      FILE_UNLOCK (fp);
      kcapi_free_mbuf (pmbMsg);
      return (CME_OS_RESOURCE_ERROR);
   }
   
   /* if the message is a Data-B3-Request, there may follow a data block, for
    * which we also need an mbuf
    */
   pMsg = (CAPIMsg_t *) (pmbMsg->m_data);
   if (CAPI_GET_CMD (pMsg) == CAPI_REQUEST (C_DATA_B3))
   {
#if defined(__alpha__) || defined (__ia64__) || defined (__amd64__) || defined (__sparc64__)

      u_int64_t qwData;
      
      /* check for valid message parameters */
      if (nMsgLen < sizeof (pMsg->head) + sizeof (pMsg->info.data_b3_64_req))
      {
         DBG (LOG_ERROR,
              "Appl. id %u: Invalid parameter(s) for Data-B3-64-Request: Msg. length %zu, must be at least %zu",
              pAppInfo->uApplID, nMsgLen,
              sizeof (pMsg->head) + sizeof (pMsg->info.data_b3_64_req));
         FILE_LOCK (fp);
         pAppInfo->nNumActiveCalls--;
         FILE_UNLOCK (fp);
         kcapi_free_mbuf (pmbMsg);
         return (CCE_ILLEGAL_MSG_PARAMETER);
      }
      nDataLen = C_GET_WORD (pMsg->info.data_b3_64_req.wLen);
      qwData = C_GET_QWORD (pMsg->info.data_b3_64_req.qwData64);
      if (nDataLen <= 0 || nDataLen > MCLBYTES ||
          qwData == 0)
      {
         DBG (LOG_ERROR,
              "Appl. id %u: Invalid parameter(s) for Data-B3-64-Request: Data length %zu, qwData64 0x%p",
              pAppInfo->uApplID, nDataLen, (void *) qwData);
         FILE_LOCK (fp);
         pAppInfo->nNumActiveCalls--;
         FILE_UNLOCK (fp);
         kcapi_free_mbuf (pmbMsg);
         return (CCE_ILLEGAL_MSG_PARAMETER);
      }

#else /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */

      u_int32_t dwData;
      
      /* check for valid message parameters */
      if (nMsgLen < sizeof (pMsg->head) + sizeof (pMsg->info.data_b3_32_req))
      {
         DBG (LOG_ERROR,
              "Appl. id %u: Invalid parameter(s) for Data-B3-Request: Msg. length %zu, must be at least %zu",
              pAppInfo->uApplID, nMsgLen,
              sizeof (pMsg->head) + sizeof (pMsg->info.data_b3_32_req));
         FILE_LOCK (fp);
         pAppInfo->nNumActiveCalls--;
         FILE_UNLOCK (fp);
         kcapi_free_mbuf (pmbMsg);
         return (CCE_ILLEGAL_MSG_PARAMETER);
      }
      nDataLen = C_GET_WORD (pMsg->info.data_b3_32_req.wLen);
      dwData = C_GET_DWORD (pMsg->info.data_b3_32_req.dwData);
      if (nDataLen <= 0 || nDataLen > MCLBYTES ||
          dwData == 0)
      {
         DBG (LOG_ERROR,
              "Appl. id %u: Invalid parameter(s) for Data-B3-Request: Data length %zu, dwData 0x%p",
              pAppInfo->uApplID, nDataLen, (void *) dwData);
         FILE_LOCK (fp);
         pAppInfo->nNumActiveCalls--;
         FILE_UNLOCK (fp);
         kcapi_free_mbuf (pmbMsg);
         return (CCE_ILLEGAL_MSG_PARAMETER);
      }

#endif /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */

      /* get mbuf for the data block */
      pmbData = kcapi_get_mbuf (nDataLen);
      if (! pmbData)
      {
         DBG (LOG_ERROR, "Appl. id %u: Out of mbufs for data block, length %zu",
              pAppInfo->uApplID, nDataLen);
         FILE_LOCK (fp);
         pAppInfo->nNumActiveCalls--;
         FILE_UNLOCK (fp);
         kcapi_free_mbuf (pmbMsg);
         return (CME_OS_RESOURCE_ERROR);
      }
      
      /* fill the mbuf with the data block */
      iRes = uiomove (pmbData->m_data, nDataLen, pUio);
      if (iRes != 0)
      {
         DBG (LOG_ERROR,
              "Appl. id %u: Error reading data block (%zu bytes) from fd: %d",
              pAppInfo->uApplID, nDataLen, iRes);
         FILE_LOCK (fp);
         pAppInfo->nNumActiveCalls--;
         FILE_UNLOCK (fp);
         kcapi_free_mbuf (pmbMsg);
         kcapi_free_mbuf (pmbData);
         return (CME_OS_RESOURCE_ERROR);
      }
      
      /* put the address of the data mbuf into the header of the message mbuf */
      pmbMsg->m_next = pmbData;
   }
   
   DBG (LOG_DEBUG, "Appl. id %u: Try to put message 0x%04X",
        pAppInfo->uApplID, CAPI_GET_CMD ((CAPIMsg_t *) (pmbMsg->m_data)));
   /* now we are finally ready to perform the CAPI_PUT_MESSAGE call */
   uRes = kcapi_put_message (pAppInfo->uApplID, pmbMsg);
   if (uRes == CAPI_OK)
   {
      DBG (LOG_DEBUG,
           "Appl. id %u: Message (and data block?) successfully sent",
           pAppInfo->uApplID);
   }
   else
   {
      /* call not successful, free all mbufs */
      DBG (LOG_ERROR, "Appl. id %u: Error sending message 0x%04X: 0x%04X",
           pAppInfo->uApplID,
           CAPI_GET_CMD ((CAPIMsg_t *) (pmbMsg->m_data)), uRes);
      kcapi_free_mbuf (pmbMsg);
   }

   FILE_LOCK (fp);
   pAppInfo->nNumActiveCalls--;
   FILE_UNLOCK (fp);
   
   return (uRes);
} /* capidev_write */





/**
 * The ioctl system call.
 */

#if defined (__FreeBSD_version) && __FreeBSD_version >= 500000
static int capidev_ioctl
   (struct file  *fp,
    u_long        ulCmd,
    void         *pData,
    struct ucred *pCred,
    d_thread_t   *pThread)
#else /* defined (__FreeBSD_version) && __FreeBSD_version >= 500000 */
static int capidev_ioctl
   (struct file *fp,
    u_long       ulCmd,
    caddr_t      pData,
    d_thread_t  *pThread)
#endif /* defined (__FreeBSD_version) && __FreeBSD_version >= 500000 */
{
   CapiAppInfo_t *pAppInfo;
   unsigned       uRes;
   
   /* first test for valid CAPI descriptor */
   FILE_LOCK (fp);
   if (fp->f_type != DTYPE_CAPI ||
       fp->f_data == NULL ||
       ((CapiAppInfo_t *) (fp->f_data))->uMagic != APP_INFO_MAGIC)
   {
      uprintf ("WARNING: capidev: Got ioctl system call for invalid file descriptor, process id %u, fd type %i\n",
               (unsigned) GET_PID (pThread), (int) (fp->f_type));
      FILE_UNLOCK (fp);
      return (CME_INVALID_APPLICATION_ID);
   }
   /* now the descriptor data must point to a app-info structure */
   pAppInfo = (CapiAppInfo_t *) (fp->f_data);

   /* check for already pending close operation */
   if (pAppInfo->fIsClosing)
   {
      FILE_UNLOCK (fp);
      DBG (LOG_ERROR, "close() already called");
      return (CME_INVALID_APPLICATION_ID);
   }
   
   /* Now declare a new call into the CAPI manager and release the file lock
    * until the CAPI call is completed. The app-info structure will remain valid
    * because the only operation to destroy it is close(). And the close()
    * operation will wait until all active calls are finished.
    */
   pAppInfo->nNumActiveCalls++;
   FILE_UNLOCK (fp);
   
   /* distinguish the different i/o commands */
   switch (ulCmd)
   {
      case C4BIOC_CAPI_REGISTER:
         /* check if already registered */
         if (! pAppInfo->fAppRegistered)
         {
            capi_register_params_t *pParams =
               (capi_register_params_t *) pData;
            
            uRes = kcapi_register (pParams->uMaxLogicalConnections,
                                   pParams->uMaxBDataBlocks,
                                   pParams->uMaxBDataLen,
                                   &(pAppInfo->uApplID));
            if (uRes == CAPI_OK)
            {
               DBG (LOG_DEBUG,
                    "CAPI_REGISTER: Successful, appl. id %u, uMaxLogicalConnections %u, uMaxBDataBlocks %u, uMaxBDataLen %u",
                    pAppInfo->uApplID,
                    pParams->uMaxLogicalConnections,
                    pParams->uMaxBDataBlocks,
                    pParams->uMaxBDataLen);

               /* fill the app-info structure */
               pAppInfo->nMinMsgBufSize =
                  sizeof (CAPIMsg_t) + pParams->uMaxBDataLen;
               pAppInfo->fAppRegistered = 1;
               
	       /* return the app. id to the caller */
	       pParams->uApplID = pAppInfo->uApplID;

               /* we must also register a callback function to wake up calls
                * waiting in the poll system call; after successful
                * registration no error should happen
                */
#if defined (__alpha__) || defined (__ia64__) || defined (__amd64__) || defined (__sparc64__)
               if (kcapi_set_signal (pAppInfo->uApplID,
                                     capidev_kcapi_callback,
                                     (u_int64_t) pAppInfo) != CAPI_OK)
#else /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
               if (kcapi_set_signal (pAppInfo->uApplID,
                                     capidev_kcapi_callback,
                                     (u_int32_t) pAppInfo) != CAPI_OK)
#endif /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
               {
                  uprintf ("capidev: WARNING: Unable to register callback function, process id %d\n",
                           (int) GET_PID (pThread));
               }

	       /* count the current number of registered applications */
	       atomic_add_int (&g_uNumApps, 1);
            }
            else
            {
               DBG (LOG_ERROR,
                    "CAPI_REGISTER: Error 0x%04X, uMaxLogicalConnections %u, uMaxBDataBlocks %u, uMaxBDataLen %u",
                    uRes,
                    pParams->uMaxLogicalConnections,
                    pParams->uMaxBDataBlocks,
                    pParams->uMaxBDataLen);
            }
         }
         else
         {
            DBG (LOG_ERROR, "CAPI_REGISTER: Appl. id %u: Already registered",
                 pAppInfo->uApplID);
            uRes = CME_INVALID_APPLICATION_ID;
         }
         break;
      
      case C4BIOC_CAPI_GET_MANUFACTURER:
         {
            capi_get_manufacturer_params_t *pParams =
               (capi_get_manufacturer_params_t *) pData;
               
            uRes = kcapi_get_manufacturer (pParams->uCtlr,
                                           pParams->szBuffer);
            DBG (LOG_DEBUG,
                 "CAPI_GET_MANUFACTURER: Result 0x%04X, controller %u",
                 uRes, pParams->uCtlr);
         }
         break;
      
      case C4BIOC_CAPI_GET_VERSION:
         {
            capi_get_version_params_t *pParams =
               (capi_get_version_params_t *) pData;
               
            uRes = kcapi_get_version (pParams->uCtlr,
                                      &(pParams->uCAPIMajor),
                                      &(pParams->uCAPIMinor),
                                      &(pParams->uManufacturerMajor),
                                      &(pParams->uManufacturerMinor),
                                      &(pParams->uBSDMajor),
                                      &(pParams->uBSDMinor));
            DBG (LOG_DEBUG,
                 "CAPI_GET_VERSION: Result 0x%04X, controller %u",
                 uRes, pParams->uCtlr);
         }
         break;
      
      case C4BIOC_CAPI_GET_SERIAL_NUMBER:
         {
            capi_get_serial_number_params_t *pParams =
               (capi_get_serial_number_params_t *) pData;
               
            uRes = kcapi_get_serial_number (pParams->uCtlr,
                                            pParams->szBuffer);
            DBG (LOG_DEBUG,
                 "CAPI_GET_SERIAL_NUMBER: Result 0x%04X, controller %u",
                 uRes, pParams->uCtlr);
         }
         break;
      
      case C4BIOC_CAPI_GET_PROFILE:
         {
            capi_get_profile_params_t *pParams =
               (capi_get_profile_params_t *) pData;
               
            uRes = kcapi_get_profile (pParams->uCtlr,
                                      &(pParams->profile));
            DBG (LOG_DEBUG,
                 "CAPI_GET_PROFILE: Result 0x%04X, controller %u",
                 uRes, pParams->uCtlr);
         }
         break;
      
      case C4BIOC_CAPI_GET_CTLR_DRIVER_INFO:
         {
            capi_get_ctlr_driver_info_params_t *pParams =
               (capi_get_ctlr_driver_info_params_t *) pData;
               
            uRes = kcapi_get_ctlr_driver_info (pParams->uCtlr,
                                               &(pParams->drvInfo));
            DBG (LOG_DEBUG,
                 "CAPI_GET_CTLR_DRIVER_INFO: Result 0x%04X, controller %u",
                 uRes, pParams->uCtlr);
         }
         break;
      
      case C4BIOC_CAPI_RESET_CTLR:
         {
            capi_reset_ctlr_params_t *pParams =
               (capi_reset_ctlr_params_t *) pData;
            CAPICtlrDataBlock_t      *paCtlrData;
            CAPICtlrDataBlock_t      *paCtlrData2;
            int                       i;
            
            /* copy the controller data into kernel memory if specified */
            uRes = CAPI_OK;
            paCtlrData = NULL;
            paCtlrData2 = NULL;
            if (pParams->nNumDataBlocks > 0)
            {
               MALLOC (paCtlrData, CAPICtlrDataBlock_t *,
                       pParams->nNumDataBlocks * sizeof (paCtlrData [0]),
                       M_CAPIDEVBUF, M_WAITOK);
               if (! paCtlrData)
               {
                  uprintf ("capidev: WARNING: Not enough memory for first array (%zu entries) to download controller %u\n",
                           pParams->nNumDataBlocks, pParams->uCtlr);
                  uRes = CRE_OS_RESOURCE_ERROR;
                  break;
               }
               MALLOC (paCtlrData2, CAPICtlrDataBlock_t *,
                       pParams->nNumDataBlocks * sizeof (paCtlrData2 [0]),
                       M_CAPIDEVBUF, M_WAITOK);
               if (! paCtlrData2)
               {
                  uprintf ("capidev: WARNING: Not enough memory for second array (%zu entries) to download controller %u\n",
                           pParams->nNumDataBlocks, pParams->uCtlr);
                  FREE (paCtlrData, M_CAPIDEVBUF);
                  uRes = CRE_OS_RESOURCE_ERROR;
                  break;
               }
               bzero (paCtlrData2,
                      pParams->nNumDataBlocks * sizeof (paCtlrData2 [0]));
               copyin (pParams->paDataBlocks, paCtlrData,
                       pParams->nNumDataBlocks * sizeof (paCtlrData [0]));
               for (i = 0; i < pParams->nNumDataBlocks; i++)
               {
                  if (paCtlrData [i].nLenDataBlock > 0)
                  {
                     MALLOC (paCtlrData2 [i].paucDataBlock,
                             unsigned char *,
                             paCtlrData [i].nLenDataBlock,
                             M_CAPIDEVBUF, M_WAITOK);
                     if (! paCtlrData2 [i].paucDataBlock)
                     {
                        uprintf ("capidev: WARNING: Not enough memory for data block %d (%zu bytes) to download controller %u\n",
                                 i, paCtlrData [i].nLenDataBlock,
                                 pParams->uCtlr);
                        uRes = CRE_OS_RESOURCE_ERROR;
                        break;
                     }
                     copyin (paCtlrData [i].paucDataBlock,
                             paCtlrData2 [i].paucDataBlock,
                             paCtlrData [i].nLenDataBlock);
                  }
                  paCtlrData2 [i].nLenDataBlock =
                     paCtlrData [i].nLenDataBlock;
               }
               if (uRes == CAPI_OK)
               {
                  DBG (LOG_DEBUG,
                       "CAPI_RESET_CTLR: %zu data blocks copied to kernel memory for controller %u",
                       pParams->nNumDataBlocks, pParams->uCtlr);
               }
            }

            /* perform the reset/download operation */
            if(uRes == CAPI_OK)
            {
               uRes = kcapi_reset_ctlr (pParams->uCtlr,
                                        pParams->nNumDataBlocks,
                                        paCtlrData2);
               DBG (LOG_DEBUG,
                    "CAPI_RESET_CTLR: Result 0x%04X, controller %u",
                    uRes, pParams->uCtlr);
            }
                                     
            /* free all memory allocated previously */
            if (paCtlrData2)
            {
               for (i = 0; i < pParams->nNumDataBlocks; i++)
               {
                  if (paCtlrData2 [i].paucDataBlock)
                  {
                     FREE (paCtlrData2 [i].paucDataBlock, M_CAPIDEVBUF);
                  }
               }
               FREE (paCtlrData2, M_CAPIDEVBUF);
            }
            if (paCtlrData)
            {
               FREE (paCtlrData, M_CAPIDEVBUF);
            }
         }
         break;
      
      case FIONREAD:
         /* The number of bytes that may be read in the next read call may only
          * be guessed. We only know if there is at least one message waiting.
          * So the result of this ioctl is the minimum CAPI message size, i.e.
          * the length of a CAPI message header.
          */
         mtx_lock (&(pAppInfo->mtxSelect));
         if (pAppInfo->uMsgsWaiting > 0)
         {
            *((int *) pData) = sizeof (CAPIMsgHead_t);
         }
         else
         {
            *((int *) pData) = 0;
         }
         mtx_unlock (&(pAppInfo->mtxSelect));
         uRes = 0;
         break;
      
      case FIONBIO:
         /* The CAPI device is always opened in non-blocking mode, no support
          * for blocking mode. So we just ignore the request to switch this
          * setting.
          */
         DBG (LOG_DEBUG,
              "Ignoring ioctl request FIONBIO, only non-blocking mode supported");
         uRes = 0;
         break;
         
      case FIOCLEX:
      case FIONCLEX:
      case FIOASYNC:
      case FIOSETOWN:
      case FIOGETOWN:
      case FIODTYPE:
      case FIOGETLBA:
         /* we do not support these file-descriptor ioctls */
         DBG (LOG_DEBUG,
              "Got unsupported file-descriptor ioctl 0x%X, process id %d\n",
              (unsigned int) ulCmd, (int) GET_PID (pThread));
         uRes = ENOTTY;
         break;
      
      default:
         uprintf ("capidev: WARNING: Got illegal ioctl command 0x%X, process id %d\n",
                  (unsigned int) ulCmd, (int) GET_PID (pThread));
         uRes = CME_ILLEGAL_COMMAND;
         break;
   }

   FILE_LOCK (fp);
   pAppInfo->nNumActiveCalls--;
   FILE_UNLOCK (fp);   

   return (uRes);
} /* capidev_ioctl */





/**
 * The poll system call.
 */

static int capidev_poll
   (struct file  *fp,
    int           iEvents,
    struct ucred *pCred,
    d_thread_t   *pThread)
{
   CapiAppInfo_t *pAppInfo;
   int            iREvents = 0;
   
   /* first test for valid CAPI descriptor */
   FILE_LOCK (fp);
   if (fp->f_type != DTYPE_CAPI ||
       fp->f_data == NULL ||
       ((CapiAppInfo_t *) (fp->f_data))->uMagic != APP_INFO_MAGIC)
   {
      uprintf ("capidev: WARNING: Got poll system call for invalid file descriptor, process id %u, fd type %i\n",
               (unsigned) GET_PID (pThread), (int) (fp->f_type));
      FILE_UNLOCK (fp);
      return (0);
   }
   /* now the descriptor data must point to an app-info structure */
   pAppInfo = (CapiAppInfo_t *) (fp->f_data);

   /* Now declare a new call into the CAPI manager and release the file lock.
    * This is needed before calling splhigh(), because this call may need to
    * wait  (sleep) and this is not allowed while holding a mutex. The app-info
    * structure will remain valid because the only operation to destroy it is
    * close(). And the close() operation will wait until all active calls are
    * finished.
    */
   pAppInfo->nNumActiveCalls++;
   FILE_UNLOCK (fp);
   
   /* write operations are allways allowed */
   iREvents |= (iEvents & (POLLOUT | POLLWRNORM));
   
   /* read operations are allowed if there are messages signalled */
   mtx_lock (&(pAppInfo->mtxSelect));
   if (pAppInfo->uMsgsWaiting > 0)
   {
      iREvents |= (iEvents & (POLLIN | POLLRDNORM));
   }
   
   /* wait for incoming messages if needed */
   if (iREvents == 0)
   {
      DBG (LOG_DEBUG, "Appl. id %u: Wait for filled message queue",
           pAppInfo->uApplID);
      selrecord (pThread, &(pAppInfo->selRdInfo));
      pAppInfo->fSelFlag = 1;
   }
   else
   {
      DBG (LOG_DEBUG, "Appl. id %u: Message queue already filled",
           pAppInfo->uApplID);
   }
   mtx_unlock (&(pAppInfo->mtxSelect));
   
   FILE_LOCK (fp);
   pAppInfo->nNumActiveCalls--;
   FILE_UNLOCK (fp);
   
   return (iREvents);
} /* capidev_poll */





/*
        the kqfilter system call
        ------------------------
*/

static int capidev_kqfilter
   (struct file  *fp,
    struct knote *pkn)
{
   /* this is operation is currently not supported for the CAPI device */
   (void) fp;
   (void) pkn;
   return (EOPNOTSUPP);
} /* capidev_kqfilter */





/**
 * The stat system call.
 */

static int capidev_stat
   (struct file  *fp,
    struct stat  *psb,
    struct ucred *pCred,
    d_thread_t   *pThread)
{
   /* this is not a valid operation for the CAPI device */
   (void) fp;
   (void) psb;
   (void) pCred;
   (void) pThread;
   return (EOPNOTSUPP);
} /* capidev_stat */






/**
 * The close system call.
 */

static int capidev_close
   (struct file *fp,
    d_thread_t  *pThread)
{
   CapiAppInfo_t *pAppInfo;
   unsigned       uRes;
   
   /* first test for valid CAPI descriptor */
   FILE_LOCK (fp);
   if (fp->f_type != DTYPE_CAPI ||
       fp->f_data == NULL ||
       ((CapiAppInfo_t *) (fp->f_data))->uMagic != APP_INFO_MAGIC)
   {
      uprintf ("capidev: WARNING: Got close system call for invalid file descriptor, process id %u, fd type %i\n",
               (unsigned) GET_PID (pThread), (int) (fp->f_type));
      FILE_UNLOCK (fp);
      return (CME_INVALID_APPLICATION_ID);
   }
   /* now the descriptor data must point to an app-info structure */
   pAppInfo = (CapiAppInfo_t *) (fp->f_data);

   /* declare the running close() call to block any call to other file
    * operations
    */
   pAppInfo->fIsClosing = 1;
   
   /* now wait until all active CAPI calls are finished */
   while (pAppInfo->nNumActiveCalls > 0)
   {
      FILE_UNLOCK (fp);
      (void) tsleep (pAppInfo, PZERO, "wait for no calls", hz / 10);
      FILE_LOCK (fp);
   }
   
   /* if application registered: do CAPI_RELEASE and free memory for app-info
    * as file descriptor data
    */
   fp->f_ops = &badfileops;
   fp->f_data = NULL;
   FILE_UNLOCK (fp);
   if (pAppInfo->fAppRegistered)
   {
      DBG (LOG_DEBUG, "Appl. id %u: Perform release at the CAPI manager",
           pAppInfo->uApplID);
      uRes = kcapi_release (pAppInfo->uApplID);
      DBG (LOG_DEBUG, "Appl. id %u: Release result 0x%04X",
           pAppInfo->uApplID, uRes);
      mtx_destroy (&(pAppInfo->mtxSelect));
      FREE (pAppInfo, M_CAPIDEVBUF);
      atomic_subtract_int (&g_uNumApps, 1);
   }
   else
   {
      DBG (LOG_DEBUG,
           "Application not registered, only close file descriptor");
      mtx_destroy (&(pAppInfo->mtxSelect));
      FREE (pAppInfo, M_CAPIDEVBUF);
      uRes = CAPI_OK;
   }

   atomic_subtract_int (&g_uNumOpenFds, 1);

   return (uRes);
} /* capidev_close */
