Path: blob/master/drivers/isdn/mISDN/dsp_pipeline.c
15111 views
/*1* dsp_pipeline.c: pipelined audio processing2*3* Copyright (C) 2007, Nadi Sarrar4*5* Nadi Sarrar <[email protected]>6*7* This program is free software; you can redistribute it and/or modify it8* under the terms of the GNU General Public License as published by the Free9* Software Foundation; either version 2 of the License, or (at your option)10* any later version.11*12* This program is distributed in the hope that it will be useful, but WITHOUT13* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or14* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for15* more details.16*17* You should have received a copy of the GNU General Public License along with18* this program; if not, write to the Free Software Foundation, Inc., 5919* Temple Place - Suite 330, Boston, MA 02111-1307, USA.20*21* The full GNU General Public License is included in this distribution in the22* file called LICENSE.23*24*/2526#include <linux/kernel.h>27#include <linux/slab.h>28#include <linux/list.h>29#include <linux/string.h>30#include <linux/mISDNif.h>31#include <linux/mISDNdsp.h>32#include "dsp.h"33#include "dsp_hwec.h"3435/* uncomment for debugging */36/*#define PIPELINE_DEBUG*/3738struct dsp_pipeline_entry {39struct mISDN_dsp_element *elem;40void *p;41struct list_head list;42};43struct dsp_element_entry {44struct mISDN_dsp_element *elem;45struct device dev;46struct list_head list;47};4849static LIST_HEAD(dsp_elements);5051/* sysfs */52static struct class *elements_class;5354static ssize_t55attr_show_args(struct device *dev, struct device_attribute *attr, char *buf)56{57struct mISDN_dsp_element *elem = dev_get_drvdata(dev);58int i;59char *p = buf;6061*buf = 0;62for (i = 0; i < elem->num_args; i++)63p += sprintf(p, "Name: %s\n%s%s%sDescription: %s\n\n",64elem->args[i].name,65elem->args[i].def ? "Default: " : "",66elem->args[i].def ? elem->args[i].def : "",67elem->args[i].def ? "\n" : "",68elem->args[i].desc);6970return p - buf;71}7273static struct device_attribute element_attributes[] = {74__ATTR(args, 0444, attr_show_args, NULL),75};7677static void78mISDN_dsp_dev_release(struct device *dev)79{80struct dsp_element_entry *entry =81container_of(dev, struct dsp_element_entry, dev);82list_del(&entry->list);83kfree(entry);84}8586int mISDN_dsp_element_register(struct mISDN_dsp_element *elem)87{88struct dsp_element_entry *entry;89int ret, i;9091if (!elem)92return -EINVAL;9394entry = kzalloc(sizeof(struct dsp_element_entry), GFP_ATOMIC);95if (!entry)96return -ENOMEM;9798entry->elem = elem;99100entry->dev.class = elements_class;101entry->dev.release = mISDN_dsp_dev_release;102dev_set_drvdata(&entry->dev, elem);103dev_set_name(&entry->dev, elem->name);104ret = device_register(&entry->dev);105if (ret) {106printk(KERN_ERR "%s: failed to register %s\n",107__func__, elem->name);108goto err1;109}110list_add_tail(&entry->list, &dsp_elements);111112for (i = 0; i < ARRAY_SIZE(element_attributes); ++i) {113ret = device_create_file(&entry->dev,114&element_attributes[i]);115if (ret) {116printk(KERN_ERR "%s: failed to create device file\n",117__func__);118goto err2;119}120}121122#ifdef PIPELINE_DEBUG123printk(KERN_DEBUG "%s: %s registered\n", __func__, elem->name);124#endif125126return 0;127128err2:129device_unregister(&entry->dev);130return ret;131err1:132kfree(entry);133return ret;134}135EXPORT_SYMBOL(mISDN_dsp_element_register);136137void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem)138{139struct dsp_element_entry *entry, *n;140141if (!elem)142return;143144list_for_each_entry_safe(entry, n, &dsp_elements, list)145if (entry->elem == elem) {146device_unregister(&entry->dev);147#ifdef PIPELINE_DEBUG148printk(KERN_DEBUG "%s: %s unregistered\n",149__func__, elem->name);150#endif151return;152}153printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name);154}155EXPORT_SYMBOL(mISDN_dsp_element_unregister);156157int dsp_pipeline_module_init(void)158{159elements_class = class_create(THIS_MODULE, "dsp_pipeline");160if (IS_ERR(elements_class))161return PTR_ERR(elements_class);162163#ifdef PIPELINE_DEBUG164printk(KERN_DEBUG "%s: dsp pipeline module initialized\n", __func__);165#endif166167dsp_hwec_init();168169return 0;170}171172void dsp_pipeline_module_exit(void)173{174struct dsp_element_entry *entry, *n;175176dsp_hwec_exit();177178class_destroy(elements_class);179180list_for_each_entry_safe(entry, n, &dsp_elements, list) {181list_del(&entry->list);182printk(KERN_WARNING "%s: element was still registered: %s\n",183__func__, entry->elem->name);184kfree(entry);185}186187#ifdef PIPELINE_DEBUG188printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __func__);189#endif190}191192int dsp_pipeline_init(struct dsp_pipeline *pipeline)193{194if (!pipeline)195return -EINVAL;196197INIT_LIST_HEAD(&pipeline->list);198199#ifdef PIPELINE_DEBUG200printk(KERN_DEBUG "%s: dsp pipeline ready\n", __func__);201#endif202203return 0;204}205206static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline)207{208struct dsp_pipeline_entry *entry, *n;209210list_for_each_entry_safe(entry, n, &pipeline->list, list) {211list_del(&entry->list);212if (entry->elem == dsp_hwec)213dsp_hwec_disable(container_of(pipeline, struct dsp,214pipeline));215else216entry->elem->free(entry->p);217kfree(entry);218}219}220221void dsp_pipeline_destroy(struct dsp_pipeline *pipeline)222{223224if (!pipeline)225return;226227_dsp_pipeline_destroy(pipeline);228229#ifdef PIPELINE_DEBUG230printk(KERN_DEBUG "%s: dsp pipeline destroyed\n", __func__);231#endif232}233234int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)235{236int len, incomplete = 0, found = 0;237char *dup, *tok, *name, *args;238struct dsp_element_entry *entry, *n;239struct dsp_pipeline_entry *pipeline_entry;240struct mISDN_dsp_element *elem;241242if (!pipeline)243return -EINVAL;244245if (!list_empty(&pipeline->list))246_dsp_pipeline_destroy(pipeline);247248if (!cfg)249return 0;250251len = strlen(cfg);252if (!len)253return 0;254255dup = kmalloc(len + 1, GFP_ATOMIC);256if (!dup)257return 0;258strcpy(dup, cfg);259while ((tok = strsep(&dup, "|"))) {260if (!strlen(tok))261continue;262name = strsep(&tok, "(");263args = strsep(&tok, ")");264if (args && !*args)265args = NULL;266267list_for_each_entry_safe(entry, n, &dsp_elements, list)268if (!strcmp(entry->elem->name, name)) {269elem = entry->elem;270271pipeline_entry = kmalloc(sizeof(struct272dsp_pipeline_entry), GFP_ATOMIC);273if (!pipeline_entry) {274printk(KERN_ERR "%s: failed to add "275"entry to pipeline: %s (out of "276"memory)\n", __func__, elem->name);277incomplete = 1;278goto _out;279}280pipeline_entry->elem = elem;281282if (elem == dsp_hwec) {283/* This is a hack to make the hwec284available as a pipeline module */285dsp_hwec_enable(container_of(pipeline,286struct dsp, pipeline), args);287list_add_tail(&pipeline_entry->list,288&pipeline->list);289} else {290pipeline_entry->p = elem->new(args);291if (pipeline_entry->p) {292list_add_tail(&pipeline_entry->293list, &pipeline->list);294#ifdef PIPELINE_DEBUG295printk(KERN_DEBUG "%s: created "296"instance of %s%s%s\n",297__func__, name, args ?298" with args " : "", args ?299args : "");300#endif301} else {302printk(KERN_ERR "%s: failed "303"to add entry to pipeline: "304"%s (new() returned NULL)\n",305__func__, elem->name);306kfree(pipeline_entry);307incomplete = 1;308}309}310found = 1;311break;312}313314if (found)315found = 0;316else {317printk(KERN_ERR "%s: element not found, skipping: "318"%s\n", __func__, name);319incomplete = 1;320}321}322323_out:324if (!list_empty(&pipeline->list))325pipeline->inuse = 1;326else327pipeline->inuse = 0;328329#ifdef PIPELINE_DEBUG330printk(KERN_DEBUG "%s: dsp pipeline built%s: %s\n",331__func__, incomplete ? " incomplete" : "", cfg);332#endif333kfree(dup);334return 0;335}336337void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len)338{339struct dsp_pipeline_entry *entry;340341if (!pipeline)342return;343344list_for_each_entry(entry, &pipeline->list, list)345if (entry->elem->process_tx)346entry->elem->process_tx(entry->p, data, len);347}348349void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len,350unsigned int txlen)351{352struct dsp_pipeline_entry *entry;353354if (!pipeline)355return;356357list_for_each_entry_reverse(entry, &pipeline->list, list)358if (entry->elem->process_rx)359entry->elem->process_rx(entry->p, data, len, txlen);360}361362363364365