DEFINE_SPINLOCK(dsp_lock); /* global dsp lock */
LIST_HEAD(dsp_ilist);
LIST_HEAD(conf_ilist); int dsp_debug; int dsp_options; int dsp_poll, dsp_tics;
/* check if rx may be turned off or must be turned on */ staticvoid
dsp_rx_off_member(struct dsp *dsp)
{ struct mISDN_ctrl_req cq; int rx_off = 1;
memset(&cq, 0, sizeof(cq));
if (!dsp->features_rx_off) return;
/* not disabled */ if (!dsp->rx_disabled)
rx_off = 0; /* software dtmf */ elseif (dsp->dtmf.software)
rx_off = 0; /* echo in software */ elseif (dsp->echo.software)
rx_off = 0; /* bridge in software */ elseif (dsp->conf && dsp->conf->software)
rx_off = 0; /* data is not required by user space and not required
* for echo dtmf detection, soft-echo, soft-bridging */
if (rx_off == dsp->rx_is_off) return;
if (!dsp->ch.peer) { if (dsp_debug & DEBUG_DSP_CORE)
printk(KERN_DEBUG "%s: no peer, no rx_off\n",
__func__); return;
}
cq.op = MISDN_CTRL_RX_OFF;
cq.p1 = rx_off; if (dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq)) {
printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n",
__func__); return;
}
dsp->rx_is_off = rx_off; if (dsp_debug & DEBUG_DSP_CORE)
printk(KERN_DEBUG "%s: %s set rx_off = %d\n",
__func__, dsp->name, rx_off);
} staticvoid
dsp_rx_off(struct dsp *dsp)
{ struct dsp_conf_member *member;
if (dsp_options & DSP_OPT_NOHARDWARE) return;
/* no conf */ if (!dsp->conf) {
dsp_rx_off_member(dsp); return;
} /* check all members in conf */
list_for_each_entry(member, &dsp->conf->mlist, list) {
dsp_rx_off_member(member->dsp);
}
}
hh = mISDN_HEAD_P(skb); switch (hh->prim) { /* FROM DOWN */ case (PH_DATA_CNF):
dsp->data_pending = 0; /* trigger next hdlc frame, if any */ if (dsp->hdlc) {
spin_lock_irqsave(&dsp_lock, flags); if (dsp->b_active)
schedule_work(&dsp->workq);
spin_unlock_irqrestore(&dsp_lock, flags);
} break; case (PH_DATA_IND): case (DL_DATA_IND): if (skb->len < 1) {
ret = -EINVAL; break;
} if (dsp->rx_is_off) { if (dsp_debug & DEBUG_DSP_CORE)
printk(KERN_DEBUG "%s: rx-data during rx_off" " for %s\n",
__func__, dsp->name);
} if (dsp->hdlc) { /* hdlc */
spin_lock_irqsave(&dsp_lock, flags);
dsp_cmx_hdlc(dsp, skb);
spin_unlock_irqrestore(&dsp_lock, flags); if (dsp->rx_disabled) { /* if receive is not allowed */ break;
}
hh->prim = DL_DATA_IND; if (dsp->up) return dsp->up->send(dsp->up, skb); break;
}
spin_lock_irqsave(&dsp_lock, flags);
/* decrypt if enabled */ if (dsp->bf_enable)
dsp_bf_decrypt(dsp, skb->data, skb->len); /* pipeline */ if (dsp->pipeline.inuse)
dsp_pipeline_process_rx(&dsp->pipeline, skb->data,
skb->len, hh->id); /* change volume if requested */ if (dsp->rx_volume)
dsp_change_volume(skb, dsp->rx_volume); /* check if dtmf soft decoding is turned on */ if (dsp->dtmf.software) {
digits = dsp_dtmf_goertzel_decode(dsp, skb->data,
skb->len, (dsp_options & DSP_OPT_ULAW) ? 1 : 0);
} /* we need to process receive data if software */ if (dsp->conf && dsp->conf->software) { /* process data from card at cmx */
dsp_cmx_receive(dsp, skb);
}
spin_unlock_irqrestore(&dsp_lock, flags);
/* send dtmf result, if any */ if (digits) { while (*digits) { int k; struct sk_buff *nskb; if (dsp_debug & DEBUG_DSP_DTMF)
printk(KERN_DEBUG "%s: digit" "(%c) to layer %s\n",
__func__, *digits, dsp->name);
k = *digits | DTMF_TONE_VAL;
nskb = _alloc_mISDN_skb(PH_CONTROL_IND,
MISDN_ID_ANY, sizeof(int), &k,
GFP_ATOMIC); if (nskb) { if (dsp->up) { if (dsp->up->send(
dsp->up, nskb))
dev_kfree_skb(nskb);
} else
dev_kfree_skb(nskb);
}
digits++;
}
} if (dsp->rx_disabled) { /* if receive is not allowed */ break;
}
hh->prim = DL_DATA_IND; if (dsp->up) return dsp->up->send(dsp->up, skb); break; case (PH_CONTROL_IND): if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
printk(KERN_DEBUG "%s: PH_CONTROL INDICATION " "received: %x (len %d) %s\n", __func__,
hh->id, skb->len, dsp->name); switch (hh->id) { case (DTMF_HFC_COEF): /* getting coefficients */ if (!dsp->dtmf.hardware) { if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
printk(KERN_DEBUG "%s: ignoring DTMF " "coefficients from HFC\n",
__func__); break;
}
digits = dsp_dtmf_goertzel_decode(dsp, skb->data,
skb->len, 2); while (*digits) { int k; struct sk_buff *nskb; if (dsp_debug & DEBUG_DSP_DTMF)
printk(KERN_DEBUG "%s: digit" "(%c) to layer %s\n",
__func__, *digits, dsp->name);
k = *digits | DTMF_TONE_VAL;
nskb = _alloc_mISDN_skb(PH_CONTROL_IND,
MISDN_ID_ANY, sizeof(int), &k,
GFP_ATOMIC); if (nskb) { if (dsp->up) { if (dsp->up->send(
dsp->up, nskb))
dev_kfree_skb(nskb);
} else
dev_kfree_skb(nskb);
}
digits++;
} break; case (HFC_VOL_CHANGE_TX): /* change volume */ if (skb->len != sizeof(int)) {
ret = -EINVAL; break;
}
spin_lock_irqsave(&dsp_lock, flags);
dsp->tx_volume = *((int *)skb->data); if (dsp_debug & DEBUG_DSP_CORE)
printk(KERN_DEBUG "%s: change tx volume to " "%d\n", __func__, dsp->tx_volume);
dsp_cmx_hardware(dsp->conf, dsp);
dsp_dtmf_hardware(dsp);
dsp_rx_off(dsp);
spin_unlock_irqrestore(&dsp_lock, flags); break; default: if (dsp_debug & DEBUG_DSP_CORE)
printk(KERN_DEBUG "%s: ctrl ind %x unhandled " "%s\n", __func__, hh->id, dsp->name);
ret = -EINVAL;
} break; case (PH_ACTIVATE_IND): case (PH_ACTIVATE_CNF): if (dsp_debug & DEBUG_DSP_CORE)
printk(KERN_DEBUG "%s: b_channel is now active %s\n",
__func__, dsp->name); /* bchannel now active */
spin_lock_irqsave(&dsp_lock, flags);
dsp->b_active = 1;
dsp->data_pending = 0;
dsp->rx_init = 1; /* rx_W and rx_R will be adjusted on first frame */
dsp->rx_W = 0;
dsp->rx_R = 0;
memset(dsp->rx_buff, 0, sizeof(dsp->rx_buff));
dsp_cmx_hardware(dsp->conf, dsp);
dsp_dtmf_hardware(dsp);
dsp_rx_off(dsp);
spin_unlock_irqrestore(&dsp_lock, flags); if (dsp_debug & DEBUG_DSP_CORE)
printk(KERN_DEBUG "%s: done with activation, sending " "confirm to user space. %s\n", __func__,
dsp->name); /* send activation to upper layer */
hh->prim = DL_ESTABLISH_CNF; if (dsp->up) return dsp->up->send(dsp->up, skb); break; case (PH_DEACTIVATE_IND): case (PH_DEACTIVATE_CNF): if (dsp_debug & DEBUG_DSP_CORE)
printk(KERN_DEBUG "%s: b_channel is now inactive %s\n",
__func__, dsp->name); /* bchannel now inactive */
spin_lock_irqsave(&dsp_lock, flags);
dsp->b_active = 0;
dsp->data_pending = 0;
dsp_cmx_hardware(dsp->conf, dsp);
dsp_rx_off(dsp);
spin_unlock_irqrestore(&dsp_lock, flags);
hh->prim = DL_RELEASE_CNF; if (dsp->up) return dsp->up->send(dsp->up, skb); break; /* FROM UP */ case (DL_DATA_REQ): case (PH_DATA_REQ): if (skb->len < 1) {
ret = -EINVAL; break;
} if (dsp->hdlc) { /* hdlc */ if (!dsp->b_active) {
ret = -EIO; break;
}
hh->prim = PH_DATA_REQ;
spin_lock_irqsave(&dsp_lock, flags);
skb_queue_tail(&dsp->sendq, skb);
schedule_work(&dsp->workq);
spin_unlock_irqrestore(&dsp_lock, flags); return0;
} /* send data to tx-buffer (if no tone is played) */ if (!dsp->tone.tone) {
spin_lock_irqsave(&dsp_lock, flags);
dsp_cmx_transmit(dsp, skb);
spin_unlock_irqrestore(&dsp_lock, flags);
} break; case (PH_CONTROL_REQ):
spin_lock_irqsave(&dsp_lock, flags);
ret = dsp_control_req(dsp, hh, skb);
spin_unlock_irqrestore(&dsp_lock, flags); break; case (DL_ESTABLISH_REQ): case (PH_ACTIVATE_REQ): if (dsp_debug & DEBUG_DSP_CORE)
printk(KERN_DEBUG "%s: activating b_channel %s\n",
__func__, dsp->name); if (dsp->dtmf.hardware || dsp->dtmf.software)
dsp_dtmf_goertzel_init(dsp);
get_features(ch); /* enable fill_empty feature */ if (dsp->features_fill_empty)
dsp_fill_empty(dsp); /* send ph_activate */
hh->prim = PH_ACTIVATE_REQ; if (ch->peer) return ch->recv(ch->peer, skb); break; case (DL_RELEASE_REQ): case (PH_DEACTIVATE_REQ): if (dsp_debug & DEBUG_DSP_CORE)
printk(KERN_DEBUG "%s: releasing b_channel %s\n",
__func__, dsp->name);
spin_lock_irqsave(&dsp_lock, flags);
dsp->tone.tone = 0;
dsp->tone.hardware = 0;
dsp->tone.software = 0; if (timer_pending(&dsp->tone.tl))
timer_delete(&dsp->tone.tl); if (dsp->conf)
dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be
called here */
skb_queue_purge(&dsp->sendq);
spin_unlock_irqrestore(&dsp_lock, flags);
hh->prim = PH_DEACTIVATE_REQ; if (ch->peer) return ch->recv(ch->peer, skb); break; default: if (dsp_debug & DEBUG_DSP_CORE)
printk(KERN_DEBUG "%s: msg %x unhandled %s\n",
__func__, hh->prim, dsp->name);
ret = -EINVAL;
} if (!ret)
dev_kfree_skb(skb); return ret;
}
if (debug & DEBUG_DSP_CTRL)
printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd);
switch (cmd) { case OPEN_CHANNEL: break; case CLOSE_CHANNEL: if (dsp->ch.peer)
dsp->ch.peer->ctrl(dsp->ch.peer, CLOSE_CHANNEL, NULL);
/* wait until workqueue has finished, *mustlockhere,orwemayhitsend-processcurrently
* queueing. */
spin_lock_irqsave(&dsp_lock, flags);
dsp->b_active = 0;
spin_unlock_irqrestore(&dsp_lock, flags); /* MUST not be locked, because it waits until queue is done. */
cancel_work_sync(&dsp->workq);
spin_lock_irqsave(&dsp_lock, flags); if (timer_pending(&dsp->tone.tl))
timer_delete(&dsp->tone.tl);
skb_queue_purge(&dsp->sendq); if (dsp_debug & DEBUG_DSP_CTRL)
printk(KERN_DEBUG "%s: releasing member %s\n",
__func__, dsp->name);
dsp->b_active = 0;
dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be called
here */
dsp_pipeline_destroy(&dsp->pipeline);
if (dsp->hdlc && dsp->data_pending) return; /* wait until data has been acknowledged */
/* send queued data */ while ((skb = skb_dequeue(&dsp->sendq))) { /* in locked date, we must have still data in queue */ if (dsp->data_pending) { if (dsp_debug & DEBUG_DSP_CORE)
printk(KERN_DEBUG "%s: fifo full %s, this is " "no bug!\n", __func__, dsp->name); /* flush transparent data, if not acked */
dev_kfree_skb(skb); continue;
}
hh = mISDN_HEAD_P(skb); if (hh->prim == DL_DATA_REQ) { /* send packet up */ if (dsp->up) { if (dsp->up->send(dsp->up, skb))
dev_kfree_skb(skb);
} else
dev_kfree_skb(skb);
} else { /* send packet down */ if (dsp->ch.peer) {
dsp->data_pending = 1; if (dsp->ch.recv(dsp->ch.peer, skb)) {
dev_kfree_skb(skb);
dsp->data_pending = 0;
}
} else
dev_kfree_skb(skb);
}
}
}
if (!list_empty(&dsp_ilist)) {
printk(KERN_ERR "mISDN_dsp: Audio DSP object inst list not " "empty.\n");
} if (!list_empty(&conf_ilist)) {
printk(KERN_ERR "mISDN_dsp: Conference list not empty. Not " "all memory freed.\n");
}
dsp_pipeline_module_exit();
}
module_init(dsp_init);
module_exit(dsp_cleanup);
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.20Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-27)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.