xref: /linux/drivers/isdn/mISDN/dsp_pipeline.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
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 struct dsp_pipeline_entry {
21 	struct mISDN_dsp_element *elem;
22 	void                *p;
23 	struct list_head     list;
24 };
25 struct dsp_element_entry {
26 	struct mISDN_dsp_element *elem;
27 	struct device	     dev;
28 	struct list_head     list;
29 };
30 
31 static LIST_HEAD(dsp_elements);
32 
33 /* sysfs */
34 static const struct class elements_class = {
35 	.name = "dsp_pipeline",
36 };
37 
38 static ssize_t
attr_show_args(struct device * dev,struct device_attribute * attr,char * buf)39 attr_show_args(struct device *dev, struct device_attribute *attr, char *buf)
40 {
41 	struct mISDN_dsp_element *elem = dev_get_drvdata(dev);
42 	int i;
43 	char *p = buf;
44 
45 	*buf = 0;
46 	for (i = 0; i < elem->num_args; i++)
47 		p += sprintf(p, "Name:        %s\n%s%s%sDescription: %s\n\n",
48 			     elem->args[i].name,
49 			     elem->args[i].def ? "Default:     " : "",
50 			     elem->args[i].def ? elem->args[i].def : "",
51 			     elem->args[i].def ? "\n" : "",
52 			     elem->args[i].desc);
53 
54 	return p - buf;
55 }
56 
57 static struct device_attribute element_attributes[] = {
58 	__ATTR(args, 0444, attr_show_args, NULL),
59 };
60 
61 static void
mISDN_dsp_dev_release(struct device * dev)62 mISDN_dsp_dev_release(struct device *dev)
63 {
64 	struct dsp_element_entry *entry =
65 		container_of(dev, struct dsp_element_entry, dev);
66 	list_del(&entry->list);
67 	kfree(entry);
68 }
69 
mISDN_dsp_element_register(struct mISDN_dsp_element * elem)70 int mISDN_dsp_element_register(struct mISDN_dsp_element *elem)
71 {
72 	struct dsp_element_entry *entry;
73 	int ret, i;
74 
75 	if (!elem)
76 		return -EINVAL;
77 
78 	entry = kzalloc(sizeof(struct dsp_element_entry), GFP_ATOMIC);
79 	if (!entry)
80 		return -ENOMEM;
81 
82 	INIT_LIST_HEAD(&entry->list);
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 	return 0;
108 
109 err2:
110 	device_unregister(&entry->dev);
111 	return ret;
112 err1:
113 	put_device(&entry->dev);
114 	return ret;
115 }
116 EXPORT_SYMBOL(mISDN_dsp_element_register);
117 
mISDN_dsp_element_unregister(struct mISDN_dsp_element * elem)118 void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem)
119 {
120 	struct dsp_element_entry *entry, *n;
121 
122 	if (!elem)
123 		return;
124 
125 	list_for_each_entry_safe(entry, n, &dsp_elements, list)
126 		if (entry->elem == elem) {
127 			device_unregister(&entry->dev);
128 			return;
129 		}
130 	printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name);
131 }
132 EXPORT_SYMBOL(mISDN_dsp_element_unregister);
133 
dsp_pipeline_module_init(void)134 int dsp_pipeline_module_init(void)
135 {
136 	int err;
137 
138 	err = class_register(&elements_class);
139 	if (err)
140 		return err;
141 
142 	dsp_hwec_init();
143 
144 	return 0;
145 }
146 
dsp_pipeline_module_exit(void)147 void dsp_pipeline_module_exit(void)
148 {
149 	struct dsp_element_entry *entry, *n;
150 
151 	dsp_hwec_exit();
152 
153 	class_unregister(&elements_class);
154 
155 	list_for_each_entry_safe(entry, n, &dsp_elements, list) {
156 		list_del(&entry->list);
157 		printk(KERN_WARNING "%s: element was still registered: %s\n",
158 		       __func__, entry->elem->name);
159 		kfree(entry);
160 	}
161 }
162 
dsp_pipeline_init(struct dsp_pipeline * pipeline)163 int dsp_pipeline_init(struct dsp_pipeline *pipeline)
164 {
165 	if (!pipeline)
166 		return -EINVAL;
167 
168 	INIT_LIST_HEAD(&pipeline->list);
169 
170 	return 0;
171 }
172 
_dsp_pipeline_destroy(struct dsp_pipeline * pipeline)173 static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
174 {
175 	struct dsp_pipeline_entry *entry, *n;
176 
177 	list_for_each_entry_safe(entry, n, &pipeline->list, list) {
178 		list_del(&entry->list);
179 		if (entry->elem == dsp_hwec)
180 			dsp_hwec_disable(container_of(pipeline, struct dsp,
181 						      pipeline));
182 		else
183 			entry->elem->free(entry->p);
184 		kfree(entry);
185 	}
186 }
187 
dsp_pipeline_destroy(struct dsp_pipeline * pipeline)188 void dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
189 {
190 
191 	if (!pipeline)
192 		return;
193 
194 	_dsp_pipeline_destroy(pipeline);
195 }
196 
dsp_pipeline_build(struct dsp_pipeline * pipeline,const char * cfg)197 int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
198 {
199 	int found = 0;
200 	char *dup, *next, *tok, *name, *args;
201 	struct dsp_element_entry *entry, *n;
202 	struct dsp_pipeline_entry *pipeline_entry;
203 	struct mISDN_dsp_element *elem;
204 
205 	if (!pipeline)
206 		return -EINVAL;
207 
208 	if (!list_empty(&pipeline->list))
209 		_dsp_pipeline_destroy(pipeline);
210 
211 	dup = next = kstrdup(cfg, GFP_ATOMIC);
212 	if (!dup)
213 		return 0;
214 	while ((tok = strsep(&next, "|"))) {
215 		if (!strlen(tok))
216 			continue;
217 		name = strsep(&tok, "(");
218 		args = strsep(&tok, ")");
219 		if (args && !*args)
220 			args = NULL;
221 
222 		list_for_each_entry_safe(entry, n, &dsp_elements, list)
223 			if (!strcmp(entry->elem->name, name)) {
224 				elem = entry->elem;
225 
226 				pipeline_entry = kmalloc(sizeof(struct
227 								dsp_pipeline_entry), GFP_ATOMIC);
228 				if (!pipeline_entry) {
229 					printk(KERN_ERR "%s: failed to add "
230 					       "entry to pipeline: %s (out of "
231 					       "memory)\n", __func__, elem->name);
232 					goto _out;
233 				}
234 				pipeline_entry->elem = elem;
235 
236 				if (elem == dsp_hwec) {
237 					/* This is a hack to make the hwec
238 					   available as a pipeline module */
239 					dsp_hwec_enable(container_of(pipeline,
240 								     struct dsp, pipeline), args);
241 					list_add_tail(&pipeline_entry->list,
242 						      &pipeline->list);
243 				} else {
244 					pipeline_entry->p = elem->new(args);
245 					if (pipeline_entry->p) {
246 						list_add_tail(&pipeline_entry->
247 							      list, &pipeline->list);
248 					} else {
249 						printk(KERN_ERR "%s: failed "
250 						       "to add entry to pipeline: "
251 						       "%s (new() returned NULL)\n",
252 						       __func__, elem->name);
253 						kfree(pipeline_entry);
254 					}
255 				}
256 				found = 1;
257 				break;
258 			}
259 
260 		if (found)
261 			found = 0;
262 		else
263 			printk(KERN_ERR "%s: element not found, skipping: "
264 			       "%s\n", __func__, name);
265 	}
266 
267 _out:
268 	if (!list_empty(&pipeline->list))
269 		pipeline->inuse = 1;
270 	else
271 		pipeline->inuse = 0;
272 
273 	kfree(dup);
274 	return 0;
275 }
276 
dsp_pipeline_process_tx(struct dsp_pipeline * pipeline,u8 * data,int len)277 void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len)
278 {
279 	struct dsp_pipeline_entry *entry;
280 
281 	if (!pipeline)
282 		return;
283 
284 	list_for_each_entry(entry, &pipeline->list, list)
285 		if (entry->elem->process_tx)
286 			entry->elem->process_tx(entry->p, data, len);
287 }
288 
dsp_pipeline_process_rx(struct dsp_pipeline * pipeline,u8 * data,int len,unsigned int txlen)289 void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len,
290 			     unsigned int txlen)
291 {
292 	struct dsp_pipeline_entry *entry;
293 
294 	if (!pipeline)
295 		return;
296 
297 	list_for_each_entry_reverse(entry, &pipeline->list, list)
298 		if (entry->elem->process_rx)
299 			entry->elem->process_rx(entry->p, data, len, txlen);
300 }
301