/*
 * dspapi.c
 *
 * DSP-BIOS Bridge driver support functions for TI OMAP processors.
 *
 * Common DSP API functions, also includes the wrapper
 * functions called directly by the DeviceIOControl interface.
 *
 * Copyright (C) 2005-2006 Texas Instruments, Inc.
 *
 * This package is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */
#include <linux/types.h>

/*  ----------------------------------- Host OS */
#include <dspbridge/host_os.h>

/*  ----------------------------------- DSP/BIOS Bridge */
#include <dspbridge/dbdefs.h>

/*  ----------------------------------- Trace & Debug */
#include <dspbridge/dbc.h>

/*  ----------------------------------- OS Adaptation Layer */
#include <dspbridge/ntfy.h>

/*  ----------------------------------- Platform Manager */
#include <dspbridge/chnl.h>
#include <dspbridge/dev.h>
#include <dspbridge/drv.h>

#include <dspbridge/proc.h>
#include <dspbridge/strm.h>

/*  ----------------------------------- Resource Manager */
#include <dspbridge/disp.h>
#include <dspbridge/mgr.h>
#include <dspbridge/node.h>
#include <dspbridge/rmm.h>

/*  ----------------------------------- Others */
#include <dspbridge/msg.h>
#include <dspbridge/cmm.h>
#include <dspbridge/io.h>

/*  ----------------------------------- This */
#include <dspbridge/dspapi.h>
#include <dspbridge/dbdcd.h>

#include <dspbridge/resourcecleanup.h>

/*  ----------------------------------- Defines, Data Structures, Typedefs */
#define MAX_TRACEBUFLEN 255
#define MAX_LOADARGS    16
#define MAX_NODES       64
#define MAX_STREAMS     16
#define MAX_BUFS	64

/* Used to get dspbridge ioctl table */
#define DB_GET_IOC_TABLE(cmd)	(DB_GET_MODULE(cmd) >> DB_MODULE_SHIFT)

/* Device IOCtl function pointer */
struct api_cmd {
	u32(*fxn) (union trapped_args *args, void *pr_ctxt);
	u32 index;
};

/*  ----------------------------------- Globals */
static u32 api_c_refs;

/*
 *  Function tables.
 *  The order of these functions MUST be the same as the order of the command
 *  numbers defined in dspapi-ioctl.h  This is how an IOCTL number in user mode
 *  turns into a function call in kernel mode.
 */

/* MGR wrapper functions */
static struct api_cmd mgr_cmd[] = {
	{mgrwrap_enum_node_info},	/* MGR_ENUMNODE_INFO */
	{mgrwrap_enum_proc_info},	/* MGR_ENUMPROC_INFO */
	{mgrwrap_register_object},	/* MGR_REGISTEROBJECT */
	{mgrwrap_unregister_object},	/* MGR_UNREGISTEROBJECT */
	{mgrwrap_wait_for_bridge_events},	/* MGR_WAIT */
	{mgrwrap_get_process_resources_info},	/* MGR_GET_PROC_RES */
};

/* PROC wrapper functions */
static struct api_cmd proc_cmd[] = {
	{procwrap_attach},	/* PROC_ATTACH */
	{procwrap_ctrl},	/* PROC_CTRL */
	{procwrap_detach},	/* PROC_DETACH */
	{procwrap_enum_node_info},	/* PROC_ENUMNODE */
	{procwrap_enum_resources},	/* PROC_ENUMRESOURCES */
	{procwrap_get_state},	/* PROC_GET_STATE */
	{procwrap_get_trace},	/* PROC_GET_TRACE */
	{procwrap_load},	/* PROC_LOAD */
	{procwrap_register_notify},	/* PROC_REGISTERNOTIFY */
	{procwrap_start},	/* PROC_START */
	{procwrap_reserve_memory},	/* PROC_RSVMEM */
	{procwrap_un_reserve_memory},	/* PROC_UNRSVMEM */
	{procwrap_map},		/* PROC_MAPMEM */
	{procwrap_un_map},	/* PROC_UNMAPMEM */
	{procwrap_flush_memory},	/* PROC_FLUSHMEMORY */
	{procwrap_stop},	/* PROC_STOP */
	{procwrap_invalidate_memory},	/* PROC_INVALIDATEMEMORY */
	{procwrap_begin_dma},	/* PROC_BEGINDMA */
	{procwrap_end_dma},	/* PROC_ENDDMA */
};

/* NODE wrapper functions */
static struct api_cmd node_cmd[] = {
	{nodewrap_allocate},	/* NODE_ALLOCATE */
	{nodewrap_alloc_msg_buf},	/* NODE_ALLOCMSGBUF */
	{nodewrap_change_priority},	/* NODE_CHANGEPRIORITY */
	{nodewrap_connect},	/* NODE_CONNECT */
	{nodewrap_create},	/* NODE_CREATE */
	{nodewrap_delete},	/* NODE_DELETE */
	{nodewrap_free_msg_buf},	/* NODE_FREEMSGBUF */
	{nodewrap_get_attr},	/* NODE_GETATTR */
	{nodewrap_get_message},	/* NODE_GETMESSAGE */
	{nodewrap_pause},	/* NODE_PAUSE */
	{nodewrap_put_message},	/* NODE_PUTMESSAGE */
	{nodewrap_register_notify},	/* NODE_REGISTERNOTIFY */
	{nodewrap_run},		/* NODE_RUN */
	{nodewrap_terminate},	/* NODE_TERMINATE */
	{nodewrap_get_uuid_props},	/* NODE_GETUUIDPROPS */
};

/* STRM wrapper functions */
static struct api_cmd strm_cmd[] = {
	{strmwrap_allocate_buffer},	/* STRM_ALLOCATEBUFFER */
	{strmwrap_close},	/* STRM_CLOSE */
	{strmwrap_free_buffer},	/* STRM_FREEBUFFER */
	{strmwrap_get_event_handle},	/* STRM_GETEVENTHANDLE */
	{strmwrap_get_info},	/* STRM_GETINFO */
	{strmwrap_idle},	/* STRM_IDLE */
	{strmwrap_issue},	/* STRM_ISSUE */
	{strmwrap_open},	/* STRM_OPEN */
	{strmwrap_reclaim},	/* STRM_RECLAIM */
	{strmwrap_register_notify},	/* STRM_REGISTERNOTIFY */
	{strmwrap_select},	/* STRM_SELECT */
};

/* CMM wrapper functions */
static struct api_cmd cmm_cmd[] = {
	{cmmwrap_calloc_buf},	/* CMM_ALLOCBUF */
	{cmmwrap_free_buf},	/* CMM_FREEBUF */
	{cmmwrap_get_handle},	/* CMM_GETHANDLE */
	{cmmwrap_get_info},	/* CMM_GETINFO */
};

