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 ssize_t len = 0; 59 int i = 0; 60 61 *buf = 0; 62 for (; i < elem->num_args; ++i) 63 len = sprintf(buf, "%sName: %s\n%s%s%sDescription: %s\n" 64 "\n", buf, 65 elem->args[i].name, 66 elem->args[i].def ? "Default: " : "", 67 elem->args[i].def ? elem->args[i].def : "", 68 elem->args[i].def ? "\n" : "", 69 elem->args[i].desc); 70 71 return len; 72 } 73 74 static struct device_attribute element_attributes[] = { 75 __ATTR(args, 0444, attr_show_args, NULL), 76 }; 77 78 int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) 79 { 80 struct dsp_element_entry *entry; 81 int ret, i; 82 83 if (!elem) 84 return -EINVAL; 85 86 entry = kzalloc(sizeof(struct dsp_element_entry), GFP_KERNEL); 87 if (!entry) 88 return -ENOMEM; 89 90 entry->elem = elem; 91 92 entry->dev.class = elements_class; 93 dev_set_drvdata(&entry->dev, elem); 94 snprintf(entry->dev.bus_id, BUS_ID_SIZE, elem->name); 95 ret = device_register(&entry->dev); 96 if (ret) { 97 printk(KERN_ERR "%s: failed to register %s\n", 98 __func__, elem->name); 99 goto err1; 100 } 101 102 for (i = 0; i < (sizeof(element_attributes) 103 / sizeof(struct device_attribute)); ++i) 104 ret = device_create_file(&entry->dev, 105 &element_attributes[i]); 106 if (ret) { 107 printk(KERN_ERR "%s: failed to create device file\n", 108 __func__); 109 goto err2; 110 } 111 112 list_add_tail(&entry->list, &dsp_elements); 113 114 printk(KERN_DEBUG "%s: %s registered\n", __func__, elem->name); 115 116 return 0; 117 118 err2: 119 device_unregister(&entry->dev); 120 err1: 121 kfree(entry); 122 return ret; 123 } 124 EXPORT_SYMBOL(mISDN_dsp_element_register); 125 126 void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem) 127 { 128 struct dsp_element_entry *entry, *n; 129 130 if (!elem) 131 return; 132 133 list_for_each_entry_safe(entry, n, &dsp_elements, list) 134 if (entry->elem == elem) { 135 list_del(&entry->list); 136 device_unregister(&entry->dev); 137 kfree(entry); 138 printk(KERN_DEBUG "%s: %s unregistered\n", 139 __func__, elem->name); 140 return; 141 } 142 printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name); 143 } 144 EXPORT_SYMBOL(mISDN_dsp_element_unregister); 145 146 int dsp_pipeline_module_init(void) 147 { 148 elements_class = class_create(THIS_MODULE, "dsp_pipeline"); 149 if (IS_ERR(elements_class)) 150 return PTR_ERR(elements_class); 151 152 #ifdef PIPELINE_DEBUG 153 printk(KERN_DEBUG "%s: dsp pipeline module initialized\n", __func__); 154 #endif 155 156 dsp_hwec_init(); 157 158 return 0; 159 } 160 161 void dsp_pipeline_module_exit(void) 162 { 163 struct dsp_element_entry *entry, *n; 164 165 dsp_hwec_exit(); 166 167 class_destroy(elements_class); 168 169 list_for_each_entry_safe(entry, n, &dsp_elements, list) { 170 list_del(&entry->list); 171 printk(KERN_WARNING "%s: element was still registered: %s\n", 172 __func__, entry->elem->name); 173 kfree(entry); 174 } 175 176 printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __func__); 177 } 178 179 int dsp_pipeline_init(struct dsp_pipeline *pipeline) 180 { 181 if (!pipeline) 182 return -EINVAL; 183 184 INIT_LIST_HEAD(&pipeline->list); 185 186 #ifdef PIPELINE_DEBUG 187 printk(KERN_DEBUG "%s: dsp pipeline ready\n", __func__); 188 #endif 189 190 return 0; 191 } 192 193 static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline) 194 { 195 struct dsp_pipeline_entry *entry, *n; 196 197 list_for_each_entry_safe(entry, n, &pipeline->list, list) { 198 list_del(&entry->list); 199 if (entry->elem == dsp_hwec) 200 dsp_hwec_disable(container_of(pipeline, struct dsp, 201 pipeline)); 202 else 203 entry->elem->free(entry->p); 204 kfree(entry); 205 } 206 } 207 208 void dsp_pipeline_destroy(struct dsp_pipeline *pipeline) 209 { 210 211 if (!pipeline) 212 return; 213 214 _dsp_pipeline_destroy(pipeline); 215 216 #ifdef PIPELINE_DEBUG 217 printk(KERN_DEBUG "%s: dsp pipeline destroyed\n", __func__); 218 #endif 219 } 220 221 int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) 222 { 223 int len, incomplete = 0, found = 0; 224 char *dup, *tok, *name, *args; 225 struct dsp_element_entry *entry, *n; 226 struct dsp_pipeline_entry *pipeline_entry; 227 struct mISDN_dsp_element *elem; 228 229 if (!pipeline) 230 return -EINVAL; 231 232 if (!list_empty(&pipeline->list)) 233 _dsp_pipeline_destroy(pipeline); 234 235 if (!cfg) 236 return 0; 237 238 len = strlen(cfg); 239 if (!len) 240 return 0; 241 242 dup = kmalloc(len + 1, GFP_KERNEL); 243 if (!dup) 244 return 0; 245 strcpy(dup, cfg); 246 while ((tok = strsep(&dup, "|"))) { 247 if (!strlen(tok)) 248 continue; 249 name = strsep(&tok, "("); 250 args = strsep(&tok, ")"); 251 if (args && !*args) 252 args = 0; 253 254 list_for_each_entry_safe(entry, n, &dsp_elements, list) 255 if (!strcmp(entry->elem->name, name)) { 256 elem = entry->elem; 257 258 pipeline_entry = kmalloc(sizeof(struct 259 dsp_pipeline_entry), GFP_KERNEL); 260 if (!pipeline_entry) { 261 printk(KERN_DEBUG "%s: failed to add " 262 "entry to pipeline: %s (out of " 263 "memory)\n", __func__, elem->name); 264 incomplete = 1; 265 goto _out; 266 } 267 pipeline_entry->elem = elem; 268 269 if (elem == dsp_hwec) { 270 /* This is a hack to make the hwec 271 available as a pipeline module */ 272 dsp_hwec_enable(container_of(pipeline, 273 struct dsp, pipeline), args); 274 list_add_tail(&pipeline_entry->list, 275 &pipeline->list); 276 } else { 277 pipeline_entry->p = elem->new(args); 278 if (pipeline_entry->p) { 279 list_add_tail(&pipeline_entry-> 280 list, &pipeline->list); 281 #ifdef PIPELINE_DEBUG 282 printk(KERN_DEBUG "%s: created " 283 "instance of %s%s%s\n", 284 __func__, name, args ? 285 " with args " : "", args ? 286 args : ""); 287 #endif 288 } else { 289 printk(KERN_DEBUG "%s: failed " 290 "to add entry to pipeline: " 291 "%s (new() returned NULL)\n", 292 __func__, elem->name); 293 kfree(pipeline_entry); 294 incomplete = 1; 295 } 296 } 297 found = 1; 298 break; 299 } 300 301 if (found) 302 found = 0; 303 else { 304 printk(KERN_DEBUG "%s: element not found, skipping: " 305 "%s\n", __func__, name); 306 incomplete = 1; 307 } 308 } 309 310 _out: 311 if (!list_empty(&pipeline->list)) 312 pipeline->inuse = 1; 313 else 314 pipeline->inuse = 0; 315 316 #ifdef PIPELINE_DEBUG 317 printk(KERN_DEBUG "%s: dsp pipeline built%s: %s\n", 318 __func__, incomplete ? " incomplete" : "", cfg); 319 #endif 320 kfree(dup); 321 return 0; 322 } 323 324 void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len) 325 { 326 struct dsp_pipeline_entry *entry; 327 328 if (!pipeline) 329 return; 330 331 list_for_each_entry(entry, &pipeline->list, list) 332 if (entry->elem->process_tx) 333 entry->elem->process_tx(entry->p, data, len); 334 } 335 336 void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len) 337 { 338 struct dsp_pipeline_entry *entry; 339 340 if (!pipeline) 341 return; 342 343 list_for_each_entry_reverse(entry, &pipeline->list, list) 344 if (entry->elem->process_rx) 345 entry->elem->process_rx(entry->p, data, len); 346 } 347 348 349