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, 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 len, 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        if (!cfg)
 251                return 0;
 252
 253        len = strlen(cfg);
 254        if (!len)
 255                return 0;
 256
 257        dup = kmalloc(len + 1, GFP_ATOMIC);
 258        if (!dup)
 259                return 0;
 260        strcpy(dup, cfg);
 261        while ((tok = strsep(&dup, "|"))) {
 262                if (!strlen(tok))
 263                        continue;
 264                name = strsep(&tok, "(");
 265                args = strsep(&tok, ")");
 266                if (args && !*args)
 267                        args = NULL;
 268
 269                list_for_each_entry_safe(entry, n, &dsp_elements, list)
 270                        if (!strcmp(entry->elem->name, name)) {
 271                                elem = entry->elem;
 272
 273                                pipeline_entry = kmalloc(sizeof(struct
 274                                                                dsp_pipeline_entry), GFP_ATOMIC);
 275                                if (!pipeline_entry) {
 276                                        printk(KERN_ERR "%s: failed to add "
 277                                               "entry to pipeline: %s (out of "
 278                                               "memory)\n", __func__, elem->name);
 279                                        incomplete = 1;
 280                                        goto _out;
 281                                }
 282                                pipeline_entry->elem = elem;
 283
 284                                if (elem == dsp_hwec) {
 285                                        /* This is a hack to make the hwec
 286                                           available as a pipeline module */
 287                                        dsp_hwec_enable(container_of(pipeline,
 288                                                                     struct dsp, pipeline), args);
 289                                        list_add_tail(&pipeline_entry->list,
 290                                                      &pipeline->list);
 291                                } else {
 292                                        pipeline_entry->p = elem->new(args);
 293                                        if (pipeline_entry->p) {
 294                                                list_add_tail(&pipeline_entry->
 295                                                              list, &pipeline->list);
 296#ifdef PIPELINE_DEBUG
 297                                                printk(KERN_DEBUG "%s: created "
 298                                                       "instance of %s%s%s\n",
 299                                                       __func__, name, args ?
 300                                                       " with args " : "", args ?
 301                                                       args : "");
 302#endif
 303                                        } else {
 304                                                printk(KERN_ERR "%s: failed "
 305                                                       "to add entry to pipeline: "
 306                                                       "%s (new() returned NULL)\n",
 307                                                       __func__, elem->name);
 308                                                kfree(pipeline_entry);
 309                                                incomplete = 1;
 310                                        }
 311                                }
 312                                found = 1;
 313                                break;
 314                        }
 315
 316                if (found)
 317                        found = 0;
 318                else {
 319                        printk(KERN_ERR "%s: element not found, skipping: "
 320                               "%s\n", __func__, name);
 321                        incomplete = 1;
 322                }
 323        }
 324
 325_out:
 326        if (!list_empty(&pipeline->list))
 327                pipeline->inuse = 1;
 328        else
 329                pipeline->inuse = 0;
 330
 331#ifdef PIPELINE_DEBUG
 332        printk(KERN_DEBUG "%s: dsp pipeline built%s: %s\n",
 333               __func__, incomplete ? " incomplete" : "", cfg);
 334#endif
 335        kfree(dup);
 336        return 0;
 337}
 338
 339void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len)
 340{
 341        struct dsp_pipeline_entry *entry;
 342
 343        if (!pipeline)
 344                return;
 345
 346        list_for_each_entry(entry, &pipeline->list, list)
 347                if (entry->elem->process_tx)
 348                        entry->elem->process_tx(entry->p, data, len);
 349}
 350
 351void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len,
 352                             unsigned int txlen)
 353{
 354        struct dsp_pipeline_entry *entry;
 355
 356        if (!pipeline)
 357                return;
 358
 359        list_for_each_entry_reverse(entry, &pipeline->list, list)
 360                if (entry->elem->process_rx)
 361                        entry->elem->process_rx(entry->p, data, len, txlen);
 362}
 363