/*****************************************************************************/
/* Copyright (c) 2008 NXP B.V.                                               */
/*                                                                           */
/* This program is free software; you can redistribute it and/or modify      */
/* it under the terms of the GNU General Public License as published by      */
/* the Free Software Foundation, using version 2 of the License.             */
/*                                                                           */
/* This program is distributed in the hope that it will be useful,           */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the              */
/* GNU General Public License for more details.                              */
/*                                                                           */
/* You should have received a copy of the GNU General Public License         */
/* along with this program; if not, write to the Free Software               */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307       */
/* USA.                                                                      */
/*                                                                           */
/*****************************************************************************/
/**
    @file   phKernelIfce_Module.c
    @brief  Linux kernel module for phKernelIfce component.

@b  Component:  phKernelIfce.

*//*

Rev Date       Author      Comments
--------------------------------------------------------------------------------
  1 20061012   batelaan    Initial
  2 20061020   batelaan    Crosscheck fixes
  3 20061108   barnish     Change to match DBG updates
  4 20070117   steel       update for linux_VARIANT support
  5 20070316   batelaan    Cope with /proc file reading over multiple-read() calls.
  6 20070524   iwillis     Added "const" qualifiers to prevent warnings in
                           tmdlMsvd_KI.c
  7 20080227   burningh    Solve PR 998, and switch all debug messages to tmDbg
  8 20080617   batelaan    Make GPL license.
  9 20080721   steel       add debug for unwise osal usage
 10 20090121   batelaan    Update author/license.
--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.
*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>        /* kmalloc() */
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <asm/semaphore.h>
#include <asm/uaccess.h>
#include <linux/delay.h>
#include <linux/hardirq.h>	/* in_interrupt() */

#include <linux/device.h>
#include <linux/cdev.h>

#include <phKernelIfce.h>
#include <phStbDbg.h>

/***********************************************
* LOCAL MACROS                                 *
* recommendation only: <MODULE><_WORD>+        *
************************************************/

/* NAB: Define this to enable the (buggy) proc file interface. See PR 998 for details. */
/* #define PH_KERNELIFCE_ENABLE_PROC_FILE */

/** Debug utility for KernelIfce_ProcOpen(). */
#define PH_KERNELIFCE_DBGPROCOPEN0 do {\
  DBG_PRINT3(gphKernelIfce_DbgUnit, DBG_LEVEL_1, "%s buf_size=%u buf_used=%u\n", __FUNCTION__, buf_size, buf_used); \
} while (0)

/** Debug utility for KernelIfce_ProcOpen(). */
#define PH_KERNELIFCE_DBGPROCOPEN1 do {\
  DBG_PRINT3(gphKernelIfce_DbgUnit, DBG_LEVEL_1, "%s written=%u truncated=%u\n", __FUNCTION__, written, truncated); \
} while (0)

#define THIS_MODULE_DESCRIPTION "Generic Kernel Ifce from UserMode"
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION(THIS_MODULE_DESCRIPTION);
MODULE_AUTHOR("NXP B.V.");

/** The number of devices we expose */
#define NR_DEVS 1

/** Allocate space for this many namespaces, initially. We can create
 * more when needed. */
#define PH_KERNELIFCE_INITIAL_NUM_NAMESPACES 25U

/** Allocate space for this many functions per namespace. We can
 * create more when needed. */
#define PH_KERNELIFCE_INITIAL_NUM_FUNCS 25U

/** If we define PH_KERNELIFCE_COPY_CALLDATA then we copy data from userspace to
    kernelspace and viceversa, but it looks like that is not needed. We can access
    the data passed from userspace in kernelmode, as long as we check access first.
*/
#define PH_KERNELIFCE_COPY_CALLDATA 0

/******************************************************************
* FUNCTION PROTOTYPES                    [p]ph<Module>_<Word>+    *
*******************************************************************/

static int __init KernelIfce_ModuleInit(void);
static void __exit KernelIfce_ModuleExit(void);
static int KernelIfce_open(struct inode *inode, struct file *file);
static ssize_t KernelIfce_release(struct inode *inode, struct file *file);
static int KernelIfce_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);

#ifdef PH_KERNELIFCE_ENABLE_PROC_FILE
static ssize_t KernelIfce_ProcRead(struct file *file, char __user *buf, size_t size, loff_t *ppos);
static int KernelIfce_ProcOpen(struct inode *inode, struct file *file);
static ssize_t KernelIfce_ProcRelease(struct inode *inode, struct file *file);
#endif /* PH_KERNELIFCE_ENABLE_PROC_FILE */

/******************************************************************
* LOCAL TYPEDEFS                                                  *
* recommendation only:                   [p]ph<Module>_<Word>+_t  *
*******************************************************************/

/** Stores information about a registered function. */
typedef struct phKernelIfce_KFunc {
    /** Name of function. */
    const char *name;
    /* Unique id within this namespace for this function. */
    uint id;
    /* Number of times this function has been called. */
    uint callCount;
    /** Handler function for this registered function. */
    phKernelIfce_Handler_t *handler;
} phKernelIfce_KFunc_t;
typedef phKernelIfce_KFunc_t *pphKernelIfce_KFunc_t;

