diff options
Diffstat (limited to 'drivers/isdn/mISDN/dsp_pipeline.c')
-rw-r--r-- | drivers/isdn/mISDN/dsp_pipeline.c | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c new file mode 100644 index 000000000000..850260ab57d0 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_pipeline.c | |||
@@ -0,0 +1,348 @@ | |||
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 | |||