Linux Audio

Check our new training course

Loading...
  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}