/** Stores information about a registered namespace. */
typedef struct phKernelIfce_KNameSpace {
    /** Name of namespace. */
    const char *name;
    /** Unique id for this namespace. */
    uint id;
    /** Number of registered functions for this namespace. */
    uint funcsCount;
    /** Maximum number of registered functions for this namespace before we have to resize the funcs array.. */
    uint funcsCountMax;
    /** Array of functions for this namespace. */
    pphKernelIfce_KFunc_t funcs;
} phKernelIfce_KNameSpace_t;

#ifdef PH_KERNELIFCE_ENABLE_PROC_FILE
/** Private file pointer data for our /proc file entry, after it is opened. */
typedef struct phKernelIfce_ProcReadData
{
    /** Amount actually in use in buf field. */
    size_t used;
    /** Contains the calculated contents of the /proc file.
        To give it some reasonable alignment, use 4 chars: */
    char buf[sizeof(long)];
} phKernelIfce_ProcReadData_t;
#endif /* PH_KERNELIFCE_ENABLE_PROC_FILE */

/*******************************************************************
* EXPORTED DATA                 g[k|p|kp|pk|kpk]ph<Module>_<Word>+ *
********************************************************************/

/** The component's debug unit. */
DBG_UNIT_STATIC(gphKernelIfce_DbgUnit);

module_init(KernelIfce_ModuleInit);
module_exit(KernelIfce_ModuleExit);

/*******************************************************************
* LOCAL DATA                                                       *
********************************************************************/

static dev_t gDev; /* The start of the reserved device number region */
static struct cdev gCdev;
static struct class *gSysClass; /* The device class under /sys/class */
static unsigned int gMajor; /* Major number of our device, obtained from gDev and held for convenience */

/** The file operations for the device */
static struct file_operations phKernelIfce_fops =
{
    THIS_MODULE,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    &KernelIfce_ioctl,
    NULL,
    NULL,
    NULL,
    &KernelIfce_open,
    NULL,
    &KernelIfce_release
};

/** Array of pointers to namespaces with handler functions.
    Protected by ::gKernelIfce_RegistryMutex.
    Counted by ::gKernelIfce_NameSpacesCount. */
static pphKernelIfce_KNameSpace_t *gppKernelIfce_NameSpaces;

/** Number of registered namespaces (including those unregistered) in
 * ::gppKernelIfce_NameSpaces. */
static uint gKernelIfce_NameSpacesCount = 0;

/** Number of elements in ::gppKernelIfce_NameSpaces array, if exceeded we
 * have to reallocate ::gppKernelIfce_NameSpaces. */
static uint gKernelIfce_NameSpacesCountMax;

/** Protects R/W access to ::gppKernelIfce_NameSpaces. */
static struct semaphore gKernelIfce_RegistryMutex;

#ifdef PH_KERNELIFCE_ENABLE_PROC_FILE
/** The file operations for our /proc file */
static struct file_operations gKernelIfce_ProcOperations = {
    THIS_MODULE,
    NULL,
    &KernelIfce_ProcRead,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    &KernelIfce_ProcOpen,
    NULL,
    &KernelIfce_ProcRelease
};
#endif /* PH_KERNELIFCE_ENABLE_PROC_FILE */

/*******************************************************************************
* FUNCTION IMPLEMENTATION  <Module>[_<Word>+] for static functions             *
*                          tm[<layer>]<Module>[_<Word>+] for exported functions*
********************************************************************************/

/******************************************************************************/
/** Module Creation function.
    - registers "KernelIfce" module with #DBG_REGISTER_MODULE.
    = creates the device (#PH_KERNELIFCE_DEVICE_NAME)
    - creates the kernelifce proc entry /proc/kernelifce .

    @retval int 0 if module initialised successfully.
*/
static int __init KernelIfce_ModuleInit(void)
{
    int result;
#ifdef PH_KERNELIFCE_ENABLE_PROC_FILE
    struct proc_dir_entry *procEntry;
#endif /* PH_KERNELIFCE_ENABLE_PROC_FILE */

    /** To track module initialisation progress, so we can undo correctly on error. */
    enum {
        notInitialisedYet = 0,
        cdevAdded = 1<<0,
        classSimpleDeviceAdded = 1<<1
    } initialised = notInitialisedYet;

    DBG_REGISTER_MODULE(gphKernelIfce_DbgUnit, "KernelIfce");

    /* Allocate a region of minor numbers (1) under a major number */
    result = alloc_chrdev_region(&gDev, 0, NR_DEVS, PH_KERNELIFCE_DEVICE_NAME);
    if (result < 0)
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_WARNING, "phKernelIfce: alloc_chrdev_region => %d\n", result);
        goto ERROR_RETURN;
    }

    gMajor = MAJOR(gDev);

    /* Set up /sys entry */
    gSysClass = class_create(THIS_MODULE, PH_KERNELIFCE_DEVICE_NAME);
    if (gSysClass == NULL)
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_WARNING, "phKernelIfce: class_create => %d\n", result);
        result = -ENOMEM;
        goto ERROR_RETURN;
    }

    cdev_init(&gCdev, &phKernelIfce_fops);
    gCdev.owner = THIS_MODULE;

    result = cdev_add(&gCdev, gDev, NR_DEVS);
    if (result)
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_WARNING, "phKernelIfce: cdev_add => %d\n", result);
        goto ERROR_RETURN;
    }
    initialised |= cdevAdded;

    class_device_create(gSysClass, NULL, gDev, NULL, PH_KERNELIFCE_DEVICE_NAME);
    initialised |= classSimpleDeviceAdded;

    init_MUTEX(&gKernelIfce_RegistryMutex);
    gppKernelIfce_NameSpaces = kmalloc(PH_KERNELIFCE_INITIAL_NUM_NAMESPACES * sizeof(pphKernelIfce_KNameSpace_t),
                                 GFP_USER);
    gKernelIfce_NameSpacesCountMax = PH_KERNELIFCE_INITIAL_NUM_NAMESPACES;

