2008-07-27 01:56:38 +02:00
/*
* dsp_pipeline . c : pipelined audio processing
*
* Copyright ( C ) 2007 , Nadi Sarrar
*
* Nadi Sarrar < nadi @ beronet . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation ; either version 2 of the License , or ( at your option )
* any later version .
*
* This program is distributed in the hope that it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program ; if not , write to the Free Software Foundation , Inc . , 59
* Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*
* The full GNU General Public License is included in this distribution in the
* file called LICENSE .
*
*/
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/string.h>
# include <linux/mISDNif.h>
# include <linux/mISDNdsp.h>
# include "dsp.h"
# include "dsp_hwec.h"
/* uncomment for debugging */
/*#define PIPELINE_DEBUG*/
struct dsp_pipeline_entry {
struct mISDN_dsp_element * elem ;
void * p ;
struct list_head list ;
} ;
struct dsp_element_entry {
struct mISDN_dsp_element * elem ;
struct device dev ;
struct list_head list ;
} ;
static LIST_HEAD ( dsp_elements ) ;
/* sysfs */
static struct class * elements_class ;
static ssize_t
attr_show_args ( struct device * dev , struct device_attribute * attr , char * buf )
{
struct mISDN_dsp_element * elem = dev_get_drvdata ( dev ) ;
ssize_t len = 0 ;
int i = 0 ;
* buf = 0 ;
for ( ; i < elem - > num_args ; + + i )
len = sprintf ( buf , " %sName: %s \n %s%s%sDescription: %s \n "
" \n " , buf ,
elem - > args [ i ] . name ,
elem - > args [ i ] . def ? " Default: " : " " ,
elem - > args [ i ] . def ? elem - > args [ i ] . def : " " ,
elem - > args [ i ] . def ? " \n " : " " ,
elem - > args [ i ] . desc ) ;
return len ;
}
static struct device_attribute element_attributes [ ] = {
__ATTR ( args , 0444 , attr_show_args , NULL ) ,
} ;
2008-09-28 15:40:15 +02:00
static void
mISDN_dsp_dev_release ( struct device * dev )
{
struct dsp_element_entry * entry =
container_of ( dev , struct dsp_element_entry , dev ) ;
list_del ( & entry - > list ) ;
kfree ( entry ) ;
}
2008-07-27 01:56:38 +02:00
int mISDN_dsp_element_register ( struct mISDN_dsp_element * elem )
{
struct dsp_element_entry * entry ;
int ret , i ;
if ( ! elem )
return - EINVAL ;
2008-10-11 08:13:29 +02:00
entry = kzalloc ( sizeof ( struct dsp_element_entry ) , GFP_ATOMIC ) ;
2008-07-27 01:56:38 +02:00
if ( ! entry )
return - ENOMEM ;
entry - > elem = elem ;
entry - > dev . class = elements_class ;
2008-09-28 15:40:15 +02:00
entry - > dev . release = mISDN_dsp_dev_release ;
2008-07-27 01:56:38 +02:00
dev_set_drvdata ( & entry - > dev , elem ) ;
2009-01-06 10:44:39 -08:00
dev_set_name ( & entry - > dev , elem - > name ) ;
2008-07-27 01:56:38 +02:00
ret = device_register ( & entry - > dev ) ;
if ( ret ) {
printk ( KERN_ERR " %s: failed to register %s \n " ,
__func__ , elem - > name ) ;
goto err1 ;
}
2008-09-28 15:40:15 +02:00
list_add_tail ( & entry - > list , & dsp_elements ) ;
2008-07-27 01:56:38 +02:00
2009-01-09 12:22:52 -08:00
for ( i = 0 ; i < ARRAY_SIZE ( element_attributes ) ; + + i ) {
2008-07-27 01:56:38 +02:00
ret = device_create_file ( & entry - > dev ,
& element_attributes [ i ] ) ;
if ( ret ) {
printk ( KERN_ERR " %s: failed to create device file \n " ,
__func__ ) ;
goto err2 ;
}
2009-01-09 12:22:52 -08:00
}
2008-07-27 01:56:38 +02:00
2008-09-28 15:40:15 +02:00
# ifdef PIPELINE_DEBUG
2008-07-27 01:56:38 +02:00
printk ( KERN_DEBUG " %s: %s registered \n " , __func__ , elem - > name ) ;
2008-09-28 15:40:15 +02:00
# endif
2008-07-27 01:56:38 +02:00
return 0 ;
err2 :
device_unregister ( & entry - > dev ) ;
2008-09-28 15:40:15 +02:00
return ret ;
2008-07-27 01:56:38 +02:00
err1 :
kfree ( entry ) ;
return ret ;
}
EXPORT_SYMBOL ( mISDN_dsp_element_register ) ;
void mISDN_dsp_element_unregister ( struct mISDN_dsp_element * elem )
{
struct dsp_element_entry * entry , * n ;
if ( ! elem )
return ;
list_for_each_entry_safe ( entry , n , & dsp_elements , list )
if ( entry - > elem = = elem ) {
device_unregister ( & entry - > dev ) ;
2008-09-28 15:40:15 +02:00
# ifdef PIPELINE_DEBUG
2008-07-27 01:56:38 +02:00
printk ( KERN_DEBUG " %s: %s unregistered \n " ,
__func__ , elem - > name ) ;
2008-09-28 15:40:15 +02:00
# endif
2008-07-27 01:56:38 +02:00
return ;
}
printk ( KERN_ERR " %s: element %s not in list. \n " , __func__ , elem - > name ) ;
}
EXPORT_SYMBOL ( mISDN_dsp_element_unregister ) ;
int dsp_pipeline_module_init ( void )
{
elements_class = class_create ( THIS_MODULE , " dsp_pipeline " ) ;
if ( IS_ERR ( elements_class ) )
return PTR_ERR ( elements_class ) ;
# ifdef PIPELINE_DEBUG
printk ( KERN_DEBUG " %s: dsp pipeline module initialized \n " , __func__ ) ;
# endif
dsp_hwec_init ( ) ;
return 0 ;
}
void dsp_pipeline_module_exit ( void )
{
struct dsp_element_entry * entry , * n ;
dsp_hwec_exit ( ) ;
class_destroy ( elements_class ) ;
list_for_each_entry_safe ( entry , n , & dsp_elements , list ) {
list_del ( & entry - > list ) ;
printk ( KERN_WARNING " %s: element was still registered: %s \n " ,
__func__ , entry - > elem - > name ) ;
kfree ( entry ) ;
}
2008-09-28 15:40:15 +02:00
# ifdef PIPELINE_DEBUG
2008-07-27 01:56:38 +02:00
printk ( KERN_DEBUG " %s: dsp pipeline module exited \n " , __func__ ) ;
2008-09-28 15:40:15 +02:00
# endif
2008-07-27 01:56:38 +02:00
}
int dsp_pipeline_init ( struct dsp_pipeline * pipeline )
{
if ( ! pipeline )
return - EINVAL ;
INIT_LIST_HEAD ( & pipeline - > list ) ;
# ifdef PIPELINE_DEBUG
printk ( KERN_DEBUG " %s: dsp pipeline ready \n " , __func__ ) ;
# endif
return 0 ;
}
static inline void _dsp_pipeline_destroy ( struct dsp_pipeline * pipeline )
{
struct dsp_pipeline_entry * entry , * n ;
list_for_each_entry_safe ( entry , n , & pipeline - > list , list ) {
list_del ( & entry - > list ) ;
if ( entry - > elem = = dsp_hwec )
dsp_hwec_disable ( container_of ( pipeline , struct dsp ,
pipeline ) ) ;
else
entry - > elem - > free ( entry - > p ) ;
kfree ( entry ) ;
}
}
void dsp_pipeline_destroy ( struct dsp_pipeline * pipeline )
{
if ( ! pipeline )
return ;
_dsp_pipeline_destroy ( pipeline ) ;
# ifdef PIPELINE_DEBUG
printk ( KERN_DEBUG " %s: dsp pipeline destroyed \n " , __func__ ) ;
# endif
}
int dsp_pipeline_build ( struct dsp_pipeline * pipeline , const char * cfg )
{
int len , incomplete = 0 , found = 0 ;
char * dup , * tok , * name , * args ;
struct dsp_element_entry * entry , * n ;
struct dsp_pipeline_entry * pipeline_entry ;
struct mISDN_dsp_element * elem ;
if ( ! pipeline )
return - EINVAL ;
if ( ! list_empty ( & pipeline - > list ) )
_dsp_pipeline_destroy ( pipeline ) ;
if ( ! cfg )
return 0 ;
len = strlen ( cfg ) ;
if ( ! len )
return 0 ;
2008-10-11 08:13:29 +02:00
dup = kmalloc ( len + 1 , GFP_ATOMIC ) ;
2008-07-27 01:56:38 +02:00
if ( ! dup )
return 0 ;
strcpy ( dup , cfg ) ;
while ( ( tok = strsep ( & dup , " | " ) ) ) {
if ( ! strlen ( tok ) )
continue ;
name = strsep ( & tok , " ( " ) ;
args = strsep ( & tok , " ) " ) ;
if ( args & & ! * args )
2008-12-12 21:11:28 -08:00
args = NULL ;
2008-07-27 01:56:38 +02:00
list_for_each_entry_safe ( entry , n , & dsp_elements , list )
if ( ! strcmp ( entry - > elem - > name , name ) ) {
elem = entry - > elem ;
pipeline_entry = kmalloc ( sizeof ( struct
2008-10-11 08:13:29 +02:00
dsp_pipeline_entry ) , GFP_ATOMIC ) ;
2008-07-27 01:56:38 +02:00
if ( ! pipeline_entry ) {
2008-09-28 15:40:15 +02:00
printk ( KERN_ERR " %s: failed to add "
2008-07-27 01:56:38 +02:00
" entry to pipeline: %s (out of "
" memory) \n " , __func__ , elem - > name ) ;
incomplete = 1 ;
goto _out ;
}
pipeline_entry - > elem = elem ;
if ( elem = = dsp_hwec ) {
/* This is a hack to make the hwec
available as a pipeline module */
dsp_hwec_enable ( container_of ( pipeline ,
struct dsp , pipeline ) , args ) ;
list_add_tail ( & pipeline_entry - > list ,
& pipeline - > list ) ;
} else {
pipeline_entry - > p = elem - > new ( args ) ;
if ( pipeline_entry - > p ) {
list_add_tail ( & pipeline_entry - >
list , & pipeline - > list ) ;
# ifdef PIPELINE_DEBUG
printk ( KERN_DEBUG " %s: created "
" instance of %s%s%s \n " ,
__func__ , name , args ?
" with args " : " " , args ?
args : " " ) ;
# endif
} else {
2008-09-28 15:40:15 +02:00
printk ( KERN_ERR " %s: failed "
2008-07-27 01:56:38 +02:00
" to add entry to pipeline: "
" %s (new() returned NULL) \n " ,
__func__ , elem - > name ) ;
kfree ( pipeline_entry ) ;
incomplete = 1 ;
}
}
found = 1 ;
break ;
}
if ( found )
found = 0 ;
else {
2008-09-28 15:40:15 +02:00
printk ( KERN_ERR " %s: element not found, skipping: "
2008-07-27 01:56:38 +02:00
" %s \n " , __func__ , name ) ;
incomplete = 1 ;
}
}
_out :
if ( ! list_empty ( & pipeline - > list ) )
pipeline - > inuse = 1 ;
else
pipeline - > inuse = 0 ;
# ifdef PIPELINE_DEBUG
printk ( KERN_DEBUG " %s: dsp pipeline built%s: %s \n " ,
__func__ , incomplete ? " incomplete " : " " , cfg ) ;
# endif
kfree ( dup ) ;
return 0 ;
}
void dsp_pipeline_process_tx ( struct dsp_pipeline * pipeline , u8 * data , int len )
{
struct dsp_pipeline_entry * entry ;
if ( ! pipeline )
return ;
list_for_each_entry ( entry , & pipeline - > list , list )
if ( entry - > elem - > process_tx )
entry - > elem - > process_tx ( entry - > p , data , len ) ;
}
2009-05-22 11:04:46 +00:00
void dsp_pipeline_process_rx ( struct dsp_pipeline * pipeline , u8 * data , int len ,
unsigned int txlen )
2008-07-27 01:56:38 +02:00
{
struct dsp_pipeline_entry * entry ;
if ( ! pipeline )
return ;
list_for_each_entry_reverse ( entry , & pipeline - > list , list )
if ( entry - > elem - > process_rx )
2009-05-22 11:04:46 +00:00
entry - > elem - > process_rx ( entry - > p , data , len , txlen ) ;
2008-07-27 01:56:38 +02:00
}