1960366cfSKarsten Keil /*
2960366cfSKarsten Keil * Author Andreas Eversberg (jolly@eversberg.eu)
3960366cfSKarsten Keil * Based on source code structure by
4960366cfSKarsten Keil * Karsten Keil (keil@isdn4linux.de)
5960366cfSKarsten Keil *
6960366cfSKarsten Keil * This file is (c) under GNU PUBLIC LICENSE
7960366cfSKarsten Keil *
8960366cfSKarsten Keil * Thanks to Karsten Keil (great drivers)
9960366cfSKarsten Keil * Cologne Chip (great chips)
10960366cfSKarsten Keil *
11960366cfSKarsten Keil * This module does:
12960366cfSKarsten Keil * Real-time tone generation
13960366cfSKarsten Keil * DTMF detection
14960366cfSKarsten Keil * Real-time cross-connection and conferrence
15960366cfSKarsten Keil * Compensate jitter due to system load and hardware fault.
16960366cfSKarsten Keil * All features are done in kernel space and will be realized
17960366cfSKarsten Keil * using hardware, if available and supported by chip set.
18960366cfSKarsten Keil * Blowfish encryption/decryption
19960366cfSKarsten Keil */
20960366cfSKarsten Keil
21960366cfSKarsten Keil /* STRUCTURE:
22960366cfSKarsten Keil *
23960366cfSKarsten Keil * The dsp module provides layer 2 for b-channels (64kbit). It provides
24960366cfSKarsten Keil * transparent audio forwarding with special digital signal processing:
25960366cfSKarsten Keil *
26960366cfSKarsten Keil * - (1) generation of tones
27960366cfSKarsten Keil * - (2) detection of dtmf tones
28960366cfSKarsten Keil * - (3) crossconnecting and conferences (clocking)
29960366cfSKarsten Keil * - (4) echo generation for delay test
30960366cfSKarsten Keil * - (5) volume control
31960366cfSKarsten Keil * - (6) disable receive data
32960366cfSKarsten Keil * - (7) pipeline
33960366cfSKarsten Keil * - (8) encryption/decryption
34960366cfSKarsten Keil *
35960366cfSKarsten Keil * Look:
36960366cfSKarsten Keil * TX RX
37960366cfSKarsten Keil * ------upper layer------
38960366cfSKarsten Keil * | ^
39960366cfSKarsten Keil * | |(6)
40960366cfSKarsten Keil * v |
41960366cfSKarsten Keil * +-----+-------------+-----+
42960366cfSKarsten Keil * |(3)(4) |
43960366cfSKarsten Keil * | CMX |
44960366cfSKarsten Keil * | |
45960366cfSKarsten Keil * | +-------------+
46960366cfSKarsten Keil * | | ^
47960366cfSKarsten Keil * | | |
48960366cfSKarsten Keil * |+---------+| +----+----+
49960366cfSKarsten Keil * ||(1) || |(2) |
50960366cfSKarsten Keil * || || | |
51960366cfSKarsten Keil * || Tones || | DTMF |
52960366cfSKarsten Keil * || || | |
53960366cfSKarsten Keil * || || | |
54960366cfSKarsten Keil * |+----+----+| +----+----+
55960366cfSKarsten Keil * +-----+-----+ ^
56960366cfSKarsten Keil * | |
57960366cfSKarsten Keil * v |
58960366cfSKarsten Keil * +----+----+ +----+----+
59960366cfSKarsten Keil * |(5) | |(5) |
60960366cfSKarsten Keil * | | | |
61960366cfSKarsten Keil * |TX Volume| |RX Volume|
62960366cfSKarsten Keil * | | | |
63960366cfSKarsten Keil * | | | |
64960366cfSKarsten Keil * +----+----+ +----+----+
65960366cfSKarsten Keil * | ^
66960366cfSKarsten Keil * | |
67960366cfSKarsten Keil * v |
68960366cfSKarsten Keil * +----+-------------+----+
69960366cfSKarsten Keil * |(7) |
70960366cfSKarsten Keil * | |
71960366cfSKarsten Keil * | Pipeline Processing |
72960366cfSKarsten Keil * | |
73960366cfSKarsten Keil * | |
74960366cfSKarsten Keil * +----+-------------+----+
75960366cfSKarsten Keil * | ^
76960366cfSKarsten Keil * | |
77960366cfSKarsten Keil * v |
78960366cfSKarsten Keil * +----+----+ +----+----+
79960366cfSKarsten Keil * |(8) | |(8) |
80960366cfSKarsten Keil * | | | |
81960366cfSKarsten Keil * | Encrypt | | Decrypt |
82960366cfSKarsten Keil * | | | |
83960366cfSKarsten Keil * | | | |
84960366cfSKarsten Keil * +----+----+ +----+----+
85960366cfSKarsten Keil * | ^
86960366cfSKarsten Keil * | |
87960366cfSKarsten Keil * v |
88960366cfSKarsten Keil * ------card layer------
89960366cfSKarsten Keil * TX RX
90960366cfSKarsten Keil *
91960366cfSKarsten Keil * Above you can see the logical data flow. If software is used to do the
92960366cfSKarsten Keil * process, it is actually the real data flow. If hardware is used, data
93960366cfSKarsten Keil * may not flow, but hardware commands to the card, to provide the data flow
94960366cfSKarsten Keil * as shown.
95960366cfSKarsten Keil *
96960366cfSKarsten Keil * NOTE: The channel must be activated in order to make dsp work, even if
97960366cfSKarsten Keil * no data flow to the upper layer is intended. Activation can be done
98960366cfSKarsten Keil * after and before controlling the setting using PH_CONTROL requests.
99960366cfSKarsten Keil *
100960366cfSKarsten Keil * DTMF: Will be detected by hardware if possible. It is done before CMX
101960366cfSKarsten Keil * processing.
102960366cfSKarsten Keil *
103960366cfSKarsten Keil * Tones: Will be generated via software if endless looped audio fifos are
104960366cfSKarsten Keil * not supported by hardware. Tones will override all data from CMX.
105960366cfSKarsten Keil * It is not required to join a conference to use tones at any time.
106960366cfSKarsten Keil *
107960366cfSKarsten Keil * CMX: Is transparent when not used. When it is used, it will do
108960366cfSKarsten Keil * crossconnections and conferences via software if not possible through
109960366cfSKarsten Keil * hardware. If hardware capability is available, hardware is used.
110960366cfSKarsten Keil *
111af901ca1SAndré Goddard Rosa * Echo: Is generated by CMX and is used to check performance of hard and
112960366cfSKarsten Keil * software CMX.
113960366cfSKarsten Keil *
114960366cfSKarsten Keil * The CMX has special functions for conferences with one, two and more
115960366cfSKarsten Keil * members. It will allow different types of data flow. Receive and transmit
11608a7e621SMasahiro Yamada * data to/form upper layer may be switched on/off individually without losing
117960366cfSKarsten Keil * features of CMX, Tones and DTMF.
118960366cfSKarsten Keil *
119960366cfSKarsten Keil * Echo Cancellation: Sometimes we like to cancel echo from the interface.
120960366cfSKarsten Keil * Note that a VoIP call may not have echo caused by the IP phone. The echo
121960366cfSKarsten Keil * is generated by the telephone line connected to it. Because the delay
122960366cfSKarsten Keil * is high, it becomes an echo. RESULT: Echo Cachelation is required if
123960366cfSKarsten Keil * both echo AND delay is applied to an interface.
124960366cfSKarsten Keil * Remember that software CMX always generates a more or less delay.
125960366cfSKarsten Keil *
126960366cfSKarsten Keil * If all used features can be realized in hardware, and if transmit and/or
127960366cfSKarsten Keil * receive data ist disabled, the card may not send/receive any data at all.
12825985edcSLucas De Marchi * Not receiving is useful if only announcements are played. Not sending is
12925985edcSLucas De Marchi * useful if an answering machine records audio. Not sending and receiving is
13025985edcSLucas De Marchi * useful during most states of the call. If supported by hardware, tones
131960366cfSKarsten Keil * will be played without cpu load. Small PBXs and NT-Mode applications will
132960366cfSKarsten Keil * not need expensive hardware when processing calls.
133960366cfSKarsten Keil *
134960366cfSKarsten Keil *
135960366cfSKarsten Keil * LOCKING:
136960366cfSKarsten Keil *
137960366cfSKarsten Keil * When data is received from upper or lower layer (card), the complete dsp
138960366cfSKarsten Keil * module is locked by a global lock. This lock MUST lock irq, because it
139960366cfSKarsten Keil * must lock timer events by DSP poll timer.
140960366cfSKarsten Keil * When data is ready to be transmitted down, the data is queued and sent
141960366cfSKarsten Keil * outside lock and timer event.
142960366cfSKarsten Keil * PH_CONTROL must not change any settings, join or split conference members
143960366cfSKarsten Keil * during process of data.
144960366cfSKarsten Keil *
145960366cfSKarsten Keil * HDLC:
146960366cfSKarsten Keil *
147960366cfSKarsten Keil * It works quite the same as transparent, except that HDLC data is forwarded
148960366cfSKarsten Keil * to all other conference members if no hardware bridging is possible.
149960366cfSKarsten Keil * Send data will be writte to sendq. Sendq will be sent if confirm is received.
150960366cfSKarsten Keil * Conference cannot join, if one member is not hdlc.
151960366cfSKarsten Keil *
152960366cfSKarsten Keil */
153960366cfSKarsten Keil
154960366cfSKarsten Keil #include <linux/delay.h>
1555a0e3ad6STejun Heo #include <linux/gfp.h>
156960366cfSKarsten Keil #include <linux/mISDNif.h>
157960366cfSKarsten Keil #include <linux/mISDNdsp.h>
158960366cfSKarsten Keil #include <linux/module.h>
159960366cfSKarsten Keil #include <linux/vmalloc.h>
160960366cfSKarsten Keil #include "core.h"
161960366cfSKarsten Keil #include "dsp.h"
162960366cfSKarsten Keil
1635b834354SHannes Eder static const char *mISDN_dsp_revision = "2.0";
164960366cfSKarsten Keil
165960366cfSKarsten Keil static int debug;
166960366cfSKarsten Keil static int options;
167960366cfSKarsten Keil static int poll;
168960366cfSKarsten Keil static int dtmfthreshold = 100;
169960366cfSKarsten Keil
170960366cfSKarsten Keil MODULE_AUTHOR("Andreas Eversberg");
171960366cfSKarsten Keil module_param(debug, uint, S_IRUGO | S_IWUSR);
172960366cfSKarsten Keil module_param(options, uint, S_IRUGO | S_IWUSR);
173960366cfSKarsten Keil module_param(poll, uint, S_IRUGO | S_IWUSR);
174960366cfSKarsten Keil module_param(dtmfthreshold, uint, S_IRUGO | S_IWUSR);
175960366cfSKarsten Keil MODULE_LICENSE("GPL");
176960366cfSKarsten Keil
177960366cfSKarsten Keil /*int spinnest = 0;*/
178960366cfSKarsten Keil
17977053fb7SShixin Liu DEFINE_SPINLOCK(dsp_lock); /* global dsp lock */
1805979415dSShixin Liu LIST_HEAD(dsp_ilist);
1815979415dSShixin Liu LIST_HEAD(conf_ilist);
182960366cfSKarsten Keil int dsp_debug;
183960366cfSKarsten Keil int dsp_options;
184960366cfSKarsten Keil int dsp_poll, dsp_tics;
185960366cfSKarsten Keil
186960366cfSKarsten Keil /* check if rx may be turned off or must be turned on */
187960366cfSKarsten Keil static void
dsp_rx_off_member(struct dsp * dsp)188960366cfSKarsten Keil dsp_rx_off_member(struct dsp *dsp)
189960366cfSKarsten Keil {
190960366cfSKarsten Keil struct mISDN_ctrl_req cq;
191960366cfSKarsten Keil int rx_off = 1;
192960366cfSKarsten Keil
1938dd2f36fSAndreas Eversberg memset(&cq, 0, sizeof(cq));
1948dd2f36fSAndreas Eversberg
195960366cfSKarsten Keil if (!dsp->features_rx_off)
196960366cfSKarsten Keil return;
197960366cfSKarsten Keil
198960366cfSKarsten Keil /* not disabled */
199960366cfSKarsten Keil if (!dsp->rx_disabled)
200960366cfSKarsten Keil rx_off = 0;
201960366cfSKarsten Keil /* software dtmf */
202960366cfSKarsten Keil else if (dsp->dtmf.software)
203960366cfSKarsten Keil rx_off = 0;
204960366cfSKarsten Keil /* echo in software */
205bc138ec4SAndreas Eversberg else if (dsp->echo.software)
206960366cfSKarsten Keil rx_off = 0;
207960366cfSKarsten Keil /* bridge in software */
208bc138ec4SAndreas Eversberg else if (dsp->conf && dsp->conf->software)
209960366cfSKarsten Keil rx_off = 0;
210bc138ec4SAndreas Eversberg /* data is not required by user space and not required
211bc138ec4SAndreas Eversberg * for echo dtmf detection, soft-echo, soft-bridging */
212960366cfSKarsten Keil
213960366cfSKarsten Keil if (rx_off == dsp->rx_is_off)
214960366cfSKarsten Keil return;
215960366cfSKarsten Keil
216960366cfSKarsten Keil if (!dsp->ch.peer) {
217960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
218960366cfSKarsten Keil printk(KERN_DEBUG "%s: no peer, no rx_off\n",
219960366cfSKarsten Keil __func__);
220960366cfSKarsten Keil return;
221960366cfSKarsten Keil }
222960366cfSKarsten Keil cq.op = MISDN_CTRL_RX_OFF;
223960366cfSKarsten Keil cq.p1 = rx_off;
224960366cfSKarsten Keil if (dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq)) {
225960366cfSKarsten Keil printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n",
226960366cfSKarsten Keil __func__);
227960366cfSKarsten Keil return;
228960366cfSKarsten Keil }
229960366cfSKarsten Keil dsp->rx_is_off = rx_off;
230960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
231960366cfSKarsten Keil printk(KERN_DEBUG "%s: %s set rx_off = %d\n",
232960366cfSKarsten Keil __func__, dsp->name, rx_off);
233960366cfSKarsten Keil }
234960366cfSKarsten Keil static void
dsp_rx_off(struct dsp * dsp)235960366cfSKarsten Keil dsp_rx_off(struct dsp *dsp)
236960366cfSKarsten Keil {
237960366cfSKarsten Keil struct dsp_conf_member *member;
238960366cfSKarsten Keil
239960366cfSKarsten Keil if (dsp_options & DSP_OPT_NOHARDWARE)
240960366cfSKarsten Keil return;
241960366cfSKarsten Keil
242960366cfSKarsten Keil /* no conf */
243960366cfSKarsten Keil if (!dsp->conf) {
244960366cfSKarsten Keil dsp_rx_off_member(dsp);
245960366cfSKarsten Keil return;
246960366cfSKarsten Keil }
247960366cfSKarsten Keil /* check all members in conf */
248960366cfSKarsten Keil list_for_each_entry(member, &dsp->conf->mlist, list) {
249960366cfSKarsten Keil dsp_rx_off_member(member->dsp);
250960366cfSKarsten Keil }
251960366cfSKarsten Keil }
252960366cfSKarsten Keil
2538dd2f36fSAndreas Eversberg /* enable "fill empty" feature */
2548dd2f36fSAndreas Eversberg static void
dsp_fill_empty(struct dsp * dsp)2558dd2f36fSAndreas Eversberg dsp_fill_empty(struct dsp *dsp)
2568dd2f36fSAndreas Eversberg {
2578dd2f36fSAndreas Eversberg struct mISDN_ctrl_req cq;
2588dd2f36fSAndreas Eversberg
2598dd2f36fSAndreas Eversberg memset(&cq, 0, sizeof(cq));
2608dd2f36fSAndreas Eversberg
2618dd2f36fSAndreas Eversberg if (!dsp->ch.peer) {
2628dd2f36fSAndreas Eversberg if (dsp_debug & DEBUG_DSP_CORE)
2638dd2f36fSAndreas Eversberg printk(KERN_DEBUG "%s: no peer, no fill_empty\n",
2648dd2f36fSAndreas Eversberg __func__);
2658dd2f36fSAndreas Eversberg return;
2668dd2f36fSAndreas Eversberg }
2678dd2f36fSAndreas Eversberg cq.op = MISDN_CTRL_FILL_EMPTY;
2688dd2f36fSAndreas Eversberg cq.p1 = 1;
2696d1ee48fSKarsten Keil cq.p2 = dsp_silence;
2708dd2f36fSAndreas Eversberg if (dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq)) {
2718dd2f36fSAndreas Eversberg printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n",
2728dd2f36fSAndreas Eversberg __func__);
2738dd2f36fSAndreas Eversberg return;
2748dd2f36fSAndreas Eversberg }
2758dd2f36fSAndreas Eversberg if (dsp_debug & DEBUG_DSP_CORE)
2768dd2f36fSAndreas Eversberg printk(KERN_DEBUG "%s: %s set fill_empty = 1\n",
2778dd2f36fSAndreas Eversberg __func__, dsp->name);
2788dd2f36fSAndreas Eversberg }
2798dd2f36fSAndreas Eversberg
280960366cfSKarsten Keil static int
dsp_control_req(struct dsp * dsp,struct mISDNhead * hh,struct sk_buff * skb)281960366cfSKarsten Keil dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb)
282960366cfSKarsten Keil {
283960366cfSKarsten Keil struct sk_buff *nskb;
284960366cfSKarsten Keil int ret = 0;
285960366cfSKarsten Keil int cont;
286960366cfSKarsten Keil u8 *data;
287960366cfSKarsten Keil int len;
288960366cfSKarsten Keil
2890d63c27dSDan Carpenter if (skb->len < sizeof(int)) {
290960366cfSKarsten Keil printk(KERN_ERR "%s: PH_CONTROL message too short\n", __func__);
2910d63c27dSDan Carpenter return -EINVAL;
2920d63c27dSDan Carpenter }
293960366cfSKarsten Keil cont = *((int *)skb->data);
294960366cfSKarsten Keil len = skb->len - sizeof(int);
295960366cfSKarsten Keil data = skb->data + sizeof(int);
296960366cfSKarsten Keil
297960366cfSKarsten Keil switch (cont) {
298960366cfSKarsten Keil case DTMF_TONE_START: /* turn on DTMF */
299960366cfSKarsten Keil if (dsp->hdlc) {
300960366cfSKarsten Keil ret = -EINVAL;
301960366cfSKarsten Keil break;
302960366cfSKarsten Keil }
303960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
304960366cfSKarsten Keil printk(KERN_DEBUG "%s: start dtmf\n", __func__);
305960366cfSKarsten Keil if (len == sizeof(int)) {
306c6a2e587SAndreas Eversberg if (dsp_debug & DEBUG_DSP_CORE)
307960366cfSKarsten Keil printk(KERN_NOTICE "changing DTMF Threshold "
308960366cfSKarsten Keil "to %d\n", *((int *)data));
309960366cfSKarsten Keil dsp->dtmf.treshold = (*(int *)data) * 10000;
310960366cfSKarsten Keil }
311b0579d74SAndreas Eversberg dsp->dtmf.enable = 1;
312960366cfSKarsten Keil /* init goertzel */
313960366cfSKarsten Keil dsp_dtmf_goertzel_init(dsp);
314960366cfSKarsten Keil
315960366cfSKarsten Keil /* check dtmf hardware */
316960366cfSKarsten Keil dsp_dtmf_hardware(dsp);
317b5df5a5cSAndreas Eversberg dsp_rx_off(dsp);
318960366cfSKarsten Keil break;
319960366cfSKarsten Keil case DTMF_TONE_STOP: /* turn off DTMF */
320960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
321960366cfSKarsten Keil printk(KERN_DEBUG "%s: stop dtmf\n", __func__);
322b0579d74SAndreas Eversberg dsp->dtmf.enable = 0;
323960366cfSKarsten Keil dsp->dtmf.hardware = 0;
324960366cfSKarsten Keil dsp->dtmf.software = 0;
325960366cfSKarsten Keil break;
326960366cfSKarsten Keil case DSP_CONF_JOIN: /* join / update conference */
327960366cfSKarsten Keil if (len < sizeof(int)) {
328960366cfSKarsten Keil ret = -EINVAL;
329960366cfSKarsten Keil break;
330960366cfSKarsten Keil }
331960366cfSKarsten Keil if (*((u32 *)data) == 0)
332960366cfSKarsten Keil goto conf_split;
333960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
334960366cfSKarsten Keil printk(KERN_DEBUG "%s: join conference %d\n",
335960366cfSKarsten Keil __func__, *((u32 *)data));
336960366cfSKarsten Keil ret = dsp_cmx_conf(dsp, *((u32 *)data));
337960366cfSKarsten Keil /* dsp_cmx_hardware will also be called here */
338960366cfSKarsten Keil dsp_rx_off(dsp);
339960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CMX)
340960366cfSKarsten Keil dsp_cmx_debug(dsp);
341960366cfSKarsten Keil break;
342960366cfSKarsten Keil case DSP_CONF_SPLIT: /* remove from conference */
343960366cfSKarsten Keil conf_split:
344960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
345960366cfSKarsten Keil printk(KERN_DEBUG "%s: release conference\n", __func__);
346960366cfSKarsten Keil ret = dsp_cmx_conf(dsp, 0);
347960366cfSKarsten Keil /* dsp_cmx_hardware will also be called here */
348960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CMX)
349960366cfSKarsten Keil dsp_cmx_debug(dsp);
350960366cfSKarsten Keil dsp_rx_off(dsp);
351960366cfSKarsten Keil break;
352960366cfSKarsten Keil case DSP_TONE_PATT_ON: /* play tone */
353960366cfSKarsten Keil if (dsp->hdlc) {
354960366cfSKarsten Keil ret = -EINVAL;
355960366cfSKarsten Keil break;
356960366cfSKarsten Keil }
357960366cfSKarsten Keil if (len < sizeof(int)) {
358960366cfSKarsten Keil ret = -EINVAL;
359960366cfSKarsten Keil break;
360960366cfSKarsten Keil }
361960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
362960366cfSKarsten Keil printk(KERN_DEBUG "%s: turn tone 0x%x on\n",
363960366cfSKarsten Keil __func__, *((int *)skb->data));
364960366cfSKarsten Keil ret = dsp_tone(dsp, *((int *)data));
365960366cfSKarsten Keil if (!ret) {
366960366cfSKarsten Keil dsp_cmx_hardware(dsp->conf, dsp);
367960366cfSKarsten Keil dsp_rx_off(dsp);
368960366cfSKarsten Keil }
369960366cfSKarsten Keil if (!dsp->tone.tone)
370960366cfSKarsten Keil goto tone_off;
371960366cfSKarsten Keil break;
372960366cfSKarsten Keil case DSP_TONE_PATT_OFF: /* stop tone */
373960366cfSKarsten Keil if (dsp->hdlc) {
374960366cfSKarsten Keil ret = -EINVAL;
375960366cfSKarsten Keil break;
376960366cfSKarsten Keil }
377960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
378960366cfSKarsten Keil printk(KERN_DEBUG "%s: turn tone off\n", __func__);
379960366cfSKarsten Keil dsp_tone(dsp, 0);
380960366cfSKarsten Keil dsp_cmx_hardware(dsp->conf, dsp);
381960366cfSKarsten Keil dsp_rx_off(dsp);
382960366cfSKarsten Keil /* reset tx buffers (user space data) */
383960366cfSKarsten Keil tone_off:
384960366cfSKarsten Keil dsp->rx_W = 0;
385960366cfSKarsten Keil dsp->rx_R = 0;
386960366cfSKarsten Keil break;
387960366cfSKarsten Keil case DSP_VOL_CHANGE_TX: /* change volume */
388960366cfSKarsten Keil if (dsp->hdlc) {
389960366cfSKarsten Keil ret = -EINVAL;
390960366cfSKarsten Keil break;
391960366cfSKarsten Keil }
392960366cfSKarsten Keil if (len < sizeof(int)) {
393960366cfSKarsten Keil ret = -EINVAL;
394960366cfSKarsten Keil break;
395960366cfSKarsten Keil }
396960366cfSKarsten Keil dsp->tx_volume = *((int *)data);
397960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
398960366cfSKarsten Keil printk(KERN_DEBUG "%s: change tx vol to %d\n",
399960366cfSKarsten Keil __func__, dsp->tx_volume);
400960366cfSKarsten Keil dsp_cmx_hardware(dsp->conf, dsp);
401960366cfSKarsten Keil dsp_dtmf_hardware(dsp);
402960366cfSKarsten Keil dsp_rx_off(dsp);
403960366cfSKarsten Keil break;
404960366cfSKarsten Keil case DSP_VOL_CHANGE_RX: /* change volume */
405960366cfSKarsten Keil if (dsp->hdlc) {
406960366cfSKarsten Keil ret = -EINVAL;
407960366cfSKarsten Keil break;
408960366cfSKarsten Keil }
409960366cfSKarsten Keil if (len < sizeof(int)) {
410960366cfSKarsten Keil ret = -EINVAL;
411960366cfSKarsten Keil break;
412960366cfSKarsten Keil }
413960366cfSKarsten Keil dsp->rx_volume = *((int *)data);
414960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
415960366cfSKarsten Keil printk(KERN_DEBUG "%s: change rx vol to %d\n",
416960366cfSKarsten Keil __func__, dsp->tx_volume);
417960366cfSKarsten Keil dsp_cmx_hardware(dsp->conf, dsp);
418960366cfSKarsten Keil dsp_dtmf_hardware(dsp);
419960366cfSKarsten Keil dsp_rx_off(dsp);
420960366cfSKarsten Keil break;
421960366cfSKarsten Keil case DSP_ECHO_ON: /* enable echo */
422bc138ec4SAndreas Eversberg dsp->echo.software = 1; /* soft echo */
423960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
424960366cfSKarsten Keil printk(KERN_DEBUG "%s: enable cmx-echo\n", __func__);
425960366cfSKarsten Keil dsp_cmx_hardware(dsp->conf, dsp);
426960366cfSKarsten Keil dsp_rx_off(dsp);
427960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CMX)
428960366cfSKarsten Keil dsp_cmx_debug(dsp);
429960366cfSKarsten Keil break;
430960366cfSKarsten Keil case DSP_ECHO_OFF: /* disable echo */
431bc138ec4SAndreas Eversberg dsp->echo.software = 0;
432bc138ec4SAndreas Eversberg dsp->echo.hardware = 0;
433960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
434960366cfSKarsten Keil printk(KERN_DEBUG "%s: disable cmx-echo\n", __func__);
435960366cfSKarsten Keil dsp_cmx_hardware(dsp->conf, dsp);
436960366cfSKarsten Keil dsp_rx_off(dsp);
437960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CMX)
438960366cfSKarsten Keil dsp_cmx_debug(dsp);
439960366cfSKarsten Keil break;
440960366cfSKarsten Keil case DSP_RECEIVE_ON: /* enable receive to user space */
441960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
442960366cfSKarsten Keil printk(KERN_DEBUG "%s: enable receive to user "
443960366cfSKarsten Keil "space\n", __func__);
444960366cfSKarsten Keil dsp->rx_disabled = 0;
445960366cfSKarsten Keil dsp_rx_off(dsp);
446960366cfSKarsten Keil break;
447960366cfSKarsten Keil case DSP_RECEIVE_OFF: /* disable receive to user space */
448960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
449960366cfSKarsten Keil printk(KERN_DEBUG "%s: disable receive to "
450960366cfSKarsten Keil "user space\n", __func__);
451960366cfSKarsten Keil dsp->rx_disabled = 1;
452960366cfSKarsten Keil dsp_rx_off(dsp);
453960366cfSKarsten Keil break;
454960366cfSKarsten Keil case DSP_MIX_ON: /* enable mixing of tx data */
455960366cfSKarsten Keil if (dsp->hdlc) {
456960366cfSKarsten Keil ret = -EINVAL;
457960366cfSKarsten Keil break;
458960366cfSKarsten Keil }
459960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
460960366cfSKarsten Keil printk(KERN_DEBUG "%s: enable mixing of "
461d939be3aSMasanari Iida "tx-data with conf members\n", __func__);
462960366cfSKarsten Keil dsp->tx_mix = 1;
463960366cfSKarsten Keil dsp_cmx_hardware(dsp->conf, dsp);
464960366cfSKarsten Keil dsp_rx_off(dsp);
465960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CMX)
466960366cfSKarsten Keil dsp_cmx_debug(dsp);
467960366cfSKarsten Keil break;
468960366cfSKarsten Keil case DSP_MIX_OFF: /* disable mixing of tx data */
469960366cfSKarsten Keil if (dsp->hdlc) {
470960366cfSKarsten Keil ret = -EINVAL;
471960366cfSKarsten Keil break;
472960366cfSKarsten Keil }
473960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
474960366cfSKarsten Keil printk(KERN_DEBUG "%s: disable mixing of "
475d939be3aSMasanari Iida "tx-data with conf members\n", __func__);
476960366cfSKarsten Keil dsp->tx_mix = 0;
477960366cfSKarsten Keil dsp_cmx_hardware(dsp->conf, dsp);
478960366cfSKarsten Keil dsp_rx_off(dsp);
479960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CMX)
480960366cfSKarsten Keil dsp_cmx_debug(dsp);
481960366cfSKarsten Keil break;
482960366cfSKarsten Keil case DSP_TXDATA_ON: /* enable txdata */
483960366cfSKarsten Keil dsp->tx_data = 1;
484960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
485960366cfSKarsten Keil printk(KERN_DEBUG "%s: enable tx-data\n", __func__);
486960366cfSKarsten Keil dsp_cmx_hardware(dsp->conf, dsp);
487960366cfSKarsten Keil dsp_rx_off(dsp);
488960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CMX)
489960366cfSKarsten Keil dsp_cmx_debug(dsp);
490960366cfSKarsten Keil break;
491960366cfSKarsten Keil case DSP_TXDATA_OFF: /* disable txdata */
492960366cfSKarsten Keil dsp->tx_data = 0;
493960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
494960366cfSKarsten Keil printk(KERN_DEBUG "%s: disable tx-data\n", __func__);
495960366cfSKarsten Keil dsp_cmx_hardware(dsp->conf, dsp);
496960366cfSKarsten Keil dsp_rx_off(dsp);
497960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CMX)
498960366cfSKarsten Keil dsp_cmx_debug(dsp);
499960366cfSKarsten Keil break;
500960366cfSKarsten Keil case DSP_DELAY: /* use delay algorithm instead of dynamic
501960366cfSKarsten Keil jitter algorithm */
502960366cfSKarsten Keil if (dsp->hdlc) {
503960366cfSKarsten Keil ret = -EINVAL;
504960366cfSKarsten Keil break;
505960366cfSKarsten Keil }
506960366cfSKarsten Keil if (len < sizeof(int)) {
507960366cfSKarsten Keil ret = -EINVAL;
508960366cfSKarsten Keil break;
509960366cfSKarsten Keil }
510960366cfSKarsten Keil dsp->cmx_delay = (*((int *)data)) << 3;
51119af5cdbSMartin Olsson /* milliseconds to samples */
512960366cfSKarsten Keil if (dsp->cmx_delay >= (CMX_BUFF_HALF >> 1))
513960366cfSKarsten Keil /* clip to half of maximum usable buffer
514960366cfSKarsten Keil (half of half buffer) */
515960366cfSKarsten Keil dsp->cmx_delay = (CMX_BUFF_HALF >> 1) - 1;
516960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
517960366cfSKarsten Keil printk(KERN_DEBUG "%s: use delay algorithm to "
518960366cfSKarsten Keil "compensate jitter (%d samples)\n",
519960366cfSKarsten Keil __func__, dsp->cmx_delay);
520960366cfSKarsten Keil break;
521960366cfSKarsten Keil case DSP_JITTER: /* use dynamic jitter algorithm instead of
522960366cfSKarsten Keil delay algorithm */
523960366cfSKarsten Keil if (dsp->hdlc) {
524960366cfSKarsten Keil ret = -EINVAL;
525960366cfSKarsten Keil break;
526960366cfSKarsten Keil }
527960366cfSKarsten Keil dsp->cmx_delay = 0;
528960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
529960366cfSKarsten Keil printk(KERN_DEBUG "%s: use jitter algorithm to "
530960366cfSKarsten Keil "compensate jitter\n", __func__);
531960366cfSKarsten Keil break;
532960366cfSKarsten Keil case DSP_TX_DEJITTER: /* use dynamic jitter algorithm for tx-buffer */
533960366cfSKarsten Keil if (dsp->hdlc) {
534960366cfSKarsten Keil ret = -EINVAL;
535960366cfSKarsten Keil break;
536960366cfSKarsten Keil }
537960366cfSKarsten Keil dsp->tx_dejitter = 1;
538960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
539960366cfSKarsten Keil printk(KERN_DEBUG "%s: use dejitter on TX "
540960366cfSKarsten Keil "buffer\n", __func__);
541960366cfSKarsten Keil break;
542960366cfSKarsten Keil case DSP_TX_DEJ_OFF: /* use tx-buffer without dejittering*/
543960366cfSKarsten Keil if (dsp->hdlc) {
544960366cfSKarsten Keil ret = -EINVAL;
545960366cfSKarsten Keil break;
546960366cfSKarsten Keil }
547960366cfSKarsten Keil dsp->tx_dejitter = 0;
548960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
549960366cfSKarsten Keil printk(KERN_DEBUG "%s: use TX buffer without "
550960366cfSKarsten Keil "dejittering\n", __func__);
551960366cfSKarsten Keil break;
552960366cfSKarsten Keil case DSP_PIPELINE_CFG:
553960366cfSKarsten Keil if (dsp->hdlc) {
554960366cfSKarsten Keil ret = -EINVAL;
555960366cfSKarsten Keil break;
556960366cfSKarsten Keil }
557960366cfSKarsten Keil if (len > 0 && ((char *)data)[len - 1]) {
558960366cfSKarsten Keil printk(KERN_DEBUG "%s: pipeline config string "
559960366cfSKarsten Keil "is not NULL terminated!\n", __func__);
560960366cfSKarsten Keil ret = -EINVAL;
561960366cfSKarsten Keil } else {
562960366cfSKarsten Keil dsp->pipeline.inuse = 1;
563960366cfSKarsten Keil dsp_cmx_hardware(dsp->conf, dsp);
564960366cfSKarsten Keil ret = dsp_pipeline_build(&dsp->pipeline,
565eac74af9SKarsten Keil len > 0 ? data : NULL);
566960366cfSKarsten Keil dsp_cmx_hardware(dsp->conf, dsp);
567960366cfSKarsten Keil dsp_rx_off(dsp);
568960366cfSKarsten Keil }
569960366cfSKarsten Keil break;
570960366cfSKarsten Keil case DSP_BF_ENABLE_KEY: /* turn blowfish on */
571960366cfSKarsten Keil if (dsp->hdlc) {
572960366cfSKarsten Keil ret = -EINVAL;
573960366cfSKarsten Keil break;
574960366cfSKarsten Keil }
575960366cfSKarsten Keil if (len < 4 || len > 56) {
576960366cfSKarsten Keil ret = -EINVAL;
577960366cfSKarsten Keil break;
578960366cfSKarsten Keil }
579960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
580960366cfSKarsten Keil printk(KERN_DEBUG "%s: turn blowfish on (key "
581960366cfSKarsten Keil "not shown)\n", __func__);
582960366cfSKarsten Keil ret = dsp_bf_init(dsp, (u8 *)data, len);
583960366cfSKarsten Keil /* set new cont */
584960366cfSKarsten Keil if (!ret)
585960366cfSKarsten Keil cont = DSP_BF_ACCEPT;
586960366cfSKarsten Keil else
587960366cfSKarsten Keil cont = DSP_BF_REJECT;
588960366cfSKarsten Keil /* send indication if it worked to set it */
589960366cfSKarsten Keil nskb = _alloc_mISDN_skb(PH_CONTROL_IND, MISDN_ID_ANY,
590960366cfSKarsten Keil sizeof(int), &cont, GFP_ATOMIC);
591960366cfSKarsten Keil if (nskb) {
592960366cfSKarsten Keil if (dsp->up) {
593960366cfSKarsten Keil if (dsp->up->send(dsp->up, nskb))
594960366cfSKarsten Keil dev_kfree_skb(nskb);
595960366cfSKarsten Keil } else
596960366cfSKarsten Keil dev_kfree_skb(nskb);
597960366cfSKarsten Keil }
598960366cfSKarsten Keil if (!ret) {
599960366cfSKarsten Keil dsp_cmx_hardware(dsp->conf, dsp);
600960366cfSKarsten Keil dsp_dtmf_hardware(dsp);
601960366cfSKarsten Keil dsp_rx_off(dsp);
602960366cfSKarsten Keil }
603960366cfSKarsten Keil break;
604960366cfSKarsten Keil case DSP_BF_DISABLE: /* turn blowfish off */
605960366cfSKarsten Keil if (dsp->hdlc) {
606960366cfSKarsten Keil ret = -EINVAL;
607960366cfSKarsten Keil break;
608960366cfSKarsten Keil }
609960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
610960366cfSKarsten Keil printk(KERN_DEBUG "%s: turn blowfish off\n", __func__);
611960366cfSKarsten Keil dsp_bf_cleanup(dsp);
612960366cfSKarsten Keil dsp_cmx_hardware(dsp->conf, dsp);
613960366cfSKarsten Keil dsp_dtmf_hardware(dsp);
614960366cfSKarsten Keil dsp_rx_off(dsp);
615960366cfSKarsten Keil break;
616960366cfSKarsten Keil default:
617960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
618960366cfSKarsten Keil printk(KERN_DEBUG "%s: ctrl req %x unhandled\n",
619960366cfSKarsten Keil __func__, cont);
620960366cfSKarsten Keil ret = -EINVAL;
621960366cfSKarsten Keil }
622960366cfSKarsten Keil return ret;
623960366cfSKarsten Keil }
624960366cfSKarsten Keil
625960366cfSKarsten Keil static void
get_features(struct mISDNchannel * ch)626960366cfSKarsten Keil get_features(struct mISDNchannel *ch)
627960366cfSKarsten Keil {
628960366cfSKarsten Keil struct dsp *dsp = container_of(ch, struct dsp, ch);
629960366cfSKarsten Keil struct mISDN_ctrl_req cq;
630960366cfSKarsten Keil
631960366cfSKarsten Keil if (!ch->peer) {
632960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
633960366cfSKarsten Keil printk(KERN_DEBUG "%s: no peer, no features\n",
634960366cfSKarsten Keil __func__);
635960366cfSKarsten Keil return;
636960366cfSKarsten Keil }
637960366cfSKarsten Keil memset(&cq, 0, sizeof(cq));
638960366cfSKarsten Keil cq.op = MISDN_CTRL_GETOP;
639960366cfSKarsten Keil if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq) < 0) {
640960366cfSKarsten Keil printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n",
641960366cfSKarsten Keil __func__);
642960366cfSKarsten Keil return;
643960366cfSKarsten Keil }
644960366cfSKarsten Keil if (cq.op & MISDN_CTRL_RX_OFF)
645960366cfSKarsten Keil dsp->features_rx_off = 1;
6468dd2f36fSAndreas Eversberg if (cq.op & MISDN_CTRL_FILL_EMPTY)
6478dd2f36fSAndreas Eversberg dsp->features_fill_empty = 1;
6488dd2f36fSAndreas Eversberg if (dsp_options & DSP_OPT_NOHARDWARE)
6498dd2f36fSAndreas Eversberg return;
650960366cfSKarsten Keil if ((cq.op & MISDN_CTRL_HW_FEATURES_OP)) {
651960366cfSKarsten Keil cq.op = MISDN_CTRL_HW_FEATURES;
652960366cfSKarsten Keil *((u_long *)&cq.p1) = (u_long)&dsp->features;
653960366cfSKarsten Keil if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq)) {
654960366cfSKarsten Keil printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n",
655960366cfSKarsten Keil __func__);
656960366cfSKarsten Keil }
657960366cfSKarsten Keil } else
658960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
659960366cfSKarsten Keil printk(KERN_DEBUG "%s: features not supported for %s\n",
660960366cfSKarsten Keil __func__, dsp->name);
661960366cfSKarsten Keil }
662960366cfSKarsten Keil
663960366cfSKarsten Keil static int
dsp_function(struct mISDNchannel * ch,struct sk_buff * skb)664960366cfSKarsten Keil dsp_function(struct mISDNchannel *ch, struct sk_buff *skb)
665960366cfSKarsten Keil {
666960366cfSKarsten Keil struct dsp *dsp = container_of(ch, struct dsp, ch);
667960366cfSKarsten Keil struct mISDNhead *hh;
668960366cfSKarsten Keil int ret = 0;
669b5df5a5cSAndreas Eversberg u8 *digits = NULL;
670960366cfSKarsten Keil u_long flags;
671960366cfSKarsten Keil
672960366cfSKarsten Keil hh = mISDN_HEAD_P(skb);
673960366cfSKarsten Keil switch (hh->prim) {
674960366cfSKarsten Keil /* FROM DOWN */
675960366cfSKarsten Keil case (PH_DATA_CNF):
676960366cfSKarsten Keil dsp->data_pending = 0;
677960366cfSKarsten Keil /* trigger next hdlc frame, if any */
678960366cfSKarsten Keil if (dsp->hdlc) {
679960366cfSKarsten Keil spin_lock_irqsave(&dsp_lock, flags);
680960366cfSKarsten Keil if (dsp->b_active)
681960366cfSKarsten Keil schedule_work(&dsp->workq);
682960366cfSKarsten Keil spin_unlock_irqrestore(&dsp_lock, flags);
683960366cfSKarsten Keil }
684960366cfSKarsten Keil break;
685960366cfSKarsten Keil case (PH_DATA_IND):
686960366cfSKarsten Keil case (DL_DATA_IND):
687960366cfSKarsten Keil if (skb->len < 1) {
688960366cfSKarsten Keil ret = -EINVAL;
689960366cfSKarsten Keil break;
690960366cfSKarsten Keil }
691960366cfSKarsten Keil if (dsp->rx_is_off) {
692960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
693960366cfSKarsten Keil printk(KERN_DEBUG "%s: rx-data during rx_off"
694960366cfSKarsten Keil " for %s\n",
695960366cfSKarsten Keil __func__, dsp->name);
696960366cfSKarsten Keil }
697960366cfSKarsten Keil if (dsp->hdlc) {
698960366cfSKarsten Keil /* hdlc */
699960366cfSKarsten Keil spin_lock_irqsave(&dsp_lock, flags);
700960366cfSKarsten Keil dsp_cmx_hdlc(dsp, skb);
701960366cfSKarsten Keil spin_unlock_irqrestore(&dsp_lock, flags);
702960366cfSKarsten Keil if (dsp->rx_disabled) {
703960366cfSKarsten Keil /* if receive is not allowed */
704960366cfSKarsten Keil break;
705960366cfSKarsten Keil }
706960366cfSKarsten Keil hh->prim = DL_DATA_IND;
707960366cfSKarsten Keil if (dsp->up)
708960366cfSKarsten Keil return dsp->up->send(dsp->up, skb);
709960366cfSKarsten Keil break;
710960366cfSKarsten Keil }
711960366cfSKarsten Keil
712ba3af34eSAndreas Eversberg spin_lock_irqsave(&dsp_lock, flags);
713ba3af34eSAndreas Eversberg
714960366cfSKarsten Keil /* decrypt if enabled */
715960366cfSKarsten Keil if (dsp->bf_enable)
716960366cfSKarsten Keil dsp_bf_decrypt(dsp, skb->data, skb->len);
717960366cfSKarsten Keil /* pipeline */
718960366cfSKarsten Keil if (dsp->pipeline.inuse)
719960366cfSKarsten Keil dsp_pipeline_process_rx(&dsp->pipeline, skb->data,
7207cfa153dSAndreas Eversberg skb->len, hh->id);
721960366cfSKarsten Keil /* change volume if requested */
722960366cfSKarsten Keil if (dsp->rx_volume)
723960366cfSKarsten Keil dsp_change_volume(skb, dsp->rx_volume);
724960366cfSKarsten Keil /* check if dtmf soft decoding is turned on */
725960366cfSKarsten Keil if (dsp->dtmf.software) {
726960366cfSKarsten Keil digits = dsp_dtmf_goertzel_decode(dsp, skb->data,
727960366cfSKarsten Keil skb->len, (dsp_options & DSP_OPT_ULAW) ? 1 : 0);
728b5df5a5cSAndreas Eversberg }
729b5df5a5cSAndreas Eversberg /* we need to process receive data if software */
730bc138ec4SAndreas Eversberg if (dsp->conf && dsp->conf->software) {
731b5df5a5cSAndreas Eversberg /* process data from card at cmx */
732b5df5a5cSAndreas Eversberg dsp_cmx_receive(dsp, skb);
733b5df5a5cSAndreas Eversberg }
734b5df5a5cSAndreas Eversberg
735b5df5a5cSAndreas Eversberg spin_unlock_irqrestore(&dsp_lock, flags);
736b5df5a5cSAndreas Eversberg
737b5df5a5cSAndreas Eversberg /* send dtmf result, if any */
738b5df5a5cSAndreas Eversberg if (digits) {
739960366cfSKarsten Keil while (*digits) {
740b5df5a5cSAndreas Eversberg int k;
741bb68b1d9SHannes Eder struct sk_buff *nskb;
742960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_DTMF)
743960366cfSKarsten Keil printk(KERN_DEBUG "%s: digit"
744960366cfSKarsten Keil "(%c) to layer %s\n",
745960366cfSKarsten Keil __func__, *digits, dsp->name);
746b5df5a5cSAndreas Eversberg k = *digits | DTMF_TONE_VAL;
747960366cfSKarsten Keil nskb = _alloc_mISDN_skb(PH_CONTROL_IND,
748b5df5a5cSAndreas Eversberg MISDN_ID_ANY, sizeof(int), &k,
749960366cfSKarsten Keil GFP_ATOMIC);
750960366cfSKarsten Keil if (nskb) {
751960366cfSKarsten Keil if (dsp->up) {
752960366cfSKarsten Keil if (dsp->up->send(
753960366cfSKarsten Keil dsp->up, nskb))
754960366cfSKarsten Keil dev_kfree_skb(nskb);
755960366cfSKarsten Keil } else
756960366cfSKarsten Keil dev_kfree_skb(nskb);
757960366cfSKarsten Keil }
758960366cfSKarsten Keil digits++;
759960366cfSKarsten Keil }
760960366cfSKarsten Keil }
761960366cfSKarsten Keil if (dsp->rx_disabled) {
762960366cfSKarsten Keil /* if receive is not allowed */
763960366cfSKarsten Keil break;
764960366cfSKarsten Keil }
765960366cfSKarsten Keil hh->prim = DL_DATA_IND;
766960366cfSKarsten Keil if (dsp->up)
767960366cfSKarsten Keil return dsp->up->send(dsp->up, skb);
768960366cfSKarsten Keil break;
769960366cfSKarsten Keil case (PH_CONTROL_IND):
770960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
771960366cfSKarsten Keil printk(KERN_DEBUG "%s: PH_CONTROL INDICATION "
772960366cfSKarsten Keil "received: %x (len %d) %s\n", __func__,
773960366cfSKarsten Keil hh->id, skb->len, dsp->name);
774960366cfSKarsten Keil switch (hh->id) {
775960366cfSKarsten Keil case (DTMF_HFC_COEF): /* getting coefficients */
776960366cfSKarsten Keil if (!dsp->dtmf.hardware) {
777960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
778960366cfSKarsten Keil printk(KERN_DEBUG "%s: ignoring DTMF "
779960366cfSKarsten Keil "coefficients from HFC\n",
780960366cfSKarsten Keil __func__);
781960366cfSKarsten Keil break;
782960366cfSKarsten Keil }
783960366cfSKarsten Keil digits = dsp_dtmf_goertzel_decode(dsp, skb->data,
784960366cfSKarsten Keil skb->len, 2);
785960366cfSKarsten Keil while (*digits) {
786960366cfSKarsten Keil int k;
787960366cfSKarsten Keil struct sk_buff *nskb;
788960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_DTMF)
789960366cfSKarsten Keil printk(KERN_DEBUG "%s: digit"
790960366cfSKarsten Keil "(%c) to layer %s\n",
791960366cfSKarsten Keil __func__, *digits, dsp->name);
792960366cfSKarsten Keil k = *digits | DTMF_TONE_VAL;
793960366cfSKarsten Keil nskb = _alloc_mISDN_skb(PH_CONTROL_IND,
794960366cfSKarsten Keil MISDN_ID_ANY, sizeof(int), &k,
795960366cfSKarsten Keil GFP_ATOMIC);
796960366cfSKarsten Keil if (nskb) {
797960366cfSKarsten Keil if (dsp->up) {
798960366cfSKarsten Keil if (dsp->up->send(
799960366cfSKarsten Keil dsp->up, nskb))
800960366cfSKarsten Keil dev_kfree_skb(nskb);
801960366cfSKarsten Keil } else
802960366cfSKarsten Keil dev_kfree_skb(nskb);
803960366cfSKarsten Keil }
804960366cfSKarsten Keil digits++;
805960366cfSKarsten Keil }
806960366cfSKarsten Keil break;
807960366cfSKarsten Keil case (HFC_VOL_CHANGE_TX): /* change volume */
808960366cfSKarsten Keil if (skb->len != sizeof(int)) {
809960366cfSKarsten Keil ret = -EINVAL;
810960366cfSKarsten Keil break;
811960366cfSKarsten Keil }
812960366cfSKarsten Keil spin_lock_irqsave(&dsp_lock, flags);
813960366cfSKarsten Keil dsp->tx_volume = *((int *)skb->data);
814960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
815960366cfSKarsten Keil printk(KERN_DEBUG "%s: change tx volume to "
816960366cfSKarsten Keil "%d\n", __func__, dsp->tx_volume);
817960366cfSKarsten Keil dsp_cmx_hardware(dsp->conf, dsp);
818960366cfSKarsten Keil dsp_dtmf_hardware(dsp);
819960366cfSKarsten Keil dsp_rx_off(dsp);
820960366cfSKarsten Keil spin_unlock_irqrestore(&dsp_lock, flags);
821960366cfSKarsten Keil break;
822960366cfSKarsten Keil default:
823960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
824960366cfSKarsten Keil printk(KERN_DEBUG "%s: ctrl ind %x unhandled "
825960366cfSKarsten Keil "%s\n", __func__, hh->id, dsp->name);
826960366cfSKarsten Keil ret = -EINVAL;
827960366cfSKarsten Keil }
828960366cfSKarsten Keil break;
829960366cfSKarsten Keil case (PH_ACTIVATE_IND):
830960366cfSKarsten Keil case (PH_ACTIVATE_CNF):
831960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
832960366cfSKarsten Keil printk(KERN_DEBUG "%s: b_channel is now active %s\n",
833960366cfSKarsten Keil __func__, dsp->name);
834960366cfSKarsten Keil /* bchannel now active */
835960366cfSKarsten Keil spin_lock_irqsave(&dsp_lock, flags);
836960366cfSKarsten Keil dsp->b_active = 1;
837960366cfSKarsten Keil dsp->data_pending = 0;
838960366cfSKarsten Keil dsp->rx_init = 1;
839960366cfSKarsten Keil /* rx_W and rx_R will be adjusted on first frame */
840960366cfSKarsten Keil dsp->rx_W = 0;
841960366cfSKarsten Keil dsp->rx_R = 0;
842960366cfSKarsten Keil memset(dsp->rx_buff, 0, sizeof(dsp->rx_buff));
843960366cfSKarsten Keil dsp_cmx_hardware(dsp->conf, dsp);
844960366cfSKarsten Keil dsp_dtmf_hardware(dsp);
845960366cfSKarsten Keil dsp_rx_off(dsp);
846960366cfSKarsten Keil spin_unlock_irqrestore(&dsp_lock, flags);
847960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
848960366cfSKarsten Keil printk(KERN_DEBUG "%s: done with activation, sending "
849960366cfSKarsten Keil "confirm to user space. %s\n", __func__,
850960366cfSKarsten Keil dsp->name);
851960366cfSKarsten Keil /* send activation to upper layer */
852960366cfSKarsten Keil hh->prim = DL_ESTABLISH_CNF;
853960366cfSKarsten Keil if (dsp->up)
854960366cfSKarsten Keil return dsp->up->send(dsp->up, skb);
855960366cfSKarsten Keil break;
856960366cfSKarsten Keil case (PH_DEACTIVATE_IND):
857960366cfSKarsten Keil case (PH_DEACTIVATE_CNF):
858960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
859960366cfSKarsten Keil printk(KERN_DEBUG "%s: b_channel is now inactive %s\n",
860960366cfSKarsten Keil __func__, dsp->name);
861960366cfSKarsten Keil /* bchannel now inactive */
862960366cfSKarsten Keil spin_lock_irqsave(&dsp_lock, flags);
863960366cfSKarsten Keil dsp->b_active = 0;
864960366cfSKarsten Keil dsp->data_pending = 0;
865960366cfSKarsten Keil dsp_cmx_hardware(dsp->conf, dsp);
866960366cfSKarsten Keil dsp_rx_off(dsp);
867960366cfSKarsten Keil spin_unlock_irqrestore(&dsp_lock, flags);
868960366cfSKarsten Keil hh->prim = DL_RELEASE_CNF;
869960366cfSKarsten Keil if (dsp->up)
870960366cfSKarsten Keil return dsp->up->send(dsp->up, skb);
871960366cfSKarsten Keil break;
872960366cfSKarsten Keil /* FROM UP */
873960366cfSKarsten Keil case (DL_DATA_REQ):
874960366cfSKarsten Keil case (PH_DATA_REQ):
875960366cfSKarsten Keil if (skb->len < 1) {
876960366cfSKarsten Keil ret = -EINVAL;
877960366cfSKarsten Keil break;
878960366cfSKarsten Keil }
879960366cfSKarsten Keil if (dsp->hdlc) {
880960366cfSKarsten Keil /* hdlc */
881e4cce225SPeter Schlaile if (!dsp->b_active) {
882e4cce225SPeter Schlaile ret = -EIO;
883e4cce225SPeter Schlaile break;
884e4cce225SPeter Schlaile }
885e4cce225SPeter Schlaile hh->prim = PH_DATA_REQ;
886960366cfSKarsten Keil spin_lock_irqsave(&dsp_lock, flags);
887960366cfSKarsten Keil skb_queue_tail(&dsp->sendq, skb);
888960366cfSKarsten Keil schedule_work(&dsp->workq);
889960366cfSKarsten Keil spin_unlock_irqrestore(&dsp_lock, flags);
890960366cfSKarsten Keil return 0;
891960366cfSKarsten Keil }
892960366cfSKarsten Keil /* send data to tx-buffer (if no tone is played) */
893960366cfSKarsten Keil if (!dsp->tone.tone) {
894960366cfSKarsten Keil spin_lock_irqsave(&dsp_lock, flags);
895960366cfSKarsten Keil dsp_cmx_transmit(dsp, skb);
896960366cfSKarsten Keil spin_unlock_irqrestore(&dsp_lock, flags);
897960366cfSKarsten Keil }
898960366cfSKarsten Keil break;
899960366cfSKarsten Keil case (PH_CONTROL_REQ):
900960366cfSKarsten Keil spin_lock_irqsave(&dsp_lock, flags);
901960366cfSKarsten Keil ret = dsp_control_req(dsp, hh, skb);
902960366cfSKarsten Keil spin_unlock_irqrestore(&dsp_lock, flags);
903960366cfSKarsten Keil break;
904960366cfSKarsten Keil case (DL_ESTABLISH_REQ):
905960366cfSKarsten Keil case (PH_ACTIVATE_REQ):
906960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
907960366cfSKarsten Keil printk(KERN_DEBUG "%s: activating b_channel %s\n",
908960366cfSKarsten Keil __func__, dsp->name);
909960366cfSKarsten Keil if (dsp->dtmf.hardware || dsp->dtmf.software)
910960366cfSKarsten Keil dsp_dtmf_goertzel_init(dsp);
911960366cfSKarsten Keil get_features(ch);
9128dd2f36fSAndreas Eversberg /* enable fill_empty feature */
9138dd2f36fSAndreas Eversberg if (dsp->features_fill_empty)
9148dd2f36fSAndreas Eversberg dsp_fill_empty(dsp);
915960366cfSKarsten Keil /* send ph_activate */
916960366cfSKarsten Keil hh->prim = PH_ACTIVATE_REQ;
917960366cfSKarsten Keil if (ch->peer)
918960366cfSKarsten Keil return ch->recv(ch->peer, skb);
919960366cfSKarsten Keil break;
920960366cfSKarsten Keil case (DL_RELEASE_REQ):
921960366cfSKarsten Keil case (PH_DEACTIVATE_REQ):
922960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
923960366cfSKarsten Keil printk(KERN_DEBUG "%s: releasing b_channel %s\n",
924960366cfSKarsten Keil __func__, dsp->name);
925960366cfSKarsten Keil spin_lock_irqsave(&dsp_lock, flags);
926960366cfSKarsten Keil dsp->tone.tone = 0;
927960366cfSKarsten Keil dsp->tone.hardware = 0;
928960366cfSKarsten Keil dsp->tone.software = 0;
929960366cfSKarsten Keil if (timer_pending(&dsp->tone.tl))
930960366cfSKarsten Keil del_timer(&dsp->tone.tl);
931960366cfSKarsten Keil if (dsp->conf)
932960366cfSKarsten Keil dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be
933960366cfSKarsten Keil called here */
934960366cfSKarsten Keil skb_queue_purge(&dsp->sendq);
935960366cfSKarsten Keil spin_unlock_irqrestore(&dsp_lock, flags);
936960366cfSKarsten Keil hh->prim = PH_DEACTIVATE_REQ;
937960366cfSKarsten Keil if (ch->peer)
938960366cfSKarsten Keil return ch->recv(ch->peer, skb);
939960366cfSKarsten Keil break;
940960366cfSKarsten Keil default:
941960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
942960366cfSKarsten Keil printk(KERN_DEBUG "%s: msg %x unhandled %s\n",
943960366cfSKarsten Keil __func__, hh->prim, dsp->name);
944960366cfSKarsten Keil ret = -EINVAL;
945960366cfSKarsten Keil }
946960366cfSKarsten Keil if (!ret)
947960366cfSKarsten Keil dev_kfree_skb(skb);
948960366cfSKarsten Keil return ret;
949960366cfSKarsten Keil }
950960366cfSKarsten Keil
951960366cfSKarsten Keil static int
dsp_ctrl(struct mISDNchannel * ch,u_int cmd,void * arg)952960366cfSKarsten Keil dsp_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
953960366cfSKarsten Keil {
954960366cfSKarsten Keil struct dsp *dsp = container_of(ch, struct dsp, ch);
955960366cfSKarsten Keil u_long flags;
956960366cfSKarsten Keil
957960366cfSKarsten Keil if (debug & DEBUG_DSP_CTRL)
958960366cfSKarsten Keil printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd);
959960366cfSKarsten Keil
960960366cfSKarsten Keil switch (cmd) {
961960366cfSKarsten Keil case OPEN_CHANNEL:
962960366cfSKarsten Keil break;
963960366cfSKarsten Keil case CLOSE_CHANNEL:
964960366cfSKarsten Keil if (dsp->ch.peer)
965960366cfSKarsten Keil dsp->ch.peer->ctrl(dsp->ch.peer, CLOSE_CHANNEL, NULL);
966960366cfSKarsten Keil
967960366cfSKarsten Keil /* wait until workqueue has finished,
968960366cfSKarsten Keil * must lock here, or we may hit send-process currently
969960366cfSKarsten Keil * queueing. */
970960366cfSKarsten Keil spin_lock_irqsave(&dsp_lock, flags);
971960366cfSKarsten Keil dsp->b_active = 0;
972960366cfSKarsten Keil spin_unlock_irqrestore(&dsp_lock, flags);
973960366cfSKarsten Keil /* MUST not be locked, because it waits until queue is done. */
974960366cfSKarsten Keil cancel_work_sync(&dsp->workq);
975960366cfSKarsten Keil spin_lock_irqsave(&dsp_lock, flags);
976960366cfSKarsten Keil if (timer_pending(&dsp->tone.tl))
977960366cfSKarsten Keil del_timer(&dsp->tone.tl);
978960366cfSKarsten Keil skb_queue_purge(&dsp->sendq);
979960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CTRL)
980960366cfSKarsten Keil printk(KERN_DEBUG "%s: releasing member %s\n",
981960366cfSKarsten Keil __func__, dsp->name);
982960366cfSKarsten Keil dsp->b_active = 0;
983960366cfSKarsten Keil dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be called
984960366cfSKarsten Keil here */
985960366cfSKarsten Keil dsp_pipeline_destroy(&dsp->pipeline);
986960366cfSKarsten Keil
987960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CTRL)
988960366cfSKarsten Keil printk(KERN_DEBUG "%s: remove & destroy object %s\n",
989960366cfSKarsten Keil __func__, dsp->name);
990960366cfSKarsten Keil list_del(&dsp->list);
991960366cfSKarsten Keil spin_unlock_irqrestore(&dsp_lock, flags);
992960366cfSKarsten Keil
993960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CTRL)
994960366cfSKarsten Keil printk(KERN_DEBUG "%s: dsp instance released\n",
995960366cfSKarsten Keil __func__);
996960366cfSKarsten Keil vfree(dsp);
997960366cfSKarsten Keil module_put(THIS_MODULE);
998960366cfSKarsten Keil break;
999960366cfSKarsten Keil }
1000762c1adbSYang Li return 0;
1001960366cfSKarsten Keil }
1002960366cfSKarsten Keil
1003960366cfSKarsten Keil static void
dsp_send_bh(struct work_struct * work)1004960366cfSKarsten Keil dsp_send_bh(struct work_struct *work)
1005960366cfSKarsten Keil {
1006960366cfSKarsten Keil struct dsp *dsp = container_of(work, struct dsp, workq);
1007960366cfSKarsten Keil struct sk_buff *skb;
1008960366cfSKarsten Keil struct mISDNhead *hh;
1009960366cfSKarsten Keil
1010960366cfSKarsten Keil if (dsp->hdlc && dsp->data_pending)
1011960366cfSKarsten Keil return; /* wait until data has been acknowledged */
1012960366cfSKarsten Keil
1013960366cfSKarsten Keil /* send queued data */
1014960366cfSKarsten Keil while ((skb = skb_dequeue(&dsp->sendq))) {
1015960366cfSKarsten Keil /* in locked date, we must have still data in queue */
1016960366cfSKarsten Keil if (dsp->data_pending) {
1017960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CORE)
1018960366cfSKarsten Keil printk(KERN_DEBUG "%s: fifo full %s, this is "
1019960366cfSKarsten Keil "no bug!\n", __func__, dsp->name);
1020960366cfSKarsten Keil /* flush transparent data, if not acked */
1021960366cfSKarsten Keil dev_kfree_skb(skb);
1022960366cfSKarsten Keil continue;
1023960366cfSKarsten Keil }
1024960366cfSKarsten Keil hh = mISDN_HEAD_P(skb);
1025960366cfSKarsten Keil if (hh->prim == DL_DATA_REQ) {
1026960366cfSKarsten Keil /* send packet up */
1027960366cfSKarsten Keil if (dsp->up) {
1028960366cfSKarsten Keil if (dsp->up->send(dsp->up, skb))
1029960366cfSKarsten Keil dev_kfree_skb(skb);
1030960366cfSKarsten Keil } else
1031960366cfSKarsten Keil dev_kfree_skb(skb);
1032960366cfSKarsten Keil } else {
1033960366cfSKarsten Keil /* send packet down */
1034960366cfSKarsten Keil if (dsp->ch.peer) {
1035960366cfSKarsten Keil dsp->data_pending = 1;
1036960366cfSKarsten Keil if (dsp->ch.recv(dsp->ch.peer, skb)) {
1037960366cfSKarsten Keil dev_kfree_skb(skb);
1038960366cfSKarsten Keil dsp->data_pending = 0;
1039960366cfSKarsten Keil }
1040960366cfSKarsten Keil } else
1041960366cfSKarsten Keil dev_kfree_skb(skb);
1042960366cfSKarsten Keil }
1043960366cfSKarsten Keil }
1044960366cfSKarsten Keil }
1045960366cfSKarsten Keil
1046960366cfSKarsten Keil static int
dspcreate(struct channel_req * crq)1047960366cfSKarsten Keil dspcreate(struct channel_req *crq)
1048960366cfSKarsten Keil {
1049960366cfSKarsten Keil struct dsp *ndsp;
1050960366cfSKarsten Keil u_long flags;
1051960366cfSKarsten Keil
1052960366cfSKarsten Keil if (crq->protocol != ISDN_P_B_L2DSP
1053960366cfSKarsten Keil && crq->protocol != ISDN_P_B_L2DSPHDLC)
1054960366cfSKarsten Keil return -EPROTONOSUPPORT;
10551ac4594dSJoe Perches ndsp = vzalloc(sizeof(struct dsp));
1056960366cfSKarsten Keil if (!ndsp) {
1057960366cfSKarsten Keil printk(KERN_ERR "%s: vmalloc struct dsp failed\n", __func__);
1058960366cfSKarsten Keil return -ENOMEM;
1059960366cfSKarsten Keil }
1060960366cfSKarsten Keil if (dsp_debug & DEBUG_DSP_CTRL)
1061960366cfSKarsten Keil printk(KERN_DEBUG "%s: creating new dsp instance\n", __func__);
1062960366cfSKarsten Keil
1063960366cfSKarsten Keil /* default enabled */
1064960366cfSKarsten Keil INIT_WORK(&ndsp->workq, (void *)dsp_send_bh);
1065960366cfSKarsten Keil skb_queue_head_init(&ndsp->sendq);
1066960366cfSKarsten Keil ndsp->ch.send = dsp_function;
1067960366cfSKarsten Keil ndsp->ch.ctrl = dsp_ctrl;
1068960366cfSKarsten Keil ndsp->up = crq->ch;
1069960366cfSKarsten Keil crq->ch = &ndsp->ch;
1070960366cfSKarsten Keil if (crq->protocol == ISDN_P_B_L2DSP) {
1071960366cfSKarsten Keil crq->protocol = ISDN_P_B_RAW;
1072960366cfSKarsten Keil ndsp->hdlc = 0;
1073960366cfSKarsten Keil } else {
1074960366cfSKarsten Keil crq->protocol = ISDN_P_B_HDLC;
1075960366cfSKarsten Keil ndsp->hdlc = 1;
1076960366cfSKarsten Keil }
1077960366cfSKarsten Keil if (!try_module_get(THIS_MODULE))
1078960366cfSKarsten Keil printk(KERN_WARNING "%s:cannot get module\n",
1079960366cfSKarsten Keil __func__);
1080960366cfSKarsten Keil
1081960366cfSKarsten Keil sprintf(ndsp->name, "DSP_C%x(0x%p)",
1082960366cfSKarsten Keil ndsp->up->st->dev->id + 1, ndsp);
1083960366cfSKarsten Keil /* set frame size to start */
1084960366cfSKarsten Keil ndsp->features.hfc_id = -1; /* current PCM id */
1085960366cfSKarsten Keil ndsp->features.pcm_id = -1; /* current PCM id */
1086960366cfSKarsten Keil ndsp->pcm_slot_rx = -1; /* current CPM slot */
1087960366cfSKarsten Keil ndsp->pcm_slot_tx = -1;
1088960366cfSKarsten Keil ndsp->pcm_bank_rx = -1;
1089960366cfSKarsten Keil ndsp->pcm_bank_tx = -1;
1090960366cfSKarsten Keil ndsp->hfc_conf = -1; /* current conference number */
1091960366cfSKarsten Keil /* set tone timer */
1092e313ac12SKees Cook timer_setup(&ndsp->tone.tl, dsp_tone_timeout, 0);
1093960366cfSKarsten Keil
1094960366cfSKarsten Keil if (dtmfthreshold < 20 || dtmfthreshold > 500)
1095960366cfSKarsten Keil dtmfthreshold = 200;
1096960366cfSKarsten Keil ndsp->dtmf.treshold = dtmfthreshold * 10000;
1097960366cfSKarsten Keil
1098960366cfSKarsten Keil /* init pipeline append to list */
1099960366cfSKarsten Keil spin_lock_irqsave(&dsp_lock, flags);
1100960366cfSKarsten Keil dsp_pipeline_init(&ndsp->pipeline);
1101960366cfSKarsten Keil list_add_tail(&ndsp->list, &dsp_ilist);
1102960366cfSKarsten Keil spin_unlock_irqrestore(&dsp_lock, flags);
1103960366cfSKarsten Keil
1104960366cfSKarsten Keil return 0;
1105960366cfSKarsten Keil }
1106960366cfSKarsten Keil
1107960366cfSKarsten Keil
1108960366cfSKarsten Keil static struct Bprotocol DSP = {
1109960366cfSKarsten Keil .Bprotocols = (1 << (ISDN_P_B_L2DSP & ISDN_P_B_MASK))
1110960366cfSKarsten Keil | (1 << (ISDN_P_B_L2DSPHDLC & ISDN_P_B_MASK)),
1111960366cfSKarsten Keil .name = "dsp",
1112960366cfSKarsten Keil .create = dspcreate
1113960366cfSKarsten Keil };
1114960366cfSKarsten Keil
dsp_init(void)11153d956d1dSPeter Huewe static int __init dsp_init(void)
1116960366cfSKarsten Keil {
1117960366cfSKarsten Keil int err;
1118960366cfSKarsten Keil int tics;
1119960366cfSKarsten Keil
11204807f643SMasanari Iida printk(KERN_INFO "DSP module %s\n", mISDN_dsp_revision);
1121960366cfSKarsten Keil
1122960366cfSKarsten Keil dsp_options = options;
1123960366cfSKarsten Keil dsp_debug = debug;
1124960366cfSKarsten Keil
1125960366cfSKarsten Keil /* set packet size */
1126960366cfSKarsten Keil dsp_poll = poll;
1127960366cfSKarsten Keil if (dsp_poll) {
1128960366cfSKarsten Keil if (dsp_poll > MAX_POLL) {
1129960366cfSKarsten Keil printk(KERN_ERR "%s: Wrong poll value (%d), use %d "
1130960366cfSKarsten Keil "maximum.\n", __func__, poll, MAX_POLL);
1131960366cfSKarsten Keil err = -EINVAL;
1132960366cfSKarsten Keil return err;
1133960366cfSKarsten Keil }
1134960366cfSKarsten Keil if (dsp_poll < 8) {
1135960366cfSKarsten Keil printk(KERN_ERR "%s: Wrong poll value (%d), use 8 "
1136960366cfSKarsten Keil "minimum.\n", __func__, dsp_poll);
1137960366cfSKarsten Keil err = -EINVAL;
1138960366cfSKarsten Keil return err;
1139960366cfSKarsten Keil }
1140960366cfSKarsten Keil dsp_tics = poll * HZ / 8000;
1141960366cfSKarsten Keil if (dsp_tics * 8000 != poll * HZ) {
1142960366cfSKarsten Keil printk(KERN_INFO "mISDN_dsp: Cannot clock every %d "
1143960366cfSKarsten Keil "samples (0,125 ms). It is not a multiple of "
1144960366cfSKarsten Keil "%d HZ.\n", poll, HZ);
1145960366cfSKarsten Keil err = -EINVAL;
1146960366cfSKarsten Keil return err;
1147960366cfSKarsten Keil }
1148960366cfSKarsten Keil } else {
1149960366cfSKarsten Keil poll = 8;
1150960366cfSKarsten Keil while (poll <= MAX_POLL) {
1151400fd978SAndreas Eversberg tics = (poll * HZ) / 8000;
1152960366cfSKarsten Keil if (tics * 8000 == poll * HZ) {
1153960366cfSKarsten Keil dsp_tics = tics;
1154960366cfSKarsten Keil dsp_poll = poll;
1155960366cfSKarsten Keil if (poll >= 64)
1156960366cfSKarsten Keil break;
1157960366cfSKarsten Keil }
1158960366cfSKarsten Keil poll++;
1159960366cfSKarsten Keil }
1160960366cfSKarsten Keil }
1161960366cfSKarsten Keil if (dsp_poll == 0) {
1162960366cfSKarsten Keil printk(KERN_INFO "mISDN_dsp: There is no multiple of kernel "
1163960366cfSKarsten Keil "clock that equals exactly the duration of 8-256 "
1164960366cfSKarsten Keil "samples. (Choose kernel clock speed like 100, 250, "
1165960366cfSKarsten Keil "300, 1000)\n");
1166960366cfSKarsten Keil err = -EINVAL;
1167960366cfSKarsten Keil return err;
1168960366cfSKarsten Keil }
1169960366cfSKarsten Keil printk(KERN_INFO "mISDN_dsp: DSP clocks every %d samples. This equals "
1170960366cfSKarsten Keil "%d jiffies.\n", dsp_poll, dsp_tics);
1171960366cfSKarsten Keil
1172960366cfSKarsten Keil /* init conversion tables */
1173960366cfSKarsten Keil dsp_audio_generate_law_tables();
1174960366cfSKarsten Keil dsp_silence = (dsp_options & DSP_OPT_ULAW) ? 0xff : 0x2a;
1175eac74af9SKarsten Keil dsp_audio_law_to_s32 = (dsp_options & DSP_OPT_ULAW) ?
1176eac74af9SKarsten Keil dsp_audio_ulaw_to_s32 : dsp_audio_alaw_to_s32;
1177960366cfSKarsten Keil dsp_audio_generate_s2law_table();
1178960366cfSKarsten Keil dsp_audio_generate_seven();
1179960366cfSKarsten Keil dsp_audio_generate_mix_table();
1180960366cfSKarsten Keil if (dsp_options & DSP_OPT_ULAW)
1181960366cfSKarsten Keil dsp_audio_generate_ulaw_samples();
1182960366cfSKarsten Keil dsp_audio_generate_volume_changes();
1183960366cfSKarsten Keil
1184960366cfSKarsten Keil err = dsp_pipeline_module_init();
1185960366cfSKarsten Keil if (err) {
1186960366cfSKarsten Keil printk(KERN_ERR "mISDN_dsp: Can't initialize pipeline, "
1187960366cfSKarsten Keil "error(%d)\n", err);
1188960366cfSKarsten Keil return err;
1189960366cfSKarsten Keil }
1190960366cfSKarsten Keil
1191960366cfSKarsten Keil err = mISDN_register_Bprotocol(&DSP);
1192960366cfSKarsten Keil if (err) {
1193960366cfSKarsten Keil printk(KERN_ERR "Can't register %s error(%d)\n", DSP.name, err);
1194960366cfSKarsten Keil return err;
1195960366cfSKarsten Keil }
1196960366cfSKarsten Keil
1197960366cfSKarsten Keil /* set sample timer */
1198*1696ec86SNathan Chancellor timer_setup(&dsp_spl_tl, dsp_cmx_send, 0);
1199960366cfSKarsten Keil dsp_spl_tl.expires = jiffies + dsp_tics;
1200960366cfSKarsten Keil dsp_spl_jiffies = dsp_spl_tl.expires;
1201960366cfSKarsten Keil add_timer(&dsp_spl_tl);
1202960366cfSKarsten Keil
1203960366cfSKarsten Keil return 0;
1204960366cfSKarsten Keil }
1205960366cfSKarsten Keil
1206960366cfSKarsten Keil
dsp_cleanup(void)12073d956d1dSPeter Huewe static void __exit dsp_cleanup(void)
1208960366cfSKarsten Keil {
1209960366cfSKarsten Keil mISDN_unregister_Bprotocol(&DSP);
1210960366cfSKarsten Keil
12114a0ae7b0SKonstantin Khlebnikov del_timer_sync(&dsp_spl_tl);
1212960366cfSKarsten Keil
1213960366cfSKarsten Keil if (!list_empty(&dsp_ilist)) {
1214960366cfSKarsten Keil printk(KERN_ERR "mISDN_dsp: Audio DSP object inst list not "
1215960366cfSKarsten Keil "empty.\n");
1216960366cfSKarsten Keil }
1217960366cfSKarsten Keil if (!list_empty(&conf_ilist)) {
1218960366cfSKarsten Keil printk(KERN_ERR "mISDN_dsp: Conference list not empty. Not "
1219960366cfSKarsten Keil "all memory freed.\n");
1220960366cfSKarsten Keil }
1221960366cfSKarsten Keil
1222960366cfSKarsten Keil dsp_pipeline_module_exit();
1223960366cfSKarsten Keil }
1224960366cfSKarsten Keil
1225960366cfSKarsten Keil module_init(dsp_init);
1226960366cfSKarsten Keil module_exit(dsp_cleanup);
1227