/*++
"This software program is available to you under a choice of one of two
licenses.  You may choose to be licensed under either the GNU General Public
License (GPL) Version 2, June 1991, available at
http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License,
the text of which follows:

"Recipient" has requested a license and Intel Corporation ("Intel") is willing
to grant a license for the software entitled InfiniBand(tm) System Software
(the "Software") being provided by Intel Corporation.

The following definitions apply to this License:

"Licensed Patents" means patent claims licensable by Intel Corporation which
are necessarily infringed by the use or sale of the Software alone or when
combined with the operating system referred to below.

"Recipient" means the party to whom Intel delivers this Software.
"Licensee" means Recipient and those third parties that receive a license to
any operating system available under the GNU Public License version 2.0 or
later.

Copyright (c) 1996-2003 Intel Corporation. All rights reserved.

The license is provided to Recipient and Recipient's Licensees under the
following terms.

Redistribution and use in source and binary forms of the Software, with or
without modification, are permitted provided that the following conditions are
met:
Redistributions of source code of the Software may retain the above copyright
notice, this list of conditions and the following disclaimer.

Redistributions in binary form of the Software may reproduce the above
copyright notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

Neither the name of Intel Corporation nor the names of its contributors shall
be used to endorse or promote products derived from this Software without
specific prior written permission.

Intel hereby grants Recipient and Licensees a non-exclusive, worldwide,
royalty-free patent license under Licensed Patents to make, use, sell, offer
to sell, import and otherwise transfer the Software, if any, in source code
and object code form. This license shall include changes to the Software that
are error corrections or other minor changes to the Software that do not add
functionality or features when the Software is incorporated in any version of
a operating system that has been distributed under the GNU General Public
License 2.0 or later.  This patent license shall apply to the combination of
the Software and any operating system licensed under the GNU Public License
version 2.0 or later if, at the time Intel provides the Software to Recipient,
such addition of the Software to the then publicly available versions of such
operating system available under the GNU Public License version 2.0 or later
(whether in gold, beta or alpha form) causes such combination to be covered by
the Licensed Patents. The patent license shall not apply to any other
combinations which include the Software. No hardware per se is licensed
hereunder.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 INTEL OR ITS 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."
--*/

/*
 * Abstract:
 *	This module defines the functionality of the device framework.
 *
 * Environment:
 *	Linux Kernel Mode
 *
 * $Revision: 1.1 $
 */

#include <complib/cl_types.h>

#include <complib/cl_memory.h>
#include <complib/cl_event.h>
#include <complib/cl_debug.h>
#include <complib/cl_spinlock.h>
#include <complib/cl_passivelock.h>
#include <complib/cl_math.h>
#include <complib/cl_qmap.h>
#include <complib/cl_syshelper.h>
#include <complib/cl_waitobj.h>

#include <complib/cl_device.h>
#include <complib/cl_system.h>
#include <complib/cl_atomic.h>

#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioctl.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/kdev_t.h>

#define SYS_DRIVER_NAME		"Linux Common Device Framework"
#define MAX_DEVICES			(10)

#if !defined minor
#define minor(kdev_t) MINOR((kdev_t))
#define major(kdev_t) MAJOR((kdev_t))
#endif

/****i* Component Library Device/device_info
* NAME
*	device_info
*
* PURPOSE
*	Structure containing information about the created system
*	device.
*/
typedef struct device_info {
	cl_dev_info_t dev_info;
	uint32_t use_count;

} device_info_t;
/***********/

/****i* Component Library Device/device_ioctl_info
* NAME
*	device_ioctl_info
*
* PURPOSE
*	Structure defining the information pertaining to the current ioctl.
*/
typedef struct device_ioctl_info {
	cl_ioctl_info_t user_ioctl_info;	/* info passed by the user. */
	cl_ioctl_info_t kernel_ioctl_info;	/* info sent to the driver. */
	cl_event_t completion_event;	/* signal on completion. */

} device_ioctl_info_t;
/***********/

/****i* Component Library Device/device_open_context
* NAME
*	device_open_context
*
* PURPOSE
*	Pointer to context passed in when the device is opened.
*/
typedef struct device_open_context {
	void *private_data;

} device_open_context_t;
/***********/

/*
 * Global Table of currently created devices.
 */

device_info_t g_device_info_table[MAX_DEVICES];
static int g_device_major = 0;
static cl_spinlock_t g_sysdev_lock;

