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 
38 struct dsp_pipeline_entry {
39 	struct mISDN_dsp_element *elem;
40 	void                *p;
41 	struct list_head     list;
42 };
43 struct dsp_element_entry {
44 	struct mISDN_dsp_element *elem;
45 	struct device	     dev;
46 	struct list_head     list;
47 };
48 
49 static LIST_HEAD(dsp_elements);
50 
51 /* sysfs */
52 static struct class *elements_class;
53 
54 static ssize_t
55 attr_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 
73 static struct device_attribute element_attributes[] = {
74 	__ATTR(args, 0444, attr_show_args, NULL),
75 };
76 
77 static void
78 mISDN_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 
86 int 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 
128 err2:
129 	device_unregister(&entry->dev);
130 	return ret;
131 err1:
132 	kfree(entry);
133 	return ret;
134 }
135 EXPORT_SYMBOL(mISDN_dsp_element_register);
136 
137 void 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 }
155 EXPORT_SYMBOL(mISDN_dsp_element_unregister);
156 
157 int 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 
172 void 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 
192 int 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 
206 static 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 
221 void 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 
234 int 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 
337 void 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 
349 void 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