/* Array used to store ioctl table sizes. It can hold up to 8 entries */
static u8 size_cmd[] = {
	ARRAY_SIZE(mgr_cmd),
	ARRAY_SIZE(proc_cmd),
	ARRAY_SIZE(node_cmd),
	ARRAY_SIZE(strm_cmd),
	ARRAY_SIZE(cmm_cmd),
};

static inline void _cp_fm_usr(void *to, const void __user * from,
			      int *err, unsigned long bytes)
{
	if (*err)
		return;

	if (unlikely(!from)) {
		*err = -EFAULT;
		return;
	}

	if (unlikely(copy_from_user(to, from, bytes)))
		*err = -EFAULT;
}

#define CP_FM_USR(to, from, err, n)				\
	_cp_fm_usr(to, from, &(err), (n) * sizeof(*(to)))

static inline void _cp_to_usr(void __user *to, const void *from,
			      int *err, unsigned long bytes)
{
	if (*err)
		return;

	if (unlikely(!to)) {
		*err = -EFAULT;
		return;
	}

	if (unlikely(copy_to_user(to, from, bytes)))
		*err = -EFAULT;
}

#define CP_TO_USR(to, from, err, n)				\
	_cp_to_usr(to, from, &(err), (n) * sizeof(*(from)))

/*
 *  ======== api_call_dev_ioctl ========
 *  Purpose:
 *      Call the (wrapper) function for the corresponding API IOCTL.
 */
inline int api_call_dev_ioctl(u32 cmd, union trapped_args *args,
				      u32 *result, void *pr_ctxt)
{
	u32(*ioctl_cmd) (union trapped_args *args, void *pr_ctxt) = NULL;
	int i;

	if (_IOC_TYPE(cmd) != DB) {
		pr_err("%s: Incompatible dspbridge ioctl number\n", __func__);
		goto err;
	}

	if (DB_GET_IOC_TABLE(cmd) > ARRAY_SIZE(size_cmd)) {
		pr_err("%s: undefined ioctl module\n", __func__);
		goto err;
	}

	/* Check the size of the required cmd table */
	i = DB_GET_IOC(cmd);
	if (i > size_cmd[DB_GET_IOC_TABLE(cmd)]) {
		pr_err("%s: requested ioctl %d out of bounds for table %d\n",
		       __func__, i, DB_GET_IOC_TABLE(cmd));
		goto err;
	}

	switch (DB_GET_MODULE(cmd)) {
	case DB_MGR:
		ioctl_cmd = mgr_cmd[i].fxn;
		break;
	case DB_PROC:
		ioctl_cmd = proc_cmd[i].fxn;
		break;
	case DB_NODE:
		ioctl_cmd = node_cmd[i].fxn;
		break;
	case DB_STRM:
		ioctl_cmd = strm_cmd[i].fxn;
		break;
	case DB_CMM:
		ioctl_cmd = cmm_cmd[i].fxn;
		break;
	}

	if (!ioctl_cmd) {
		pr_err("%s: requested ioctl not defined\n", __func__);
		goto err;
	} else {
		*result = (*ioctl_cmd) (args, pr_ctxt);
	}

	return 0;

err:
	return -EINVAL;
}

/*
 *  ======== api_exit ========
 */
void api_exit(void)
{
	DBC_REQUIRE(api_c_refs > 0);
	api_c_refs--;

	if (api_c_refs == 0) {
		/* Release all modules initialized in api_init(). */
		cod_exit();
		dev_exit();
		chnl_exit();
		msg_exit();
		io_exit();
		strm_exit();
		disp_exit();
		node_exit();
		proc_exit();
		mgr_exit();
		rmm_exit();
		drv_exit();
	}
	DBC_ENSURE(api_c_refs >= 0);
}

/*
 *  ======== api_init ========
 *  Purpose:
 *      Module initialization used by Bridge API.
 */
bool api_init(void)
{
	bool ret = true;
	bool fdrv, fdev, fcod, fchnl, fmsg, fio;
	bool fmgr, fproc, fnode, fdisp, fstrm, frmm;

	if (api_c_refs == 0) {
		/* initialize driver and other modules */
		fdrv = drv_init();
		fmgr = mgr_init();
		fproc = proc_init();
		fnode = node_init();
		fdisp = disp_init();
		fstrm = strm_init();
		frmm = rmm_init();
		fchnl = chnl_init();
		fmsg = msg_mod_init();
		fio = io_init();
		fdev = dev_init();
		fcod = cod_init();
		ret = fdrv && fdev && fchnl && fcod && fmsg && fio;
		ret = ret && fmgr && fproc && frmm;
		if (!ret) {
			if (fdrv)
				drv_exit();

			if (fmgr)
				mgr_exit();

			if (fstrm)
				strm_exit();

			if (fproc)
				proc_exit();

			if (fnode)
				node_exit();

			if (fdisp)
				disp_exit();

			if (fchnl)
				chnl_exit();

			if (fmsg)
				msg_exit();

			if (fio)
				io_exit();

			if (fdev)
				dev_exit();

			if (fcod)
				cod_exit();

			if (frmm)
				rmm_exit();

		}
	}
	if (ret)
		api_c_refs++;

	return ret;
}

/*
 *  ======== api_init_complete2 ========
 *  Purpose:
 *      Perform any required bridge initialization which cannot
 *      be performed in api_init() or dev_start_device() due
 *      to the fact that some services are not yet
 *      completely initialized.
 *  Parameters:
 *  Returns:
 *      0:	Allow this device to load
 *      -EPERM:      Failure.
 *  Requires:
 *      Bridge API initialized.
 *  Ensures:
 */
int api_init_complete2(void)
{
	int status = 0;
	struct cfg_devnode *dev_node;
	struct dev_object *hdev_obj;
	struct drv_data *drv_datap;
	u8 dev_type;

	DBC_REQUIRE(api_c_refs > 0);

	/*  Walk the list of DevObjects, get each devnode, and attempting to
	 *  autostart the board. Note that this requires COF loading, which
	 *  requires KFILE. */
	for (hdev_obj = dev_get_first(); hdev_obj != NULL;
	     hdev_obj = dev_get_next(hdev_obj)) {
		if (dev_get_dev_node(hdev_obj, &dev_node))
			continue;

		if (dev_get_dev_type(hdev_obj, &dev_type))
			continue;

		if ((dev_type == DSP_UNIT) || (dev_type == IVA_UNIT)) {
			drv_datap = dev_get_drvdata(bridge);

			if (drv_datap && drv_datap->base_img)
				proc_auto_start(dev_node, hdev_obj);
		}
	}

	return status;
}

/* TODO: Remove deprecated and not implemented ioctl wrappers */

/*
 * ======== mgrwrap_enum_node_info ========
 */