/* Helper function to create a new system device */
cl_status_t
cl_dev_create(IN cl_dev_info_t * p_dev_info, OUT cl_dev_handle_t * p_dev_handle)
{
	cl_status_t status = CL_SUCCESS;
	char *p_prog;
	char *argv[7], *envp[1];
	char arg_major[10];
	char arg_minor[10];

	uint32_t new_minor;

	/*
	 * Pick a new minor number
	 */

	if (p_dev_info == NULL) {
		cl_msg_out("sys_create_device: p_dev_info is NULL\n");
		return CL_INVALID_PARAMETER;
	}

	cl_spinlock_acquire(&g_sysdev_lock);

	for (new_minor = 0; new_minor < MAX_DEVICES; new_minor++) {
		if (g_device_info_table[new_minor].dev_info.name == NULL) {
			/* found a new minor # */
			break;
		}
	}

	cl_spinlock_release(&g_sysdev_lock);

	/* Have we reached the max ? */
	if (new_minor == MAX_DEVICES) {
		cl_msg_out
		    ("sys_create_device: minor numbers exhausted, total(%d)\n",
		     MAX_DEVICES);
		*p_dev_handle = NULL;
		status = CL_ERROR;
		goto failed_end;
	}
	/* Set the context for the new device in g_device_info_table */
	g_device_info_table[new_minor].dev_info = *p_dev_info;
	g_device_info_table[new_minor].use_count = 0;

	/* Construct a new device name, based on device major and minor */
	sprintf(arg_major, "%d", g_device_major);
	sprintf(arg_minor, "%d", new_minor);

	/* Delete the device, if one exists */
	p_prog = "/bin/rm";	/* shell command */
	argv[0] = p_prog;
	argv[1] = "-f";		// force
	argv[2] = g_device_info_table[new_minor].dev_info.name;
	argv[3] = NULL;
	envp[0] = NULL;

	/* Now remove it ! */
	cl_user_mode_helper(p_prog, argv, envp);

	/* All cleaned up, now create the device. */
	p_prog = "/bin/mknod";	/* shell command */

	argv[0] = p_prog;
	argv[1] = "--mode=666";
	argv[2] = g_device_info_table[new_minor].dev_info.name;
	argv[3] = "c";		/* yup it's a character device */
	argv[4] = arg_major;	/* major number, see /proc/devices */
	argv[5] = arg_minor;	/* minor for this instance */
	argv[6] = NULL;

	envp[0] = NULL;

	cl_user_mode_helper(p_prog, argv, envp);

	/*
	 *      Pass the pointer to the g_device_info_table entry for this minor
	 *      instance as a handle to the caller for future reference.
	 */

	cl_msg_out("sys_create_device: Succeeded (%s, major %s, minor %s)\n",
		   g_device_info_table[new_minor].dev_info.name,
		   arg_major, arg_minor);

	*p_dev_handle = (void *) &g_device_info_table[new_minor];

      failed_end:

	return (status);
}

void
cl_dev_destroy(IN cl_dev_handle_t dev_handle)
{				/* handle to a previously created device */
	char *p_prog;
	char *argv[4], *envp[1];
	device_info_t *p_dev_info = NULL;

	p_dev_info = (device_info_t *) dev_handle;

	cl_msg_out("sys_delete_device: removing [%s]\n",
		   p_dev_info->dev_info.name);

	p_prog = "/bin/rm";	/* shell command */

	argv[0] = p_prog;
	argv[1] = "-f";		/* force remove */
	argv[2] = p_dev_info->dev_info.name;
	argv[3] = NULL;

	envp[0] = NULL;

	cl_user_mode_helper(p_prog, argv, envp);

	p_dev_info->dev_info.name = NULL;	// Remove its entry from the Table
}

/*
 * Function (for file_ops) exported by the framework.
 * NOTE: These are all internal to the device framework.
 */

static int
 _open_device(IN struct inode *p_inode, IN struct file *p_file);

static int
 _release_device(IN struct inode *p_inode, IN struct file *p_file);

static ssize_t
_read_device(IN struct file *p_file,
	     OUT char *p_buf, IN size_t count, OUT loff_t * p_filepos);

static ssize_t
_write_device(IN struct file *p_file,
	      IN const char *p_buf, IN size_t count, OUT loff_t * p_filepos);

static int
 _ioctl_device(IN struct inode *p_inode,
	       IN struct file *p_file,
	       IN unsigned int command, IN OUT unsigned long p_arg);

static int
 _mmap_device(IN struct file *p_file, IN struct vm_area_struct *p_vma);

