#include #include #include #include #include #include #include #include #include #include "swi_ifx_common.h" #define MULTIPLEXER_PROCFILE_NAME "avm_pa_ifx_multiplexer" /*------------------------------------------------------------------------------------------*\ * AVM PA multiplexer \*------------------------------------------------------------------------------------------*/ struct avm_pa_multiplexer_session { struct avm_hardware_pa_instance *hw_pa_instance; /* identifiziert den HW_PA von dem die Session verarbeitet wird */ struct avm_pa_session *avm_session; volatile unsigned char valid_session; }; static int avm_pa_multiplexer_add_session(struct avm_pa_session *avm_session); static int avm_pa_multiplexer_remove_one_session(struct avm_pa_session *avm_session); static int avm_pa_multiplexer_change_session(struct avm_pa_session *avm_session); static void avm_pa_multiplexer_remove_all_sessions( struct avm_hardware_pa_instance *hw_pa_instance ); static DEFINE_RWLOCK( hw_pa_list_lock ); static DEFINE_SPINLOCK( session_list_lock ); static struct avm_hardware_pa avm_pa_multiplexer = { .add_session = avm_pa_multiplexer_add_session, .remove_session = avm_pa_multiplexer_remove_one_session, .change_session = avm_pa_multiplexer_change_session, .try_to_accelerate = ifx_ppa_try_to_accelerate, .alloc_rx_channel = ifx_ppa_alloc_virtual_rx_device, .alloc_tx_channel = ifx_ppa_alloc_virtual_tx_device, .free_rx_channel = ifx_ppa_free_virtual_rx_device, .free_tx_channel = ifx_ppa_free_virtual_tx_device, }; static struct avm_pa_multiplexer_session multiplexer_session_array[CONFIG_AVM_PA_MAX_SESSION]; static struct avm_hardware_pa_instance *pa_instance_array[MAX_HW_PA_INSTANCES]; #define PROC_BUFF_LEN 1024 char proc_buff[PROC_BUFF_LEN]; static int proc_read_avm_pa_ifx_multiplexer_open(struct inode *inode, struct file *file); static int proc_read_avm_pa_ifx_multiplexer_show(struct seq_file *seq, void *data); static int proc_write_avm_pa_ifx_multiplexer(struct file *file __attribute__ ((unused)), const char *buf, size_t count, loff_t *data __attribute__ ((unused))); static const struct file_operations avm_pa_ifx_multiplexer_fops = { .open = proc_read_avm_pa_ifx_multiplexer_open, .read = seq_read, .write = proc_write_avm_pa_ifx_multiplexer, .llseek = seq_lseek, .release = single_release, }; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void avm_pa_multiplexer_init(void){ int i; memset( &multiplexer_session_array[0], 0 , sizeof( struct avm_pa_multiplexer_session ) * CONFIG_AVM_PA_MAX_SESSION ); // memset( &pa_instance_array[0], 0 ,sizeof(struct avm_hardware_pa_instance *) * MAX_HW_PA_INSTANCES); for (i = 0; i < MAX_HW_PA_INSTANCES; i++) pa_instance_array[i] = NULL; avm_pa_register_hardware_pa( &avm_pa_multiplexer ); printk(KERN_ERR "[%s] start \n", __func__); avmnet_cfg_add_seq_procentry( avmnet_hw_config_entry->config, MULTIPLEXER_PROCFILE_NAME, &avm_pa_ifx_multiplexer_fops); printk(KERN_ERR "[%s] init complete \n", __func__); } EXPORT_SYMBOL(avm_pa_multiplexer_init); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if 0 //we never shut down void avm_pa_multiplexer_exit(void){ avmnet_cfg_remove_procentry( avmnet_hw_config_entry->config, MULTIPLEXER_PROCFILE_NAME ); } #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void avm_pa_multiplexer_register_instance( struct avm_hardware_pa_instance *hw_pa_instance ) { unsigned long hlock_flags; int i; printk(KERN_ERR "[%s] \n\tadd:%pF \n\tremove:%pF\n", __FUNCTION__, hw_pa_instance->add_session, hw_pa_instance->remove_session ); hw_pa_instance->enabled = 1; mb(); write_lock_irqsave( &hw_pa_list_lock, hlock_flags); for (i = 0; i < MAX_HW_PA_INSTANCES; i++){ if ( pa_instance_array[i] == NULL){ pa_instance_array[i] = hw_pa_instance; break; } } write_unlock_irqrestore( &hw_pa_list_lock, hlock_flags); } EXPORT_SYMBOL(avm_pa_multiplexer_register_instance); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void avm_pa_multiplexer_unregister_instance( struct avm_hardware_pa_instance *hw_pa_instance ) { unsigned long hlock_flags; int i; printk(KERN_ERR "[%s] \n\tadd:%pF \n\tremove:%pF\n", __FUNCTION__, hw_pa_instance->add_session, hw_pa_instance->remove_session ); hw_pa_instance->enabled = 0; mb(); write_lock_irqsave( &hw_pa_list_lock, hlock_flags); for ( i = 0; i < MAX_HW_PA_INSTANCES; i++ ){ if ( pa_instance_array[i] == hw_pa_instance ){ pa_instance_array[i] = NULL; break; } } write_unlock_irqrestore( &hw_pa_list_lock, hlock_flags); avm_pa_multiplexer_remove_all_sessions( hw_pa_instance ); } EXPORT_SYMBOL(avm_pa_multiplexer_unregister_instance); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int avm_pa_multiplexer_change_session(struct avm_pa_session *avm_session) { int status = AVM_PA_TX_ERROR_EGRESS; unsigned long slock_flags; struct avm_pa_multiplexer_session session_to_change; BUG_ON( avm_session->session_handle >= CONFIG_AVM_PA_MAX_SESSION); // take_session_list_lock spin_lock_irqsave( &session_list_lock, slock_flags); if ( ( avm_session == multiplexer_session_array[ avm_session->session_handle ].avm_session) && multiplexer_session_array[ avm_session->session_handle ].valid_session && multiplexer_session_array[ avm_session->session_handle ].hw_pa_instance->enabled ){ session_to_change = multiplexer_session_array[ avm_session->session_handle ]; mb(); } else { spin_unlock_irqrestore( &session_list_lock, slock_flags); return status; } // release_session_list_lock spin_unlock_irqrestore( &session_list_lock, slock_flags); return session_to_change.hw_pa_instance->change_session( avm_session ); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int avm_pa_multiplexer_add_session(struct avm_pa_session *avm_session){ unsigned long slock_flags; unsigned long hlock_flags; int i; struct avm_pa_multiplexer_session new_session; int res = AVM_PA_TX_ERROR_SESSION; BUG_ON( avm_session->session_handle >= CONFIG_AVM_PA_MAX_SESSION); for (i = 0; i < MAX_HW_PA_INSTANCES; i++){ struct avm_hardware_pa_instance *hw_pa_instance = NULL; mb(); read_lock_irqsave( &hw_pa_list_lock, hlock_flags); hw_pa_instance = pa_instance_array[i]; read_unlock_irqrestore( &hw_pa_list_lock, hlock_flags); if ( hw_pa_instance && hw_pa_instance->enabled && (hw_pa_instance->add_session( avm_session ) == AVM_PA_TX_SESSION_ADDED )) { int free_session = 0; new_session.hw_pa_instance = hw_pa_instance; new_session.avm_session = avm_session; new_session.valid_session = 1; mb(); // take_session_list_lock spin_lock_irqsave( &session_list_lock, slock_flags); if ( !multiplexer_session_array[ avm_session->session_handle ].valid_session ){ multiplexer_session_array[ avm_session->session_handle ] = new_session; res = AVM_PA_TX_SESSION_ADDED; } else { free_session = 1; res = AVM_PA_TX_ERROR_SESSION; } // release_session_list_lock spin_unlock_irqrestore( &session_list_lock, slock_flags); if ( free_session ) { hw_pa_instance->remove_session( avm_session ); printk(KERN_ERR "[%s] hw session already registered for session_id=%hu \n", __func__ ,avm_session->session_handle ); } } } return res; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int avm_pa_multiplexer_remove_one_session( struct avm_pa_session *avm_session ){ int status = AVM_PA_TX_ERROR_SESSION; unsigned long slock_flags; struct avm_pa_multiplexer_session session_to_remove; BUG_ON( avm_session->session_handle >= CONFIG_AVM_PA_MAX_SESSION); // take_session_list_lock spin_lock_irqsave( &session_list_lock, slock_flags); if ( ( avm_session == multiplexer_session_array[ avm_session->session_handle ].avm_session) && multiplexer_session_array[ avm_session->session_handle ].valid_session && multiplexer_session_array[ avm_session->session_handle ].hw_pa_instance->enabled ){ session_to_remove = multiplexer_session_array[ avm_session->session_handle ]; multiplexer_session_array[ avm_session->session_handle ].valid_session = 0; mb(); } else { // no valid session found // release_session_list_lock and return spin_unlock_irqrestore( &session_list_lock, slock_flags); /* printk( KERN_ERR "[%s] %s session %hu of disabled PA will not be removed\n", __func__, multiplexer_session_array[ avm_session->session_handle ].valid_session?"valid":"invalid", avm_session->session_handle ); */ return status; } // release_session_list_lock spin_unlock_irqrestore( &session_list_lock, slock_flags); session_to_remove.hw_pa_instance->remove_session( avm_session ); // take_session_list_lock spin_lock_irqsave( &session_list_lock, slock_flags); memset( &multiplexer_session_array[ avm_session->session_handle ],0 ,sizeof(struct avm_pa_multiplexer_session) ); mb(); // release_session_list_lock spin_unlock_irqrestore( &session_list_lock, slock_flags); status = AVM_PA_TX_OK; return status; } /*------------------------------------------------------------------------------------------*\ * Remove all Sessions dedicated to hw_pa_instance \*------------------------------------------------------------------------------------------*/ static void avm_pa_multiplexer_remove_all_sessions( struct avm_hardware_pa_instance *hw_pa_instance ){ struct avm_pa_multiplexer_session session_to_remove; unsigned int remove_count = 0; unsigned int removed_count = 0; unsigned int i; unsigned long slock_flags; printk(KERN_ERR "[%s] \n", __FUNCTION__); // take_session_list_lock spin_lock_irqsave( &session_list_lock, slock_flags); for (i = 0 ; i < CONFIG_AVM_PA_MAX_SESSION; i++){ if( multiplexer_session_array[i].valid_session && ( multiplexer_session_array[i].hw_pa_instance == hw_pa_instance ) ){ session_to_remove = multiplexer_session_array[i]; multiplexer_session_array[i].valid_session = 0; remove_count += 1; mb(); // release_session_list_lock spin_unlock_irqrestore( &session_list_lock, slock_flags); if (session_to_remove.avm_session){ session_to_remove.hw_pa_instance->remove_session( session_to_remove.avm_session ); removed_count += 1; } //take_session_list_lock spin_lock_irqsave( &session_list_lock, slock_flags); memset(&multiplexer_session_array[ i ],0 , sizeof(struct avm_pa_multiplexer_session) ); } } // release_session_list_lock spin_unlock_irqrestore( &session_list_lock, slock_flags ); printk(KERN_ERR "[%s] removed %u / %d \n", __FUNCTION__,removed_count, remove_count); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int proc_write_avm_pa_ifx_multiplexer(struct file *file __attribute__ ((unused)), const char *buf, size_t count, loff_t *data __attribute__ ((unused))) { int len; int parse_res; int pa_nr; int enable; unsigned long hlock_flags; len = sizeof(proc_buff) < count ? sizeof(proc_buff) - 1 : count; len = len - copy_from_user(proc_buff, buf, len); proc_buff[len] = 0; parse_res = sscanf(proc_buff, "enable %d", &pa_nr); if ( parse_res == 1) { enable = 1; } else { parse_res = sscanf(proc_buff, "disable %d", &pa_nr); if ( parse_res == 1){ enable = 0; } else { // neither enable nor disable return count; } } read_lock_irqsave( &hw_pa_list_lock, hlock_flags); if ( (pa_nr >= 0) && (pa_nr < MAX_HW_PA_INSTANCES) && pa_instance_array[pa_nr] ) { if (enable) { pa_instance_array[pa_nr]->enabled = 1; } else { pa_instance_array[pa_nr]->enabled = 0; avm_pa_multiplexer_remove_all_sessions( pa_instance_array[pa_nr] ); } } mb(); read_unlock_irqrestore( &hw_pa_list_lock, hlock_flags); return count; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int proc_read_avm_pa_ifx_multiplexer_show(struct seq_file *seq, void *data __attribute__((unused))){ int i; unsigned long hlock_flags; read_lock_irqsave( &hw_pa_list_lock, hlock_flags); for (i = 0; i < MAX_HW_PA_INSTANCES; i++){ if ( pa_instance_array[i] ){ seq_printf(seq, "[%d] %s: {%s}\n", i, pa_instance_array[i]->name, (pa_instance_array[i]->enabled)?"enabled":"disabled" ); seq_printf(seq, " add: %pF\n", pa_instance_array[i]->add_session ); seq_printf(seq, " del: %pF\n", pa_instance_array[i]->remove_session ); seq_printf(seq, "\n"); } } read_unlock_irqrestore( &hw_pa_list_lock, hlock_flags); ifx_ppa_show_virtual_devices(seq); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int proc_read_avm_pa_ifx_multiplexer_open(struct inode *inode __attribute__((unused)), struct file *file){ return single_open(file, proc_read_avm_pa_ifx_multiplexer_show, NULL); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ avm_session_handle (*ifx_lookup_routing_entry)(unsigned int routing_entry, unsigned int is_lan, unsigned int is_mc) = NULL; EXPORT_SYMBOL( ifx_lookup_routing_entry );