u32 mgrwrap_enum_node_info(union trapped_args *args, void *pr_ctxt)
{
	u8 *pndb_props;
	u32 num_nodes;
	int status = 0;
	u32 size = args->args_mgr_enumnode_info.ndb_props_size;

	if (size < sizeof(struct dsp_ndbprops))
		return -EINVAL;

	pndb_props = kmalloc(size, GFP_KERNEL);
	if (pndb_props == NULL)
		status = -ENOMEM;

	if (!status) {
		status =
		    mgr_enum_node_info(args->args_mgr_enumnode_info.node_id,
				       (struct dsp_ndbprops *)pndb_props, size,
				       &num_nodes);
	}
	CP_TO_USR(args->args_mgr_enumnode_info.ndb_props, pndb_props, status,
		  size);
	CP_TO_USR(args->args_mgr_enumnode_info.num_nodes, &num_nodes, status,
		  1);
	kfree(pndb_props);

	return status;
}

/*
 * ======== mgrwrap_enum_proc_info ========
 */
u32 mgrwrap_enum_proc_info(union trapped_args *args, void *pr_ctxt)
{
	u8 *processor_info;
	u8 num_procs;
	int status = 0;
	u32 size = args->args_mgr_enumproc_info.processor_info_size;

	if (size < sizeof(struct dsp_processorinfo))
		return -EINVAL;

	processor_info = kmalloc(size, GFP_KERNEL);
	if (processor_info == NULL)
		status = -ENOMEM;

	if (!status) {
		status =
		    mgr_enum_processor_info(args->args_mgr_enumproc_info.
					    processor_id,
					    (struct dsp_processorinfo *)
					    processor_info, size, &num_procs);
	}
	CP_TO_USR(args->args_mgr_enumproc_info.processor_info, processor_info,
		  status, size);
	CP_TO_USR(args->args_mgr_enumproc_info.num_procs, &num_procs,
		  status, 1);
	kfree(processor_info);

	return status;
}

#define WRAP_MAP2CALLER(x) x
/*
 * ======== mgrwrap_register_object ========
 */
u32 mgrwrap_register_object(union trapped_args *args, void *pr_ctxt)
{
	u32 ret;
	struct dsp_uuid uuid_obj;
	u32 path_size = 0;
	char *psz_path_name = NULL;
	int status = 0;

	CP_FM_USR(&uuid_obj, args->args_mgr_registerobject.uuid_obj, status, 1);
	if (status)
		goto func_end;
	/* path_size is increased by 1 to accommodate NULL */
	path_size = strlen_user((char *)
				args->args_mgr_registerobject.sz_path_name) +
	    1;
	psz_path_name = kmalloc(path_size, GFP_KERNEL);
	if (!psz_path_name) {
		status = -ENOMEM;
		goto func_end;
	}
	ret = strncpy_from_user(psz_path_name,
				(char *)args->args_mgr_registerobject.
				sz_path_name, path_size);
	if (!ret) {
		status = -EFAULT;
		goto func_end;
	}

	if (args->args_mgr_registerobject.obj_type >= DSP_DCDMAXOBJTYPE) {
		status = -EINVAL;
		goto func_end;
	}

	status = dcd_register_object(&uuid_obj,
				     args->args_mgr_registerobject.obj_type,
				     (char *)psz_path_name);
func_end:
	kfree(psz_path_name);
	return status;
}

/*
 * ======== mgrwrap_unregister_object ========
 */
u32 mgrwrap_unregister_object(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	struct dsp_uuid uuid_obj;

	CP_FM_USR(&uuid_obj, args->args_mgr_registerobject.uuid_obj, status, 1);
	if (status)
		goto func_end;

	status = dcd_unregister_object(&uuid_obj,
				       args->args_mgr_unregisterobject.
				       obj_type);
func_end:
	return status;

}

/*
 * ======== mgrwrap_wait_for_bridge_events ========
 */
u32 mgrwrap_wait_for_bridge_events(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	struct dsp_notification *anotifications[MAX_EVENTS];
	struct dsp_notification notifications[MAX_EVENTS];
	u32 index, i;
	u32 count = args->args_mgr_wait.count;

	if (count > MAX_EVENTS)
		status = -EINVAL;

	/* get the array of pointers to user structures */
	CP_FM_USR(anotifications, args->args_mgr_wait.anotifications,
		  status, count);
	/* get the events */
	for (i = 0; i < count; i++) {
		CP_FM_USR(&notifications[i], anotifications[i], status, 1);
		if (status || !notifications[i].handle) {
			status = -EINVAL;
			break;
		}
		/* set the array of pointers to kernel structures */
		anotifications[i] = &notifications[i];
	}
	if (!status) {
		status = mgr_wait_for_bridge_events(anotifications, count,
							 &index,
							 args->args_mgr_wait.
							 timeout);
	}
	CP_TO_USR(args->args_mgr_wait.index, &index, status, 1);
	return status;
}

/*
 * ======== MGRWRAP_GetProcessResourceInfo ========
 */
u32 __deprecated mgrwrap_get_process_resources_info(union trapped_args * args,
						    void *pr_ctxt)
{
	pr_err("%s: deprecated dspbridge ioctl\n", __func__);
	return 0;
}

/*
 * ======== procwrap_attach ========
 */
u32 procwrap_attach(union trapped_args *args, void *pr_ctxt)
{
	void *processor;
	int status = 0;
	struct dsp_processorattrin proc_attr_in, *attr_in = NULL;

	/* Optional argument */
	if (args->args_proc_attach.attr_in) {
		CP_FM_USR(&proc_attr_in, args->args_proc_attach.attr_in, status,
			  1);
		if (!status)
			attr_in = &proc_attr_in;
		else
			goto func_end;

	}
	status = proc_attach(args->args_proc_attach.processor_id, attr_in,
			     &processor, pr_ctxt);
	CP_TO_USR(args->args_proc_attach.ph_processor, &processor, status, 1);
func_end:
	return status;
}

/*
 * ======== procwrap_ctrl ========
 */
u32 procwrap_ctrl(union trapped_args *args, void *pr_ctxt)
{
	u32 cb_data_size, __user * psize = (u32 __user *)
	    args->args_proc_ctrl.args;
	u8 *pargs = NULL;
	int status = 0;
	void *hprocessor = ((struct process_context *)pr_ctxt)->processor;

	if (psize) {
		if (get_user(cb_data_size, psize)) {
			status = -EPERM;
			goto func_end;
		}
		cb_data_size += sizeof(u32);
		pargs = kmalloc(cb_data_size, GFP_KERNEL);
		if (pargs == NULL) {
			status = -ENOMEM;
			goto func_end;
		}

		CP_FM_USR(pargs, args->args_proc_ctrl.args, status,
			  cb_data_size);
	}
	if (!status) {
		status = proc_ctrl(hprocessor,
				   args->args_proc_ctrl.cmd,
				   (struct dsp_cbdata *)pargs);
	}

	/* CP_TO_USR(args->args_proc_ctrl.args, pargs, status, 1); */
	kfree(pargs);
func_end:
	return status;
}

/*
 * ======== procwrap_detach ========
 */
u32 __deprecated procwrap_detach(union trapped_args * args, void *pr_ctxt)
{
	/* proc_detach called at bridge_release only */
	pr_err("%s: deprecated dspbridge ioctl\n", __func__);
	return 0;
}

