Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/isdn/mISDN/dsp_cmx.c
15111 views
1
/*
2
* Audio crossconnecting/conferrencing (hardware level).
3
*
4
* Copyright 2002 by Andreas Eversberg ([email protected])
5
*
6
* This software may be used and distributed according to the terms
7
* of the GNU General Public License, incorporated herein by reference.
8
*
9
*/
10
11
/*
12
* The process of adding and removing parties to/from a conference:
13
*
14
* There is a chain of struct dsp_conf which has one or more members in a chain
15
* of struct dsp_conf_member.
16
*
17
* After a party is added, the conference is checked for hardware capability.
18
* Also if a party is removed, the conference is checked again.
19
*
20
* There are 3 different solutions: -1 = software, 0 = hardware-crossconnect
21
* 1-n = hardware-conference. The n will give the conference number.
22
*
23
* Depending on the change after removal or insertion of a party, hardware
24
* commands are given.
25
*
26
* The current solution is stored within the struct dsp_conf entry.
27
*/
28
29
/*
30
* HOW THE CMX WORKS:
31
*
32
* There are 3 types of interaction: One member is alone, in this case only
33
* data flow from upper to lower layer is done.
34
* Two members will also exchange their data so they are crossconnected.
35
* Three or more members will be added in a conference and will hear each
36
* other but will not receive their own speech (echo) if not enabled.
37
*
38
* Features of CMX are:
39
* - Crossconnecting or even conference, if more than two members are together.
40
* - Force mixing of transmit data with other crossconnect/conference members.
41
* - Echo generation to benchmark the delay of audio processing.
42
* - Use hardware to minimize cpu load, disable FIFO load and minimize delay.
43
* - Dejittering and clock generation.
44
*
45
* There are 2 buffers:
46
*
47
*
48
* RX-Buffer
49
* R W
50
* | |
51
* ----------------+-------------+-------------------
52
*
53
* The rx-buffer is a ring buffer used to store the received data for each
54
* individual member. This is only the case if data needs to be dejittered
55
* or in case of a conference where different clocks require reclocking.
56
* The transmit-clock (R) will read the buffer.
57
* If the clock overruns the write-pointer, we will have a buffer underrun.
58
* If the write pointer always has a certain distance from the transmit-
59
* clock, we will have a delay. The delay will dynamically be increased and
60
* reduced.
61
*
62
*
63
* TX-Buffer
64
* R W
65
* | |
66
* -----------------+--------+-----------------------
67
*
68
* The tx-buffer is a ring buffer to queue the transmit data from user space
69
* until it will be mixed or sent. There are two pointers, R and W. If the write
70
* pointer W would reach or overrun R, the buffer would overrun. In this case
71
* (some) data is dropped so that it will not overrun.
72
* Additionally a dynamic dejittering can be enabled. this allows data from
73
* user space that have jitter and different clock source.
74
*
75
*
76
* Clock:
77
*
78
* A Clock is not required, if the data source has exactly one clock. In this
79
* case the data source is forwarded to the destination.
80
*
81
* A Clock is required, because the data source
82
* - has multiple clocks.
83
* - has no usable clock due to jitter or packet loss (VoIP).
84
* In this case the system's clock is used. The clock resolution depends on
85
* the jiffie resolution.
86
*
87
* If a member joins a conference:
88
*
89
* - If a member joins, its rx_buff is set to silence and change read pointer
90
* to transmit clock.
91
*
92
* The procedure of received data from card is explained in cmx_receive.
93
* The procedure of received data from user space is explained in cmx_transmit.
94
* The procedure of transmit data to card is cmx_send.
95
*
96
*
97
* Interaction with other features:
98
*
99
* DTMF:
100
* DTMF decoding is done before the data is crossconnected.
101
*
102
* Volume change:
103
* Changing rx-volume is done before the data is crossconnected. The tx-volume
104
* must be changed whenever data is transmitted to the card by the cmx.
105
*
106
* Tones:
107
* If a tone is enabled, it will be processed whenever data is transmitted to
108
* the card. It will replace the tx-data from the user space.
109
* If tones are generated by hardware, this conference member is removed for
110
* this time.
111
*
112
* Disable rx-data:
113
* If cmx is realized in hardware, rx data will be disabled if requested by
114
* the upper layer. If dtmf decoding is done by software and enabled, rx data
115
* will not be disabled but blocked to the upper layer.
116
*
117
* HFC conference engine:
118
* If it is possible to realize all features using hardware, hardware will be
119
* used if not forbidden by control command. Disabling rx-data provides
120
* absolutely traffic free audio processing. (except for the quick 1-frame
121
* upload of a tone loop, only once for a new tone)
122
*
123
*/
124
125
/* delay.h is required for hw_lock.h */
126
127
#include <linux/slab.h>
128
#include <linux/delay.h>
129
#include <linux/mISDNif.h>
130
#include <linux/mISDNdsp.h>
131
#include "core.h"
132
#include "dsp.h"
133
/*
134
* debugging of multi party conference,
135
* by using conference even with two members
136
*/
137
138
/* #define CMX_CONF_DEBUG */
139
140
/*#define CMX_DEBUG * massive read/write pointer output */
141
/*#define CMX_DELAY_DEBUG * gives rx-buffer delay overview */
142
/*#define CMX_TX_DEBUG * massive read/write on tx-buffer with content */
143
144
static inline int
145
count_list_member(struct list_head *head)
146
{
147
int cnt = 0;
148
struct list_head *m;
149
150
list_for_each(m, head)
151
cnt++;
152
return cnt;
153
}
154
155
/*
156
* debug cmx memory structure
157
*/
158
void
159
dsp_cmx_debug(struct dsp *dsp)
160
{
161
struct dsp_conf *conf;
162
struct dsp_conf_member *member;
163
struct dsp *odsp;
164
165
printk(KERN_DEBUG "-----Current DSP\n");
166
list_for_each_entry(odsp, &dsp_ilist, list) {
167
printk(KERN_DEBUG "* %s hardecho=%d softecho=%d txmix=%d",
168
odsp->name, odsp->echo.hardware, odsp->echo.software,
169
odsp->tx_mix);
170
if (odsp->conf)
171
printk(" (Conf %d)", odsp->conf->id);
172
if (dsp == odsp)
173
printk(" *this*");
174
printk("\n");
175
}
176
printk(KERN_DEBUG "-----Current Conf:\n");
177
list_for_each_entry(conf, &conf_ilist, list) {
178
printk(KERN_DEBUG "* Conf %d (%p)\n", conf->id, conf);
179
list_for_each_entry(member, &conf->mlist, list) {
180
printk(KERN_DEBUG
181
" - member = %s (slot_tx %d, bank_tx %d, "
182
"slot_rx %d, bank_rx %d hfc_conf %d "
183
"tx_data %d rx_is_off %d)%s\n",
184
member->dsp->name, member->dsp->pcm_slot_tx,
185
member->dsp->pcm_bank_tx, member->dsp->pcm_slot_rx,
186
member->dsp->pcm_bank_rx, member->dsp->hfc_conf,
187
member->dsp->tx_data, member->dsp->rx_is_off,
188
(member->dsp == dsp) ? " *this*" : "");
189
}
190
}
191
printk(KERN_DEBUG "-----end\n");
192
}
193
194
/*
195
* search conference
196
*/
197
static struct dsp_conf *
198
dsp_cmx_search_conf(u32 id)
199
{
200
struct dsp_conf *conf;
201
202
if (!id) {
203
printk(KERN_WARNING "%s: conference ID is 0.\n", __func__);
204
return NULL;
205
}
206
207
/* search conference */
208
list_for_each_entry(conf, &conf_ilist, list)
209
if (conf->id == id)
210
return conf;
211
212
return NULL;
213
}
214
215
216
/*
217
* add member to conference
218
*/
219
static int
220
dsp_cmx_add_conf_member(struct dsp *dsp, struct dsp_conf *conf)
221
{
222
struct dsp_conf_member *member;
223
224
if (!conf || !dsp) {
225
printk(KERN_WARNING "%s: conf or dsp is 0.\n", __func__);
226
return -EINVAL;
227
}
228
if (dsp->member) {
229
printk(KERN_WARNING "%s: dsp is already member in a conf.\n",
230
__func__);
231
return -EINVAL;
232
}
233
234
if (dsp->conf) {
235
printk(KERN_WARNING "%s: dsp is already in a conf.\n",
236
__func__);
237
return -EINVAL;
238
}
239
240
member = kzalloc(sizeof(struct dsp_conf_member), GFP_ATOMIC);
241
if (!member) {
242
printk(KERN_ERR "kzalloc struct dsp_conf_member failed\n");
243
return -ENOMEM;
244
}
245
member->dsp = dsp;
246
/* clear rx buffer */
247
memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
248
dsp->rx_init = 1; /* rx_W and rx_R will be adjusted on first frame */
249
dsp->rx_W = 0;
250
dsp->rx_R = 0;
251
252
list_add_tail(&member->list, &conf->mlist);
253
254
dsp->conf = conf;
255
dsp->member = member;
256
257
return 0;
258
}
259
260
261
/*
262
* del member from conference
263
*/
264
int
265
dsp_cmx_del_conf_member(struct dsp *dsp)
266
{
267
struct dsp_conf_member *member;
268
269
if (!dsp) {
270
printk(KERN_WARNING "%s: dsp is 0.\n",
271
__func__);
272
return -EINVAL;
273
}
274
275
if (!dsp->conf) {
276
printk(KERN_WARNING "%s: dsp is not in a conf.\n",
277
__func__);
278
return -EINVAL;
279
}
280
281
if (list_empty(&dsp->conf->mlist)) {
282
printk(KERN_WARNING "%s: dsp has linked an empty conf.\n",
283
__func__);
284
return -EINVAL;
285
}
286
287
/* find us in conf */
288
list_for_each_entry(member, &dsp->conf->mlist, list) {
289
if (member->dsp == dsp) {
290
list_del(&member->list);
291
dsp->conf = NULL;
292
dsp->member = NULL;
293
kfree(member);
294
return 0;
295
}
296
}
297
printk(KERN_WARNING
298
"%s: dsp is not present in its own conf_meber list.\n",
299
__func__);
300
301
return -EINVAL;
302
}
303
304
305
/*
306
* new conference
307
*/
308
static struct dsp_conf
309
*dsp_cmx_new_conf(u32 id)
310
{
311
struct dsp_conf *conf;
312
313
if (!id) {
314
printk(KERN_WARNING "%s: id is 0.\n",
315
__func__);
316
return NULL;
317
}
318
319
conf = kzalloc(sizeof(struct dsp_conf), GFP_ATOMIC);
320
if (!conf) {
321
printk(KERN_ERR "kzalloc struct dsp_conf failed\n");
322
return NULL;
323
}
324
INIT_LIST_HEAD(&conf->mlist);
325
conf->id = id;
326
327
list_add_tail(&conf->list, &conf_ilist);
328
329
return conf;
330
}
331
332
333
/*
334
* del conference
335
*/
336
int
337
dsp_cmx_del_conf(struct dsp_conf *conf)
338
{
339
if (!conf) {
340
printk(KERN_WARNING "%s: conf is null.\n",
341
__func__);
342
return -EINVAL;
343
}
344
345
if (!list_empty(&conf->mlist)) {
346
printk(KERN_WARNING "%s: conf not empty.\n",
347
__func__);
348
return -EINVAL;
349
}
350
list_del(&conf->list);
351
kfree(conf);
352
353
return 0;
354
}
355
356
357
/*
358
* send HW message to hfc card
359
*/
360
static void
361
dsp_cmx_hw_message(struct dsp *dsp, u32 message, u32 param1, u32 param2,
362
u32 param3, u32 param4)
363
{
364
struct mISDN_ctrl_req cq;
365
366
memset(&cq, 0, sizeof(cq));
367
cq.op = message;
368
cq.p1 = param1 | (param2 << 8);
369
cq.p2 = param3 | (param4 << 8);
370
if (dsp->ch.peer)
371
dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq);
372
}
373
374
375
/*
376
* do hardware update and set the software/hardware flag
377
*
378
* either a conference or a dsp instance can be given
379
* if only dsp instance is given, the instance is not associated with a conf
380
* and therefore removed. if a conference is given, the dsp is expected to
381
* be member of that conference.
382
*/
383
void
384
dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp)
385
{
386
struct dsp_conf_member *member, *nextm;
387
struct dsp *finddsp;
388
int memb = 0, i, ii, i1, i2;
389
int freeunits[8];
390
u_char freeslots[256];
391
int same_hfc = -1, same_pcm = -1, current_conf = -1,
392
all_conf = 1, tx_data = 0;
393
394
/* dsp gets updated (no conf) */
395
if (!conf) {
396
if (!dsp)
397
return;
398
if (dsp_debug & DEBUG_DSP_CMX)
399
printk(KERN_DEBUG "%s checking dsp %s\n",
400
__func__, dsp->name);
401
one_member:
402
/* remove HFC conference if enabled */
403
if (dsp->hfc_conf >= 0) {
404
if (dsp_debug & DEBUG_DSP_CMX)
405
printk(KERN_DEBUG
406
"%s removing %s from HFC conf %d "
407
"because dsp is split\n", __func__,
408
dsp->name, dsp->hfc_conf);
409
dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_CONF_SPLIT,
410
0, 0, 0, 0);
411
dsp->hfc_conf = -1;
412
}
413
/* process hw echo */
414
if (dsp->features.pcm_banks < 1)
415
return;
416
if (!dsp->echo.software && !dsp->echo.hardware) {
417
/* NO ECHO: remove PCM slot if assigned */
418
if (dsp->pcm_slot_tx >= 0 || dsp->pcm_slot_rx >= 0) {
419
if (dsp_debug & DEBUG_DSP_CMX)
420
printk(KERN_DEBUG "%s removing %s from"
421
" PCM slot %d (TX) %d (RX) because"
422
" dsp is split (no echo)\n",
423
__func__, dsp->name,
424
dsp->pcm_slot_tx, dsp->pcm_slot_rx);
425
dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_DISC,
426
0, 0, 0, 0);
427
dsp->pcm_slot_tx = -1;
428
dsp->pcm_bank_tx = -1;
429
dsp->pcm_slot_rx = -1;
430
dsp->pcm_bank_rx = -1;
431
}
432
return;
433
}
434
/* echo is enabled, find out if we use soft or hardware */
435
dsp->echo.software = dsp->tx_data;
436
dsp->echo.hardware = 0;
437
/* ECHO: already echo */
438
if (dsp->pcm_slot_tx >= 0 && dsp->pcm_slot_rx < 0 &&
439
dsp->pcm_bank_tx == 2 && dsp->pcm_bank_rx == 2) {
440
dsp->echo.hardware = 1;
441
return;
442
}
443
/* ECHO: if slot already assigned */
444
if (dsp->pcm_slot_tx >= 0) {
445
dsp->pcm_slot_rx = dsp->pcm_slot_tx;
446
dsp->pcm_bank_tx = 2; /* 2 means loop */
447
dsp->pcm_bank_rx = 2;
448
if (dsp_debug & DEBUG_DSP_CMX)
449
printk(KERN_DEBUG
450
"%s refresh %s for echo using slot %d\n",
451
__func__, dsp->name,
452
dsp->pcm_slot_tx);
453
dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN,
454
dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2);
455
dsp->echo.hardware = 1;
456
return;
457
}
458
/* ECHO: find slot */
459
dsp->pcm_slot_tx = -1;
460
dsp->pcm_slot_rx = -1;
461
memset(freeslots, 1, sizeof(freeslots));
462
list_for_each_entry(finddsp, &dsp_ilist, list) {
463
if (finddsp->features.pcm_id == dsp->features.pcm_id) {
464
if (finddsp->pcm_slot_rx >= 0 &&
465
finddsp->pcm_slot_rx < sizeof(freeslots))
466
freeslots[finddsp->pcm_slot_rx] = 0;
467
if (finddsp->pcm_slot_tx >= 0 &&
468
finddsp->pcm_slot_tx < sizeof(freeslots))
469
freeslots[finddsp->pcm_slot_tx] = 0;
470
}
471
}
472
i = 0;
473
ii = dsp->features.pcm_slots;
474
while (i < ii) {
475
if (freeslots[i])
476
break;
477
i++;
478
}
479
if (i == ii) {
480
if (dsp_debug & DEBUG_DSP_CMX)
481
printk(KERN_DEBUG
482
"%s no slot available for echo\n",
483
__func__);
484
/* no more slots available */
485
dsp->echo.software = 1;
486
return;
487
}
488
/* assign free slot */
489
dsp->pcm_slot_tx = i;
490
dsp->pcm_slot_rx = i;
491
dsp->pcm_bank_tx = 2; /* loop */
492
dsp->pcm_bank_rx = 2;
493
if (dsp_debug & DEBUG_DSP_CMX)
494
printk(KERN_DEBUG
495
"%s assign echo for %s using slot %d\n",
496
__func__, dsp->name, dsp->pcm_slot_tx);
497
dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN,
498
dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2);
499
dsp->echo.hardware = 1;
500
return;
501
}
502
503
/* conf gets updated (all members) */
504
if (dsp_debug & DEBUG_DSP_CMX)
505
printk(KERN_DEBUG "%s checking conference %d\n",
506
__func__, conf->id);
507
508
if (list_empty(&conf->mlist)) {
509
printk(KERN_ERR "%s: conference whithout members\n",
510
__func__);
511
return;
512
}
513
member = list_entry(conf->mlist.next, struct dsp_conf_member, list);
514
same_hfc = member->dsp->features.hfc_id;
515
same_pcm = member->dsp->features.pcm_id;
516
/* check all members in our conference */
517
list_for_each_entry(member, &conf->mlist, list) {
518
/* check if member uses mixing */
519
if (member->dsp->tx_mix) {
520
if (dsp_debug & DEBUG_DSP_CMX)
521
printk(KERN_DEBUG
522
"%s dsp %s cannot form a conf, because "
523
"tx_mix is turned on\n", __func__,
524
member->dsp->name);
525
conf_software:
526
list_for_each_entry(member, &conf->mlist, list) {
527
dsp = member->dsp;
528
/* remove HFC conference if enabled */
529
if (dsp->hfc_conf >= 0) {
530
if (dsp_debug & DEBUG_DSP_CMX)
531
printk(KERN_DEBUG
532
"%s removing %s from HFC "
533
"conf %d because not "
534
"possible with hardware\n",
535
__func__,
536
dsp->name,
537
dsp->hfc_conf);
538
dsp_cmx_hw_message(dsp,
539
MISDN_CTRL_HFC_CONF_SPLIT,
540
0, 0, 0, 0);
541
dsp->hfc_conf = -1;
542
}
543
/* remove PCM slot if assigned */
544
if (dsp->pcm_slot_tx >= 0 ||
545
dsp->pcm_slot_rx >= 0) {
546
if (dsp_debug & DEBUG_DSP_CMX)
547
printk(KERN_DEBUG "%s removing "
548
"%s from PCM slot %d (TX)"
549
" slot %d (RX) because not"
550
" possible with hardware\n",
551
__func__,
552
dsp->name,
553
dsp->pcm_slot_tx,
554
dsp->pcm_slot_rx);
555
dsp_cmx_hw_message(dsp,
556
MISDN_CTRL_HFC_PCM_DISC,
557
0, 0, 0, 0);
558
dsp->pcm_slot_tx = -1;
559
dsp->pcm_bank_tx = -1;
560
dsp->pcm_slot_rx = -1;
561
dsp->pcm_bank_rx = -1;
562
}
563
}
564
conf->hardware = 0;
565
conf->software = 1;
566
return;
567
}
568
/* check if member has echo turned on */
569
if (member->dsp->echo.hardware || member->dsp->echo.software) {
570
if (dsp_debug & DEBUG_DSP_CMX)
571
printk(KERN_DEBUG
572
"%s dsp %s cannot form a conf, because "
573
"echo is turned on\n", __func__,
574
member->dsp->name);
575
goto conf_software;
576
}
577
/* check if member has tx_mix turned on */
578
if (member->dsp->tx_mix) {
579
if (dsp_debug & DEBUG_DSP_CMX)
580
printk(KERN_DEBUG
581
"%s dsp %s cannot form a conf, because "
582
"tx_mix is turned on\n",
583
__func__, member->dsp->name);
584
goto conf_software;
585
}
586
/* check if member changes volume at an not suppoted level */
587
if (member->dsp->tx_volume) {
588
if (dsp_debug & DEBUG_DSP_CMX)
589
printk(KERN_DEBUG
590
"%s dsp %s cannot form a conf, because "
591
"tx_volume is changed\n",
592
__func__, member->dsp->name);
593
goto conf_software;
594
}
595
if (member->dsp->rx_volume) {
596
if (dsp_debug & DEBUG_DSP_CMX)
597
printk(KERN_DEBUG
598
"%s dsp %s cannot form a conf, because "
599
"rx_volume is changed\n",
600
__func__, member->dsp->name);
601
goto conf_software;
602
}
603
/* check if tx-data turned on */
604
if (member->dsp->tx_data) {
605
if (dsp_debug & DEBUG_DSP_CMX)
606
printk(KERN_DEBUG
607
"%s dsp %s tx_data is turned on\n",
608
__func__, member->dsp->name);
609
tx_data = 1;
610
}
611
/* check if pipeline exists */
612
if (member->dsp->pipeline.inuse) {
613
if (dsp_debug & DEBUG_DSP_CMX)
614
printk(KERN_DEBUG
615
"%s dsp %s cannot form a conf, because "
616
"pipeline exists\n", __func__,
617
member->dsp->name);
618
goto conf_software;
619
}
620
/* check if encryption is enabled */
621
if (member->dsp->bf_enable) {
622
if (dsp_debug & DEBUG_DSP_CMX)
623
printk(KERN_DEBUG "%s dsp %s cannot form a "
624
"conf, because encryption is enabled\n",
625
__func__, member->dsp->name);
626
goto conf_software;
627
}
628
/* check if member is on a card with PCM support */
629
if (member->dsp->features.pcm_id < 0) {
630
if (dsp_debug & DEBUG_DSP_CMX)
631
printk(KERN_DEBUG
632
"%s dsp %s cannot form a conf, because "
633
"dsp has no PCM bus\n",
634
__func__, member->dsp->name);
635
goto conf_software;
636
}
637
/* check if relations are on the same PCM bus */
638
if (member->dsp->features.pcm_id != same_pcm) {
639
if (dsp_debug & DEBUG_DSP_CMX)
640
printk(KERN_DEBUG
641
"%s dsp %s cannot form a conf, because "
642
"dsp is on a different PCM bus than the "
643
"first dsp\n",
644
__func__, member->dsp->name);
645
goto conf_software;
646
}
647
/* determine if members are on the same hfc chip */
648
if (same_hfc != member->dsp->features.hfc_id)
649
same_hfc = -1;
650
/* if there are members already in a conference */
651
if (current_conf < 0 && member->dsp->hfc_conf >= 0)
652
current_conf = member->dsp->hfc_conf;
653
/* if any member is not in a conference */
654
if (member->dsp->hfc_conf < 0)
655
all_conf = 0;
656
657
memb++;
658
}
659
660
/* if no member, this is an error */
661
if (memb < 1)
662
return;
663
664
/* one member */
665
if (memb == 1) {
666
if (dsp_debug & DEBUG_DSP_CMX)
667
printk(KERN_DEBUG
668
"%s conf %d cannot form a HW conference, "
669
"because dsp is alone\n", __func__, conf->id);
670
conf->hardware = 0;
671
conf->software = 0;
672
member = list_entry(conf->mlist.next, struct dsp_conf_member,
673
list);
674
dsp = member->dsp;
675
goto one_member;
676
}
677
678
/*
679
* ok, now we are sure that all members are on the same pcm.
680
* now we will see if we have only two members, so we can do
681
* crossconnections, which don't have any limitations.
682
*/
683
684
/* if we have only two members */
685
if (memb == 2) {
686
member = list_entry(conf->mlist.next, struct dsp_conf_member,
687
list);
688
nextm = list_entry(member->list.next, struct dsp_conf_member,
689
list);
690
/* remove HFC conference if enabled */
691
if (member->dsp->hfc_conf >= 0) {
692
if (dsp_debug & DEBUG_DSP_CMX)
693
printk(KERN_DEBUG
694
"%s removing %s from HFC conf %d because "
695
"two parties require only a PCM slot\n",
696
__func__, member->dsp->name,
697
member->dsp->hfc_conf);
698
dsp_cmx_hw_message(member->dsp,
699
MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0);
700
member->dsp->hfc_conf = -1;
701
}
702
if (nextm->dsp->hfc_conf >= 0) {
703
if (dsp_debug & DEBUG_DSP_CMX)
704
printk(KERN_DEBUG
705
"%s removing %s from HFC conf %d because "
706
"two parties require only a PCM slot\n",
707
__func__, nextm->dsp->name,
708
nextm->dsp->hfc_conf);
709
dsp_cmx_hw_message(nextm->dsp,
710
MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0);
711
nextm->dsp->hfc_conf = -1;
712
}
713
/* if members have two banks (and not on the same chip) */
714
if (member->dsp->features.pcm_banks > 1 &&
715
nextm->dsp->features.pcm_banks > 1 &&
716
member->dsp->features.hfc_id !=
717
nextm->dsp->features.hfc_id) {
718
/* if both members have same slots with crossed banks */
719
if (member->dsp->pcm_slot_tx >= 0 &&
720
member->dsp->pcm_slot_rx >= 0 &&
721
nextm->dsp->pcm_slot_tx >= 0 &&
722
nextm->dsp->pcm_slot_rx >= 0 &&
723
nextm->dsp->pcm_slot_tx ==
724
member->dsp->pcm_slot_rx &&
725
nextm->dsp->pcm_slot_rx ==
726
member->dsp->pcm_slot_tx &&
727
nextm->dsp->pcm_slot_tx ==
728
member->dsp->pcm_slot_tx &&
729
member->dsp->pcm_bank_tx !=
730
member->dsp->pcm_bank_rx &&
731
nextm->dsp->pcm_bank_tx !=
732
nextm->dsp->pcm_bank_rx) {
733
/* all members have same slot */
734
if (dsp_debug & DEBUG_DSP_CMX)
735
printk(KERN_DEBUG
736
"%s dsp %s & %s stay joined on "
737
"PCM slot %d bank %d (TX) bank %d "
738
"(RX) (on different chips)\n",
739
__func__,
740
member->dsp->name,
741
nextm->dsp->name,
742
member->dsp->pcm_slot_tx,
743
member->dsp->pcm_bank_tx,
744
member->dsp->pcm_bank_rx);
745
conf->hardware = 0;
746
conf->software = 1;
747
return;
748
}
749
/* find a new slot */
750
memset(freeslots, 1, sizeof(freeslots));
751
list_for_each_entry(dsp, &dsp_ilist, list) {
752
if (dsp != member->dsp &&
753
dsp != nextm->dsp &&
754
member->dsp->features.pcm_id ==
755
dsp->features.pcm_id) {
756
if (dsp->pcm_slot_rx >= 0 &&
757
dsp->pcm_slot_rx <
758
sizeof(freeslots))
759
freeslots[dsp->pcm_slot_rx] = 0;
760
if (dsp->pcm_slot_tx >= 0 &&
761
dsp->pcm_slot_tx <
762
sizeof(freeslots))
763
freeslots[dsp->pcm_slot_tx] = 0;
764
}
765
}
766
i = 0;
767
ii = member->dsp->features.pcm_slots;
768
while (i < ii) {
769
if (freeslots[i])
770
break;
771
i++;
772
}
773
if (i == ii) {
774
if (dsp_debug & DEBUG_DSP_CMX)
775
printk(KERN_DEBUG
776
"%s no slot available for "
777
"%s & %s\n", __func__,
778
member->dsp->name,
779
nextm->dsp->name);
780
/* no more slots available */
781
goto conf_software;
782
}
783
/* assign free slot */
784
member->dsp->pcm_slot_tx = i;
785
member->dsp->pcm_slot_rx = i;
786
nextm->dsp->pcm_slot_tx = i;
787
nextm->dsp->pcm_slot_rx = i;
788
member->dsp->pcm_bank_rx = 0;
789
member->dsp->pcm_bank_tx = 1;
790
nextm->dsp->pcm_bank_rx = 1;
791
nextm->dsp->pcm_bank_tx = 0;
792
if (dsp_debug & DEBUG_DSP_CMX)
793
printk(KERN_DEBUG
794
"%s adding %s & %s to new PCM slot %d "
795
"(TX and RX on different chips) because "
796
"both members have not same slots\n",
797
__func__,
798
member->dsp->name,
799
nextm->dsp->name,
800
member->dsp->pcm_slot_tx);
801
dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN,
802
member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx,
803
member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx);
804
dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN,
805
nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx,
806
nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx);
807
conf->hardware = 1;
808
conf->software = tx_data;
809
return;
810
/* if members have one bank (or on the same chip) */
811
} else {
812
/* if both members have different crossed slots */
813
if (member->dsp->pcm_slot_tx >= 0 &&
814
member->dsp->pcm_slot_rx >= 0 &&
815
nextm->dsp->pcm_slot_tx >= 0 &&
816
nextm->dsp->pcm_slot_rx >= 0 &&
817
nextm->dsp->pcm_slot_tx ==
818
member->dsp->pcm_slot_rx &&
819
nextm->dsp->pcm_slot_rx ==
820
member->dsp->pcm_slot_tx &&
821
member->dsp->pcm_slot_tx !=
822
member->dsp->pcm_slot_rx &&
823
member->dsp->pcm_bank_tx == 0 &&
824
member->dsp->pcm_bank_rx == 0 &&
825
nextm->dsp->pcm_bank_tx == 0 &&
826
nextm->dsp->pcm_bank_rx == 0) {
827
/* all members have same slot */
828
if (dsp_debug & DEBUG_DSP_CMX)
829
printk(KERN_DEBUG
830
"%s dsp %s & %s stay joined on PCM "
831
"slot %d (TX) %d (RX) on same chip "
832
"or one bank PCM)\n", __func__,
833
member->dsp->name,
834
nextm->dsp->name,
835
member->dsp->pcm_slot_tx,
836
member->dsp->pcm_slot_rx);
837
conf->hardware = 0;
838
conf->software = 1;
839
return;
840
}
841
/* find two new slot */
842
memset(freeslots, 1, sizeof(freeslots));
843
list_for_each_entry(dsp, &dsp_ilist, list) {
844
if (dsp != member->dsp &&
845
dsp != nextm->dsp &&
846
member->dsp->features.pcm_id ==
847
dsp->features.pcm_id) {
848
if (dsp->pcm_slot_rx >= 0 &&
849
dsp->pcm_slot_rx <
850
sizeof(freeslots))
851
freeslots[dsp->pcm_slot_rx] = 0;
852
if (dsp->pcm_slot_tx >= 0 &&
853
dsp->pcm_slot_tx <
854
sizeof(freeslots))
855
freeslots[dsp->pcm_slot_tx] = 0;
856
}
857
}
858
i1 = 0;
859
ii = member->dsp->features.pcm_slots;
860
while (i1 < ii) {
861
if (freeslots[i1])
862
break;
863
i1++;
864
}
865
if (i1 == ii) {
866
if (dsp_debug & DEBUG_DSP_CMX)
867
printk(KERN_DEBUG
868
"%s no slot available "
869
"for %s & %s\n", __func__,
870
member->dsp->name,
871
nextm->dsp->name);
872
/* no more slots available */
873
goto conf_software;
874
}
875
i2 = i1+1;
876
while (i2 < ii) {
877
if (freeslots[i2])
878
break;
879
i2++;
880
}
881
if (i2 == ii) {
882
if (dsp_debug & DEBUG_DSP_CMX)
883
printk(KERN_DEBUG
884
"%s no slot available "
885
"for %s & %s\n",
886
__func__,
887
member->dsp->name,
888
nextm->dsp->name);
889
/* no more slots available */
890
goto conf_software;
891
}
892
/* assign free slots */
893
member->dsp->pcm_slot_tx = i1;
894
member->dsp->pcm_slot_rx = i2;
895
nextm->dsp->pcm_slot_tx = i2;
896
nextm->dsp->pcm_slot_rx = i1;
897
member->dsp->pcm_bank_rx = 0;
898
member->dsp->pcm_bank_tx = 0;
899
nextm->dsp->pcm_bank_rx = 0;
900
nextm->dsp->pcm_bank_tx = 0;
901
if (dsp_debug & DEBUG_DSP_CMX)
902
printk(KERN_DEBUG
903
"%s adding %s & %s to new PCM slot %d "
904
"(TX) %d (RX) on same chip or one bank "
905
"PCM, because both members have not "
906
"crossed slots\n", __func__,
907
member->dsp->name,
908
nextm->dsp->name,
909
member->dsp->pcm_slot_tx,
910
member->dsp->pcm_slot_rx);
911
dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN,
912
member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx,
913
member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx);
914
dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN,
915
nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx,
916
nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx);
917
conf->hardware = 1;
918
conf->software = tx_data;
919
return;
920
}
921
}
922
923
/*
924
* if we have more than two, we may check if we have a conference
925
* unit available on the chip. also all members must be on the same
926
*/
927
928
/* if not the same HFC chip */
929
if (same_hfc < 0) {
930
if (dsp_debug & DEBUG_DSP_CMX)
931
printk(KERN_DEBUG
932
"%s conference %d cannot be formed, because "
933
"members are on different chips or not "
934
"on HFC chip\n",
935
__func__, conf->id);
936
goto conf_software;
937
}
938
939
/* for more than two members.. */
940
941
/* if all members already have the same conference */
942
if (all_conf)
943
return;
944
945
/*
946
* if there is an existing conference, but not all members have joined
947
*/
948
if (current_conf >= 0) {
949
join_members:
950
list_for_each_entry(member, &conf->mlist, list) {
951
/* if no conference engine on our chip, change to
952
* software */
953
if (!member->dsp->features.hfc_conf)
954
goto conf_software;
955
/* in case of hdlc, change to software */
956
if (member->dsp->hdlc)
957
goto conf_software;
958
/* join to current conference */
959
if (member->dsp->hfc_conf == current_conf)
960
continue;
961
/* get a free timeslot first */
962
memset(freeslots, 1, sizeof(freeslots));
963
list_for_each_entry(dsp, &dsp_ilist, list) {
964
/*
965
* not checking current member, because
966
* slot will be overwritten.
967
*/
968
if (
969
dsp != member->dsp &&
970
/* dsp must be on the same PCM */
971
member->dsp->features.pcm_id ==
972
dsp->features.pcm_id) {
973
/* dsp must be on a slot */
974
if (dsp->pcm_slot_tx >= 0 &&
975
dsp->pcm_slot_tx <
976
sizeof(freeslots))
977
freeslots[dsp->pcm_slot_tx] = 0;
978
if (dsp->pcm_slot_rx >= 0 &&
979
dsp->pcm_slot_rx <
980
sizeof(freeslots))
981
freeslots[dsp->pcm_slot_rx] = 0;
982
}
983
}
984
i = 0;
985
ii = member->dsp->features.pcm_slots;
986
while (i < ii) {
987
if (freeslots[i])
988
break;
989
i++;
990
}
991
if (i == ii) {
992
/* no more slots available */
993
if (dsp_debug & DEBUG_DSP_CMX)
994
printk(KERN_DEBUG
995
"%s conference %d cannot be formed,"
996
" because no slot free\n",
997
__func__, conf->id);
998
goto conf_software;
999
}
1000
if (dsp_debug & DEBUG_DSP_CMX)
1001
printk(KERN_DEBUG
1002
"%s changing dsp %s to HW conference "
1003
"%d slot %d\n", __func__,
1004
member->dsp->name, current_conf, i);
1005
/* assign free slot & set PCM & join conf */
1006
member->dsp->pcm_slot_tx = i;
1007
member->dsp->pcm_slot_rx = i;
1008
member->dsp->pcm_bank_tx = 2; /* loop */
1009
member->dsp->pcm_bank_rx = 2;
1010
member->dsp->hfc_conf = current_conf;
1011
dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN,
1012
i, 2, i, 2);
1013
dsp_cmx_hw_message(member->dsp,
1014
MISDN_CTRL_HFC_CONF_JOIN, current_conf, 0, 0, 0);
1015
}
1016
return;
1017
}
1018
1019
/*
1020
* no member is in a conference yet, so we find a free one
1021
*/
1022
memset(freeunits, 1, sizeof(freeunits));
1023
list_for_each_entry(dsp, &dsp_ilist, list) {
1024
/* dsp must be on the same chip */
1025
if (dsp->features.hfc_id == same_hfc &&
1026
/* dsp must have joined a HW conference */
1027
dsp->hfc_conf >= 0 &&
1028
/* slot must be within range */
1029
dsp->hfc_conf < 8)
1030
freeunits[dsp->hfc_conf] = 0;
1031
}
1032
i = 0;
1033
ii = 8;
1034
while (i < ii) {
1035
if (freeunits[i])
1036
break;
1037
i++;
1038
}
1039
if (i == ii) {
1040
/* no more conferences available */
1041
if (dsp_debug & DEBUG_DSP_CMX)
1042
printk(KERN_DEBUG
1043
"%s conference %d cannot be formed, because "
1044
"no conference number free\n",
1045
__func__, conf->id);
1046
goto conf_software;
1047
}
1048
/* join all members */
1049
current_conf = i;
1050
goto join_members;
1051
}
1052
1053
1054
/*
1055
* conf_id != 0: join or change conference
1056
* conf_id == 0: split from conference if not already
1057
*/
1058
int
1059
dsp_cmx_conf(struct dsp *dsp, u32 conf_id)
1060
{
1061
int err;
1062
struct dsp_conf *conf;
1063
struct dsp_conf_member *member;
1064
1065
/* if conference doesn't change */
1066
if (dsp->conf_id == conf_id)
1067
return 0;
1068
1069
/* first remove us from current conf */
1070
if (dsp->conf_id) {
1071
if (dsp_debug & DEBUG_DSP_CMX)
1072
printk(KERN_DEBUG "removing us from conference %d\n",
1073
dsp->conf->id);
1074
/* remove us from conf */
1075
conf = dsp->conf;
1076
err = dsp_cmx_del_conf_member(dsp);
1077
if (err)
1078
return err;
1079
dsp->conf_id = 0;
1080
1081
/* update hardware */
1082
dsp_cmx_hardware(NULL, dsp);
1083
1084
/* conf now empty? */
1085
if (list_empty(&conf->mlist)) {
1086
if (dsp_debug & DEBUG_DSP_CMX)
1087
printk(KERN_DEBUG
1088
"conference is empty, so we remove it.\n");
1089
err = dsp_cmx_del_conf(conf);
1090
if (err)
1091
return err;
1092
} else {
1093
/* update members left on conf */
1094
dsp_cmx_hardware(conf, NULL);
1095
}
1096
}
1097
1098
/* if split */
1099
if (!conf_id)
1100
return 0;
1101
1102
/* now add us to conf */
1103
if (dsp_debug & DEBUG_DSP_CMX)
1104
printk(KERN_DEBUG "searching conference %d\n",
1105
conf_id);
1106
conf = dsp_cmx_search_conf(conf_id);
1107
if (!conf) {
1108
if (dsp_debug & DEBUG_DSP_CMX)
1109
printk(KERN_DEBUG
1110
"conference doesn't exist yet, creating.\n");
1111
/* the conference doesn't exist, so we create */
1112
conf = dsp_cmx_new_conf(conf_id);
1113
if (!conf)
1114
return -EINVAL;
1115
} else if (!list_empty(&conf->mlist)) {
1116
member = list_entry(conf->mlist.next, struct dsp_conf_member,
1117
list);
1118
if (dsp->hdlc && !member->dsp->hdlc) {
1119
if (dsp_debug & DEBUG_DSP_CMX)
1120
printk(KERN_DEBUG
1121
"cannot join transparent conference.\n");
1122
return -EINVAL;
1123
}
1124
if (!dsp->hdlc && member->dsp->hdlc) {
1125
if (dsp_debug & DEBUG_DSP_CMX)
1126
printk(KERN_DEBUG
1127
"cannot join hdlc conference.\n");
1128
return -EINVAL;
1129
}
1130
}
1131
/* add conference member */
1132
err = dsp_cmx_add_conf_member(dsp, conf);
1133
if (err)
1134
return err;
1135
dsp->conf_id = conf_id;
1136
1137
/* if we are alone, we do nothing! */
1138
if (list_empty(&conf->mlist)) {
1139
if (dsp_debug & DEBUG_DSP_CMX)
1140
printk(KERN_DEBUG
1141
"we are alone in this conference, so exit.\n");
1142
/* update hardware */
1143
dsp_cmx_hardware(NULL, dsp);
1144
return 0;
1145
}
1146
1147
/* update members on conf */
1148
dsp_cmx_hardware(conf, NULL);
1149
1150
return 0;
1151
}
1152
1153
#ifdef CMX_DELAY_DEBUG
1154
int delaycount;
1155
static void
1156
showdelay(struct dsp *dsp, int samples, int delay)
1157
{
1158
char bar[] = "--------------------------------------------------|";
1159
int sdelay;
1160
1161
delaycount += samples;
1162
if (delaycount < 8000)
1163
return;
1164
delaycount = 0;
1165
1166
sdelay = delay * 50 / (dsp_poll << 2);
1167
1168
printk(KERN_DEBUG "DELAY (%s) %3d >%s\n", dsp->name, delay,
1169
sdelay > 50 ? "..." : bar + 50 - sdelay);
1170
}
1171
#endif
1172
1173
/*
1174
* audio data is received from card
1175
*/
1176
void
1177
dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb)
1178
{
1179
u8 *d, *p;
1180
int len = skb->len;
1181
struct mISDNhead *hh = mISDN_HEAD_P(skb);
1182
int w, i, ii;
1183
1184
/* check if we have sompen */
1185
if (len < 1)
1186
return;
1187
1188
/* half of the buffer should be larger than maximum packet size */
1189
if (len >= CMX_BUFF_HALF) {
1190
printk(KERN_ERR
1191
"%s line %d: packet from card is too large (%d bytes). "
1192
"please make card send smaller packets OR increase "
1193
"CMX_BUFF_SIZE\n", __FILE__, __LINE__, len);
1194
return;
1195
}
1196
1197
/*
1198
* initialize pointers if not already -
1199
* also add delay if requested by PH_SIGNAL
1200
*/
1201
if (dsp->rx_init) {
1202
dsp->rx_init = 0;
1203
if (dsp->features.unordered) {
1204
dsp->rx_R = (hh->id & CMX_BUFF_MASK);
1205
if (dsp->cmx_delay)
1206
dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
1207
& CMX_BUFF_MASK;
1208
else
1209
dsp->rx_W = (dsp->rx_R + (dsp_poll >> 1))
1210
& CMX_BUFF_MASK;
1211
} else {
1212
dsp->rx_R = 0;
1213
if (dsp->cmx_delay)
1214
dsp->rx_W = dsp->cmx_delay;
1215
else
1216
dsp->rx_W = dsp_poll >> 1;
1217
}
1218
}
1219
/* if frame contains time code, write directly */
1220
if (dsp->features.unordered) {
1221
dsp->rx_W = (hh->id & CMX_BUFF_MASK);
1222
/* printk(KERN_DEBUG "%s %08x\n", dsp->name, hh->id); */
1223
}
1224
/*
1225
* if we underrun (or maybe overrun),
1226
* we set our new read pointer, and write silence to buffer
1227
*/
1228
if (((dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) {
1229
if (dsp_debug & DEBUG_DSP_CLOCK)
1230
printk(KERN_DEBUG
1231
"cmx_receive(dsp=%lx): UNDERRUN (or overrun the "
1232
"maximum delay), adjusting read pointer! "
1233
"(inst %s)\n", (u_long)dsp, dsp->name);
1234
/* flush rx buffer and set delay to dsp_poll / 2 */
1235
if (dsp->features.unordered) {
1236
dsp->rx_R = (hh->id & CMX_BUFF_MASK);
1237
if (dsp->cmx_delay)
1238
dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
1239
& CMX_BUFF_MASK;
1240
else
1241
dsp->rx_W = (dsp->rx_R + (dsp_poll >> 1))
1242
& CMX_BUFF_MASK;
1243
} else {
1244
dsp->rx_R = 0;
1245
if (dsp->cmx_delay)
1246
dsp->rx_W = dsp->cmx_delay;
1247
else
1248
dsp->rx_W = dsp_poll >> 1;
1249
}
1250
memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
1251
}
1252
/* if we have reached double delay, jump back to middle */
1253
if (dsp->cmx_delay)
1254
if (((dsp->rx_W - dsp->rx_R) & CMX_BUFF_MASK) >=
1255
(dsp->cmx_delay << 1)) {
1256
if (dsp_debug & DEBUG_DSP_CLOCK)
1257
printk(KERN_DEBUG
1258
"cmx_receive(dsp=%lx): OVERRUN (because "
1259
"twice the delay is reached), adjusting "
1260
"read pointer! (inst %s)\n",
1261
(u_long)dsp, dsp->name);
1262
/* flush buffer */
1263
if (dsp->features.unordered) {
1264
dsp->rx_R = (hh->id & CMX_BUFF_MASK);
1265
dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
1266
& CMX_BUFF_MASK;
1267
} else {
1268
dsp->rx_R = 0;
1269
dsp->rx_W = dsp->cmx_delay;
1270
}
1271
memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
1272
}
1273
1274
/* show where to write */
1275
#ifdef CMX_DEBUG
1276
printk(KERN_DEBUG
1277
"cmx_receive(dsp=%lx): rx_R(dsp)=%05x rx_W(dsp)=%05x len=%d %s\n",
1278
(u_long)dsp, dsp->rx_R, dsp->rx_W, len, dsp->name);
1279
#endif
1280
1281
/* write data into rx_buffer */
1282
p = skb->data;
1283
d = dsp->rx_buff;
1284
w = dsp->rx_W;
1285
i = 0;
1286
ii = len;
1287
while (i < ii) {
1288
d[w++ & CMX_BUFF_MASK] = *p++;
1289
i++;
1290
}
1291
1292
/* increase write-pointer */
1293
dsp->rx_W = ((dsp->rx_W+len) & CMX_BUFF_MASK);
1294
#ifdef CMX_DELAY_DEBUG
1295
showdelay(dsp, len, (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK);
1296
#endif
1297
}
1298
1299
1300
/*
1301
* send (mixed) audio data to card and control jitter
1302
*/
1303
static void
1304
dsp_cmx_send_member(struct dsp *dsp, int len, s32 *c, int members)
1305
{
1306
struct dsp_conf *conf = dsp->conf;
1307
struct dsp *member, *other;
1308
register s32 sample;
1309
u8 *d, *p, *q, *o_q;
1310
struct sk_buff *nskb, *txskb;
1311
int r, rr, t, tt, o_r, o_rr;
1312
int preload = 0;
1313
struct mISDNhead *hh, *thh;
1314
int tx_data_only = 0;
1315
1316
/* don't process if: */
1317
if (!dsp->b_active) { /* if not active */
1318
dsp->last_tx = 0;
1319
return;
1320
}
1321
if (((dsp->conf && dsp->conf->hardware) || /* hardware conf */
1322
dsp->echo.hardware) && /* OR hardware echo */
1323
dsp->tx_R == dsp->tx_W && /* AND no tx-data */
1324
!(dsp->tone.tone && dsp->tone.software)) { /* AND not soft tones */
1325
if (!dsp->tx_data) { /* no tx_data for user space required */
1326
dsp->last_tx = 0;
1327
return;
1328
}
1329
if (dsp->conf && dsp->conf->software && dsp->conf->hardware)
1330
tx_data_only = 1;
1331
if (dsp->conf->software && dsp->echo.hardware)
1332
tx_data_only = 1;
1333
}
1334
1335
#ifdef CMX_DEBUG
1336
printk(KERN_DEBUG
1337
"SEND members=%d dsp=%s, conf=%p, rx_R=%05x rx_W=%05x\n",
1338
members, dsp->name, conf, dsp->rx_R, dsp->rx_W);
1339
#endif
1340
1341
/* preload if we have delay set */
1342
if (dsp->cmx_delay && !dsp->last_tx) {
1343
preload = len;
1344
if (preload < 128)
1345
preload = 128;
1346
}
1347
1348
/* PREPARE RESULT */
1349
nskb = mI_alloc_skb(len + preload, GFP_ATOMIC);
1350
if (!nskb) {
1351
printk(KERN_ERR
1352
"FATAL ERROR in mISDN_dsp.o: cannot alloc %d bytes\n",
1353
len + preload);
1354
return;
1355
}
1356
hh = mISDN_HEAD_P(nskb);
1357
hh->prim = PH_DATA_REQ;
1358
hh->id = 0;
1359
dsp->last_tx = 1;
1360
1361
/* set pointers, indexes and stuff */
1362
member = dsp;
1363
p = dsp->tx_buff; /* transmit data */
1364
q = dsp->rx_buff; /* received data */
1365
d = skb_put(nskb, preload + len); /* result */
1366
t = dsp->tx_R; /* tx-pointers */
1367
tt = dsp->tx_W;
1368
r = dsp->rx_R; /* rx-pointers */
1369
rr = (r + len) & CMX_BUFF_MASK;
1370
1371
/* preload with silence, if required */
1372
if (preload) {
1373
memset(d, dsp_silence, preload);
1374
d += preload;
1375
}
1376
1377
/* PROCESS TONES/TX-DATA ONLY */
1378
if (dsp->tone.tone && dsp->tone.software) {
1379
/* -> copy tone */
1380
dsp_tone_copy(dsp, d, len);
1381
dsp->tx_R = 0; /* clear tx buffer */
1382
dsp->tx_W = 0;
1383
goto send_packet;
1384
}
1385
/* if we have tx-data but do not use mixing */
1386
if (!dsp->tx_mix && t != tt) {
1387
/* -> send tx-data and continue when not enough */
1388
#ifdef CMX_TX_DEBUG
1389
sprintf(debugbuf, "TX sending (%04x-%04x)%p: ", t, tt, p);
1390
#endif
1391
while (r != rr && t != tt) {
1392
#ifdef CMX_TX_DEBUG
1393
if (strlen(debugbuf) < 48)
1394
sprintf(debugbuf+strlen(debugbuf), " %02x",
1395
p[t]);
1396
#endif
1397
*d++ = p[t]; /* write tx_buff */
1398
t = (t+1) & CMX_BUFF_MASK;
1399
r = (r+1) & CMX_BUFF_MASK;
1400
}
1401
if (r == rr) {
1402
dsp->tx_R = t;
1403
#ifdef CMX_TX_DEBUG
1404
printk(KERN_DEBUG "%s\n", debugbuf);
1405
#endif
1406
goto send_packet;
1407
}
1408
}
1409
#ifdef CMX_TX_DEBUG
1410
printk(KERN_DEBUG "%s\n", debugbuf);
1411
#endif
1412
1413
/* PROCESS DATA (one member / no conf) */
1414
if (!conf || members <= 1) {
1415
/* -> if echo is NOT enabled */
1416
if (!dsp->echo.software) {
1417
/* -> send tx-data if available or use 0-volume */
1418
while (r != rr && t != tt) {
1419
*d++ = p[t]; /* write tx_buff */
1420
t = (t+1) & CMX_BUFF_MASK;
1421
r = (r+1) & CMX_BUFF_MASK;
1422
}
1423
if (r != rr) {
1424
if (dsp_debug & DEBUG_DSP_CLOCK)
1425
printk(KERN_DEBUG "%s: RX empty\n",
1426
__func__);
1427
memset(d, dsp_silence, (rr-r)&CMX_BUFF_MASK);
1428
}
1429
/* -> if echo is enabled */
1430
} else {
1431
/*
1432
* -> mix tx-data with echo if available,
1433
* or use echo only
1434
*/
1435
while (r != rr && t != tt) {
1436
*d++ = dsp_audio_mix_law[(p[t]<<8)|q[r]];
1437
t = (t+1) & CMX_BUFF_MASK;
1438
r = (r+1) & CMX_BUFF_MASK;
1439
}
1440
while (r != rr) {
1441
*d++ = q[r]; /* echo */
1442
r = (r+1) & CMX_BUFF_MASK;
1443
}
1444
}
1445
dsp->tx_R = t;
1446
goto send_packet;
1447
}
1448
/* PROCESS DATA (two members) */
1449
#ifdef CMX_CONF_DEBUG
1450
if (0) {
1451
#else
1452
if (members == 2) {
1453
#endif
1454
/* "other" becomes other party */
1455
other = (list_entry(conf->mlist.next,
1456
struct dsp_conf_member, list))->dsp;
1457
if (other == member)
1458
other = (list_entry(conf->mlist.prev,
1459
struct dsp_conf_member, list))->dsp;
1460
o_q = other->rx_buff; /* received data */
1461
o_rr = (other->rx_R + len) & CMX_BUFF_MASK;
1462
/* end of rx-pointer */
1463
o_r = (o_rr - rr + r) & CMX_BUFF_MASK;
1464
/* start rx-pointer at current read position*/
1465
/* -> if echo is NOT enabled */
1466
if (!dsp->echo.software) {
1467
/*
1468
* -> copy other member's rx-data,
1469
* if tx-data is available, mix
1470
*/
1471
while (o_r != o_rr && t != tt) {
1472
*d++ = dsp_audio_mix_law[(p[t]<<8)|o_q[o_r]];
1473
t = (t+1) & CMX_BUFF_MASK;
1474
o_r = (o_r+1) & CMX_BUFF_MASK;
1475
}
1476
while (o_r != o_rr) {
1477
*d++ = o_q[o_r];
1478
o_r = (o_r+1) & CMX_BUFF_MASK;
1479
}
1480
/* -> if echo is enabled */
1481
} else {
1482
/*
1483
* -> mix other member's rx-data with echo,
1484
* if tx-data is available, mix
1485
*/
1486
while (r != rr && t != tt) {
1487
sample = dsp_audio_law_to_s32[p[t]] +
1488
dsp_audio_law_to_s32[q[r]] +
1489
dsp_audio_law_to_s32[o_q[o_r]];
1490
if (sample < -32768)
1491
sample = -32768;
1492
else if (sample > 32767)
1493
sample = 32767;
1494
*d++ = dsp_audio_s16_to_law[sample & 0xffff];
1495
/* tx-data + rx_data + echo */
1496
t = (t+1) & CMX_BUFF_MASK;
1497
r = (r+1) & CMX_BUFF_MASK;
1498
o_r = (o_r+1) & CMX_BUFF_MASK;
1499
}
1500
while (r != rr) {
1501
*d++ = dsp_audio_mix_law[(q[r]<<8)|o_q[o_r]];
1502
r = (r+1) & CMX_BUFF_MASK;
1503
o_r = (o_r+1) & CMX_BUFF_MASK;
1504
}
1505
}
1506
dsp->tx_R = t;
1507
goto send_packet;
1508
}
1509
#ifdef DSP_NEVER_DEFINED
1510
}
1511
#endif
1512
/* PROCESS DATA (three or more members) */
1513
/* -> if echo is NOT enabled */
1514
if (!dsp->echo.software) {
1515
/*
1516
* -> subtract rx-data from conf-data,
1517
* if tx-data is available, mix
1518
*/
1519
while (r != rr && t != tt) {
1520
sample = dsp_audio_law_to_s32[p[t]] + *c++ -
1521
dsp_audio_law_to_s32[q[r]];
1522
if (sample < -32768)
1523
sample = -32768;
1524
else if (sample > 32767)
1525
sample = 32767;
1526
*d++ = dsp_audio_s16_to_law[sample & 0xffff];
1527
/* conf-rx+tx */
1528
r = (r+1) & CMX_BUFF_MASK;
1529
t = (t+1) & CMX_BUFF_MASK;
1530
}
1531
while (r != rr) {
1532
sample = *c++ - dsp_audio_law_to_s32[q[r]];
1533
if (sample < -32768)
1534
sample = -32768;
1535
else if (sample > 32767)
1536
sample = 32767;
1537
*d++ = dsp_audio_s16_to_law[sample & 0xffff];
1538
/* conf-rx */
1539
r = (r+1) & CMX_BUFF_MASK;
1540
}
1541
/* -> if echo is enabled */
1542
} else {
1543
/*
1544
* -> encode conf-data, if tx-data
1545
* is available, mix
1546
*/
1547
while (r != rr && t != tt) {
1548
sample = dsp_audio_law_to_s32[p[t]] + *c++;
1549
if (sample < -32768)
1550
sample = -32768;
1551
else if (sample > 32767)
1552
sample = 32767;
1553
*d++ = dsp_audio_s16_to_law[sample & 0xffff];
1554
/* conf(echo)+tx */
1555
t = (t+1) & CMX_BUFF_MASK;
1556
r = (r+1) & CMX_BUFF_MASK;
1557
}
1558
while (r != rr) {
1559
sample = *c++;
1560
if (sample < -32768)
1561
sample = -32768;
1562
else if (sample > 32767)
1563
sample = 32767;
1564
*d++ = dsp_audio_s16_to_law[sample & 0xffff];
1565
/* conf(echo) */
1566
r = (r+1) & CMX_BUFF_MASK;
1567
}
1568
}
1569
dsp->tx_R = t;
1570
goto send_packet;
1571
1572
send_packet:
1573
/*
1574
* send tx-data if enabled - don't filter,
1575
* because we want what we send, not what we filtered
1576
*/
1577
if (dsp->tx_data) {
1578
if (tx_data_only) {
1579
hh->prim = DL_DATA_REQ;
1580
hh->id = 0;
1581
/* queue and trigger */
1582
skb_queue_tail(&dsp->sendq, nskb);
1583
schedule_work(&dsp->workq);
1584
/* exit because only tx_data is used */
1585
return;
1586
} else {
1587
txskb = mI_alloc_skb(len, GFP_ATOMIC);
1588
if (!txskb) {
1589
printk(KERN_ERR
1590
"FATAL ERROR in mISDN_dsp.o: "
1591
"cannot alloc %d bytes\n", len);
1592
} else {
1593
thh = mISDN_HEAD_P(txskb);
1594
thh->prim = DL_DATA_REQ;
1595
thh->id = 0;
1596
memcpy(skb_put(txskb, len), nskb->data+preload,
1597
len);
1598
/* queue (trigger later) */
1599
skb_queue_tail(&dsp->sendq, txskb);
1600
}
1601
}
1602
}
1603
1604
/* send data only to card, if we don't just calculated tx_data */
1605
/* adjust volume */
1606
if (dsp->tx_volume)
1607
dsp_change_volume(nskb, dsp->tx_volume);
1608
/* pipeline */
1609
if (dsp->pipeline.inuse)
1610
dsp_pipeline_process_tx(&dsp->pipeline, nskb->data,
1611
nskb->len);
1612
/* crypt */
1613
if (dsp->bf_enable)
1614
dsp_bf_encrypt(dsp, nskb->data, nskb->len);
1615
/* queue and trigger */
1616
skb_queue_tail(&dsp->sendq, nskb);
1617
schedule_work(&dsp->workq);
1618
}
1619
1620
static u32 jittercount; /* counter for jitter check */
1621
struct timer_list dsp_spl_tl;
1622
u32 dsp_spl_jiffies; /* calculate the next time to fire */
1623
static u16 dsp_count; /* last sample count */
1624
static int dsp_count_valid ; /* if we have last sample count */
1625
1626
void
1627
dsp_cmx_send(void *arg)
1628
{
1629
struct dsp_conf *conf;
1630
struct dsp_conf_member *member;
1631
struct dsp *dsp;
1632
int mustmix, members;
1633
static s32 mixbuffer[MAX_POLL+100];
1634
s32 *c;
1635
u8 *p, *q;
1636
int r, rr;
1637
int jittercheck = 0, delay, i;
1638
u_long flags;
1639
u16 length, count;
1640
1641
/* lock */
1642
spin_lock_irqsave(&dsp_lock, flags);
1643
1644
if (!dsp_count_valid) {
1645
dsp_count = mISDN_clock_get();
1646
length = dsp_poll;
1647
dsp_count_valid = 1;
1648
} else {
1649
count = mISDN_clock_get();
1650
length = count - dsp_count;
1651
dsp_count = count;
1652
}
1653
if (length > MAX_POLL + 100)
1654
length = MAX_POLL + 100;
1655
/* printk(KERN_DEBUG "len=%d dsp_count=0x%x\n", length, dsp_count); */
1656
1657
/*
1658
* check if jitter needs to be checked (this is every second)
1659
*/
1660
jittercount += length;
1661
if (jittercount >= 8000) {
1662
jittercount -= 8000;
1663
jittercheck = 1;
1664
}
1665
1666
/* loop all members that do not require conference mixing */
1667
list_for_each_entry(dsp, &dsp_ilist, list) {
1668
if (dsp->hdlc)
1669
continue;
1670
conf = dsp->conf;
1671
mustmix = 0;
1672
members = 0;
1673
if (conf) {
1674
members = count_list_member(&conf->mlist);
1675
#ifdef CMX_CONF_DEBUG
1676
if (conf->software && members > 1)
1677
#else
1678
if (conf->software && members > 2)
1679
#endif
1680
mustmix = 1;
1681
}
1682
1683
/* transmission required */
1684
if (!mustmix) {
1685
dsp_cmx_send_member(dsp, length, mixbuffer, members);
1686
1687
/*
1688
* unused mixbuffer is given to prevent a
1689
* potential null-pointer-bug
1690
*/
1691
}
1692
}
1693
1694
/* loop all members that require conference mixing */
1695
list_for_each_entry(conf, &conf_ilist, list) {
1696
/* count members and check hardware */
1697
members = count_list_member(&conf->mlist);
1698
#ifdef CMX_CONF_DEBUG
1699
if (conf->software && members > 1) {
1700
#else
1701
if (conf->software && members > 2) {
1702
#endif
1703
/* check for hdlc conf */
1704
member = list_entry(conf->mlist.next,
1705
struct dsp_conf_member, list);
1706
if (member->dsp->hdlc)
1707
continue;
1708
/* mix all data */
1709
memset(mixbuffer, 0, length*sizeof(s32));
1710
list_for_each_entry(member, &conf->mlist, list) {
1711
dsp = member->dsp;
1712
/* get range of data to mix */
1713
c = mixbuffer;
1714
q = dsp->rx_buff;
1715
r = dsp->rx_R;
1716
rr = (r + length) & CMX_BUFF_MASK;
1717
/* add member's data */
1718
while (r != rr) {
1719
*c++ += dsp_audio_law_to_s32[q[r]];
1720
r = (r+1) & CMX_BUFF_MASK;
1721
}
1722
}
1723
1724
/* process each member */
1725
list_for_each_entry(member, &conf->mlist, list) {
1726
/* transmission */
1727
dsp_cmx_send_member(member->dsp, length,
1728
mixbuffer, members);
1729
}
1730
}
1731
}
1732
1733
/* delete rx-data, increment buffers, change pointers */
1734
list_for_each_entry(dsp, &dsp_ilist, list) {
1735
if (dsp->hdlc)
1736
continue;
1737
p = dsp->rx_buff;
1738
q = dsp->tx_buff;
1739
r = dsp->rx_R;
1740
/* move receive pointer when receiving */
1741
if (!dsp->rx_is_off) {
1742
rr = (r + length) & CMX_BUFF_MASK;
1743
/* delete rx-data */
1744
while (r != rr) {
1745
p[r] = dsp_silence;
1746
r = (r+1) & CMX_BUFF_MASK;
1747
}
1748
/* increment rx-buffer pointer */
1749
dsp->rx_R = r; /* write incremented read pointer */
1750
}
1751
1752
/* check current rx_delay */
1753
delay = (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK;
1754
if (delay >= CMX_BUFF_HALF)
1755
delay = 0; /* will be the delay before next write */
1756
/* check for lower delay */
1757
if (delay < dsp->rx_delay[0])
1758
dsp->rx_delay[0] = delay;
1759
/* check current tx_delay */
1760
delay = (dsp->tx_W-dsp->tx_R) & CMX_BUFF_MASK;
1761
if (delay >= CMX_BUFF_HALF)
1762
delay = 0; /* will be the delay before next write */
1763
/* check for lower delay */
1764
if (delay < dsp->tx_delay[0])
1765
dsp->tx_delay[0] = delay;
1766
if (jittercheck) {
1767
/* find the lowest of all rx_delays */
1768
delay = dsp->rx_delay[0];
1769
i = 1;
1770
while (i < MAX_SECONDS_JITTER_CHECK) {
1771
if (delay > dsp->rx_delay[i])
1772
delay = dsp->rx_delay[i];
1773
i++;
1774
}
1775
/*
1776
* remove rx_delay only if we have delay AND we
1777
* have not preset cmx_delay AND
1778
* the delay is greater dsp_poll
1779
*/
1780
if (delay > dsp_poll && !dsp->cmx_delay) {
1781
if (dsp_debug & DEBUG_DSP_CLOCK)
1782
printk(KERN_DEBUG
1783
"%s lowest rx_delay of %d bytes for"
1784
" dsp %s are now removed.\n",
1785
__func__, delay,
1786
dsp->name);
1787
r = dsp->rx_R;
1788
rr = (r + delay - (dsp_poll >> 1))
1789
& CMX_BUFF_MASK;
1790
/* delete rx-data */
1791
while (r != rr) {
1792
p[r] = dsp_silence;
1793
r = (r+1) & CMX_BUFF_MASK;
1794
}
1795
/* increment rx-buffer pointer */
1796
dsp->rx_R = r;
1797
/* write incremented read pointer */
1798
}
1799
/* find the lowest of all tx_delays */
1800
delay = dsp->tx_delay[0];
1801
i = 1;
1802
while (i < MAX_SECONDS_JITTER_CHECK) {
1803
if (delay > dsp->tx_delay[i])
1804
delay = dsp->tx_delay[i];
1805
i++;
1806
}
1807
/*
1808
* remove delay only if we have delay AND we
1809
* have enabled tx_dejitter
1810
*/
1811
if (delay > dsp_poll && dsp->tx_dejitter) {
1812
if (dsp_debug & DEBUG_DSP_CLOCK)
1813
printk(KERN_DEBUG
1814
"%s lowest tx_delay of %d bytes for"
1815
" dsp %s are now removed.\n",
1816
__func__, delay,
1817
dsp->name);
1818
r = dsp->tx_R;
1819
rr = (r + delay - (dsp_poll >> 1))
1820
& CMX_BUFF_MASK;
1821
/* delete tx-data */
1822
while (r != rr) {
1823
q[r] = dsp_silence;
1824
r = (r+1) & CMX_BUFF_MASK;
1825
}
1826
/* increment rx-buffer pointer */
1827
dsp->tx_R = r;
1828
/* write incremented read pointer */
1829
}
1830
/* scroll up delays */
1831
i = MAX_SECONDS_JITTER_CHECK - 1;
1832
while (i) {
1833
dsp->rx_delay[i] = dsp->rx_delay[i-1];
1834
dsp->tx_delay[i] = dsp->tx_delay[i-1];
1835
i--;
1836
}
1837
dsp->tx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */
1838
dsp->rx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */
1839
}
1840
}
1841
1842
/* if next event would be in the past ... */
1843
if ((s32)(dsp_spl_jiffies+dsp_tics-jiffies) <= 0)
1844
dsp_spl_jiffies = jiffies + 1;
1845
else
1846
dsp_spl_jiffies += dsp_tics;
1847
1848
dsp_spl_tl.expires = dsp_spl_jiffies;
1849
add_timer(&dsp_spl_tl);
1850
1851
/* unlock */
1852
spin_unlock_irqrestore(&dsp_lock, flags);
1853
}
1854
1855
/*
1856
* audio data is transmitted from upper layer to the dsp
1857
*/
1858
void
1859
dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb)
1860
{
1861
u_int w, ww;
1862
u8 *d, *p;
1863
int space; /* todo: , l = skb->len; */
1864
#ifdef CMX_TX_DEBUG
1865
char debugbuf[256] = "";
1866
#endif
1867
1868
/* check if there is enough space, and then copy */
1869
w = dsp->tx_W;
1870
ww = dsp->tx_R;
1871
p = dsp->tx_buff;
1872
d = skb->data;
1873
space = (ww - w - 1) & CMX_BUFF_MASK;
1874
/* write-pointer should not overrun nor reach read pointer */
1875
if (space < skb->len) {
1876
/* write to the space we have left */
1877
ww = (ww - 1) & CMX_BUFF_MASK; /* end one byte prior tx_R */
1878
if (dsp_debug & DEBUG_DSP_CLOCK)
1879
printk(KERN_DEBUG "%s: TX overflow space=%d skb->len="
1880
"%d, w=0x%04x, ww=0x%04x\n", __func__, space,
1881
skb->len, w, ww);
1882
} else
1883
/* write until all byte are copied */
1884
ww = (w + skb->len) & CMX_BUFF_MASK;
1885
dsp->tx_W = ww;
1886
1887
/* show current buffer */
1888
#ifdef CMX_DEBUG
1889
printk(KERN_DEBUG
1890
"cmx_transmit(dsp=%lx) %d bytes to 0x%x-0x%x. %s\n",
1891
(u_long)dsp, (ww-w)&CMX_BUFF_MASK, w, ww, dsp->name);
1892
#endif
1893
1894
/* copy transmit data to tx-buffer */
1895
#ifdef CMX_TX_DEBUG
1896
sprintf(debugbuf, "TX getting (%04x-%04x)%p: ", w, ww, p);
1897
#endif
1898
while (w != ww) {
1899
#ifdef CMX_TX_DEBUG
1900
if (strlen(debugbuf) < 48)
1901
sprintf(debugbuf+strlen(debugbuf), " %02x", *d);
1902
#endif
1903
p[w] = *d++;
1904
w = (w+1) & CMX_BUFF_MASK;
1905
}
1906
#ifdef CMX_TX_DEBUG
1907
printk(KERN_DEBUG "%s\n", debugbuf);
1908
#endif
1909
1910
}
1911
1912
/*
1913
* hdlc data is received from card and sent to all members.
1914
*/
1915
void
1916
dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb)
1917
{
1918
struct sk_buff *nskb = NULL;
1919
struct dsp_conf_member *member;
1920
struct mISDNhead *hh;
1921
1922
/* not if not active */
1923
if (!dsp->b_active)
1924
return;
1925
1926
/* check if we have sompen */
1927
if (skb->len < 1)
1928
return;
1929
1930
/* no conf */
1931
if (!dsp->conf) {
1932
/* in case of software echo */
1933
if (dsp->echo.software) {
1934
nskb = skb_clone(skb, GFP_ATOMIC);
1935
if (nskb) {
1936
hh = mISDN_HEAD_P(nskb);
1937
hh->prim = PH_DATA_REQ;
1938
hh->id = 0;
1939
skb_queue_tail(&dsp->sendq, nskb);
1940
schedule_work(&dsp->workq);
1941
}
1942
}
1943
return;
1944
}
1945
/* in case of hardware conference */
1946
if (dsp->conf->hardware)
1947
return;
1948
list_for_each_entry(member, &dsp->conf->mlist, list) {
1949
if (dsp->echo.software || member->dsp != dsp) {
1950
nskb = skb_clone(skb, GFP_ATOMIC);
1951
if (nskb) {
1952
hh = mISDN_HEAD_P(nskb);
1953
hh->prim = PH_DATA_REQ;
1954
hh->id = 0;
1955
skb_queue_tail(&member->dsp->sendq, nskb);
1956
schedule_work(&member->dsp->workq);
1957
}
1958
}
1959
}
1960
}
1961
1962
1963
1964