static struct file_operations g_device_fops = {
      owner:THIS_MODULE,
      read:_read_device,
      write:_write_device,
      ioctl:_ioctl_device,
      open:_open_device,
      mmap:_mmap_device,
      release:_release_device,
};

static int
_open_device(IN struct inode *p_inode, IN struct file *p_file)
{
	int minor_val = 0;
	cl_status_t status = CL_ERROR;
	device_open_context_t *p_device_open_ctx = NULL;

	/*
	 * Identify the device with its minor # and check if
	 * there is a device registered. Also check if the
	 * device has an open function.
	 */
	minor_val = minor(p_inode->i_rdev);

	if ((g_device_info_table[minor_val].dev_info.name == NULL) ||
	    (g_device_info_table[minor_val].dev_info.pfn_open == NULL)) {
		/* Bad File Descriptor */
		return (-EBADF);
	}

	/*
	 * Check if max opens are exceeded.
	 * But if max_use_count == 0 then assume no limit
	 */
	if (g_device_info_table[minor_val].dev_info.max_use_count != 0) {
		/* Okay there is a limit, check if we've reached it */
		if (g_device_info_table[minor_val].use_count ==
		    g_device_info_table[minor_val].dev_info.max_use_count) {
			cl_msg_out
			    ("_open_device: Max open count limit reached \n");

			cl_msg_out(" dev: %s max = %d)\n",
				   g_device_info_table[minor_val].dev_info.name,
				   g_device_info_table[minor_val].dev_info.
				   max_use_count);

			/* No more space on device */
			return (-ENOSPC);
		}
	}

	/*
	 * Allocate memory for this Open Context.
	 * The generic device framework will use this for housekeeping
	 */
	p_device_open_ctx = cl_zalloc(sizeof (device_open_context_t));

	if (p_device_open_ctx == NULL) {
		return (-ENOSPC);
	}

	p_file->private_data = p_device_open_ctx;

	/*
	 * Now call the registered "open" function and get the open context
	 * that it returns.
	 */
	status =
	    (*g_device_info_table[minor_val].dev_info.
	     pfn_open) (g_device_info_table[minor_val].dev_info.
			p_device_context, &(p_device_open_ctx->private_data));

	/*
	 * Map the returned status to OS specific status
	 */

	if (status == CL_SUCCESS) {
		cl_atomic_inc(&g_device_info_table[minor_val].use_count);
	} else {
		cl_free(p_device_open_ctx);
		p_file->private_data = NULL;

		/* device error */
		return (-ENXIO);
	}

	/* Success ! */
	return 0;
}

int
_release_device(IN struct inode *p_inode, IN struct file *p_file)
{
	int minor_val = 0;
	cl_status_t status = CL_SUCCESS;
	device_open_context_t *p_device_open_ctx = NULL;

	/*
	 * Identify the device by its minor_val
	 */
	minor_val = minor(p_inode->i_rdev);

	// Get the Open Context from p_file->private_data
	p_device_open_ctx = p_file->private_data;

	if (p_device_open_ctx != NULL) {
		if (g_device_info_table[minor_val].dev_info.pfn_close != NULL) {

			status =
			    (g_device_info_table[minor_val].dev_info.
			     pfn_close) (g_device_info_table[minor_val].
					 dev_info.p_device_context,
					 p_device_open_ctx->private_data);
		} else {
			cl_msg_out
			    ("_close_device: no close handler registered for %s \n",
			     g_device_info_table[minor_val].dev_info.name);

			status = CL_ERROR;
		}

		/*
		 * It is okay to deallocate this memory, it is allocated per
		 * open instance
		 * and every new open call gets a new p_file handle.
		 */
		cl_free(p_device_open_ctx);
		p_file->private_data = NULL;

		cl_atomic_dec(&g_device_info_table[minor_val].use_count);

		if (status == CL_SUCCESS) {
			return (0);
		} else {
			return (-EFAULT);
		}
	} else {
		return (-ENODEV);
	}

	return (0);
}

ssize_t
_read_device(IN struct file * p_file,
	     OUT char *p_buf, IN size_t count, OUT loff_t * p_filepos)
{
	cl_msg_out("_read_device: unsupported\n");
	return (-ENXIO);
}

ssize_t
_write_device(IN struct file * p_file,
	      IN const char *p_buf, IN size_t count, OUT loff_t * p_filepos)
{
	cl_msg_out("_write_device: unsupported\n");
	return (-ENXIO);
}