#ifdef PH_KERNELIFCE_ENABLE_PROC_FILE
    procEntry = create_proc_entry(PH_KERNELIFCE_PROC_ENTRY, 0444, NULL);
    if (procEntry != NULL)
    {
        procEntry->proc_fops = &gKernelIfce_ProcOperations;
    }
#endif /* PH_KERNELIFCE_ENABLE_PROC_FILE */

    printk("%s (%s-%s)\n", THIS_MODULE_DESCRIPTION, __DATE__, __TIME__);
    return 0;

  ERROR_RETURN:
    DBG_PRINT4(gphKernelIfce_DbgUnit, DBG_WARNING, "%s (%s-%s) => %d\n", THIS_MODULE_DESCRIPTION, __DATE__, __TIME__, result);
    if (initialised & classSimpleDeviceAdded)
    {
        class_device_destroy(gSysClass, gDev);
    }

    if (initialised & cdevAdded)
    {
        cdev_del(&gCdev);
    }

    if (gSysClass != NULL)
    {
        class_unregister(gSysClass);
    }

    return result;
}


/******************************************************************************/
/** Module exit function.
    - Remove the device.
    - Remove proc entry.
    - frees memory.
    - de-init the module.
*/
static void __exit KernelIfce_ModuleExit(void)
{
    uint n;
    printk("Shutting down %s\n", THIS_MODULE_DESCRIPTION);

#ifdef PH_KERNELIFCE_ENABLE_PROC_FILE
    remove_proc_entry(PH_KERNELIFCE_PROC_ENTRY, NULL);
#endif /* PH_KERNELIFCE_ENABLE_PROC_FILE */

    class_device_destroy(gSysClass, gDev);
    class_unregister(gSysClass);
    cdev_del(&gCdev);

    for (n = 0; n < gKernelIfce_NameSpacesCount; n++)
    {
        pphKernelIfce_KNameSpace_t pNameSpace = gppKernelIfce_NameSpaces[n];
        if (pNameSpace != NULL)
        {
            kfree(pNameSpace->funcs);
            kfree(pNameSpace);
        }
    }
    kfree(gppKernelIfce_NameSpaces);
}

/******************************************************************************/
/** Find a registered namespace.
    @param name @b IN Namespace name.
    @return Namespace structure address or NULL if not found.
    @note Pre-condition is ::gKernelIfce_RegistryMutex is locked.
 */
static pphKernelIfce_KNameSpace_t KernelIfce_NameSpaceLookup(
    const char *name
)
{
    pphKernelIfce_KNameSpace_t pNameSpace = NULL;
    uint n;
    for (n = 0; n < gKernelIfce_NameSpacesCount; n++)
    {
        pphKernelIfce_KNameSpace_t pNameSpaceTmp = gppKernelIfce_NameSpaces[n];
        if ((pNameSpaceTmp != NULL) && (strcmp(name, pNameSpaceTmp->name) == 0))
        {
            pNameSpace = pNameSpaceTmp;
            break;
        }
    }
    return pNameSpace;
}

/******************************************************************************/
/** Lookup a registered function.
    @param name @b IN Function name.
    @param pNameSpace @b IN Namespace, returned from phKernelIfce_NameSpaceRegister().
    @return Address of function structure or NULL if not found.
    @note Pre-condition is ::gKernelIfce_RegistryMutex is locked.
 */
static pphKernelIfce_KFunc_t KernelIfce_FuncLookup(
    const char *name,
    pphKernelIfce_KNameSpace_t pNameSpace
)
{
    pphKernelIfce_KFunc_t pFunc = NULL;
    uint n;
    for (n = 0; n < pNameSpace->funcsCount; n++)
    {
        pphKernelIfce_KFunc_t pFuncTmp = &pNameSpace->funcs[n];
        if (strcmp(name, pFuncTmp->name) == 0)
        {
            pFunc = pFuncTmp;
            break;
        }
    }
    return pFunc;
}

