summaryrefslogtreecommitdiff
path: root/drivers/dsp/syslink/omap_notify/drv_notify.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dsp/syslink/omap_notify/drv_notify.c')
-rw-r--r--drivers/dsp/syslink/omap_notify/drv_notify.c1005
1 files changed, 1005 insertions, 0 deletions
diff --git a/drivers/dsp/syslink/omap_notify/drv_notify.c b/drivers/dsp/syslink/omap_notify/drv_notify.c
new file mode 100644
index 00000000000..aafe34667e7
--- /dev/null
+++ b/drivers/dsp/syslink/omap_notify/drv_notify.c
@@ -0,0 +1,1005 @@
+/*
+ * drv_notify.c
+ *
+ * Syslink support functions for TI OMAP processors.
+ *
+ * Copyright (C) 2008-2009 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 <generated/autoconf.h>
+#include <linux/spinlock.h>
+#include <linux/semaphore.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <asm/pgtable.h>
+#include <linux/types.h>
+#include <linux/cdev.h>
+
+#include <syslink/platform_mem.h>
+#include <syslink/ipc_ioctl.h>
+#include <syslink/ipc.h>
+#include <syslink/drv_notify.h>
+#include <syslink/notify_driver.h>
+#include <syslink/notify.h>
+#include <syslink/notify_ioctl.h>
+
+
+/** ============================================================================
+ * Macros and types
+ * ============================================================================
+ */
+/* Maximum number of user supported. */
+#define MAX_PROCESSES 32
+
+/*Structure of Event callback argument passed to register fucntion*/
+struct notify_drv_event_cbck {
+ struct list_head element; /* List element header */
+ u16 proc_id; /* Processor identifier */
+ u16 line_id; /* line identifier */
+ u32 event_id; /* Event identifier */
+ notify_fn_notify_cbck func; /* Callback function for the event. */
+ void *param; /* User callback argument. */
+ u32 pid; /* Process Identifier for user process. */
+};
+
+/* Keeps the information related to Event.*/
+struct notify_drv_event_state {
+ struct list_head buf_list;
+ /* Head of received event list. */
+ u32 pid;
+ /* User process ID. */
+ u32 ref_count;
+ /*Reference count, used when multiple Notify_registerEvent are called
+ from same process space(multi threads/processes). */
+ struct semaphore *semhandle;
+ /* Semaphore for waiting on event. */
+ struct semaphore *tersemhandle;
+ /* Termination synchronization semaphore. */
+};
+
+/* NotifyDrv module state object */
+struct notify_drv_module_object {
+ bool is_setup;
+ /* Indicates whether the module has been already setup */
+ bool open_ref_count;
+ /* Open reference count. */
+ struct mutex *gate_handle;
+ /* Handle of gate to be used for local thread safety */
+ struct list_head event_cbck_list;
+ /* List containg callback arguments for all registered handlers from
+ * user mode. */
+ struct list_head single_event_cbck_list;
+ /* List containg callback arguments for all registered handlers from
+ user mode for 'single' registrations. */
+ struct notify_drv_event_state event_state[MAX_PROCESSES];
+ /* List for all user processes registered. */
+};
+
+static struct notify_drv_module_object notifydrv_state = {
+ .is_setup = false,
+ .open_ref_count = 0,
+ .gate_handle = NULL
+ /*.event_cbck_list = NULL,
+ .single_event_cbck_list = NULL*/
+};
+
+
+/* Attach a process to notify user support framework. */
+static int notify_drv_attach(u32 pid);
+
+/* Detach a process from notify user support framework. */
+static int notify_drv_detach(u32 pid, bool force);
+
+/* This function implements the callback registered with IPS. Here to pass
+ * event no. back to user function (so that it can do another level of
+ * demultiplexing of callbacks) */
+static void _notify_drv_callback(u16 proc_id, u16 line_id, u32 event_id,
+ uint *arg, u32 payload);
+
+/* This function adds a data to a registered process. */
+static int _notify_drv_add_buf_by_pid(u16 proc_id, u16 line_id, u32 pid,
+ u32 event_id, u32 data, notify_fn_notify_cbck cb_fxn,
+ void *param);
+
+
+static struct resource_info *find_notify_drv_resource(
+ struct ipc_process_context *pr_ctxt,
+ unsigned int cmd,
+ void *args)
+{
+ struct resource_info *info = NULL;
+ bool found = false;
+
+ spin_lock(&pr_ctxt->res_lock);
+
+ list_for_each_entry(info, &pr_ctxt->resources, res) {
+ if (info->cmd == cmd) {
+ switch (cmd) {
+ case CMD_NOTIFY_DESTROY:
+ found = true;
+ break;
+ case CMD_NOTIFY_THREADDETACH:
+ if ((u32)args == *(u32 *)info->data)
+ found = true;
+ break;
+ }
+ }
+ if (found == true)
+ break;
+ }
+
+ spin_unlock(&pr_ctxt->res_lock);
+
+ if (found == false)
+ info = NULL;
+
+ return info;
+}
+
+/*
+ * read data from the driver
+ */
+int notify_drv_read(struct file *filp, char __user *dst, size_t size,
+ loff_t *offset)
+{
+
+ bool flag = false;
+ struct notify_drv_event_packet *u_buf = NULL;
+ int ret_val = 0;
+ u32 i;
+ struct list_head *elem;
+ struct notify_drv_event_packet t_buf;
+
+ if (WARN_ON(notifydrv_state.is_setup == false)) {
+ ret_val = -EFAULT;
+ goto func_end;
+ }
+
+ ret_val = copy_from_user((void *)&t_buf,
+ (void __user *)dst,
+ sizeof(struct notify_drv_event_packet));
+ if (WARN_ON(ret_val != 0))
+ ret_val = -EFAULT;
+
+ for (i = 0; i < MAX_PROCESSES; i++) {
+ if (notifydrv_state.event_state[i].pid == t_buf.pid) {
+ flag = true;
+ break;
+ }
+ }
+ if (flag == false) {
+ ret_val = -EFAULT;
+ goto func_end;
+ }
+
+ /* Wait for the event */
+ ret_val = down_interruptible(notifydrv_state.event_state[i].semhandle);
+ if (ret_val < 0) {
+ ret_val = -ERESTARTSYS;
+ goto func_end;
+ }
+ WARN_ON(mutex_lock_interruptible(notifydrv_state.gate_handle));
+ elem = ((struct list_head *)
+ &(notifydrv_state.event_state[i].buf_list))->next;
+ u_buf = container_of(elem, struct notify_drv_event_packet, element);
+ list_del(elem);
+ mutex_unlock(notifydrv_state.gate_handle);
+ if (u_buf == NULL) {
+ ret_val = -EFAULT;
+ goto func_end;
+ }
+ ret_val = copy_to_user((void __user *)dst, u_buf,
+ sizeof(struct notify_drv_event_packet));
+ if (WARN_ON(ret_val != 0))
+ ret_val = -EFAULT;
+ ret_val = sizeof(struct notify_drv_event_packet);
+
+ if (u_buf->is_exit == true)
+ up(notifydrv_state.event_state[i].tersemhandle);
+
+ kfree(u_buf);
+ u_buf = NULL;
+
+func_end:
+ return ret_val;
+}
+
+int notify_drv_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ vma->vm_page_prot = pgprot_dmacoherent(vma->vm_page_prot);
+
+ if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot))
+ return -EAGAIN;
+ return 0;
+}
+
+/* ioctl function for of Linux Notify driver. */
+int notify_drv_ioctl(struct inode *inode, struct file *filp, u32 cmd,
+ unsigned long args, bool user)
+{
+ int status = NOTIFY_S_SUCCESS;
+ int os_status = 0;
+ unsigned long size;
+ struct notify_cmd_args *cmd_args = (struct notify_cmd_args *)args;
+ struct notify_cmd_args common_args;
+ struct ipc_process_context *pr_ctxt =
+ (struct ipc_process_context *)filp->private_data;
+
+#ifdef CONFIG_SYSLINK_RECOVERY
+ if (ipc_recovering() && user && cmd != CMD_NOTIFY_THREADDETACH)
+ return -EIO;
+#endif
+
+ switch (cmd) {
+ case CMD_NOTIFY_GETCONFIG:
+ {
+ struct notify_cmd_args_get_config *src_args =
+ (struct notify_cmd_args_get_config *)args;
+ struct notify_config cfg;
+
+ notify_get_config(&cfg);
+ size = copy_to_user((void __user *) (src_args->cfg),
+ (const void *) &cfg, sizeof(struct notify_config));
+ if (WARN_ON(size != 0))
+ os_status = -EFAULT;
+ }
+ break;
+
+ case CMD_NOTIFY_SETUP:
+ {
+ struct notify_cmd_args_setup *src_args =
+ (struct notify_cmd_args_setup *) args;
+ struct notify_config cfg;
+
+ size = copy_from_user((void *) &cfg,
+ (const void __user *) (src_args->cfg),
+ sizeof(struct notify_config));
+ if (WARN_ON(size != 0)) {
+ os_status = -EFAULT;
+ goto func_end;
+ }
+ status = notify_setup(&cfg);
+ if (status >= 0)
+ add_pr_res(pr_ctxt, CMD_NOTIFY_DESTROY, NULL);
+ }
+ break;
+
+ case CMD_NOTIFY_DESTROY:
+ {
+ struct resource_info *info = NULL;
+ /* copy_from_user is not needed for Notify_getConfig, since the
+ * user's config is not used.
+ */
+ info = find_notify_drv_resource(pr_ctxt,
+ CMD_NOTIFY_DESTROY,
+ NULL);
+ status = notify_destroy();
+ remove_pr_res(pr_ctxt, info);
+ }
+ break;
+
+ case CMD_NOTIFY_REGISTEREVENTSINGLE:
+ {
+ struct notify_cmd_args_register_event src_args;
+ struct notify_drv_event_cbck *cbck = NULL;
+
+ /* Copy the full args from user-side. */
+ size = copy_from_user((void *) &src_args,
+ (const void __user *) (args),
+ sizeof(struct notify_cmd_args_register_event));
+ if (WARN_ON(size != 0)) {
+ os_status = -EFAULT;
+ goto func_end;
+ }
+
+ cbck = kmalloc(sizeof(struct notify_drv_event_cbck),
+ GFP_ATOMIC);
+ WARN_ON(cbck == NULL);
+ cbck->proc_id = src_args.proc_id;
+ cbck->line_id = src_args.line_id;
+ cbck->event_id = src_args.event_id;
+ cbck->pid = src_args.pid;
+ cbck->func = src_args.fn_notify_cbck;
+ cbck->param = src_args.cbck_arg;
+ status = notify_register_event_single(src_args.proc_id,
+ src_args.line_id, src_args.event_id,
+ _notify_drv_callback, (void *)cbck);
+ if (status < 0) {
+ /* This does not impact return status of this function,
+ * so retval comment is not used. */
+ kfree(cbck);
+ } else {
+ WARN_ON(mutex_lock_interruptible
+ (notifydrv_state.gate_handle));
+ INIT_LIST_HEAD((struct list_head *)&(cbck->element));
+ list_add_tail(&(cbck->element),
+ &(notifydrv_state.single_event_cbck_list));
+ mutex_unlock(notifydrv_state.gate_handle);
+ }
+ }
+ break;
+
+ case CMD_NOTIFY_REGISTEREVENT:
+ {
+ struct notify_cmd_args_register_event src_args;
+ struct notify_drv_event_cbck *cbck = NULL;
+
+ /* Copy the full args from user-side. */
+ size = copy_from_user((void *) &src_args,
+ (const void __user *) (args),
+ sizeof(struct notify_cmd_args_register_event));
+ if (WARN_ON(size != 0)) {
+ os_status = -EFAULT;
+ goto func_end;
+ }
+
+ cbck = kmalloc(sizeof(struct notify_drv_event_cbck),
+ GFP_ATOMIC);
+ WARN_ON(cbck == NULL);
+ cbck->proc_id = src_args.proc_id;
+ cbck->line_id = src_args.line_id;
+ cbck->event_id = src_args.event_id;
+ cbck->func = src_args.fn_notify_cbck;
+ cbck->param = src_args.cbck_arg;
+ cbck->pid = src_args.pid;
+ status = notify_register_event(src_args.proc_id,
+ src_args.line_id, src_args.event_id,
+ _notify_drv_callback, (void *)cbck);
+ if (status < 0) {
+ /* This does not impact return status of this function,
+ * so retval comment is not used. */
+ kfree(cbck);
+ } else {
+ WARN_ON(mutex_lock_interruptible
+ (notifydrv_state.gate_handle));
+ INIT_LIST_HEAD((struct list_head *)&(cbck->element));
+ list_add_tail(&(cbck->element),
+ &(notifydrv_state.event_cbck_list));
+ mutex_unlock(notifydrv_state.gate_handle);
+ }
+ }
+ break;
+
+ case CMD_NOTIFY_UNREGISTEREVENTSINGLE:
+ {
+ bool found = false;
+ u32 pid;
+ struct notify_drv_event_cbck *cbck = NULL;
+ struct list_head *entry = NULL;
+ struct notify_cmd_args_unregister_event src_args;
+
+ /* Copy the full args from user-side. */
+ size = copy_from_user((void *)&src_args,
+ (const void __user *)(args),
+ sizeof(struct notify_cmd_args_unregister_event));
+ if (WARN_ON(size != 0)) {
+ os_status = -EFAULT;
+ goto func_end;
+ }
+
+ WARN_ON(mutex_lock_interruptible(notifydrv_state.gate_handle));
+ pid = src_args.pid;
+ list_for_each(entry, (struct list_head *)
+ &(notifydrv_state.single_event_cbck_list)) {
+ cbck = (struct notify_drv_event_cbck *)(entry);
+ if ((cbck->proc_id == src_args.proc_id) &&
+ (cbck->line_id == src_args.line_id) &&
+ (cbck->event_id == src_args.event_id) &&
+ (cbck->pid == pid)) {
+ found = true;
+ break;
+ }
+ }
+ mutex_unlock(notifydrv_state.gate_handle);
+ if (found == false) {
+ status = NOTIFY_E_NOTFOUND;
+ goto func_end;
+ }
+ status = notify_unregister_event_single(src_args.proc_id,
+ src_args.line_id, src_args.event_id);
+ /* This check is needed at run-time also to propagate the
+ * status to user-side. This must not be optimized out. */
+ if (status >= 0) {
+ WARN_ON(mutex_lock_interruptible
+ (notifydrv_state.gate_handle));
+ list_del((struct list_head *)cbck);
+ mutex_unlock(notifydrv_state.gate_handle);
+ kfree(cbck);
+ }
+ }
+ break;
+
+ case CMD_NOTIFY_UNREGISTEREVENT:
+ {
+ bool found = false;
+ u32 pid;
+ struct notify_drv_event_cbck *cbck = NULL;
+ struct list_head *entry = NULL;
+ struct notify_cmd_args_unregister_event src_args;
+
+ /* Copy the full args from user-side. */
+ size = copy_from_user((void *)&src_args,
+ (const void __user *)(args),
+ sizeof(struct notify_cmd_args_unregister_event));
+ if (WARN_ON(size != 0)) {
+ os_status = -EFAULT;
+ goto func_end;
+ }
+
+ WARN_ON(mutex_lock_interruptible(notifydrv_state.gate_handle));
+ pid = src_args.pid;
+ list_for_each(entry, (struct list_head *)
+ &(notifydrv_state.event_cbck_list)) {
+ cbck = (struct notify_drv_event_cbck *)(entry);
+ if ((cbck->func == src_args.fn_notify_cbck) &&
+ (cbck->param == src_args.cbck_arg) &&
+ (cbck->proc_id == src_args.proc_id) &&
+ (cbck->line_id == src_args.line_id) &&
+ (cbck->event_id == src_args.event_id) &&
+ (cbck->pid == pid)) {
+ found = true;
+ break;
+ }
+ }
+ mutex_unlock(notifydrv_state.gate_handle);
+ if (found == false) {
+ status = NOTIFY_E_NOTFOUND;
+ goto func_end;
+ }
+ status = notify_unregister_event(src_args.proc_id,
+ src_args.line_id, src_args.event_id,
+ _notify_drv_callback, (void *) cbck);
+ /* This check is needed at run-time also to propagate the
+ * status to user-side. This must not be optimized out. */
+ if (status >= 0) {
+ WARN_ON(mutex_lock_interruptible
+ (notifydrv_state.gate_handle));
+ list_del((struct list_head *)cbck);
+ mutex_unlock(notifydrv_state.gate_handle);
+ kfree(cbck);
+ }
+ }
+ break;
+
+ case CMD_NOTIFY_SENDEVENT:
+ {
+ struct notify_cmd_args_send_event src_args;
+
+ /* Copy the full args from user-side. */
+ size = copy_from_user((void *) &src_args,
+ (const void __user *) (args),
+ sizeof(struct notify_cmd_args_send_event));
+ if (WARN_ON(size != 0)) {
+ os_status = -EFAULT;
+ goto func_end;
+ }
+ status = notify_send_event(src_args.proc_id, src_args.line_id,
+ src_args.event_id, src_args.payload,
+ src_args.wait_clear);
+ }
+ break;
+
+ case CMD_NOTIFY_DISABLE:
+ {
+ struct notify_cmd_args_disable src_args;
+
+ /* Copy the full args from user-side. */
+ size = copy_from_user((void *) &src_args,
+ (const void __user *) (args),
+ sizeof(struct notify_cmd_args_disable));
+ if (WARN_ON(size != 0)) {
+ os_status = -EFAULT;
+ goto func_end;
+ }
+ src_args.flags = notify_disable(src_args.proc_id,
+ src_args.line_id);
+
+ /* Copy the full args to user-side */
+ size = copy_to_user((void __user *) (args),
+ (const void *) &src_args,
+ sizeof(struct notify_cmd_args_disable));
+ /* This check is needed at run-time also since it depends on
+ * run environment. It must not be optimized out. */
+ if (WARN_ON(size != 0))
+ os_status = -EFAULT;
+ }
+ break;
+
+ case CMD_NOTIFY_RESTORE:
+ {
+ struct notify_cmd_args_restore src_args;
+
+ /* Copy the full args from user-side. */
+ size = copy_from_user((void *) &src_args,
+ (const void __user *)(args),
+ sizeof(struct notify_cmd_args_restore));
+ if (WARN_ON(size != 0)) {
+ os_status = -EFAULT;
+ goto func_end;
+ }
+ notify_restore(src_args.proc_id, src_args.line_id,
+ src_args.key);
+ }
+ break;
+
+ case CMD_NOTIFY_DISABLEEVENT:
+ {
+ struct notify_cmd_args_disable_event src_args;
+
+ /* Copy the full args from user-side. */
+ size = copy_from_user((void *) &src_args,
+ (const void __user *)(args),
+ sizeof(struct notify_cmd_args_disable_event));
+ if (WARN_ON(size != 0)) {
+ os_status = -EFAULT;
+ goto func_end;
+ }
+ notify_disable_event(src_args.proc_id, src_args.line_id,
+ src_args.event_id);
+ }
+ break;
+
+ case CMD_NOTIFY_ENABLEEVENT:
+ {
+ struct notify_cmd_args_enable_event src_args;
+
+ /* Copy the full args from user-side. */
+ size = copy_from_user((void *)&src_args,
+ (const void __user *)(args),
+ sizeof(struct notify_cmd_args_enable_event));
+ if (WARN_ON(size != 0)) {
+ os_status = -EFAULT;
+ goto func_end;
+ }
+ notify_enable_event(src_args.proc_id, src_args.line_id,
+ src_args.event_id);
+ }
+ break;
+
+ case CMD_NOTIFY_THREADATTACH:
+ {
+ u32 pid = *((u32 *)args);
+ status = notify_drv_attach(pid);
+ if (status < 0)
+ pr_err("NOTIFY_ATTACH FAILED\n");
+ else {
+ u32 *data = kmalloc(sizeof(u32), GFP_KERNEL);
+ if (WARN_ON(!data)) {
+ notify_drv_detach(pid, true);
+ os_status = -ENOMEM;
+ goto func_end;
+ }
+ *data = pid;
+ add_pr_res(pr_ctxt, CMD_NOTIFY_THREADDETACH,
+ (void *)data);
+ }
+ }
+ break;
+
+ case CMD_NOTIFY_THREADDETACH:
+ {
+ struct resource_info *info = NULL;
+ u32 pid = *((u32 *)args);
+ info = find_notify_drv_resource(pr_ctxt,
+ CMD_NOTIFY_THREADDETACH,
+ (void *)pid);
+ if (user == true)
+ status = notify_drv_detach(pid, false);
+ else
+ status = notify_drv_detach(pid, true);
+ if (status < 0)
+ pr_err("NOTIFY_DETACH FAILED\n");
+ else
+ remove_pr_res(pr_ctxt, info);
+ }
+ break;
+
+ case CMD_NOTIFY_ATTACH:
+ {
+ struct notify_cmd_args_attach src_args;
+ void *knl_shared_addr;
+
+ size = copy_from_user((void *) &src_args,
+ (const void __user *)(args),
+ sizeof(struct notify_cmd_args_attach));
+ if (size != 0) {
+ os_status = -EFAULT;
+ goto func_end;
+ }
+
+ /* knl_shared_addr = Memory_translate(src_args.shared_addr,
+ Memory_XltFlags_Phys2Virt); */
+ knl_shared_addr = platform_mem_translate(
+ (void *)src_args.shared_addr,
+ PLATFORM_MEM_XLT_FLAGS_PHYS2VIRT);
+ status = notify_attach(src_args.proc_id, knl_shared_addr);
+ }
+ break;
+
+ case CMD_NOTIFY_DETACH:
+ {
+ struct notify_cmd_args_detach src_args;
+
+ size = copy_from_user((void *) &src_args,
+ (const void __user *)(args),
+ sizeof(struct notify_cmd_args_detach));
+ if (size != 0) {
+ os_status = -EFAULT;
+ goto func_end;
+ }
+
+ status = notify_detach(src_args.proc_id);
+ }
+ break;
+
+ case CMD_NOTIFY_SHAREDMEMREQ:
+ {
+ struct notify_cmd_args_shared_mem_req src_args;
+ void *knl_shared_addr;
+
+ size = copy_from_user((void *) &src_args,
+ (const void __user *)(args),
+ sizeof(struct notify_cmd_args_shared_mem_req));
+ if (size != 0) {
+ os_status = -EFAULT;
+ goto func_end;
+ }
+
+ /* knl_shared_addr = Memory_translate(src_args.shared_addr,
+ Memory_XltFlags_Phys2Virt); */
+ knl_shared_addr = platform_mem_translate(
+ (void *)src_args.shared_addr,
+ PLATFORM_MEM_XLT_FLAGS_PHYS2VIRT);
+ status = notify_shared_mem_req(src_args.proc_id,
+ knl_shared_addr);
+ }
+ break;
+
+ case CMD_NOTIFY_ISREGISTERED:
+ {
+ struct notify_cmd_args_is_registered src_args;
+
+ size = copy_from_user((void *) &src_args,
+ (const void __user *)(args),
+ sizeof(struct notify_cmd_args_is_registered));
+ if (size != 0) {
+ os_status = -EFAULT;
+ goto func_end;
+ }
+
+ src_args.is_registered = notify_is_registered(src_args.proc_id,
+ src_args.line_id);
+ size = copy_to_user((void __user *) (args),
+ (const void *)&src_args,
+ sizeof(struct notify_cmd_args_is_registered));
+ if (size != 0) {
+ os_status = -EFAULT;
+ goto func_end;
+ }
+ }
+ break;
+
+ default:
+ {
+ /* This does not impact return status of this function,so retval
+ * comment is not used. */
+ status = NOTIFY_E_INVALIDARG;
+ pr_err("not valid command\n");
+ }
+ break;
+ }
+
+func_end:
+ /* Set the status and copy the common args to user-side. */
+ common_args.api_status = status;
+ if (user == true) {
+ size = copy_to_user((void __user *) cmd_args,
+ (const void *) &common_args,
+ sizeof(struct notify_cmd_args));
+ if (size)
+ os_status = -EFAULT;
+ }
+ return os_status;
+}
+
+/* This function implements the callback registered with IPS. Here
+ * to pass event no. back to user function(so that it can do another
+ * level of demultiplexing of callbacks) */
+static void _notify_drv_callback(u16 proc_id, u16 line_id, u32 event_id,
+ uint *arg, u32 payload)
+{
+ struct notify_drv_event_cbck *cbck;
+ int status = 0;
+
+ if (WARN_ON(notifydrv_state.is_setup == false)) {
+ status = -EFAULT;
+ goto func_end;
+ }
+
+ if (WARN_ON(arg == NULL)) {
+ status = -EINVAL;
+ goto func_end;
+ }
+
+ cbck = (struct notify_drv_event_cbck *)arg;
+ status = _notify_drv_add_buf_by_pid(proc_id, line_id, cbck->pid,
+ event_id, payload, cbck->func, cbck->param);
+
+func_end:
+ if (status < 0)
+ pr_err("_notify_drv_callback failed! status = 0x%x", status);
+ return;
+}
+
+/* This function adds a data to a registered process. */
+static int _notify_drv_add_buf_by_pid(u16 proc_id, u16 line_id, u32 pid,
+ u32 event_id, u32 data,
+ notify_fn_notify_cbck cb_fxn,
+ void *param)
+{
+ s32 status = 0;
+ bool flag = false;
+ bool is_exit = false;
+ struct notify_drv_event_packet *u_buf = NULL;
+ u32 i;
+
+ WARN_ON(mutex_lock_interruptible(notifydrv_state.gate_handle));
+ for (i = 0; (i < MAX_PROCESSES) && (flag != true); i++) {
+ if (notifydrv_state.event_state[i].pid == pid) {
+ flag = true;
+ break;
+ }
+ }
+ mutex_unlock(notifydrv_state.gate_handle);
+
+ if (WARN_ON(flag == false)) {
+ status = -EFAULT;
+ goto func_end;
+ }
+
+ u_buf = kmalloc(sizeof(struct notify_drv_event_packet), GFP_ATOMIC);
+ if (u_buf == NULL) {
+ status = -ENOMEM;
+ goto func_end;
+ }
+
+ INIT_LIST_HEAD((struct list_head *)&u_buf->element);
+ u_buf->proc_id = proc_id;
+ u_buf->line_id = line_id;
+ u_buf->data = data;
+ u_buf->event_id = event_id;
+ u_buf->func = cb_fxn;
+ u_buf->param = param;
+ u_buf->is_exit = false;
+ if (u_buf->event_id == (u32) -1) {
+ u_buf->is_exit = true;
+ is_exit = true;
+ }
+ WARN_ON(mutex_lock_interruptible(notifydrv_state.gate_handle));
+ list_add_tail((struct list_head *)&(u_buf->element),
+ (struct list_head *)&(notifydrv_state.event_state[i].buf_list));
+ mutex_unlock(notifydrv_state.gate_handle);
+ up(notifydrv_state.event_state[i].semhandle);
+
+ /* Termination packet */
+ if (is_exit == true) {
+ if (down_interruptible(
+ notifydrv_state.event_state[i].tersemhandle))
+ status = NOTIFY_E_OSFAILURE;
+ }
+
+func_end:
+ if (status < 0) {
+ pr_err("_notify_drv_add_buf_by_pid failed! "
+ "status = 0x%x", status);
+ }
+ return status;
+}
+
+/* Module setup function.*/
+void _notify_drv_setup(void)
+{
+ int i;
+
+ INIT_LIST_HEAD((struct list_head *)&(notifydrv_state.event_cbck_list));
+ INIT_LIST_HEAD(
+ (struct list_head *)&(notifydrv_state.single_event_cbck_list));
+ notifydrv_state.gate_handle = kmalloc(sizeof(struct mutex), GFP_KERNEL);
+ mutex_init(notifydrv_state.gate_handle);
+ for (i = 0; i < MAX_PROCESSES; i++) {
+ notifydrv_state.event_state[i].pid = -1;
+ notifydrv_state.event_state[i].ref_count = 0;
+ INIT_LIST_HEAD((struct list_head *)
+ &(notifydrv_state.event_state[i].buf_list));
+ }
+ notifydrv_state.is_setup = true;
+}
+
+/* Module destroy function.*/
+void _notify_drv_destroy(void)
+{
+ int i;
+ struct notify_drv_event_packet *packet;
+ struct list_head *entry, *n;
+ struct notify_drv_event_cbck *cbck;
+
+ for (i = 0; i < MAX_PROCESSES; i++) {
+ notifydrv_state.event_state[i].pid = -1;
+ notifydrv_state.event_state[i].ref_count = 0;
+ /* Free event packets for any received but unprocessed events.*/
+ list_for_each_safe(entry, n, (struct list_head *)
+ &(notifydrv_state.event_state[i].buf_list)) {
+ packet = (struct notify_drv_event_packet *)entry;
+ kfree(packet);
+ }
+ INIT_LIST_HEAD(&notifydrv_state.event_state[i].buf_list);
+ }
+
+ /* Clear any event registrations that were not unregistered. */
+ list_for_each_safe(entry, n, (struct list_head *)
+ &(notifydrv_state.event_cbck_list)) {
+ cbck = (struct notify_drv_event_cbck *)(entry);
+ kfree(cbck);
+ }
+ INIT_LIST_HEAD(&notifydrv_state.event_cbck_list);
+
+ /* Clear any event registrations that were not unregistered from single
+ * list. */
+ list_for_each_safe(entry, n,
+ (struct list_head *)&(notifydrv_state.single_event_cbck_list)) {
+ cbck = (struct notify_drv_event_cbck *)(entry);
+ kfree(cbck);
+ }
+ INIT_LIST_HEAD(&notifydrv_state.single_event_cbck_list);
+
+ mutex_destroy(notifydrv_state.gate_handle);
+ kfree(notifydrv_state.gate_handle);
+ notifydrv_state.is_setup = false;
+ return;
+}
+
+/* Attach a process to notify user support framework. */
+static int notify_drv_attach(u32 pid)
+{
+ bool flag = false;
+ bool is_init = false;
+ u32 i;
+ struct semaphore *sem_handle = NULL;
+ struct semaphore *ter_sem_handle = NULL;
+ int ret_val = 0;
+
+ if (WARN_ON(notifydrv_state.is_setup == false)) {
+ ret_val = NOTIFY_E_FAIL;
+ goto exit;
+ }
+
+ WARN_ON(mutex_lock_interruptible(notifydrv_state.gate_handle));
+ for (i = 0; (i < MAX_PROCESSES); i++) {
+ if (notifydrv_state.event_state[i].pid == pid) {
+ notifydrv_state.event_state[i].ref_count++;
+ is_init = true;
+ break;
+ }
+ }
+ if (is_init == true) {
+ mutex_unlock(notifydrv_state.gate_handle);
+ return 0;
+ }
+
+ sem_handle = kmalloc(sizeof(struct semaphore), GFP_ATOMIC);
+ ter_sem_handle = kmalloc(sizeof(struct semaphore), GFP_ATOMIC);
+ if (sem_handle == NULL || ter_sem_handle == NULL) {
+ ret_val = -ENOMEM;
+ goto sem_fail;
+ }
+ sema_init(sem_handle, 0);
+ /* Create the termination semaphore */
+ sema_init(ter_sem_handle, 0);
+
+ /* Search for an available slot for user process. */
+ for (i = 0; i < MAX_PROCESSES; i++) {
+ if (notifydrv_state.event_state[i].pid == -1) {
+ notifydrv_state.event_state[i].semhandle = \
+ sem_handle;
+ notifydrv_state.event_state[i].tersemhandle = \
+ ter_sem_handle;
+ notifydrv_state.event_state[i].pid = pid;
+ notifydrv_state.event_state[i].ref_count = 1;
+ INIT_LIST_HEAD(&(notifydrv_state.event_state[i].
+ buf_list));
+ flag = true;
+ break;
+ }
+ }
+ mutex_unlock(notifydrv_state.gate_handle);
+
+ if (WARN_ON(flag != true)) {
+ /* Max users have registered. No more clients
+ * can be supported */
+ ret_val = NOTIFY_E_RESOURCE;
+ goto sem_fail;
+ }
+
+ return 0;
+
+sem_fail:
+ kfree(ter_sem_handle);
+ kfree(sem_handle);
+exit:
+ return ret_val;
+}
+
+
+/* Detach a process from notify user support framework. */
+static int notify_drv_detach(u32 pid, bool force)
+{
+ s32 status = NOTIFY_S_SUCCESS;
+ bool flag = false;
+ u32 i;
+ struct semaphore *sem_handle = NULL;
+ struct semaphore *ter_sem_handle = NULL;
+
+ if (WARN_ON(notifydrv_state.is_setup == false)) {
+ status = NOTIFY_E_FAIL;
+ goto func_end;
+ }
+
+ /* Send termination packet only if this is not a forced detach */
+ if (force == false) {
+ /* Send the termination packet to notify thread */
+ status = _notify_drv_add_buf_by_pid(0, 0, pid, (u32)-1, (u32)0,
+ NULL, NULL);
+ }
+
+ WARN_ON(mutex_lock_interruptible(notifydrv_state.gate_handle));
+ for (i = 0; i < MAX_PROCESSES; i++) {
+ if (notifydrv_state.event_state[i].pid == pid) {
+ if (notifydrv_state.event_state[i].ref_count == 1) {
+ /* Last client being unregistered for this
+ * process*/
+ notifydrv_state.event_state[i].pid = -1;
+ notifydrv_state.event_state[i].ref_count = 0;
+ sem_handle =
+ notifydrv_state.event_state[i].semhandle;
+ ter_sem_handle =
+ notifydrv_state.event_state[i].tersemhandle;
+ INIT_LIST_HEAD((struct list_head *)
+ &(notifydrv_state.event_state[i].buf_list));
+ notifydrv_state.event_state[i].semhandle =
+ NULL;
+ notifydrv_state.event_state[i].tersemhandle =
+ NULL;
+ flag = true;
+ break;
+ } else
+ notifydrv_state.event_state[i].ref_count--;
+ }
+ }
+ mutex_unlock(notifydrv_state.gate_handle);
+
+ if ((flag == false) && (i == MAX_PROCESSES)) {
+ /* The specified user process was not found registered with
+ * Notify Driver module. */
+ status = NOTIFY_E_NOTFOUND;
+ } else {
+ kfree(sem_handle);
+ kfree(ter_sem_handle);
+ }
+
+func_end:
+ return status;
+}