int
_ioctl_device(IN struct inode *p_inode,
	      IN struct file *p_file,
	      IN unsigned int command, IN unsigned long p_arg)
{
	int minor_val = 0;
	int retval = -EINVAL;
	cl_status_t status = CL_ERROR;
	void *p_kernel_buf = NULL;
	device_ioctl_info_t *p_device_ioctl_info = NULL;
	device_open_context_t *p_device_open_ctx = NULL;

	minor_val = minor(p_inode->i_rdev);

	if (g_device_info_table[minor_val].dev_info.pfn_ioctl == NULL) {
		cl_msg_out("ioctl_device: No IOCTL Handler!! [%s]\n",
			   g_device_info_table[minor_val].dev_info.name);
		return -EINVAL;
	}

	/*
	 * Allocate memory for device_ioctl_info_t for this Ioctl.
	 * This memory needs to be freed if the Ioctl succeeds.
	 * If the Ioctl return pending then it needs to be
	 * either queued up (for a non blocking Ioctl and freed when
	 * the async completion is received) or is freed after a
	 * wait on the event associated with the Ioctl.
	 */
	p_device_ioctl_info = cl_zalloc(sizeof (device_ioctl_info_t));

	if (p_device_ioctl_info == NULL) {
		retval = -ENOSPC;
		goto ioctl_fail;
	}

	cl_event_construct(&(p_device_ioctl_info->completion_event));

	/* Copy the argumets from User space to Kernel space */
	CL_ASSERT(p_arg);

	if (p_arg) {
		/*
		 * p_device_ioctl_info->kernel_ioctl_info is filled
		 * internally and passed to the consumer of this ioctl
		 * (typically a driver registered ioctl handler).
		 *
		 * The user does not provide data for it so don't copy
		 * data of size = sizeof(device_ioctl_info_t)
		 */
		retval = copy_from_user(&(p_device_ioctl_info->user_ioctl_info),
					(void *) p_arg,
					sizeof (cl_ioctl_info_t));
		if (retval) {
			retval = -EFAULT;
			goto ioctl_fail;
		}
	} else {
		retval = -EINVAL;
		goto ioctl_fail;
	}

	/*
	 * Allocate Kernel memmory for the input/output buffer
	 * Consumer gets just one buffer that is the larger of the two
	 * and is responsible to copy back the necessary data to this
	 * single buffer after a successful ioctl call.
	 */

	if (p_device_ioctl_info->user_ioctl_info.buf_size) {
		p_kernel_buf =
		    cl_zalloc(p_device_ioctl_info->user_ioctl_info.buf_size);
		if (p_kernel_buf == NULL) {
			retval = -ENOSPC;
			goto ioctl_fail;
		}

		retval = copy_from_user(p_kernel_buf,
					p_device_ioctl_info->user_ioctl_info.
					p_buf,
					p_device_ioctl_info->user_ioctl_info.
					buf_size);

		if (retval) {
			retval = -EFAULT;
			goto ioctl_fail;
		}
	}

	/*
	 *
	 * Now fill up the kernel_ioctl_info struct which is passed
	 * onwards to the ioctl handler. This info will also be used
	 * by the ioctl handler at the time of completions for
	 * copying data back into the kernelbuffer provided to it.
	 */
	p_device_ioctl_info->kernel_ioctl_info.command =
	    p_device_ioctl_info->user_ioctl_info.command;
	p_device_ioctl_info->kernel_ioctl_info.p_buf = p_kernel_buf;
	p_device_ioctl_info->kernel_ioctl_info.buf_size =
	    p_device_ioctl_info->user_ioctl_info.buf_size;
	p_device_ioctl_info->kernel_ioctl_info.num_bytes_ret = 0;

	/*
	 * Create an "internal" completion event in case this ioctl returns
	 * with CL_PENDING
	 */
	cl_event_init(&p_device_ioctl_info->completion_event, FALSE);

	p_device_open_ctx = p_file->private_data;

	/* Call the consumer's Ioctl handler. */
	(*g_device_info_table[minor_val].dev_info.
	 pfn_ioctl) (g_device_info_table[minor_val].dev_info.p_device_context,
		     p_device_open_ctx->private_data,
		     &(p_device_ioctl_info->kernel_ioctl_info));

	status = p_device_ioctl_info->kernel_ioctl_info.io_status;
	if (status == CL_SUCCESS) {
		/*
		 * If data is available then copy back the buffer into User space
		 */
		if (p_device_ioctl_info->kernel_ioctl_info.p_buf != NULL) {
			if ((p_device_ioctl_info->kernel_ioctl_info.
			     num_bytes_ret > 0)
			    && (p_device_ioctl_info->kernel_ioctl_info.
				num_bytes_ret <=
				p_device_ioctl_info->user_ioctl_info.
				buf_size)) {
				if (copy_to_user
				    (p_device_ioctl_info->user_ioctl_info.p_buf,
				     p_device_ioctl_info->kernel_ioctl_info.
				     p_buf,
				     p_device_ioctl_info->kernel_ioctl_info.
				     num_bytes_ret)) {
					retval = -EFAULT;
					goto ioctl_fail;
				}
			}
		}

		/*
		 * Set io_status and num_bytes_ret in user_ioctl_info and copy it
		 * back into user space
		 */
		p_device_ioctl_info->user_ioctl_info.io_status =
		    p_device_ioctl_info->kernel_ioctl_info.io_status;
		p_device_ioctl_info->user_ioctl_info.num_bytes_ret =
		    p_device_ioctl_info->kernel_ioctl_info.num_bytes_ret;

		if (copy_to_user((void *) p_arg,
				 &(p_device_ioctl_info->user_ioctl_info),
				 sizeof (cl_ioctl_info_t))) {
			retval = -EFAULT;
			goto ioctl_fail;
		}
	} else if (status == CL_PENDING) {
		/*
		 * Block the user thread and wait for the kernel handler
		 * to trigger the internal event.
		 */

		/* interruptible wait ... */
		status =
		    cl_event_wait_on(&(p_device_ioctl_info->completion_event),
				     EVENT_NO_TIMEOUT, TRUE);
		if (CL_NOT_DONE == status) {
			/*
			 * Ioctl was interrupted due to a signal
			 * Tell the consumer to cancel the ioctl
			 */
			if (NULL !=
			    *g_device_info_table[minor_val].dev_info.
			    pfn_cancel_ioctl) {
				(*g_device_info_table[minor_val].dev_info.
				 pfn_cancel_ioctl) (g_device_info_table
						    [minor_val].dev_info.
						    p_device_context,
						    p_device_open_ctx->
						    private_data,
						    &(p_device_ioctl_info->
						      kernel_ioctl_info));
			}
		}

		/*
		 * IO completion was received.
		 * Copy back buffer into user space
		 */
		if (p_device_ioctl_info->kernel_ioctl_info.p_buf != NULL) {
			if ((p_device_ioctl_info->kernel_ioctl_info.
			     num_bytes_ret > 0)
			    && (p_device_ioctl_info->kernel_ioctl_info.
				num_bytes_ret <=
				p_device_ioctl_info->user_ioctl_info.
				buf_size)) {
				if (copy_to_user
				    (p_device_ioctl_info->user_ioctl_info.p_buf,
				     p_device_ioctl_info->kernel_ioctl_info.
				     p_buf,
				     p_device_ioctl_info->kernel_ioctl_info.
				     num_bytes_ret)) {
					retval = -EFAULT;
					goto ioctl_fail;
				}
			}
		}

		/*
		 * Set io_status in user_ioctl_info and copy it back into
		 * user space
		 */
		p_device_ioctl_info->user_ioctl_info.io_status =
		    p_device_ioctl_info->kernel_ioctl_info.io_status;
		p_device_ioctl_info->user_ioctl_info.num_bytes_ret =
		    p_device_ioctl_info->kernel_ioctl_info.num_bytes_ret;
	}

	/*
	 * Copy back user_ioctl_info with status and num_bytes_ret into
	 * user space
	 */
	if (copy_to_user
	    ((void *) p_arg, &(p_device_ioctl_info->user_ioctl_info),
	     sizeof (cl_ioctl_info_t))) {
		retval = -EFAULT;
		goto ioctl_fail;
	}

	retval = 0;

      ioctl_fail:

	cl_event_destroy(&(p_device_ioctl_info->completion_event));

	if (p_device_ioctl_info) {
		cl_free(p_device_ioctl_info);
	}

	if (p_kernel_buf) {
		cl_free(p_kernel_buf);
	}

	return retval;
}