/******************************************************************************/
tmErrorCode_t phKernelIfce_NameSpaceRegister(
    const char *name,
    uint funcsCountMax,
    pphKernelIfce_KNameSpace_t *ppNameSpace
)
{
    tmErrorCode_t err;
    pphKernelIfce_KNameSpace_t pNameSpace;

    if (strlen(name) > PH_KERNELIFCE_MAX_NAME_LEN)
    {
        return PH_ERR_KERNELIFCE_NAME_TOO_LONG;
    }

    if (down_interruptible(&gKernelIfce_RegistryMutex) != 0)
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_LEVEL_1, "phKernelIfce_NameSpaceRegister(\"%s\"): interrupt\n", name);
        return PH_ERR_KERNELIFCE_INTERRUPTED;
    }

    *ppNameSpace = KernelIfce_NameSpaceLookup(name);
    if ((*ppNameSpace) != NULL)
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_WARNING, "phKernelIfce_NameSpaceRegister(\"%s\"): duplicate\n", name);
        err = PH_ERR_KERNELIFCE_DUPL_NAMESPACE;
        goto RETURN;
    }
    if (gKernelIfce_NameSpacesCount == gKernelIfce_NameSpacesCountMax)
    {
        pphKernelIfce_KNameSpace_t *ppNameSpacesNew;
        ppNameSpacesNew = (pphKernelIfce_KNameSpace_t*)kmalloc((gKernelIfce_NameSpacesCountMax + PH_KERNELIFCE_INITIAL_NUM_NAMESPACES)
                                                  * sizeof(pphKernelIfce_KNameSpace_t),
                                                  GFP_USER);
        if (ppNameSpacesNew == NULL)
        {
            err = PH_ERR_KERNELIFCE_MEMORY;
            goto RETURN;
        }
        memcpy(ppNameSpacesNew, gppKernelIfce_NameSpaces, gKernelIfce_NameSpacesCount * sizeof(pphKernelIfce_KNameSpace_t));
        kfree(gppKernelIfce_NameSpaces);
        gppKernelIfce_NameSpaces = ppNameSpacesNew;
        gKernelIfce_NameSpacesCountMax += PH_KERNELIFCE_INITIAL_NUM_NAMESPACES;
    }

    pNameSpace = (pphKernelIfce_KNameSpace_t)kmalloc(sizeof(phKernelIfce_KNameSpace_t), GFP_USER);
    if (pNameSpace == NULL)
    {
        err = PH_ERR_KERNELIFCE_MEMORY;
        goto RETURN;
    }

    if (funcsCountMax == 0)
    {
        funcsCountMax = PH_KERNELIFCE_INITIAL_NUM_FUNCS;
    }
    pNameSpace->funcs = (pphKernelIfce_KFunc_t)kmalloc(funcsCountMax * sizeof(phKernelIfce_KFunc_t), GFP_USER);
    if (pNameSpace->funcs == NULL)
    {
        err = PH_ERR_KERNELIFCE_MEMORY;
        kfree(pNameSpace);
        goto RETURN;
    }
    pNameSpace->name = name;
    pNameSpace->id = gKernelIfce_NameSpacesCount;
    pNameSpace->funcsCount = 0;
    pNameSpace->funcsCountMax = funcsCountMax;

    gppKernelIfce_NameSpaces[gKernelIfce_NameSpacesCount] = pNameSpace;
    gKernelIfce_NameSpacesCount++;
    *ppNameSpace = pNameSpace;
    DBG_PRINT2(gphKernelIfce_DbgUnit, DBG_LEVEL_1,
               "phKernelIfce_NameSpaceRegister(\"%s\"): id=%u OK\n", name, pNameSpace->id);
    err = TM_OK;

  RETURN:
    up(&gKernelIfce_RegistryMutex);
    if (err != TM_OK)
    {
        DBG_PRINT2(gphKernelIfce_DbgUnit, DBG_WARNING,
                   "phKernelIfce_NameSpaceRegister(\"%s\"): error 0x%x\n", name, err);
    }
    return err;
}
EXPORT_SYMBOL(phKernelIfce_NameSpaceRegister);

/******************************************************************************/
tmErrorCode_t phKernelIfce_NameSpaceUnregister(
    const char *name
)
{
    tmErrorCode_t err;
    pphKernelIfce_KNameSpace_t pNameSpace;

    if (down_interruptible(&gKernelIfce_RegistryMutex) != 0)
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_LEVEL_1,
                   "phKernelIfce_NameSpaceUnregister(\"%s\"): interrupt\n", name);
        return PH_ERR_KERNELIFCE_INTERRUPTED;
    }

    pNameSpace = KernelIfce_NameSpaceLookup(name);
    if (pNameSpace == NULL)
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_WARNING,
                   "phKernelIfce_NameSpaceUnregister(\"%s\"): not registered\n", name);
        err = PH_ERR_KERNELIFCE_UNKNOWN_NAMESPACE;
        goto RETURN;
    }
    gppKernelIfce_NameSpaces[pNameSpace->id] = NULL;
    kfree(pNameSpace->funcs);
    kfree(pNameSpace);
    err = TM_OK;

  RETURN:
    up(&gKernelIfce_RegistryMutex);
    if (err == TM_OK)
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_LEVEL_1,
                   "phKernelIfce_NameSpaceUnregister(\"%s\"): OK\n", name);
    }
    else
    {
        DBG_PRINT2(gphKernelIfce_DbgUnit, DBG_WARNING,
                   "phKernelIfce_NameSpaceUnregister(\"%s\"): error 0x%x\n", name, err);
    }
    return err;
}
EXPORT_SYMBOL(phKernelIfce_NameSpaceUnregister);

