android_kernel_lenovo_1050f/sound/core/effects_offload.c

317 lines
7.6 KiB
C

/*
* effect_offload.c - effects offload core
*
* Copyright (C) 2013 Intel Corporation
* Authors: Lakshmi N Vinnakota <lakshmi.n.vinnakota@intel.com>
* Vinod Koul <vinod.koul@intel.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
*/
#define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__
#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt)
#include <linux/module.h>
#include <linux/uio.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/effect_offload.h>
#include <sound/effect_driver.h>
static DEFINE_MUTEX(effect_mutex);
int snd_ctl_effect_create(struct snd_card *card, void *arg)
{
int retval = 0;
struct snd_effect *effect;
effect = kmalloc(sizeof(*effect), GFP_KERNEL);
if (!effect)
return -ENOMEM;
if (copy_from_user(effect, (void __user *)arg, sizeof(*effect))) {
retval = -EFAULT;
goto out;
}
pr_debug("effect_offload: device %u, pos %u, mode%u\n",
effect->device, effect->pos, effect->mode);
mutex_lock(&card->effect_lock);
retval = card->effect_ops->create(card, effect);
mutex_unlock(&card->effect_lock);
out:
kfree(effect);
return retval;
}
EXPORT_SYMBOL_GPL(snd_ctl_effect_create);
int snd_ctl_effect_destroy(struct snd_card *card, void *arg)
{
int retval = 0;
struct snd_effect *effect;
effect = kmalloc(sizeof(*effect), GFP_KERNEL);
if (!effect)
return -ENOMEM;
if (copy_from_user(effect, (void __user *)arg, sizeof(*effect))) {
retval = -EFAULT;
goto out;
}
mutex_lock(&card->effect_lock);
retval = card->effect_ops->destroy(card, effect);
mutex_unlock(&card->effect_lock);
out:
kfree(effect);
return retval;
}
EXPORT_SYMBOL_GPL(snd_ctl_effect_destroy);
int snd_ctl_effect_set_params(struct snd_card *card, void *arg)
{
int retval = 0;
struct snd_effect_params *params;
char *params_ptr;
char __user *argp = (char __user *)arg;
params = kmalloc(sizeof(*params), GFP_KERNEL);
if (!params)
return -ENOMEM;
if (copy_from_user(params, argp, sizeof(*params))) {
retval = -EFAULT;
goto out;
}
params_ptr = kmalloc(params->size, GFP_KERNEL);
if (!params_ptr) {
retval = -ENOMEM;
goto out;
}
if (copy_from_user((void *)params_ptr, (void __user *)params->buffer_ptr,
params->size)) {
retval = -EFAULT;
goto free_buf;
}
params->buffer_ptr = (unsigned long)params_ptr;
mutex_lock(&card->effect_lock);
retval = card->effect_ops->set_params(card, params);
mutex_unlock(&card->effect_lock);
free_buf:
kfree(params_ptr);
out:
kfree(params);
return retval;
}
EXPORT_SYMBOL_GPL(snd_ctl_effect_set_params);
int snd_ctl_effect_get_params(struct snd_card *card, void *arg)
{
int retval = 0;
struct snd_effect_params inparams;
struct snd_effect_params *outparams;
unsigned int offset;
char *params_ptr;
char __user *argp = (char __user *)arg;
if (copy_from_user((void *)&inparams, argp, sizeof(inparams)))
retval = -EFAULT;
outparams = kmalloc(sizeof(*outparams), GFP_KERNEL);
if (!outparams)
return -ENOMEM;
memcpy(outparams, &inparams, sizeof(inparams));
params_ptr = kmalloc(inparams.size, GFP_KERNEL);
if (!params_ptr) {
retval = -ENOMEM;
goto free_out;
}
if (copy_from_user((void *)params_ptr, (void *)inparams.buffer_ptr,
inparams.size)) {
retval = -EFAULT;
goto free_buf;
}
outparams->buffer_ptr = (unsigned long)params_ptr;
mutex_lock(&card->effect_lock);
retval = card->effect_ops->get_params(card, outparams);
mutex_unlock(&card->effect_lock);
if (retval)
goto free_buf;
if (!outparams->size)
goto free_buf;
if (outparams->size > inparams.size) {
pr_err("mem insufficient to copy\n");
retval = -EMSGSIZE;
goto free_buf;
} else {
offset = offsetof(struct snd_effect_params, size);
if (copy_to_user((argp + offset), (void *)&outparams->size,
sizeof(u32)))
retval = -EFAULT;
if (copy_to_user((void *)inparams.buffer_ptr,
(void *) outparams->buffer_ptr, outparams->size))
retval = -EFAULT;
}
free_buf:
kfree(params_ptr);
free_out:
kfree(outparams);
return retval;
}
EXPORT_SYMBOL_GPL(snd_ctl_effect_get_params);
int snd_ctl_effect_query_num_effects(struct snd_card *card, void *arg)
{
int retval = 0;
int __user *ip = arg;
mutex_lock(&card->effect_lock);
retval = card->effect_ops->query_num_effects(card);
mutex_unlock(&card->effect_lock);
if (retval < 0)
goto out;
retval = put_user(retval, ip) ? -EFAULT : 0;
out:
return retval;
}
EXPORT_SYMBOL_GPL(snd_ctl_effect_query_num_effects);
int snd_ctl_effect_query_effect_caps(struct snd_card *card, void *arg)
{
int retval = 0;
struct snd_effect_caps *caps;
unsigned int offset, insize;
char *caps_ptr;
char __user *argp = (char __user *)arg;
char __user *bufp;
caps = kzalloc(sizeof(*caps), GFP_KERNEL);
if (!caps)
return -ENOMEM;
if (copy_from_user(caps, argp, sizeof(*caps))) {
retval = -EFAULT;
goto out;
}
bufp = (void __user *)caps->buffer_ptr;
insize = caps->size;
caps_ptr = kmalloc(caps->size, GFP_KERNEL);
if (!caps_ptr) {
retval = -ENOMEM;
goto out;
}
caps->buffer_ptr = (unsigned long)caps_ptr;
mutex_lock(&card->effect_lock);
retval = card->effect_ops->query_effect_caps(card, caps);
mutex_unlock(&card->effect_lock);
if (retval)
goto free_buf;
if (insize < caps->size) {
pr_err("mem insufficient to copy\n");
retval = -EMSGSIZE;
goto free_buf;
}
offset = offsetof(struct snd_effect_caps, size);
if (copy_to_user((argp + offset), (void *)&caps->size, sizeof(u32))) {
retval = -EFAULT;
goto free_buf;
}
if (copy_to_user(bufp, (void *)caps->buffer_ptr, caps->size))
retval = -EFAULT;
free_buf:
kfree(caps_ptr);
out:
kfree(caps);
return retval;
}
EXPORT_SYMBOL_GPL(snd_ctl_effect_query_effect_caps);
/**
* snd_effect_register - register compressed device
*
* @card : snd card to which the effect is registered
* @ops : effect_ops to register
*/
int snd_effect_register(struct snd_card *card, struct snd_effect_ops *ops)
{
if (card == NULL || ops == NULL)
return -EINVAL;
if (snd_BUG_ON(!ops->create))
return -EINVAL;
if (snd_BUG_ON(!ops->destroy))
return -EINVAL;
if (snd_BUG_ON(!ops->set_params))
return -EINVAL;
mutex_init(&card->effect_lock);
pr_debug("Registering Effects to card %s\n", card->shortname);
/* register the effect ops with the card */
mutex_lock(&effect_mutex);
card->effect_ops = ops;
mutex_unlock(&effect_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_effect_register);
int snd_effect_deregister(struct snd_card *card)
{
pr_debug("Removing effects for card %s\n", card->shortname);
mutex_lock(&effect_mutex);
card->effect_ops = NULL;
mutex_unlock(&effect_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_effect_deregister);
static int __init snd_effect_init(void)
{
return 0;
}
static void __exit snd_effect_exit(void)
{
}
module_init(snd_effect_init);
module_exit(snd_effect_exit);
MODULE_DESCRIPTION("ALSA Effect offload framework");
MODULE_AUTHOR("Lakshmi N Vinnakota <lakshmi.n.vinnakota@intel.com>");
MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
MODULE_LICENSE("GPL v2");