int
_mmap_device(IN struct file *p_file, IN struct vm_area_struct *p_vma)
{

	int minor_val = 0;
	int retval = -EINVAL;
	struct inode *p_inode;

	device_open_context_t *p_device_open_ctx = NULL;
	cl_status_t status = CL_ERROR;

	p_inode = p_file->f_dentry->d_inode;

	minor_val = minor(p_inode->i_rdev);

	if (g_device_info_table[minor_val].dev_info.pfn_mmap == NULL) {
		cl_msg_out("_mmap_device: No mmap Handler registered for %s\n",
			   g_device_info_table[minor_val].dev_info.name);

		retval = -EINVAL;
		goto mmap_fail;
	}

	p_device_open_ctx = p_file->private_data;

	status =
	    (*g_device_info_table[minor_val].dev_info.
	     pfn_mmap) (g_device_info_table[minor_val].dev_info.
			p_device_context, p_device_open_ctx->private_data,
			p_vma);

	if (status != CL_SUCCESS) {
		retval = -EFAULT;
		goto mmap_fail;
	}

	retval = 0;

      mmap_fail:

	return retval;
}

/*
* This function is called by the comsumer of this framework.
* only manages completion notification to the user mode program
*/
cl_status_t
cl_complete_io(IN cl_ioctl_info_t * p_kernel_ioctl_info)
{
	device_ioctl_info_t *p_device_ioctl_info;

	/*
	 * If User mode has provided with a Wait Object
	 * then trigger it else trigger the internal Event.
	 */

	/*
	 * Get the parent of cl_ioctl_info_t which should
	 * of type device_ioctl_info_t
	 */
	p_device_ioctl_info =
	    (device_ioctl_info_t *) PARENT_STRUCT(p_kernel_ioctl_info,
						  device_ioctl_info_t,
						  kernel_ioctl_info);

	if (p_device_ioctl_info == NULL) {
		return CL_INVALID_PARAMETER;
	}

	cl_event_signal(&(p_device_ioctl_info->completion_event));

	return CL_SUCCESS;

}

