/* * 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(¬ifications[i], anotifications[i], status, 1); if (status || !notifications[i].handle) { status = -EINVAL; break; } /* set the array of pointers to kernel structures */ anotifications[i] = ¬ifications[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, ¬ification); CP_TO_USR(args->args_proc_register_notify.notification, ¬ification, 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(¬ification, 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, ¬ification); CP_TO_USR(args->args_node_registernotify.notification, ¬ification, 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, ¬ification); CP_TO_USR(args->args_strm_registernotify.notification, ¬ification, 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; }