/******************************************************************************/
tmErrorCode_t phKernelIfce_FuncRegister(
    pphKernelIfce_KNameSpace_t pNameSpace,
    const char *name,
    phKernelIfce_Handler_t *pHandler
    )
{
    tmErrorCode_t err;
    pphKernelIfce_KFunc_t pFunc;

    if (strlen(name) > PH_KERNELIFCE_MAX_NAME_LEN)
    {
        return PH_ERR_KERNELIFCE_NAME_TOO_LONG;
    }

    if (down_interruptible(&gKernelIfce_RegistryMutex) != 0)
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_LEVEL_1,
                   "phKernelIfce_FuncRegister(\"%s\"): interrupt\n", name);
        return PH_ERR_KERNELIFCE_INTERRUPTED;
    }

    if (KernelIfce_FuncLookup(name, pNameSpace) != NULL)
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_WARNING,
                   "phKernelIfce_FuncRegister(\"%s\"): duplicate\n", name);
        err = PH_ERR_KERNELIFCE_DUPL_FUNC;
        goto RETURN;
    }

    if (pNameSpace->funcsCount == pNameSpace->funcsCountMax)
    {
        pphKernelIfce_KFunc_t pFuncsNew;
        pFuncsNew = (pphKernelIfce_KFunc_t)kmalloc((pNameSpace->funcsCountMax + PH_KERNELIFCE_INITIAL_NUM_FUNCS)
                                                  * sizeof(phKernelIfce_KFunc_t),
                                                  GFP_USER);
        if (pFuncsNew == NULL)
        {
            err = PH_ERR_KERNELIFCE_MEMORY;
            goto RETURN;
        }
        memcpy(pFuncsNew, pNameSpace->funcs, pNameSpace->funcsCount * sizeof(phKernelIfce_KFunc_t));
        kfree(pNameSpace->funcs);
        pNameSpace->funcs = pFuncsNew;
        pNameSpace->funcsCountMax += PH_KERNELIFCE_INITIAL_NUM_FUNCS;
    }

    pFunc = &pNameSpace->funcs[pNameSpace->funcsCount];
    pFunc->name = name;
    pFunc->id = pNameSpace->funcsCount;
    pFunc->callCount = 0;
    pFunc->handler = pHandler;

    pNameSpace->funcsCount++;
    DBG_PRINT2(gphKernelIfce_DbgUnit, DBG_INTERFACE_LEAVE,
               "phKernelIfce_FuncRegister(\"%s\"): id=%u OK\n", name, pFunc->id);
    err = TM_OK;

  RETURN:
    up(&gKernelIfce_RegistryMutex);
    if (err != TM_OK)
    {
        DBG_PRINT2(gphKernelIfce_DbgUnit, DBG_WARNING,
                   "phKernelIfce_FuncRegister(\"%s\"): error 0x%x\n", name, err);
    }
    return err;
}
EXPORT_SYMBOL(phKernelIfce_FuncRegister);

/******************************************************************************/
tmErrorCode_t phKernelIfce_Register(
    const char *nameSpace,
    const phKernelIfce_FuncSetup_t functions[],
    uint numFunctions)
{
    pphKernelIfce_KNameSpace_t pNameSpace;
    tmErrorCode_t err;

    err = phKernelIfce_NameSpaceRegister(nameSpace, numFunctions, &pNameSpace);
    if (err != TM_OK)
    {
        return err;
    }

    err = phKernelIfce_RegisterFunctions(pNameSpace, functions, numFunctions);
    return err;
}

EXPORT_SYMBOL(phKernelIfce_Register);

/******************************************************************************/
tmErrorCode_t phKernelIfce_RegisterFunctions(
    pphKernelIfce_KNameSpace_t pNameSpace,
    const phKernelIfce_FuncSetup_t functions[],
    uint numFunctions)
{
    tmErrorCode_t err;
    uint n;

    for (n = 0; n < numFunctions; n++)
    {
        const phKernelIfce_FuncSetup_t *pFunc = &functions[n];
        err = phKernelIfce_FuncRegister(
            pNameSpace,
            pFunc->name,
            pFunc->handler);
        if (err != TM_OK)
        {
            return err;
        }
    }
    return TM_OK;
}

EXPORT_SYMBOL(phKernelIfce_RegisterFunctions);

#ifdef PH_KERNELIFCE_ENABLE_PROC_FILE
/******************************************************************************/
/** Open the proc file.
 */