/*
* Initialize the device framework for use
*/
cl_status_t
__cl_dev_frmwk_init(void)
{
	cl_spinlock_construct(&g_sysdev_lock);

	g_device_major = register_chrdev(0, SYS_DRIVER_NAME, &g_device_fops);

	if (g_device_major < 0) {
		cl_msg_out("system_dev_init: Failed to register_chrdev \n");
		return CL_INSUFFICIENT_RESOURCES;
	}

	cl_spinlock_init(&g_sysdev_lock);

	/*
	 * Initialize the "Global Device Info Table" for this device.
	 * Only clear out the name field for each device
	 */

	cl_memclr(g_device_info_table, sizeof (g_device_info_table));

	return (CL_SUCCESS);
}

void
__cl_dev_frmwk_destroy(void)
{
	cl_msg_out("system_dev_exit: char-dev-major 0x%x\n", g_device_major);

	cl_spinlock_destroy(&g_sysdev_lock);

	if ((g_device_major != 0) && (g_device_major > 0)) {
		cl_msg_out
		    ("system_dev_exit: unregistering device [major-%d] %s\n",
		     g_device_major, SYS_DRIVER_NAME);

		unregister_chrdev(g_device_major, SYS_DRIVER_NAME);
	}
}

cl_status_t
cl_get_open_context(IN int fd,
		    OUT void **pp_open_context,
		    OUT cl_dev_info_t ** pp_dev_info)
{
	struct file *p_file = NULL;
	device_open_context_t *p_device_open_ctx = NULL;
	int minor_val = 0;
	int major_val = 0;
	struct inode *p_inode;

	*pp_open_context = NULL;
	*pp_dev_info = NULL;

	if (!(p_file = fget(fd))) {
		cl_msg_out("__cl_get_open_context: Invalid fd (%d)\n", fd);
		return CL_INVALID_PARAMETER;
	}

	/* Now find the minor number for this fd */
	if (!p_file->f_dentry || !p_file->f_dentry->d_inode) {
		/* No way to get the minor number and device info */
		cl_dbg_out("Could not find the minor number of this device \n");
		fput(p_file);
		return CL_INVALID_PARAMETER;
	}

	p_inode = p_file->f_dentry->d_inode;
	minor_val = minor(p_inode->i_rdev);
	major_val = major(p_inode->i_rdev);

	if (major_val != g_device_major) {
		cl_dbg_out("Major number mismatch, fd not owned by complib\n");
		fput(p_file);
		return CL_INVALID_PARAMETER;
	}

	/* Now this seems like a complib FD, set the open_context */
	p_device_open_ctx = p_file->private_data;
	*pp_open_context = p_device_open_ctx->private_data;

	/* Now get the dev_info from the table based on the minor number */
	*pp_dev_info = &g_device_info_table[minor_val].dev_info;

	/* Done with validation. Put the file back */
	fput(p_file);

	return CL_SUCCESS;
}