/*
 * ======== procwrap_enum_node_info ========
 */
u32 procwrap_enum_node_info(union trapped_args *args, void *pr_ctxt)
{
	int status;
	void *node_tab[MAX_NODES];
	u32 num_nodes;
	u32 alloc_cnt;
	void *hprocessor = ((struct process_context *)pr_ctxt)->processor;

	if (!args->args_proc_enumnode_info.node_tab_size)
		return -EINVAL;

	status = proc_enum_nodes(hprocessor,
				 node_tab,
				 args->args_proc_enumnode_info.node_tab_size,
				 &num_nodes, &alloc_cnt);
	CP_TO_USR(args->args_proc_enumnode_info.node_tab, node_tab, status,
		  num_nodes);
	CP_TO_USR(args->args_proc_enumnode_info.num_nodes, &num_nodes,
		  status, 1);
	CP_TO_USR(args->args_proc_enumnode_info.allocated, &alloc_cnt,
		  status, 1);
	return status;
}

u32 procwrap_end_dma(union trapped_args *args, void *pr_ctxt)
{
	int status;

	if (args->args_proc_dma.dir >= DMA_NONE)
		return -EINVAL;

	status = proc_end_dma(pr_ctxt,
				   args->args_proc_dma.mpu_addr,
				   args->args_proc_dma.size,
				   args->args_proc_dma.dir);
	return status;
}

u32 procwrap_begin_dma(union trapped_args *args, void *pr_ctxt)
{
	int status;

	if (args->args_proc_dma.dir >= DMA_NONE)
		return -EINVAL;

	status = proc_begin_dma(pr_ctxt,
				   args->args_proc_dma.mpu_addr,
				   args->args_proc_dma.size,
				   args->args_proc_dma.dir);
	return status;
}

/*
 * ======== procwrap_flush_memory ========
 */
u32 procwrap_flush_memory(union trapped_args *args, void *pr_ctxt)
{
	int status;

	if (args->args_proc_flushmemory.flags >
	    PROC_WRITEBACK_INVALIDATE_MEM)
		return -EINVAL;

	status = proc_flush_memory(pr_ctxt,
				   args->args_proc_flushmemory.mpu_addr,
				   args->args_proc_flushmemory.size,
				   args->args_proc_flushmemory.flags);
	return status;
}

/*
 * ======== procwrap_invalidate_memory ========
 */
u32 procwrap_invalidate_memory(union trapped_args *args, void *pr_ctxt)
{
	int status;

	status =
	    proc_invalidate_memory(pr_ctxt,
				   args->args_proc_invalidatememory.mpu_addr,
				   args->args_proc_invalidatememory.size);
	return status;
}

/*
 * ======== procwrap_enum_resources ========
 */
u32 procwrap_enum_resources(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	struct dsp_resourceinfo resource_info;
	void *hprocessor = ((struct process_context *)pr_ctxt)->processor;

	if (args->args_proc_enumresources.resource_info_size <
	    sizeof(struct dsp_resourceinfo))
		return -EINVAL;

	status =
	    proc_get_resource_info(hprocessor,
				   args->args_proc_enumresources.resource_type,
				   &resource_info,
				   args->args_proc_enumresources.
				   resource_info_size);

	CP_TO_USR(args->args_proc_enumresources.resource_info, &resource_info,
		  status, 1);

	return status;

}

/*
 * ======== procwrap_get_state ========
 */
u32 procwrap_get_state(union trapped_args *args, void *pr_ctxt)
{
	int status;
	struct dsp_processorstate proc_state;
	void *hprocessor = ((struct process_context *)pr_ctxt)->processor;

	if (args->args_proc_getstate.state_info_size <
	    sizeof(struct dsp_processorstate))
		return -EINVAL;

	status = proc_get_state(hprocessor, &proc_state,
			   args->args_proc_getstate.state_info_size);
	CP_TO_USR(args->args_proc_getstate.proc_state_obj, &proc_state, status,
		  1);
	return status;

}

/*
 * ======== procwrap_get_trace ========
 */
u32 procwrap_get_trace(union trapped_args *args, void *pr_ctxt)
{
	int status;
	u8 *pbuf;
	void *hprocessor = ((struct process_context *)pr_ctxt)->processor;

	if (args->args_proc_gettrace.max_size > MAX_TRACEBUFLEN)
		return -EINVAL;

	pbuf = kzalloc(args->args_proc_gettrace.max_size, GFP_KERNEL);
	if (pbuf != NULL) {
		status = proc_get_trace(hprocessor, pbuf,
					args->args_proc_gettrace.max_size);
	} else {
		status = -ENOMEM;
	}
	CP_TO_USR(args->args_proc_gettrace.buf, pbuf, status,
		  args->args_proc_gettrace.max_size);
	kfree(pbuf);

	return status;
}

/*
 * ======== procwrap_load ========
 */
u32 procwrap_load(union trapped_args *args, void *pr_ctxt)
{
	s32 i, len;
	int status = 0;
	char *temp;
	s32 count = args->args_proc_load.argc_index;
	u8 **argv = NULL, **envp = NULL;
	void *hprocessor = ((struct process_context *)pr_ctxt)->processor;

	if (count <= 0 || count > MAX_LOADARGS) {
		status = -EINVAL;
		goto func_cont;
	}

	argv = kmalloc(count * sizeof(u8 *), GFP_KERNEL);
	if (!argv) {
		status = -ENOMEM;
		goto func_cont;
	}

	CP_FM_USR(argv, args->args_proc_load.user_args, status, count);
	if (status) {
		kfree(argv);
		argv = NULL;
		goto func_cont;
	}

	for (i = 0; i < count; i++) {
		if (argv[i]) {
			/* User space pointer to argument */
			temp = (char *)argv[i];
			/* len is increased by 1 to accommodate NULL */
			len = strlen_user((char *)temp) + 1;
			/* Kernel space pointer to argument */
			argv[i] = kmalloc(len, GFP_KERNEL);
			if (argv[i]) {
				CP_FM_USR(argv[i], temp, status, len);
				if (status) {
					kfree(argv[i]);
					argv[i] = NULL;
					goto func_cont;
				}
			} else {
				status = -ENOMEM;
				goto func_cont;
			}
		}
	}
	/* TODO: validate this */
	if (args->args_proc_load.user_envp) {
		/* number of elements in the envp array including NULL */
		count = 0;
		do {
			if (get_user(temp,
				     args->args_proc_load.user_envp + count)) {
				status = -EFAULT;
				goto func_cont;
			}
			count++;
		} while (temp);
		envp = kmalloc(count * sizeof(u8 *), GFP_KERNEL);
		if (!envp) {
			status = -ENOMEM;
			goto func_cont;
		}

		CP_FM_USR(envp, args->args_proc_load.user_envp, status, count);
		if (status) {
			kfree(envp);
			envp = NULL;
			goto func_cont;
		}
		for (i = 0; envp[i]; i++) {
			/* User space pointer to argument */
			temp = (char *)envp[i];
			/* len is increased by 1 to accommodate NULL */
			len = strlen_user((char *)temp) + 1;
			/* Kernel space pointer to argument */
			envp[i] = kmalloc(len, GFP_KERNEL);
			if (envp[i]) {
				CP_FM_USR(envp[i], temp, status, len);
				if (status) {
					kfree(envp[i]);
					envp[i] = NULL;
					goto func_cont;
				}
			} else {
				status = -ENOMEM;
				goto func_cont;
			}
		}
	}

	if (!status) {
		status = proc_load(hprocessor,
				   args->args_proc_load.argc_index,
				   (const char **)argv, (const char **)envp);
	}
func_cont:
	if (envp) {
		i = 0;
		while (envp[i])
			kfree(envp[i++]);

		kfree(envp);
	}

	if (argv) {
		count = args->args_proc_load.argc_index;
		for (i = 0; (i < count) && argv[i]; i++)
			kfree(argv[i]);

		kfree(argv);
	}

	return status;
}

