1 /* 2 * dsp_pipeline.c: pipelined audio processing 3 * 4 * Copyright (C) 2007, Nadi Sarrar 5 * 6 * Nadi Sarrar <nadi@beronet.com> 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License as published by the Free 10 * Software Foundation; either version 2 of the License, or (at your option) 11 * any later version. 12 * 13 * This program is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 * more details. 17 * 18 * You should have received a copy of the GNU General Public License along with 19 * this program; if not, write to the Free Software Foundation, Inc., 59 20 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. 21 * 22 * The full GNU General Public License is included in this distribution in the 23 * file called LICENSE. 24 * 25 */ 26 27 #include <linux/kernel.h> 28 #include <linux/list.h> 29 #include <linux/string.h> 30 #include <linux/mISDNif.h> 31 #include <linux/mISDNdsp.h> 32 #include "dsp.h" 33 #include "dsp_hwec.h" 34 35 /* uncomment for debugging */ 36 /*#define PIPELINE_DEBUG*/ 37 38 struct dsp_pipeline_entry { 39 struct mISDN_dsp_element *elem; 40 void *p; 41 struct list_head list; 42 }; 43 struct dsp_element_entry { 44 struct mISDN_dsp_element *elem; 45 struct device dev; 46 struct list_head list; 47 }; 48 49 static LIST_HEAD(dsp_elements); 50 51 /* sysfs */ 52 static struct class *elements_class; 53 54 static ssize_t 55 attr_show_args(struct device *dev, struct device_attribute *attr, char *buf) 56 { 57 struct mISDN_dsp_element *elem = dev_get_drvdata(dev); 58 int i; 59 char *p = buf; 60 61 *buf = 0; 62 for (i = 0; i < elem->num_args; i++) 63 p += sprintf(p, "Name: %s\n%s%s%sDescription: %s\n\n", 64 elem->args[i].name, 65 elem->args[i].def ? "Default: " : "", 66 elem->args[i].def ? elem->args[i].def : "", 67 elem->args[i].def ? "\n" : "", 68 elem->args[i].desc); 69 70 return p - buf; 71 } 72 73 static struct device_attribute element_attributes[] = { 74 __ATTR(args, 0444, attr_show_args, NULL), 75 }; 76 77 static void 78 mISDN_dsp_dev_release(struct device *dev) 79 { 80 struct dsp_element_entry *entry = 81 container_of(dev, struct dsp_element_entry, dev); 82 list_del(&entry->list); 83 kfree(entry); 84 } 85 86 int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) 87 { 88 struct dsp_element_entry *entry; 89 int ret, i; 90 91 if (!elem) 92 return -EINVAL; 93 94 entry = kzalloc(sizeof(struct dsp_element_entry), GFP_ATOMIC); 95 if (!entry) 96 return -ENOMEM; 97 98 entry->elem = elem; 99 100 entry->dev.class = elements_class; 101 entry->dev.release = mISDN_dsp_dev_release; 102 dev_set_drvdata(&entry->dev, elem); 103 dev_set_name(&entry->dev, elem->name); 104 ret = device_register(&entry->dev); 105 if (ret) { 106 printk(KERN_ERR "%s: failed to register %s\n", 107 __func__, elem->name); 108 goto err1; 109 } 110 list_add_tail(&entry->list, &dsp_elements); 111 112 for (i = 0; i < ARRAY_SIZE(element_attributes); ++i) { 113 ret = device_create_file(&entry->dev, 114 &element_attributes[i]); 115 if (ret) { 116 printk(KERN_ERR "%s: failed to create device file\n", 117 __func__); 118 goto err2; 119 } 120 } 121 122 #ifdef PIPELINE_DEBUG 123 printk(KERN_DEBUG "%s: %s registered\n", __func__, elem->name); 124 #endif 125 126 return 0; 127 128 err2: 129 device_unregister(&entry->dev); 130 return ret; 131 err1: 132 kfree(entry); 133 return ret; 134 } 135 EXPORT_SYMBOL(mISDN_dsp_element_register); 136 137 void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem) 138 { 139 struct dsp_element_entry *entry, *n; 140 141 if (!elem) 142 return; 143 144 list_for_each_entry_safe(entry, n, &dsp_elements, list) 145 if (entry->elem == elem) { 146 device_unregister(&entry->dev); 147 #ifdef PIPELINE_DEBUG 148 printk(KERN_DEBUG "%s: %s unregistered\n", 149 __func__, elem->name); 150 #endif 151 return; 152 } 153 printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name); 154 } 155 EXPORT_SYMBOL(mISDN_dsp_element_unregister); 156 157 int dsp_pipeline_module_init(void) 158 { 159 elements_class = class_create(THIS_MODULE, "dsp_pipeline"); 160 if (IS_ERR(elements_class)) 161 return PTR_ERR(elements_class); 162 163 #ifdef PIPELINE_DEBUG 164 printk(KERN_DEBUG "%s: dsp pipeline module initialized\n", __func__); 165 #endif 166 167 dsp_hwec_init(); 168 169 return 0; 170 } 171 172 void dsp_pipeline_module_exit(void) 173 { 174 struct dsp_element_entry *entry, *n; 175 176 dsp_hwec_exit(); 177 178 class_destroy(elements_class); 179 180 list_for_each_entry_safe(entry, n, &dsp_elements, list) { 181 list_del(&entry->list); 182 printk(KERN_WARNING "%s: element was still registered: %s\n", 183 __func__, entry->elem->name); 184 kfree(entry); 185 } 186 187 #ifdef PIPELINE_DEBUG 188 printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __func__); 189 #endif 190 } 191 192 int dsp_pipeline_init(struct dsp_pipeline *pipeline) 193 { 194 if (!pipeline) 195 return -EINVAL; 196 197 INIT_LIST_HEAD(&pipeline->list); 198 199 #ifdef PIPELINE_DEBUG 200 printk(KERN_DEBUG "%s: dsp pipeline ready\n", __func__); 201 #endif 202 203 return 0; 204 } 205 206 static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline) 207 { 208 struct dsp_pipeline_entry *entry, *n; 209 210 list_for_each_entry_safe(entry, n, &pipeline->list, list) { 211 list_del(&entry->list); 212 if (entry->elem == dsp_hwec) 213 dsp_hwec_disable(container_of(pipeline, struct dsp, 214 pipeline)); 215 else 216 entry->elem->free(entry->p); 217 kfree(entry); 218 } 219 } 220 221 void dsp_pipeline_destroy(struct dsp_pipeline *pipeline) 222 { 223 224 if (!pipeline) 225 return; 226 227 _dsp_pipeline_destroy(pipeline); 228 229 #ifdef PIPELINE_DEBUG 230 printk(KERN_DEBUG "%s: dsp pipeline destroyed\n", __func__); 231 #endif 232 } 233 234 int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) 235 { 236 int len, incomplete = 0, found = 0; 237 char *dup, *tok, *name, *args; 238 struct dsp_element_entry *entry, *n; 239 struct dsp_pipeline_entry *pipeline_entry; 240 struct mISDN_dsp_element *elem; 241 242 if (!pipeline) 243 return -EINVAL; 244 245 if (!list_empty(&pipeline->list)) 246 _dsp_pipeline_destroy(pipeline); 247 248 if (!cfg) 249 return 0; 250 251 len = strlen(cfg); 252 if (!len) 253 return 0; 254 255 dup = kmalloc(len + 1, GFP_ATOMIC); 256 if (!dup) 257 return 0; 258 strcpy(dup, cfg); 259 while ((tok = strsep(&dup, "|"))) { 260 if (!strlen(tok)) 261 continue; 262 name = strsep(&tok, "("); 263 args = strsep(&tok, ")"); 264 if (args && !*args) 265 args = NULL; 266 267 list_for_each_entry_safe(entry, n, &dsp_elements, list) 268 if (!strcmp(entry->elem->name, name)) { 269 elem = entry->elem; 270 271 pipeline_entry = kmalloc(sizeof(struct 272 dsp_pipeline_entry), GFP_ATOMIC); 273 if (!pipeline_entry) { 274 printk(KERN_ERR "%s: failed to add " 275 "entry to pipeline: %s (out of " 276 "memory)\n", __func__, elem->name); 277 incomplete = 1; 278 goto _out; 279 } 280 pipeline_entry->elem = elem; 281 282 if (elem == dsp_hwec) { 283 /* This is a hack to make the hwec 284 available as a pipeline module */ 285 dsp_hwec_enable(container_of(pipeline, 286 struct dsp, pipeline), args); 287 list_add_tail(&pipeline_entry->list, 288 &pipeline->list); 289 } else { 290 pipeline_entry->p = elem->new(args); 291 if (pipeline_entry->p) { 292 list_add_tail(&pipeline_entry-> 293 list, &pipeline->list); 294 #ifdef PIPELINE_DEBUG 295 printk(KERN_DEBUG "%s: created " 296 "instance of %s%s%s\n", 297 __func__, name, args ? 298 " with args " : "", args ? 299 args : ""); 300 #endif 301 } else { 302 printk(KERN_ERR "%s: failed " 303 "to add entry to pipeline: " 304 "%s (new() returned NULL)\n", 305 __func__, elem->name); 306 kfree(pipeline_entry); 307 incomplete = 1; 308 } 309 } 310 found = 1; 311 break; 312 } 313 314 if (found) 315 found = 0; 316 else { 317 printk(KERN_ERR "%s: element not found, skipping: " 318 "%s\n", __func__, name); 319 incomplete = 1; 320 } 321 } 322 323 _out: 324 if (!list_empty(&pipeline->list)) 325 pipeline->inuse = 1; 326 else 327 pipeline->inuse = 0; 328 329 #ifdef PIPELINE_DEBUG 330 printk(KERN_DEBUG "%s: dsp pipeline built%s: %s\n", 331 __func__, incomplete ? " incomplete" : "", cfg); 332 #endif 333 kfree(dup); 334 return 0; 335 } 336 337 void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len) 338 { 339 struct dsp_pipeline_entry *entry; 340 341 if (!pipeline) 342 return; 343 344 list_for_each_entry(entry, &pipeline->list, list) 345 if (entry->elem->process_tx) 346 entry->elem->process_tx(entry->p, data, len); 347 } 348 349 void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len, 350 unsigned int txlen) 351 { 352 struct dsp_pipeline_entry *entry; 353 354 if (!pipeline) 355 return; 356 357 list_for_each_entry_reverse(entry, &pipeline->list, list) 358 if (entry->elem->process_rx) 359 entry->elem->process_rx(entry->p, data, len, txlen); 360 } 361 362 363