Complete rework of DSP audio bridging (CMX).
- faster (much faster) - capable of handling jitter - capable of handling misordered frames - new clocking mechanism - compiles and sucessfully tested Modified Files: Tag: mqueue mISDN/drivers/isdn/hardware/mISDN/dsp.h mISDN/drivers/isdn/hardware/mISDN/dsp_cmx.c mISDN/drivers/isdn/hardware/mISDN/dsp_core.c
This commit is contained in:
parent
4da89691bc
commit
0e241668db
|
@ -9,8 +9,6 @@
|
|||
*
|
||||
*/
|
||||
|
||||
//#define AUTOJITTER
|
||||
|
||||
#define DEBUG_DSP_MGR 0x0001
|
||||
#define DEBUG_DSP_CORE 0x0002
|
||||
#define DEBUG_DSP_DTMF 0x0004
|
||||
|
@ -47,6 +45,8 @@
|
|||
|
||||
extern int dsp_options;
|
||||
extern int dsp_debug;
|
||||
extern int dsp_poll;
|
||||
extern int dsp_tics;
|
||||
|
||||
/***************
|
||||
* audio stuff *
|
||||
|
@ -72,9 +72,18 @@ extern u8 dsp_silence;
|
|||
* cmx stuff *
|
||||
*************/
|
||||
|
||||
#define CMX_BUFF_SIZE 0x4000 /* must be 2**n */
|
||||
#define CMX_BUFF_HALF 0x2000 /* CMX_BUFF_SIZE / 2 */
|
||||
#define CMX_BUFF_MASK 0x3fff /* CMX_BUFF_SIZE - 1 */
|
||||
#define MAX_POLL 256 /* maximum number of send-chunks */
|
||||
|
||||
#define CMX_BUFF_SIZE 0x8000 /* must be 2**n (0x1000 about 1/2 second) */
|
||||
#define CMX_BUFF_HALF 0x4000 /* CMX_BUFF_SIZE / 2 */
|
||||
#define CMX_BUFF_MASK 0x7fff /* CMX_BUFF_SIZE - 1 */
|
||||
|
||||
/* how many seconds will we check the lowest delay until the jitter buffer
|
||||
is reduced by that delay */
|
||||
#define MAX_SECONDS_JITTER_CHECK 5
|
||||
|
||||
extern struct timer_list dsp_spl_tl;
|
||||
extern u64 dsp_spl_jiffies;
|
||||
|
||||
/* the structure of conferences:
|
||||
*
|
||||
|
@ -98,11 +107,6 @@ typedef struct _conference {
|
|||
struct list_head mlist;
|
||||
int software; /* conf is processed by software */
|
||||
int hardware; /* conf is processed by hardware */
|
||||
//#ifndef AUTOJITTER
|
||||
int largest; /* largest frame received in conf's life. */
|
||||
//#endif
|
||||
int W_min, W_max; /* min/maximum rx-write pointer of members */
|
||||
s32 conf_buff[CMX_BUFF_SIZE];
|
||||
} conference_t;
|
||||
|
||||
extern mISDNobject_t dsp_obj;
|
||||
|
@ -160,6 +164,7 @@ struct dsp_features {
|
|||
int pcm_id; /* unique id to identify the pcm bus (or -1) */
|
||||
int pcm_slots; /* number of slots on the pcm bus */
|
||||
int pcm_banks; /* number of IO banks of pcm bus */
|
||||
int has_jitter; /* data is jittered and unsorted */
|
||||
};
|
||||
|
||||
typedef struct _dsp {
|
||||
|
@ -179,17 +184,13 @@ typedef struct _dsp {
|
|||
conf_member_t *member;
|
||||
|
||||
/* buffer stuff */
|
||||
int largest; /* largest frame received in dsp's life. */
|
||||
int R_tx, W_tx; /* pointers of transmit buffer */
|
||||
int R_rx, W_rx; /* pointers of receive buffer and conference buffer */
|
||||
int rx_W; /* current write pos for data without timestamp */
|
||||
int rx_R; /* current read pos for transmit clock */
|
||||
int tx_W; /* current write pos for transmit data */
|
||||
int tx_R; /* current read pos for transmit clock */
|
||||
int delay[MAX_SECONDS_JITTER_CHECK];
|
||||
u8 tx_buff[CMX_BUFF_SIZE];
|
||||
u8 rx_buff[CMX_BUFF_SIZE];
|
||||
#ifdef AUTOJITTER
|
||||
int tx_delay; /* used to find the delay of tx buffer */
|
||||
int tx_delay_count;
|
||||
int rx_delay; /* used to find the delay of rx buffer */
|
||||
int rx_delay_count;
|
||||
#endif
|
||||
|
||||
/* hardware stuff */
|
||||
struct dsp_features features; /* features */
|
||||
|
@ -243,7 +244,11 @@ extern void dsp_cmx_debug(dsp_t *dsp);
|
|||
extern void dsp_cmx_hardware(conference_t *conf, dsp_t *dsp);
|
||||
extern int dsp_cmx_conf(dsp_t *dsp, u32 conf_id);
|
||||
extern void dsp_cmx_receive(dsp_t *dsp, struct sk_buff *skb);
|
||||
#ifdef OLDCMX
|
||||
extern struct sk_buff *dsp_cmx_send(dsp_t *dsp, int len, int dinfo);
|
||||
#else
|
||||
extern void dsp_cmx_send(void *data);
|
||||
#endif
|
||||
extern void dsp_cmx_transmit(dsp_t *dsp, struct sk_buff *skb);
|
||||
extern int dsp_cmx_del_conf_member(dsp_t *dsp);
|
||||
extern int dsp_cmx_del_conf(conference_t *conf);
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
/* HOW THE CMX WORKS:
|
||||
*
|
||||
* There are 3 types of interaction: One member is alone, in this case only
|
||||
* data flow is done.
|
||||
* data flow from upper to lower layer is done.
|
||||
* Two members will also exchange their data so they are crossconnected.
|
||||
* Three or more members will be added in a conference and will hear each
|
||||
* other but will not receive their own speech (echo) if not enabled.
|
||||
|
@ -40,37 +40,24 @@
|
|||
* - Force mixing of transmit data with other crossconnect/conference members.
|
||||
* - Echo generation to benchmark the delay of audio processing.
|
||||
* - Use hardware to minimize cpu load, disable FIFO load and minimize delay.
|
||||
* - Dejittering and clock generation.
|
||||
*
|
||||
* There are 3 buffers:
|
||||
*
|
||||
* The conference buffer
|
||||
*
|
||||
* R-3 R-2 R-1 W-2 W-1 W-3
|
||||
* | | | | | |
|
||||
* --+-------+-----+------+------+---+---------------
|
||||
* | |
|
||||
* W-min W-max
|
||||
*
|
||||
* The conference buffer is a ring buffer used to mix all data from all members.
|
||||
* To compensate the echo, data of individual member will later be substracted
|
||||
* before it is sent to that member. Each conference has a W-min and a W-max
|
||||
* pointer. Each individual member has a write pointer (W-x) and a read pointer
|
||||
* (R-x). W-min shows the lowest value of all W-x. The W-max shows the highest
|
||||
* value of all W-x. Whenever data is written, it is mixed by adding to the
|
||||
* existing sample value in the buffer. If W-max would increase, the additional
|
||||
* range is cleared so old data will be erased in the ring buffer.
|
||||
* There are 2 buffers:
|
||||
*
|
||||
*
|
||||
* RX-Buffer
|
||||
* R-1 W-1
|
||||
* R W
|
||||
* | |
|
||||
* ----------------+-------------+-------------------
|
||||
*
|
||||
* The rx-buffer is a ring buffer used to store the received data for each
|
||||
* individual member. To compensate echo, this data will later be substracted
|
||||
* from the conference's data before it is sent to that member. If only two
|
||||
* members are in one conference, this data is used to get the queued data from
|
||||
* the other member.
|
||||
* individual member. This is only the case if data needs to be dejittered
|
||||
* or in case of a conference where different clocks require reclocking.
|
||||
* The transmit-clock (R) will read the buffer.
|
||||
* If the clock overruns the write-pointer, we will have a buffer underrun.
|
||||
* If the write pointer always has a certain distance from the transmit-
|
||||
* clock, we will have a delay. The delay will dynamically be increased and
|
||||
* reduced.
|
||||
*
|
||||
*
|
||||
* TX-Buffer
|
||||
|
@ -84,28 +71,25 @@
|
|||
* (some) data is dropped so that it will not overrun.
|
||||
*
|
||||
*
|
||||
* Clock:
|
||||
*
|
||||
* A Clock is not required, if the data source has exactly one clock. In this
|
||||
* case the data source is forwarded to the destination.
|
||||
*
|
||||
* A Clock is required, because the data source
|
||||
* - has multiple clocks.
|
||||
* - has no clock due to jitter (VoIP).
|
||||
* In this case the system's clock is used. The clock resolution depends on
|
||||
* the jiffie resolution.
|
||||
*
|
||||
* If a member joins a conference:
|
||||
*
|
||||
* - If a member joins, its rx_buff is set to silence.
|
||||
* - If the conference reaches three members, the conf-buffer is cleared.
|
||||
* - When a member is joined, it will set its write and read pointer to W_max.
|
||||
* - If a member joins, its rx_buff is set to silence and change read pointer
|
||||
* to transmit clock.
|
||||
*
|
||||
* The procedure of received data from card is explained in cmx_receive.
|
||||
* The procedure of received data from user space is explained in cmx_transmit.
|
||||
*
|
||||
*
|
||||
* LIMITS:
|
||||
*
|
||||
* The max_queue value is 2* the samples of largest packet ever received by any
|
||||
* conference member from her card. It also changes during life of conference.
|
||||
*
|
||||
*
|
||||
* AUDIO PROCESS:
|
||||
*
|
||||
* Writing data to conference's and member's buffer is done by adding the sample
|
||||
* value to the existing ring buffer. Writing user space data to the member's
|
||||
* buffer is done by substracting the sample value from the existing ring
|
||||
* buffer.
|
||||
* The procedure of transmit data to card is cmx_send.
|
||||
*
|
||||
*
|
||||
* Interaction with other features:
|
||||
|
@ -160,8 +144,7 @@ dsp_cmx_debug(dsp_t *dsp)
|
|||
dsp_t *odsp;
|
||||
|
||||
printk(KERN_DEBUG "-----Current DSP\n");
|
||||
list_for_each_entry(odsp, &dsp_obj.ilist, list)
|
||||
{
|
||||
list_for_each_entry(odsp, &dsp_obj.ilist, list) {
|
||||
printk(KERN_DEBUG "* %s echo=%d txmix=%d", odsp->inst.name, odsp->echo, odsp->tx_mix);
|
||||
if (odsp->conf)
|
||||
printk(" (Conf %d)", odsp->conf->id);
|
||||
|
@ -170,11 +153,9 @@ dsp_cmx_debug(dsp_t *dsp)
|
|||
printk("\n");
|
||||
}
|
||||
printk(KERN_DEBUG "-----Current Conf:\n");
|
||||
list_for_each_entry(conf, &Conf_list, list)
|
||||
{
|
||||
list_for_each_entry(conf, &Conf_list, list) {
|
||||
printk(KERN_DEBUG "* Conf %d (%p)\n", conf->id, conf);
|
||||
list_for_each_entry(member, &conf->mlist, list)
|
||||
{
|
||||
list_for_each_entry(member, &conf->mlist, list) {
|
||||
printk(KERN_DEBUG
|
||||
" - member = %s (slot_tx %d, bank_tx %d, slot_rx %d, bank_rx %d hfc_conf %d)%s\n",
|
||||
member->dsp->inst.name, member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx,
|
||||
|
@ -237,18 +218,13 @@ dsp_cmx_add_conf_member(dsp_t *dsp, conference_t *conf)
|
|||
return(-ENOMEM);
|
||||
}
|
||||
memset(member, 0, sizeof(conf_member_t));
|
||||
memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
|
||||
member->dsp = dsp;
|
||||
/* set initial values */
|
||||
dsp->W_rx = conf->W_max;
|
||||
dsp->R_rx = conf->W_max;
|
||||
/* clear rx buffer */
|
||||
memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
|
||||
dsp->rx_W = dsp->rx_R = -1; /* reset pointers */
|
||||
|
||||
list_add_tail(&member->list, &conf->mlist);
|
||||
|
||||
/* zero conf-buffer if we change from 2 to 3 members */
|
||||
if (3 == count_list_member(&conf->mlist))
|
||||
memset(conf->conf_buff, 0, sizeof(conf->conf_buff));
|
||||
|
||||
dsp->conf = conf;
|
||||
dsp->member = member;
|
||||
|
||||
|
@ -957,219 +933,127 @@ dsp_cmx_conf(dsp_t *dsp, u32 conf_id)
|
|||
/*
|
||||
* audio data is received from card
|
||||
*/
|
||||
|
||||
void
|
||||
dsp_cmx_receive(dsp_t *dsp, struct sk_buff *skb)
|
||||
{
|
||||
conference_t *conf = dsp->conf;
|
||||
conf_member_t *member;
|
||||
s32 *c;
|
||||
// s32 *c;
|
||||
u8 *d, *p;
|
||||
int len = skb->len;
|
||||
int w, ww, i, ii;
|
||||
int W_min, W_max;
|
||||
mISDN_head_t *hh = mISDN_HEAD_P(skb);
|
||||
int w, i, ii;
|
||||
// int direct = 0; /* use rx data to clock tx-data */
|
||||
|
||||
/* check if we have sompen */
|
||||
if (len < 1)
|
||||
return;
|
||||
|
||||
//#ifndef AUTOJITTER
|
||||
/* -> if length*2 is greater largest */
|
||||
if (dsp->largest < (len<<1))
|
||||
dsp->largest = (len<<1);
|
||||
//#endif
|
||||
#if 0
|
||||
/* check if we can use our clock and directly forward data */
|
||||
if (!dsp->features.has_jitter) {
|
||||
if (!conf)
|
||||
direct = 1;
|
||||
else {
|
||||
if (count_list_member(&conf->mlist) <= 2)
|
||||
direct = 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* half of the buffer should be 4 time larger than maximum packet size */
|
||||
if (len >= (CMX_BUFF_HALF>>2)) {
|
||||
/* half of the buffer should be larger than maximum packet size */
|
||||
if (len >= CMX_BUFF_HALF) {
|
||||
printk(KERN_ERR "%s line %d: packet from card is too large (%d bytes). please make card send smaller packets OR increase CMX_BUFF_SIZE\n", __FILE__, __LINE__, len);
|
||||
return;
|
||||
}
|
||||
|
||||
/* STEP 1: WRITE DOWN WHAT WE GOT (into the buffer(s)) */
|
||||
|
||||
/* -> new W-min & W-max is calculated:
|
||||
* W_min will be the write pointer of this dsp (after writing 'len'
|
||||
* of bytes).
|
||||
* If there are other members in a conference, W_min will be the
|
||||
* lowest of all member's writer pointers.
|
||||
* W_max respectively
|
||||
*/
|
||||
W_max = W_min = (dsp->W_rx + len) & CMX_BUFF_MASK;
|
||||
if (conf) {
|
||||
/* -> who is larger? dsp or conf */
|
||||
if (conf->largest < dsp->largest)
|
||||
conf->largest = dsp->largest;
|
||||
else if (conf->largest > dsp->largest)
|
||||
dsp->largest = conf->largest;
|
||||
|
||||
list_for_each_entry(member, &conf->mlist, list) {
|
||||
if (member != dsp->member) {
|
||||
/* if W_rx is lower */
|
||||
if (((member->dsp->W_rx - W_min) & CMX_BUFF_MASK) >= CMX_BUFF_HALF)
|
||||
W_min = member->dsp->W_rx;
|
||||
/* if W_rx is higher */
|
||||
if (((W_max - member->dsp->W_rx) & CMX_BUFF_MASK) >= CMX_BUFF_HALF)
|
||||
W_max = member->dsp->W_rx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CMX_DEBUG
|
||||
printk(KERN_DEBUG "cmx_receive(dsp=%lx): W_rx(dsp)=%05x W_min=%05x W_max=%05x largest=%05x %s\n", dsp, dsp->W_rx, W_min, W_max, dsp->largest, dsp->inst.name);
|
||||
#endif
|
||||
|
||||
/* -> if data is not too fast (exceed maximum queue):
|
||||
* data is written if 'new W_rx' is not too far behind W_min.
|
||||
*/
|
||||
if (((dsp->W_rx + len - W_min) & CMX_BUFF_MASK) <= dsp->largest) {
|
||||
/* -> received data is written to rx-buffer */
|
||||
p = skb->data;
|
||||
d = dsp->rx_buff;
|
||||
w = dsp->W_rx;
|
||||
i = 0;
|
||||
ii = len;
|
||||
while(i < ii) {
|
||||
d[w++ & CMX_BUFF_MASK] = *p++;
|
||||
i++;
|
||||
}
|
||||
/* -> if conference has three or more members */
|
||||
if (conf) {
|
||||
#ifdef CMX_CONF_DEBUG
|
||||
#warning CMX_CONF_DEBUG is enabled, it causes performance loss with normal 2-party crossconnects
|
||||
if (2 <= count_list_member(&conf->mlist)) {
|
||||
#else
|
||||
if (3 <= count_list_member(&conf->mlist)) {
|
||||
#endif
|
||||
//printk(KERN_DEBUG "cmxing dsp:%s dsp->W_rx=%04x conf->W_max=%04x\n", dsp->inst.name, dsp->W_rx, conf->W_max);
|
||||
/* -> received data is added to conf-buffer
|
||||
* new space is overwritten */
|
||||
p = skb->data;
|
||||
c = conf->conf_buff;
|
||||
w = dsp->W_rx;
|
||||
ww = conf->W_max;
|
||||
i = 0;
|
||||
ii = len;
|
||||
/* loop until done or old W_max is reached */
|
||||
while(i<ii && w!=ww) {
|
||||
c[w] += dsp_audio_law_to_s32[*p++]; /* add to existing */
|
||||
w = (w+1) & CMX_BUFF_MASK; /* must be always masked, for loop condition */
|
||||
i++;
|
||||
}
|
||||
/* loop the rest */
|
||||
while(i < ii) {
|
||||
c[w++ & CMX_BUFF_MASK] = dsp_audio_law_to_s32[*p++]; /* write to new */
|
||||
i++;
|
||||
}
|
||||
}
|
||||
/* if W_max is lower new dsp->W_rx */
|
||||
if (((W_max - (dsp->W_rx+len)) & CMX_BUFF_MASK) >= CMX_BUFF_HALF)
|
||||
W_max = (dsp->W_rx + len) & CMX_BUFF_MASK;
|
||||
/* store for dsp_cmx_send */
|
||||
conf->W_min = W_min;
|
||||
/* -> write new W_max */
|
||||
conf->W_max = W_max;
|
||||
}
|
||||
/* -> write new W_rx */
|
||||
dsp->W_rx = (dsp->W_rx + len) & CMX_BUFF_MASK;
|
||||
/* initialize pointers if not already */
|
||||
if (dsp->rx_W < 0) {
|
||||
if (dsp->features.has_jitter)
|
||||
dsp->rx_R = dsp->rx_W = (hh->dinfo & CMX_BUFF_MASK);
|
||||
else
|
||||
dsp->rx_R = dsp->rx_W = 0;
|
||||
} else {
|
||||
if (dsp_debug & DEBUG_DSP_CMX)
|
||||
printk(KERN_DEBUG "CMX: receiving too fast (rx_buff) dsp=%p\n", dsp);
|
||||
#ifdef CMX_DEBUG
|
||||
printk(KERN_DEBUG "W_max=%x-W_min=%x = %d, largest = %d\n",
|
||||
W_max, W_min, (W_max - W_min) & CMX_BUFF_MASK, dsp->largest);
|
||||
#endif
|
||||
if (dsp->features.has_jitter) {
|
||||
dsp->rx_W = (hh->dinfo & CMX_BUFF_MASK);
|
||||
}
|
||||
/* if we underrun (or maybe overrun), we set our new read pointer, and write silence to buffer */
|
||||
if (((dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) {
|
||||
if (dsp_debug & DEBUG_DSP_CMX)
|
||||
printk(KERN_DEBUG "cmx_receive(dsp=%lx): UNDERRUN (or overrun), adjusting read pointer! (inst %s)\n", (u_long)dsp, dsp->inst.name);
|
||||
dsp->rx_R = dsp->rx_W;
|
||||
memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
|
||||
}
|
||||
}
|
||||
|
||||
/* show where to write */
|
||||
#ifdef CMX_DEBUG
|
||||
printk(KERN_DEBUG "cmx_receive(dsp=%lx): rx_R(dsp) rx_W(dsp)=%05x len=%d %s\n", (u_long)dsp, dsp->rx_R, dsp->rx_W, len, dsp->inst.name);
|
||||
#endif
|
||||
|
||||
/* write data into rx_buffer */
|
||||
p = skb->data;
|
||||
d = dsp->rx_buff;
|
||||
w = dsp->rx_W;
|
||||
i = 0;
|
||||
ii = len;
|
||||
while(i < ii) {
|
||||
d[w++ & CMX_BUFF_MASK] = *p++;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* increase write-pointer */
|
||||
dsp->rx_W = ((dsp->rx_W+len) & CMX_BUFF_MASK);
|
||||
}
|
||||
|
||||
/*
|
||||
* send mixed audio data to card
|
||||
*/
|
||||
|
||||
struct sk_buff
|
||||
*dsp_cmx_send(dsp_t *dsp, int len, int dinfo)
|
||||
/*
|
||||
* send (mixed) audio data to card and control jitter
|
||||
*/
|
||||
static void
|
||||
dsp_cmx_send_member(dsp_t *dsp, int len, s32 *c, int members)
|
||||
{
|
||||
int dinfo = 0;
|
||||
conference_t *conf = dsp->conf;
|
||||
dsp_t *member, *other;
|
||||
register s32 sample;
|
||||
s32 *c;
|
||||
u8 *d, *o, *p, *q;
|
||||
u8 *d, *p, *q, *o_q;
|
||||
struct sk_buff *nskb;
|
||||
int r, rr, t, tt;
|
||||
int r, rr, t, tt, o_r, o_rr;
|
||||
|
||||
/* don't process if: */
|
||||
if (dsp->pcm_slot_tx >= 0 /* connected to pcm slot */
|
||||
&& dsp->tx_R == dsp->tx_W /* AND no tx-data */
|
||||
&& !(dsp->tone.tone && dsp->tone.software)) /* AND not soft tones */
|
||||
return;
|
||||
if (!dsp->b_active) /* if not active */
|
||||
return;
|
||||
#ifdef CMX_DEBUG
|
||||
printk(KERN_DEBUG "SEND members=%d dsp=%s, conf=%p, rx_R=%05x rx_W=%05x\n", members, dsp->inst.name, conf, dsp->rx_R, dsp->rx_W);
|
||||
#endif
|
||||
|
||||
/* PREPARE RESULT */
|
||||
nskb = alloc_skb(len, GFP_ATOMIC);
|
||||
if (!nskb) {
|
||||
printk(KERN_ERR "FATAL ERROR in mISDN_dsp.o: cannot alloc %d bytes\n", len);
|
||||
return(NULL);
|
||||
return;
|
||||
}
|
||||
mISDN_sethead(PH_DATA | REQUEST, dinfo, nskb);
|
||||
|
||||
/* set pointers, indexes and stuff */
|
||||
member = dsp;
|
||||
p = dsp->tx_buff; /* transmit data */
|
||||
q = dsp->rx_buff; /* received data */
|
||||
d = skb_put(nskb, len); /* result */
|
||||
t = dsp->R_tx; /* tx-pointers */
|
||||
tt = dsp->W_tx;
|
||||
r = dsp->R_rx; /* rx-pointers */
|
||||
if (conf) {
|
||||
/* special hardware access */
|
||||
if (conf->hardware) {
|
||||
if (dsp->tone.tone && dsp->tone.software) {
|
||||
/* -> copy tone */
|
||||
dsp_tone_copy(dsp, d, len);
|
||||
dsp->R_tx = dsp->W_tx = 0; /* clear tx buffer */
|
||||
return(nskb);
|
||||
}
|
||||
if (t != tt) {
|
||||
while(len && t!=tt) {
|
||||
*d++ = p[t]; /* write tx_buff */
|
||||
t = (t+1) & CMX_BUFF_MASK;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
if (len)
|
||||
memset(d, dsp_silence, len);
|
||||
dsp->R_tx = t;
|
||||
return(nskb);
|
||||
}
|
||||
/* W_min is also limit for read */
|
||||
rr = conf->W_min;
|
||||
} else
|
||||
rr = dsp->W_rx;
|
||||
t = dsp->tx_R; /* tx-pointers */
|
||||
tt = dsp->tx_W;
|
||||
r = dsp->rx_R; /* rx-pointers */
|
||||
rr = (r + len) & CMX_BUFF_MASK;
|
||||
|
||||
/* increase r, if too far behind rr
|
||||
* (this happens if interrupts get lost, so transmission is delayed) */
|
||||
if (((rr - r) & CMX_BUFF_MASK) > dsp->largest) {
|
||||
if (dsp_debug & DEBUG_DSP_CMX)
|
||||
printk(KERN_DEBUG "r=%04x is too far behind rr=%04x, correcting. (larger than %04x)\n", r, rr, dsp->largest);
|
||||
r = (rr - dsp->largest) & CMX_BUFF_MASK;
|
||||
}
|
||||
/* calculate actual r (if r+len would overrun rr) */
|
||||
if (((rr - r - len) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) {
|
||||
#ifdef CMX_DEBUG
|
||||
printk(KERN_DEBUG "r+len=%04x overruns rr=%04x\n", (r+len) & CMX_BUFF_MASK, rr);
|
||||
#endif
|
||||
/* r is set "len" bytes before W_min */
|
||||
r = (rr - len) & CMX_BUFF_MASK;
|
||||
if (dsp_debug & DEBUG_DSP_CMX)
|
||||
printk(KERN_DEBUG "CMX: sending too fast (tx_buff) dsp=%p\n", dsp);
|
||||
} else
|
||||
/* rr is set "len" bytes after R_rx */
|
||||
rr = (r + len) & CMX_BUFF_MASK;
|
||||
dsp->R_rx = rr;
|
||||
/* now: rr is exactly "len" bytes after r now */
|
||||
#ifdef CMX_DEBUG
|
||||
printk(KERN_DEBUG
|
||||
"CMX_SEND(dsp=%p) %d bytes from tx:0x%05x-0x%05x rx:0x%05x-0x%05x echo=%d %s\n",
|
||||
dsp, len, t, tt, r, rr, dsp->echo, dsp->inst.name);
|
||||
#endif
|
||||
|
||||
/* STEP 2.0: PROCESS TONES/TX-DATA ONLY */
|
||||
/* PROCESS TONES/TX-DATA ONLY */
|
||||
if (dsp->tone.tone && dsp->tone.software) {
|
||||
/* -> copy tone */
|
||||
dsp_tone_copy(dsp, d, len);
|
||||
dsp->R_tx = dsp->W_tx = 0; /* clear tx buffer */
|
||||
return(nskb);
|
||||
dsp->tx_R = dsp->tx_W = 0; /* clear tx buffer */
|
||||
goto send_packet;
|
||||
}
|
||||
/* if we have tx-data but do not use mixing */
|
||||
if (!dsp->tx_mix && t!=tt) {
|
||||
|
@ -1180,14 +1064,13 @@ struct sk_buff
|
|||
r = (r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
if(r == rr) {
|
||||
dsp->R_tx = t;
|
||||
return(nskb);
|
||||
dsp->tx_R = t;
|
||||
goto send_packet;
|
||||
}
|
||||
}
|
||||
|
||||
/* STEP 2.1: PROCESS DATA (one member / no conf) */
|
||||
if (!conf) {
|
||||
single:
|
||||
/* PROCESS DATA (one member / no conf) */
|
||||
if (!conf || members<=1) {
|
||||
/* -> if echo is NOT enabled */
|
||||
if (!dsp->echo) {
|
||||
/* -> send tx-data if available or use 0-volume */
|
||||
|
@ -1211,40 +1094,40 @@ struct sk_buff
|
|||
r = (r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
}
|
||||
dsp->R_tx = t;
|
||||
return(nskb);
|
||||
dsp->tx_R = t;
|
||||
goto send_packet;
|
||||
}
|
||||
if (1 == count_list_member(&conf->mlist)) {
|
||||
goto single;
|
||||
}
|
||||
/* STEP 2.2: PROCESS DATA (two members) */
|
||||
/* PROCESS DATA (two members) */
|
||||
#ifdef CMX_CONF_DEBUG
|
||||
if (0) {
|
||||
#else
|
||||
if (2 == count_list_member(&conf->mlist)) {
|
||||
if (members == 2) {
|
||||
#endif
|
||||
/* "other" becomes other party */
|
||||
other = (list_entry(conf->mlist.next, conf_member_t, list))->dsp;
|
||||
if (other == member)
|
||||
other = (list_entry(conf->mlist.prev, conf_member_t, list))->dsp;
|
||||
o = other->rx_buff; /* received data */
|
||||
o_q = other->rx_buff; /* received data */
|
||||
o_r = other->rx_R; /* rx-pointers */
|
||||
o_rr = (o_r + len) & CMX_BUFF_MASK;
|
||||
/* -> if echo is NOT enabled */
|
||||
if (!dsp->echo) {
|
||||
//if (o_r!=o_rr) printk(KERN_DEBUG "receive data=0x%02x\n", o_q[o_r]); else printk(KERN_DEBUG "NO R!!!\n");
|
||||
/* -> copy other member's rx-data, if tx-data is available, mix */
|
||||
while(r!=rr && t!=tt) {
|
||||
*d++ = dsp_audio_mix_law[(p[t]<<8)|o[r]];
|
||||
while(o_r!=o_rr && t!=tt) {
|
||||
*d++ = dsp_audio_mix_law[(p[t]<<8)|o_q[o_r]];
|
||||
t = (t+1) & CMX_BUFF_MASK;
|
||||
r = (r+1) & CMX_BUFF_MASK;
|
||||
o_r = (o_r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
while(r != rr) {
|
||||
*d++ = o[r];
|
||||
r = (r+1) & CMX_BUFF_MASK;
|
||||
while(o_r != o_rr) {
|
||||
*d++ = o_q[o_r];
|
||||
o_r = (o_r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
/* -> if echo is enabled */
|
||||
} else {
|
||||
/* -> mix other member's rx-data with echo, if tx-data is available, mix */
|
||||
while(r!=rr && t!=tt) {
|
||||
sample = dsp_audio_law_to_s32[p[t]] + dsp_audio_law_to_s32[o[r]] + dsp_audio_law_to_s32[q[r]];
|
||||
sample = dsp_audio_law_to_s32[p[t]] + dsp_audio_law_to_s32[q[r]] + dsp_audio_law_to_s32[o_q[o_r]];
|
||||
if (sample < -32768)
|
||||
sample = -32768;
|
||||
else if (sample > 32767)
|
||||
|
@ -1252,22 +1135,23 @@ struct sk_buff
|
|||
*d++ = dsp_audio_s16_to_law[sample & 0xffff]; /* tx-data + rx_data + echo */
|
||||
t = (t+1) & CMX_BUFF_MASK;
|
||||
r = (r+1) & CMX_BUFF_MASK;
|
||||
o_r = (o_r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
while(r != rr) {
|
||||
*d++ = dsp_audio_mix_law[(o[r]<<8)|q[r]];
|
||||
*d++ = dsp_audio_mix_law[(q[r]<<8)|o_q[o_r]];
|
||||
r = (r+1) & CMX_BUFF_MASK;
|
||||
o_r = (o_r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
}
|
||||
dsp->R_tx = t;
|
||||
return(nskb);
|
||||
dsp->tx_R = t;
|
||||
goto send_packet;
|
||||
}
|
||||
/* STEP 2.3: PROCESS DATA (three or more members) */
|
||||
c = conf->conf_buff;
|
||||
/* PROCESS DATA (three or more members) */
|
||||
/* -> if echo is NOT enabled */
|
||||
if (!dsp->echo) {
|
||||
/* -> substract rx-data from conf-data, if tx-data is available, mix */
|
||||
while(r!=rr && t!=tt) {
|
||||
sample = dsp_audio_law_to_s32[p[t]] + c[r] - dsp_audio_law_to_s32[q[r]];
|
||||
sample = dsp_audio_law_to_s32[p[t]] + *c++ - dsp_audio_law_to_s32[q[r]];
|
||||
if (sample < -32768)
|
||||
sample = -32768;
|
||||
else if (sample > 32767)
|
||||
|
@ -1277,7 +1161,7 @@ struct sk_buff
|
|||
t = (t+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
while(r != rr) {
|
||||
sample = c[r] - dsp_audio_law_to_s32[q[r]];
|
||||
sample = *c++ - dsp_audio_law_to_s32[q[r]];
|
||||
if (sample < -32768)
|
||||
sample = -32768;
|
||||
else if (sample > 32767)
|
||||
|
@ -1289,7 +1173,7 @@ struct sk_buff
|
|||
} else {
|
||||
/* -> encode conf-data, if tx-data is available, mix */
|
||||
while(r!=rr && t!=tt) {
|
||||
sample = dsp_audio_law_to_s32[p[t]] + c[r];
|
||||
sample = dsp_audio_law_to_s32[p[t]] + *c++;
|
||||
if (sample < -32768)
|
||||
sample = -32768;
|
||||
else if (sample > 32767)
|
||||
|
@ -1299,7 +1183,7 @@ struct sk_buff
|
|||
r = (r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
while(r != rr) {
|
||||
sample = c[r];
|
||||
sample = *c++;
|
||||
if (sample < -32768)
|
||||
sample = -32768;
|
||||
else if (sample > 32767)
|
||||
|
@ -1308,8 +1192,171 @@ struct sk_buff
|
|||
r = (r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
}
|
||||
dsp->R_tx = t;
|
||||
return(nskb);
|
||||
dsp->tx_R = t;
|
||||
goto send_packet;
|
||||
|
||||
send_packet:
|
||||
/* adjust volume */
|
||||
if (dsp->tx_volume)
|
||||
dsp_change_volume(nskb, dsp->tx_volume);
|
||||
|
||||
/* cancel echo */
|
||||
if (dsp->cancel_enable)
|
||||
dsp_cancel_tx(dsp, nskb->data, nskb->len);
|
||||
|
||||
/* crypt */
|
||||
if (dsp->bf_enable)
|
||||
dsp_bf_encrypt(dsp, nskb->data, nskb->len);
|
||||
|
||||
/* send packet */
|
||||
if (mISDN_queue_down(&dsp->inst, 0, nskb)) {
|
||||
dev_kfree_skb(nskb);
|
||||
printk(KERN_ERR "%s: failed to send tx-packet\n", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
u32 samplecount;
|
||||
struct timer_list dsp_spl_tl;
|
||||
u64 dsp_spl_jiffies;
|
||||
|
||||
void dsp_cmx_send(void *data)
|
||||
{
|
||||
conference_t *conf;
|
||||
conf_member_t *member;
|
||||
dsp_t *dsp;
|
||||
int mustmix, members;
|
||||
s32 mixbuffer[MAX_POLL], *c;
|
||||
u8 *q;
|
||||
int r, rr;
|
||||
int jittercheck = 0, delay, i;
|
||||
u_long flags;
|
||||
|
||||
/* lock */
|
||||
spin_lock_irqsave(&dsp_obj.lock, flags);
|
||||
|
||||
/* check if jitter needs to be checked */
|
||||
samplecount += dsp_poll;
|
||||
if (samplecount%8000 < dsp_poll)
|
||||
jittercheck = 1;
|
||||
|
||||
/* loop all members that do not require conference mixing */
|
||||
list_for_each_entry(dsp, &dsp_obj.ilist, list) {
|
||||
conf = dsp->conf;
|
||||
mustmix = 0;
|
||||
members = 0;
|
||||
if (conf) {
|
||||
members = count_list_member(&conf->mlist);
|
||||
#ifdef CMX_CONF_DEBUG
|
||||
if (conf->software && members>1)
|
||||
#else
|
||||
if (conf->software && members>2)
|
||||
#endif
|
||||
mustmix = 1;
|
||||
}
|
||||
|
||||
/* transmission required */
|
||||
if (!mustmix)
|
||||
dsp_cmx_send_member(dsp, dsp_poll, mixbuffer, members); // unused mixbuffer is given to prevent a potential null-pointer-bug
|
||||
}
|
||||
|
||||
/* loop all members that require conference mixing */
|
||||
list_for_each_entry(conf, &Conf_list, list) {
|
||||
/* count members and check hardware */
|
||||
members = count_list_member(&conf->mlist);
|
||||
#ifdef CMX_CONF_DEBUG
|
||||
if (conf->software && members>1) {
|
||||
#else
|
||||
if (conf->software && members>2) {
|
||||
#endif
|
||||
/* mix all data */
|
||||
memset(mixbuffer, 0, dsp_poll*sizeof(s32));
|
||||
list_for_each_entry(member, &conf->mlist, list) {
|
||||
dsp = member->dsp;
|
||||
/* get range of data to mix */
|
||||
c = mixbuffer;
|
||||
q = dsp->rx_buff;
|
||||
r = dsp->rx_R;
|
||||
rr = (r + dsp_poll) & CMX_BUFF_MASK;
|
||||
/* add member's data */
|
||||
while(r != rr) {
|
||||
*c++ += dsp_audio_law_to_s32[q[r]];
|
||||
r = (r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
/* process each member */
|
||||
list_for_each_entry(member, &conf->mlist, list) {
|
||||
/* transmission */
|
||||
dsp_cmx_send_member(member->dsp, dsp_poll, mixbuffer, members);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* delete rx-data, increment buffers, change pointers */
|
||||
list_for_each_entry(dsp, &dsp_obj.ilist, list) {
|
||||
q = dsp->rx_buff;
|
||||
r = dsp->rx_R;
|
||||
rr = (r + dsp_poll) & CMX_BUFF_MASK;
|
||||
/* delete rx-data */
|
||||
while(r != rr) {
|
||||
q[r] = dsp_silence;
|
||||
r = (r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
/* increment rx-buffer pointer */
|
||||
dsp->rx_R = r; /* write incremented read pointer */
|
||||
|
||||
/* check current delay */
|
||||
delay = (dsp->rx_W-r) & CMX_BUFF_MASK;
|
||||
if (delay >= CMX_BUFF_HALF)
|
||||
delay = 0; /* will be the delay before next write */
|
||||
/* check for lower delay */
|
||||
if (delay < dsp->delay[0])
|
||||
dsp->delay[0] = delay;
|
||||
if (jittercheck) {
|
||||
/* find the lowest of all delays */
|
||||
delay = dsp->delay[0];
|
||||
i = 1;
|
||||
while (i < MAX_SECONDS_JITTER_CHECK) {
|
||||
if (delay > dsp->delay[i])
|
||||
delay = dsp->delay[i];
|
||||
i++;
|
||||
}
|
||||
if (dsp_debug & DEBUG_DSP_CMX)
|
||||
printk(KERN_DEBUG "%s lowest delay of %d bytes for dsp %s\n", __FUNCTION__, delay, dsp->inst.name);
|
||||
/* remove delay */
|
||||
if (delay) {
|
||||
r = dsp->rx_R;
|
||||
rr = (r + delay) & CMX_BUFF_MASK;
|
||||
/* delete rx-data */
|
||||
while(r != rr) {
|
||||
q[r] = dsp_silence;
|
||||
r = (r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
/* increment rx-buffer pointer */
|
||||
dsp->rx_R = r; /* write incremented read pointer */
|
||||
}
|
||||
/* scroll up delays */
|
||||
i = MAX_SECONDS_JITTER_CHECK - 1;
|
||||
while (i) {
|
||||
dsp->delay[i] = dsp->delay[i-1];
|
||||
i--;
|
||||
}
|
||||
dsp->delay[0] = CMX_BUFF_HALF; /* (infinite) delay */
|
||||
}
|
||||
}
|
||||
|
||||
/* restart timer */
|
||||
// init_timer(&dsp_spl_tl);
|
||||
if (dsp_spl_jiffies + dsp_tics < jiffies) /* if next event would be in the past ... */
|
||||
dsp_spl_jiffies = jiffies;
|
||||
else
|
||||
dsp_spl_jiffies += dsp_tics;
|
||||
|
||||
dsp_spl_tl.expires = dsp_spl_jiffies;
|
||||
add_timer(&dsp_spl_tl);
|
||||
|
||||
/* unlock */
|
||||
spin_unlock_irqrestore(&dsp_obj.lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1321,36 +1368,15 @@ dsp_cmx_transmit(dsp_t *dsp, struct sk_buff *skb)
|
|||
u_int w, ww;
|
||||
u8 *d, *p;
|
||||
int space, l;
|
||||
#ifdef AUTOJITTER
|
||||
int use;
|
||||
#endif
|
||||
|
||||
/* check if we have sompen */
|
||||
l = skb->len;
|
||||
w = dsp->W_tx;
|
||||
ww = dsp->R_tx;
|
||||
if (l < 1)
|
||||
return;
|
||||
|
||||
#ifdef AUTOJITTER
|
||||
/* check the delay */
|
||||
use = w-ww;
|
||||
if (use < 0)
|
||||
use += CMX_BUFF_SIZE;
|
||||
if (!dsp->tx_delay || dsp->tx_delay>use)
|
||||
dsp->tx_delay = use;
|
||||
dsp->tx_delay_count += l;
|
||||
if (dsp->tx_delay_count >= DELAY_CHECK) {
|
||||
/* now remove the delay */
|
||||
if (dsp_debug & DEBUG_DSP_DELAY)
|
||||
printk(KERN_DEBUG "%s(dsp=0x%x) removing delay of %d bytes\n", __FUNCTION__, (u32)dsp, dsp->tx_delay);
|
||||
dsp->tx_delay_count = 0;
|
||||
dsp->R_tx = ww = (ww + dsp->tx_delay) & CMX_BUFF_MASK;
|
||||
dsp->tx_delay = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* check if there is enough space, and then copy */
|
||||
w = dsp->tx_W;
|
||||
ww = dsp->tx_R;
|
||||
p = dsp->tx_buff;
|
||||
d = skb->data;
|
||||
space = ww-w;
|
||||
|
@ -1363,10 +1389,11 @@ dsp_cmx_transmit(dsp_t *dsp, struct sk_buff *skb)
|
|||
else
|
||||
/* write until all byte are copied */
|
||||
ww = (w + skb->len) & CMX_BUFF_MASK;
|
||||
dsp->W_tx = ww;
|
||||
dsp->tx_W = ww;
|
||||
|
||||
/* show current buffer */
|
||||
#ifdef CMX_DEBUG
|
||||
printk(KERN_DEBUG "cmx_transmit(dsp=%lx) %d bytes to 0x%x-0x%x. %s\n", dsp, (ww-w)&CMX_BUFF_MASK, w, ww, dsp->inst.name);
|
||||
printk(KERN_DEBUG "cmx_transmit(dsp=%lx) %d bytes to 0x%x-0x%x. %s\n", (u_long)dsp, (ww-w)&CMX_BUFF_MASK, w, ww, dsp->inst.name);
|
||||
#endif
|
||||
|
||||
/* copy transmit data to tx-buffer */
|
||||
|
@ -1377,5 +1404,3 @@ dsp_cmx_transmit(dsp_t *dsp, struct sk_buff *skb)
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -187,17 +187,14 @@ static int debug = 0;
|
|||
int dsp_debug;
|
||||
static int options = 0;
|
||||
int dsp_options;
|
||||
#ifndef AUTOJITTER
|
||||
int poll = 0;
|
||||
#endif
|
||||
static int poll = 0;
|
||||
int dsp_poll, dsp_tics;
|
||||
|
||||
#ifdef MODULE
|
||||
MODULE_AUTHOR("Andreas Eversberg");
|
||||
MODULE_PARM(debug, "1i");
|
||||
MODULE_PARM(options, "1i");
|
||||
#ifndef AUTOJITTER
|
||||
MODULE_PARM(poll, "1i");
|
||||
#endif
|
||||
#ifdef MODULE_LICENSE
|
||||
MODULE_LICENSE("GPL");
|
||||
#endif
|
||||
|
@ -284,7 +281,7 @@ dsp_control_req(dsp_t *dsp, mISDN_head_t *hh, struct sk_buff *skb)
|
|||
dsp_cmx_hardware(dsp->conf, dsp);
|
||||
/* reset tx buffers (user space data) */
|
||||
tone_off:
|
||||
dsp->R_tx = dsp->W_tx = 0;
|
||||
dsp->tx_R = dsp->tx_W = 0;
|
||||
break;
|
||||
case VOL_CHANGE_TX: /* change volume */
|
||||
if (len != sizeof(int)) {
|
||||
|
@ -549,37 +546,7 @@ dsp_from_down(mISDNinstance_t *inst, struct sk_buff *skb)
|
|||
/* process data from card at cmx */
|
||||
dsp_cmx_receive(dsp, skb);
|
||||
}
|
||||
/* we send data only if software or if we have some
|
||||
* or if we cannot do tones with hardware
|
||||
*/
|
||||
if (((dsp->pcm_slot_tx<0 && !dsp->features.hfc_loops) /* software crossconnects OR software loops */
|
||||
|| dsp->R_tx != dsp->W_tx /* data in buffer */
|
||||
|| (dsp->echo==1 && dsp->pcm_slot_tx<0) /* software echo */
|
||||
|| (dsp->tone.tone && dsp->tone.software)) /* software loops */
|
||||
&& (dsp->b_active)) {
|
||||
// NOTE: b_active will be importaint to trigger sending by a forthcoming dsp clock.
|
||||
/* get data from cmx */
|
||||
nskb = dsp_cmx_send(dsp, skb->len, 0);
|
||||
spin_unlock_irqrestore(&dsp_obj.lock, flags);
|
||||
if (nskb) {
|
||||
/* change volume if requested */
|
||||
if (dsp->tx_volume)
|
||||
dsp_change_volume(nskb, dsp->tx_volume);
|
||||
/* if echo cancellation is enabled */
|
||||
if (dsp->cancel_enable)
|
||||
dsp_cancel_tx(dsp, nskb->data, nskb->len);
|
||||
/* crypt if enabled */
|
||||
if (dsp->bf_enable)
|
||||
dsp_bf_encrypt(dsp, nskb->data, nskb->len);
|
||||
/* send subsequent requests to card */
|
||||
if (mISDN_queue_down(&dsp->inst, 0, nskb)) {
|
||||
dev_kfree_skb(nskb);
|
||||
printk(KERN_ERR "%s: failed to send tx packet\n", __FUNCTION__);
|
||||
}
|
||||
} else
|
||||
printk(KERN_ERR "%s: failed to create tx packet\n", __FUNCTION__);
|
||||
} else
|
||||
spin_unlock_irqrestore(&dsp_obj.lock, flags);
|
||||
spin_unlock_irqrestore(&dsp_obj.lock, flags);
|
||||
if (dsp->rx_disabled) {
|
||||
/* if receive is not allowed */
|
||||
dev_kfree_skb(skb);
|
||||
|
@ -631,10 +598,8 @@ dsp_from_down(mISDNinstance_t *inst, struct sk_buff *skb)
|
|||
/* bchannel now active */
|
||||
spin_lock_irqsave(&dsp_obj.lock, flags);
|
||||
dsp->b_active = 1;
|
||||
dsp->W_tx = dsp->R_tx = 0; /* clear TX buffer */
|
||||
dsp->W_rx = dsp->R_rx = 0; /* clear RX buffer */
|
||||
if (dsp->conf)
|
||||
dsp->W_rx = dsp->R_rx = dsp->conf->W_max;
|
||||
dsp->tx_W = dsp->tx_R = 0; /* clear TX buffer */
|
||||
dsp->rx_W = dsp->rx_R = -1; /* reset RX buffer */
|
||||
memset(dsp->rx_buff, 0, sizeof(dsp->rx_buff));
|
||||
dsp_cmx_hardware(dsp->conf, dsp);
|
||||
spin_unlock_irqrestore(&dsp_obj.lock, flags);
|
||||
|
@ -799,11 +764,8 @@ new_dsp(mISDNstack_t *st, mISDN_pid_t *pid)
|
|||
return(err);
|
||||
}
|
||||
sprintf(ndsp->inst.name, "DSP_S%x/C%x",
|
||||
(st->id&0xff), (st->id&0xff00)>>8);
|
||||
//#ifndef AUTOJITTER
|
||||
(st->id&0xff00)>>8, (st->id&0xff0000)>>16);
|
||||
/* set frame size to start */
|
||||
ndsp->largest = 64 << 1;
|
||||
//#endif
|
||||
ndsp->features.hfc_id = -1; /* current PCM id */
|
||||
ndsp->features.pcm_id = -1; /* current PCM id */
|
||||
ndsp->pcm_slot_rx = -1; /* current CPM slot */
|
||||
|
@ -811,10 +773,11 @@ new_dsp(mISDNstack_t *st, mISDN_pid_t *pid)
|
|||
ndsp->pcm_bank_rx = -1;
|
||||
ndsp->pcm_bank_tx = -1;
|
||||
ndsp->hfc_conf = -1; /* current conference number */
|
||||
/* set timer */
|
||||
/* set tone timer */
|
||||
ndsp->tone.tl.function = (void *)dsp_tone_timeout;
|
||||
ndsp->tone.tl.data = (long) ndsp;
|
||||
init_timer(&ndsp->tone.tl);
|
||||
/* set dsp feture timer */
|
||||
ndsp->feature_tl.function = (void *)dsp_feat;
|
||||
ndsp->feature_tl.data = (long) ndsp;
|
||||
ndsp->feature_state = FEAT_STATE_INIT;
|
||||
|
@ -917,29 +880,26 @@ static int dsp_init(void)
|
|||
/* display revision */
|
||||
printk(KERN_INFO "mISDN_dsp: Audio DSP Rev. %s (debug=0x%x)\n", mISDN_getrev(dsp_revision), debug);
|
||||
|
||||
#ifndef AUTOJITTER
|
||||
/* set packet size */
|
||||
switch(poll) {
|
||||
case 8:
|
||||
break;
|
||||
case 16:
|
||||
break;
|
||||
case 32:
|
||||
break;
|
||||
case 64: case 0:
|
||||
if (poll == 0)
|
||||
poll = 64;
|
||||
break;
|
||||
case 128:
|
||||
break;
|
||||
case 256:
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s: Wrong poll value (%d).\n", __FUNCTION__, poll);
|
||||
if (poll > MAX_POLL) {
|
||||
printk(KERN_ERR "%s: Wrong poll value (%d), using %d.\n", __FUNCTION__, poll, MAX_POLL);
|
||||
poll = MAX_POLL;
|
||||
}
|
||||
if (poll < 8) {
|
||||
printk(KERN_ERR "%s: Wrong poll value (%d), using 8.\n", __FUNCTION__, poll);
|
||||
poll = 8;
|
||||
}
|
||||
dsp_poll = poll;
|
||||
dsp_tics = poll * HZ / 8000;
|
||||
if (dsp_tics * 8000 == poll * HZ)
|
||||
printk(KERN_INFO "mISDN_dsp: DSP clocks every %d samples. This equals %d jiffies.\n", poll, dsp_tics);
|
||||
else {
|
||||
printk(KERN_INFO "mISDN_dsp: Cannot clock ever %d samples. Use a multiple of %d (jiffies)\n", poll, 8000 / HZ);
|
||||
err = -EINVAL;
|
||||
return(err);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
/* fill mISDN object (dsp_obj) */
|
||||
memset(&dsp_obj, 0, sizeof(dsp_obj));
|
||||
|
@ -968,6 +928,14 @@ static int dsp_init(void)
|
|||
return(err);
|
||||
}
|
||||
|
||||
/* set sample timer */
|
||||
dsp_spl_tl.function = (void *)dsp_cmx_send;
|
||||
dsp_spl_tl.data = 0;
|
||||
init_timer(&dsp_spl_tl);
|
||||
dsp_spl_tl.expires = jiffies + dsp_tics + 1; /* safer */
|
||||
dsp_spl_jiffies = dsp_spl_tl.expires;
|
||||
add_timer(&dsp_spl_tl);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
@ -980,6 +948,9 @@ static void dsp_cleanup(void)
|
|||
dsp_t *dspl, *nd;
|
||||
int err;
|
||||
|
||||
if (timer_pending(&dsp_spl_tl))
|
||||
del_timer(&dsp_spl_tl);
|
||||
|
||||
if (dsp_debug & DEBUG_DSP_MGR)
|
||||
printk(KERN_DEBUG "%s: removing module\n", __FUNCTION__);
|
||||
|
||||
|
|
Loading…
Reference in New Issue