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