Merge tag 'batadv-next-for-davem-20200114' of git://git.open-mesh.org/linux-merge
[linux-2.6-microblaze.git] / drivers / isdn / mISDN / dsp_pipeline.c
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 }