1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * dsp_pipeline.c: pipelined audio processing
5 * Copyright (C) 2007, Nadi Sarrar
7 * Nadi Sarrar <nadi@beronet.com>
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>
20 struct dsp_pipeline_entry
{
21 struct mISDN_dsp_element
*elem
;
23 struct list_head list
;
25 struct dsp_element_entry
{
26 struct mISDN_dsp_element
*elem
;
28 struct list_head list
;
31 static LIST_HEAD(dsp_elements
);
34 static const struct class elements_class
= {
35 .name
= "dsp_pipeline",
39 attr_show_args(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
41 struct mISDN_dsp_element
*elem
= dev_get_drvdata(dev
);
46 for (i
= 0; i
< elem
->num_args
; i
++)
47 p
+= sprintf(p
, "Name: %s\n%s%s%sDescription: %s\n\n",
49 elem
->args
[i
].def
? "Default: " : "",
50 elem
->args
[i
].def
? elem
->args
[i
].def
: "",
51 elem
->args
[i
].def
? "\n" : "",
57 static struct device_attribute element_attributes
[] = {
58 __ATTR(args
, 0444, attr_show_args
, NULL
),
62 mISDN_dsp_dev_release(struct device
*dev
)
64 struct dsp_element_entry
*entry
=
65 container_of(dev
, struct dsp_element_entry
, dev
);
66 list_del(&entry
->list
);
70 int mISDN_dsp_element_register(struct mISDN_dsp_element
*elem
)
72 struct dsp_element_entry
*entry
;
78 entry
= kzalloc(sizeof(struct dsp_element_entry
), GFP_ATOMIC
);
82 INIT_LIST_HEAD(&entry
->list
);
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
);
91 printk(KERN_ERR
"%s: failed to register %s\n",
92 __func__
, elem
->name
);
95 list_add_tail(&entry
->list
, &dsp_elements
);
97 for (i
= 0; i
< ARRAY_SIZE(element_attributes
); ++i
) {
98 ret
= device_create_file(&entry
->dev
,
99 &element_attributes
[i
]);
101 printk(KERN_ERR
"%s: failed to create device file\n",
110 device_unregister(&entry
->dev
);
113 put_device(&entry
->dev
);
116 EXPORT_SYMBOL(mISDN_dsp_element_register
);
118 void mISDN_dsp_element_unregister(struct mISDN_dsp_element
*elem
)
120 struct dsp_element_entry
*entry
, *n
;
125 list_for_each_entry_safe(entry
, n
, &dsp_elements
, list
)
126 if (entry
->elem
== elem
) {
127 device_unregister(&entry
->dev
);
130 printk(KERN_ERR
"%s: element %s not in list.\n", __func__
, elem
->name
);
132 EXPORT_SYMBOL(mISDN_dsp_element_unregister
);
134 int dsp_pipeline_module_init(void)
138 err
= class_register(&elements_class
);
147 void dsp_pipeline_module_exit(void)
149 struct dsp_element_entry
*entry
, *n
;
153 class_unregister(&elements_class
);
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
);
163 int dsp_pipeline_init(struct dsp_pipeline
*pipeline
)
168 INIT_LIST_HEAD(&pipeline
->list
);
173 static inline void _dsp_pipeline_destroy(struct dsp_pipeline
*pipeline
)
175 struct dsp_pipeline_entry
*entry
, *n
;
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
,
183 entry
->elem
->free(entry
->p
);
188 void dsp_pipeline_destroy(struct dsp_pipeline
*pipeline
)
194 _dsp_pipeline_destroy(pipeline
);
197 int dsp_pipeline_build(struct dsp_pipeline
*pipeline
, const char *cfg
)
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
;
208 if (!list_empty(&pipeline
->list
))
209 _dsp_pipeline_destroy(pipeline
);
211 dup
= next
= kstrdup(cfg
, GFP_ATOMIC
);
214 while ((tok
= strsep(&next
, "|"))) {
217 name
= strsep(&tok
, "(");
218 args
= strsep(&tok
, ")");
222 list_for_each_entry_safe(entry
, n
, &dsp_elements
, list
)
223 if (!strcmp(entry
->elem
->name
, name
)) {
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
);
234 pipeline_entry
->elem
= elem
;
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
,
244 pipeline_entry
->p
= elem
->new(args
);
245 if (pipeline_entry
->p
) {
246 list_add_tail(&pipeline_entry
->
247 list
, &pipeline
->list
);
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
);
263 printk(KERN_ERR
"%s: element not found, skipping: "
264 "%s\n", __func__
, name
);
268 if (!list_empty(&pipeline
->list
))
277 void dsp_pipeline_process_tx(struct dsp_pipeline
*pipeline
, u8
*data
, int len
)
279 struct dsp_pipeline_entry
*entry
;
284 list_for_each_entry(entry
, &pipeline
->list
, list
)
285 if (entry
->elem
->process_tx
)
286 entry
->elem
->process_tx(entry
->p
, data
, len
);
289 void dsp_pipeline_process_rx(struct dsp_pipeline
*pipeline
, u8
*data
, int len
,
292 struct dsp_pipeline_entry
*entry
;
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
);