static int KernelIfce_ProcOpen(struct inode *inode, struct file *file)
{
    int st = 0;
    char *buf;
    size_t buf_size = PAGE_SIZE - sizeof(phKernelIfce_ProcReadData_t);
    size_t buf_used = 0;
    bool truncated = false;
    phKernelIfce_ProcReadData_t *procReadData;
    uint namespace = 0;
    int func = -1;

    DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_INTERFACE_ENTER, ">>%s\n", __FUNCTION__);

    if (down_interruptible(&gKernelIfce_RegistryMutex) != 0)
    {
        return -EINTR;
    }

    procReadData = (phKernelIfce_ProcReadData_t*)kmalloc(
        sizeof(phKernelIfce_ProcReadData_t) + buf_size, GFP_KERNEL);
    if (procReadData == NULL)
    {
        st = -ENOMEM;
        goto RETURN;
    }
    buf = procReadData->buf;

    do {
        for (; namespace < gKernelIfce_NameSpacesCount; namespace++)
        {
            pphKernelIfce_KNameSpace_t pNameSpace = gppKernelIfce_NameSpaces[namespace];

            /* Was namespace deregistered? */
            if (pNameSpace == NULL)
            {
                continue;
            }

            if (func < 0)
            {
                /* Write header for namespace */
                size_t free = buf_size - buf_used;
                size_t written;
                DBG_PRINT2(gphKernelIfce_DbgUnit, DBG_LEVEL_2, "%s first func for %u\n", __FUNCTION__, namespace);
                PH_KERNELIFCE_DBGPROCOPEN0;
                written = (size_t)snprintf(
                    buf + buf_used, free,
                    "#namespace %u %s\n", pNameSpace->id, pNameSpace->name);
                truncated = (bool)(written >= free);
                PH_KERNELIFCE_DBGPROCOPEN1;
                if (truncated)
                {
                    break;
                }
                buf_used += written;
                func = 0;
            }
            for (; ((uint)func) < pNameSpace->funcsCount; func++)
            {
                size_t free = buf_size - buf_used;
                pphKernelIfce_KFunc_t pFunc = &pNameSpace->funcs[func];
                size_t written;
                DBG_PRINT2(gphKernelIfce_DbgUnit, DBG_LEVEL_2, "%s func %u\n", __FUNCTION__, func);
                PH_KERNELIFCE_DBGPROCOPEN0;
                written = (size_t)snprintf(
                    buf + buf_used, free,
                    "%u %s %u\n", pFunc->id, pFunc->name, pFunc->callCount);
                truncated = (bool)(written >= free);
                PH_KERNELIFCE_DBGPROCOPEN1;
                if (truncated)
                {
                    break;
                }
                buf_used += written;
            }
            if (truncated)
            {
                break;
            }
            /* For next namespace iteration: */
            func = -1;
        }
        if (truncated)
        {
            /* Reallocate for more space. */
            phKernelIfce_ProcReadData_t *procReadDataNew;
            DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_LEVEL_1, "allocating more space - buf_size %u insufficient\n", buf_size);
            procReadDataNew = (phKernelIfce_ProcReadData_t*)kmalloc(
                sizeof(phKernelIfce_ProcReadData_t) + buf_size + PAGE_SIZE, GFP_KERNEL);
            if (procReadDataNew == NULL)
            {
                kfree(procReadData);
                st = -ENOMEM;
                goto RETURN;
            }
            memcpy(procReadDataNew, procReadData, sizeof(phKernelIfce_ProcReadData_t) + buf_used);
            procReadDataNew = procReadData;
            kfree(procReadData);
            buf_size += PAGE_SIZE;
            buf = procReadData->buf;
        }
    } while (truncated);
    procReadData->used = buf_used;
    file->private_data = procReadData;

    DBG_PRINT2(gphKernelIfce_DbgUnit, DBG_LEVEL_1, "%s used=%u\n", __FUNCTION__, procReadData->used);
  RETURN:
    up(&gKernelIfce_RegistryMutex);
    DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_INTERFACE_LEAVE, "<<%s\n", __FUNCTION__);
    return st;
}

/******************************************************************************/
/** Close the proc file.
 */
static ssize_t KernelIfce_ProcRelease(struct inode *inode, struct file *file)
{
    kfree(file->private_data);
    return 0;
}

/******************************************************************************/
/**
 * Read the /proc file.
 * The format of the proc file is:
 * <pre>
 * {
 *   #namespace NAMESPACEID NAME
 *   {
 *     FUNCID NAME NUMCALLS
 *   }
 * }
 * </pre>
 * @note '{ ...}' indicates zero or more occurances.
 */
static
ssize_t KernelIfce_ProcRead(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
    phKernelIfce_ProcReadData_t *procReadData =
        (phKernelIfce_ProcReadData_t *)file->private_data;
    uint buf_used;
    DBG_PRINT3(gphKernelIfce_DbgUnit, DBG_INTERFACE_ENTER, ">>%s size=%u pos=%llu\n", __FUNCTION__, size, *ppos);

    buf_used = procReadData->used;
    DBG_PRINT2(gphKernelIfce_DbgUnit, DBG_LEVEL_2, "%s buf_used=%u\n", __FUNCTION__, buf_used);

    if ((*ppos) >= (loff_t)buf_used)
    {
        /* we have finished to read, return 0 */
        size = 0U;
    }
    else
    {
        /* How much can we read? */
        if ((((loff_t)buf_used) - (*ppos)) < (loff_t)size)
        {
            size = (size_t)(((loff_t)buf_used) - (*ppos));
            DBG_PRINT2(gphKernelIfce_DbgUnit, DBG_LEVEL_2, "%s truncating size to=%u\n", __FUNCTION__, size);
        }
        DBG_PRINT2(gphKernelIfce_DbgUnit, DBG_LEVEL_2, "%s copying %u bytes\n", __FUNCTION__, size);
        /* Read it */
        if (copy_to_user(buf, procReadData->buf + (*ppos), size) != 0)
        {
            return -EFAULT;
        }
        *ppos += (loff_t)size;
    }

    return (ssize_t)size;
}
#endif /* PH_KERNELIFCE_ENABLE_PROC_FILE */