/*
 * ======== procwrap_map ========
 */
u32 procwrap_map(union trapped_args *args, void *pr_ctxt)
{
	int status;
	void *map_addr;
	void *hprocessor = ((struct process_context *)pr_ctxt)->processor;

	if (!args->args_proc_mapmem.size)
		return -EINVAL;

	status = proc_map(args->args_proc_mapmem.processor,
			  args->args_proc_mapmem.mpu_addr,
			  args->args_proc_mapmem.size,
			  args->args_proc_mapmem.req_addr, &map_addr,
			  args->args_proc_mapmem.map_attr, pr_ctxt);
	if (!status) {
		if (put_user(map_addr, args->args_proc_mapmem.map_addr)) {
			status = -EINVAL;
			proc_un_map(hprocessor, map_addr, pr_ctxt);
		}

	}
	return status;
}

/*
 * ======== procwrap_register_notify ========
 */
u32 procwrap_register_notify(union trapped_args *args, void *pr_ctxt)
{
	int status;
	struct dsp_notification notification;
	void *hprocessor = ((struct process_context *)pr_ctxt)->processor;

	/* Initialize the notification data structure */
	notification.name = NULL;
	notification.handle = NULL;

	status = proc_register_notify(hprocessor,
				 args->args_proc_register_notify.event_mask,
				 args->args_proc_register_notify.notify_type,
				 &notification);
	CP_TO_USR(args->args_proc_register_notify.notification, &notification,
		  status, 1);
	return status;
}

/*
 * ======== procwrap_reserve_memory ========
 */
u32 procwrap_reserve_memory(union trapped_args *args, void *pr_ctxt)
{
	int status;
	void *prsv_addr;
	void *hprocessor = ((struct process_context *)pr_ctxt)->processor;

	if ((args->args_proc_rsvmem.size <= 0) ||
	    (args->args_proc_rsvmem.size & (PG_SIZE4K - 1)) != 0)
		return -EINVAL;

	status = proc_reserve_memory(hprocessor,
				     args->args_proc_rsvmem.size, &prsv_addr,
				     pr_ctxt);
	if (!status) {
		if (put_user(prsv_addr, args->args_proc_rsvmem.rsv_addr)) {
			status = -EINVAL;
			proc_un_reserve_memory(args->args_proc_rsvmem.
					       processor, prsv_addr, pr_ctxt);
		}
	}
	return status;
}

/*
 * ======== procwrap_start ========
 */
u32 procwrap_start(union trapped_args *args, void *pr_ctxt)
{
	u32 ret;

	ret = proc_start(((struct process_context *)pr_ctxt)->processor);
	return ret;
}

/*
 * ======== procwrap_un_map ========
 */
u32 procwrap_un_map(union trapped_args *args, void *pr_ctxt)
{
	int status;

	status = proc_un_map(((struct process_context *)pr_ctxt)->processor,
			     args->args_proc_unmapmem.map_addr, pr_ctxt);
	return status;
}

/*
 * ======== procwrap_un_reserve_memory ========
 */
u32 procwrap_un_reserve_memory(union trapped_args *args, void *pr_ctxt)
{
	int status;
	void *hprocessor = ((struct process_context *)pr_ctxt)->processor;

	status = proc_un_reserve_memory(hprocessor,
					args->args_proc_unrsvmem.rsv_addr,
					pr_ctxt);
	return status;
}

/*
 * ======== procwrap_stop ========
 */
u32 procwrap_stop(union trapped_args *args, void *pr_ctxt)
{
	u32 ret;

	ret = proc_stop(((struct process_context *)pr_ctxt)->processor);

	return ret;
}

/*
 * ======== find_handle =========
 */
inline void find_node_handle(struct node_res_object **noderes,
				void *pr_ctxt, void *hnode)
{
	rcu_read_lock();
	*noderes = idr_find(((struct process_context *)pr_ctxt)->node_id,
								(int)hnode - 1);
	rcu_read_unlock();
	return;
}


/*
 * ======== nodewrap_allocate ========
 */
u32 nodewrap_allocate(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	struct dsp_uuid node_uuid;
	u32 cb_data_size = 0;
	u32 __user *psize = (u32 __user *) args->args_node_allocate.args;
	u8 *pargs = NULL;
	struct dsp_nodeattrin proc_attr_in, *attr_in = NULL;
	struct node_res_object *node_res;
	int nodeid;
	void *hprocessor = ((struct process_context *)pr_ctxt)->processor;

	/* Optional argument */
	if (psize) {
		if (get_user(cb_data_size, psize))
			status = -EPERM;

		cb_data_size += sizeof(u32);
		if (!status) {
			pargs = kmalloc(cb_data_size, GFP_KERNEL);
			if (pargs == NULL)
				status = -ENOMEM;

		}
		CP_FM_USR(pargs, args->args_node_allocate.args, status,
			  cb_data_size);
	}
	CP_FM_USR(&node_uuid, args->args_node_allocate.node_id_ptr, status, 1);
	if (status)
		goto func_cont;
	/* Optional argument */
	if (args->args_node_allocate.attr_in) {
		CP_FM_USR(&proc_attr_in, args->args_node_allocate.attr_in,
			  status, 1);
		if (!status)
			attr_in = &proc_attr_in;
		else
			status = -ENOMEM;

	}
	if (!status) {
		status = node_allocate(hprocessor,
				       &node_uuid, (struct dsp_cbdata *)pargs,
				       attr_in, &node_res, pr_ctxt);
	}
	if (!status) {
		nodeid = node_res->id + 1;
		CP_TO_USR(args->args_node_allocate.node, &nodeid,
			status, 1);
		if (status) {
			status = -EFAULT;
			node_delete(node_res, pr_ctxt);
		}
	}
func_cont:
	kfree(pargs);

	return status;
}

/*
 *  ======== nodewrap_alloc_msg_buf ========
 */
