/* * Author Andreas Eversberg (jolly@eversberg.eu) * Based on source code structure by * Karsten Keil (keil@isdn4linux.de) * * This file is (c) under GNU PUBLIC LICENSE * * Thanks to Karsten Keil (great drivers) * Cologne Chip (great chips) * * This module does: * Real-time tone generation * DTMF detection * Real-time cross-connection and conferrence * Compensate jitter due to system load and hardware fault. * All features are done in kernel space and will be realized * using hardware, if available and supported by chip set. * Blowfish encryption/decryption
*/
/* STRUCTURE: * * The dsp module provides layer 2 for b-channels (64kbit). It provides * transparent audio forwarding with special digital signal processing: * * - (1) generation of tones * - (2) detection of dtmf tones * - (3) crossconnecting and conferences (clocking) * - (4) echo generation for delay test * - (5) volume control * - (6) disable receive data * - (7) pipeline * - (8) encryption/decryption * * Look: * TX RX * ------upper layer------ * | ^ * | |(6) * v | * +-----+-------------+-----+ * |(3)(4) | * | CMX | * | | * | +-------------+ * | | ^ * | | | * |+---------+| +----+----+ * ||(1) || |(2) | * || || | | * || Tones || | DTMF | * || || | | * || || | | * |+----+----+| +----+----+ * +-----+-----+ ^ * | | * v | * +----+----+ +----+----+ * |(5) | |(5) | * | | | | * |TX Volume| |RX Volume| * | | | | * | | | | * +----+----+ +----+----+ * | ^ * | | * v | * +----+-------------+----+ * |(7) | * | | * | Pipeline Processing | * | | * | | * +----+-------------+----+ * | ^ * | | * v | * +----+----+ +----+----+ * |(8) | |(8) | * | | | | * | Encrypt | | Decrypt | * | | | | * | | | | * +----+----+ +----+----+ * | ^ * | | * v | * ------card layer------ * TX RX * * Above you can see the logical data flow. If software is used to do the * process, it is actually the real data flow. If hardware is used, data * may not flow, but hardware commands to the card, to provide the data flow * as shown. * * NOTE: The channel must be activated in order to make dsp work, even if * no data flow to the upper layer is intended. Activation can be done * after and before controlling the setting using PH_CONTROL requests. * * DTMF: Will be detected by hardware if possible. It is done before CMX * processing. * * Tones: Will be generated via software if endless looped audio fifos are * not supported by hardware. Tones will override all data from CMX. * It is not required to join a conference to use tones at any time. * * CMX: Is transparent when not used. When it is used, it will do * crossconnections and conferences via software if not possible through * hardware. If hardware capability is available, hardware is used. * * Echo: Is generated by CMX and is used to check performance of hard and * software CMX. * * The CMX has special functions for conferences with one, two and more * members. It will allow different types of data flow. Receive and transmit * data to/form upper layer may be switched on/off individually without losing * features of CMX, Tones and DTMF. * * Echo Cancellation: Sometimes we like to cancel echo from the interface. * Note that a VoIP call may not have echo caused by the IP phone. The echo * is generated by the telephone line connected to it. Because the delay * is high, it becomes an echo. RESULT: Echo Cachelation is required if * both echo AND delay is applied to an interface. * Remember that software CMX always generates a more or less delay. * * If all used features can be realized in hardware, and if transmit and/or * receive data ist disabled, the card may not send/receive any data at all. * Not receiving is useful if only announcements are played. Not sending is * useful if an answering machine records audio. Not sending and receiving is * useful during most states of the call. If supported by hardware, tones * will be played without cpu load. Small PBXs and NT-Mode applications will * not need expensive hardware when processing calls. * * * LOCKING: * * When data is received from upper or lower layer (card), the complete dsp * module is locked by a global lock. This lock MUST lock irq, because it * must lock timer events by DSP poll timer. * When data is ready to be transmitted down, the data is queued and sent * outside lock and timer event. * PH_CONTROL must not change any settings, join or split conference members * during process of data. * * HDLC: * * It works quite the same as transparent, except that HDLC data is forwarded * to all other conference members if no hardware bridging is possible. * Send data will be writte to sendq. Sendq will be sent if confirm is received. * Conference cannot join, if one member is not hdlc. *
*/
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); return 0;
} /* 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, * must lock here, or we may hit send-process currently
* 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
¤ Dauer der Verarbeitung: 0.30 Sekunden
(vorverarbeitet)
¤
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.