/******************************************************************************/
/** Open the device.
 */
static int KernelIfce_open(struct inode *inode, struct file *file)
{
    return 0;
}

/******************************************************************************/
/** Close the device.
 */
static ssize_t KernelIfce_release(struct inode *inode, struct file *file)
{
    return 0;
}

/******************************************************************************/
/** Handle call from user mode.
    @return A negative error code on error, or 0 if the function call is performed. */
static int KernelIfce_UserModeCall(pphKernelIfce_CallData_t pCallData)
{
    tmErrorCode_t err = PH_ERR_KERNELIFCE_UNKNOWN;
    int ret;
#if PH_KERNELIFCE_COPY_CALLDATA
    phKernelIfce_NameSpace_t userNameSpace;
    phKernelIfce_Func_t userFunc;
#endif
    pphKernelIfce_NameSpace_t pUserNameSpace;
    pphKernelIfce_Func_t pUserFunc;
    pphKernelIfce_KNameSpace_t pNameSpace;
    pphKernelIfce_KFunc_t pFunc;
    size_t len;
    pphKernelIfce_CallData_t pCallDataK = NULL;
    char name[PH_KERNELIFCE_MAX_NAME_LEN + 1];
    pphKernelIfce_Handler_t handler;
    bool freeMutex = false;

    /* Note: VERIFY_WRITE implies VERIFY_READ. */
    if (!access_ok(VERIFY_WRITE, pCallData, sizeof(phKernelIfce_CallData_t)))
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_WARNING, "KernelIfce_UserModeCall(%p) EFAULT\n", pCallData);
        return -EFAULT;
    }
    ret = get_user(len, (size_t __user *)&(pCallData->len));
    if (ret != 0)
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_WARNING, "KernelIfce_UserModeCall(%p) get_user\n", &pCallData->len);
        return ret;
    }

#if PH_KERNELIFCE_COPY_CALLDATA
    pCallDataK = kmalloc(len, GFP_USER);
    if (pCallDataK == NULL)
    {
        DBG_PRINT2(gphKernelIfce_DbgUnit, DBG_WARNING, "KernelIfce_UserModeCall(%p) kmalloc(%u)\n", pCallData, len);
        return ret;
    }
    if (copy_from_user(pCallDataK, pCallData, len))
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_WARNING, "KernelIfce_UserModeCall(%p) copy_from_user\n", pCallData);
        return -EFAULT;
    }
#else /* #if PH_KERNELIFCE_COPY_CALLDATA */
    pCallDataK = pCallData;
#endif

#if PH_KERNELIFCE_COPY_CALLDATA
    /* Handle function and namespace data. */
    if (copy_from_user(&userFunc, pCallDataK->pFunc, sizeof(phKernelIfce_Func_t)))
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_WARNING, "KernelIfce_UserModeCall(%p) pFunc EFAULT\n", pCallData->pFunc);
        return -EFAULT;
    }
    if (copy_from_user(&userNameSpace, userFunc.pNameSpace, sizeof(phKernelIfce_NameSpace_t)))
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_WARNING, "KernelIfce_UserModeCall(%p) pNameSpace EFAULT\n", userFunc.pNameSpace);
        return -EFAULT;
    }
#else /* #if PH_KERNELIFCE_COPY_CALLDATA */
    if (!access_ok(VERIFY_WRITE, pCallData->pFunc, sizeof(phKernelIfce_Func_t)))
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_WARNING, "KernelIfce_UserModeCall(%p) pFunc EFAULT\n", pCallData);
        return -EFAULT;
    }
    pUserFunc = pCallData->pFunc;
    if (!access_ok(VERIFY_WRITE, pUserFunc->pNameSpace, sizeof(phKernelIfce_NameSpace_t)))
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_WARNING, "KernelIfce_UserModeCall(%p) pNameSpace EFAULT\n", pCallData);
        return -EFAULT;
    }
    pUserNameSpace = pUserFunc->pNameSpace;
