18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Audio crossconnecting/conferrencing (hardware level). 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 2002 by Andreas Eversberg (jolly@eversberg.eu) 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This software may be used and distributed according to the terms 78c2ecf20Sopenharmony_ci * of the GNU General Public License, incorporated herein by reference. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/* 128c2ecf20Sopenharmony_ci * The process of adding and removing parties to/from a conference: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * There is a chain of struct dsp_conf which has one or more members in a chain 158c2ecf20Sopenharmony_ci * of struct dsp_conf_member. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * After a party is added, the conference is checked for hardware capability. 188c2ecf20Sopenharmony_ci * Also if a party is removed, the conference is checked again. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * There are 3 different solutions: -1 = software, 0 = hardware-crossconnect 218c2ecf20Sopenharmony_ci * 1-n = hardware-conference. The n will give the conference number. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Depending on the change after removal or insertion of a party, hardware 248c2ecf20Sopenharmony_ci * commands are given. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * The current solution is stored within the struct dsp_conf entry. 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * HOW THE CMX WORKS: 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * There are 3 types of interaction: One member is alone, in this case only 338c2ecf20Sopenharmony_ci * data flow from upper to lower layer is done. 348c2ecf20Sopenharmony_ci * Two members will also exchange their data so they are crossconnected. 358c2ecf20Sopenharmony_ci * Three or more members will be added in a conference and will hear each 368c2ecf20Sopenharmony_ci * other but will not receive their own speech (echo) if not enabled. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * Features of CMX are: 398c2ecf20Sopenharmony_ci * - Crossconnecting or even conference, if more than two members are together. 408c2ecf20Sopenharmony_ci * - Force mixing of transmit data with other crossconnect/conference members. 418c2ecf20Sopenharmony_ci * - Echo generation to benchmark the delay of audio processing. 428c2ecf20Sopenharmony_ci * - Use hardware to minimize cpu load, disable FIFO load and minimize delay. 438c2ecf20Sopenharmony_ci * - Dejittering and clock generation. 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * There are 2 buffers: 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * RX-Buffer 498c2ecf20Sopenharmony_ci * R W 508c2ecf20Sopenharmony_ci * | | 518c2ecf20Sopenharmony_ci * ----------------+-------------+------------------- 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * The rx-buffer is a ring buffer used to store the received data for each 548c2ecf20Sopenharmony_ci * individual member. This is only the case if data needs to be dejittered 558c2ecf20Sopenharmony_ci * or in case of a conference where different clocks require reclocking. 568c2ecf20Sopenharmony_ci * The transmit-clock (R) will read the buffer. 578c2ecf20Sopenharmony_ci * If the clock overruns the write-pointer, we will have a buffer underrun. 588c2ecf20Sopenharmony_ci * If the write pointer always has a certain distance from the transmit- 598c2ecf20Sopenharmony_ci * clock, we will have a delay. The delay will dynamically be increased and 608c2ecf20Sopenharmony_ci * reduced. 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * TX-Buffer 648c2ecf20Sopenharmony_ci * R W 658c2ecf20Sopenharmony_ci * | | 668c2ecf20Sopenharmony_ci * -----------------+--------+----------------------- 678c2ecf20Sopenharmony_ci * 688c2ecf20Sopenharmony_ci * The tx-buffer is a ring buffer to queue the transmit data from user space 698c2ecf20Sopenharmony_ci * until it will be mixed or sent. There are two pointers, R and W. If the write 708c2ecf20Sopenharmony_ci * pointer W would reach or overrun R, the buffer would overrun. In this case 718c2ecf20Sopenharmony_ci * (some) data is dropped so that it will not overrun. 728c2ecf20Sopenharmony_ci * Additionally a dynamic dejittering can be enabled. this allows data from 738c2ecf20Sopenharmony_ci * user space that have jitter and different clock source. 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci * Clock: 778c2ecf20Sopenharmony_ci * 788c2ecf20Sopenharmony_ci * A Clock is not required, if the data source has exactly one clock. In this 798c2ecf20Sopenharmony_ci * case the data source is forwarded to the destination. 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * A Clock is required, because the data source 828c2ecf20Sopenharmony_ci * - has multiple clocks. 838c2ecf20Sopenharmony_ci * - has no usable clock due to jitter or packet loss (VoIP). 848c2ecf20Sopenharmony_ci * In this case the system's clock is used. The clock resolution depends on 858c2ecf20Sopenharmony_ci * the jiffie resolution. 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci * If a member joins a conference: 888c2ecf20Sopenharmony_ci * 898c2ecf20Sopenharmony_ci * - If a member joins, its rx_buff is set to silence and change read pointer 908c2ecf20Sopenharmony_ci * to transmit clock. 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * The procedure of received data from card is explained in cmx_receive. 938c2ecf20Sopenharmony_ci * The procedure of received data from user space is explained in cmx_transmit. 948c2ecf20Sopenharmony_ci * The procedure of transmit data to card is cmx_send. 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * 978c2ecf20Sopenharmony_ci * Interaction with other features: 988c2ecf20Sopenharmony_ci * 998c2ecf20Sopenharmony_ci * DTMF: 1008c2ecf20Sopenharmony_ci * DTMF decoding is done before the data is crossconnected. 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * Volume change: 1038c2ecf20Sopenharmony_ci * Changing rx-volume is done before the data is crossconnected. The tx-volume 1048c2ecf20Sopenharmony_ci * must be changed whenever data is transmitted to the card by the cmx. 1058c2ecf20Sopenharmony_ci * 1068c2ecf20Sopenharmony_ci * Tones: 1078c2ecf20Sopenharmony_ci * If a tone is enabled, it will be processed whenever data is transmitted to 1088c2ecf20Sopenharmony_ci * the card. It will replace the tx-data from the user space. 1098c2ecf20Sopenharmony_ci * If tones are generated by hardware, this conference member is removed for 1108c2ecf20Sopenharmony_ci * this time. 1118c2ecf20Sopenharmony_ci * 1128c2ecf20Sopenharmony_ci * Disable rx-data: 1138c2ecf20Sopenharmony_ci * If cmx is realized in hardware, rx data will be disabled if requested by 1148c2ecf20Sopenharmony_ci * the upper layer. If dtmf decoding is done by software and enabled, rx data 1158c2ecf20Sopenharmony_ci * will not be disabled but blocked to the upper layer. 1168c2ecf20Sopenharmony_ci * 1178c2ecf20Sopenharmony_ci * HFC conference engine: 1188c2ecf20Sopenharmony_ci * If it is possible to realize all features using hardware, hardware will be 1198c2ecf20Sopenharmony_ci * used if not forbidden by control command. Disabling rx-data provides 1208c2ecf20Sopenharmony_ci * absolutely traffic free audio processing. (except for the quick 1-frame 1218c2ecf20Sopenharmony_ci * upload of a tone loop, only once for a new tone) 1228c2ecf20Sopenharmony_ci * 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* delay.h is required for hw_lock.h */ 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci#include <linux/slab.h> 1288c2ecf20Sopenharmony_ci#include <linux/delay.h> 1298c2ecf20Sopenharmony_ci#include <linux/mISDNif.h> 1308c2ecf20Sopenharmony_ci#include <linux/mISDNdsp.h> 1318c2ecf20Sopenharmony_ci#include "core.h" 1328c2ecf20Sopenharmony_ci#include "dsp.h" 1338c2ecf20Sopenharmony_ci/* 1348c2ecf20Sopenharmony_ci * debugging of multi party conference, 1358c2ecf20Sopenharmony_ci * by using conference even with two members 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/* #define CMX_CONF_DEBUG */ 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/*#define CMX_DEBUG * massive read/write pointer output */ 1418c2ecf20Sopenharmony_ci/*#define CMX_DELAY_DEBUG * gives rx-buffer delay overview */ 1428c2ecf20Sopenharmony_ci/*#define CMX_TX_DEBUG * massive read/write on tx-buffer with content */ 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic inline int 1458c2ecf20Sopenharmony_cicount_list_member(struct list_head *head) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci int cnt = 0; 1488c2ecf20Sopenharmony_ci struct list_head *m; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci list_for_each(m, head) 1518c2ecf20Sopenharmony_ci cnt++; 1528c2ecf20Sopenharmony_ci return cnt; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* 1568c2ecf20Sopenharmony_ci * debug cmx memory structure 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_civoid 1598c2ecf20Sopenharmony_cidsp_cmx_debug(struct dsp *dsp) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct dsp_conf *conf; 1628c2ecf20Sopenharmony_ci struct dsp_conf_member *member; 1638c2ecf20Sopenharmony_ci struct dsp *odsp; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci printk(KERN_DEBUG "-----Current DSP\n"); 1668c2ecf20Sopenharmony_ci list_for_each_entry(odsp, &dsp_ilist, list) { 1678c2ecf20Sopenharmony_ci printk(KERN_DEBUG "* %s hardecho=%d softecho=%d txmix=%d", 1688c2ecf20Sopenharmony_ci odsp->name, odsp->echo.hardware, odsp->echo.software, 1698c2ecf20Sopenharmony_ci odsp->tx_mix); 1708c2ecf20Sopenharmony_ci if (odsp->conf) 1718c2ecf20Sopenharmony_ci printk(" (Conf %d)", odsp->conf->id); 1728c2ecf20Sopenharmony_ci if (dsp == odsp) 1738c2ecf20Sopenharmony_ci printk(" *this*"); 1748c2ecf20Sopenharmony_ci printk("\n"); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci printk(KERN_DEBUG "-----Current Conf:\n"); 1778c2ecf20Sopenharmony_ci list_for_each_entry(conf, &conf_ilist, list) { 1788c2ecf20Sopenharmony_ci printk(KERN_DEBUG "* Conf %d (%p)\n", conf->id, conf); 1798c2ecf20Sopenharmony_ci list_for_each_entry(member, &conf->mlist, list) { 1808c2ecf20Sopenharmony_ci printk(KERN_DEBUG 1818c2ecf20Sopenharmony_ci " - member = %s (slot_tx %d, bank_tx %d, " 1828c2ecf20Sopenharmony_ci "slot_rx %d, bank_rx %d hfc_conf %d " 1838c2ecf20Sopenharmony_ci "tx_data %d rx_is_off %d)%s\n", 1848c2ecf20Sopenharmony_ci member->dsp->name, member->dsp->pcm_slot_tx, 1858c2ecf20Sopenharmony_ci member->dsp->pcm_bank_tx, member->dsp->pcm_slot_rx, 1868c2ecf20Sopenharmony_ci member->dsp->pcm_bank_rx, member->dsp->hfc_conf, 1878c2ecf20Sopenharmony_ci member->dsp->tx_data, member->dsp->rx_is_off, 1888c2ecf20Sopenharmony_ci (member->dsp == dsp) ? " *this*" : ""); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci printk(KERN_DEBUG "-----end\n"); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/* 1958c2ecf20Sopenharmony_ci * search conference 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_cistatic struct dsp_conf * 1988c2ecf20Sopenharmony_cidsp_cmx_search_conf(u32 id) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct dsp_conf *conf; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (!id) { 2038c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: conference ID is 0.\n", __func__); 2048c2ecf20Sopenharmony_ci return NULL; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* search conference */ 2088c2ecf20Sopenharmony_ci list_for_each_entry(conf, &conf_ilist, list) 2098c2ecf20Sopenharmony_ci if (conf->id == id) 2108c2ecf20Sopenharmony_ci return conf; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return NULL; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* 2178c2ecf20Sopenharmony_ci * add member to conference 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_cistatic int 2208c2ecf20Sopenharmony_cidsp_cmx_add_conf_member(struct dsp *dsp, struct dsp_conf *conf) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct dsp_conf_member *member; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (!conf || !dsp) { 2258c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: conf or dsp is 0.\n", __func__); 2268c2ecf20Sopenharmony_ci return -EINVAL; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci if (dsp->member) { 2298c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: dsp is already member in a conf.\n", 2308c2ecf20Sopenharmony_ci __func__); 2318c2ecf20Sopenharmony_ci return -EINVAL; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (dsp->conf) { 2358c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: dsp is already in a conf.\n", 2368c2ecf20Sopenharmony_ci __func__); 2378c2ecf20Sopenharmony_ci return -EINVAL; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci member = kzalloc(sizeof(struct dsp_conf_member), GFP_ATOMIC); 2418c2ecf20Sopenharmony_ci if (!member) { 2428c2ecf20Sopenharmony_ci printk(KERN_ERR "kzalloc struct dsp_conf_member failed\n"); 2438c2ecf20Sopenharmony_ci return -ENOMEM; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci member->dsp = dsp; 2468c2ecf20Sopenharmony_ci /* clear rx buffer */ 2478c2ecf20Sopenharmony_ci memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); 2488c2ecf20Sopenharmony_ci dsp->rx_init = 1; /* rx_W and rx_R will be adjusted on first frame */ 2498c2ecf20Sopenharmony_ci dsp->rx_W = 0; 2508c2ecf20Sopenharmony_ci dsp->rx_R = 0; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci list_add_tail(&member->list, &conf->mlist); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci dsp->conf = conf; 2558c2ecf20Sopenharmony_ci dsp->member = member; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return 0; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci/* 2628c2ecf20Sopenharmony_ci * del member from conference 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ciint 2658c2ecf20Sopenharmony_cidsp_cmx_del_conf_member(struct dsp *dsp) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct dsp_conf_member *member; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (!dsp) { 2708c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: dsp is 0.\n", 2718c2ecf20Sopenharmony_ci __func__); 2728c2ecf20Sopenharmony_ci return -EINVAL; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (!dsp->conf) { 2768c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: dsp is not in a conf.\n", 2778c2ecf20Sopenharmony_ci __func__); 2788c2ecf20Sopenharmony_ci return -EINVAL; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (list_empty(&dsp->conf->mlist)) { 2828c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: dsp has linked an empty conf.\n", 2838c2ecf20Sopenharmony_ci __func__); 2848c2ecf20Sopenharmony_ci return -EINVAL; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* find us in conf */ 2888c2ecf20Sopenharmony_ci list_for_each_entry(member, &dsp->conf->mlist, list) { 2898c2ecf20Sopenharmony_ci if (member->dsp == dsp) { 2908c2ecf20Sopenharmony_ci list_del(&member->list); 2918c2ecf20Sopenharmony_ci dsp->conf = NULL; 2928c2ecf20Sopenharmony_ci dsp->member = NULL; 2938c2ecf20Sopenharmony_ci kfree(member); 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci printk(KERN_WARNING 2988c2ecf20Sopenharmony_ci "%s: dsp is not present in its own conf_member list.\n", 2998c2ecf20Sopenharmony_ci __func__); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return -EINVAL; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci/* 3068c2ecf20Sopenharmony_ci * new conference 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_cistatic struct dsp_conf 3098c2ecf20Sopenharmony_ci*dsp_cmx_new_conf(u32 id) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct dsp_conf *conf; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (!id) { 3148c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: id is 0.\n", 3158c2ecf20Sopenharmony_ci __func__); 3168c2ecf20Sopenharmony_ci return NULL; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci conf = kzalloc(sizeof(struct dsp_conf), GFP_ATOMIC); 3208c2ecf20Sopenharmony_ci if (!conf) { 3218c2ecf20Sopenharmony_ci printk(KERN_ERR "kzalloc struct dsp_conf failed\n"); 3228c2ecf20Sopenharmony_ci return NULL; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&conf->mlist); 3258c2ecf20Sopenharmony_ci conf->id = id; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci list_add_tail(&conf->list, &conf_ilist); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci return conf; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* 3348c2ecf20Sopenharmony_ci * del conference 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_ciint 3378c2ecf20Sopenharmony_cidsp_cmx_del_conf(struct dsp_conf *conf) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci if (!conf) { 3408c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: conf is null.\n", 3418c2ecf20Sopenharmony_ci __func__); 3428c2ecf20Sopenharmony_ci return -EINVAL; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (!list_empty(&conf->mlist)) { 3468c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: conf not empty.\n", 3478c2ecf20Sopenharmony_ci __func__); 3488c2ecf20Sopenharmony_ci return -EINVAL; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci list_del(&conf->list); 3518c2ecf20Sopenharmony_ci kfree(conf); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return 0; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci/* 3588c2ecf20Sopenharmony_ci * send HW message to hfc card 3598c2ecf20Sopenharmony_ci */ 3608c2ecf20Sopenharmony_cistatic void 3618c2ecf20Sopenharmony_cidsp_cmx_hw_message(struct dsp *dsp, u32 message, u32 param1, u32 param2, 3628c2ecf20Sopenharmony_ci u32 param3, u32 param4) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct mISDN_ctrl_req cq; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci memset(&cq, 0, sizeof(cq)); 3678c2ecf20Sopenharmony_ci cq.op = message; 3688c2ecf20Sopenharmony_ci cq.p1 = param1 | (param2 << 8); 3698c2ecf20Sopenharmony_ci cq.p2 = param3 | (param4 << 8); 3708c2ecf20Sopenharmony_ci if (dsp->ch.peer) 3718c2ecf20Sopenharmony_ci dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci/* 3768c2ecf20Sopenharmony_ci * do hardware update and set the software/hardware flag 3778c2ecf20Sopenharmony_ci * 3788c2ecf20Sopenharmony_ci * either a conference or a dsp instance can be given 3798c2ecf20Sopenharmony_ci * if only dsp instance is given, the instance is not associated with a conf 3808c2ecf20Sopenharmony_ci * and therefore removed. if a conference is given, the dsp is expected to 3818c2ecf20Sopenharmony_ci * be member of that conference. 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_civoid 3848c2ecf20Sopenharmony_cidsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct dsp_conf_member *member, *nextm; 3878c2ecf20Sopenharmony_ci struct dsp *finddsp; 3888c2ecf20Sopenharmony_ci int memb = 0, i, ii, i1, i2; 3898c2ecf20Sopenharmony_ci int freeunits[8]; 3908c2ecf20Sopenharmony_ci u_char freeslots[256]; 3918c2ecf20Sopenharmony_ci int same_hfc = -1, same_pcm = -1, current_conf = -1, 3928c2ecf20Sopenharmony_ci all_conf = 1, tx_data = 0; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* dsp gets updated (no conf) */ 3958c2ecf20Sopenharmony_ci if (!conf) { 3968c2ecf20Sopenharmony_ci if (!dsp) 3978c2ecf20Sopenharmony_ci return; 3988c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 3998c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s checking dsp %s\n", 4008c2ecf20Sopenharmony_ci __func__, dsp->name); 4018c2ecf20Sopenharmony_ci one_member: 4028c2ecf20Sopenharmony_ci /* remove HFC conference if enabled */ 4038c2ecf20Sopenharmony_ci if (dsp->hfc_conf >= 0) { 4048c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 4058c2ecf20Sopenharmony_ci printk(KERN_DEBUG 4068c2ecf20Sopenharmony_ci "%s removing %s from HFC conf %d " 4078c2ecf20Sopenharmony_ci "because dsp is split\n", __func__, 4088c2ecf20Sopenharmony_ci dsp->name, dsp->hfc_conf); 4098c2ecf20Sopenharmony_ci dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_CONF_SPLIT, 4108c2ecf20Sopenharmony_ci 0, 0, 0, 0); 4118c2ecf20Sopenharmony_ci dsp->hfc_conf = -1; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci /* process hw echo */ 4148c2ecf20Sopenharmony_ci if (dsp->features.pcm_banks < 1) 4158c2ecf20Sopenharmony_ci return; 4168c2ecf20Sopenharmony_ci if (!dsp->echo.software && !dsp->echo.hardware) { 4178c2ecf20Sopenharmony_ci /* NO ECHO: remove PCM slot if assigned */ 4188c2ecf20Sopenharmony_ci if (dsp->pcm_slot_tx >= 0 || dsp->pcm_slot_rx >= 0) { 4198c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 4208c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s removing %s from" 4218c2ecf20Sopenharmony_ci " PCM slot %d (TX) %d (RX) because" 4228c2ecf20Sopenharmony_ci " dsp is split (no echo)\n", 4238c2ecf20Sopenharmony_ci __func__, dsp->name, 4248c2ecf20Sopenharmony_ci dsp->pcm_slot_tx, dsp->pcm_slot_rx); 4258c2ecf20Sopenharmony_ci dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_DISC, 4268c2ecf20Sopenharmony_ci 0, 0, 0, 0); 4278c2ecf20Sopenharmony_ci dsp->pcm_slot_tx = -1; 4288c2ecf20Sopenharmony_ci dsp->pcm_bank_tx = -1; 4298c2ecf20Sopenharmony_ci dsp->pcm_slot_rx = -1; 4308c2ecf20Sopenharmony_ci dsp->pcm_bank_rx = -1; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci return; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci /* echo is enabled, find out if we use soft or hardware */ 4358c2ecf20Sopenharmony_ci dsp->echo.software = dsp->tx_data; 4368c2ecf20Sopenharmony_ci dsp->echo.hardware = 0; 4378c2ecf20Sopenharmony_ci /* ECHO: already echo */ 4388c2ecf20Sopenharmony_ci if (dsp->pcm_slot_tx >= 0 && dsp->pcm_slot_rx < 0 && 4398c2ecf20Sopenharmony_ci dsp->pcm_bank_tx == 2 && dsp->pcm_bank_rx == 2) { 4408c2ecf20Sopenharmony_ci dsp->echo.hardware = 1; 4418c2ecf20Sopenharmony_ci return; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci /* ECHO: if slot already assigned */ 4448c2ecf20Sopenharmony_ci if (dsp->pcm_slot_tx >= 0) { 4458c2ecf20Sopenharmony_ci dsp->pcm_slot_rx = dsp->pcm_slot_tx; 4468c2ecf20Sopenharmony_ci dsp->pcm_bank_tx = 2; /* 2 means loop */ 4478c2ecf20Sopenharmony_ci dsp->pcm_bank_rx = 2; 4488c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 4498c2ecf20Sopenharmony_ci printk(KERN_DEBUG 4508c2ecf20Sopenharmony_ci "%s refresh %s for echo using slot %d\n", 4518c2ecf20Sopenharmony_ci __func__, dsp->name, 4528c2ecf20Sopenharmony_ci dsp->pcm_slot_tx); 4538c2ecf20Sopenharmony_ci dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN, 4548c2ecf20Sopenharmony_ci dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2); 4558c2ecf20Sopenharmony_ci dsp->echo.hardware = 1; 4568c2ecf20Sopenharmony_ci return; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci /* ECHO: find slot */ 4598c2ecf20Sopenharmony_ci dsp->pcm_slot_tx = -1; 4608c2ecf20Sopenharmony_ci dsp->pcm_slot_rx = -1; 4618c2ecf20Sopenharmony_ci memset(freeslots, 1, sizeof(freeslots)); 4628c2ecf20Sopenharmony_ci list_for_each_entry(finddsp, &dsp_ilist, list) { 4638c2ecf20Sopenharmony_ci if (finddsp->features.pcm_id == dsp->features.pcm_id) { 4648c2ecf20Sopenharmony_ci if (finddsp->pcm_slot_rx >= 0 && 4658c2ecf20Sopenharmony_ci finddsp->pcm_slot_rx < sizeof(freeslots)) 4668c2ecf20Sopenharmony_ci freeslots[finddsp->pcm_slot_rx] = 0; 4678c2ecf20Sopenharmony_ci if (finddsp->pcm_slot_tx >= 0 && 4688c2ecf20Sopenharmony_ci finddsp->pcm_slot_tx < sizeof(freeslots)) 4698c2ecf20Sopenharmony_ci freeslots[finddsp->pcm_slot_tx] = 0; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci i = 0; 4738c2ecf20Sopenharmony_ci ii = dsp->features.pcm_slots; 4748c2ecf20Sopenharmony_ci while (i < ii) { 4758c2ecf20Sopenharmony_ci if (freeslots[i]) 4768c2ecf20Sopenharmony_ci break; 4778c2ecf20Sopenharmony_ci i++; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci if (i == ii) { 4808c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 4818c2ecf20Sopenharmony_ci printk(KERN_DEBUG 4828c2ecf20Sopenharmony_ci "%s no slot available for echo\n", 4838c2ecf20Sopenharmony_ci __func__); 4848c2ecf20Sopenharmony_ci /* no more slots available */ 4858c2ecf20Sopenharmony_ci dsp->echo.software = 1; 4868c2ecf20Sopenharmony_ci return; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci /* assign free slot */ 4898c2ecf20Sopenharmony_ci dsp->pcm_slot_tx = i; 4908c2ecf20Sopenharmony_ci dsp->pcm_slot_rx = i; 4918c2ecf20Sopenharmony_ci dsp->pcm_bank_tx = 2; /* loop */ 4928c2ecf20Sopenharmony_ci dsp->pcm_bank_rx = 2; 4938c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 4948c2ecf20Sopenharmony_ci printk(KERN_DEBUG 4958c2ecf20Sopenharmony_ci "%s assign echo for %s using slot %d\n", 4968c2ecf20Sopenharmony_ci __func__, dsp->name, dsp->pcm_slot_tx); 4978c2ecf20Sopenharmony_ci dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN, 4988c2ecf20Sopenharmony_ci dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2); 4998c2ecf20Sopenharmony_ci dsp->echo.hardware = 1; 5008c2ecf20Sopenharmony_ci return; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci /* conf gets updated (all members) */ 5048c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 5058c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s checking conference %d\n", 5068c2ecf20Sopenharmony_ci __func__, conf->id); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (list_empty(&conf->mlist)) { 5098c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: conference without members\n", 5108c2ecf20Sopenharmony_ci __func__); 5118c2ecf20Sopenharmony_ci return; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci member = list_entry(conf->mlist.next, struct dsp_conf_member, list); 5148c2ecf20Sopenharmony_ci same_hfc = member->dsp->features.hfc_id; 5158c2ecf20Sopenharmony_ci same_pcm = member->dsp->features.pcm_id; 5168c2ecf20Sopenharmony_ci /* check all members in our conference */ 5178c2ecf20Sopenharmony_ci list_for_each_entry(member, &conf->mlist, list) { 5188c2ecf20Sopenharmony_ci /* check if member uses mixing */ 5198c2ecf20Sopenharmony_ci if (member->dsp->tx_mix) { 5208c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 5218c2ecf20Sopenharmony_ci printk(KERN_DEBUG 5228c2ecf20Sopenharmony_ci "%s dsp %s cannot form a conf, because " 5238c2ecf20Sopenharmony_ci "tx_mix is turned on\n", __func__, 5248c2ecf20Sopenharmony_ci member->dsp->name); 5258c2ecf20Sopenharmony_ci conf_software: 5268c2ecf20Sopenharmony_ci list_for_each_entry(member, &conf->mlist, list) { 5278c2ecf20Sopenharmony_ci dsp = member->dsp; 5288c2ecf20Sopenharmony_ci /* remove HFC conference if enabled */ 5298c2ecf20Sopenharmony_ci if (dsp->hfc_conf >= 0) { 5308c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 5318c2ecf20Sopenharmony_ci printk(KERN_DEBUG 5328c2ecf20Sopenharmony_ci "%s removing %s from HFC " 5338c2ecf20Sopenharmony_ci "conf %d because not " 5348c2ecf20Sopenharmony_ci "possible with hardware\n", 5358c2ecf20Sopenharmony_ci __func__, 5368c2ecf20Sopenharmony_ci dsp->name, 5378c2ecf20Sopenharmony_ci dsp->hfc_conf); 5388c2ecf20Sopenharmony_ci dsp_cmx_hw_message(dsp, 5398c2ecf20Sopenharmony_ci MISDN_CTRL_HFC_CONF_SPLIT, 5408c2ecf20Sopenharmony_ci 0, 0, 0, 0); 5418c2ecf20Sopenharmony_ci dsp->hfc_conf = -1; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci /* remove PCM slot if assigned */ 5448c2ecf20Sopenharmony_ci if (dsp->pcm_slot_tx >= 0 || 5458c2ecf20Sopenharmony_ci dsp->pcm_slot_rx >= 0) { 5468c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 5478c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s removing " 5488c2ecf20Sopenharmony_ci "%s from PCM slot %d (TX)" 5498c2ecf20Sopenharmony_ci " slot %d (RX) because not" 5508c2ecf20Sopenharmony_ci " possible with hardware\n", 5518c2ecf20Sopenharmony_ci __func__, 5528c2ecf20Sopenharmony_ci dsp->name, 5538c2ecf20Sopenharmony_ci dsp->pcm_slot_tx, 5548c2ecf20Sopenharmony_ci dsp->pcm_slot_rx); 5558c2ecf20Sopenharmony_ci dsp_cmx_hw_message(dsp, 5568c2ecf20Sopenharmony_ci MISDN_CTRL_HFC_PCM_DISC, 5578c2ecf20Sopenharmony_ci 0, 0, 0, 0); 5588c2ecf20Sopenharmony_ci dsp->pcm_slot_tx = -1; 5598c2ecf20Sopenharmony_ci dsp->pcm_bank_tx = -1; 5608c2ecf20Sopenharmony_ci dsp->pcm_slot_rx = -1; 5618c2ecf20Sopenharmony_ci dsp->pcm_bank_rx = -1; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci conf->hardware = 0; 5658c2ecf20Sopenharmony_ci conf->software = 1; 5668c2ecf20Sopenharmony_ci return; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci /* check if member has echo turned on */ 5698c2ecf20Sopenharmony_ci if (member->dsp->echo.hardware || member->dsp->echo.software) { 5708c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 5718c2ecf20Sopenharmony_ci printk(KERN_DEBUG 5728c2ecf20Sopenharmony_ci "%s dsp %s cannot form a conf, because " 5738c2ecf20Sopenharmony_ci "echo is turned on\n", __func__, 5748c2ecf20Sopenharmony_ci member->dsp->name); 5758c2ecf20Sopenharmony_ci goto conf_software; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci /* check if member has tx_mix turned on */ 5788c2ecf20Sopenharmony_ci if (member->dsp->tx_mix) { 5798c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 5808c2ecf20Sopenharmony_ci printk(KERN_DEBUG 5818c2ecf20Sopenharmony_ci "%s dsp %s cannot form a conf, because " 5828c2ecf20Sopenharmony_ci "tx_mix is turned on\n", 5838c2ecf20Sopenharmony_ci __func__, member->dsp->name); 5848c2ecf20Sopenharmony_ci goto conf_software; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci /* check if member changes volume at an not suppoted level */ 5878c2ecf20Sopenharmony_ci if (member->dsp->tx_volume) { 5888c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 5898c2ecf20Sopenharmony_ci printk(KERN_DEBUG 5908c2ecf20Sopenharmony_ci "%s dsp %s cannot form a conf, because " 5918c2ecf20Sopenharmony_ci "tx_volume is changed\n", 5928c2ecf20Sopenharmony_ci __func__, member->dsp->name); 5938c2ecf20Sopenharmony_ci goto conf_software; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci if (member->dsp->rx_volume) { 5968c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 5978c2ecf20Sopenharmony_ci printk(KERN_DEBUG 5988c2ecf20Sopenharmony_ci "%s dsp %s cannot form a conf, because " 5998c2ecf20Sopenharmony_ci "rx_volume is changed\n", 6008c2ecf20Sopenharmony_ci __func__, member->dsp->name); 6018c2ecf20Sopenharmony_ci goto conf_software; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci /* check if tx-data turned on */ 6048c2ecf20Sopenharmony_ci if (member->dsp->tx_data) { 6058c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 6068c2ecf20Sopenharmony_ci printk(KERN_DEBUG 6078c2ecf20Sopenharmony_ci "%s dsp %s tx_data is turned on\n", 6088c2ecf20Sopenharmony_ci __func__, member->dsp->name); 6098c2ecf20Sopenharmony_ci tx_data = 1; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci /* check if pipeline exists */ 6128c2ecf20Sopenharmony_ci if (member->dsp->pipeline.inuse) { 6138c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 6148c2ecf20Sopenharmony_ci printk(KERN_DEBUG 6158c2ecf20Sopenharmony_ci "%s dsp %s cannot form a conf, because " 6168c2ecf20Sopenharmony_ci "pipeline exists\n", __func__, 6178c2ecf20Sopenharmony_ci member->dsp->name); 6188c2ecf20Sopenharmony_ci goto conf_software; 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci /* check if encryption is enabled */ 6218c2ecf20Sopenharmony_ci if (member->dsp->bf_enable) { 6228c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 6238c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s dsp %s cannot form a " 6248c2ecf20Sopenharmony_ci "conf, because encryption is enabled\n", 6258c2ecf20Sopenharmony_ci __func__, member->dsp->name); 6268c2ecf20Sopenharmony_ci goto conf_software; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci /* check if member is on a card with PCM support */ 6298c2ecf20Sopenharmony_ci if (member->dsp->features.pcm_id < 0) { 6308c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 6318c2ecf20Sopenharmony_ci printk(KERN_DEBUG 6328c2ecf20Sopenharmony_ci "%s dsp %s cannot form a conf, because " 6338c2ecf20Sopenharmony_ci "dsp has no PCM bus\n", 6348c2ecf20Sopenharmony_ci __func__, member->dsp->name); 6358c2ecf20Sopenharmony_ci goto conf_software; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci /* check if relations are on the same PCM bus */ 6388c2ecf20Sopenharmony_ci if (member->dsp->features.pcm_id != same_pcm) { 6398c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 6408c2ecf20Sopenharmony_ci printk(KERN_DEBUG 6418c2ecf20Sopenharmony_ci "%s dsp %s cannot form a conf, because " 6428c2ecf20Sopenharmony_ci "dsp is on a different PCM bus than the " 6438c2ecf20Sopenharmony_ci "first dsp\n", 6448c2ecf20Sopenharmony_ci __func__, member->dsp->name); 6458c2ecf20Sopenharmony_ci goto conf_software; 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci /* determine if members are on the same hfc chip */ 6488c2ecf20Sopenharmony_ci if (same_hfc != member->dsp->features.hfc_id) 6498c2ecf20Sopenharmony_ci same_hfc = -1; 6508c2ecf20Sopenharmony_ci /* if there are members already in a conference */ 6518c2ecf20Sopenharmony_ci if (current_conf < 0 && member->dsp->hfc_conf >= 0) 6528c2ecf20Sopenharmony_ci current_conf = member->dsp->hfc_conf; 6538c2ecf20Sopenharmony_ci /* if any member is not in a conference */ 6548c2ecf20Sopenharmony_ci if (member->dsp->hfc_conf < 0) 6558c2ecf20Sopenharmony_ci all_conf = 0; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci memb++; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* if no member, this is an error */ 6618c2ecf20Sopenharmony_ci if (memb < 1) 6628c2ecf20Sopenharmony_ci return; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci /* one member */ 6658c2ecf20Sopenharmony_ci if (memb == 1) { 6668c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 6678c2ecf20Sopenharmony_ci printk(KERN_DEBUG 6688c2ecf20Sopenharmony_ci "%s conf %d cannot form a HW conference, " 6698c2ecf20Sopenharmony_ci "because dsp is alone\n", __func__, conf->id); 6708c2ecf20Sopenharmony_ci conf->hardware = 0; 6718c2ecf20Sopenharmony_ci conf->software = 0; 6728c2ecf20Sopenharmony_ci member = list_entry(conf->mlist.next, struct dsp_conf_member, 6738c2ecf20Sopenharmony_ci list); 6748c2ecf20Sopenharmony_ci dsp = member->dsp; 6758c2ecf20Sopenharmony_ci goto one_member; 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* 6798c2ecf20Sopenharmony_ci * ok, now we are sure that all members are on the same pcm. 6808c2ecf20Sopenharmony_ci * now we will see if we have only two members, so we can do 6818c2ecf20Sopenharmony_ci * crossconnections, which don't have any limitations. 6828c2ecf20Sopenharmony_ci */ 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci /* if we have only two members */ 6858c2ecf20Sopenharmony_ci if (memb == 2) { 6868c2ecf20Sopenharmony_ci member = list_entry(conf->mlist.next, struct dsp_conf_member, 6878c2ecf20Sopenharmony_ci list); 6888c2ecf20Sopenharmony_ci nextm = list_entry(member->list.next, struct dsp_conf_member, 6898c2ecf20Sopenharmony_ci list); 6908c2ecf20Sopenharmony_ci /* remove HFC conference if enabled */ 6918c2ecf20Sopenharmony_ci if (member->dsp->hfc_conf >= 0) { 6928c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 6938c2ecf20Sopenharmony_ci printk(KERN_DEBUG 6948c2ecf20Sopenharmony_ci "%s removing %s from HFC conf %d because " 6958c2ecf20Sopenharmony_ci "two parties require only a PCM slot\n", 6968c2ecf20Sopenharmony_ci __func__, member->dsp->name, 6978c2ecf20Sopenharmony_ci member->dsp->hfc_conf); 6988c2ecf20Sopenharmony_ci dsp_cmx_hw_message(member->dsp, 6998c2ecf20Sopenharmony_ci MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0); 7008c2ecf20Sopenharmony_ci member->dsp->hfc_conf = -1; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci if (nextm->dsp->hfc_conf >= 0) { 7038c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 7048c2ecf20Sopenharmony_ci printk(KERN_DEBUG 7058c2ecf20Sopenharmony_ci "%s removing %s from HFC conf %d because " 7068c2ecf20Sopenharmony_ci "two parties require only a PCM slot\n", 7078c2ecf20Sopenharmony_ci __func__, nextm->dsp->name, 7088c2ecf20Sopenharmony_ci nextm->dsp->hfc_conf); 7098c2ecf20Sopenharmony_ci dsp_cmx_hw_message(nextm->dsp, 7108c2ecf20Sopenharmony_ci MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0); 7118c2ecf20Sopenharmony_ci nextm->dsp->hfc_conf = -1; 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci /* if members have two banks (and not on the same chip) */ 7148c2ecf20Sopenharmony_ci if (member->dsp->features.pcm_banks > 1 && 7158c2ecf20Sopenharmony_ci nextm->dsp->features.pcm_banks > 1 && 7168c2ecf20Sopenharmony_ci member->dsp->features.hfc_id != 7178c2ecf20Sopenharmony_ci nextm->dsp->features.hfc_id) { 7188c2ecf20Sopenharmony_ci /* if both members have same slots with crossed banks */ 7198c2ecf20Sopenharmony_ci if (member->dsp->pcm_slot_tx >= 0 && 7208c2ecf20Sopenharmony_ci member->dsp->pcm_slot_rx >= 0 && 7218c2ecf20Sopenharmony_ci nextm->dsp->pcm_slot_tx >= 0 && 7228c2ecf20Sopenharmony_ci nextm->dsp->pcm_slot_rx >= 0 && 7238c2ecf20Sopenharmony_ci nextm->dsp->pcm_slot_tx == 7248c2ecf20Sopenharmony_ci member->dsp->pcm_slot_rx && 7258c2ecf20Sopenharmony_ci nextm->dsp->pcm_slot_rx == 7268c2ecf20Sopenharmony_ci member->dsp->pcm_slot_tx && 7278c2ecf20Sopenharmony_ci nextm->dsp->pcm_slot_tx == 7288c2ecf20Sopenharmony_ci member->dsp->pcm_slot_tx && 7298c2ecf20Sopenharmony_ci member->dsp->pcm_bank_tx != 7308c2ecf20Sopenharmony_ci member->dsp->pcm_bank_rx && 7318c2ecf20Sopenharmony_ci nextm->dsp->pcm_bank_tx != 7328c2ecf20Sopenharmony_ci nextm->dsp->pcm_bank_rx) { 7338c2ecf20Sopenharmony_ci /* all members have same slot */ 7348c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 7358c2ecf20Sopenharmony_ci printk(KERN_DEBUG 7368c2ecf20Sopenharmony_ci "%s dsp %s & %s stay joined on " 7378c2ecf20Sopenharmony_ci "PCM slot %d bank %d (TX) bank %d " 7388c2ecf20Sopenharmony_ci "(RX) (on different chips)\n", 7398c2ecf20Sopenharmony_ci __func__, 7408c2ecf20Sopenharmony_ci member->dsp->name, 7418c2ecf20Sopenharmony_ci nextm->dsp->name, 7428c2ecf20Sopenharmony_ci member->dsp->pcm_slot_tx, 7438c2ecf20Sopenharmony_ci member->dsp->pcm_bank_tx, 7448c2ecf20Sopenharmony_ci member->dsp->pcm_bank_rx); 7458c2ecf20Sopenharmony_ci conf->hardware = 1; 7468c2ecf20Sopenharmony_ci conf->software = tx_data; 7478c2ecf20Sopenharmony_ci return; 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci /* find a new slot */ 7508c2ecf20Sopenharmony_ci memset(freeslots, 1, sizeof(freeslots)); 7518c2ecf20Sopenharmony_ci list_for_each_entry(dsp, &dsp_ilist, list) { 7528c2ecf20Sopenharmony_ci if (dsp != member->dsp && 7538c2ecf20Sopenharmony_ci dsp != nextm->dsp && 7548c2ecf20Sopenharmony_ci member->dsp->features.pcm_id == 7558c2ecf20Sopenharmony_ci dsp->features.pcm_id) { 7568c2ecf20Sopenharmony_ci if (dsp->pcm_slot_rx >= 0 && 7578c2ecf20Sopenharmony_ci dsp->pcm_slot_rx < 7588c2ecf20Sopenharmony_ci sizeof(freeslots)) 7598c2ecf20Sopenharmony_ci freeslots[dsp->pcm_slot_rx] = 0; 7608c2ecf20Sopenharmony_ci if (dsp->pcm_slot_tx >= 0 && 7618c2ecf20Sopenharmony_ci dsp->pcm_slot_tx < 7628c2ecf20Sopenharmony_ci sizeof(freeslots)) 7638c2ecf20Sopenharmony_ci freeslots[dsp->pcm_slot_tx] = 0; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci i = 0; 7678c2ecf20Sopenharmony_ci ii = member->dsp->features.pcm_slots; 7688c2ecf20Sopenharmony_ci while (i < ii) { 7698c2ecf20Sopenharmony_ci if (freeslots[i]) 7708c2ecf20Sopenharmony_ci break; 7718c2ecf20Sopenharmony_ci i++; 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci if (i == ii) { 7748c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 7758c2ecf20Sopenharmony_ci printk(KERN_DEBUG 7768c2ecf20Sopenharmony_ci "%s no slot available for " 7778c2ecf20Sopenharmony_ci "%s & %s\n", __func__, 7788c2ecf20Sopenharmony_ci member->dsp->name, 7798c2ecf20Sopenharmony_ci nextm->dsp->name); 7808c2ecf20Sopenharmony_ci /* no more slots available */ 7818c2ecf20Sopenharmony_ci goto conf_software; 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci /* assign free slot */ 7848c2ecf20Sopenharmony_ci member->dsp->pcm_slot_tx = i; 7858c2ecf20Sopenharmony_ci member->dsp->pcm_slot_rx = i; 7868c2ecf20Sopenharmony_ci nextm->dsp->pcm_slot_tx = i; 7878c2ecf20Sopenharmony_ci nextm->dsp->pcm_slot_rx = i; 7888c2ecf20Sopenharmony_ci member->dsp->pcm_bank_rx = 0; 7898c2ecf20Sopenharmony_ci member->dsp->pcm_bank_tx = 1; 7908c2ecf20Sopenharmony_ci nextm->dsp->pcm_bank_rx = 1; 7918c2ecf20Sopenharmony_ci nextm->dsp->pcm_bank_tx = 0; 7928c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 7938c2ecf20Sopenharmony_ci printk(KERN_DEBUG 7948c2ecf20Sopenharmony_ci "%s adding %s & %s to new PCM slot %d " 7958c2ecf20Sopenharmony_ci "(TX and RX on different chips) because " 7968c2ecf20Sopenharmony_ci "both members have not same slots\n", 7978c2ecf20Sopenharmony_ci __func__, 7988c2ecf20Sopenharmony_ci member->dsp->name, 7998c2ecf20Sopenharmony_ci nextm->dsp->name, 8008c2ecf20Sopenharmony_ci member->dsp->pcm_slot_tx); 8018c2ecf20Sopenharmony_ci dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, 8028c2ecf20Sopenharmony_ci member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, 8038c2ecf20Sopenharmony_ci member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx); 8048c2ecf20Sopenharmony_ci dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN, 8058c2ecf20Sopenharmony_ci nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx, 8068c2ecf20Sopenharmony_ci nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx); 8078c2ecf20Sopenharmony_ci conf->hardware = 1; 8088c2ecf20Sopenharmony_ci conf->software = tx_data; 8098c2ecf20Sopenharmony_ci return; 8108c2ecf20Sopenharmony_ci /* if members have one bank (or on the same chip) */ 8118c2ecf20Sopenharmony_ci } else { 8128c2ecf20Sopenharmony_ci /* if both members have different crossed slots */ 8138c2ecf20Sopenharmony_ci if (member->dsp->pcm_slot_tx >= 0 && 8148c2ecf20Sopenharmony_ci member->dsp->pcm_slot_rx >= 0 && 8158c2ecf20Sopenharmony_ci nextm->dsp->pcm_slot_tx >= 0 && 8168c2ecf20Sopenharmony_ci nextm->dsp->pcm_slot_rx >= 0 && 8178c2ecf20Sopenharmony_ci nextm->dsp->pcm_slot_tx == 8188c2ecf20Sopenharmony_ci member->dsp->pcm_slot_rx && 8198c2ecf20Sopenharmony_ci nextm->dsp->pcm_slot_rx == 8208c2ecf20Sopenharmony_ci member->dsp->pcm_slot_tx && 8218c2ecf20Sopenharmony_ci member->dsp->pcm_slot_tx != 8228c2ecf20Sopenharmony_ci member->dsp->pcm_slot_rx && 8238c2ecf20Sopenharmony_ci member->dsp->pcm_bank_tx == 0 && 8248c2ecf20Sopenharmony_ci member->dsp->pcm_bank_rx == 0 && 8258c2ecf20Sopenharmony_ci nextm->dsp->pcm_bank_tx == 0 && 8268c2ecf20Sopenharmony_ci nextm->dsp->pcm_bank_rx == 0) { 8278c2ecf20Sopenharmony_ci /* all members have same slot */ 8288c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 8298c2ecf20Sopenharmony_ci printk(KERN_DEBUG 8308c2ecf20Sopenharmony_ci "%s dsp %s & %s stay joined on PCM " 8318c2ecf20Sopenharmony_ci "slot %d (TX) %d (RX) on same chip " 8328c2ecf20Sopenharmony_ci "or one bank PCM)\n", __func__, 8338c2ecf20Sopenharmony_ci member->dsp->name, 8348c2ecf20Sopenharmony_ci nextm->dsp->name, 8358c2ecf20Sopenharmony_ci member->dsp->pcm_slot_tx, 8368c2ecf20Sopenharmony_ci member->dsp->pcm_slot_rx); 8378c2ecf20Sopenharmony_ci conf->hardware = 1; 8388c2ecf20Sopenharmony_ci conf->software = tx_data; 8398c2ecf20Sopenharmony_ci return; 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci /* find two new slot */ 8428c2ecf20Sopenharmony_ci memset(freeslots, 1, sizeof(freeslots)); 8438c2ecf20Sopenharmony_ci list_for_each_entry(dsp, &dsp_ilist, list) { 8448c2ecf20Sopenharmony_ci if (dsp != member->dsp && 8458c2ecf20Sopenharmony_ci dsp != nextm->dsp && 8468c2ecf20Sopenharmony_ci member->dsp->features.pcm_id == 8478c2ecf20Sopenharmony_ci dsp->features.pcm_id) { 8488c2ecf20Sopenharmony_ci if (dsp->pcm_slot_rx >= 0 && 8498c2ecf20Sopenharmony_ci dsp->pcm_slot_rx < 8508c2ecf20Sopenharmony_ci sizeof(freeslots)) 8518c2ecf20Sopenharmony_ci freeslots[dsp->pcm_slot_rx] = 0; 8528c2ecf20Sopenharmony_ci if (dsp->pcm_slot_tx >= 0 && 8538c2ecf20Sopenharmony_ci dsp->pcm_slot_tx < 8548c2ecf20Sopenharmony_ci sizeof(freeslots)) 8558c2ecf20Sopenharmony_ci freeslots[dsp->pcm_slot_tx] = 0; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci i1 = 0; 8598c2ecf20Sopenharmony_ci ii = member->dsp->features.pcm_slots; 8608c2ecf20Sopenharmony_ci while (i1 < ii) { 8618c2ecf20Sopenharmony_ci if (freeslots[i1]) 8628c2ecf20Sopenharmony_ci break; 8638c2ecf20Sopenharmony_ci i1++; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci if (i1 == ii) { 8668c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 8678c2ecf20Sopenharmony_ci printk(KERN_DEBUG 8688c2ecf20Sopenharmony_ci "%s no slot available " 8698c2ecf20Sopenharmony_ci "for %s & %s\n", __func__, 8708c2ecf20Sopenharmony_ci member->dsp->name, 8718c2ecf20Sopenharmony_ci nextm->dsp->name); 8728c2ecf20Sopenharmony_ci /* no more slots available */ 8738c2ecf20Sopenharmony_ci goto conf_software; 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci i2 = i1 + 1; 8768c2ecf20Sopenharmony_ci while (i2 < ii) { 8778c2ecf20Sopenharmony_ci if (freeslots[i2]) 8788c2ecf20Sopenharmony_ci break; 8798c2ecf20Sopenharmony_ci i2++; 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci if (i2 == ii) { 8828c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 8838c2ecf20Sopenharmony_ci printk(KERN_DEBUG 8848c2ecf20Sopenharmony_ci "%s no slot available " 8858c2ecf20Sopenharmony_ci "for %s & %s\n", 8868c2ecf20Sopenharmony_ci __func__, 8878c2ecf20Sopenharmony_ci member->dsp->name, 8888c2ecf20Sopenharmony_ci nextm->dsp->name); 8898c2ecf20Sopenharmony_ci /* no more slots available */ 8908c2ecf20Sopenharmony_ci goto conf_software; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci /* assign free slots */ 8938c2ecf20Sopenharmony_ci member->dsp->pcm_slot_tx = i1; 8948c2ecf20Sopenharmony_ci member->dsp->pcm_slot_rx = i2; 8958c2ecf20Sopenharmony_ci nextm->dsp->pcm_slot_tx = i2; 8968c2ecf20Sopenharmony_ci nextm->dsp->pcm_slot_rx = i1; 8978c2ecf20Sopenharmony_ci member->dsp->pcm_bank_rx = 0; 8988c2ecf20Sopenharmony_ci member->dsp->pcm_bank_tx = 0; 8998c2ecf20Sopenharmony_ci nextm->dsp->pcm_bank_rx = 0; 9008c2ecf20Sopenharmony_ci nextm->dsp->pcm_bank_tx = 0; 9018c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 9028c2ecf20Sopenharmony_ci printk(KERN_DEBUG 9038c2ecf20Sopenharmony_ci "%s adding %s & %s to new PCM slot %d " 9048c2ecf20Sopenharmony_ci "(TX) %d (RX) on same chip or one bank " 9058c2ecf20Sopenharmony_ci "PCM, because both members have not " 9068c2ecf20Sopenharmony_ci "crossed slots\n", __func__, 9078c2ecf20Sopenharmony_ci member->dsp->name, 9088c2ecf20Sopenharmony_ci nextm->dsp->name, 9098c2ecf20Sopenharmony_ci member->dsp->pcm_slot_tx, 9108c2ecf20Sopenharmony_ci member->dsp->pcm_slot_rx); 9118c2ecf20Sopenharmony_ci dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, 9128c2ecf20Sopenharmony_ci member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, 9138c2ecf20Sopenharmony_ci member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx); 9148c2ecf20Sopenharmony_ci dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN, 9158c2ecf20Sopenharmony_ci nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx, 9168c2ecf20Sopenharmony_ci nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx); 9178c2ecf20Sopenharmony_ci conf->hardware = 1; 9188c2ecf20Sopenharmony_ci conf->software = tx_data; 9198c2ecf20Sopenharmony_ci return; 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci /* 9248c2ecf20Sopenharmony_ci * if we have more than two, we may check if we have a conference 9258c2ecf20Sopenharmony_ci * unit available on the chip. also all members must be on the same 9268c2ecf20Sopenharmony_ci */ 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci /* if not the same HFC chip */ 9298c2ecf20Sopenharmony_ci if (same_hfc < 0) { 9308c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 9318c2ecf20Sopenharmony_ci printk(KERN_DEBUG 9328c2ecf20Sopenharmony_ci "%s conference %d cannot be formed, because " 9338c2ecf20Sopenharmony_ci "members are on different chips or not " 9348c2ecf20Sopenharmony_ci "on HFC chip\n", 9358c2ecf20Sopenharmony_ci __func__, conf->id); 9368c2ecf20Sopenharmony_ci goto conf_software; 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* for more than two members.. */ 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci /* if all members already have the same conference */ 9428c2ecf20Sopenharmony_ci if (all_conf) { 9438c2ecf20Sopenharmony_ci conf->hardware = 1; 9448c2ecf20Sopenharmony_ci conf->software = tx_data; 9458c2ecf20Sopenharmony_ci return; 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci /* 9498c2ecf20Sopenharmony_ci * if there is an existing conference, but not all members have joined 9508c2ecf20Sopenharmony_ci */ 9518c2ecf20Sopenharmony_ci if (current_conf >= 0) { 9528c2ecf20Sopenharmony_ci join_members: 9538c2ecf20Sopenharmony_ci list_for_each_entry(member, &conf->mlist, list) { 9548c2ecf20Sopenharmony_ci /* if no conference engine on our chip, change to 9558c2ecf20Sopenharmony_ci * software */ 9568c2ecf20Sopenharmony_ci if (!member->dsp->features.hfc_conf) 9578c2ecf20Sopenharmony_ci goto conf_software; 9588c2ecf20Sopenharmony_ci /* in case of hdlc, change to software */ 9598c2ecf20Sopenharmony_ci if (member->dsp->hdlc) 9608c2ecf20Sopenharmony_ci goto conf_software; 9618c2ecf20Sopenharmony_ci /* join to current conference */ 9628c2ecf20Sopenharmony_ci if (member->dsp->hfc_conf == current_conf) 9638c2ecf20Sopenharmony_ci continue; 9648c2ecf20Sopenharmony_ci /* get a free timeslot first */ 9658c2ecf20Sopenharmony_ci memset(freeslots, 1, sizeof(freeslots)); 9668c2ecf20Sopenharmony_ci list_for_each_entry(dsp, &dsp_ilist, list) { 9678c2ecf20Sopenharmony_ci /* 9688c2ecf20Sopenharmony_ci * not checking current member, because 9698c2ecf20Sopenharmony_ci * slot will be overwritten. 9708c2ecf20Sopenharmony_ci */ 9718c2ecf20Sopenharmony_ci if ( 9728c2ecf20Sopenharmony_ci dsp != member->dsp && 9738c2ecf20Sopenharmony_ci /* dsp must be on the same PCM */ 9748c2ecf20Sopenharmony_ci member->dsp->features.pcm_id == 9758c2ecf20Sopenharmony_ci dsp->features.pcm_id) { 9768c2ecf20Sopenharmony_ci /* dsp must be on a slot */ 9778c2ecf20Sopenharmony_ci if (dsp->pcm_slot_tx >= 0 && 9788c2ecf20Sopenharmony_ci dsp->pcm_slot_tx < 9798c2ecf20Sopenharmony_ci sizeof(freeslots)) 9808c2ecf20Sopenharmony_ci freeslots[dsp->pcm_slot_tx] = 0; 9818c2ecf20Sopenharmony_ci if (dsp->pcm_slot_rx >= 0 && 9828c2ecf20Sopenharmony_ci dsp->pcm_slot_rx < 9838c2ecf20Sopenharmony_ci sizeof(freeslots)) 9848c2ecf20Sopenharmony_ci freeslots[dsp->pcm_slot_rx] = 0; 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci i = 0; 9888c2ecf20Sopenharmony_ci ii = member->dsp->features.pcm_slots; 9898c2ecf20Sopenharmony_ci while (i < ii) { 9908c2ecf20Sopenharmony_ci if (freeslots[i]) 9918c2ecf20Sopenharmony_ci break; 9928c2ecf20Sopenharmony_ci i++; 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci if (i == ii) { 9958c2ecf20Sopenharmony_ci /* no more slots available */ 9968c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 9978c2ecf20Sopenharmony_ci printk(KERN_DEBUG 9988c2ecf20Sopenharmony_ci "%s conference %d cannot be formed," 9998c2ecf20Sopenharmony_ci " because no slot free\n", 10008c2ecf20Sopenharmony_ci __func__, conf->id); 10018c2ecf20Sopenharmony_ci goto conf_software; 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 10048c2ecf20Sopenharmony_ci printk(KERN_DEBUG 10058c2ecf20Sopenharmony_ci "%s changing dsp %s to HW conference " 10068c2ecf20Sopenharmony_ci "%d slot %d\n", __func__, 10078c2ecf20Sopenharmony_ci member->dsp->name, current_conf, i); 10088c2ecf20Sopenharmony_ci /* assign free slot & set PCM & join conf */ 10098c2ecf20Sopenharmony_ci member->dsp->pcm_slot_tx = i; 10108c2ecf20Sopenharmony_ci member->dsp->pcm_slot_rx = i; 10118c2ecf20Sopenharmony_ci member->dsp->pcm_bank_tx = 2; /* loop */ 10128c2ecf20Sopenharmony_ci member->dsp->pcm_bank_rx = 2; 10138c2ecf20Sopenharmony_ci member->dsp->hfc_conf = current_conf; 10148c2ecf20Sopenharmony_ci dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, 10158c2ecf20Sopenharmony_ci i, 2, i, 2); 10168c2ecf20Sopenharmony_ci dsp_cmx_hw_message(member->dsp, 10178c2ecf20Sopenharmony_ci MISDN_CTRL_HFC_CONF_JOIN, current_conf, 0, 0, 0); 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci conf->hardware = 1; 10208c2ecf20Sopenharmony_ci conf->software = tx_data; 10218c2ecf20Sopenharmony_ci return; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci /* 10258c2ecf20Sopenharmony_ci * no member is in a conference yet, so we find a free one 10268c2ecf20Sopenharmony_ci */ 10278c2ecf20Sopenharmony_ci memset(freeunits, 1, sizeof(freeunits)); 10288c2ecf20Sopenharmony_ci list_for_each_entry(dsp, &dsp_ilist, list) { 10298c2ecf20Sopenharmony_ci /* dsp must be on the same chip */ 10308c2ecf20Sopenharmony_ci if (dsp->features.hfc_id == same_hfc && 10318c2ecf20Sopenharmony_ci /* dsp must have joined a HW conference */ 10328c2ecf20Sopenharmony_ci dsp->hfc_conf >= 0 && 10338c2ecf20Sopenharmony_ci /* slot must be within range */ 10348c2ecf20Sopenharmony_ci dsp->hfc_conf < 8) 10358c2ecf20Sopenharmony_ci freeunits[dsp->hfc_conf] = 0; 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci i = 0; 10388c2ecf20Sopenharmony_ci ii = 8; 10398c2ecf20Sopenharmony_ci while (i < ii) { 10408c2ecf20Sopenharmony_ci if (freeunits[i]) 10418c2ecf20Sopenharmony_ci break; 10428c2ecf20Sopenharmony_ci i++; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci if (i == ii) { 10458c2ecf20Sopenharmony_ci /* no more conferences available */ 10468c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 10478c2ecf20Sopenharmony_ci printk(KERN_DEBUG 10488c2ecf20Sopenharmony_ci "%s conference %d cannot be formed, because " 10498c2ecf20Sopenharmony_ci "no conference number free\n", 10508c2ecf20Sopenharmony_ci __func__, conf->id); 10518c2ecf20Sopenharmony_ci goto conf_software; 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci /* join all members */ 10548c2ecf20Sopenharmony_ci current_conf = i; 10558c2ecf20Sopenharmony_ci goto join_members; 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci/* 10608c2ecf20Sopenharmony_ci * conf_id != 0: join or change conference 10618c2ecf20Sopenharmony_ci * conf_id == 0: split from conference if not already 10628c2ecf20Sopenharmony_ci */ 10638c2ecf20Sopenharmony_ciint 10648c2ecf20Sopenharmony_cidsp_cmx_conf(struct dsp *dsp, u32 conf_id) 10658c2ecf20Sopenharmony_ci{ 10668c2ecf20Sopenharmony_ci int err; 10678c2ecf20Sopenharmony_ci struct dsp_conf *conf; 10688c2ecf20Sopenharmony_ci struct dsp_conf_member *member; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci /* if conference doesn't change */ 10718c2ecf20Sopenharmony_ci if (dsp->conf_id == conf_id) 10728c2ecf20Sopenharmony_ci return 0; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci /* first remove us from current conf */ 10758c2ecf20Sopenharmony_ci if (dsp->conf_id) { 10768c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 10778c2ecf20Sopenharmony_ci printk(KERN_DEBUG "removing us from conference %d\n", 10788c2ecf20Sopenharmony_ci dsp->conf->id); 10798c2ecf20Sopenharmony_ci /* remove us from conf */ 10808c2ecf20Sopenharmony_ci conf = dsp->conf; 10818c2ecf20Sopenharmony_ci err = dsp_cmx_del_conf_member(dsp); 10828c2ecf20Sopenharmony_ci if (err) 10838c2ecf20Sopenharmony_ci return err; 10848c2ecf20Sopenharmony_ci dsp->conf_id = 0; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci /* update hardware */ 10878c2ecf20Sopenharmony_ci dsp_cmx_hardware(NULL, dsp); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci /* conf now empty? */ 10908c2ecf20Sopenharmony_ci if (list_empty(&conf->mlist)) { 10918c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 10928c2ecf20Sopenharmony_ci printk(KERN_DEBUG 10938c2ecf20Sopenharmony_ci "conference is empty, so we remove it.\n"); 10948c2ecf20Sopenharmony_ci err = dsp_cmx_del_conf(conf); 10958c2ecf20Sopenharmony_ci if (err) 10968c2ecf20Sopenharmony_ci return err; 10978c2ecf20Sopenharmony_ci } else { 10988c2ecf20Sopenharmony_ci /* update members left on conf */ 10998c2ecf20Sopenharmony_ci dsp_cmx_hardware(conf, NULL); 11008c2ecf20Sopenharmony_ci } 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci /* if split */ 11048c2ecf20Sopenharmony_ci if (!conf_id) 11058c2ecf20Sopenharmony_ci return 0; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci /* now add us to conf */ 11088c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 11098c2ecf20Sopenharmony_ci printk(KERN_DEBUG "searching conference %d\n", 11108c2ecf20Sopenharmony_ci conf_id); 11118c2ecf20Sopenharmony_ci conf = dsp_cmx_search_conf(conf_id); 11128c2ecf20Sopenharmony_ci if (!conf) { 11138c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 11148c2ecf20Sopenharmony_ci printk(KERN_DEBUG 11158c2ecf20Sopenharmony_ci "conference doesn't exist yet, creating.\n"); 11168c2ecf20Sopenharmony_ci /* the conference doesn't exist, so we create */ 11178c2ecf20Sopenharmony_ci conf = dsp_cmx_new_conf(conf_id); 11188c2ecf20Sopenharmony_ci if (!conf) 11198c2ecf20Sopenharmony_ci return -EINVAL; 11208c2ecf20Sopenharmony_ci } else if (!list_empty(&conf->mlist)) { 11218c2ecf20Sopenharmony_ci member = list_entry(conf->mlist.next, struct dsp_conf_member, 11228c2ecf20Sopenharmony_ci list); 11238c2ecf20Sopenharmony_ci if (dsp->hdlc && !member->dsp->hdlc) { 11248c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 11258c2ecf20Sopenharmony_ci printk(KERN_DEBUG 11268c2ecf20Sopenharmony_ci "cannot join transparent conference.\n"); 11278c2ecf20Sopenharmony_ci return -EINVAL; 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci if (!dsp->hdlc && member->dsp->hdlc) { 11308c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 11318c2ecf20Sopenharmony_ci printk(KERN_DEBUG 11328c2ecf20Sopenharmony_ci "cannot join hdlc conference.\n"); 11338c2ecf20Sopenharmony_ci return -EINVAL; 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci } 11368c2ecf20Sopenharmony_ci /* add conference member */ 11378c2ecf20Sopenharmony_ci err = dsp_cmx_add_conf_member(dsp, conf); 11388c2ecf20Sopenharmony_ci if (err) 11398c2ecf20Sopenharmony_ci return err; 11408c2ecf20Sopenharmony_ci dsp->conf_id = conf_id; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci /* if we are alone, we do nothing! */ 11438c2ecf20Sopenharmony_ci if (list_empty(&conf->mlist)) { 11448c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CMX) 11458c2ecf20Sopenharmony_ci printk(KERN_DEBUG 11468c2ecf20Sopenharmony_ci "we are alone in this conference, so exit.\n"); 11478c2ecf20Sopenharmony_ci /* update hardware */ 11488c2ecf20Sopenharmony_ci dsp_cmx_hardware(NULL, dsp); 11498c2ecf20Sopenharmony_ci return 0; 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci /* update members on conf */ 11538c2ecf20Sopenharmony_ci dsp_cmx_hardware(conf, NULL); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci return 0; 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci#ifdef CMX_DELAY_DEBUG 11598c2ecf20Sopenharmony_ciint delaycount; 11608c2ecf20Sopenharmony_cistatic void 11618c2ecf20Sopenharmony_cishowdelay(struct dsp *dsp, int samples, int delay) 11628c2ecf20Sopenharmony_ci{ 11638c2ecf20Sopenharmony_ci char bar[] = "--------------------------------------------------|"; 11648c2ecf20Sopenharmony_ci int sdelay; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci delaycount += samples; 11678c2ecf20Sopenharmony_ci if (delaycount < 8000) 11688c2ecf20Sopenharmony_ci return; 11698c2ecf20Sopenharmony_ci delaycount = 0; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci sdelay = delay * 50 / (dsp_poll << 2); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci printk(KERN_DEBUG "DELAY (%s) %3d >%s\n", dsp->name, delay, 11748c2ecf20Sopenharmony_ci sdelay > 50 ? "..." : bar + 50 - sdelay); 11758c2ecf20Sopenharmony_ci} 11768c2ecf20Sopenharmony_ci#endif 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci/* 11798c2ecf20Sopenharmony_ci * audio data is received from card 11808c2ecf20Sopenharmony_ci */ 11818c2ecf20Sopenharmony_civoid 11828c2ecf20Sopenharmony_cidsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb) 11838c2ecf20Sopenharmony_ci{ 11848c2ecf20Sopenharmony_ci u8 *d, *p; 11858c2ecf20Sopenharmony_ci int len = skb->len; 11868c2ecf20Sopenharmony_ci struct mISDNhead *hh = mISDN_HEAD_P(skb); 11878c2ecf20Sopenharmony_ci int w, i, ii; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci /* check if we have sompen */ 11908c2ecf20Sopenharmony_ci if (len < 1) 11918c2ecf20Sopenharmony_ci return; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci /* half of the buffer should be larger than maximum packet size */ 11948c2ecf20Sopenharmony_ci if (len >= CMX_BUFF_HALF) { 11958c2ecf20Sopenharmony_ci printk(KERN_ERR 11968c2ecf20Sopenharmony_ci "%s line %d: packet from card is too large (%d bytes). " 11978c2ecf20Sopenharmony_ci "please make card send smaller packets OR increase " 11988c2ecf20Sopenharmony_ci "CMX_BUFF_SIZE\n", __FILE__, __LINE__, len); 11998c2ecf20Sopenharmony_ci return; 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci /* 12038c2ecf20Sopenharmony_ci * initialize pointers if not already - 12048c2ecf20Sopenharmony_ci * also add delay if requested by PH_SIGNAL 12058c2ecf20Sopenharmony_ci */ 12068c2ecf20Sopenharmony_ci if (dsp->rx_init) { 12078c2ecf20Sopenharmony_ci dsp->rx_init = 0; 12088c2ecf20Sopenharmony_ci if (dsp->features.unordered) { 12098c2ecf20Sopenharmony_ci dsp->rx_R = (hh->id & CMX_BUFF_MASK); 12108c2ecf20Sopenharmony_ci if (dsp->cmx_delay) 12118c2ecf20Sopenharmony_ci dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) 12128c2ecf20Sopenharmony_ci & CMX_BUFF_MASK; 12138c2ecf20Sopenharmony_ci else 12148c2ecf20Sopenharmony_ci dsp->rx_W = (dsp->rx_R + (dsp_poll >> 1)) 12158c2ecf20Sopenharmony_ci & CMX_BUFF_MASK; 12168c2ecf20Sopenharmony_ci } else { 12178c2ecf20Sopenharmony_ci dsp->rx_R = 0; 12188c2ecf20Sopenharmony_ci if (dsp->cmx_delay) 12198c2ecf20Sopenharmony_ci dsp->rx_W = dsp->cmx_delay; 12208c2ecf20Sopenharmony_ci else 12218c2ecf20Sopenharmony_ci dsp->rx_W = dsp_poll >> 1; 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci /* if frame contains time code, write directly */ 12258c2ecf20Sopenharmony_ci if (dsp->features.unordered) { 12268c2ecf20Sopenharmony_ci dsp->rx_W = (hh->id & CMX_BUFF_MASK); 12278c2ecf20Sopenharmony_ci /* printk(KERN_DEBUG "%s %08x\n", dsp->name, hh->id); */ 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci /* 12308c2ecf20Sopenharmony_ci * if we underrun (or maybe overrun), 12318c2ecf20Sopenharmony_ci * we set our new read pointer, and write silence to buffer 12328c2ecf20Sopenharmony_ci */ 12338c2ecf20Sopenharmony_ci if (((dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) { 12348c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CLOCK) 12358c2ecf20Sopenharmony_ci printk(KERN_DEBUG 12368c2ecf20Sopenharmony_ci "cmx_receive(dsp=%lx): UNDERRUN (or overrun the " 12378c2ecf20Sopenharmony_ci "maximum delay), adjusting read pointer! " 12388c2ecf20Sopenharmony_ci "(inst %s)\n", (u_long)dsp, dsp->name); 12398c2ecf20Sopenharmony_ci /* flush rx buffer and set delay to dsp_poll / 2 */ 12408c2ecf20Sopenharmony_ci if (dsp->features.unordered) { 12418c2ecf20Sopenharmony_ci dsp->rx_R = (hh->id & CMX_BUFF_MASK); 12428c2ecf20Sopenharmony_ci if (dsp->cmx_delay) 12438c2ecf20Sopenharmony_ci dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) 12448c2ecf20Sopenharmony_ci & CMX_BUFF_MASK; 12458c2ecf20Sopenharmony_ci else 12468c2ecf20Sopenharmony_ci dsp->rx_W = (dsp->rx_R + (dsp_poll >> 1)) 12478c2ecf20Sopenharmony_ci & CMX_BUFF_MASK; 12488c2ecf20Sopenharmony_ci } else { 12498c2ecf20Sopenharmony_ci dsp->rx_R = 0; 12508c2ecf20Sopenharmony_ci if (dsp->cmx_delay) 12518c2ecf20Sopenharmony_ci dsp->rx_W = dsp->cmx_delay; 12528c2ecf20Sopenharmony_ci else 12538c2ecf20Sopenharmony_ci dsp->rx_W = dsp_poll >> 1; 12548c2ecf20Sopenharmony_ci } 12558c2ecf20Sopenharmony_ci memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); 12568c2ecf20Sopenharmony_ci } 12578c2ecf20Sopenharmony_ci /* if we have reached double delay, jump back to middle */ 12588c2ecf20Sopenharmony_ci if (dsp->cmx_delay) 12598c2ecf20Sopenharmony_ci if (((dsp->rx_W - dsp->rx_R) & CMX_BUFF_MASK) >= 12608c2ecf20Sopenharmony_ci (dsp->cmx_delay << 1)) { 12618c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CLOCK) 12628c2ecf20Sopenharmony_ci printk(KERN_DEBUG 12638c2ecf20Sopenharmony_ci "cmx_receive(dsp=%lx): OVERRUN (because " 12648c2ecf20Sopenharmony_ci "twice the delay is reached), adjusting " 12658c2ecf20Sopenharmony_ci "read pointer! (inst %s)\n", 12668c2ecf20Sopenharmony_ci (u_long)dsp, dsp->name); 12678c2ecf20Sopenharmony_ci /* flush buffer */ 12688c2ecf20Sopenharmony_ci if (dsp->features.unordered) { 12698c2ecf20Sopenharmony_ci dsp->rx_R = (hh->id & CMX_BUFF_MASK); 12708c2ecf20Sopenharmony_ci dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) 12718c2ecf20Sopenharmony_ci & CMX_BUFF_MASK; 12728c2ecf20Sopenharmony_ci } else { 12738c2ecf20Sopenharmony_ci dsp->rx_R = 0; 12748c2ecf20Sopenharmony_ci dsp->rx_W = dsp->cmx_delay; 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); 12778c2ecf20Sopenharmony_ci } 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci /* show where to write */ 12808c2ecf20Sopenharmony_ci#ifdef CMX_DEBUG 12818c2ecf20Sopenharmony_ci printk(KERN_DEBUG 12828c2ecf20Sopenharmony_ci "cmx_receive(dsp=%lx): rx_R(dsp)=%05x rx_W(dsp)=%05x len=%d %s\n", 12838c2ecf20Sopenharmony_ci (u_long)dsp, dsp->rx_R, dsp->rx_W, len, dsp->name); 12848c2ecf20Sopenharmony_ci#endif 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci /* write data into rx_buffer */ 12878c2ecf20Sopenharmony_ci p = skb->data; 12888c2ecf20Sopenharmony_ci d = dsp->rx_buff; 12898c2ecf20Sopenharmony_ci w = dsp->rx_W; 12908c2ecf20Sopenharmony_ci i = 0; 12918c2ecf20Sopenharmony_ci ii = len; 12928c2ecf20Sopenharmony_ci while (i < ii) { 12938c2ecf20Sopenharmony_ci d[w++ & CMX_BUFF_MASK] = *p++; 12948c2ecf20Sopenharmony_ci i++; 12958c2ecf20Sopenharmony_ci } 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci /* increase write-pointer */ 12988c2ecf20Sopenharmony_ci dsp->rx_W = ((dsp->rx_W + len) & CMX_BUFF_MASK); 12998c2ecf20Sopenharmony_ci#ifdef CMX_DELAY_DEBUG 13008c2ecf20Sopenharmony_ci showdelay(dsp, len, (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK); 13018c2ecf20Sopenharmony_ci#endif 13028c2ecf20Sopenharmony_ci} 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci/* 13068c2ecf20Sopenharmony_ci * send (mixed) audio data to card and control jitter 13078c2ecf20Sopenharmony_ci */ 13088c2ecf20Sopenharmony_cistatic void 13098c2ecf20Sopenharmony_cidsp_cmx_send_member(struct dsp *dsp, int len, s32 *c, int members) 13108c2ecf20Sopenharmony_ci{ 13118c2ecf20Sopenharmony_ci struct dsp_conf *conf = dsp->conf; 13128c2ecf20Sopenharmony_ci struct dsp *member, *other; 13138c2ecf20Sopenharmony_ci register s32 sample; 13148c2ecf20Sopenharmony_ci u8 *d, *p, *q, *o_q; 13158c2ecf20Sopenharmony_ci struct sk_buff *nskb, *txskb; 13168c2ecf20Sopenharmony_ci int r, rr, t, tt, o_r, o_rr; 13178c2ecf20Sopenharmony_ci int preload = 0; 13188c2ecf20Sopenharmony_ci struct mISDNhead *hh, *thh; 13198c2ecf20Sopenharmony_ci int tx_data_only = 0; 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci /* don't process if: */ 13228c2ecf20Sopenharmony_ci if (!dsp->b_active) { /* if not active */ 13238c2ecf20Sopenharmony_ci dsp->last_tx = 0; 13248c2ecf20Sopenharmony_ci return; 13258c2ecf20Sopenharmony_ci } 13268c2ecf20Sopenharmony_ci if (((dsp->conf && dsp->conf->hardware) || /* hardware conf */ 13278c2ecf20Sopenharmony_ci dsp->echo.hardware) && /* OR hardware echo */ 13288c2ecf20Sopenharmony_ci dsp->tx_R == dsp->tx_W && /* AND no tx-data */ 13298c2ecf20Sopenharmony_ci !(dsp->tone.tone && dsp->tone.software)) { /* AND not soft tones */ 13308c2ecf20Sopenharmony_ci if (!dsp->tx_data) { /* no tx_data for user space required */ 13318c2ecf20Sopenharmony_ci dsp->last_tx = 0; 13328c2ecf20Sopenharmony_ci return; 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci if (dsp->conf && dsp->conf->software && dsp->conf->hardware) 13358c2ecf20Sopenharmony_ci tx_data_only = 1; 13368c2ecf20Sopenharmony_ci if (dsp->echo.software && dsp->echo.hardware) 13378c2ecf20Sopenharmony_ci tx_data_only = 1; 13388c2ecf20Sopenharmony_ci } 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci#ifdef CMX_DEBUG 13418c2ecf20Sopenharmony_ci printk(KERN_DEBUG 13428c2ecf20Sopenharmony_ci "SEND members=%d dsp=%s, conf=%p, rx_R=%05x rx_W=%05x\n", 13438c2ecf20Sopenharmony_ci members, dsp->name, conf, dsp->rx_R, dsp->rx_W); 13448c2ecf20Sopenharmony_ci#endif 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci /* preload if we have delay set */ 13478c2ecf20Sopenharmony_ci if (dsp->cmx_delay && !dsp->last_tx) { 13488c2ecf20Sopenharmony_ci preload = len; 13498c2ecf20Sopenharmony_ci if (preload < 128) 13508c2ecf20Sopenharmony_ci preload = 128; 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci /* PREPARE RESULT */ 13548c2ecf20Sopenharmony_ci nskb = mI_alloc_skb(len + preload, GFP_ATOMIC); 13558c2ecf20Sopenharmony_ci if (!nskb) { 13568c2ecf20Sopenharmony_ci printk(KERN_ERR 13578c2ecf20Sopenharmony_ci "FATAL ERROR in mISDN_dsp.o: cannot alloc %d bytes\n", 13588c2ecf20Sopenharmony_ci len + preload); 13598c2ecf20Sopenharmony_ci return; 13608c2ecf20Sopenharmony_ci } 13618c2ecf20Sopenharmony_ci hh = mISDN_HEAD_P(nskb); 13628c2ecf20Sopenharmony_ci hh->prim = PH_DATA_REQ; 13638c2ecf20Sopenharmony_ci hh->id = 0; 13648c2ecf20Sopenharmony_ci dsp->last_tx = 1; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci /* set pointers, indexes and stuff */ 13678c2ecf20Sopenharmony_ci member = dsp; 13688c2ecf20Sopenharmony_ci p = dsp->tx_buff; /* transmit data */ 13698c2ecf20Sopenharmony_ci q = dsp->rx_buff; /* received data */ 13708c2ecf20Sopenharmony_ci d = skb_put(nskb, preload + len); /* result */ 13718c2ecf20Sopenharmony_ci t = dsp->tx_R; /* tx-pointers */ 13728c2ecf20Sopenharmony_ci tt = dsp->tx_W; 13738c2ecf20Sopenharmony_ci r = dsp->rx_R; /* rx-pointers */ 13748c2ecf20Sopenharmony_ci rr = (r + len) & CMX_BUFF_MASK; 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci /* preload with silence, if required */ 13778c2ecf20Sopenharmony_ci if (preload) { 13788c2ecf20Sopenharmony_ci memset(d, dsp_silence, preload); 13798c2ecf20Sopenharmony_ci d += preload; 13808c2ecf20Sopenharmony_ci } 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci /* PROCESS TONES/TX-DATA ONLY */ 13838c2ecf20Sopenharmony_ci if (dsp->tone.tone && dsp->tone.software) { 13848c2ecf20Sopenharmony_ci /* -> copy tone */ 13858c2ecf20Sopenharmony_ci dsp_tone_copy(dsp, d, len); 13868c2ecf20Sopenharmony_ci dsp->tx_R = 0; /* clear tx buffer */ 13878c2ecf20Sopenharmony_ci dsp->tx_W = 0; 13888c2ecf20Sopenharmony_ci goto send_packet; 13898c2ecf20Sopenharmony_ci } 13908c2ecf20Sopenharmony_ci /* if we have tx-data but do not use mixing */ 13918c2ecf20Sopenharmony_ci if (!dsp->tx_mix && t != tt) { 13928c2ecf20Sopenharmony_ci /* -> send tx-data and continue when not enough */ 13938c2ecf20Sopenharmony_ci#ifdef CMX_TX_DEBUG 13948c2ecf20Sopenharmony_ci sprintf(debugbuf, "TX sending (%04x-%04x)%p: ", t, tt, p); 13958c2ecf20Sopenharmony_ci#endif 13968c2ecf20Sopenharmony_ci while (r != rr && t != tt) { 13978c2ecf20Sopenharmony_ci#ifdef CMX_TX_DEBUG 13988c2ecf20Sopenharmony_ci if (strlen(debugbuf) < 48) 13998c2ecf20Sopenharmony_ci sprintf(debugbuf + strlen(debugbuf), " %02x", 14008c2ecf20Sopenharmony_ci p[t]); 14018c2ecf20Sopenharmony_ci#endif 14028c2ecf20Sopenharmony_ci *d++ = p[t]; /* write tx_buff */ 14038c2ecf20Sopenharmony_ci t = (t + 1) & CMX_BUFF_MASK; 14048c2ecf20Sopenharmony_ci r = (r + 1) & CMX_BUFF_MASK; 14058c2ecf20Sopenharmony_ci } 14068c2ecf20Sopenharmony_ci if (r == rr) { 14078c2ecf20Sopenharmony_ci dsp->tx_R = t; 14088c2ecf20Sopenharmony_ci#ifdef CMX_TX_DEBUG 14098c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s\n", debugbuf); 14108c2ecf20Sopenharmony_ci#endif 14118c2ecf20Sopenharmony_ci goto send_packet; 14128c2ecf20Sopenharmony_ci } 14138c2ecf20Sopenharmony_ci } 14148c2ecf20Sopenharmony_ci#ifdef CMX_TX_DEBUG 14158c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s\n", debugbuf); 14168c2ecf20Sopenharmony_ci#endif 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci /* PROCESS DATA (one member / no conf) */ 14198c2ecf20Sopenharmony_ci if (!conf || members <= 1) { 14208c2ecf20Sopenharmony_ci /* -> if echo is NOT enabled */ 14218c2ecf20Sopenharmony_ci if (!dsp->echo.software) { 14228c2ecf20Sopenharmony_ci /* -> send tx-data if available or use 0-volume */ 14238c2ecf20Sopenharmony_ci while (r != rr && t != tt) { 14248c2ecf20Sopenharmony_ci *d++ = p[t]; /* write tx_buff */ 14258c2ecf20Sopenharmony_ci t = (t + 1) & CMX_BUFF_MASK; 14268c2ecf20Sopenharmony_ci r = (r + 1) & CMX_BUFF_MASK; 14278c2ecf20Sopenharmony_ci } 14288c2ecf20Sopenharmony_ci if (r != rr) { 14298c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CLOCK) 14308c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: RX empty\n", 14318c2ecf20Sopenharmony_ci __func__); 14328c2ecf20Sopenharmony_ci memset(d, dsp_silence, (rr - r) & CMX_BUFF_MASK); 14338c2ecf20Sopenharmony_ci } 14348c2ecf20Sopenharmony_ci /* -> if echo is enabled */ 14358c2ecf20Sopenharmony_ci } else { 14368c2ecf20Sopenharmony_ci /* 14378c2ecf20Sopenharmony_ci * -> mix tx-data with echo if available, 14388c2ecf20Sopenharmony_ci * or use echo only 14398c2ecf20Sopenharmony_ci */ 14408c2ecf20Sopenharmony_ci while (r != rr && t != tt) { 14418c2ecf20Sopenharmony_ci *d++ = dsp_audio_mix_law[(p[t] << 8) | q[r]]; 14428c2ecf20Sopenharmony_ci t = (t + 1) & CMX_BUFF_MASK; 14438c2ecf20Sopenharmony_ci r = (r + 1) & CMX_BUFF_MASK; 14448c2ecf20Sopenharmony_ci } 14458c2ecf20Sopenharmony_ci while (r != rr) { 14468c2ecf20Sopenharmony_ci *d++ = q[r]; /* echo */ 14478c2ecf20Sopenharmony_ci r = (r + 1) & CMX_BUFF_MASK; 14488c2ecf20Sopenharmony_ci } 14498c2ecf20Sopenharmony_ci } 14508c2ecf20Sopenharmony_ci dsp->tx_R = t; 14518c2ecf20Sopenharmony_ci goto send_packet; 14528c2ecf20Sopenharmony_ci } 14538c2ecf20Sopenharmony_ci /* PROCESS DATA (two members) */ 14548c2ecf20Sopenharmony_ci#ifdef CMX_CONF_DEBUG 14558c2ecf20Sopenharmony_ci if (0) { 14568c2ecf20Sopenharmony_ci#else 14578c2ecf20Sopenharmony_ci if (members == 2) { 14588c2ecf20Sopenharmony_ci#endif 14598c2ecf20Sopenharmony_ci /* "other" becomes other party */ 14608c2ecf20Sopenharmony_ci other = (list_entry(conf->mlist.next, 14618c2ecf20Sopenharmony_ci struct dsp_conf_member, list))->dsp; 14628c2ecf20Sopenharmony_ci if (other == member) 14638c2ecf20Sopenharmony_ci other = (list_entry(conf->mlist.prev, 14648c2ecf20Sopenharmony_ci struct dsp_conf_member, list))->dsp; 14658c2ecf20Sopenharmony_ci o_q = other->rx_buff; /* received data */ 14668c2ecf20Sopenharmony_ci o_rr = (other->rx_R + len) & CMX_BUFF_MASK; 14678c2ecf20Sopenharmony_ci /* end of rx-pointer */ 14688c2ecf20Sopenharmony_ci o_r = (o_rr - rr + r) & CMX_BUFF_MASK; 14698c2ecf20Sopenharmony_ci /* start rx-pointer at current read position*/ 14708c2ecf20Sopenharmony_ci /* -> if echo is NOT enabled */ 14718c2ecf20Sopenharmony_ci if (!dsp->echo.software) { 14728c2ecf20Sopenharmony_ci /* 14738c2ecf20Sopenharmony_ci * -> copy other member's rx-data, 14748c2ecf20Sopenharmony_ci * if tx-data is available, mix 14758c2ecf20Sopenharmony_ci */ 14768c2ecf20Sopenharmony_ci while (o_r != o_rr && t != tt) { 14778c2ecf20Sopenharmony_ci *d++ = dsp_audio_mix_law[(p[t] << 8) | o_q[o_r]]; 14788c2ecf20Sopenharmony_ci t = (t + 1) & CMX_BUFF_MASK; 14798c2ecf20Sopenharmony_ci o_r = (o_r + 1) & CMX_BUFF_MASK; 14808c2ecf20Sopenharmony_ci } 14818c2ecf20Sopenharmony_ci while (o_r != o_rr) { 14828c2ecf20Sopenharmony_ci *d++ = o_q[o_r]; 14838c2ecf20Sopenharmony_ci o_r = (o_r + 1) & CMX_BUFF_MASK; 14848c2ecf20Sopenharmony_ci } 14858c2ecf20Sopenharmony_ci /* -> if echo is enabled */ 14868c2ecf20Sopenharmony_ci } else { 14878c2ecf20Sopenharmony_ci /* 14888c2ecf20Sopenharmony_ci * -> mix other member's rx-data with echo, 14898c2ecf20Sopenharmony_ci * if tx-data is available, mix 14908c2ecf20Sopenharmony_ci */ 14918c2ecf20Sopenharmony_ci while (r != rr && t != tt) { 14928c2ecf20Sopenharmony_ci sample = dsp_audio_law_to_s32[p[t]] + 14938c2ecf20Sopenharmony_ci dsp_audio_law_to_s32[q[r]] + 14948c2ecf20Sopenharmony_ci dsp_audio_law_to_s32[o_q[o_r]]; 14958c2ecf20Sopenharmony_ci if (sample < -32768) 14968c2ecf20Sopenharmony_ci sample = -32768; 14978c2ecf20Sopenharmony_ci else if (sample > 32767) 14988c2ecf20Sopenharmony_ci sample = 32767; 14998c2ecf20Sopenharmony_ci *d++ = dsp_audio_s16_to_law[sample & 0xffff]; 15008c2ecf20Sopenharmony_ci /* tx-data + rx_data + echo */ 15018c2ecf20Sopenharmony_ci t = (t + 1) & CMX_BUFF_MASK; 15028c2ecf20Sopenharmony_ci r = (r + 1) & CMX_BUFF_MASK; 15038c2ecf20Sopenharmony_ci o_r = (o_r + 1) & CMX_BUFF_MASK; 15048c2ecf20Sopenharmony_ci } 15058c2ecf20Sopenharmony_ci while (r != rr) { 15068c2ecf20Sopenharmony_ci *d++ = dsp_audio_mix_law[(q[r] << 8) | o_q[o_r]]; 15078c2ecf20Sopenharmony_ci r = (r + 1) & CMX_BUFF_MASK; 15088c2ecf20Sopenharmony_ci o_r = (o_r + 1) & CMX_BUFF_MASK; 15098c2ecf20Sopenharmony_ci } 15108c2ecf20Sopenharmony_ci } 15118c2ecf20Sopenharmony_ci dsp->tx_R = t; 15128c2ecf20Sopenharmony_ci goto send_packet; 15138c2ecf20Sopenharmony_ci } 15148c2ecf20Sopenharmony_ci /* PROCESS DATA (three or more members) */ 15158c2ecf20Sopenharmony_ci /* -> if echo is NOT enabled */ 15168c2ecf20Sopenharmony_ci if (!dsp->echo.software) { 15178c2ecf20Sopenharmony_ci /* 15188c2ecf20Sopenharmony_ci * -> subtract rx-data from conf-data, 15198c2ecf20Sopenharmony_ci * if tx-data is available, mix 15208c2ecf20Sopenharmony_ci */ 15218c2ecf20Sopenharmony_ci while (r != rr && t != tt) { 15228c2ecf20Sopenharmony_ci sample = dsp_audio_law_to_s32[p[t]] + *c++ - 15238c2ecf20Sopenharmony_ci dsp_audio_law_to_s32[q[r]]; 15248c2ecf20Sopenharmony_ci if (sample < -32768) 15258c2ecf20Sopenharmony_ci sample = -32768; 15268c2ecf20Sopenharmony_ci else if (sample > 32767) 15278c2ecf20Sopenharmony_ci sample = 32767; 15288c2ecf20Sopenharmony_ci *d++ = dsp_audio_s16_to_law[sample & 0xffff]; 15298c2ecf20Sopenharmony_ci /* conf-rx+tx */ 15308c2ecf20Sopenharmony_ci r = (r + 1) & CMX_BUFF_MASK; 15318c2ecf20Sopenharmony_ci t = (t + 1) & CMX_BUFF_MASK; 15328c2ecf20Sopenharmony_ci } 15338c2ecf20Sopenharmony_ci while (r != rr) { 15348c2ecf20Sopenharmony_ci sample = *c++ - dsp_audio_law_to_s32[q[r]]; 15358c2ecf20Sopenharmony_ci if (sample < -32768) 15368c2ecf20Sopenharmony_ci sample = -32768; 15378c2ecf20Sopenharmony_ci else if (sample > 32767) 15388c2ecf20Sopenharmony_ci sample = 32767; 15398c2ecf20Sopenharmony_ci *d++ = dsp_audio_s16_to_law[sample & 0xffff]; 15408c2ecf20Sopenharmony_ci /* conf-rx */ 15418c2ecf20Sopenharmony_ci r = (r + 1) & CMX_BUFF_MASK; 15428c2ecf20Sopenharmony_ci } 15438c2ecf20Sopenharmony_ci /* -> if echo is enabled */ 15448c2ecf20Sopenharmony_ci } else { 15458c2ecf20Sopenharmony_ci /* 15468c2ecf20Sopenharmony_ci * -> encode conf-data, if tx-data 15478c2ecf20Sopenharmony_ci * is available, mix 15488c2ecf20Sopenharmony_ci */ 15498c2ecf20Sopenharmony_ci while (r != rr && t != tt) { 15508c2ecf20Sopenharmony_ci sample = dsp_audio_law_to_s32[p[t]] + *c++; 15518c2ecf20Sopenharmony_ci if (sample < -32768) 15528c2ecf20Sopenharmony_ci sample = -32768; 15538c2ecf20Sopenharmony_ci else if (sample > 32767) 15548c2ecf20Sopenharmony_ci sample = 32767; 15558c2ecf20Sopenharmony_ci *d++ = dsp_audio_s16_to_law[sample & 0xffff]; 15568c2ecf20Sopenharmony_ci /* conf(echo)+tx */ 15578c2ecf20Sopenharmony_ci t = (t + 1) & CMX_BUFF_MASK; 15588c2ecf20Sopenharmony_ci r = (r + 1) & CMX_BUFF_MASK; 15598c2ecf20Sopenharmony_ci } 15608c2ecf20Sopenharmony_ci while (r != rr) { 15618c2ecf20Sopenharmony_ci sample = *c++; 15628c2ecf20Sopenharmony_ci if (sample < -32768) 15638c2ecf20Sopenharmony_ci sample = -32768; 15648c2ecf20Sopenharmony_ci else if (sample > 32767) 15658c2ecf20Sopenharmony_ci sample = 32767; 15668c2ecf20Sopenharmony_ci *d++ = dsp_audio_s16_to_law[sample & 0xffff]; 15678c2ecf20Sopenharmony_ci /* conf(echo) */ 15688c2ecf20Sopenharmony_ci r = (r + 1) & CMX_BUFF_MASK; 15698c2ecf20Sopenharmony_ci } 15708c2ecf20Sopenharmony_ci } 15718c2ecf20Sopenharmony_ci dsp->tx_R = t; 15728c2ecf20Sopenharmony_ci goto send_packet; 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_cisend_packet: 15758c2ecf20Sopenharmony_ci /* 15768c2ecf20Sopenharmony_ci * send tx-data if enabled - don't filter, 15778c2ecf20Sopenharmony_ci * because we want what we send, not what we filtered 15788c2ecf20Sopenharmony_ci */ 15798c2ecf20Sopenharmony_ci if (dsp->tx_data) { 15808c2ecf20Sopenharmony_ci if (tx_data_only) { 15818c2ecf20Sopenharmony_ci hh->prim = DL_DATA_REQ; 15828c2ecf20Sopenharmony_ci hh->id = 0; 15838c2ecf20Sopenharmony_ci /* queue and trigger */ 15848c2ecf20Sopenharmony_ci skb_queue_tail(&dsp->sendq, nskb); 15858c2ecf20Sopenharmony_ci schedule_work(&dsp->workq); 15868c2ecf20Sopenharmony_ci /* exit because only tx_data is used */ 15878c2ecf20Sopenharmony_ci return; 15888c2ecf20Sopenharmony_ci } else { 15898c2ecf20Sopenharmony_ci txskb = mI_alloc_skb(len, GFP_ATOMIC); 15908c2ecf20Sopenharmony_ci if (!txskb) { 15918c2ecf20Sopenharmony_ci printk(KERN_ERR 15928c2ecf20Sopenharmony_ci "FATAL ERROR in mISDN_dsp.o: " 15938c2ecf20Sopenharmony_ci "cannot alloc %d bytes\n", len); 15948c2ecf20Sopenharmony_ci } else { 15958c2ecf20Sopenharmony_ci thh = mISDN_HEAD_P(txskb); 15968c2ecf20Sopenharmony_ci thh->prim = DL_DATA_REQ; 15978c2ecf20Sopenharmony_ci thh->id = 0; 15988c2ecf20Sopenharmony_ci skb_put_data(txskb, nskb->data + preload, len); 15998c2ecf20Sopenharmony_ci /* queue (trigger later) */ 16008c2ecf20Sopenharmony_ci skb_queue_tail(&dsp->sendq, txskb); 16018c2ecf20Sopenharmony_ci } 16028c2ecf20Sopenharmony_ci } 16038c2ecf20Sopenharmony_ci } 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci /* send data only to card, if we don't just calculated tx_data */ 16068c2ecf20Sopenharmony_ci /* adjust volume */ 16078c2ecf20Sopenharmony_ci if (dsp->tx_volume) 16088c2ecf20Sopenharmony_ci dsp_change_volume(nskb, dsp->tx_volume); 16098c2ecf20Sopenharmony_ci /* pipeline */ 16108c2ecf20Sopenharmony_ci if (dsp->pipeline.inuse) 16118c2ecf20Sopenharmony_ci dsp_pipeline_process_tx(&dsp->pipeline, nskb->data, 16128c2ecf20Sopenharmony_ci nskb->len); 16138c2ecf20Sopenharmony_ci /* crypt */ 16148c2ecf20Sopenharmony_ci if (dsp->bf_enable) 16158c2ecf20Sopenharmony_ci dsp_bf_encrypt(dsp, nskb->data, nskb->len); 16168c2ecf20Sopenharmony_ci /* queue and trigger */ 16178c2ecf20Sopenharmony_ci skb_queue_tail(&dsp->sendq, nskb); 16188c2ecf20Sopenharmony_ci schedule_work(&dsp->workq); 16198c2ecf20Sopenharmony_ci} 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_cistatic u32 jittercount; /* counter for jitter check */ 16228c2ecf20Sopenharmony_cistruct timer_list dsp_spl_tl; 16238c2ecf20Sopenharmony_ciunsigned long dsp_spl_jiffies; /* calculate the next time to fire */ 16248c2ecf20Sopenharmony_cistatic u16 dsp_count; /* last sample count */ 16258c2ecf20Sopenharmony_cistatic int dsp_count_valid; /* if we have last sample count */ 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_civoid 16288c2ecf20Sopenharmony_cidsp_cmx_send(struct timer_list *arg) 16298c2ecf20Sopenharmony_ci{ 16308c2ecf20Sopenharmony_ci struct dsp_conf *conf; 16318c2ecf20Sopenharmony_ci struct dsp_conf_member *member; 16328c2ecf20Sopenharmony_ci struct dsp *dsp; 16338c2ecf20Sopenharmony_ci int mustmix, members; 16348c2ecf20Sopenharmony_ci static s32 mixbuffer[MAX_POLL + 100]; 16358c2ecf20Sopenharmony_ci s32 *c; 16368c2ecf20Sopenharmony_ci u8 *p, *q; 16378c2ecf20Sopenharmony_ci int r, rr; 16388c2ecf20Sopenharmony_ci int jittercheck = 0, delay, i; 16398c2ecf20Sopenharmony_ci u_long flags; 16408c2ecf20Sopenharmony_ci u16 length, count; 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci /* lock */ 16438c2ecf20Sopenharmony_ci spin_lock_irqsave(&dsp_lock, flags); 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci if (!dsp_count_valid) { 16468c2ecf20Sopenharmony_ci dsp_count = mISDN_clock_get(); 16478c2ecf20Sopenharmony_ci length = dsp_poll; 16488c2ecf20Sopenharmony_ci dsp_count_valid = 1; 16498c2ecf20Sopenharmony_ci } else { 16508c2ecf20Sopenharmony_ci count = mISDN_clock_get(); 16518c2ecf20Sopenharmony_ci length = count - dsp_count; 16528c2ecf20Sopenharmony_ci dsp_count = count; 16538c2ecf20Sopenharmony_ci } 16548c2ecf20Sopenharmony_ci if (length > MAX_POLL + 100) 16558c2ecf20Sopenharmony_ci length = MAX_POLL + 100; 16568c2ecf20Sopenharmony_ci /* printk(KERN_DEBUG "len=%d dsp_count=0x%x\n", length, dsp_count); */ 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci /* 16598c2ecf20Sopenharmony_ci * check if jitter needs to be checked (this is every second) 16608c2ecf20Sopenharmony_ci */ 16618c2ecf20Sopenharmony_ci jittercount += length; 16628c2ecf20Sopenharmony_ci if (jittercount >= 8000) { 16638c2ecf20Sopenharmony_ci jittercount -= 8000; 16648c2ecf20Sopenharmony_ci jittercheck = 1; 16658c2ecf20Sopenharmony_ci } 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci /* loop all members that do not require conference mixing */ 16688c2ecf20Sopenharmony_ci list_for_each_entry(dsp, &dsp_ilist, list) { 16698c2ecf20Sopenharmony_ci if (dsp->hdlc) 16708c2ecf20Sopenharmony_ci continue; 16718c2ecf20Sopenharmony_ci conf = dsp->conf; 16728c2ecf20Sopenharmony_ci mustmix = 0; 16738c2ecf20Sopenharmony_ci members = 0; 16748c2ecf20Sopenharmony_ci if (conf) { 16758c2ecf20Sopenharmony_ci members = count_list_member(&conf->mlist); 16768c2ecf20Sopenharmony_ci#ifdef CMX_CONF_DEBUG 16778c2ecf20Sopenharmony_ci if (conf->software && members > 1) 16788c2ecf20Sopenharmony_ci#else 16798c2ecf20Sopenharmony_ci if (conf->software && members > 2) 16808c2ecf20Sopenharmony_ci#endif 16818c2ecf20Sopenharmony_ci mustmix = 1; 16828c2ecf20Sopenharmony_ci } 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci /* transmission required */ 16858c2ecf20Sopenharmony_ci if (!mustmix) { 16868c2ecf20Sopenharmony_ci dsp_cmx_send_member(dsp, length, mixbuffer, members); 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci /* 16898c2ecf20Sopenharmony_ci * unused mixbuffer is given to prevent a 16908c2ecf20Sopenharmony_ci * potential null-pointer-bug 16918c2ecf20Sopenharmony_ci */ 16928c2ecf20Sopenharmony_ci } 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci /* loop all members that require conference mixing */ 16968c2ecf20Sopenharmony_ci list_for_each_entry(conf, &conf_ilist, list) { 16978c2ecf20Sopenharmony_ci /* count members and check hardware */ 16988c2ecf20Sopenharmony_ci members = count_list_member(&conf->mlist); 16998c2ecf20Sopenharmony_ci#ifdef CMX_CONF_DEBUG 17008c2ecf20Sopenharmony_ci if (conf->software && members > 1) { 17018c2ecf20Sopenharmony_ci#else 17028c2ecf20Sopenharmony_ci if (conf->software && members > 2) { 17038c2ecf20Sopenharmony_ci#endif 17048c2ecf20Sopenharmony_ci /* check for hdlc conf */ 17058c2ecf20Sopenharmony_ci member = list_entry(conf->mlist.next, 17068c2ecf20Sopenharmony_ci struct dsp_conf_member, list); 17078c2ecf20Sopenharmony_ci if (member->dsp->hdlc) 17088c2ecf20Sopenharmony_ci continue; 17098c2ecf20Sopenharmony_ci /* mix all data */ 17108c2ecf20Sopenharmony_ci memset(mixbuffer, 0, length * sizeof(s32)); 17118c2ecf20Sopenharmony_ci list_for_each_entry(member, &conf->mlist, list) { 17128c2ecf20Sopenharmony_ci dsp = member->dsp; 17138c2ecf20Sopenharmony_ci /* get range of data to mix */ 17148c2ecf20Sopenharmony_ci c = mixbuffer; 17158c2ecf20Sopenharmony_ci q = dsp->rx_buff; 17168c2ecf20Sopenharmony_ci r = dsp->rx_R; 17178c2ecf20Sopenharmony_ci rr = (r + length) & CMX_BUFF_MASK; 17188c2ecf20Sopenharmony_ci /* add member's data */ 17198c2ecf20Sopenharmony_ci while (r != rr) { 17208c2ecf20Sopenharmony_ci *c++ += dsp_audio_law_to_s32[q[r]]; 17218c2ecf20Sopenharmony_ci r = (r + 1) & CMX_BUFF_MASK; 17228c2ecf20Sopenharmony_ci } 17238c2ecf20Sopenharmony_ci } 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci /* process each member */ 17268c2ecf20Sopenharmony_ci list_for_each_entry(member, &conf->mlist, list) { 17278c2ecf20Sopenharmony_ci /* transmission */ 17288c2ecf20Sopenharmony_ci dsp_cmx_send_member(member->dsp, length, 17298c2ecf20Sopenharmony_ci mixbuffer, members); 17308c2ecf20Sopenharmony_ci } 17318c2ecf20Sopenharmony_ci } 17328c2ecf20Sopenharmony_ci } 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci /* delete rx-data, increment buffers, change pointers */ 17358c2ecf20Sopenharmony_ci list_for_each_entry(dsp, &dsp_ilist, list) { 17368c2ecf20Sopenharmony_ci if (dsp->hdlc) 17378c2ecf20Sopenharmony_ci continue; 17388c2ecf20Sopenharmony_ci p = dsp->rx_buff; 17398c2ecf20Sopenharmony_ci q = dsp->tx_buff; 17408c2ecf20Sopenharmony_ci r = dsp->rx_R; 17418c2ecf20Sopenharmony_ci /* move receive pointer when receiving */ 17428c2ecf20Sopenharmony_ci if (!dsp->rx_is_off) { 17438c2ecf20Sopenharmony_ci rr = (r + length) & CMX_BUFF_MASK; 17448c2ecf20Sopenharmony_ci /* delete rx-data */ 17458c2ecf20Sopenharmony_ci while (r != rr) { 17468c2ecf20Sopenharmony_ci p[r] = dsp_silence; 17478c2ecf20Sopenharmony_ci r = (r + 1) & CMX_BUFF_MASK; 17488c2ecf20Sopenharmony_ci } 17498c2ecf20Sopenharmony_ci /* increment rx-buffer pointer */ 17508c2ecf20Sopenharmony_ci dsp->rx_R = r; /* write incremented read pointer */ 17518c2ecf20Sopenharmony_ci } 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci /* check current rx_delay */ 17548c2ecf20Sopenharmony_ci delay = (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK; 17558c2ecf20Sopenharmony_ci if (delay >= CMX_BUFF_HALF) 17568c2ecf20Sopenharmony_ci delay = 0; /* will be the delay before next write */ 17578c2ecf20Sopenharmony_ci /* check for lower delay */ 17588c2ecf20Sopenharmony_ci if (delay < dsp->rx_delay[0]) 17598c2ecf20Sopenharmony_ci dsp->rx_delay[0] = delay; 17608c2ecf20Sopenharmony_ci /* check current tx_delay */ 17618c2ecf20Sopenharmony_ci delay = (dsp->tx_W-dsp->tx_R) & CMX_BUFF_MASK; 17628c2ecf20Sopenharmony_ci if (delay >= CMX_BUFF_HALF) 17638c2ecf20Sopenharmony_ci delay = 0; /* will be the delay before next write */ 17648c2ecf20Sopenharmony_ci /* check for lower delay */ 17658c2ecf20Sopenharmony_ci if (delay < dsp->tx_delay[0]) 17668c2ecf20Sopenharmony_ci dsp->tx_delay[0] = delay; 17678c2ecf20Sopenharmony_ci if (jittercheck) { 17688c2ecf20Sopenharmony_ci /* find the lowest of all rx_delays */ 17698c2ecf20Sopenharmony_ci delay = dsp->rx_delay[0]; 17708c2ecf20Sopenharmony_ci i = 1; 17718c2ecf20Sopenharmony_ci while (i < MAX_SECONDS_JITTER_CHECK) { 17728c2ecf20Sopenharmony_ci if (delay > dsp->rx_delay[i]) 17738c2ecf20Sopenharmony_ci delay = dsp->rx_delay[i]; 17748c2ecf20Sopenharmony_ci i++; 17758c2ecf20Sopenharmony_ci } 17768c2ecf20Sopenharmony_ci /* 17778c2ecf20Sopenharmony_ci * remove rx_delay only if we have delay AND we 17788c2ecf20Sopenharmony_ci * have not preset cmx_delay AND 17798c2ecf20Sopenharmony_ci * the delay is greater dsp_poll 17808c2ecf20Sopenharmony_ci */ 17818c2ecf20Sopenharmony_ci if (delay > dsp_poll && !dsp->cmx_delay) { 17828c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CLOCK) 17838c2ecf20Sopenharmony_ci printk(KERN_DEBUG 17848c2ecf20Sopenharmony_ci "%s lowest rx_delay of %d bytes for" 17858c2ecf20Sopenharmony_ci " dsp %s are now removed.\n", 17868c2ecf20Sopenharmony_ci __func__, delay, 17878c2ecf20Sopenharmony_ci dsp->name); 17888c2ecf20Sopenharmony_ci r = dsp->rx_R; 17898c2ecf20Sopenharmony_ci rr = (r + delay - (dsp_poll >> 1)) 17908c2ecf20Sopenharmony_ci & CMX_BUFF_MASK; 17918c2ecf20Sopenharmony_ci /* delete rx-data */ 17928c2ecf20Sopenharmony_ci while (r != rr) { 17938c2ecf20Sopenharmony_ci p[r] = dsp_silence; 17948c2ecf20Sopenharmony_ci r = (r + 1) & CMX_BUFF_MASK; 17958c2ecf20Sopenharmony_ci } 17968c2ecf20Sopenharmony_ci /* increment rx-buffer pointer */ 17978c2ecf20Sopenharmony_ci dsp->rx_R = r; 17988c2ecf20Sopenharmony_ci /* write incremented read pointer */ 17998c2ecf20Sopenharmony_ci } 18008c2ecf20Sopenharmony_ci /* find the lowest of all tx_delays */ 18018c2ecf20Sopenharmony_ci delay = dsp->tx_delay[0]; 18028c2ecf20Sopenharmony_ci i = 1; 18038c2ecf20Sopenharmony_ci while (i < MAX_SECONDS_JITTER_CHECK) { 18048c2ecf20Sopenharmony_ci if (delay > dsp->tx_delay[i]) 18058c2ecf20Sopenharmony_ci delay = dsp->tx_delay[i]; 18068c2ecf20Sopenharmony_ci i++; 18078c2ecf20Sopenharmony_ci } 18088c2ecf20Sopenharmony_ci /* 18098c2ecf20Sopenharmony_ci * remove delay only if we have delay AND we 18108c2ecf20Sopenharmony_ci * have enabled tx_dejitter 18118c2ecf20Sopenharmony_ci */ 18128c2ecf20Sopenharmony_ci if (delay > dsp_poll && dsp->tx_dejitter) { 18138c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CLOCK) 18148c2ecf20Sopenharmony_ci printk(KERN_DEBUG 18158c2ecf20Sopenharmony_ci "%s lowest tx_delay of %d bytes for" 18168c2ecf20Sopenharmony_ci " dsp %s are now removed.\n", 18178c2ecf20Sopenharmony_ci __func__, delay, 18188c2ecf20Sopenharmony_ci dsp->name); 18198c2ecf20Sopenharmony_ci r = dsp->tx_R; 18208c2ecf20Sopenharmony_ci rr = (r + delay - (dsp_poll >> 1)) 18218c2ecf20Sopenharmony_ci & CMX_BUFF_MASK; 18228c2ecf20Sopenharmony_ci /* delete tx-data */ 18238c2ecf20Sopenharmony_ci while (r != rr) { 18248c2ecf20Sopenharmony_ci q[r] = dsp_silence; 18258c2ecf20Sopenharmony_ci r = (r + 1) & CMX_BUFF_MASK; 18268c2ecf20Sopenharmony_ci } 18278c2ecf20Sopenharmony_ci /* increment rx-buffer pointer */ 18288c2ecf20Sopenharmony_ci dsp->tx_R = r; 18298c2ecf20Sopenharmony_ci /* write incremented read pointer */ 18308c2ecf20Sopenharmony_ci } 18318c2ecf20Sopenharmony_ci /* scroll up delays */ 18328c2ecf20Sopenharmony_ci i = MAX_SECONDS_JITTER_CHECK - 1; 18338c2ecf20Sopenharmony_ci while (i) { 18348c2ecf20Sopenharmony_ci dsp->rx_delay[i] = dsp->rx_delay[i - 1]; 18358c2ecf20Sopenharmony_ci dsp->tx_delay[i] = dsp->tx_delay[i - 1]; 18368c2ecf20Sopenharmony_ci i--; 18378c2ecf20Sopenharmony_ci } 18388c2ecf20Sopenharmony_ci dsp->tx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */ 18398c2ecf20Sopenharmony_ci dsp->rx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */ 18408c2ecf20Sopenharmony_ci } 18418c2ecf20Sopenharmony_ci } 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci /* if next event would be in the past ... */ 18448c2ecf20Sopenharmony_ci if ((s32)(dsp_spl_jiffies + dsp_tics-jiffies) <= 0) 18458c2ecf20Sopenharmony_ci dsp_spl_jiffies = jiffies + 1; 18468c2ecf20Sopenharmony_ci else 18478c2ecf20Sopenharmony_ci dsp_spl_jiffies += dsp_tics; 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci dsp_spl_tl.expires = dsp_spl_jiffies; 18508c2ecf20Sopenharmony_ci add_timer(&dsp_spl_tl); 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci /* unlock */ 18538c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dsp_lock, flags); 18548c2ecf20Sopenharmony_ci} 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci/* 18578c2ecf20Sopenharmony_ci * audio data is transmitted from upper layer to the dsp 18588c2ecf20Sopenharmony_ci */ 18598c2ecf20Sopenharmony_civoid 18608c2ecf20Sopenharmony_cidsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb) 18618c2ecf20Sopenharmony_ci{ 18628c2ecf20Sopenharmony_ci u_int w, ww; 18638c2ecf20Sopenharmony_ci u8 *d, *p; 18648c2ecf20Sopenharmony_ci int space; /* todo: , l = skb->len; */ 18658c2ecf20Sopenharmony_ci#ifdef CMX_TX_DEBUG 18668c2ecf20Sopenharmony_ci char debugbuf[256] = ""; 18678c2ecf20Sopenharmony_ci#endif 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci /* check if there is enough space, and then copy */ 18708c2ecf20Sopenharmony_ci w = dsp->tx_W; 18718c2ecf20Sopenharmony_ci ww = dsp->tx_R; 18728c2ecf20Sopenharmony_ci p = dsp->tx_buff; 18738c2ecf20Sopenharmony_ci d = skb->data; 18748c2ecf20Sopenharmony_ci space = (ww - w - 1) & CMX_BUFF_MASK; 18758c2ecf20Sopenharmony_ci /* write-pointer should not overrun nor reach read pointer */ 18768c2ecf20Sopenharmony_ci if (space < skb->len) { 18778c2ecf20Sopenharmony_ci /* write to the space we have left */ 18788c2ecf20Sopenharmony_ci ww = (ww - 1) & CMX_BUFF_MASK; /* end one byte prior tx_R */ 18798c2ecf20Sopenharmony_ci if (dsp_debug & DEBUG_DSP_CLOCK) 18808c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: TX overflow space=%d skb->len=" 18818c2ecf20Sopenharmony_ci "%d, w=0x%04x, ww=0x%04x\n", __func__, space, 18828c2ecf20Sopenharmony_ci skb->len, w, ww); 18838c2ecf20Sopenharmony_ci } else 18848c2ecf20Sopenharmony_ci /* write until all byte are copied */ 18858c2ecf20Sopenharmony_ci ww = (w + skb->len) & CMX_BUFF_MASK; 18868c2ecf20Sopenharmony_ci dsp->tx_W = ww; 18878c2ecf20Sopenharmony_ci /* show current buffer */ 18888c2ecf20Sopenharmony_ci#ifdef CMX_DEBUG 18898c2ecf20Sopenharmony_ci printk(KERN_DEBUG 18908c2ecf20Sopenharmony_ci "cmx_transmit(dsp=%lx) %d bytes to 0x%x-0x%x. %s\n", 18918c2ecf20Sopenharmony_ci (u_long)dsp, (ww - w) & CMX_BUFF_MASK, w, ww, dsp->name); 18928c2ecf20Sopenharmony_ci#endif 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci /* copy transmit data to tx-buffer */ 18958c2ecf20Sopenharmony_ci#ifdef CMX_TX_DEBUG 18968c2ecf20Sopenharmony_ci sprintf(debugbuf, "TX getting (%04x-%04x)%p: ", w, ww, p); 18978c2ecf20Sopenharmony_ci#endif 18988c2ecf20Sopenharmony_ci while (w != ww) { 18998c2ecf20Sopenharmony_ci#ifdef CMX_TX_DEBUG 19008c2ecf20Sopenharmony_ci if (strlen(debugbuf) < 48) 19018c2ecf20Sopenharmony_ci sprintf(debugbuf + strlen(debugbuf), " %02x", *d); 19028c2ecf20Sopenharmony_ci#endif 19038c2ecf20Sopenharmony_ci p[w] = *d++; 19048c2ecf20Sopenharmony_ci w = (w + 1) & CMX_BUFF_MASK; 19058c2ecf20Sopenharmony_ci } 19068c2ecf20Sopenharmony_ci#ifdef CMX_TX_DEBUG 19078c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s\n", debugbuf); 19088c2ecf20Sopenharmony_ci#endif 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci} 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci/* 19138c2ecf20Sopenharmony_ci * hdlc data is received from card and sent to all members. 19148c2ecf20Sopenharmony_ci */ 19158c2ecf20Sopenharmony_civoid 19168c2ecf20Sopenharmony_cidsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb) 19178c2ecf20Sopenharmony_ci{ 19188c2ecf20Sopenharmony_ci struct sk_buff *nskb = NULL; 19198c2ecf20Sopenharmony_ci struct dsp_conf_member *member; 19208c2ecf20Sopenharmony_ci struct mISDNhead *hh; 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci /* not if not active */ 19238c2ecf20Sopenharmony_ci if (!dsp->b_active) 19248c2ecf20Sopenharmony_ci return; 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci /* check if we have sompen */ 19278c2ecf20Sopenharmony_ci if (skb->len < 1) 19288c2ecf20Sopenharmony_ci return; 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci /* no conf */ 19318c2ecf20Sopenharmony_ci if (!dsp->conf) { 19328c2ecf20Sopenharmony_ci /* in case of software echo */ 19338c2ecf20Sopenharmony_ci if (dsp->echo.software) { 19348c2ecf20Sopenharmony_ci nskb = skb_clone(skb, GFP_ATOMIC); 19358c2ecf20Sopenharmony_ci if (nskb) { 19368c2ecf20Sopenharmony_ci hh = mISDN_HEAD_P(nskb); 19378c2ecf20Sopenharmony_ci hh->prim = PH_DATA_REQ; 19388c2ecf20Sopenharmony_ci hh->id = 0; 19398c2ecf20Sopenharmony_ci skb_queue_tail(&dsp->sendq, nskb); 19408c2ecf20Sopenharmony_ci schedule_work(&dsp->workq); 19418c2ecf20Sopenharmony_ci } 19428c2ecf20Sopenharmony_ci } 19438c2ecf20Sopenharmony_ci return; 19448c2ecf20Sopenharmony_ci } 19458c2ecf20Sopenharmony_ci /* in case of hardware conference */ 19468c2ecf20Sopenharmony_ci if (dsp->conf->hardware) 19478c2ecf20Sopenharmony_ci return; 19488c2ecf20Sopenharmony_ci list_for_each_entry(member, &dsp->conf->mlist, list) { 19498c2ecf20Sopenharmony_ci if (dsp->echo.software || member->dsp != dsp) { 19508c2ecf20Sopenharmony_ci nskb = skb_clone(skb, GFP_ATOMIC); 19518c2ecf20Sopenharmony_ci if (nskb) { 19528c2ecf20Sopenharmony_ci hh = mISDN_HEAD_P(nskb); 19538c2ecf20Sopenharmony_ci hh->prim = PH_DATA_REQ; 19548c2ecf20Sopenharmony_ci hh->id = 0; 19558c2ecf20Sopenharmony_ci skb_queue_tail(&member->dsp->sendq, nskb); 19568c2ecf20Sopenharmony_ci schedule_work(&member->dsp->workq); 19578c2ecf20Sopenharmony_ci } 19588c2ecf20Sopenharmony_ci } 19598c2ecf20Sopenharmony_ci } 19608c2ecf20Sopenharmony_ci} 1961