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/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"
  34
  35/* uncomment for debugging */
  36/*#define PIPELINE_DEBUG*/
  37
  38struct dsp_pipeline_entry {
  39        struct mISDN_dsp_element *elem;
  40        void                *p;
  41        struct list_head     list;
  42};
  43struct dsp_element_entry {
  44        struct mISDN_dsp_element *elem;
  45        struct device        dev;
  46        struct list_head     list;
  47};
  48
  49static LIST_HEAD(dsp_elements);
  50
  51/* sysfs */
  52static struct class *elements_class;
  53
  54static ssize_t
  55attr_show_args(struct device *dev, struct device_attribute *attr, char *buf)
  56{
  57        struct mISDN_dsp_element *elem = dev_get_drvdata(dev);
  58        int i;
  59        char *p = buf;
  60
  61        *buf = 0;
  62        for (i = 0; i < elem->num_args; i++)
  63                p += sprintf(p, "Name:        %s\n%s%s%sDescription: %s\n\n",
  64                          elem->args[i].name,
  65                          elem->args[i].def ? "Default:     " : "",
  66                          elem->args[i].def ? elem->args[i].def : "",
  67                          elem->args[i].def ? "\n" : "",
  68                          elem->args[i].desc);
  69
  70        return p - buf;
  71}
  72
  73static struct device_attribute element_attributes[] = {
  74        __ATTR(args, 0444, attr_show_args, NULL),
  75};
  76
  77static void
  78mISDN_dsp_dev_release(struct device *dev)
  79{
  80        struct dsp_element_entry *entry =
  81                container_of(dev, struct dsp_element_entry, dev);
  82        list_del(&entry->list);
  83        kfree(entry);
  84}
  85
  86int mISDN_dsp_element_register(struct mISDN_dsp_element *elem)
  87{
  88        struct dsp_element_entry *entry;
  89        int ret, i;
  90
  91        if (!elem)
  92                return -EINVAL;
  93
  94        entry = kzalloc(sizeof(struct dsp_element_entry), GFP_ATOMIC);
  95        if (!entry)
  96                return -ENOMEM;
  97
  98        entry->elem = elem;
  99
 100        entry->dev.class = elements_class;
 101        entry->dev.release = mISDN_dsp_dev_release;
 102        dev_set_drvdata(&entry->dev, elem);
 103        dev_set_name(&entry->dev, elem->name);
 104        ret = device_register(&entry->dev);
 105        if (ret) {
 106                printk(KERN_ERR "%s: failed to register %s\n",
 107                        __func__, elem->name);
 108                goto err1;
 109        }
 110        list_add_tail(&entry->list, &dsp_elements);
 111
 112        for (i = 0; i < ARRAY_SIZE(element_attributes); ++i) {
 113                ret = device_create_file(&entry->dev,
 114                                &element_attributes[i]);
 115                if (ret) {
 116                        printk(KERN_ERR "%s: failed to create device file\n",
 117                                __func__);
 118                        goto err2;
 119                }
 120        }
 121
 122#ifdef PIPELINE_DEBUG
 123        printk(KERN_DEBUG "%s: %s registered\n", __func__, elem->name);
 124#endif
 125
 126        return 0;
 127
 128err2:
 129        device_unregister(&entry->dev);
 130        return ret;
 131err1:
 132        kfree(entry);
 133        return ret;
 134}
 135EXPORT_SYMBOL(mISDN_dsp_element_register);
 136
 137void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem)
 138{
 139        struct dsp_element_entry *entry, *n;
 140
 141        if (!elem)
 142                return;
 143
 144        list_for_each_entry_safe(entry, n, &dsp_elements, list)
 145                if (entry->elem == elem) {
 146                        device_unregister(&entry->dev);
 147#ifdef PIPELINE_DEBUG
 148                        printk(KERN_DEBUG "%s: %s unregistered\n",
 149                                __func__, elem->name);
 150#endif
 151                        return;
 152                }
 153        printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name);
 154}
 155EXPORT_SYMBOL(mISDN_dsp_element_unregister);
 156
 157int dsp_pipeline_module_init(void)
 158{
 159        elements_class = class_create(THIS_MODULE, "dsp_pipeline");
 160        if (IS_ERR(elements_class))
 161                return PTR_ERR(elements_class);
 162
 163#ifdef PIPELINE_DEBUG
 164        printk(KERN_DEBUG "%s: dsp pipeline module initialized\n", __func__);
 165#endif
 166
 167        dsp_hwec_init();
 168
 169        return 0;
 170}
 171
 172void dsp_pipeline_module_exit(void)
 173{
 174        struct dsp_element_entry *entry, *n;
 175
 176        dsp_hwec_exit();
 177
 178        class_destroy(elements_class);
 179
 180        list_for_each_entry_safe(entry, n, &dsp_elements, list) {
 181                list_del(&entry->list);
 182                printk(KERN_WARNING "%s: element was still registered: %s\n",
 183                        __func__, entry->elem->name);
 184                kfree(entry);
 185        }
 186
 187#ifdef PIPELINE_DEBUG
 188        printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __func__);
 189#endif
 190}
 191
 192int dsp_pipeline_init(struct dsp_pipeline *pipeline)
 193{
 194        if (!pipeline)
 195                return -EINVAL;
 196
 197        INIT_LIST_HEAD(&pipeline->list);
 198
 199#ifdef PIPELINE_DEBUG
 200        printk(KERN_DEBUG "%s: dsp pipeline ready\n", __func__);
 201#endif
 202
 203        return 0;
 204}
 205
 206static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
 207{
 208        struct dsp_pipeline_entry *entry, *n;
 209
 210        list_for_each_entry_safe(entry, n, &pipeline->list, list) {
 211                list_del(&entry->list);
 212                if (entry->elem == dsp_hwec)
 213                        dsp_hwec_disable(container_of(pipeline, struct dsp,
 214                                pipeline));
 215                else
 216                        entry->elem->free(entry->p);
 217                kfree(entry);
 218        }
 219}
 220
 221void dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
 222{
 223
 224        if (!pipeline)
 225                return;
 226
 227        _dsp_pipeline_destroy(pipeline);
 228
 229#ifdef PIPELINE_DEBUG
 230        printk(KERN_DEBUG "%s: dsp pipeline destroyed\n", __func__);
 231#endif
 232}
 233
 234int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
 235{
 236        int len, incomplete = 0, found = 0;
 237        char *dup, *tok, *name, *args;
 238        struct dsp_element_entry *entry, *n;
 239        struct dsp_pipeline_entry *pipeline_entry;
 240        struct mISDN_dsp_element *elem;
 241
 242        if (!pipeline)
 243                return -EINVAL;
 244
 245        if (!list_empty(&pipeline->list))
 246                _dsp_pipeline_destroy(pipeline);
 247
 248        if (!cfg)
 249                return 0;
 250
 251        len = strlen(cfg);
 252        if (!len)
 253                return 0;
 254
 255        dup = kmalloc(len + 1, GFP_ATOMIC);
 256        if (!dup)
 257                return 0;
 258        strcpy(dup, cfg);
 259        while ((tok = strsep(&dup, "|"))) {
 260                if (!strlen(tok))
 261                        continue;
 262                name = strsep(&tok, "(");
 263                args = strsep(&tok, ")");
 264                if (args && !*args)
 265                        args = NULL;
 266
 267                list_for_each_entry_safe(entry, n, &dsp_elements, list)
 268                        if (!strcmp(entry->elem->name, name)) {
 269                                elem = entry->elem;
 270
 271                                pipeline_entry = kmalloc(sizeof(struct
 272                                        dsp_pipeline_entry), GFP_ATOMIC);
 273                                if (!pipeline_entry) {
 274                                        printk(KERN_ERR "%s: failed to add "
 275                                            "entry to pipeline: %s (out of "
 276                                            "memory)\n", __func__, elem->name);
 277                                        incomplete = 1;
 278                                        goto _out;
 279                                }
 280                                pipeline_entry->elem = elem;
 281
 282                                if (elem == dsp_hwec) {
 283                                        /* This is a hack to make the hwec
 284                                           available as a pipeline module */
 285                                        dsp_hwec_enable(container_of(pipeline,
 286                                                struct dsp, pipeline), args);
 287                                        list_add_tail(&pipeline_entry->list,
 288                                                &pipeline->list);
 289                                } else {
 290                                        pipeline_entry->p = elem->new(args);
 291                                        if (pipeline_entry->p) {
 292                                                list_add_tail(&pipeline_entry->
 293                                                        list, &pipeline->list);
 294#ifdef PIPELINE_DEBUG
 295                                                printk(KERN_DEBUG "%s: created "
 296                                                    "instance of %s%s%s\n",
 297                                                    __func__, name, args ?
 298                                                    " with args " : "", args ?
 299                                                    args : "");
 300#endif
 301                                        } else {
 302                                                printk(KERN_ERR "%s: failed "
 303                                                  "to add entry to pipeline: "
 304                                                  "%s (new() returned NULL)\n",
 305                                                  __func__, elem->name);
 306                                                kfree(pipeline_entry);
 307                                                incomplete = 1;
 308                                        }
 309                                }
 310                                found = 1;
 311                                break;
 312                        }
 313
 314                if (found)
 315                        found = 0;
 316                else {
 317                        printk(KERN_ERR "%s: element not found, skipping: "
 318                                "%s\n", __func__, name);
 319                        incomplete = 1;
 320                }
 321        }
 322
 323_out:
 324        if (!list_empty(&pipeline->list))
 325                pipeline->inuse = 1;
 326        else
 327                pipeline->inuse = 0;
 328
 329#ifdef PIPELINE_DEBUG
 330        printk(KERN_DEBUG "%s: dsp pipeline built%s: %s\n",
 331                __func__, incomplete ? " incomplete" : "", cfg);
 332#endif
 333        kfree(dup);
 334        return 0;
 335}
 336
 337void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len)
 338{
 339        struct dsp_pipeline_entry *entry;
 340
 341        if (!pipeline)
 342                return;
 343
 344        list_for_each_entry(entry, &pipeline->list, list)
 345                if (entry->elem->process_tx)
 346                        entry->elem->process_tx(entry->p, data, len);
 347}
 348
 349void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len,
 350        unsigned int txlen)
 351{
 352        struct dsp_pipeline_entry *entry;
 353
 354        if (!pipeline)
 355                return;
 356
 357        list_for_each_entry_reverse(entry, &pipeline->list, list)
 358                if (entry->elem->process_rx)
 359                        entry->elem->process_rx(entry->p, data, len, txlen);
 360}
 361
 362
 363