u32 nodewrap_alloc_msg_buf(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	struct dsp_bufferattr *pattr = NULL;
	struct dsp_bufferattr attr;
	u8 *pbuffer = NULL;
	struct node_res_object *node_res;

	find_node_handle(&node_res,  pr_ctxt,
				args->args_node_allocmsgbuf.node);

	if (!node_res)
		return -EFAULT;

	if (!args->args_node_allocmsgbuf.size)
		return -EINVAL;

	if (args->args_node_allocmsgbuf.attr) {	/* Optional argument */
		CP_FM_USR(&attr, args->args_node_allocmsgbuf.attr, status, 1);
		if (!status)
			pattr = &attr;

	}
	/* argument */
	CP_FM_USR(&pbuffer, args->args_node_allocmsgbuf.buffer, status, 1);
	if (!status) {
		status = node_alloc_msg_buf(node_res->node,
					    args->args_node_allocmsgbuf.size,
					    pattr, &pbuffer);
	}
	CP_TO_USR(args->args_node_allocmsgbuf.buffer, &pbuffer, status, 1);
	return status;
}

/*
 * ======== nodewrap_change_priority ========
 */
u32 nodewrap_change_priority(union trapped_args *args, void *pr_ctxt)
{
	u32 ret;
	struct node_res_object *node_res;

	find_node_handle(&node_res, pr_ctxt,
				args->args_node_changepriority.node);

	if (!node_res)
		return -EFAULT;

	ret = node_change_priority(node_res->node,
				   args->args_node_changepriority.prio);

	return ret;
}

/*
 * ======== nodewrap_connect ========
 */
u32 nodewrap_connect(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	struct dsp_strmattr attrs;
	struct dsp_strmattr *pattrs = NULL;
	u32 cb_data_size;
	u32 __user *psize = (u32 __user *) args->args_node_connect.conn_param;
	u8 *pargs = NULL;
	struct node_res_object *node_res1, *node_res2;
	struct node_object *node1 = NULL, *node2 = NULL;

	if ((int)args->args_node_connect.node != DSP_HGPPNODE) {
		find_node_handle(&node_res1, pr_ctxt,
				args->args_node_connect.node);
		if (node_res1)
			node1 = node_res1->node;
	} else {
		node1 = args->args_node_connect.node;
	}

	if ((int)args->args_node_connect.other_node != DSP_HGPPNODE) {
		find_node_handle(&node_res2, pr_ctxt,
				args->args_node_connect.other_node);
		if (node_res2)
			node2 = node_res2->node;
	} else {
		node2 = args->args_node_connect.other_node;
	}

	if (!node1 || !node2)
		return -EFAULT;

	/* Optional argument */
	if (psize) {
		if (get_user(cb_data_size, psize))
			status = -EPERM;

		cb_data_size += sizeof(u32);
		if (!status) {
			pargs = kmalloc(cb_data_size, GFP_KERNEL);
			if (pargs == NULL) {
				status = -ENOMEM;
				goto func_cont;
			}

		}
		CP_FM_USR(pargs, args->args_node_connect.conn_param, status,
			  cb_data_size);
		if (status)
			goto func_cont;
	}
	if (args->args_node_connect.attrs) {	/* Optional argument */
		CP_FM_USR(&attrs, args->args_node_connect.attrs, status, 1);
		if (!status)
			pattrs = &attrs;

	}
	if (!status) {
		status = node_connect(node1,
				      args->args_node_connect.stream_id,
				      node2,
				      args->args_node_connect.other_stream,
				      pattrs, (struct dsp_cbdata *)pargs);
	}
func_cont:
	kfree(pargs);

	return status;
}

/*
 * ======== nodewrap_create ========
 */
u32 nodewrap_create(union trapped_args *args, void *pr_ctxt)
{
	u32 ret;
	struct node_res_object *node_res;

	find_node_handle(&node_res, pr_ctxt, args->args_node_create.node);

	if (!node_res)
		return -EFAULT;

	ret = node_create(node_res->node);

	return ret;
}

/*
 * ======== nodewrap_delete ========
 */
u32 nodewrap_delete(union trapped_args *args, void *pr_ctxt)
{
	u32 ret;
	struct node_res_object *node_res;

	find_node_handle(&node_res, pr_ctxt, args->args_node_delete.node);

	if (!node_res)
		return -EFAULT;

	ret = node_delete(node_res, pr_ctxt);

	return ret;
}

/*
 *  ======== nodewrap_free_msg_buf ========
 */
u32 nodewrap_free_msg_buf(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	struct dsp_bufferattr *pattr = NULL;
	struct dsp_bufferattr attr;
	struct node_res_object *node_res;

	find_node_handle(&node_res, pr_ctxt, args->args_node_freemsgbuf.node);

	if (!node_res)
		return -EFAULT;

	if (args->args_node_freemsgbuf.attr) {	/* Optional argument */
		CP_FM_USR(&attr, args->args_node_freemsgbuf.attr, status, 1);
		if (!status)
			pattr = &attr;

	}

	if (!args->args_node_freemsgbuf.buffer)
		return -EFAULT;

	if (!status) {
		status = node_free_msg_buf(node_res->node,
					   args->args_node_freemsgbuf.buffer,
					   pattr);
	}

	return status;
}

/*
 * ======== nodewrap_get_attr ========
 */
u32 nodewrap_get_attr(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	struct dsp_nodeattr attr;
	struct node_res_object *node_res;

	find_node_handle(&node_res, pr_ctxt, args->args_node_getattr.node);

	if (!node_res)
		return -EFAULT;

	status = node_get_attr(node_res->node, &attr,
			       args->args_node_getattr.attr_size);
	CP_TO_USR(args->args_node_getattr.attr, &attr, status, 1);

	return status;
}

/*
 * ======== nodewrap_get_message ========
 */
u32 nodewrap_get_message(union trapped_args *args, void *pr_ctxt)
{
	int status;
	struct dsp_msg msg;
	struct node_res_object *node_res;

	find_node_handle(&node_res, pr_ctxt, args->args_node_getmessage.node);

	if (!node_res)
		return -EFAULT;

	status = node_get_message(node_res->node, &msg,
				  args->args_node_getmessage.timeout);

	CP_TO_USR(args->args_node_getmessage.message, &msg, status, 1);

	return status;
}

/*
 * ======== nodewrap_pause ========
 */
u32 nodewrap_pause(union trapped_args *args, void *pr_ctxt)
{
	u32 ret;
	struct node_res_object *node_res;

	find_node_handle(&node_res, pr_ctxt, args->args_node_pause.node);

	if (!node_res)
		return -EFAULT;

	ret = node_pause(node_res->node);

	return ret;
}

/*
 * ======== nodewrap_put_message ========
 */
