linux/drivers/isdn/mISDN/dsp_pipeline.c
<<
>>
Prefs
   1/*
   2 * dsp_pipeline.c: pipelined audio processing
   3 *
   4 * Copyright (C) 2007, Nadi Sarrar
   5 *
   6 * Nadi Sarrar <nadi@beronet.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify it
   9 * under the terms of the GNU General Public License as published by the Free
  10 * Software Foundation; either version 2 of the License, or (at your option)
  11 * any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful, but WITHOUT
  14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  16 * more details.
  17 *
  18 * You should have received a copy of the GNU General Public License along with
  19 * this program; if not, write to the Free Software Foundation, Inc., 59
  20 * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  21 *
  22 * The full GNU General Public License is included in this distribution in the
  23 * file called LICENSE.
  24 *
  25 */
  26
  27#include <linux/kernel.h>
  28#include <linux/slab.h>
  29#include <linux/list.h>
  30#include <linux/string.h>
  31#include <linux/mISDNif.h>
  32#include <linux/mISDNdsp.h>
  33#include <linux/export.h>
  34#include "dsp.h"
  35#include "dsp_hwec.h"
  36
  37/* uncomment for debugging */
  38/*#define PIPELINE_DEBUG*/
  39
  40struct dsp_pipeline_entry {
  41        struct mISDN_dsp_element *elem;
  42        void                *p;
  43        struct list_head     list;
  44};
  45struct dsp_element_entry {
  46        struct mISDN_dsp_element *elem;
  47        struct device        dev;
  48        struct list_head     list;
  49};
  50
  51static LIST_HEAD(dsp_elements);
  52
  53/* sysfs */
  54static struct class *elements_class;
  55
  56static ssize_t
  57attr_show_args(struct device *dev, struct device_attribute *attr, char *buf)
  58{
  59        struct mISDN_dsp_element *elem = dev_get_drvdata(dev);
  60        int i;
  61        char *p = buf;
  62
  63        *buf = 0;
  64        for (i = 0; i < elem->num_args; i++)
  65                p += sprintf(p, "Name:        %s\n%s%s%sDescription: %s\n\n",
  66                             elem->args[i].name,
  67                             elem->args[i].def ? "Default:     " : "",
  68                             elem->args[i].def ? elem->args[i].def : "",
  69                             elem->args[i].def ? "\n" : "",
  70                             elem->args[i].desc);
  71
  72        return p - buf;
  73}
  74
  75static struct device_attribute element_attributes[] = {
  76        __ATTR(args, 0444, attr_show_args, NULL),
  77};
  78
  79static void
  80mISDN_dsp_dev_release(struct device *dev)
  81{
  82        struct dsp_element_entry *entry =
  83                container_of(dev, struct dsp_element_entry, dev);
  84        list_del(&entry->list);
  85        kfree(entry);
  86}
  87
  88int mISDN_dsp_element_register(struct mISDN_dsp_element *elem)
  89{
  90        struct dsp_element_entry *entry;
  91        int ret, i;
  92
  93        if (!elem)
  94                return -EINVAL;
  95
  96        entry = kzalloc(sizeof(struct dsp_element_entry), GFP_ATOMIC);
  97        if (!entry)
  98                return -ENOMEM;
  99
 100        entry->elem = elem;
 101
 102        entry->dev.class = elements_class;
 103        entry->dev.release = mISDN_dsp_dev_release;
 104        dev_set_drvdata(&entry->dev, elem);
 105        dev_set_name(&entry->dev, "%s", elem->name);
 106        ret = device_register(&entry->dev);
 107        if (ret) {
 108                printk(KERN_ERR "%s: failed to register %s\n",
 109                       __func__, elem->name);
 110                goto err1;
 111        }
 112        list_add_tail(&entry->list, &dsp_elements);
 113
 114        for (i = 0; i < ARRAY_SIZE(element_attributes); ++i) {
 115                ret = device_create_file(&entry->dev,
 116                                         &element_attributes[i]);
 117                if (ret) {
 118                        printk(KERN_ERR "%s: failed to create device file\n",
 119                               __func__);
 120                        goto err2;
 121                }
 122        }
 123
 124#ifdef PIPELINE_DEBUG
 125        printk(KERN_DEBUG "%s: %s registered\n", __func__, elem->name);
 126#endif
 127
 128        return 0;
 129
 130err2:
 131        device_unregister(&entry->dev);
 132        return ret;
 133err1:
 134        kfree(entry);
 135        return ret;
 136}
 137EXPORT_SYMBOL(mISDN_dsp_element_register);
 138
 139void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem)
 140{
 141        struct dsp_element_entry *entry, *n;
 142
 143        if (!elem)
 144                return;
 145
 146        list_for_each_entry_safe(entry, n, &dsp_elements, list)
 147                if (entry->elem == elem) {
 148                        device_unregister(&entry->dev);
 149#ifdef PIPELINE_DEBUG
 150                        printk(KERN_DEBUG "%s: %s unregistered\n",
 151                               __func__, elem->name);
 152#endif
 153                        return;
 154                }
 155        printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name);
 156}
 157EXPORT_SYMBOL(mISDN_dsp_element_unregister);
 158
 159int dsp_pipeline_module_init(void)
 160{
 161        elements_class = class_create(THIS_MODULE, "dsp_pipeline");
 162        if (IS_ERR(elements_class))
 163                return PTR_ERR(elements_class);
 164
 165#ifdef PIPELINE_DEBUG
 166        printk(KERN_DEBUG "%s: dsp pipeline module initialized\n", __func__);
 167#endif
 168
 169        dsp_hwec_init();
 170
 171        return 0;
 172}
 173
 174void dsp_pipeline_module_exit(void)
 175{
 176        struct dsp_element_entry *entry, *n;
 177
 178        dsp_hwec_exit();
 179
 180        class_destroy(elements_class);
 181
 182        list_for_each_entry_safe(entry, n, &dsp_elements, list) {
 183                list_del(&entry->list);
 184                printk(KERN_WARNING "%s: element was still registered: %s\n",
 185                       __func__, entry->elem->name);
 186                kfree(entry);
 187        }
 188
 189#ifdef PIPELINE_DEBUG
 190        printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __func__);
 191#endif
 192}
 193
 194int dsp_pipeline_init(struct dsp_pipeline *pipeline)
 195{
 196        if (!pipeline)
 197                return -EINVAL;
 198
 199        INIT_LIST_HEAD(&pipeline->list);
 200
 201#ifdef PIPELINE_DEBUG
 202        printk(KERN_DEBUG "%s: dsp pipeline ready\n", __func__);
 203#endif
 204
 205        return 0;
 206}
 207
 208static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
 209{
 210        struct dsp_pipeline_entry *entry, *n;
 211
 212        list_for_each_entry_safe(entry, n, &pipeline->list, list) {
 213                list_del(&entry->list);
 214                if (entry->elem == dsp_hwec)
 215                        dsp_hwec_disable(container_of(pipeline, struct dsp,
 216                                                      pipeline));
 217                else
 218                        entry->elem->free(entry->p);
 219                kfree(entry);
 220        }
 221}
 222
 223void dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
 224{
 225
 226        if (!pipeline)
 227                return;
 228
 229        _dsp_pipeline_destroy(pipeline);
 230
 231#ifdef PIPELINE_DEBUG
 232        printk(KERN_DEBUG "%s: dsp pipeline destroyed\n", __func__);
 233#endif
 234}
 235
 236int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
 237{
 238        int incomplete = 0, found = 0;
 239        char *dup, *tok, *name, *args;
 240        struct dsp_element_entry *entry, *n;
 241        struct dsp_pipeline_entry *pipeline_entry;
 242        struct mISDN_dsp_element *elem;
 243
 244        if (!pipeline)
 245                return -EINVAL;
 246
 247        if (!list_empty(&pipeline->list))
 248                _dsp_pipeline_destroy(pipeline);
 249
 250        dup = kstrdup(cfg, GFP_ATOMIC);
 251        if (!dup)
 252                return 0;
 253        while ((tok = strsep(&dup, "|"))) {
 254                if (!strlen(tok))
 255                        continue;
 256                name = strsep(&tok, "(");
 257                args = strsep(&tok, ")");
 258                if (args && !*args)
 259                        args = NULL;
 260
 261                list_for_each_entry_safe(entry, n, &dsp_elements, list)
 262                        if (!strcmp(entry->elem->name, name)) {
 263                                elem = entry->elem;
 264
 265                                pipeline_entry = kmalloc(sizeof(struct
 266                                                                dsp_pipeline_entry), GFP_ATOMIC);
 267                                if (!pipeline_entry) {
 268                                        printk(KERN_ERR "%s: failed to add "
 269                                               "entry to pipeline: %s (out of "
 270                                               "memory)\n", __func__, elem->name);
 271                                        incomplete = 1;
 272                                        goto _out;
 273                                }
 274                                pipeline_entry->elem = elem;
 275
 276                                if (elem == dsp_hwec) {
 277                                        /* This is a hack to make the hwec
 278                                           available as a pipeline module */
 279                                        dsp_hwec_enable(container_of(pipeline,
 280                                                                     struct dsp, pipeline), args);
 281                                        list_add_tail(&pipeline_entry->list,
 282                                                      &pipeline->list);
 283                                } else {
 284                                        pipeline_entry->p = elem->new(args);
 285                                        if (pipeline_entry->p) {
 286                                                list_add_tail(&pipeline_entry->
 287                                                              list, &pipeline->list);
 288#ifdef PIPELINE_DEBUG
 289                                                printk(KERN_DEBUG "%s: created "
 290                                                       "instance of %s%s%s\n",
 291                                                       __func__, name, args ?
 292                                                       " with args " : "", args ?
 293                                                       args : "");
 294#endif
 295                                        } else {
 296                                                printk(KERN_ERR "%s: failed "
 297                                                       "to add entry to pipeline: "
 298                                                       "%s (new() returned NULL)\n",
 299                                                       __func__, elem->name);
 300                                                kfree(pipeline_entry);
 301                                                incomplete = 1;
 302                                        }
 303                                }
 304                                found = 1;
 305                                break;
 306                        }
 307
 308                if (found)
 309                        found = 0;
 310                else {
 311                        printk(KERN_ERR "%s: element not found, skipping: "
 312                               "%s\n", __func__, name);
 313                        incomplete = 1;
 314                }
 315        }
 316
 317_out:
 318        if (!list_empty(&pipeline->list))
 319                pipeline->inuse = 1;
 320        else
 321                pipeline->inuse = 0;
 322
 323#ifdef PIPELINE_DEBUG
 324        printk(KERN_DEBUG "%s: dsp pipeline built%s: %s\n",
 325               __func__, incomplete ? " incomplete" : "", cfg);
 326#endif
 327        kfree(dup);
 328        return 0;
 329}
 330
 331void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len)
 332{
 333        struct dsp_pipeline_entry *entry;
 334
 335        if (!pipeline)
 336                return;
 337
 338        list_for_each_entry(entry, &pipeline->list, list)
 339                if (entry->elem->process_tx)
 340                        entry->elem->process_tx(entry->p, data, len);
 341}
 342
 343void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len,
 344                             unsigned int txlen)
 345{
 346        struct dsp_pipeline_entry *entry;
 347
 348        if (!pipeline)
 349                return;
 350
 351        list_for_each_entry_reverse(entry, &pipeline->list, list)
 352                if (entry->elem->process_rx)
 353                        entry->elem->process_rx(entry->p, data, len, txlen);
 354}
 355