1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * dsp_pipeline.c: pipelined audio processing
4  *
5  * Copyright (C) 2007, Nadi Sarrar
6  *
7  * Nadi Sarrar <nadi@beronet.com>
8  */
9 
10 #include <linux/kernel.h>
11 #include <linux/slab.h>
12 #include <linux/list.h>
13 #include <linux/string.h>
14 #include <linux/mISDNif.h>
15 #include <linux/mISDNdsp.h>
16 #include <linux/export.h>
17 #include "dsp.h"
18 #include "dsp_hwec.h"
19 
20 /* uncomment for debugging */
21 /*#define PIPELINE_DEBUG*/
22 
23 struct dsp_pipeline_entry {
24 	struct mISDN_dsp_element *elem;
25 	void                *p;
26 	struct list_head     list;
27 };
28 struct dsp_element_entry {
29 	struct mISDN_dsp_element *elem;
30 	struct device	     dev;
31 	struct list_head     list;
32 };
33 
34 static LIST_HEAD(dsp_elements);
35 
36 /* sysfs */
37 static struct class *elements_class;
38 
39 static ssize_t
40 attr_show_args(struct device *dev, struct device_attribute *attr, char *buf)
41 {
42 	struct mISDN_dsp_element *elem = dev_get_drvdata(dev);
43 	int i;
44 	char *p = buf;
45 
46 	*buf = 0;
47 	for (i = 0; i < elem->num_args; i++)
48 		p += sprintf(p, "Name:        %s\n%s%s%sDescription: %s\n\n",
49 			     elem->args[i].name,
50 			     elem->args[i].def ? "Default:     " : "",
51 			     elem->args[i].def ? elem->args[i].def : "",
52 			     elem->args[i].def ? "\n" : "",
53 			     elem->args[i].desc);
54 
55 	return p - buf;
56 }
57 
58 static struct device_attribute element_attributes[] = {
59 	__ATTR(args, 0444, attr_show_args, NULL),
60 };
61 
62 static void
63 mISDN_dsp_dev_release(struct device *dev)
64 {
65 	struct dsp_element_entry *entry =
66 		container_of(dev, struct dsp_element_entry, dev);
67 	list_del(&entry->list);
68 	kfree(entry);
69 }
70 
71 int mISDN_dsp_element_register(struct mISDN_dsp_element *elem)
72 {
73 	struct dsp_element_entry *entry;
74 	int ret, i;
75 
76 	if (!elem)
77 		return -EINVAL;
78 
79 	entry = kzalloc(sizeof(struct dsp_element_entry), GFP_ATOMIC);
80 	if (!entry)
81 		return -ENOMEM;
82 
83 	entry->elem = elem;
84 
85 	entry->dev.class = elements_class;
86 	entry->dev.release = mISDN_dsp_dev_release;
87 	dev_set_drvdata(&entry->dev, elem);
88 	dev_set_name(&entry->dev, "%s", elem->name);
89 	ret = device_register(&entry->dev);
90 	if (ret) {
91 		printk(KERN_ERR "%s: failed to register %s\n",
92 		       __func__, elem->name);
93 		goto err1;
94 	}
95 	list_add_tail(&entry->list, &dsp_elements);
96 
97 	for (i = 0; i < ARRAY_SIZE(element_attributes); ++i) {
98 		ret = device_create_file(&entry->dev,
99 					 &element_attributes[i]);
100 		if (ret) {
101 			printk(KERN_ERR "%s: failed to create device file\n",
102 			       __func__);
103 			goto err2;
104 		}
105 	}
106 
107 #ifdef PIPELINE_DEBUG
108 	printk(KERN_DEBUG "%s: %s registered\n", __func__, elem->name);
109 #endif
110 
111 	return 0;
112 
113 err2:
114 	device_unregister(&entry->dev);
115 	return ret;
116 err1:
117 	kfree(entry);
118 	return ret;
119 }
120 EXPORT_SYMBOL(mISDN_dsp_element_register);
121 
122 void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem)
123 {
124 	struct dsp_element_entry *entry, *n;
125 
126 	if (!elem)
127 		return;
128 
129 	list_for_each_entry_safe(entry, n, &dsp_elements, list)
130 		if (entry->elem == elem) {
131 			device_unregister(&entry->dev);
132 #ifdef PIPELINE_DEBUG
133 			printk(KERN_DEBUG "%s: %s unregistered\n",
134 			       __func__, elem->name);
135 #endif
136 			return;
137 		}
138 	printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name);
139 }
140 EXPORT_SYMBOL(mISDN_dsp_element_unregister);
141 
142 int dsp_pipeline_module_init(void)
143 {
144 	elements_class = class_create(THIS_MODULE, "dsp_pipeline");
145 	if (IS_ERR(elements_class))
146 		return PTR_ERR(elements_class);
147 
148 #ifdef PIPELINE_DEBUG
149 	printk(KERN_DEBUG "%s: dsp pipeline module initialized\n", __func__);
150 #endif
151 
152 	dsp_hwec_init();
153 
154 	return 0;
155 }
156 
157 void dsp_pipeline_module_exit(void)
158 {
159 	struct dsp_element_entry *entry, *n;
160 
161 	dsp_hwec_exit();
162 
163 	class_destroy(elements_class);
164 
165 	list_for_each_entry_safe(entry, n, &dsp_elements, list) {
166 		list_del(&entry->list);
167 		printk(KERN_WARNING "%s: element was still registered: %s\n",
168 		       __func__, entry->elem->name);
169 		kfree(entry);
170 	}
171 
172 #ifdef PIPELINE_DEBUG
173 	printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __func__);
174 #endif
175 }
176 
177 int dsp_pipeline_init(struct dsp_pipeline *pipeline)
178 {
179 	if (!pipeline)
180 		return -EINVAL;
181 
182 	INIT_LIST_HEAD(&pipeline->list);
183 
184 #ifdef PIPELINE_DEBUG
185 	printk(KERN_DEBUG "%s: dsp pipeline ready\n", __func__);
186 #endif
187 
188 	return 0;
189 }
190 
191 static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
192 {
193 	struct dsp_pipeline_entry *entry, *n;
194 
195 	list_for_each_entry_safe(entry, n, &pipeline->list, list) {
196 		list_del(&entry->list);
197 		if (entry->elem == dsp_hwec)
198 			dsp_hwec_disable(container_of(pipeline, struct dsp,
199 						      pipeline));
200 		else
201 			entry->elem->free(entry->p);
202 		kfree(entry);
203 	}
204 }
205 
206 void dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
207 {
208 
209 	if (!pipeline)
210 		return;
211 
212 	_dsp_pipeline_destroy(pipeline);
213 
214 #ifdef PIPELINE_DEBUG
215 	printk(KERN_DEBUG "%s: dsp pipeline destroyed\n", __func__);
216 #endif
217 }
218 
219 int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
220 {
221 	int incomplete = 0, found = 0;
222 	char *dup, *tok, *name, *args;
223 	struct dsp_element_entry *entry, *n;
224 	struct dsp_pipeline_entry *pipeline_entry;
225 	struct mISDN_dsp_element *elem;
226 
227 	if (!pipeline)
228 		return -EINVAL;
229 
230 	if (!list_empty(&pipeline->list))
231 		_dsp_pipeline_destroy(pipeline);
232 
233 	dup = kstrdup(cfg, GFP_ATOMIC);
234 	if (!dup)
235 		return 0;
236 	while ((tok = strsep(&dup, "|"))) {
237 		if (!strlen(tok))
238 			continue;
239 		name = strsep(&tok, "(");
240 		args = strsep(&tok, ")");
241 		if (args && !*args)
242 			args = NULL;
243 
244 		list_for_each_entry_safe(entry, n, &dsp_elements, list)
245 			if (!strcmp(entry->elem->name, name)) {
246 				elem = entry->elem;
247 
248 				pipeline_entry = kmalloc(sizeof(struct
249 								dsp_pipeline_entry), GFP_ATOMIC);
250 				if (!pipeline_entry) {
251 					printk(KERN_ERR "%s: failed to add "
252 					       "entry to pipeline: %s (out of "
253 					       "memory)\n", __func__, elem->name);
254 					incomplete = 1;
255 					goto _out;
256 				}
257 				pipeline_entry->elem = elem;
258 
259 				if (elem == dsp_hwec) {
260 					/* This is a hack to make the hwec
261 					   available as a pipeline module */
262 					dsp_hwec_enable(container_of(pipeline,
263 								     struct dsp, pipeline), args);
264 					list_add_tail(&pipeline_entry->list,
265 						      &pipeline->list);
266 				} else {
267 					pipeline_entry->p = elem->new(args);
268 					if (pipeline_entry->p) {
269 						list_add_tail(&pipeline_entry->
270 							      list, &pipeline->list);
271 #ifdef PIPELINE_DEBUG
272 						printk(KERN_DEBUG "%s: created "
273 						       "instance of %s%s%s\n",
274 						       __func__, name, args ?
275 						       " with args " : "", args ?
276 						       args : "");
277 #endif
278 					} else {
279 						printk(KERN_ERR "%s: failed "
280 						       "to add entry to pipeline: "
281 						       "%s (new() returned NULL)\n",
282 						       __func__, elem->name);
283 						kfree(pipeline_entry);
284 						incomplete = 1;
285 					}
286 				}
287 				found = 1;
288 				break;
289 			}
290 
291 		if (found)
292 			found = 0;
293 		else {
294 			printk(KERN_ERR "%s: element not found, skipping: "
295 			       "%s\n", __func__, name);
296 			incomplete = 1;
297 		}
298 	}
299 
300 _out:
301 	if (!list_empty(&pipeline->list))
302 		pipeline->inuse = 1;
303 	else
304 		pipeline->inuse = 0;
305 
306 #ifdef PIPELINE_DEBUG
307 	printk(KERN_DEBUG "%s: dsp pipeline built%s: %s\n",
308 	       __func__, incomplete ? " incomplete" : "", cfg);
309 #endif
310 	kfree(dup);
311 	return 0;
312 }
313 
314 void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len)
315 {
316 	struct dsp_pipeline_entry *entry;
317 
318 	if (!pipeline)
319 		return;
320 
321 	list_for_each_entry(entry, &pipeline->list, list)
322 		if (entry->elem->process_tx)
323 			entry->elem->process_tx(entry->p, data, len);
324 }
325 
326 void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len,
327 			     unsigned int txlen)
328 {
329 	struct dsp_pipeline_entry *entry;
330 
331 	if (!pipeline)
332 		return;
333 
334 	list_for_each_entry_reverse(entry, &pipeline->list, list)
335 		if (entry->elem->process_rx)
336 			entry->elem->process_rx(entry->p, data, len, txlen);
337 }
338