u32 nodewrap_put_message(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	struct dsp_msg msg;
	struct node_res_object *node_res;

	find_node_handle(&node_res, pr_ctxt, args->args_node_putmessage.node);

	if (!node_res)
		return -EFAULT;

	CP_FM_USR(&msg, args->args_node_putmessage.message, status, 1);

	if (!status) {
		status =
		    node_put_message(node_res->node, &msg,
				     args->args_node_putmessage.timeout);
	}

	return status;
}

/*
 * ======== nodewrap_register_notify ========
 */
u32 nodewrap_register_notify(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	struct dsp_notification notification;
	struct node_res_object *node_res;

	find_node_handle(&node_res, pr_ctxt,
			args->args_node_registernotify.node);

	if (!node_res)
		return -EFAULT;

	/* Initialize the notification data structure */
	notification.name = NULL;
	notification.handle = NULL;

	if (!args->args_proc_register_notify.event_mask)
		CP_FM_USR(&notification,
			  args->args_proc_register_notify.notification,
			  status, 1);

	status = node_register_notify(node_res->node,
				      args->args_node_registernotify.event_mask,
				      args->args_node_registernotify.
				      notify_type, &notification);
	CP_TO_USR(args->args_node_registernotify.notification, &notification,
		  status, 1);
	return status;
}

/*
 * ======== nodewrap_run ========
 */
u32 nodewrap_run(union trapped_args *args, void *pr_ctxt)
{
	u32 ret;
	struct node_res_object *node_res;

	find_node_handle(&node_res, pr_ctxt, args->args_node_run.node);

	if (!node_res)
		return -EFAULT;

	ret = node_run(node_res->node);

	return ret;
}

/*
 * ======== nodewrap_terminate ========
 */
u32 nodewrap_terminate(union trapped_args *args, void *pr_ctxt)
{
	int status;
	int tempstatus;
	struct node_res_object *node_res;

	find_node_handle(&node_res, pr_ctxt, args->args_node_terminate.node);

	if (!node_res)
		return -EFAULT;

	status = node_terminate(node_res->node, &tempstatus);

	CP_TO_USR(args->args_node_terminate.status, &tempstatus, status, 1);

	return status;
}

/*
 * ======== nodewrap_get_uuid_props ========
 */
u32 nodewrap_get_uuid_props(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	struct dsp_uuid node_uuid;
	struct dsp_ndbprops *pnode_props = NULL;
	void *hprocessor = ((struct process_context *)pr_ctxt)->processor;

	CP_FM_USR(&node_uuid, args->args_node_getuuidprops.node_id_ptr, status,
		  1);
	if (status)
		goto func_cont;
	pnode_props = kmalloc(sizeof(struct dsp_ndbprops), GFP_KERNEL);
	if (pnode_props != NULL) {
		status =
		    node_get_uuid_props(hprocessor, &node_uuid, pnode_props);
		CP_TO_USR(args->args_node_getuuidprops.node_props, pnode_props,
			  status, 1);
	} else
		status = -ENOMEM;
func_cont:
	kfree(pnode_props);
	return status;
}

/*
 * ======== find_strm_handle =========
 */
inline void find_strm_handle(struct strm_res_object **strmres,
				void *pr_ctxt, void *hstream)
{
	rcu_read_lock();
	*strmres = idr_find(((struct process_context *)pr_ctxt)->stream_id,
							(int)hstream - 1);
	rcu_read_unlock();
	return;
}

/*
 * ======== strmwrap_allocate_buffer ========
 */
u32 strmwrap_allocate_buffer(union trapped_args *args, void *pr_ctxt)
{
	int status;
	u8 **ap_buffer = NULL;
	u32 num_bufs = args->args_strm_allocatebuffer.num_bufs;
	struct strm_res_object *strm_res;

	find_strm_handle(&strm_res, pr_ctxt,
		args->args_strm_allocatebuffer.stream);

	if (!strm_res)
		return -EFAULT;

	if (num_bufs > MAX_BUFS)
		return -EINVAL;

	ap_buffer = kmalloc((num_bufs * sizeof(u8 *)), GFP_KERNEL);
	if (ap_buffer == NULL)
		return -ENOMEM;

	status = strm_allocate_buffer(strm_res,
				      args->args_strm_allocatebuffer.size,
				      ap_buffer, num_bufs, pr_ctxt);
	if (!status) {
		CP_TO_USR(args->args_strm_allocatebuffer.ap_buffer, ap_buffer,
			  status, num_bufs);
		if (status) {
			status = -EFAULT;
			strm_free_buffer(strm_res,
					 ap_buffer, num_bufs, pr_ctxt);
		}
	}
	kfree(ap_buffer);

	return status;
}

/*
 * ======== strmwrap_close ========
 */
u32 strmwrap_close(union trapped_args *args, void *pr_ctxt)
{
	struct strm_res_object *strm_res;

	find_strm_handle(&strm_res, pr_ctxt, args->args_strm_close.stream);

	if (!strm_res)
		return -EFAULT;

	return strm_close(strm_res, pr_ctxt);
}

/*
 * ======== strmwrap_free_buffer ========
 */
u32 strmwrap_free_buffer(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	u8 **ap_buffer = NULL;
	u32 num_bufs = args->args_strm_freebuffer.num_bufs;
	struct strm_res_object *strm_res;

	find_strm_handle(&strm_res, pr_ctxt,
			args->args_strm_freebuffer.stream);

	if (!strm_res)
		return -EFAULT;

	if (num_bufs > MAX_BUFS)
		return -EINVAL;

	ap_buffer = kmalloc((num_bufs * sizeof(u8 *)), GFP_KERNEL);
	if (ap_buffer == NULL)
		return -ENOMEM;

	CP_FM_USR(ap_buffer, args->args_strm_freebuffer.ap_buffer, status,
		  num_bufs);

	if (!status)
		status = strm_free_buffer(strm_res,
					  ap_buffer, num_bufs, pr_ctxt);

	CP_TO_USR(args->args_strm_freebuffer.ap_buffer, ap_buffer, status,
		  num_bufs);
	kfree(ap_buffer);

	return status;
}

/*
 * ======== strmwrap_get_event_handle ========
 */
u32 __deprecated strmwrap_get_event_handle(union trapped_args * args,
					   void *pr_ctxt)
{
	pr_err("%s: deprecated dspbridge ioctl\n", __func__);
	return -ENOSYS;
}

/*
 * ======== strmwrap_get_info ========
 */
u32 strmwrap_get_info(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	struct stream_info strm_info;
	struct dsp_streaminfo user;
	struct dsp_streaminfo *temp;
	struct strm_res_object *strm_res;

	find_strm_handle(&strm_res, pr_ctxt,
			args->args_strm_getinfo.stream);

	if (!strm_res)
		return -EFAULT;

	CP_FM_USR(&strm_info, args->args_strm_getinfo.stream_info, status, 1);
	temp = strm_info.user_strm;

	strm_info.user_strm = &user;

	if (!status) {
		status = strm_get_info(strm_res->stream,
				       &strm_info,
				       args->args_strm_getinfo.
				       stream_info_size);
	}
	CP_TO_USR(temp, strm_info.user_strm, status, 1);
	strm_info.user_strm = temp;
	CP_TO_USR(args->args_strm_getinfo.stream_info, &strm_info, status, 1);
	return status;
}

