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