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