/*
 * ======== strmwrap_idle ========
 */
u32 strmwrap_idle(union trapped_args *args, void *pr_ctxt)
{
	u32 ret;
	struct strm_res_object *strm_res;

	find_strm_handle(&strm_res, pr_ctxt, args->args_strm_idle.stream);

	if (!strm_res)
		return -EFAULT;

	ret = strm_idle(strm_res->stream, args->args_strm_idle.flush_flag);

	return ret;
}

/*
 * ======== strmwrap_issue ========
 */
u32 strmwrap_issue(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	struct strm_res_object *strm_res;

	find_strm_handle(&strm_res, pr_ctxt, args->args_strm_issue.stream);

	if (!strm_res)
		return -EFAULT;

	if (!args->args_strm_issue.buffer)
		return -EFAULT;

	/* No need of doing CP_FM_USR for the user buffer (pbuffer)
	   as this is done in Bridge internal function bridge_chnl_add_io_req
	   in chnl_sm.c */
	status = strm_issue(strm_res->stream,
			    args->args_strm_issue.buffer,
			    args->args_strm_issue.bytes,
			    args->args_strm_issue.buf_size,
			    args->args_strm_issue.arg);

	return status;
}

/*
 * ======== strmwrap_open ========
 */
u32 strmwrap_open(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	struct strm_attr attr;
	struct strm_res_object *strm_res_obj;
	struct dsp_streamattrin strm_attr_in;
	struct node_res_object *node_res;
	int strmid;

	find_node_handle(&node_res, pr_ctxt, args->args_strm_open.node);

	if (!node_res)
		return -EFAULT;

	CP_FM_USR(&attr, args->args_strm_open.attr_in, status, 1);

	if (attr.stream_attr_in != NULL) {	/* Optional argument */
		CP_FM_USR(&strm_attr_in, attr.stream_attr_in, status, 1);
		if (!status) {
			attr.stream_attr_in = &strm_attr_in;
			if (attr.stream_attr_in->strm_mode == STRMMODE_LDMA)
				return -ENOSYS;
		}

	}
	status = strm_open(node_res->node,
			   args->args_strm_open.direction,
			   args->args_strm_open.index, &attr, &strm_res_obj,
			   pr_ctxt);
	if (!status) {
		strmid = strm_res_obj->id + 1;
		CP_TO_USR(args->args_strm_open.stream, &strmid, status, 1);
	}
	return status;
}

/*
 * ======== strmwrap_reclaim ========
 */
u32 strmwrap_reclaim(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	u8 *buf_ptr;
	u32 ul_bytes;
	u32 dw_arg;
	u32 ul_buf_size;
	struct strm_res_object *strm_res;

	find_strm_handle(&strm_res, pr_ctxt, args->args_strm_reclaim.stream);

	if (!strm_res)
		return -EFAULT;

	status = strm_reclaim(strm_res->stream, &buf_ptr,
			      &ul_bytes, &ul_buf_size, &dw_arg);
	CP_TO_USR(args->args_strm_reclaim.buf_ptr, &buf_ptr, status, 1);
	CP_TO_USR(args->args_strm_reclaim.bytes, &ul_bytes, status, 1);
	CP_TO_USR(args->args_strm_reclaim.arg, &dw_arg, status, 1);

	if (args->args_strm_reclaim.buf_size_ptr != NULL) {
		CP_TO_USR(args->args_strm_reclaim.buf_size_ptr, &ul_buf_size,
			  status, 1);
	}

	return status;
}

/*
 * ======== strmwrap_register_notify ========
 */
u32 strmwrap_register_notify(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	struct dsp_notification notification;
	struct strm_res_object *strm_res;

	find_strm_handle(&strm_res, pr_ctxt,
			args->args_strm_registernotify.stream);

	if (!strm_res)
		return -EFAULT;

	/* Initialize the notification data structure */
	notification.name = NULL;
	notification.handle = NULL;

	status = strm_register_notify(strm_res->stream,
				      args->args_strm_registernotify.event_mask,
				      args->args_strm_registernotify.
				      notify_type, &notification);
	CP_TO_USR(args->args_strm_registernotify.notification, &notification,
		  status, 1);

	return status;
}

/*
 * ======== strmwrap_select ========
 */
u32 strmwrap_select(union trapped_args *args, void *pr_ctxt)
{
	u32 mask;
	struct strm_object *strm_tab[MAX_STREAMS];
	int status = 0;
	struct strm_res_object *strm_res;
	int *ids[MAX_STREAMS];
	int i;

	if (args->args_strm_select.strm_num > MAX_STREAMS)
		return -EINVAL;

	CP_FM_USR(ids, args->args_strm_select.stream_tab, status,
		args->args_strm_select.strm_num);

	if (status)
		return status;

	for (i = 0; i < args->args_strm_select.strm_num; i++) {
		find_strm_handle(&strm_res, pr_ctxt, ids[i]);

		if (!strm_res)
			return -EFAULT;

		strm_tab[i] = strm_res->stream;
	}

	if (!status) {
		status = strm_select(strm_tab, args->args_strm_select.strm_num,
				     &mask, args->args_strm_select.timeout);
	}
	CP_TO_USR(args->args_strm_select.mask, &mask, status, 1);
	return status;
}

/* CMM */

/*
 * ======== cmmwrap_calloc_buf ========
 */
u32 __deprecated cmmwrap_calloc_buf(union trapped_args * args, void *pr_ctxt)
{
	/* This operation is done in kernel */
	pr_err("%s: deprecated dspbridge ioctl\n", __func__);
	return -ENOSYS;
}

/*
 * ======== cmmwrap_free_buf ========
 */
u32 __deprecated cmmwrap_free_buf(union trapped_args * args, void *pr_ctxt)
{
	/* This operation is done in kernel */
	pr_err("%s: deprecated dspbridge ioctl\n", __func__);
	return -ENOSYS;
}

/*
 * ======== cmmwrap_get_handle ========
 */
u32 cmmwrap_get_handle(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	struct cmm_object *hcmm_mgr;
	void *hprocessor = ((struct process_context *)pr_ctxt)->processor;

	status = cmm_get_handle(hprocessor, &hcmm_mgr);

	CP_TO_USR(args->args_cmm_gethandle.cmm_mgr, &hcmm_mgr, status, 1);

	return status;
}

/*
 * ======== cmmwrap_get_info ========
 */
u32 cmmwrap_get_info(union trapped_args *args, void *pr_ctxt)
{
	int status = 0;
	struct cmm_info cmm_info_obj;

	status = cmm_get_info(args->args_cmm_getinfo.cmm_mgr, &cmm_info_obj);

	CP_TO_USR(args->args_cmm_getinfo.cmm_info_obj, &cmm_info_obj, status,
		  1);

	return status;
}