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