#endif

    DBG_PRINT2(gphKernelIfce_DbgUnit, DBG_LEVEL_2,
               "KernelIfce_UserModeCall nameSpace=%d func=%d",
               pUserNameSpace->id, pUserFunc->id);

    if (down_interruptible(&gKernelIfce_RegistryMutex) != 0)
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_LEVEL_1, "phKernelIfce_NameSpaceRegister(\"%s\"): interrupt\n", name);
        ret = -EINTR;
        goto RETURN;
    }
    freeMutex = true;

    /* Which namespace is being used here? */
    if (pUserNameSpace->id >= 0)
    {
        /* Is this a valid namespace id? */
        if (((uint)pUserNameSpace->id) >= gKernelIfce_NameSpacesCount)
        {
            err = PH_ERR_KERNELIFCE_BAD_NAMESPACE_ID;
            goto RETURN;
        }
        pNameSpace = gppKernelIfce_NameSpaces[pUserNameSpace->id];
    }
    else
    {
        /* This is the first time this namespace is accessed by this userspace process.
           We'll get the name and lookup the id of the namespace. */
        long slen;
        slen = strncpy_from_user(name, pUserNameSpace->name, PH_KERNELIFCE_MAX_NAME_LEN);
        if (slen < 0)
        {
            DBG_PRINT0(gphKernelIfce_DbgUnit, DBG_WARNING, "KernelIfce_UserModeCall() nameSpace name EFAULT\n");
            ret = -EFAULT;
            goto RETURN;
        }
        name[slen] = 0;
        pNameSpace = KernelIfce_NameSpaceLookup(name);
        if (pNameSpace == NULL)
        {
            err = PH_ERR_KERNELIFCE_UNKNOWN_NAMESPACE;
            goto RETURN;
        }
        if (put_user(pNameSpace->id, (int __user *)&(pUserFunc->pNameSpace->id)))
        {
            DBG_PRINT0(gphKernelIfce_DbgUnit, DBG_WARNING, "KernelIfce_UserModeCall() write back nameSpace id\n");
            ret = -EFAULT;
            goto RETURN;
        }
    }

    /* Which function is being used here? */
    if (pUserFunc->id >= 0)
    {
        /* Is this a valid function id for the namespace? */
        if (((uint)pUserFunc->id) >= pNameSpace->funcsCount)
        {
            err = PH_ERR_KERNELIFCE_BAD_FUNC_ID;
            goto RETURN;
        }
        pFunc = &pNameSpace->funcs[pUserFunc->id];
    }
    else
    {
        /* This is the first time this function is accessed by this userspace process.
           We'll get the name and lookup the id of the function. */
        long slen;
        slen = strncpy_from_user(name, pUserFunc->name, PH_KERNELIFCE_MAX_NAME_LEN);
        if (slen < 0)
        {
            DBG_PRINT0(gphKernelIfce_DbgUnit, DBG_WARNING, "KernelIfce_UserModeCall() copy func name\n");
            ret = -EFAULT;
            goto RETURN;
        }
        name[slen] = 0;
        pFunc = KernelIfce_FuncLookup(name, pNameSpace);
        if (pFunc == NULL)
        {
            err = PH_ERR_KERNELIFCE_UNKNOWN_FUNC;
            goto RETURN;
        }
        if (put_user(pFunc->id, (int __user *)&(pCallDataK->pFunc->id)))
        {
            DBG_PRINT0(gphKernelIfce_DbgUnit, DBG_WARNING, "KernelIfce_UserModeCall() write back func id\n");
            ret = -EFAULT;
            goto RETURN;
        }
    }

    pFunc->callCount++;
    handler = pFunc->handler;

    /* Now it should be save to free the mutex. After this we should NOT access pNameSpace or pFunc anymore,
       because they may be moved in memory as part of registration of namespaces/functions.
       We must free the mutex before calling the function,
       otherwise only one function could be called at a time! */
    up(&gKernelIfce_RegistryMutex);
    freeMutex = false;

    /* Call the function (handler). */
    pCallDataK->err = TM_OK;
    (handler)(pCallDataK);
    err = pCallDataK->err;

#if PH_KERNELIFCE_COPY_CALLDATA
    if (copy_to_user(pCallData, pCallDataK, len))
    {
        DBG_PRINT1(gphKernelIfce_DbgUnit, DBG_WARNING, "KernelIfce_UserModeCall(%p) copy_to_user\n", pCallData);
        err = PH_ERR_KERNELIFCE_FAULT;
        goto RETURN;
    }
#else /* #if PH_KERNELIFCE_COPY_CALLDATA */
    pCallDataK = pCallData;
#endif
    
  RETURN:
    if (freeMutex)
    {
        up(&gKernelIfce_RegistryMutex);
    }
#if PH_KERNELIFCE_COPY_CALLDATA
    if (pCallDataK != NULL)
    {
        kfree(pCallDataK);
    }
    /* Write back the error code: */
    put_user(err, (tmErrorCode_t __user *)&(pCallData->err));
#endif

    /* If no error occured, then we return 0. Otherwise we set ret to
     * -err only if ret is still 0. */
    if (err == TM_OK)
    {
        ret = 0;
    }
    else
    {
        if (ret == 0)
        {
            ret = -(int)err;
        }
    }
    return ret;
}

/******************************************************************************/
/** Handle all ioctls supported by phKernelIfce.
    @param inode @b IN File inode.
    @param file @b IN File pointer for the inode.
    @param cmd @b IN IOCTL command code.
    @param arg @b INOUT IOCTL command code specific argument. 
    @return 0 on success, otherwise a negative error code.
*/
static int KernelIfce_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
    int result = -ENOTTY;
    switch (cmd)
    {
        case PH_KERNELIFCE_IOCTL_CALL:
        {
            pphKernelIfce_CallData_t pCallData = (pphKernelIfce_CallData_t)arg;
            result = KernelIfce_UserModeCall(pCallData);
        }
        break;

        default:
            break;
    }

    if (in_interrupt() || in_atomic())
    {
        BUG();
    }

    return result;
}

/******************************************************************************/
