diff options
| -rw-r--r-- | drivers/isdn/mISDN/Kconfig | 18 | ||||
| -rw-r--r-- | drivers/isdn/mISDN/Makefile | 2 | ||||
| -rw-r--r-- | drivers/isdn/mISDN/dsp.h | 263 | ||||
| -rw-r--r-- | drivers/isdn/mISDN/dsp_audio.c | 434 | ||||
| -rw-r--r-- | drivers/isdn/mISDN/dsp_biquad.h | 65 | ||||
| -rw-r--r-- | drivers/isdn/mISDN/dsp_blowfish.c | 672 | ||||
| -rw-r--r-- | drivers/isdn/mISDN/dsp_cmx.c | 1886 | ||||
| -rw-r--r-- | drivers/isdn/mISDN/dsp_core.c | 1191 | ||||
| -rw-r--r-- | drivers/isdn/mISDN/dsp_dtmf.c | 303 | ||||
| -rw-r--r-- | drivers/isdn/mISDN/dsp_ecdis.h | 110 | ||||
| -rw-r--r-- | drivers/isdn/mISDN/dsp_hwec.c | 138 | ||||
| -rw-r--r-- | drivers/isdn/mISDN/dsp_hwec.h | 10 | ||||
| -rw-r--r-- | drivers/isdn/mISDN/dsp_pipeline.c | 348 | ||||
| -rw-r--r-- | drivers/isdn/mISDN/dsp_tones.c | 551 | ||||
| -rw-r--r-- | include/linux/mISDNdsp.h | 37 |
15 files changed, 6028 insertions, 0 deletions
diff --git a/drivers/isdn/mISDN/Kconfig b/drivers/isdn/mISDN/Kconfig index 231bd0d08316..6a97e86e7f21 100644 --- a/drivers/isdn/mISDN/Kconfig +++ b/drivers/isdn/mISDN/Kconfig | |||
| @@ -7,3 +7,21 @@ menuconfig MISDN | |||
| 7 | help | 7 | help |
| 8 | Enable support for the modular ISDN driver. | 8 | Enable support for the modular ISDN driver. |
| 9 | 9 | ||
| 10 | if MISDN != n | ||
| 11 | |||
| 12 | config MISDN_DSP | ||
| 13 | tristate "Digital Audio Processing of transparent data" | ||
| 14 | depends on MISDN | ||
| 15 | help | ||
| 16 | Enable support for digital audio processing capability. | ||
| 17 | This module may be used for special applications that require | ||
| 18 | cross connecting of bchannels, conferencing, dtmf decoding | ||
| 19 | echo cancelation, tone generation, and Blowfish encryption and | ||
| 20 | decryption. | ||
| 21 | It may use hardware features if available. | ||
| 22 | E.g. it is required for PBX4Linux. Go to http://isdn.eversberg.eu | ||
| 23 | and get more informations about this module and it's usage. | ||
| 24 | If unsure, say 'N'. | ||
| 25 | |||
| 26 | source "drivers/isdn/hardware/mISDN/Kconfig" | ||
| 27 | endif #MISDN | ||
diff --git a/drivers/isdn/mISDN/Makefile b/drivers/isdn/mISDN/Makefile index 87c563d33612..7f1a21804208 100644 --- a/drivers/isdn/mISDN/Makefile +++ b/drivers/isdn/mISDN/Makefile | |||
| @@ -3,7 +3,9 @@ | |||
| 3 | # | 3 | # |
| 4 | 4 | ||
| 5 | obj-$(CONFIG_MISDN) += mISDN_core.o | 5 | obj-$(CONFIG_MISDN) += mISDN_core.o |
| 6 | obj-$(CONFIG_MISDN_DSP) += mISDN_dsp.o | ||
| 6 | 7 | ||
| 7 | # multi objects | 8 | # multi objects |
| 8 | 9 | ||
| 9 | mISDN_core-objs := core.o fsm.o socket.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o | 10 | mISDN_core-objs := core.o fsm.o socket.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o |
| 11 | mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_pipeline.o dsp_hwec.o | ||
diff --git a/drivers/isdn/mISDN/dsp.h b/drivers/isdn/mISDN/dsp.h new file mode 100644 index 000000000000..6c3fed6b8d4f --- /dev/null +++ b/drivers/isdn/mISDN/dsp.h | |||
| @@ -0,0 +1,263 @@ | |||
| 1 | /* | ||
| 2 | * Audio support data for ISDN4Linux. | ||
| 3 | * | ||
| 4 | * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu) | ||
| 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 | #define DEBUG_DSP_CTRL 0x0001 | ||
| 12 | #define DEBUG_DSP_CORE 0x0002 | ||
| 13 | #define DEBUG_DSP_DTMF 0x0004 | ||
| 14 | #define DEBUG_DSP_CMX 0x0010 | ||
| 15 | #define DEBUG_DSP_TONE 0x0020 | ||
| 16 | #define DEBUG_DSP_BLOWFISH 0x0040 | ||
| 17 | #define DEBUG_DSP_DELAY 0x0100 | ||
| 18 | #define DEBUG_DSP_DTMFCOEFF 0x8000 /* heavy output */ | ||
| 19 | |||
| 20 | /* options may be: | ||
| 21 | * | ||
| 22 | * bit 0 = use ulaw instead of alaw | ||
| 23 | * bit 1 = enable hfc hardware accelleration for all channels | ||
| 24 | * | ||
| 25 | */ | ||
| 26 | #define DSP_OPT_ULAW (1<<0) | ||
| 27 | #define DSP_OPT_NOHARDWARE (1<<1) | ||
| 28 | |||
| 29 | #include <linux/timer.h> | ||
| 30 | #include <linux/workqueue.h> | ||
| 31 | |||
| 32 | #include "dsp_ecdis.h" | ||
| 33 | |||
| 34 | extern int dsp_options; | ||
| 35 | extern int dsp_debug; | ||
| 36 | extern int dsp_poll; | ||
| 37 | extern int dsp_tics; | ||
| 38 | extern spinlock_t dsp_lock; | ||
| 39 | extern struct work_struct dsp_workq; | ||
| 40 | extern u32 dsp_poll_diff; /* calculated fix-comma corrected poll value */ | ||
| 41 | |||
| 42 | /*************** | ||
| 43 | * audio stuff * | ||
| 44 | ***************/ | ||
| 45 | |||
| 46 | extern s32 dsp_audio_alaw_to_s32[256]; | ||
| 47 | extern s32 dsp_audio_ulaw_to_s32[256]; | ||
| 48 | extern s32 *dsp_audio_law_to_s32; | ||
| 49 | extern u8 dsp_audio_s16_to_law[65536]; | ||
| 50 | extern u8 dsp_audio_alaw_to_ulaw[256]; | ||
| 51 | extern u8 dsp_audio_mix_law[65536]; | ||
| 52 | extern u8 dsp_audio_seven2law[128]; | ||
| 53 | extern u8 dsp_audio_law2seven[256]; | ||
| 54 | extern void dsp_audio_generate_law_tables(void); | ||
| 55 | extern void dsp_audio_generate_s2law_table(void); | ||
| 56 | extern void dsp_audio_generate_seven(void); | ||
| 57 | extern void dsp_audio_generate_mix_table(void); | ||
| 58 | extern void dsp_audio_generate_ulaw_samples(void); | ||
| 59 | extern void dsp_audio_generate_volume_changes(void); | ||
| 60 | extern u8 dsp_silence; | ||
| 61 | |||
| 62 | |||
| 63 | /************* | ||
| 64 | * cmx stuff * | ||
| 65 | *************/ | ||
| 66 | |||
| 67 | #define MAX_POLL 256 /* maximum number of send-chunks */ | ||
| 68 | |||
| 69 | #define CMX_BUFF_SIZE 0x8000 /* must be 2**n (0x1000 about 1/2 second) */ | ||
| 70 | #define CMX_BUFF_HALF 0x4000 /* CMX_BUFF_SIZE / 2 */ | ||
| 71 | #define CMX_BUFF_MASK 0x7fff /* CMX_BUFF_SIZE - 1 */ | ||
| 72 | |||
| 73 | /* how many seconds will we check the lowest delay until the jitter buffer | ||
| 74 | is reduced by that delay */ | ||
| 75 | #define MAX_SECONDS_JITTER_CHECK 5 | ||
| 76 | |||
| 77 | extern struct timer_list dsp_spl_tl; | ||
| 78 | extern u32 dsp_spl_jiffies; | ||
| 79 | |||
| 80 | /* the structure of conferences: | ||
| 81 | * | ||
| 82 | * each conference has a unique number, given by user space. | ||
| 83 | * the conferences are linked in a chain. | ||
| 84 | * each conference has members linked in a chain. | ||
| 85 | * each dsplayer points to a member, each member points to a dsplayer. | ||
| 86 | */ | ||
| 87 | |||
| 88 | /* all members within a conference (this is linked 1:1 with the dsp) */ | ||
| 89 | struct dsp; | ||
| 90 | struct dsp_conf_member { | ||
| 91 | struct list_head list; | ||
| 92 | struct dsp *dsp; | ||
| 93 | }; | ||
| 94 | |||
| 95 | /* the list of all conferences */ | ||
| 96 | struct dsp_conf { | ||
| 97 | struct list_head list; | ||
| 98 | u32 id; | ||
| 99 | /* all cmx stacks with the same ID are | ||
| 100 | connected */ | ||
| 101 | struct list_head mlist; | ||
| 102 | int software; /* conf is processed by software */ | ||
| 103 | int hardware; /* conf is processed by hardware */ | ||
| 104 | /* note: if both unset, has only one member */ | ||
| 105 | }; | ||
| 106 | |||
| 107 | |||
| 108 | /************** | ||
| 109 | * DTMF stuff * | ||
| 110 | **************/ | ||
| 111 | |||
| 112 | #define DSP_DTMF_NPOINTS 102 | ||
| 113 | |||
| 114 | #define ECHOCAN_BUFLEN (4*128) | ||
| 115 | |||
| 116 | struct dsp_dtmf { | ||
| 117 | int treshold; /* above this is dtmf (square of) */ | ||
| 118 | int software; /* dtmf uses software decoding */ | ||
| 119 | int hardware; /* dtmf uses hardware decoding */ | ||
| 120 | int size; /* number of bytes in buffer */ | ||
| 121 | signed short buffer[DSP_DTMF_NPOINTS]; | ||
| 122 | /* buffers one full dtmf frame */ | ||
| 123 | u8 lastwhat, lastdigit; | ||
| 124 | int count; | ||
| 125 | u8 digits[16]; /* just the dtmf result */ | ||
| 126 | }; | ||
| 127 | |||
| 128 | |||
| 129 | /****************** | ||
| 130 | * pipeline stuff * | ||
| 131 | ******************/ | ||
| 132 | struct dsp_pipeline { | ||
| 133 | rwlock_t lock; | ||
| 134 | struct list_head list; | ||
| 135 | int inuse; | ||
| 136 | }; | ||
| 137 | |||
| 138 | /*************** | ||
| 139 | * tones stuff * | ||
| 140 | ***************/ | ||
| 141 | |||
| 142 | struct dsp_tone { | ||
| 143 | int software; /* tones are generated by software */ | ||
| 144 | int hardware; /* tones are generated by hardware */ | ||
| 145 | int tone; | ||
| 146 | void *pattern; | ||
| 147 | int count; | ||
| 148 | int index; | ||
| 149 | struct timer_list tl; | ||
| 150 | }; | ||
| 151 | |||
| 152 | /***************** | ||
| 153 | * general stuff * | ||
| 154 | *****************/ | ||
| 155 | |||
| 156 | struct dsp { | ||
| 157 | struct list_head list; | ||
| 158 | struct mISDNchannel ch; | ||
| 159 | struct mISDNchannel *up; | ||
| 160 | unsigned char name[64]; | ||
| 161 | int b_active; | ||
| 162 | int echo; /* echo is enabled */ | ||
| 163 | int rx_disabled; /* what the user wants */ | ||
| 164 | int rx_is_off; /* what the card is */ | ||
| 165 | int tx_mix; | ||
| 166 | struct dsp_tone tone; | ||
| 167 | struct dsp_dtmf dtmf; | ||
| 168 | int tx_volume, rx_volume; | ||
| 169 | |||
| 170 | /* queue for sending frames */ | ||
| 171 | struct work_struct workq; | ||
| 172 | struct sk_buff_head sendq; | ||
| 173 | int hdlc; /* if mode is hdlc */ | ||
| 174 | int data_pending; /* currently an unconfirmed frame */ | ||
| 175 | |||
| 176 | /* conference stuff */ | ||
| 177 | u32 conf_id; | ||
| 178 | struct dsp_conf *conf; | ||
| 179 | struct dsp_conf_member | ||
| 180 | *member; | ||
| 181 | |||
| 182 | /* buffer stuff */ | ||
| 183 | int rx_W; /* current write pos for data without timestamp */ | ||
| 184 | int rx_R; /* current read pos for transmit clock */ | ||
| 185 | int rx_init; /* if set, pointers will be adjusted first */ | ||
| 186 | int tx_W; /* current write pos for transmit data */ | ||
| 187 | int tx_R; /* current read pos for transmit clock */ | ||
| 188 | int rx_delay[MAX_SECONDS_JITTER_CHECK]; | ||
| 189 | int tx_delay[MAX_SECONDS_JITTER_CHECK]; | ||
| 190 | u8 tx_buff[CMX_BUFF_SIZE]; | ||
| 191 | u8 rx_buff[CMX_BUFF_SIZE]; | ||
| 192 | int last_tx; /* if set, we transmitted last poll interval */ | ||
| 193 | int cmx_delay; /* initial delay of buffers, | ||
| 194 | or 0 for dynamic jitter buffer */ | ||
| 195 | int tx_dejitter; /* if set, dejitter tx buffer */ | ||
| 196 | int tx_data; /* enables tx-data of CMX to upper layer */ | ||
| 197 | |||
| 198 | /* hardware stuff */ | ||
| 199 | struct dsp_features features; | ||
| 200 | int features_rx_off; /* set if rx_off is featured */ | ||
| 201 | int pcm_slot_rx; /* current PCM slot (or -1) */ | ||
| 202 | int pcm_bank_rx; | ||
| 203 | int pcm_slot_tx; | ||
| 204 | int pcm_bank_tx; | ||
| 205 | int hfc_conf; /* unique id of current conference (or -1) */ | ||
| 206 | |||
| 207 | /* encryption stuff */ | ||
| 208 | int bf_enable; | ||
| 209 | u32 bf_p[18]; | ||
| 210 | u32 bf_s[1024]; | ||
| 211 | int bf_crypt_pos; | ||
| 212 | u8 bf_data_in[9]; | ||
| 213 | u8 bf_crypt_out[9]; | ||
| 214 | int bf_decrypt_in_pos; | ||
| 215 | int bf_decrypt_out_pos; | ||
| 216 | u8 bf_crypt_inring[16]; | ||
| 217 | u8 bf_data_out[9]; | ||
| 218 | int bf_sync; | ||
| 219 | |||
| 220 | struct dsp_pipeline | ||
| 221 | pipeline; | ||
| 222 | }; | ||
| 223 | |||
| 224 | /* functions */ | ||
| 225 | |||
| 226 | extern void dsp_change_volume(struct sk_buff *skb, int volume); | ||
| 227 | |||
| 228 | extern struct list_head dsp_ilist; | ||
| 229 | extern struct list_head conf_ilist; | ||
| 230 | extern void dsp_cmx_debug(struct dsp *dsp); | ||
| 231 | extern void dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp); | ||
| 232 | extern int dsp_cmx_conf(struct dsp *dsp, u32 conf_id); | ||
| 233 | extern void dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb); | ||
| 234 | extern void dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb); | ||
| 235 | extern void dsp_cmx_send(void *arg); | ||
| 236 | extern void dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb); | ||
| 237 | extern int dsp_cmx_del_conf_member(struct dsp *dsp); | ||
| 238 | extern int dsp_cmx_del_conf(struct dsp_conf *conf); | ||
| 239 | |||
| 240 | extern void dsp_dtmf_goertzel_init(struct dsp *dsp); | ||
| 241 | extern void dsp_dtmf_hardware(struct dsp *dsp); | ||
| 242 | extern u8 *dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len, | ||
| 243 | int fmt); | ||
| 244 | |||
| 245 | extern int dsp_tone(struct dsp *dsp, int tone); | ||
| 246 | extern void dsp_tone_copy(struct dsp *dsp, u8 *data, int len); | ||
| 247 | extern void dsp_tone_timeout(void *arg); | ||
| 248 | |||
| 249 | extern void dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len); | ||
| 250 | extern void dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len); | ||
| 251 | extern int dsp_bf_init(struct dsp *dsp, const u8 *key, unsigned int keylen); | ||
| 252 | extern void dsp_bf_cleanup(struct dsp *dsp); | ||
| 253 | |||
| 254 | extern int dsp_pipeline_module_init(void); | ||
| 255 | extern void dsp_pipeline_module_exit(void); | ||
| 256 | extern int dsp_pipeline_init(struct dsp_pipeline *pipeline); | ||
| 257 | extern void dsp_pipeline_destroy(struct dsp_pipeline *pipeline); | ||
| 258 | extern int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg); | ||
| 259 | extern void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, | ||
| 260 | int len); | ||
| 261 | extern void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, | ||
| 262 | int len); | ||
| 263 | |||
diff --git a/drivers/isdn/mISDN/dsp_audio.c b/drivers/isdn/mISDN/dsp_audio.c new file mode 100644 index 000000000000..1c2dd5694773 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_audio.c | |||
| @@ -0,0 +1,434 @@ | |||
| 1 | /* | ||
| 2 | * Audio support data for mISDN_dsp. | ||
| 3 | * | ||
| 4 | * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu) | ||
| 5 | * Rewritten by Peter | ||
| 6 | * | ||
| 7 | * This software may be used and distributed according to the terms | ||
| 8 | * of the GNU General Public License, incorporated herein by reference. | ||
| 9 | * | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/delay.h> | ||
| 13 | #include <linux/mISDNif.h> | ||
| 14 | #include <linux/mISDNdsp.h> | ||
| 15 | #include "core.h" | ||
| 16 | #include "dsp.h" | ||
| 17 | |||
| 18 | /* ulaw[unsigned char] -> signed 16-bit */ | ||
| 19 | s32 dsp_audio_ulaw_to_s32[256]; | ||
| 20 | /* alaw[unsigned char] -> signed 16-bit */ | ||
| 21 | s32 dsp_audio_alaw_to_s32[256]; | ||
| 22 | |||
| 23 | s32 *dsp_audio_law_to_s32; | ||
| 24 | EXPORT_SYMBOL(dsp_audio_law_to_s32); | ||
| 25 | |||
| 26 | /* signed 16-bit -> law */ | ||
| 27 | u8 dsp_audio_s16_to_law[65536]; | ||
| 28 | EXPORT_SYMBOL(dsp_audio_s16_to_law); | ||
| 29 | |||
| 30 | /* alaw -> ulaw */ | ||
| 31 | u8 dsp_audio_alaw_to_ulaw[256]; | ||
| 32 | /* ulaw -> alaw */ | ||
| 33 | u8 dsp_audio_ulaw_to_alaw[256]; | ||
| 34 | u8 dsp_silence; | ||
| 35 | |||
| 36 | |||
| 37 | /***************************************************** | ||
| 38 | * generate table for conversion of s16 to alaw/ulaw * | ||
| 39 | *****************************************************/ | ||
| 40 | |||
| 41 | #define AMI_MASK 0x55 | ||
| 42 | |||
| 43 | static inline unsigned char linear2alaw(short int linear) | ||
| 44 | { | ||
| 45 | int mask; | ||
| 46 | int seg; | ||
| 47 | int pcm_val; | ||
| 48 | static int seg_end[8] = { | ||
| 49 | 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF | ||
| 50 | }; | ||
| 51 | |||
| 52 | pcm_val = linear; | ||
| 53 | if (pcm_val >= 0) { | ||
| 54 | /* Sign (7th) bit = 1 */ | ||
| 55 | mask = AMI_MASK | 0x80; | ||
| 56 | } else { | ||
| 57 | /* Sign bit = 0 */ | ||
| 58 | mask = AMI_MASK; | ||
| 59 | pcm_val = -pcm_val; | ||
| 60 | } | ||
| 61 | |||
| 62 | /* Convert the scaled magnitude to segment number. */ | ||
| 63 | for (seg = 0; seg < 8; seg++) { | ||
| 64 | if (pcm_val <= seg_end[seg]) | ||
| 65 | break; | ||
| 66 | } | ||
| 67 | /* Combine the sign, segment, and quantization bits. */ | ||
| 68 | return ((seg << 4) | | ||
| 69 | ((pcm_val >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask; | ||
| 70 | } | ||
| 71 | |||
| 72 | |||
| 73 | static inline short int alaw2linear(unsigned char alaw) | ||
| 74 | { | ||
| 75 | int i; | ||
| 76 | int seg; | ||
| 77 | |||
| 78 | alaw ^= AMI_MASK; | ||
| 79 | i = ((alaw & 0x0F) << 4) + 8 /* rounding error */; | ||
| 80 | seg = (((int) alaw & 0x70) >> 4); | ||
| 81 | if (seg) | ||
| 82 | i = (i + 0x100) << (seg - 1); | ||
| 83 | return (short int) ((alaw & 0x80) ? i : -i); | ||
| 84 | } | ||
| 85 | |||
| 86 | static inline short int ulaw2linear(unsigned char ulaw) | ||
| 87 | { | ||
| 88 | short mu, e, f, y; | ||
| 89 | static short etab[] = {0, 132, 396, 924, 1980, 4092, 8316, 16764}; | ||
| 90 | |||
| 91 | mu = 255 - ulaw; | ||
| 92 | e = (mu & 0x70) / 16; | ||
| 93 | f = mu & 0x0f; | ||
| 94 | y = f * (1 << (e + 3)); | ||
| 95 | y += etab[e]; | ||
| 96 | if (mu & 0x80) | ||
| 97 | y = -y; | ||
| 98 | return y; | ||
| 99 | } | ||
| 100 | |||
| 101 | #define BIAS 0x84 /*!< define the add-in bias for 16 bit samples */ | ||
| 102 | |||
| 103 | static unsigned char linear2ulaw(short sample) | ||
| 104 | { | ||
| 105 | static int exp_lut[256] = { | ||
| 106 | 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, | ||
| 107 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, | ||
| 108 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, | ||
| 109 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, | ||
| 110 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, | ||
| 111 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, | ||
| 112 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, | ||
| 113 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, | ||
| 114 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | ||
| 115 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | ||
| 116 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | ||
| 117 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | ||
| 118 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | ||
| 119 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | ||
| 120 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | ||
| 121 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; | ||
| 122 | int sign, exponent, mantissa; | ||
| 123 | unsigned char ulawbyte; | ||
| 124 | |||
| 125 | /* Get the sample into sign-magnitude. */ | ||
| 126 | sign = (sample >> 8) & 0x80; /* set aside the sign */ | ||
| 127 | if (sign != 0) | ||
| 128 | sample = -sample; /* get magnitude */ | ||
| 129 | |||
| 130 | /* Convert from 16 bit linear to ulaw. */ | ||
| 131 | sample = sample + BIAS; | ||
| 132 | exponent = exp_lut[(sample >> 7) & 0xFF]; | ||
| 133 | mantissa = (sample >> (exponent + 3)) & 0x0F; | ||
| 134 | ulawbyte = ~(sign | (exponent << 4) | mantissa); | ||
| 135 | |||
| 136 | return ulawbyte; | ||
| 137 | } | ||
| 138 | |||
| 139 | static int reverse_bits(int i) | ||
| 140 | { | ||
| 141 | int z, j; | ||
| 142 | z = 0; | ||
| 143 | |||
| 144 | for (j = 0; j < 8; j++) { | ||
| 145 | if ((i & (1 << j)) != 0) | ||
| 146 | z |= 1 << (7 - j); | ||
| 147 | } | ||
| 148 | return z; | ||
| 149 | } | ||
| 150 | |||
| 151 | |||
| 152 | void dsp_audio_generate_law_tables(void) | ||
| 153 | { | ||
| 154 | int i; | ||
| 155 | for (i = 0; i < 256; i++) | ||
| 156 | dsp_audio_alaw_to_s32[i] = alaw2linear(reverse_bits(i)); | ||
| 157 | |||
| 158 | for (i = 0; i < 256; i++) | ||
| 159 | dsp_audio_ulaw_to_s32[i] = ulaw2linear(reverse_bits(i)); | ||
| 160 | |||
| 161 | for (i = 0; i < 256; i++) { | ||
| 162 | dsp_audio_alaw_to_ulaw[i] = | ||
| 163 | linear2ulaw(dsp_audio_alaw_to_s32[i]); | ||
| 164 | dsp_audio_ulaw_to_alaw[i] = | ||
| 165 | linear2alaw(dsp_audio_ulaw_to_s32[i]); | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | void | ||
| 170 | dsp_audio_generate_s2law_table(void) | ||
| 171 | { | ||
| 172 | int i; | ||
| 173 | |||
| 174 | if (dsp_options & DSP_OPT_ULAW) { | ||
| 175 | /* generating ulaw-table */ | ||
| 176 | for (i = -32768; i < 32768; i++) { | ||
| 177 | dsp_audio_s16_to_law[i & 0xffff] = | ||
| 178 | reverse_bits(linear2ulaw(i)); | ||
| 179 | } | ||
| 180 | } else { | ||
| 181 | /* generating alaw-table */ | ||
| 182 | for (i = -32768; i < 32768; i++) { | ||
| 183 | dsp_audio_s16_to_law[i & 0xffff] = | ||
| 184 | reverse_bits(linear2alaw(i)); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | |||
| 190 | /* | ||
| 191 | * the seven bit sample is the number of every second alaw-sample ordered by | ||
| 192 | * aplitude. 0x00 is negative, 0x7f is positive amplitude. | ||
| 193 | */ | ||
| 194 | u8 dsp_audio_seven2law[128]; | ||
| 195 | u8 dsp_audio_law2seven[256]; | ||
| 196 | |||
| 197 | /******************************************************************** | ||
| 198 | * generate table for conversion law from/to 7-bit alaw-like sample * | ||
| 199 | ********************************************************************/ | ||
| 200 | |||
| 201 | void | ||
| 202 | dsp_audio_generate_seven(void) | ||
| 203 | { | ||
| 204 | int i, j, k; | ||
| 205 | u8 spl; | ||
| 206 | u8 sorted_alaw[256]; | ||
| 207 | |||
| 208 | /* generate alaw table, sorted by the linear value */ | ||
| 209 | for (i = 0; i < 256; i++) { | ||
| 210 | j = 0; | ||
| 211 | for (k = 0; k < 256; k++) { | ||
| 212 | if (dsp_audio_alaw_to_s32[k] | ||
| 213 | < dsp_audio_alaw_to_s32[i]) { | ||
| 214 | j++; | ||
| 215 | } | ||
| 216 | } | ||
| 217 | sorted_alaw[j] = i; | ||
| 218 | } | ||
| 219 | |||
| 220 | /* generate tabels */ | ||
| 221 | for (i = 0; i < 256; i++) { | ||
| 222 | /* spl is the source: the law-sample (converted to alaw) */ | ||
| 223 | spl = i; | ||
| 224 | if (dsp_options & DSP_OPT_ULAW) | ||
| 225 | spl = dsp_audio_ulaw_to_alaw[i]; | ||
| 226 | /* find the 7-bit-sample */ | ||
| 227 | for (j = 0; j < 256; j++) { | ||
| 228 | if (sorted_alaw[j] == spl) | ||
| 229 | break; | ||
| 230 | } | ||
| 231 | /* write 7-bit audio value */ | ||
| 232 | dsp_audio_law2seven[i] = j >> 1; | ||
| 233 | } | ||
| 234 | for (i = 0; i < 128; i++) { | ||
| 235 | spl = sorted_alaw[i << 1]; | ||
| 236 | if (dsp_options & DSP_OPT_ULAW) | ||
| 237 | spl = dsp_audio_alaw_to_ulaw[spl]; | ||
| 238 | dsp_audio_seven2law[i] = spl; | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | |||
| 243 | /* mix 2*law -> law */ | ||
| 244 | u8 dsp_audio_mix_law[65536]; | ||
| 245 | |||
| 246 | /****************************************************** | ||
| 247 | * generate mix table to mix two law samples into one * | ||
| 248 | ******************************************************/ | ||
| 249 | |||
| 250 | void | ||
| 251 | dsp_audio_generate_mix_table(void) | ||
| 252 | { | ||
| 253 | int i, j; | ||
| 254 | s32 sample; | ||
| 255 | |||
| 256 | i = 0; | ||
| 257 | while (i < 256) { | ||
| 258 | j = 0; | ||
| 259 | while (j < 256) { | ||
| 260 | sample = dsp_audio_law_to_s32[i]; | ||
| 261 | sample += dsp_audio_law_to_s32[j]; | ||
| 262 | if (sample > 32767) | ||
| 263 | sample = 32767; | ||
| 264 | if (sample < -32768) | ||
| 265 | sample = -32768; | ||
| 266 | dsp_audio_mix_law[(i<<8)|j] = | ||
| 267 | dsp_audio_s16_to_law[sample & 0xffff]; | ||
| 268 | j++; | ||
| 269 | } | ||
| 270 | i++; | ||
| 271 | } | ||
| 272 | } | ||
| 273 | |||
| 274 | |||
| 275 | /************************************* | ||
| 276 | * generate different volume changes * | ||
| 277 | *************************************/ | ||
| 278 | |||
| 279 | static u8 dsp_audio_reduce8[256]; | ||
| 280 | static u8 dsp_audio_reduce7[256]; | ||
| 281 | static u8 dsp_audio_reduce6[256]; | ||
| 282 | static u8 dsp_audio_reduce5[256]; | ||
| 283 | static u8 dsp_audio_reduce4[256]; | ||
| 284 | static u8 dsp_audio_reduce3[256]; | ||
| 285 | static u8 dsp_audio_reduce2[256]; | ||
| 286 | static u8 dsp_audio_reduce1[256]; | ||
| 287 | static u8 dsp_audio_increase1[256]; | ||
| 288 | static u8 dsp_audio_increase2[256]; | ||
| 289 | static u8 dsp_audio_increase3[256]; | ||
| 290 | static u8 dsp_audio_increase4[256]; | ||
| 291 | static u8 dsp_audio_increase5[256]; | ||
| 292 | static u8 dsp_audio_increase6[256]; | ||
| 293 | static u8 dsp_audio_increase7[256]; | ||
| 294 | static u8 dsp_audio_increase8[256]; | ||
| 295 | |||
| 296 | static u8 *dsp_audio_volume_change[16] = { | ||
| 297 | dsp_audio_reduce8, | ||
| 298 | dsp_audio_reduce7, | ||
| 299 | dsp_audio_reduce6, | ||
| 300 | dsp_audio_reduce5, | ||
| 301 | dsp_audio_reduce4, | ||
| 302 | dsp_audio_reduce3, | ||
| 303 | dsp_audio_reduce2, | ||
| 304 | dsp_audio_reduce1, | ||
| 305 | dsp_audio_increase1, | ||
| 306 | dsp_audio_increase2, | ||
| 307 | dsp_audio_increase3, | ||
| 308 | dsp_audio_increase4, | ||
| 309 | dsp_audio_increase5, | ||
| 310 | dsp_audio_increase6, | ||
| 311 | dsp_audio_increase7, | ||
| 312 | dsp_audio_increase8, | ||
| 313 | }; | ||
| 314 | |||
| 315 | void | ||
| 316 | dsp_audio_generate_volume_changes(void) | ||
| 317 | { | ||
| 318 | register s32 sample; | ||
| 319 | int i; | ||
| 320 | int num[] = { 110, 125, 150, 175, 200, 300, 400, 500 }; | ||
| 321 | int denum[] = { 100, 100, 100, 100, 100, 100, 100, 100 }; | ||
| 322 | |||
| 323 | i = 0; | ||
| 324 | while (i < 256) { | ||
| 325 | dsp_audio_reduce8[i] = dsp_audio_s16_to_law[ | ||
| 326 | (dsp_audio_law_to_s32[i] * denum[7] / num[7]) & 0xffff]; | ||
| 327 | dsp_audio_reduce7[i] = dsp_audio_s16_to_law[ | ||
| 328 | (dsp_audio_law_to_s32[i] * denum[6] / num[6]) & 0xffff]; | ||
| 329 | dsp_audio_reduce6[i] = dsp_audio_s16_to_law[ | ||
| 330 | (dsp_audio_law_to_s32[i] * denum[5] / num[5]) & 0xffff]; | ||
| 331 | dsp_audio_reduce5[i] = dsp_audio_s16_to_law[ | ||
| 332 | (dsp_audio_law_to_s32[i] * denum[4] / num[4]) & 0xffff]; | ||
| 333 | dsp_audio_reduce4[i] = dsp_audio_s16_to_law[ | ||
| 334 | (dsp_audio_law_to_s32[i] * denum[3] / num[3]) & 0xffff]; | ||
| 335 | dsp_audio_reduce3[i] = dsp_audio_s16_to_law[ | ||
| 336 | (dsp_audio_law_to_s32[i] * denum[2] / num[2]) & 0xffff]; | ||
| 337 | dsp_audio_reduce2[i] = dsp_audio_s16_to_law[ | ||
| 338 | (dsp_audio_law_to_s32[i] * denum[1] / num[1]) & 0xffff]; | ||
| 339 | dsp_audio_reduce1[i] = dsp_audio_s16_to_law[ | ||
| 340 | (dsp_audio_law_to_s32[i] * denum[0] / num[0]) & 0xffff]; | ||
| 341 | sample = dsp_audio_law_to_s32[i] * num[0] / denum[0]; | ||
| 342 | if (sample < -32768) | ||
| 343 | sample = -32768; | ||
| 344 | else if (sample > 32767) | ||
| 345 | sample = 32767; | ||
| 346 | dsp_audio_increase1[i] = dsp_audio_s16_to_law[sample & 0xffff]; | ||
| 347 | sample = dsp_audio_law_to_s32[i] * num[1] / denum[1]; | ||
| 348 | if (sample < -32768) | ||
| 349 | sample = -32768; | ||
| 350 | else if (sample > 32767) | ||
| 351 | sample = 32767; | ||
| 352 | dsp_audio_increase2[i] = dsp_audio_s16_to_law[sample & 0xffff]; | ||
| 353 | sample = dsp_audio_law_to_s32[i] * num[2] / denum[2]; | ||
| 354 | if (sample < -32768) | ||
| 355 | sample = -32768; | ||
| 356 | else if (sample > 32767) | ||
| 357 | sample = 32767; | ||
| 358 | dsp_audio_increase3[i] = dsp_audio_s16_to_law[sample & 0xffff]; | ||
| 359 | sample = dsp_audio_law_to_s32[i] * num[3] / denum[3]; | ||
| 360 | if (sample < -32768) | ||
| 361 | sample = -32768; | ||
| 362 | else if (sample > 32767) | ||
| 363 | sample = 32767; | ||
| 364 | dsp_audio_increase4[i] = dsp_audio_s16_to_law[sample & 0xffff]; | ||
| 365 | sample = dsp_audio_law_to_s32[i] * num[4] / denum[4]; | ||
| 366 | if (sample < -32768) | ||
| 367 | sample = -32768; | ||
| 368 | else if (sample > 32767) | ||
| 369 | sample = 32767; | ||
| 370 | dsp_audio_increase5[i] = dsp_audio_s16_to_law[sample & 0xffff]; | ||
| 371 | sample = dsp_audio_law_to_s32[i] * num[5] / denum[5]; | ||
| 372 | if (sample < -32768) | ||
| 373 | sample = -32768; | ||
| 374 | else if (sample > 32767) | ||
| 375 | sample = 32767; | ||
| 376 | dsp_audio_increase6[i] = dsp_audio_s16_to_law[sample & 0xffff]; | ||
| 377 | sample = dsp_audio_law_to_s32[i] * num[6] / denum[6]; | ||
| 378 | if (sample < -32768) | ||
| 379 | sample = -32768; | ||
| 380 | else if (sample > 32767) | ||
| 381 | sample = 32767; | ||
| 382 | dsp_audio_increase7[i] = dsp_audio_s16_to_law[sample & 0xffff]; | ||
| 383 | sample = dsp_audio_law_to_s32[i] * num[7] / denum[7]; | ||
| 384 | if (sample < -32768) | ||
| 385 | sample = -32768; | ||
| 386 | else if (sample > 32767) | ||
| 387 | sample = 32767; | ||
| 388 | dsp_audio_increase8[i] = dsp_audio_s16_to_law[sample & 0xffff]; | ||
| 389 | |||
| 390 | i++; | ||
| 391 | } | ||
| 392 | } | ||
| 393 | |||
| 394 | |||
| 395 | /************************************** | ||
| 396 | * change the volume of the given skb * | ||
| 397 | **************************************/ | ||
| 398 | |||
| 399 | /* this is a helper function for changing volume of skb. the range may be | ||
| 400 | * -8 to 8, which is a shift to the power of 2. 0 == no volume, 3 == volume*8 | ||
| 401 | */ | ||
| 402 | void | ||
| 403 | dsp_change_volume(struct sk_buff *skb, int volume) | ||
| 404 | { | ||
| 405 | u8 *volume_change; | ||
| 406 | int i, ii; | ||
| 407 | u8 *p; | ||
| 408 | int shift; | ||
| 409 | |||
| 410 | if (volume == 0) | ||
| 411 | return; | ||
| 412 | |||
| 413 | /* get correct conversion table */ | ||
| 414 | if (volume < 0) { | ||
| 415 | shift = volume + 8; | ||
| 416 | if (shift < 0) | ||
| 417 | shift = 0; | ||
| 418 | } else { | ||
| 419 | shift = volume + 7; | ||
| 420 | if (shift > 15) | ||
| 421 | shift = 15; | ||
| 422 | } | ||
| 423 | volume_change = dsp_audio_volume_change[shift]; | ||
| 424 | i = 0; | ||
| 425 | ii = skb->len; | ||
| 426 | p = skb->data; | ||
| 427 | /* change volume */ | ||
| 428 | while (i < ii) { | ||
| 429 | *p = volume_change[*p]; | ||
| 430 | p++; | ||
| 431 | i++; | ||
| 432 | } | ||
| 433 | } | ||
| 434 | |||
diff --git a/drivers/isdn/mISDN/dsp_biquad.h b/drivers/isdn/mISDN/dsp_biquad.h new file mode 100644 index 000000000000..038191bc45f5 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_biquad.h | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | /* | ||
| 2 | * SpanDSP - a series of DSP components for telephony | ||
| 3 | * | ||
| 4 | * biquad.h - General telephony bi-quad section routines (currently this just | ||
| 5 | * handles canonic/type 2 form) | ||
| 6 | * | ||
| 7 | * Written by Steve Underwood <steveu@coppice.org> | ||
| 8 | * | ||
| 9 | * Copyright (C) 2001 Steve Underwood | ||
| 10 | * | ||
| 11 | * All rights reserved. | ||
| 12 | * | ||
| 13 | * This program is free software; you can redistribute it and/or modify | ||
| 14 | * it under the terms of the GNU General Public License as published by | ||
| 15 | * the Free Software Foundation; either version 2 of the License, or | ||
| 16 | * (at your option) any later version. | ||
| 17 | * | ||
| 18 | * This program is distributed in the hope that it will be useful, | ||
| 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 21 | * GNU General Public License for more details. | ||
| 22 | * | ||
| 23 | * You should have received a copy of the GNU General Public License | ||
| 24 | * along with this program; if not, write to the Free Software | ||
| 25 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 26 | * | ||
| 27 | */ | ||
| 28 | |||
| 29 | struct biquad2_state { | ||
| 30 | int32_t gain; | ||
| 31 | int32_t a1; | ||
| 32 | int32_t a2; | ||
| 33 | int32_t b1; | ||
| 34 | int32_t b2; | ||
| 35 | |||
| 36 | int32_t z1; | ||
| 37 | int32_t z2; | ||
| 38 | }; | ||
| 39 | |||
| 40 | static inline void biquad2_init(struct biquad2_state *bq, | ||
| 41 | int32_t gain, int32_t a1, int32_t a2, int32_t b1, int32_t b2) | ||
| 42 | { | ||
| 43 | bq->gain = gain; | ||
| 44 | bq->a1 = a1; | ||
| 45 | bq->a2 = a2; | ||
| 46 | bq->b1 = b1; | ||
| 47 | bq->b2 = b2; | ||
| 48 | |||
| 49 | bq->z1 = 0; | ||
| 50 | bq->z2 = 0; | ||
| 51 | } | ||
| 52 | |||
| 53 | static inline int16_t biquad2(struct biquad2_state *bq, int16_t sample) | ||
| 54 | { | ||
| 55 | int32_t y; | ||
| 56 | int32_t z0; | ||
| 57 | |||
| 58 | z0 = sample*bq->gain + bq->z1*bq->a1 + bq->z2*bq->a2; | ||
| 59 | y = z0 + bq->z1*bq->b1 + bq->z2*bq->b2; | ||
| 60 | |||
| 61 | bq->z2 = bq->z1; | ||
| 62 | bq->z1 = z0 >> 15; | ||
| 63 | y >>= 15; | ||
| 64 | return y; | ||
| 65 | } | ||
diff --git a/drivers/isdn/mISDN/dsp_blowfish.c b/drivers/isdn/mISDN/dsp_blowfish.c new file mode 100644 index 000000000000..18e411e95bba --- /dev/null +++ b/drivers/isdn/mISDN/dsp_blowfish.c | |||
| @@ -0,0 +1,672 @@ | |||
| 1 | /* | ||
| 2 | * Blowfish encryption/decryption for mISDN_dsp. | ||
| 3 | * | ||
| 4 | * Copyright Andreas Eversberg (jolly@eversberg.eu) | ||
| 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 | #include <linux/mISDNif.h> | ||
| 12 | #include <linux/mISDNdsp.h> | ||
| 13 | #include "core.h" | ||
| 14 | #include "dsp.h" | ||
| 15 | |||
| 16 | /* | ||
| 17 | * how to encode a sample stream to 64-bit blocks that will be encryped | ||
| 18 | * | ||
| 19 | * first of all, data is collected until a block of 9 samples are received. | ||
| 20 | * of course, a packet may have much more than 9 sample, but is may have | ||
| 21 | * not excacly the multiple of 9 samples. if there is a rest, the next | ||
| 22 | * received data will complete the block. | ||
| 23 | * | ||
| 24 | * the block is then converted to 9 uLAW samples without the least sigificant | ||
| 25 | * bit. the result is a 7-bit encoded sample. | ||
| 26 | * | ||
| 27 | * the samples will be reoganised to form 8 bytes of data: | ||
| 28 | * (5(6) means: encoded sample no. 5, bit 6) | ||
| 29 | * | ||
| 30 | * 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) 0(0) 1(6) | ||
| 31 | * 1(5) 1(4) 1(3) 1(2) 1(1) 1(0) 2(6) 2(5) | ||
| 32 | * 2(4) 2(3) 2(2) 2(1) 2(0) 3(6) 3(5) 3(4) | ||
| 33 | * 3(3) 3(2) 3(1) 3(0) 4(6) 4(5) 4(4) 4(3) | ||
| 34 | * 4(2) 4(1) 4(0) 5(6) 5(5) 5(4) 5(3) 5(2) | ||
| 35 | * 5(1) 5(0) 6(6) 6(5) 6(4) 6(3) 6(2) 6(1) | ||
| 36 | * 6(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0) | ||
| 37 | * 8(6) 8(5) 8(4) 8(3) 8(2) 8(1) 8(0) | ||
| 38 | * | ||
| 39 | * the missing bit 0 of the last byte is filled with some | ||
| 40 | * random noise, to fill all 8 bytes. | ||
| 41 | * | ||
| 42 | * the 8 bytes will be encrypted using blowfish. | ||
| 43 | * | ||
| 44 | * the result will be converted into 9 bytes. the bit 7 is used for | ||
| 45 | * checksumme (CS) for sync (0, 1) and for the last bit: | ||
| 46 | * (5(6) means: crypted byte 5, bit 6) | ||
| 47 | * | ||
| 48 | * 1 0(7) 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) | ||
| 49 | * 0 0(0) 1(7) 1(6) 1(5) 1(4) 1(3) 1(2) | ||
| 50 | * 0 1(1) 1(0) 2(7) 2(6) 2(5) 2(4) 2(3) | ||
| 51 | * 0 2(2) 2(1) 2(0) 3(7) 3(6) 3(5) 3(4) | ||
| 52 | * 0 3(3) 3(2) 3(1) 3(0) 4(7) 4(6) 4(5) | ||
| 53 | * CS 4(4) 4(3) 4(2) 4(1) 4(0) 5(7) 5(6) | ||
| 54 | * CS 5(5) 5(4) 5(3) 5(2) 5(1) 5(0) 6(7) | ||
| 55 | * CS 6(6) 6(5) 6(4) 6(3) 6(2) 6(1) 6(0) | ||
| 56 | * 7(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0) | ||
| 57 | * | ||
| 58 | * the checksum is used to detect transmission errors and frame drops. | ||
| 59 | * | ||
| 60 | * synchronisation of received block is done by shifting the upper bit of each | ||
| 61 | * byte (bit 7) to a shift register. if the rigister has the first five bits | ||
| 62 | * (10000), this is used to find the sync. only if sync has been found, the | ||
| 63 | * current block of 9 received bytes are decrypted. before that the check | ||
| 64 | * sum is calculated. if it is incorrect the block is dropped. | ||
| 65 | * this will avoid loud noise due to corrupt encrypted data. | ||
| 66 | * | ||
| 67 | * if the last block is corrupt, the current decoded block is repeated | ||
| 68 | * until a valid block has been received. | ||
| 69 | */ | ||
| 70 | |||
| 71 | /* | ||
| 72 | * some blowfish parts are taken from the | ||
| 73 | * crypto-api for faster implementation | ||
| 74 | */ | ||
| 75 | |||
| 76 | struct bf_ctx { | ||
| 77 | u32 p[18]; | ||
| 78 | u32 s[1024]; | ||
| 79 | }; | ||
| 80 | |||
| 81 | static const u32 bf_pbox[16 + 2] = { | ||
| 82 | 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, | ||
| 83 | 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, | ||
| 84 | 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, | ||
| 85 | 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, | ||
| 86 | 0x9216d5d9, 0x8979fb1b, | ||
| 87 | }; | ||
| 88 | |||
| 89 | static const u32 bf_sbox[256 * 4] = { | ||
| 90 | 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, | ||
| 91 | 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, | ||
| 92 | 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, | ||
| 93 | 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, | ||
| 94 | 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, | ||
| 95 | 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, | ||
| 96 | 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, | ||
| 97 | 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, | ||
| 98 | 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, | ||
| 99 | 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, | ||
| 100 | 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, | ||
| 101 | 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, | ||
| 102 | 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, | ||
| 103 | 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, | ||
| 104 | 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, | ||
| 105 | 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, | ||
| 106 | 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, | ||
| 107 | 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, | ||
| 108 | 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, | ||
| 109 | 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, | ||
| 110 | 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, | ||
| 111 | 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, | ||
| 112 | 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, | ||
| 113 | 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, | ||
| 114 | 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, | ||
| 115 | 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, | ||
| 116 | 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, | ||
| 117 | 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, | ||
| 118 | 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, | ||
| 119 | 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, | ||
| 120 | 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, | ||
| 121 | 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, | ||
| 122 | 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, | ||
| 123 | 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, | ||
| 124 | 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, | ||
| 125 | 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, | ||
| 126 | 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, | ||
| 127 | 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, | ||
| 128 | 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, | ||
| 129 | 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, | ||
| 130 | 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, | ||
| 131 | 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, | ||
| 132 | 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, | ||
| 133 | 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, | ||
| 134 | 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, | ||
| 135 | 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, | ||
| 136 | 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, | ||
| 137 | 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, | ||
| 138 | 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, | ||
| 139 | 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, | ||
| 140 | 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, | ||
| 141 | 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, | ||
| 142 | 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, | ||
| 143 | 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, | ||
| 144 | 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, | ||
| 145 | 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, | ||
| 146 | 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, | ||
| 147 | 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, | ||
| 148 | 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, | ||
| 149 | 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, | ||
| 150 | 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, | ||
| 151 | 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, | ||
| 152 | 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, | ||
| 153 | 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, | ||
| 154 | 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, | ||
| 155 | 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, | ||
| 156 | 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, | ||
| 157 | 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, | ||
| 158 | 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, | ||
| 159 | 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, | ||
| 160 | 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, | ||
| 161 | 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, | ||
| 162 | 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, | ||
| 163 | 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, | ||
| 164 | 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, | ||
| 165 | 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, | ||
| 166 | 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, | ||
| 167 | 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, | ||
| 168 | 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, | ||
| 169 | 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, | ||
| 170 | 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, | ||
| 171 | 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, | ||
| 172 | 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, | ||
| 173 | 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, | ||
| 174 | 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, | ||
| 175 | 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, | ||
| 176 | 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, | ||
| 177 | 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, | ||
| 178 | 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, | ||
| 179 | 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, | ||
| 180 | 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, | ||
| 181 | 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, | ||
| 182 | 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, | ||
| 183 | 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, | ||
| 184 | 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, | ||
| 185 | 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, | ||
| 186 | 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, | ||
| 187 | 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, | ||
| 188 | 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, | ||
| 189 | 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, | ||
| 190 | 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, | ||
| 191 | 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, | ||
| 192 | 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, | ||
| 193 | 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, | ||
| 194 | 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, | ||
| 195 | 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, | ||
| 196 | 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, | ||
| 197 | 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, | ||
| 198 | 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, | ||
| 199 | 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, | ||
| 200 | 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, | ||
| 201 | 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, | ||
| 202 | 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, | ||
| 203 | 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, | ||
| 204 | 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, | ||
| 205 | 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, | ||
| 206 | 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, | ||
| 207 | 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, | ||
| 208 | 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, | ||
| 209 | 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, | ||
| 210 | 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, | ||
| 211 | 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, | ||
| 212 | 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, | ||
| 213 | 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, | ||
| 214 | 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, | ||
| 215 | 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, | ||
| 216 | 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, | ||
| 217 | 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, | ||
| 218 | 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, | ||
| 219 | 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, | ||
| 220 | 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, | ||
| 221 | 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, | ||
| 222 | 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, | ||
| 223 | 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, | ||
| 224 | 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, | ||
| 225 | 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, | ||
| 226 | 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, | ||
| 227 | 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, | ||
| 228 | 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, | ||
| 229 | 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, | ||
| 230 | 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, | ||
| 231 | 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, | ||
| 232 | 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, | ||
| 233 | 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, | ||
| 234 | 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, | ||
| 235 | 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, | ||
| 236 | 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, | ||
| 237 | 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, | ||
| 238 | 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, | ||
| 239 | 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, | ||
| 240 | 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, | ||
| 241 | 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, | ||
| 242 | 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, | ||
| 243 | 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, | ||
| 244 | 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, | ||
| 245 | 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, | ||
| 246 | 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, | ||
| 247 | 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, | ||
| 248 | 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, | ||
| 249 | 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, | ||
| 250 | 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, | ||
| 251 | 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, | ||
| 252 | 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, | ||
| 253 | 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, | ||
| 254 | 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, | ||
| 255 | 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, | ||
| 256 | 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, | ||
| 257 | 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, | ||
| 258 | 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, | ||
| 259 | 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, | ||
| 260 | 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, | ||
| 261 | 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, | ||
| 262 | 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, | ||
| 263 | 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, | ||
| 264 | 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, | ||
| 265 | 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, | ||
| 266 | 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, | ||
| 267 | 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, | ||
| 268 | 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, | ||
| 269 | 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, | ||
| 270 | 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, | ||
| 271 | 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, | ||
| 272 | 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, | ||
| 273 | 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, | ||
| 274 | 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, | ||
| 275 | 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, | ||
| 276 | 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, | ||
| 277 | 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, | ||
| 278 | 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, | ||
| 279 | 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, | ||
| 280 | 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, | ||
| 281 | 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, | ||
| 282 | 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, | ||
| 283 | 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, | ||
| 284 | 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, | ||
| 285 | 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, | ||
| 286 | 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, | ||
| 287 | 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, | ||
| 288 | 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, | ||
| 289 | 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, | ||
| 290 | 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, | ||
| 291 | 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, | ||
| 292 | 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, | ||
| 293 | 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, | ||
| 294 | 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, | ||
| 295 | 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, | ||
| 296 | 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, | ||
| 297 | 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, | ||
| 298 | 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, | ||
| 299 | 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, | ||
| 300 | 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, | ||
| 301 | 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, | ||
| 302 | 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, | ||
| 303 | 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, | ||
| 304 | 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, | ||
| 305 | 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, | ||
| 306 | 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, | ||
| 307 | 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, | ||
| 308 | 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, | ||
| 309 | 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, | ||
| 310 | 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, | ||
| 311 | 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, | ||
| 312 | 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, | ||
| 313 | 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, | ||
| 314 | 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, | ||
| 315 | 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, | ||
| 316 | 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, | ||
| 317 | 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, | ||
| 318 | 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, | ||
| 319 | 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, | ||
| 320 | 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, | ||
| 321 | 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, | ||
| 322 | 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, | ||
| 323 | 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, | ||
| 324 | 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, | ||
| 325 | 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, | ||
| 326 | 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, | ||
| 327 | 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, | ||
| 328 | 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, | ||
| 329 | 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, | ||
| 330 | 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, | ||
| 331 | 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, | ||
| 332 | 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, | ||
| 333 | 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, | ||
| 334 | 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, | ||
| 335 | 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, | ||
| 336 | 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, | ||
| 337 | 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, | ||
| 338 | 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, | ||
| 339 | 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, | ||
| 340 | 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, | ||
| 341 | 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, | ||
| 342 | 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, | ||
| 343 | 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, | ||
| 344 | 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, | ||
| 345 | 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, | ||
| 346 | }; | ||
| 347 | |||
| 348 | /* | ||
| 349 | * Round loop unrolling macros, S is a pointer to a S-Box array | ||
| 350 | * organized in 4 unsigned longs at a row. | ||
| 351 | */ | ||
| 352 | #define GET32_3(x) (((x) & 0xff)) | ||
| 353 | #define GET32_2(x) (((x) >> (8)) & (0xff)) | ||
| 354 | #define GET32_1(x) (((x) >> (16)) & (0xff)) | ||
| 355 | #define GET32_0(x) (((x) >> (24)) & (0xff)) | ||
| 356 | |||
| 357 | #define bf_F(x) (((S[GET32_0(x)] + S[256 + GET32_1(x)]) ^ \ | ||
| 358 | S[512 + GET32_2(x)]) + S[768 + GET32_3(x)]) | ||
| 359 | |||
| 360 | #define EROUND(a, b, n) do { b ^= P[n]; a ^= bf_F(b); } while (0) | ||
| 361 | #define DROUND(a, b, n) do { a ^= bf_F(b); b ^= P[n]; } while (0) | ||
| 362 | |||
| 363 | |||
| 364 | /* | ||
| 365 | * encrypt isdn data frame | ||
| 366 | * every block with 9 samples is encrypted | ||
| 367 | */ | ||
| 368 | void | ||
| 369 | dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len) | ||
| 370 | { | ||
| 371 | int i = 0, j = dsp->bf_crypt_pos; | ||
| 372 | u8 *bf_data_in = dsp->bf_data_in; | ||
| 373 | u8 *bf_crypt_out = dsp->bf_crypt_out; | ||
| 374 | u32 *P = dsp->bf_p; | ||
| 375 | u32 *S = dsp->bf_s; | ||
| 376 | u32 yl, yr; | ||
| 377 | u32 cs; | ||
| 378 | u8 nibble; | ||
| 379 | |||
| 380 | while (i < len) { | ||
| 381 | /* collect a block of 9 samples */ | ||
| 382 | if (j < 9) { | ||
| 383 | bf_data_in[j] = *data; | ||
| 384 | *data++ = bf_crypt_out[j++]; | ||
| 385 | i++; | ||
| 386 | continue; | ||
| 387 | } | ||
| 388 | j = 0; | ||
| 389 | /* transcode 9 samples xlaw to 8 bytes */ | ||
| 390 | yl = dsp_audio_law2seven[bf_data_in[0]]; | ||
| 391 | yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[1]]; | ||
| 392 | yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[2]]; | ||
| 393 | yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[3]]; | ||
| 394 | nibble = dsp_audio_law2seven[bf_data_in[4]]; | ||
| 395 | yr = nibble; | ||
| 396 | yl = (yl<<4) | (nibble>>3); | ||
| 397 | yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[5]]; | ||
| 398 | yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[6]]; | ||
| 399 | yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[7]]; | ||
| 400 | yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[8]]; | ||
| 401 | yr = (yr<<1) | (bf_data_in[0] & 1); | ||
| 402 | |||
| 403 | /* fill unused bit with random noise of audio input */ | ||
| 404 | /* encrypt */ | ||
| 405 | |||
| 406 | EROUND(yr, yl, 0); | ||
| 407 | EROUND(yl, yr, 1); | ||
| 408 | EROUND(yr, yl, 2); | ||
| 409 | EROUND(yl, yr, 3); | ||
| 410 | EROUND(yr, yl, 4); | ||
| 411 | EROUND(yl, yr, 5); | ||
| 412 | EROUND(yr, yl, 6); | ||
| 413 | EROUND(yl, yr, 7); | ||
| 414 | EROUND(yr, yl, 8); | ||
| 415 | EROUND(yl, yr, 9); | ||
| 416 | EROUND(yr, yl, 10); | ||
| 417 | EROUND(yl, yr, 11); | ||
| 418 | EROUND(yr, yl, 12); | ||
| 419 | EROUND(yl, yr, 13); | ||
| 420 | EROUND(yr, yl, 14); | ||
| 421 | EROUND(yl, yr, 15); | ||
| 422 | yl ^= P[16]; | ||
| 423 | yr ^= P[17]; | ||
| 424 | |||
| 425 | /* calculate 3-bit checksumme */ | ||
| 426 | cs = yl ^ (yl>>3) ^ (yl>>6) ^ (yl>>9) ^ (yl>>12) ^ (yl>>15) | ||
| 427 | ^ (yl>>18) ^ (yl>>21) ^ (yl>>24) ^ (yl>>27) ^ (yl>>30) | ||
| 428 | ^ (yr<<2) ^ (yr>>1) ^ (yr>>4) ^ (yr>>7) ^ (yr>>10) | ||
| 429 | ^ (yr>>13) ^ (yr>>16) ^ (yr>>19) ^ (yr>>22) ^ (yr>>25) | ||
| 430 | ^ (yr>>28) ^ (yr>>31); | ||
| 431 | |||
| 432 | /* | ||
| 433 | * transcode 8 crypted bytes to 9 data bytes with sync | ||
| 434 | * and checksum information | ||
| 435 | */ | ||
| 436 | bf_crypt_out[0] = (yl>>25) | 0x80; | ||
| 437 | bf_crypt_out[1] = (yl>>18) & 0x7f; | ||
| 438 | bf_crypt_out[2] = (yl>>11) & 0x7f; | ||
| 439 | bf_crypt_out[3] = (yl>>4) & 0x7f; | ||
| 440 | bf_crypt_out[4] = ((yl<<3) & 0x78) | ((yr>>29) & 0x07); | ||
| 441 | bf_crypt_out[5] = ((yr>>22) & 0x7f) | ((cs<<5) & 0x80); | ||
| 442 | bf_crypt_out[6] = ((yr>>15) & 0x7f) | ((cs<<6) & 0x80); | ||
| 443 | bf_crypt_out[7] = ((yr>>8) & 0x7f) | (cs<<7); | ||
| 444 | bf_crypt_out[8] = yr; | ||
| 445 | } | ||
| 446 | |||
| 447 | /* write current count */ | ||
| 448 | dsp->bf_crypt_pos = j; | ||
| 449 | |||
| 450 | } | ||
| 451 | |||
| 452 | |||
| 453 | /* | ||
| 454 | * decrypt isdn data frame | ||
| 455 | * every block with 9 bytes is decrypted | ||
| 456 | */ | ||
| 457 | void | ||
| 458 | dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len) | ||
| 459 | { | ||
| 460 | int i = 0; | ||
| 461 | u8 j = dsp->bf_decrypt_in_pos; | ||
| 462 | u8 k = dsp->bf_decrypt_out_pos; | ||
| 463 | u8 *bf_crypt_inring = dsp->bf_crypt_inring; | ||
| 464 | u8 *bf_data_out = dsp->bf_data_out; | ||
| 465 | u16 sync = dsp->bf_sync; | ||
| 466 | u32 *P = dsp->bf_p; | ||
| 467 | u32 *S = dsp->bf_s; | ||
| 468 | u32 yl, yr; | ||
| 469 | u8 nibble; | ||
| 470 | u8 cs, cs0, cs1, cs2; | ||
| 471 | |||
| 472 | while (i < len) { | ||
| 473 | /* | ||
| 474 | * shift upper bit and rotate data to buffer ring | ||
| 475 | * send current decrypted data | ||
| 476 | */ | ||
| 477 | sync = (sync<<1) | ((*data)>>7); | ||
| 478 | bf_crypt_inring[j++ & 15] = *data; | ||
| 479 | *data++ = bf_data_out[k++]; | ||
| 480 | i++; | ||
| 481 | if (k == 9) | ||
| 482 | k = 0; /* repeat if no sync has been found */ | ||
| 483 | /* check if not in sync */ | ||
| 484 | if ((sync&0x1f0) != 0x100) | ||
| 485 | continue; | ||
| 486 | j -= 9; | ||
| 487 | /* transcode receive data to 64 bit block of encrypted data */ | ||
| 488 | yl = bf_crypt_inring[j++ & 15]; | ||
| 489 | yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ | ||
| 490 | yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ | ||
| 491 | yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ | ||
| 492 | nibble = bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ | ||
| 493 | yr = nibble; | ||
| 494 | yl = (yl<<4) | (nibble>>3); | ||
| 495 | cs2 = bf_crypt_inring[j++ & 15]; | ||
| 496 | yr = (yr<<7) | (cs2 & 0x7f); | ||
| 497 | cs1 = bf_crypt_inring[j++ & 15]; | ||
| 498 | yr = (yr<<7) | (cs1 & 0x7f); | ||
| 499 | cs0 = bf_crypt_inring[j++ & 15]; | ||
| 500 | yr = (yr<<7) | (cs0 & 0x7f); | ||
| 501 | yr = (yr<<8) | bf_crypt_inring[j++ & 15]; | ||
| 502 | |||
| 503 | /* calculate 3-bit checksumme */ | ||
| 504 | cs = yl ^ (yl>>3) ^ (yl>>6) ^ (yl>>9) ^ (yl>>12) ^ (yl>>15) | ||
| 505 | ^ (yl>>18) ^ (yl>>21) ^ (yl>>24) ^ (yl>>27) ^ (yl>>30) | ||
| 506 | ^ (yr<<2) ^ (yr>>1) ^ (yr>>4) ^ (yr>>7) ^ (yr>>10) | ||
| 507 | ^ (yr>>13) ^ (yr>>16) ^ (yr>>19) ^ (yr>>22) ^ (yr>>25) | ||
| 508 | ^ (yr>>28) ^ (yr>>31); | ||
| 509 | |||
| 510 | /* check if frame is valid */ | ||
| 511 | if ((cs&0x7) != (((cs2>>5)&4) | ((cs1>>6)&2) | (cs0 >> 7))) { | ||
| 512 | if (dsp_debug & DEBUG_DSP_BLOWFISH) | ||
| 513 | printk(KERN_DEBUG | ||
| 514 | "DSP BLOWFISH: received corrupt frame, " | ||
| 515 | "checksumme is not correct\n"); | ||
| 516 | continue; | ||
| 517 | } | ||
| 518 | |||
| 519 | /* decrypt */ | ||
| 520 | yr ^= P[17]; | ||
| 521 | yl ^= P[16]; | ||
| 522 | DROUND(yl, yr, 15); | ||
| 523 | DROUND(yr, yl, 14); | ||
| 524 | DROUND(yl, yr, 13); | ||
| 525 | DROUND(yr, yl, 12); | ||
| 526 | DROUND(yl, yr, 11); | ||
| 527 | DROUND(yr, yl, 10); | ||
| 528 | DROUND(yl, yr, 9); | ||
| 529 | DROUND(yr, yl, 8); | ||
| 530 | DROUND(yl, yr, 7); | ||
| 531 | DROUND(yr, yl, 6); | ||
| 532 | DROUND(yl, yr, 5); | ||
| 533 | DROUND(yr, yl, 4); | ||
| 534 | DROUND(yl, yr, 3); | ||
| 535 | DROUND(yr, yl, 2); | ||
| 536 | DROUND(yl, yr, 1); | ||
| 537 | DROUND(yr, yl, 0); | ||
| 538 | |||
| 539 | /* transcode 8 crypted bytes to 9 sample bytes */ | ||
| 540 | bf_data_out[0] = dsp_audio_seven2law[(yl>>25) & 0x7f]; | ||
| 541 | bf_data_out[1] = dsp_audio_seven2law[(yl>>18) & 0x7f]; | ||
| 542 | bf_data_out[2] = dsp_audio_seven2law[(yl>>11) & 0x7f]; | ||
| 543 | bf_data_out[3] = dsp_audio_seven2law[(yl>>4) & 0x7f]; | ||
| 544 | bf_data_out[4] = dsp_audio_seven2law[((yl<<3) & 0x78) | | ||
| 545 | ((yr>>29) & 0x07)]; | ||
| 546 | |||
| 547 | bf_data_out[5] = dsp_audio_seven2law[(yr>>22) & 0x7f]; | ||
| 548 | bf_data_out[6] = dsp_audio_seven2law[(yr>>15) & 0x7f]; | ||
| 549 | bf_data_out[7] = dsp_audio_seven2law[(yr>>8) & 0x7f]; | ||
| 550 | bf_data_out[8] = dsp_audio_seven2law[(yr>>1) & 0x7f]; | ||
| 551 | k = 0; /* start with new decoded frame */ | ||
| 552 | } | ||
| 553 | |||
| 554 | /* write current count and sync */ | ||
| 555 | dsp->bf_decrypt_in_pos = j; | ||
| 556 | dsp->bf_decrypt_out_pos = k; | ||
| 557 | dsp->bf_sync = sync; | ||
| 558 | } | ||
| 559 | |||
| 560 | |||
| 561 | /* used to encrypt S and P boxes */ | ||
| 562 | static inline void | ||
| 563 | encrypt_block(const u32 *P, const u32 *S, u32 *dst, u32 *src) | ||
| 564 | { | ||
| 565 | u32 yl = src[0]; | ||
| 566 | u32 yr = src[1]; | ||
| 567 | |||
| 568 | EROUND(yr, yl, 0); | ||
| 569 | EROUND(yl, yr, 1); | ||
| 570 | EROUND(yr, yl, 2); | ||
| 571 | EROUND(yl, yr, 3); | ||
| 572 | EROUND(yr, yl, 4); | ||
| 573 | EROUND(yl, yr, 5); | ||
| 574 | EROUND(yr, yl, 6); | ||
| 575 | EROUND(yl, yr, 7); | ||
| 576 | EROUND(yr, yl, 8); | ||
| 577 | EROUND(yl, yr, 9); | ||
| 578 | EROUND(yr, yl, 10); | ||
| 579 | EROUND(yl, yr, 11); | ||
| 580 | EROUND(yr, yl, 12); | ||
| 581 | EROUND(yl, yr, 13); | ||
| 582 | EROUND(yr, yl, 14); | ||
| 583 | EROUND(yl, yr, 15); | ||
| 584 | |||
| 585 | yl ^= P[16]; | ||
| 586 | yr ^= P[17]; | ||
| 587 | |||
| 588 | dst[0] = yr; | ||
| 589 | dst[1] = yl; | ||
| 590 | } | ||
| 591 | |||
| 592 | /* | ||
| 593 | * initialize the dsp for encryption and decryption using the same key | ||
| 594 | * Calculates the blowfish S and P boxes for encryption and decryption. | ||
| 595 | * The margin of keylen must be 4-56 bytes. | ||
| 596 | * returns 0 if ok. | ||
| 597 | */ | ||
| 598 | int | ||
| 599 | dsp_bf_init(struct dsp *dsp, const u8 *key, uint keylen) | ||
| 600 | { | ||
| 601 | short i, j, count; | ||
| 602 | u32 data[2], temp; | ||
| 603 | u32 *P = (u32 *)dsp->bf_p; | ||
| 604 | u32 *S = (u32 *)dsp->bf_s; | ||
| 605 | |||
| 606 | if (keylen < 4 || keylen > 56) | ||
| 607 | return 1; | ||
| 608 | |||
| 609 | /* Set dsp states */ | ||
| 610 | i = 0; | ||
| 611 | while (i < 9) { | ||
| 612 | dsp->bf_crypt_out[i] = 0xff; | ||
| 613 | dsp->bf_data_out[i] = dsp_silence; | ||
| 614 | i++; | ||
| 615 | } | ||
| 616 | dsp->bf_crypt_pos = 0; | ||
| 617 | dsp->bf_decrypt_in_pos = 0; | ||
| 618 | dsp->bf_decrypt_out_pos = 0; | ||
| 619 | dsp->bf_sync = 0x1ff; | ||
| 620 | dsp->bf_enable = 1; | ||
| 621 | |||
| 622 | /* Copy the initialization s-boxes */ | ||
| 623 | for (i = 0, count = 0; i < 256; i++) | ||
| 624 | for (j = 0; j < 4; j++, count++) | ||
| 625 | S[count] = bf_sbox[count]; | ||
| 626 | |||
| 627 | /* Set the p-boxes */ | ||
| 628 | for (i = 0; i < 16 + 2; i++) | ||
| 629 | P[i] = bf_pbox[i]; | ||
| 630 | |||
| 631 | /* Actual subkey generation */ | ||
| 632 | for (j = 0, i = 0; i < 16 + 2; i++) { | ||
| 633 | temp = (((u32)key[j] << 24) | | ||
| 634 | ((u32)key[(j + 1) % keylen] << 16) | | ||
| 635 | ((u32)key[(j + 2) % keylen] << 8) | | ||
| 636 | ((u32)key[(j + 3) % keylen])); | ||
| 637 | |||
| 638 | P[i] = P[i] ^ temp; | ||
| 639 | j = (j + 4) % keylen; | ||
| 640 | } | ||
| 641 | |||
| 642 | data[0] = 0x00000000; | ||
| 643 | data[1] = 0x00000000; | ||
| 644 | |||
| 645 | for (i = 0; i < 16 + 2; i += 2) { | ||
| 646 | encrypt_block(P, S, data, data); | ||
| 647 | |||
| 648 | P[i] = data[0]; | ||
| 649 | P[i + 1] = data[1]; | ||
| 650 | } | ||
| 651 | |||
| 652 | for (i = 0; i < 4; i++) { | ||
| 653 | for (j = 0, count = i * 256; j < 256; j += 2, count += 2) { | ||
| 654 | encrypt_block(P, S, data, data); | ||
| 655 | |||
| 656 | S[count] = data[0]; | ||
| 657 | S[count + 1] = data[1]; | ||
| 658 | } | ||
| 659 | } | ||
| 660 | |||
| 661 | return 0; | ||
| 662 | } | ||
| 663 | |||
| 664 | |||
| 665 | /* | ||
| 666 | * turn encryption off | ||
| 667 | */ | ||
| 668 | void | ||
| 669 | dsp_bf_cleanup(struct dsp *dsp) | ||
| 670 | { | ||
| 671 | dsp->bf_enable = 0; | ||
| 672 | } | ||
diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c new file mode 100644 index 000000000000..e92b1ba4b45e --- /dev/null +++ b/drivers/isdn/mISDN/dsp_cmx.c | |||
| @@ -0,0 +1,1886 @@ | |||
| 1 | /* | ||
| 2 | * Audio crossconnecting/conferrencing (hardware level). | ||
| 3 | * | ||
| 4 | * Copyright 2002 by Andreas Eversberg (jolly@eversberg.eu) | ||
| 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 diabled 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/delay.h> | ||
| 128 | #include <linux/mISDNif.h> | ||
| 129 | #include <linux/mISDNdsp.h> | ||
| 130 | #include "core.h" | ||
| 131 | #include "dsp.h" | ||
| 132 | /* | ||
| 133 | * debugging of multi party conference, | ||
| 134 | * by using conference even with two members | ||
| 135 | */ | ||
| 136 | |||
| 137 | /* #define CMX_CONF_DEBUG */ | ||
| 138 | |||
| 139 | /*#define CMX_DEBUG * massive read/write pointer output */ | ||
| 140 | /*#define CMX_TX_DEBUG * massive read/write on tx-buffer with content */ | ||
| 141 | |||
| 142 | static inline int | ||
| 143 | count_list_member(struct list_head *head) | ||
| 144 | { | ||
| 145 | int cnt = 0; | ||
| 146 | struct list_head *m; | ||
| 147 | |||
| 148 | list_for_each(m, head) | ||
| 149 | cnt++; | ||
| 150 | return cnt; | ||
| 151 | } | ||
| 152 | |||
| 153 | /* | ||
| 154 | * debug cmx memory structure | ||
| 155 | */ | ||
| 156 | void | ||
| 157 | dsp_cmx_debug(struct dsp *dsp) | ||
| 158 | { | ||
| 159 | struct dsp_conf *conf; | ||
| 160 | struct dsp_conf_member *member; | ||
| 161 | struct dsp *odsp; | ||
| 162 | |||
| 163 | printk(KERN_DEBUG "-----Current DSP\n"); | ||
| 164 | list_for_each_entry(odsp, &dsp_ilist, list) { | ||
| 165 | printk(KERN_DEBUG "* %s echo=%d txmix=%d", | ||
| 166 | odsp->name, odsp->echo, odsp->tx_mix); | ||
| 167 | if (odsp->conf) | ||
| 168 | printk(" (Conf %d)", odsp->conf->id); | ||
| 169 | if (dsp == odsp) | ||
| 170 | printk(" *this*"); | ||
| 171 | printk("\n"); | ||
| 172 | } | ||
| 173 | printk(KERN_DEBUG "-----Current Conf:\n"); | ||
| 174 | list_for_each_entry(conf, &conf_ilist, list) { | ||
| 175 | printk(KERN_DEBUG "* Conf %d (%p)\n", conf->id, conf); | ||
| 176 | list_for_each_entry(member, &conf->mlist, list) { | ||
| 177 | printk(KERN_DEBUG | ||
| 178 | " - member = %s (slot_tx %d, bank_tx %d, " | ||
| 179 | "slot_rx %d, bank_rx %d hfc_conf %d)%s\n", | ||
| 180 | member->dsp->name, member->dsp->pcm_slot_tx, | ||
| 181 | member->dsp->pcm_bank_tx, member->dsp->pcm_slot_rx, | ||
| 182 | member->dsp->pcm_bank_rx, member->dsp->hfc_conf, | ||
| 183 | (member->dsp == dsp) ? " *this*" : ""); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | printk(KERN_DEBUG "-----end\n"); | ||
| 187 | } | ||
| 188 | |||
| 189 | /* | ||
| 190 | * search conference | ||
| 191 | */ | ||
| 192 | static struct dsp_conf * | ||
| 193 | dsp_cmx_search_conf(u32 id) | ||
| 194 | { | ||
| 195 | struct dsp_conf *conf; | ||
| 196 | |||
| 197 | if (!id) { | ||
| 198 | printk(KERN_WARNING "%s: conference ID is 0.\n", __func__); | ||
| 199 | return NULL; | ||
| 200 | } | ||
| 201 | |||
| 202 | /* search conference */ | ||
| 203 | list_for_each_entry(conf, &conf_ilist, list) | ||
| 204 | if (conf->id == id) | ||
| 205 | return conf; | ||
| 206 | |||
| 207 | return NULL; | ||
| 208 | } | ||
| 209 | |||
| 210 | |||
| 211 | /* | ||
| 212 | * add member to conference | ||
| 213 | */ | ||
| 214 | static int | ||
| 215 | dsp_cmx_add_conf_member(struct dsp *dsp, struct dsp_conf *conf) | ||
| 216 | { | ||
| 217 | struct dsp_conf_member *member; | ||
| 218 | |||
| 219 | if (!conf || !dsp) { | ||
| 220 | printk(KERN_WARNING "%s: conf or dsp is 0.\n", __func__); | ||
| 221 | return -EINVAL; | ||
| 222 | } | ||
| 223 | if (dsp->member) { | ||
| 224 | printk(KERN_WARNING "%s: dsp is already member in a conf.\n", | ||
| 225 | __func__); | ||
| 226 | return -EINVAL; | ||
| 227 | } | ||
| 228 | |||
| 229 | if (dsp->conf) { | ||
| 230 | printk(KERN_WARNING "%s: dsp is already in a conf.\n", | ||
| 231 | __func__); | ||
| 232 | return -EINVAL; | ||
| 233 | } | ||
| 234 | |||
| 235 | member = kzalloc(sizeof(struct dsp_conf_member), GFP_ATOMIC); | ||
| 236 | if (!member) { | ||
| 237 | printk(KERN_ERR "kmalloc struct dsp_conf_member failed\n"); | ||
| 238 | return -ENOMEM; | ||
| 239 | } | ||
| 240 | member->dsp = dsp; | ||
| 241 | /* clear rx buffer */ | ||
| 242 | memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); | ||
| 243 | dsp->rx_init = 1; /* rx_W and rx_R will be adjusted on first frame */ | ||
| 244 | dsp->rx_W = 0; | ||
| 245 | dsp->rx_R = 0; | ||
| 246 | |||
| 247 | list_add_tail(&member->list, &conf->mlist); | ||
| 248 | |||
| 249 | dsp->conf = conf; | ||
| 250 | dsp->member = member; | ||
| 251 | |||
| 252 | return 0; | ||
| 253 | } | ||
| 254 | |||
| 255 | |||
| 256 | /* | ||
| 257 | * del member from conference | ||
| 258 | */ | ||
| 259 | int | ||
| 260 | dsp_cmx_del_conf_member(struct dsp *dsp) | ||
| 261 | { | ||
| 262 | struct dsp_conf_member *member; | ||
| 263 | |||
| 264 | if (!dsp) { | ||
| 265 | printk(KERN_WARNING "%s: dsp is 0.\n", | ||
| 266 | __func__); | ||
| 267 | return -EINVAL; | ||
| 268 | } | ||
| 269 | |||
| 270 | if (!dsp->conf) { | ||
| 271 | printk(KERN_WARNING "%s: dsp is not in a conf.\n", | ||
| 272 | __func__); | ||
| 273 | return -EINVAL; | ||
| 274 | } | ||
| 275 | |||
| 276 | if (list_empty(&dsp->conf->mlist)) { | ||
| 277 | printk(KERN_WARNING "%s: dsp has linked an empty conf.\n", | ||
| 278 | __func__); | ||
| 279 | return -EINVAL; | ||
| 280 | } | ||
| 281 | |||
| 282 | /* find us in conf */ | ||
| 283 | list_for_each_entry(member, &dsp->conf->mlist, list) { | ||
| 284 | if (member->dsp == dsp) { | ||
| 285 | list_del(&member->list); | ||
| 286 | dsp->conf = NULL; | ||
| 287 | dsp->member = NULL; | ||
| 288 | kfree(member); | ||
| 289 | return 0; | ||
| 290 | } | ||
| 291 | } | ||
| 292 | printk(KERN_WARNING | ||
| 293 | "%s: dsp is not present in its own conf_meber list.\n", | ||
| 294 | __func__); | ||
| 295 | |||
| 296 | return -EINVAL; | ||
| 297 | } | ||
| 298 | |||
| 299 | |||
| 300 | /* | ||
| 301 | * new conference | ||
| 302 | */ | ||
| 303 | static struct dsp_conf | ||
| 304 | *dsp_cmx_new_conf(u32 id) | ||
| 305 | { | ||
| 306 | struct dsp_conf *conf; | ||
| 307 | |||
| 308 | if (!id) { | ||
| 309 | printk(KERN_WARNING "%s: id is 0.\n", | ||
| 310 | __func__); | ||
| 311 | return NULL; | ||
| 312 | } | ||
| 313 | |||
| 314 | conf = kzalloc(sizeof(struct dsp_conf), GFP_ATOMIC); | ||
| 315 | if (!conf) { | ||
| 316 | printk(KERN_ERR "kmalloc struct dsp_conf failed\n"); | ||
| 317 | return NULL; | ||
| 318 | } | ||
| 319 | INIT_LIST_HEAD(&conf->mlist); | ||
| 320 | conf->id = id; | ||
| 321 | |||
| 322 | list_add_tail(&conf->list, &conf_ilist); | ||
| 323 | |||
| 324 | return conf; | ||
| 325 | } | ||
| 326 | |||
| 327 | |||
| 328 | /* | ||
| 329 | * del conference | ||
| 330 | */ | ||
| 331 | int | ||
| 332 | dsp_cmx_del_conf(struct dsp_conf *conf) | ||
| 333 | { | ||
| 334 | if (!conf) { | ||
| 335 | printk(KERN_WARNING "%s: conf is null.\n", | ||
| 336 | __func__); | ||
| 337 | return -EINVAL; | ||
| 338 | } | ||
| 339 | |||
| 340 | if (!list_empty(&conf->mlist)) { | ||
| 341 | printk(KERN_WARNING "%s: conf not empty.\n", | ||
| 342 | __func__); | ||
| 343 | return -EINVAL; | ||
| 344 | } | ||
| 345 | list_del(&conf->list); | ||
| 346 | kfree(conf); | ||
| 347 | |||
| 348 | return 0; | ||
| 349 | } | ||
| 350 | |||
| 351 | |||
| 352 | /* | ||
| 353 | * send HW message to hfc card | ||
| 354 | */ | ||
| 355 | static void | ||
| 356 | dsp_cmx_hw_message(struct dsp *dsp, u32 message, u32 param1, u32 param2, | ||
| 357 | u32 param3, u32 param4) | ||
| 358 | { | ||
| 359 | struct mISDN_ctrl_req cq; | ||
| 360 | |||
| 361 | memset(&cq, 0, sizeof(cq)); | ||
| 362 | cq.op = message; | ||
| 363 | cq.p1 = param1 | (param2 << 8); | ||
| 364 | cq.p2 = param3 | (param4 << 8); | ||
| 365 | if (dsp->ch.peer) | ||
| 366 | dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq); | ||
| 367 | } | ||
| 368 | |||
| 369 | |||
| 370 | /* | ||
| 371 | * do hardware update and set the software/hardware flag | ||
| 372 | * | ||
| 373 | * either a conference or a dsp instance can be given | ||
| 374 | * if only dsp instance is given, the instance is not associated with a conf | ||
| 375 | * and therefore removed. if a conference is given, the dsp is expected to | ||
| 376 | * be member of that conference. | ||
| 377 | */ | ||
| 378 | void | ||
| 379 | dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp) | ||
| 380 | { | ||
| 381 | struct dsp_conf_member *member, *nextm; | ||
| 382 | struct dsp *finddsp; | ||
| 383 | int memb = 0, i, ii, i1, i2; | ||
| 384 | int freeunits[8]; | ||
| 385 | u_char freeslots[256]; | ||
| 386 | int same_hfc = -1, same_pcm = -1, current_conf = -1, | ||
| 387 | all_conf = 1; | ||
| 388 | |||
| 389 | /* dsp gets updated (no conf) */ | ||
| 390 | if (!conf) { | ||
| 391 | if (!dsp) | ||
| 392 | return; | ||
| 393 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 394 | printk(KERN_DEBUG "%s checking dsp %s\n", | ||
| 395 | __func__, dsp->name); | ||
| 396 | one_member: | ||
| 397 | /* remove HFC conference if enabled */ | ||
| 398 | if (dsp->hfc_conf >= 0) { | ||
| 399 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 400 | printk(KERN_DEBUG | ||
| 401 | "%s removing %s from HFC conf %d " | ||
| 402 | "because dsp is split\n", __func__, | ||
| 403 | dsp->name, dsp->hfc_conf); | ||
| 404 | dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_CONF_SPLIT, | ||
| 405 | 0, 0, 0, 0); | ||
| 406 | dsp->hfc_conf = -1; | ||
| 407 | } | ||
| 408 | /* process hw echo */ | ||
| 409 | if (dsp->features.pcm_banks < 1) | ||
| 410 | return; | ||
| 411 | if (!dsp->echo) { | ||
| 412 | /* NO ECHO: remove PCM slot if assigned */ | ||
| 413 | if (dsp->pcm_slot_tx >= 0 || dsp->pcm_slot_rx >= 0) { | ||
| 414 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 415 | printk(KERN_DEBUG "%s removing %s from" | ||
| 416 | " PCM slot %d (TX) %d (RX) because" | ||
| 417 | " dsp is split (no echo)\n", | ||
| 418 | __func__, dsp->name, | ||
| 419 | dsp->pcm_slot_tx, dsp->pcm_slot_rx); | ||
| 420 | dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_DISC, | ||
| 421 | 0, 0, 0, 0); | ||
| 422 | dsp->pcm_slot_tx = -1; | ||
| 423 | dsp->pcm_bank_tx = -1; | ||
| 424 | dsp->pcm_slot_rx = -1; | ||
| 425 | dsp->pcm_bank_rx = -1; | ||
| 426 | } | ||
| 427 | return; | ||
| 428 | } | ||
| 429 | /* ECHO: already echo */ | ||
| 430 | if (dsp->pcm_slot_tx >= 0 && dsp->pcm_slot_rx < 0 && | ||
| 431 | dsp->pcm_bank_tx == 2 && dsp->pcm_bank_rx == 2) | ||
| 432 | return; | ||
| 433 | /* ECHO: if slot already assigned */ | ||
| 434 | if (dsp->pcm_slot_tx >= 0) { | ||
| 435 | dsp->pcm_slot_rx = dsp->pcm_slot_tx; | ||
| 436 | dsp->pcm_bank_tx = 2; /* 2 means loop */ | ||
| 437 | dsp->pcm_bank_rx = 2; | ||
| 438 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 439 | printk(KERN_DEBUG | ||
| 440 | "%s refresh %s for echo using slot %d\n", | ||
| 441 | __func__, dsp->name, | ||
| 442 | dsp->pcm_slot_tx); | ||
| 443 | dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN, | ||
| 444 | dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2); | ||
| 445 | return; | ||
| 446 | } | ||
| 447 | /* ECHO: find slot */ | ||
| 448 | dsp->pcm_slot_tx = -1; | ||
| 449 | dsp->pcm_slot_rx = -1; | ||
| 450 | memset(freeslots, 1, sizeof(freeslots)); | ||
| 451 | list_for_each_entry(finddsp, &dsp_ilist, list) { | ||
| 452 | if (finddsp->features.pcm_id == dsp->features.pcm_id) { | ||
| 453 | if (finddsp->pcm_slot_rx >= 0 && | ||
| 454 | finddsp->pcm_slot_rx < sizeof(freeslots)) | ||
| 455 | freeslots[finddsp->pcm_slot_tx] = 0; | ||
| 456 | if (finddsp->pcm_slot_tx >= 0 && | ||
| 457 | finddsp->pcm_slot_tx < sizeof(freeslots)) | ||
| 458 | freeslots[finddsp->pcm_slot_rx] = 0; | ||
| 459 | } | ||
| 460 | } | ||
| 461 | i = 0; | ||
| 462 | ii = dsp->features.pcm_slots; | ||
| 463 | while (i < ii) { | ||
| 464 | if (freeslots[i]) | ||
| 465 | break; | ||
| 466 | i++; | ||
| 467 | } | ||
| 468 | if (i == ii) { | ||
| 469 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 470 | printk(KERN_DEBUG | ||
| 471 | "%s no slot available for echo\n", | ||
| 472 | __func__); | ||
| 473 | /* no more slots available */ | ||
| 474 | return; | ||
| 475 | } | ||
| 476 | /* assign free slot */ | ||
| 477 | dsp->pcm_slot_tx = i; | ||
| 478 | dsp->pcm_slot_rx = i; | ||
| 479 | dsp->pcm_bank_tx = 2; /* loop */ | ||
| 480 | dsp->pcm_bank_rx = 2; | ||
| 481 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 482 | printk(KERN_DEBUG | ||
| 483 | "%s assign echo for %s using slot %d\n", | ||
| 484 | __func__, dsp->name, dsp->pcm_slot_tx); | ||
| 485 | dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN, | ||
| 486 | dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2); | ||
| 487 | return; | ||
| 488 | } | ||
| 489 | |||
| 490 | /* conf gets updated (all members) */ | ||
| 491 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 492 | printk(KERN_DEBUG "%s checking conference %d\n", | ||
| 493 | __func__, conf->id); | ||
| 494 | |||
| 495 | if (list_empty(&conf->mlist)) { | ||
| 496 | printk(KERN_ERR "%s: conference whithout members\n", | ||
| 497 | __func__); | ||
| 498 | return; | ||
| 499 | } | ||
| 500 | member = list_entry(conf->mlist.next, struct dsp_conf_member, list); | ||
| 501 | same_hfc = member->dsp->features.hfc_id; | ||
| 502 | same_pcm = member->dsp->features.pcm_id; | ||
| 503 | /* check all members in our conference */ | ||
| 504 | list_for_each_entry(member, &conf->mlist, list) { | ||
| 505 | /* check if member uses mixing */ | ||
| 506 | if (member->dsp->tx_mix) { | ||
| 507 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 508 | printk(KERN_DEBUG | ||
| 509 | "%s dsp %s cannot form a conf, because " | ||
| 510 | "tx_mix is turned on\n", __func__, | ||
| 511 | member->dsp->name); | ||
| 512 | conf_software: | ||
| 513 | list_for_each_entry(member, &conf->mlist, list) { | ||
| 514 | dsp = member->dsp; | ||
| 515 | /* remove HFC conference if enabled */ | ||
| 516 | if (dsp->hfc_conf >= 0) { | ||
| 517 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 518 | printk(KERN_DEBUG | ||
| 519 | "%s removing %s from HFC " | ||
| 520 | "conf %d because not " | ||
| 521 | "possible with hardware\n", | ||
| 522 | __func__, | ||
| 523 | dsp->name, | ||
| 524 | dsp->hfc_conf); | ||
| 525 | dsp_cmx_hw_message(dsp, | ||
| 526 | MISDN_CTRL_HFC_CONF_SPLIT, | ||
| 527 | 0, 0, 0, 0); | ||
| 528 | dsp->hfc_conf = -1; | ||
| 529 | } | ||
| 530 | /* remove PCM slot if assigned */ | ||
| 531 | if (dsp->pcm_slot_tx >= 0 || | ||
| 532 | dsp->pcm_slot_rx >= 0) { | ||
| 533 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 534 | printk(KERN_DEBUG "%s removing " | ||
| 535 | "%s from PCM slot %d (TX)" | ||
| 536 | " slot %d (RX) because not" | ||
| 537 | " possible with hardware\n", | ||
| 538 | __func__, | ||
| 539 | dsp->name, | ||
| 540 | dsp->pcm_slot_tx, | ||
| 541 | dsp->pcm_slot_rx); | ||
| 542 | dsp_cmx_hw_message(dsp, | ||
| 543 | MISDN_CTRL_HFC_PCM_DISC, | ||
| 544 | 0, 0, 0, 0); | ||
| 545 | dsp->pcm_slot_tx = -1; | ||
| 546 | dsp->pcm_bank_tx = -1; | ||
| 547 | dsp->pcm_slot_rx = -1; | ||
| 548 | dsp->pcm_bank_rx = -1; | ||
| 549 | } | ||
| 550 | } | ||
| 551 | conf->hardware = 0; | ||
| 552 | conf->software = 1; | ||
| 553 | return; | ||
| 554 | } | ||
| 555 | /* check if member has echo turned on */ | ||
| 556 | if (member->dsp->echo) { | ||
| 557 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 558 | printk(KERN_DEBUG | ||
| 559 | "%s dsp %s cannot form a conf, because " | ||
| 560 | "echo is turned on\n", __func__, | ||
| 561 | member->dsp->name); | ||
| 562 | goto conf_software; | ||
| 563 | } | ||
| 564 | /* check if member has tx_mix turned on */ | ||
| 565 | if (member->dsp->tx_mix) { | ||
| 566 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 567 | printk(KERN_DEBUG | ||
| 568 | "%s dsp %s cannot form a conf, because " | ||
| 569 | "tx_mix is turned on\n", | ||
| 570 | __func__, member->dsp->name); | ||
| 571 | goto conf_software; | ||
| 572 | } | ||
| 573 | /* check if member changes volume at an not suppoted level */ | ||
| 574 | if (member->dsp->tx_volume) { | ||
| 575 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 576 | printk(KERN_DEBUG | ||
| 577 | "%s dsp %s cannot form a conf, because " | ||
| 578 | "tx_volume is changed\n", | ||
| 579 | __func__, member->dsp->name); | ||
| 580 | goto conf_software; | ||
| 581 | } | ||
| 582 | if (member->dsp->rx_volume) { | ||
| 583 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 584 | printk(KERN_DEBUG | ||
| 585 | "%s dsp %s cannot form a conf, because " | ||
| 586 | "rx_volume is changed\n", | ||
| 587 | __func__, member->dsp->name); | ||
| 588 | goto conf_software; | ||
| 589 | } | ||
| 590 | /* check if tx-data turned on */ | ||
| 591 | if (member->dsp->tx_data) { | ||
| 592 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 593 | printk(KERN_DEBUG | ||
| 594 | "%s dsp %s cannot form a conf, because " | ||
| 595 | "tx_data is turned on\n", | ||
| 596 | __func__, member->dsp->name); | ||
| 597 | goto conf_software; | ||
| 598 | } | ||
| 599 | /* check if pipeline exists */ | ||
| 600 | if (member->dsp->pipeline.inuse) { | ||
| 601 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 602 | printk(KERN_DEBUG | ||
| 603 | "%s dsp %s cannot form a conf, because " | ||
| 604 | "pipeline exists\n", __func__, | ||
| 605 | member->dsp->name); | ||
| 606 | goto conf_software; | ||
| 607 | } | ||
| 608 | /* check if encryption is enabled */ | ||
| 609 | if (member->dsp->bf_enable) { | ||
| 610 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 611 | printk(KERN_DEBUG "%s dsp %s cannot form a " | ||
| 612 | "conf, because encryption is enabled\n", | ||
| 613 | __func__, member->dsp->name); | ||
| 614 | goto conf_software; | ||
| 615 | } | ||
| 616 | /* check if member is on a card with PCM support */ | ||
| 617 | if (member->dsp->features.pcm_id < 0) { | ||
| 618 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 619 | printk(KERN_DEBUG | ||
| 620 | "%s dsp %s cannot form a conf, because " | ||
| 621 | "dsp has no PCM bus\n", | ||
| 622 | __func__, member->dsp->name); | ||
| 623 | goto conf_software; | ||
| 624 | } | ||
| 625 | /* check if relations are on the same PCM bus */ | ||
| 626 | if (member->dsp->features.pcm_id != same_pcm) { | ||
| 627 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 628 | printk(KERN_DEBUG | ||
| 629 | "%s dsp %s cannot form a conf, because " | ||
| 630 | "dsp is on a different PCM bus than the " | ||
| 631 | "first dsp\n", | ||
| 632 | __func__, member->dsp->name); | ||
| 633 | goto conf_software; | ||
| 634 | } | ||
| 635 | /* determine if members are on the same hfc chip */ | ||
| 636 | if (same_hfc != member->dsp->features.hfc_id) | ||
| 637 | same_hfc = -1; | ||
| 638 | /* if there are members already in a conference */ | ||
| 639 | if (current_conf < 0 && member->dsp->hfc_conf >= 0) | ||
| 640 | current_conf = member->dsp->hfc_conf; | ||
| 641 | /* if any member is not in a conference */ | ||
| 642 | if (member->dsp->hfc_conf < 0) | ||
| 643 | all_conf = 0; | ||
| 644 | |||
| 645 | memb++; | ||
| 646 | } | ||
| 647 | |||
| 648 | /* if no member, this is an error */ | ||
| 649 | if (memb < 1) | ||
| 650 | return; | ||
| 651 | |||
| 652 | /* one member */ | ||
| 653 | if (memb == 1) { | ||
| 654 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 655 | printk(KERN_DEBUG | ||
| 656 | "%s conf %d cannot form a HW conference, " | ||
| 657 | "because dsp is alone\n", __func__, conf->id); | ||
| 658 | conf->hardware = 0; | ||
| 659 | conf->software = 0; | ||
| 660 | member = list_entry(conf->mlist.next, struct dsp_conf_member, | ||
| 661 | list); | ||
| 662 | dsp = member->dsp; | ||
| 663 | goto one_member; | ||
| 664 | } | ||
| 665 | |||
| 666 | /* | ||
| 667 | * ok, now we are sure that all members are on the same pcm. | ||
| 668 | * now we will see if we have only two members, so we can do | ||
| 669 | * crossconnections, which don't have any limitations. | ||
| 670 | */ | ||
| 671 | |||
| 672 | /* if we have only two members */ | ||
| 673 | if (memb == 2) { | ||
| 674 | member = list_entry(conf->mlist.next, struct dsp_conf_member, | ||
| 675 | list); | ||
| 676 | nextm = list_entry(member->list.next, struct dsp_conf_member, | ||
| 677 | list); | ||
| 678 | /* remove HFC conference if enabled */ | ||
| 679 | if (member->dsp->hfc_conf >= 0) { | ||
| 680 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 681 | printk(KERN_DEBUG | ||
| 682 | "%s removing %s from HFC conf %d because " | ||
| 683 | "two parties require only a PCM slot\n", | ||
| 684 | __func__, member->dsp->name, | ||
| 685 | member->dsp->hfc_conf); | ||
| 686 | dsp_cmx_hw_message(member->dsp, | ||
| 687 | MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0); | ||
| 688 | member->dsp->hfc_conf = -1; | ||
| 689 | } | ||
| 690 | if (nextm->dsp->hfc_conf >= 0) { | ||
| 691 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 692 | printk(KERN_DEBUG | ||
| 693 | "%s removing %s from HFC conf %d because " | ||
| 694 | "two parties require only a PCM slot\n", | ||
| 695 | __func__, nextm->dsp->name, | ||
| 696 | nextm->dsp->hfc_conf); | ||
| 697 | dsp_cmx_hw_message(nextm->dsp, | ||
| 698 | MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0); | ||
| 699 | nextm->dsp->hfc_conf = -1; | ||
| 700 | } | ||
| 701 | /* if members have two banks (and not on the same chip) */ | ||
| 702 | if (member->dsp->features.pcm_banks > 1 && | ||
| 703 | nextm->dsp->features.pcm_banks > 1 && | ||
| 704 | member->dsp->features.hfc_id != | ||
| 705 | nextm->dsp->features.hfc_id) { | ||
| 706 | /* if both members have same slots with crossed banks */ | ||
| 707 | if (member->dsp->pcm_slot_tx >= 0 && | ||
| 708 | member->dsp->pcm_slot_rx >= 0 && | ||
| 709 | nextm->dsp->pcm_slot_tx >= 0 && | ||
| 710 | nextm->dsp->pcm_slot_rx >= 0 && | ||
| 711 | nextm->dsp->pcm_slot_tx == | ||
| 712 | member->dsp->pcm_slot_rx && | ||
| 713 | nextm->dsp->pcm_slot_rx == | ||
| 714 | member->dsp->pcm_slot_tx && | ||
| 715 | nextm->dsp->pcm_slot_tx == | ||
| 716 | member->dsp->pcm_slot_tx && | ||
| 717 | member->dsp->pcm_bank_tx != | ||
| 718 | member->dsp->pcm_bank_rx && | ||
| 719 | nextm->dsp->pcm_bank_tx != | ||
| 720 | nextm->dsp->pcm_bank_rx) { | ||
| 721 | /* all members have same slot */ | ||
| 722 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 723 | printk(KERN_DEBUG | ||
| 724 | "%s dsp %s & %s stay joined on " | ||
| 725 | "PCM slot %d bank %d (TX) bank %d " | ||
| 726 | "(RX) (on different chips)\n", | ||
| 727 | __func__, | ||
| 728 | member->dsp->name, | ||
| 729 | nextm->dsp->name, | ||
| 730 | member->dsp->pcm_slot_tx, | ||
| 731 | member->dsp->pcm_bank_tx, | ||
| 732 | member->dsp->pcm_bank_rx); | ||
| 733 | conf->hardware = 0; | ||
| 734 | conf->software = 1; | ||
| 735 | return; | ||
| 736 | } | ||
| 737 | /* find a new slot */ | ||
| 738 | memset(freeslots, 1, sizeof(freeslots)); | ||
| 739 | list_for_each_entry(dsp, &dsp_ilist, list) { | ||
| 740 | if (dsp != member->dsp && | ||
| 741 | dsp != nextm->dsp && | ||
| 742 | member->dsp->features.pcm_id == | ||
| 743 | dsp->features.pcm_id) { | ||
| 744 | if (dsp->pcm_slot_rx >= 0 && | ||
| 745 | dsp->pcm_slot_rx < | ||
| 746 | sizeof(freeslots)) | ||
| 747 | freeslots[dsp->pcm_slot_tx] = 0; | ||
| 748 | if (dsp->pcm_slot_tx >= 0 && | ||
| 749 | dsp->pcm_slot_tx < | ||
| 750 | sizeof(freeslots)) | ||
| 751 | freeslots[dsp->pcm_slot_rx] = 0; | ||
| 752 | } | ||
| 753 | } | ||
| 754 | i = 0; | ||
| 755 | ii = member->dsp->features.pcm_slots; | ||
| 756 | while (i < ii) { | ||
| 757 | if (freeslots[i]) | ||
| 758 | break; | ||
| 759 | i++; | ||
| 760 | } | ||
| 761 | if (i == ii) { | ||
| 762 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 763 | printk(KERN_DEBUG | ||
| 764 | "%s no slot available for " | ||
| 765 | "%s & %s\n", __func__, | ||
| 766 | member->dsp->name, | ||
| 767 | nextm->dsp->name); | ||
| 768 | /* no more slots available */ | ||
| 769 | goto conf_software; | ||
| 770 | } | ||
| 771 | /* assign free slot */ | ||
| 772 | member->dsp->pcm_slot_tx = i; | ||
| 773 | member->dsp->pcm_slot_rx = i; | ||
| 774 | nextm->dsp->pcm_slot_tx = i; | ||
| 775 | nextm->dsp->pcm_slot_rx = i; | ||
| 776 | member->dsp->pcm_bank_rx = 0; | ||
| 777 | member->dsp->pcm_bank_tx = 1; | ||
| 778 | nextm->dsp->pcm_bank_rx = 1; | ||
| 779 | nextm->dsp->pcm_bank_tx = 0; | ||
| 780 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 781 | printk(KERN_DEBUG | ||
| 782 | "%s adding %s & %s to new PCM slot %d " | ||
| 783 | "(TX and RX on different chips) because " | ||
| 784 | "both members have not same slots\n", | ||
| 785 | __func__, | ||
| 786 | member->dsp->name, | ||
| 787 | nextm->dsp->name, | ||
| 788 | member->dsp->pcm_slot_tx); | ||
| 789 | dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, | ||
| 790 | member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, | ||
| 791 | member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx); | ||
| 792 | dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN, | ||
| 793 | nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx, | ||
| 794 | nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx); | ||
| 795 | conf->hardware = 1; | ||
| 796 | conf->software = 0; | ||
| 797 | return; | ||
| 798 | /* if members have one bank (or on the same chip) */ | ||
| 799 | } else { | ||
| 800 | /* if both members have different crossed slots */ | ||
| 801 | if (member->dsp->pcm_slot_tx >= 0 && | ||
| 802 | member->dsp->pcm_slot_rx >= 0 && | ||
| 803 | nextm->dsp->pcm_slot_tx >= 0 && | ||
| 804 | nextm->dsp->pcm_slot_rx >= 0 && | ||
| 805 | nextm->dsp->pcm_slot_tx == | ||
| 806 | member->dsp->pcm_slot_rx && | ||
| 807 | nextm->dsp->pcm_slot_rx == | ||
| 808 | member->dsp->pcm_slot_tx && | ||
| 809 | member->dsp->pcm_slot_tx != | ||
| 810 | member->dsp->pcm_slot_rx && | ||
| 811 | member->dsp->pcm_bank_tx == 0 && | ||
| 812 | member->dsp->pcm_bank_rx == 0 && | ||
| 813 | nextm->dsp->pcm_bank_tx == 0 && | ||
| 814 | nextm->dsp->pcm_bank_rx == 0) { | ||
| 815 | /* all members have same slot */ | ||
| 816 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 817 | printk(KERN_DEBUG | ||
| 818 | "%s dsp %s & %s stay joined on PCM " | ||
| 819 | "slot %d (TX) %d (RX) on same chip " | ||
| 820 | "or one bank PCM)\n", __func__, | ||
| 821 | member->dsp->name, | ||
| 822 | nextm->dsp->name, | ||
| 823 | member->dsp->pcm_slot_tx, | ||
| 824 | member->dsp->pcm_slot_rx); | ||
| 825 | conf->hardware = 0; | ||
| 826 | conf->software = 1; | ||
| 827 | return; | ||
| 828 | } | ||
| 829 | /* find two new slot */ | ||
| 830 | memset(freeslots, 1, sizeof(freeslots)); | ||
| 831 | list_for_each_entry(dsp, &dsp_ilist, list) { | ||
| 832 | if (dsp != member->dsp && | ||
| 833 | dsp != nextm->dsp && | ||
| 834 | member->dsp->features.pcm_id == | ||
| 835 | dsp->features.pcm_id) { | ||
| 836 | if (dsp->pcm_slot_rx >= 0 && | ||
| 837 | dsp->pcm_slot_rx < | ||
| 838 | sizeof(freeslots)) | ||
| 839 | freeslots[dsp->pcm_slot_tx] = 0; | ||
| 840 | if (dsp->pcm_slot_tx >= 0 && | ||
| 841 | dsp->pcm_slot_tx < | ||
| 842 | sizeof(freeslots)) | ||
| 843 | freeslots[dsp->pcm_slot_rx] = 0; | ||
| 844 | } | ||
| 845 | } | ||
| 846 | i1 = 0; | ||
| 847 | ii = member->dsp->features.pcm_slots; | ||
| 848 | while (i1 < ii) { | ||
| 849 | if (freeslots[i1]) | ||
| 850 | break; | ||
| 851 | i1++; | ||
| 852 | } | ||
| 853 | if (i1 == ii) { | ||
| 854 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 855 | printk(KERN_DEBUG | ||
| 856 | "%s no slot available " | ||
| 857 | "for %s & %s\n", __func__, | ||
| 858 | member->dsp->name, | ||
| 859 | nextm->dsp->name); | ||
| 860 | /* no more slots available */ | ||
| 861 | goto conf_software; | ||
| 862 | } | ||
| 863 | i2 = i1+1; | ||
| 864 | while (i2 < ii) { | ||
| 865 | if (freeslots[i2]) | ||
| 866 | break; | ||
| 867 | i2++; | ||
| 868 | } | ||
| 869 | if (i2 == ii) { | ||
| 870 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 871 | printk(KERN_DEBUG | ||
| 872 | "%s no slot available " | ||
| 873 | "for %s & %s\n", | ||
| 874 | __func__, | ||
| 875 | member->dsp->name, | ||
| 876 | nextm->dsp->name); | ||
| 877 | /* no more slots available */ | ||
| 878 | goto conf_software; | ||
| 879 | } | ||
| 880 | /* assign free slots */ | ||
| 881 | member->dsp->pcm_slot_tx = i1; | ||
| 882 | member->dsp->pcm_slot_rx = i2; | ||
| 883 | nextm->dsp->pcm_slot_tx = i2; | ||
| 884 | nextm->dsp->pcm_slot_rx = i1; | ||
| 885 | member->dsp->pcm_bank_rx = 0; | ||
| 886 | member->dsp->pcm_bank_tx = 0; | ||
| 887 | nextm->dsp->pcm_bank_rx = 0; | ||
| 888 | nextm->dsp->pcm_bank_tx = 0; | ||
| 889 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 890 | printk(KERN_DEBUG | ||
| 891 | "%s adding %s & %s to new PCM slot %d " | ||
| 892 | "(TX) %d (RX) on same chip or one bank " | ||
| 893 | "PCM, because both members have not " | ||
| 894 | "crossed slots\n", __func__, | ||
| 895 | member->dsp->name, | ||
| 896 | nextm->dsp->name, | ||
| 897 | member->dsp->pcm_slot_tx, | ||
| 898 | member->dsp->pcm_slot_rx); | ||
| 899 | dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, | ||
| 900 | member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, | ||
| 901 | member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx); | ||
| 902 | dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN, | ||
| 903 | nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx, | ||
| 904 | nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx); | ||
| 905 | conf->hardware = 1; | ||
| 906 | conf->software = 0; | ||
| 907 | return; | ||
| 908 | } | ||
| 909 | } | ||
| 910 | |||
| 911 | /* | ||
| 912 | * if we have more than two, we may check if we have a conference | ||
| 913 | * unit available on the chip. also all members must be on the same | ||
| 914 | */ | ||
| 915 | |||
| 916 | /* if not the same HFC chip */ | ||
| 917 | if (same_hfc < 0) { | ||
| 918 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 919 | printk(KERN_DEBUG | ||
| 920 | "%s conference %d cannot be formed, because " | ||
| 921 | "members are on different chips or not " | ||
| 922 | "on HFC chip\n", | ||
| 923 | __func__, conf->id); | ||
| 924 | goto conf_software; | ||
| 925 | } | ||
| 926 | |||
| 927 | /* for more than two members.. */ | ||
| 928 | |||
| 929 | /* in case of hdlc, we change to software */ | ||
| 930 | if (dsp->hdlc) | ||
| 931 | goto conf_software; | ||
| 932 | |||
| 933 | /* if all members already have the same conference */ | ||
| 934 | if (all_conf) | ||
| 935 | return; | ||
| 936 | |||
| 937 | /* | ||
| 938 | * if there is an existing conference, but not all members have joined | ||
| 939 | */ | ||
| 940 | if (current_conf >= 0) { | ||
| 941 | join_members: | ||
| 942 | list_for_each_entry(member, &conf->mlist, list) { | ||
| 943 | /* join to current conference */ | ||
| 944 | if (member->dsp->hfc_conf == current_conf) | ||
| 945 | continue; | ||
| 946 | /* get a free timeslot first */ | ||
| 947 | memset(freeslots, 1, sizeof(freeslots)); | ||
| 948 | list_for_each_entry(dsp, &dsp_ilist, list) { | ||
| 949 | /* | ||
| 950 | * not checking current member, because | ||
| 951 | * slot will be overwritten. | ||
| 952 | */ | ||
| 953 | if ( | ||
| 954 | dsp != member->dsp && | ||
| 955 | /* dsp must be on the same PCM */ | ||
| 956 | member->dsp->features.pcm_id == | ||
| 957 | dsp->features.pcm_id) { | ||
| 958 | /* dsp must be on a slot */ | ||
| 959 | if (dsp->pcm_slot_tx >= 0 && | ||
| 960 | dsp->pcm_slot_tx < | ||
| 961 | sizeof(freeslots)) | ||
| 962 | freeslots[dsp->pcm_slot_tx] = 0; | ||
| 963 | if (dsp->pcm_slot_rx >= 0 && | ||
| 964 | dsp->pcm_slot_rx < | ||
| 965 | sizeof(freeslots)) | ||
| 966 | freeslots[dsp->pcm_slot_rx] = 0; | ||
| 967 | } | ||
| 968 | } | ||
| 969 | i = 0; | ||
| 970 | ii = member->dsp->features.pcm_slots; | ||
| 971 | while (i < ii) { | ||
| 972 | if (freeslots[i]) | ||
| 973 | break; | ||
| 974 | i++; | ||
| 975 | } | ||
| 976 | if (i == ii) { | ||
| 977 | /* no more slots available */ | ||
| 978 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 979 | printk(KERN_DEBUG | ||
| 980 | "%s conference %d cannot be formed," | ||
| 981 | " because no slot free\n", | ||
| 982 | __func__, conf->id); | ||
| 983 | goto conf_software; | ||
| 984 | } | ||
| 985 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 986 | printk(KERN_DEBUG | ||
| 987 | "%s changing dsp %s to HW conference " | ||
| 988 | "%d slot %d\n", __func__, | ||
| 989 | member->dsp->name, current_conf, i); | ||
| 990 | /* assign free slot & set PCM & join conf */ | ||
| 991 | member->dsp->pcm_slot_tx = i; | ||
| 992 | member->dsp->pcm_slot_rx = i; | ||
| 993 | member->dsp->pcm_bank_tx = 2; /* loop */ | ||
| 994 | member->dsp->pcm_bank_rx = 2; | ||
| 995 | member->dsp->hfc_conf = current_conf; | ||
| 996 | dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, | ||
| 997 | i, 2, i, 2); | ||
| 998 | dsp_cmx_hw_message(member->dsp, | ||
| 999 | MISDN_CTRL_HFC_CONF_JOIN, current_conf, 0, 0, 0); | ||
| 1000 | } | ||
| 1001 | return; | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | /* | ||
| 1005 | * no member is in a conference yet, so we find a free one | ||
| 1006 | */ | ||
| 1007 | memset(freeunits, 1, sizeof(freeunits)); | ||
| 1008 | list_for_each_entry(dsp, &dsp_ilist, list) { | ||
| 1009 | /* dsp must be on the same chip */ | ||
| 1010 | if (dsp->features.hfc_id == same_hfc && | ||
| 1011 | /* dsp must have joined a HW conference */ | ||
| 1012 | dsp->hfc_conf >= 0 && | ||
| 1013 | /* slot must be within range */ | ||
| 1014 | dsp->hfc_conf < 8) | ||
| 1015 | freeunits[dsp->hfc_conf] = 0; | ||
| 1016 | } | ||
| 1017 | i = 0; | ||
| 1018 | ii = 8; | ||
| 1019 | while (i < ii) { | ||
| 1020 | if (freeunits[i]) | ||
| 1021 | break; | ||
| 1022 | i++; | ||
| 1023 | } | ||
| 1024 | if (i == ii) { | ||
| 1025 | /* no more conferences available */ | ||
| 1026 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 1027 | printk(KERN_DEBUG | ||
| 1028 | "%s conference %d cannot be formed, because " | ||
| 1029 | "no conference number free\n", | ||
| 1030 | __func__, conf->id); | ||
| 1031 | goto conf_software; | ||
| 1032 | } | ||
| 1033 | /* join all members */ | ||
| 1034 | current_conf = i; | ||
| 1035 | goto join_members; | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | |||
| 1039 | /* | ||
| 1040 | * conf_id != 0: join or change conference | ||
| 1041 | * conf_id == 0: split from conference if not already | ||
| 1042 | */ | ||
| 1043 | int | ||
| 1044 | dsp_cmx_conf(struct dsp *dsp, u32 conf_id) | ||
| 1045 | { | ||
| 1046 | int err; | ||
| 1047 | struct dsp_conf *conf; | ||
| 1048 | struct dsp_conf_member *member; | ||
| 1049 | |||
| 1050 | /* if conference doesn't change */ | ||
| 1051 | if (dsp->conf_id == conf_id) | ||
| 1052 | return 0; | ||
| 1053 | |||
| 1054 | /* first remove us from current conf */ | ||
| 1055 | if (dsp->conf_id) { | ||
| 1056 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 1057 | printk(KERN_DEBUG "removing us from conference %d\n", | ||
| 1058 | dsp->conf->id); | ||
| 1059 | /* remove us from conf */ | ||
| 1060 | conf = dsp->conf; | ||
| 1061 | err = dsp_cmx_del_conf_member(dsp); | ||
| 1062 | if (err) | ||
| 1063 | return err; | ||
| 1064 | dsp->conf_id = 0; | ||
| 1065 | |||
| 1066 | /* update hardware */ | ||
| 1067 | dsp_cmx_hardware(NULL, dsp); | ||
| 1068 | |||
| 1069 | /* conf now empty? */ | ||
| 1070 | if (list_empty(&conf->mlist)) { | ||
| 1071 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 1072 | printk(KERN_DEBUG | ||
| 1073 | "conference is empty, so we remove it.\n"); | ||
| 1074 | err = dsp_cmx_del_conf(conf); | ||
| 1075 | if (err) | ||
| 1076 | return err; | ||
| 1077 | } else { | ||
| 1078 | /* update members left on conf */ | ||
| 1079 | dsp_cmx_hardware(conf, NULL); | ||
| 1080 | } | ||
| 1081 | } | ||
| 1082 | |||
| 1083 | /* if split */ | ||
| 1084 | if (!conf_id) | ||
| 1085 | return 0; | ||
| 1086 | |||
| 1087 | /* now add us to conf */ | ||
| 1088 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 1089 | printk(KERN_DEBUG "searching conference %d\n", | ||
| 1090 | conf_id); | ||
| 1091 | conf = dsp_cmx_search_conf(conf_id); | ||
| 1092 | if (!conf) { | ||
| 1093 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 1094 | printk(KERN_DEBUG | ||
| 1095 | "conference doesn't exist yet, creating.\n"); | ||
| 1096 | /* the conference doesn't exist, so we create */ | ||
| 1097 | conf = dsp_cmx_new_conf(conf_id); | ||
| 1098 | if (!conf) | ||
| 1099 | return -EINVAL; | ||
| 1100 | } else if (!list_empty(&conf->mlist)) { | ||
| 1101 | member = list_entry(conf->mlist.next, struct dsp_conf_member, | ||
| 1102 | list); | ||
| 1103 | if (dsp->hdlc && !member->dsp->hdlc) { | ||
| 1104 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 1105 | printk(KERN_DEBUG | ||
| 1106 | "cannot join transparent conference.\n"); | ||
| 1107 | return -EINVAL; | ||
| 1108 | } | ||
| 1109 | if (!dsp->hdlc && member->dsp->hdlc) { | ||
| 1110 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 1111 | printk(KERN_DEBUG | ||
| 1112 | "cannot join hdlc conference.\n"); | ||
| 1113 | return -EINVAL; | ||
| 1114 | } | ||
| 1115 | } | ||
| 1116 | /* add conference member */ | ||
| 1117 | err = dsp_cmx_add_conf_member(dsp, conf); | ||
| 1118 | if (err) | ||
| 1119 | return err; | ||
| 1120 | dsp->conf_id = conf_id; | ||
| 1121 | |||
| 1122 | /* if we are alone, we do nothing! */ | ||
| 1123 | if (list_empty(&conf->mlist)) { | ||
| 1124 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 1125 | printk(KERN_DEBUG | ||
| 1126 | "we are alone in this conference, so exit.\n"); | ||
| 1127 | /* update hardware */ | ||
| 1128 | dsp_cmx_hardware(NULL, dsp); | ||
| 1129 | return 0; | ||
| 1130 | } | ||
| 1131 | |||
| 1132 | /* update members on conf */ | ||
| 1133 | dsp_cmx_hardware(conf, NULL); | ||
| 1134 | |||
| 1135 | return 0; | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | |||
| 1139 | /* | ||
| 1140 | * audio data is received from card | ||
| 1141 | */ | ||
| 1142 | void | ||
| 1143 | dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb) | ||
| 1144 | { | ||
| 1145 | u8 *d, *p; | ||
| 1146 | int len = skb->len; | ||
| 1147 | struct mISDNhead *hh = mISDN_HEAD_P(skb); | ||
| 1148 | int w, i, ii; | ||
| 1149 | |||
| 1150 | /* check if we have sompen */ | ||
| 1151 | if (len < 1) | ||
| 1152 | return; | ||
| 1153 | |||
| 1154 | /* half of the buffer should be larger than maximum packet size */ | ||
| 1155 | if (len >= CMX_BUFF_HALF) { | ||
| 1156 | printk(KERN_ERR | ||
| 1157 | "%s line %d: packet from card is too large (%d bytes). " | ||
| 1158 | "please make card send smaller packets OR increase " | ||
| 1159 | "CMX_BUFF_SIZE\n", __FILE__, __LINE__, len); | ||
| 1160 | return; | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | /* | ||
| 1164 | * initialize pointers if not already - | ||
| 1165 | * also add delay if requested by PH_SIGNAL | ||
| 1166 | */ | ||
| 1167 | if (dsp->rx_init) { | ||
| 1168 | dsp->rx_init = 0; | ||
| 1169 | if (dsp->features.unordered) { | ||
| 1170 | dsp->rx_R = (hh->id & CMX_BUFF_MASK); | ||
| 1171 | dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) | ||
| 1172 | & CMX_BUFF_MASK; | ||
| 1173 | } else { | ||
| 1174 | dsp->rx_R = 0; | ||
| 1175 | dsp->rx_W = dsp->cmx_delay; | ||
| 1176 | } | ||
| 1177 | } | ||
| 1178 | /* if frame contains time code, write directly */ | ||
| 1179 | if (dsp->features.unordered) { | ||
| 1180 | dsp->rx_W = (hh->id & CMX_BUFF_MASK); | ||
| 1181 | /* printk(KERN_DEBUG "%s %08x\n", dsp->name, hh->id); */ | ||
| 1182 | } | ||
| 1183 | /* | ||
| 1184 | * if we underrun (or maybe overrun), | ||
| 1185 | * we set our new read pointer, and write silence to buffer | ||
| 1186 | */ | ||
| 1187 | if (((dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) { | ||
| 1188 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 1189 | printk(KERN_DEBUG | ||
| 1190 | "cmx_receive(dsp=%lx): UNDERRUN (or overrun the " | ||
| 1191 | "maximum delay), adjusting read pointer! " | ||
| 1192 | "(inst %s)\n", (u_long)dsp, dsp->name); | ||
| 1193 | /* flush buffer */ | ||
| 1194 | if (dsp->features.unordered) { | ||
| 1195 | dsp->rx_R = (hh->id & CMX_BUFF_MASK); | ||
| 1196 | dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) | ||
| 1197 | & CMX_BUFF_MASK; | ||
| 1198 | } else { | ||
| 1199 | dsp->rx_R = 0; | ||
| 1200 | dsp->rx_W = dsp->cmx_delay; | ||
| 1201 | } | ||
| 1202 | memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); | ||
| 1203 | } | ||
| 1204 | /* if we have reached double delay, jump back to middle */ | ||
| 1205 | if (dsp->cmx_delay) | ||
| 1206 | if (((dsp->rx_W - dsp->rx_R) & CMX_BUFF_MASK) >= | ||
| 1207 | (dsp->cmx_delay << 1)) { | ||
| 1208 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 1209 | printk(KERN_DEBUG | ||
| 1210 | "cmx_receive(dsp=%lx): OVERRUN (because " | ||
| 1211 | "twice the delay is reached), adjusting " | ||
| 1212 | "read pointer! (inst %s)\n", | ||
| 1213 | (u_long)dsp, dsp->name); | ||
| 1214 | /* flush buffer */ | ||
| 1215 | if (dsp->features.unordered) { | ||
| 1216 | dsp->rx_R = (hh->id & CMX_BUFF_MASK); | ||
| 1217 | dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) | ||
| 1218 | & CMX_BUFF_MASK; | ||
| 1219 | } else { | ||
| 1220 | dsp->rx_R = 0; | ||
| 1221 | dsp->rx_W = dsp->cmx_delay; | ||
| 1222 | } | ||
| 1223 | memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | /* show where to write */ | ||
| 1227 | #ifdef CMX_DEBUG | ||
| 1228 | printk(KERN_DEBUG | ||
| 1229 | "cmx_receive(dsp=%lx): rx_R(dsp)=%05x rx_W(dsp)=%05x len=%d %s\n", | ||
| 1230 | (u_long)dsp, dsp->rx_R, dsp->rx_W, len, dsp->name); | ||
| 1231 | #endif | ||
| 1232 | |||
| 1233 | /* write data into rx_buffer */ | ||
| 1234 | p = skb->data; | ||
| 1235 | d = dsp->rx_buff; | ||
| 1236 | w = dsp->rx_W; | ||
| 1237 | i = 0; | ||
| 1238 | ii = len; | ||
| 1239 | while (i < ii) { | ||
| 1240 | d[w++ & CMX_BUFF_MASK] = *p++; | ||
| 1241 | i++; | ||
| 1242 | } | ||
| 1243 | |||
| 1244 | /* increase write-pointer */ | ||
| 1245 | dsp->rx_W = ((dsp->rx_W+len) & CMX_BUFF_MASK); | ||
| 1246 | } | ||
| 1247 | |||
| 1248 | |||
| 1249 | /* | ||
| 1250 | * send (mixed) audio data to card and control jitter | ||
| 1251 | */ | ||
| 1252 | static void | ||
| 1253 | dsp_cmx_send_member(struct dsp *dsp, int len, s32 *c, int members) | ||
| 1254 | { | ||
| 1255 | struct dsp_conf *conf = dsp->conf; | ||
| 1256 | struct dsp *member, *other; | ||
| 1257 | register s32 sample; | ||
| 1258 | u8 *d, *p, *q, *o_q; | ||
| 1259 | struct sk_buff *nskb, *txskb; | ||
| 1260 | int r, rr, t, tt, o_r, o_rr; | ||
| 1261 | int preload = 0; | ||
| 1262 | struct mISDNhead *hh, *thh; | ||
| 1263 | |||
| 1264 | /* don't process if: */ | ||
| 1265 | if (!dsp->b_active) { /* if not active */ | ||
| 1266 | dsp->last_tx = 0; | ||
| 1267 | return; | ||
| 1268 | } | ||
| 1269 | if (dsp->pcm_slot_tx >= 0 && /* connected to pcm slot */ | ||
| 1270 | dsp->tx_R == dsp->tx_W && /* AND no tx-data */ | ||
| 1271 | !(dsp->tone.tone && dsp->tone.software)) { /* AND not soft tones */ | ||
| 1272 | dsp->last_tx = 0; | ||
| 1273 | return; | ||
| 1274 | } | ||
| 1275 | |||
| 1276 | #ifdef CMX_DEBUG | ||
| 1277 | printk(KERN_DEBUG | ||
| 1278 | "SEND members=%d dsp=%s, conf=%p, rx_R=%05x rx_W=%05x\n", | ||
| 1279 | members, dsp->name, conf, dsp->rx_R, dsp->rx_W); | ||
| 1280 | #endif | ||
| 1281 | |||
| 1282 | /* preload if we have delay set */ | ||
| 1283 | if (dsp->cmx_delay && !dsp->last_tx) { | ||
| 1284 | preload = len; | ||
| 1285 | if (preload < 128) | ||
| 1286 | preload = 128; | ||
| 1287 | } | ||
| 1288 | |||
| 1289 | /* PREPARE RESULT */ | ||
| 1290 | nskb = mI_alloc_skb(len + preload, GFP_ATOMIC); | ||
| 1291 | if (!nskb) { | ||
| 1292 | printk(KERN_ERR | ||
| 1293 | "FATAL ERROR in mISDN_dsp.o: cannot alloc %d bytes\n", | ||
| 1294 | len + preload); | ||
| 1295 | return; | ||
| 1296 | } | ||
| 1297 | hh = mISDN_HEAD_P(nskb); | ||
| 1298 | hh->prim = PH_DATA_REQ; | ||
| 1299 | hh->id = 0; | ||
| 1300 | dsp->last_tx = 1; | ||
| 1301 | |||
| 1302 | /* set pointers, indexes and stuff */ | ||
| 1303 | member = dsp; | ||
| 1304 | p = dsp->tx_buff; /* transmit data */ | ||
| 1305 | q = dsp->rx_buff; /* received data */ | ||
| 1306 | d = skb_put(nskb, preload + len); /* result */ | ||
| 1307 | t = dsp->tx_R; /* tx-pointers */ | ||
| 1308 | tt = dsp->tx_W; | ||
| 1309 | r = dsp->rx_R; /* rx-pointers */ | ||
| 1310 | rr = (r + len) & CMX_BUFF_MASK; | ||
| 1311 | |||
| 1312 | /* preload with silence, if required */ | ||
| 1313 | if (preload) { | ||
| 1314 | memset(d, dsp_silence, preload); | ||
| 1315 | d += preload; | ||
| 1316 | } | ||
| 1317 | |||
| 1318 | /* PROCESS TONES/TX-DATA ONLY */ | ||
| 1319 | if (dsp->tone.tone && dsp->tone.software) { | ||
| 1320 | /* -> copy tone */ | ||
| 1321 | dsp_tone_copy(dsp, d, len); | ||
| 1322 | dsp->tx_R = 0; /* clear tx buffer */ | ||
| 1323 | dsp->tx_W = 0; | ||
| 1324 | goto send_packet; | ||
| 1325 | } | ||
| 1326 | /* if we have tx-data but do not use mixing */ | ||
| 1327 | if (!dsp->tx_mix && t != tt) { | ||
| 1328 | /* -> send tx-data and continue when not enough */ | ||
| 1329 | #ifdef CMX_TX_DEBUG | ||
| 1330 | sprintf(debugbuf, "TX sending (%04x-%04x)%p: ", t, tt, p); | ||
| 1331 | #endif | ||
| 1332 | while (r != rr && t != tt) { | ||
| 1333 | #ifdef CMX_TX_DEBUG | ||
| 1334 | if (strlen(debugbuf) < 48) | ||
| 1335 | sprintf(debugbuf+strlen(debugbuf), " %02x", p[t]); | ||
| 1336 | #endif | ||
| 1337 | *d++ = p[t]; /* write tx_buff */ | ||
| 1338 | t = (t+1) & CMX_BUFF_MASK; | ||
| 1339 | r = (r+1) & CMX_BUFF_MASK; | ||
| 1340 | } | ||
| 1341 | if (r == rr) { | ||
| 1342 | dsp->tx_R = t; | ||
| 1343 | #ifdef CMX_TX_DEBUG | ||
| 1344 | printk(KERN_DEBUG "%s\n", debugbuf); | ||
| 1345 | #endif | ||
| 1346 | goto send_packet; | ||
| 1347 | } | ||
| 1348 | } | ||
| 1349 | #ifdef CMX_TX_DEBUG | ||
| 1350 | printk(KERN_DEBUG "%s\n", debugbuf); | ||
| 1351 | #endif | ||
| 1352 | |||
| 1353 | /* PROCESS DATA (one member / no conf) */ | ||
| 1354 | if (!conf || members <= 1) { | ||
| 1355 | /* -> if echo is NOT enabled */ | ||
| 1356 | if (!dsp->echo) { | ||
| 1357 | /* -> send tx-data if available or use 0-volume */ | ||
| 1358 | while (r != rr && t != tt) { | ||
| 1359 | *d++ = p[t]; /* write tx_buff */ | ||
| 1360 | t = (t+1) & CMX_BUFF_MASK; | ||
| 1361 | r = (r+1) & CMX_BUFF_MASK; | ||
| 1362 | } | ||
| 1363 | if (r != rr) | ||
| 1364 | memset(d, dsp_silence, (rr-r)&CMX_BUFF_MASK); | ||
| 1365 | /* -> if echo is enabled */ | ||
| 1366 | } else { | ||
| 1367 | /* | ||
| 1368 | * -> mix tx-data with echo if available, | ||
| 1369 | * or use echo only | ||
| 1370 | */ | ||
| 1371 | while (r != rr && t != tt) { | ||
| 1372 | *d++ = dsp_audio_mix_law[(p[t]<<8)|q[r]]; | ||
| 1373 | t = (t+1) & CMX_BUFF_MASK; | ||
| 1374 | r = (r+1) & CMX_BUFF_MASK; | ||
| 1375 | } | ||
| 1376 | while (r != rr) { | ||
| 1377 | *d++ = q[r]; /* echo */ | ||
| 1378 | r = (r+1) & CMX_BUFF_MASK; | ||
| 1379 | } | ||
| 1380 | } | ||
| 1381 | dsp->tx_R = t; | ||
| 1382 | goto send_packet; | ||
| 1383 | } | ||
| 1384 | /* PROCESS DATA (two members) */ | ||
| 1385 | #ifdef CMX_CONF_DEBUG | ||
| 1386 | if (0) { | ||
| 1387 | #else | ||
| 1388 | if (members == 2) { | ||
| 1389 | #endif | ||
| 1390 | /* "other" becomes other party */ | ||
| 1391 | other = (list_entry(conf->mlist.next, | ||
| 1392 | struct dsp_conf_member, list))->dsp; | ||
| 1393 | if (other == member) | ||
| 1394 | other = (list_entry(conf->mlist.prev, | ||
| 1395 | struct dsp_conf_member, list))->dsp; | ||
| 1396 | o_q = other->rx_buff; /* received data */ | ||
| 1397 | o_rr = (other->rx_R + len) & CMX_BUFF_MASK; | ||
| 1398 | /* end of rx-pointer */ | ||
| 1399 | o_r = (o_rr - rr + r) & CMX_BUFF_MASK; | ||
| 1400 | /* start rx-pointer at current read position*/ | ||
| 1401 | /* -> if echo is NOT enabled */ | ||
| 1402 | if (!dsp->echo) { | ||
| 1403 | /* | ||
| 1404 | * -> copy other member's rx-data, | ||
| 1405 | * if tx-data is available, mix | ||
| 1406 | */ | ||
| 1407 | while (o_r != o_rr && t != tt) { | ||
| 1408 | *d++ = dsp_audio_mix_law[(p[t]<<8)|o_q[o_r]]; | ||
| 1409 | t = (t+1) & CMX_BUFF_MASK; | ||
| 1410 | o_r = (o_r+1) & CMX_BUFF_MASK; | ||
| 1411 | } | ||
| 1412 | while (o_r != o_rr) { | ||
| 1413 | *d++ = o_q[o_r]; | ||
| 1414 | o_r = (o_r+1) & CMX_BUFF_MASK; | ||
| 1415 | } | ||
| 1416 | /* -> if echo is enabled */ | ||
| 1417 | } else { | ||
| 1418 | /* | ||
| 1419 | * -> mix other member's rx-data with echo, | ||
| 1420 | * if tx-data is available, mix | ||
| 1421 | */ | ||
| 1422 | while (r != rr && t != tt) { | ||
| 1423 | sample = dsp_audio_law_to_s32[p[t]] + | ||
| 1424 | dsp_audio_law_to_s32[q[r]] + | ||
| 1425 | dsp_audio_law_to_s32[o_q[o_r]]; | ||
| 1426 | if (sample < -32768) | ||
| 1427 | sample = -32768; | ||
| 1428 | else if (sample > 32767) | ||
| 1429 | sample = 32767; | ||
| 1430 | *d++ = dsp_audio_s16_to_law[sample & 0xffff]; | ||
| 1431 | /* tx-data + rx_data + echo */ | ||
| 1432 | t = (t+1) & CMX_BUFF_MASK; | ||
| 1433 | r = (r+1) & CMX_BUFF_MASK; | ||
| 1434 | o_r = (o_r+1) & CMX_BUFF_MASK; | ||
| 1435 | } | ||
| 1436 | while (r != rr) { | ||
| 1437 | *d++ = dsp_audio_mix_law[(q[r]<<8)|o_q[o_r]]; | ||
| 1438 | r = (r+1) & CMX_BUFF_MASK; | ||
| 1439 | o_r = (o_r+1) & CMX_BUFF_MASK; | ||
| 1440 | } | ||
| 1441 | } | ||
| 1442 | dsp->tx_R = t; | ||
| 1443 | goto send_packet; | ||
| 1444 | } | ||
| 1445 | #ifdef DSP_NEVER_DEFINED | ||
| 1446 | } | ||
| 1447 | #endif | ||
| 1448 | /* PROCESS DATA (three or more members) */ | ||
| 1449 | /* -> if echo is NOT enabled */ | ||
| 1450 | if (!dsp->echo) { | ||
| 1451 | /* | ||
| 1452 | * -> substract rx-data from conf-data, | ||
| 1453 | * if tx-data is available, mix | ||
| 1454 | */ | ||
| 1455 | while (r != rr && t != tt) { | ||
| 1456 | sample = dsp_audio_law_to_s32[p[t]] + *c++ - | ||
| 1457 | dsp_audio_law_to_s32[q[r]]; | ||
| 1458 | if (sample < -32768) | ||
| 1459 | sample = -32768; | ||
| 1460 | else if (sample > 32767) | ||
| 1461 | sample = 32767; | ||
| 1462 | *d++ = dsp_audio_s16_to_law[sample & 0xffff]; | ||
| 1463 | /* conf-rx+tx */ | ||
| 1464 | r = (r+1) & CMX_BUFF_MASK; | ||
| 1465 | t = (t+1) & CMX_BUFF_MASK; | ||
| 1466 | } | ||
| 1467 | while (r != rr) { | ||
| 1468 | sample = *c++ - dsp_audio_law_to_s32[q[r]]; | ||
| 1469 | if (sample < -32768) | ||
| 1470 | sample = -32768; | ||
| 1471 | else if (sample > 32767) | ||
| 1472 | sample = 32767; | ||
| 1473 | *d++ = dsp_audio_s16_to_law[sample & 0xffff]; | ||
| 1474 | /* conf-rx */ | ||
| 1475 | r = (r+1) & CMX_BUFF_MASK; | ||
| 1476 | } | ||
| 1477 | /* -> if echo is enabled */ | ||
| 1478 | } else { | ||
| 1479 | /* | ||
| 1480 | * -> encode conf-data, if tx-data | ||
| 1481 | * is available, mix | ||
| 1482 | */ | ||
| 1483 | while (r != rr && t != tt) { | ||
| 1484 | sample = dsp_audio_law_to_s32[p[t]] + *c++; | ||
| 1485 | if (sample < -32768) | ||
| 1486 | sample = -32768; | ||
| 1487 | else if (sample > 32767) | ||
| 1488 | sample = 32767; | ||
| 1489 | *d++ = dsp_audio_s16_to_law[sample & 0xffff]; | ||
| 1490 | /* conf(echo)+tx */ | ||
| 1491 | t = (t+1) & CMX_BUFF_MASK; | ||
| 1492 | r = (r+1) & CMX_BUFF_MASK; | ||
| 1493 | } | ||
| 1494 | while (r != rr) { | ||
| 1495 | sample = *c++; | ||
| 1496 | if (sample < -32768) | ||
| 1497 | sample = -32768; | ||
| 1498 | else if (sample > 32767) | ||
| 1499 | sample = 32767; | ||
| 1500 | *d++ = dsp_audio_s16_to_law[sample & 0xffff]; | ||
| 1501 | /* conf(echo) */ | ||
| 1502 | r = (r+1) & CMX_BUFF_MASK; | ||
| 1503 | } | ||
| 1504 | } | ||
| 1505 | dsp->tx_R = t; | ||
| 1506 | goto send_packet; | ||
| 1507 | |||
| 1508 | send_packet: | ||
| 1509 | /* | ||
| 1510 | * send tx-data if enabled - don't filter, | ||
| 1511 | * becuase we want what we send, not what we filtered | ||
| 1512 | */ | ||
| 1513 | if (dsp->tx_data) { | ||
| 1514 | /* PREPARE RESULT */ | ||
| 1515 | txskb = mI_alloc_skb(len, GFP_ATOMIC); | ||
| 1516 | if (!txskb) { | ||
| 1517 | printk(KERN_ERR | ||
| 1518 | "FATAL ERROR in mISDN_dsp.o: " | ||
| 1519 | "cannot alloc %d bytes\n", len); | ||
| 1520 | } else { | ||
| 1521 | thh = mISDN_HEAD_P(txskb); | ||
| 1522 | thh->prim = DL_DATA_REQ; | ||
| 1523 | thh->id = 0; | ||
| 1524 | memcpy(skb_put(txskb, len), nskb->data+preload, len); | ||
| 1525 | /* queue (trigger later) */ | ||
| 1526 | skb_queue_tail(&dsp->sendq, txskb); | ||
| 1527 | } | ||
| 1528 | } | ||
| 1529 | /* adjust volume */ | ||
| 1530 | if (dsp->tx_volume) | ||
| 1531 | dsp_change_volume(nskb, dsp->tx_volume); | ||
| 1532 | /* pipeline */ | ||
| 1533 | if (dsp->pipeline.inuse) | ||
| 1534 | dsp_pipeline_process_tx(&dsp->pipeline, nskb->data, nskb->len); | ||
| 1535 | /* crypt */ | ||
| 1536 | if (dsp->bf_enable) | ||
| 1537 | dsp_bf_encrypt(dsp, nskb->data, nskb->len); | ||
| 1538 | /* queue and trigger */ | ||
| 1539 | skb_queue_tail(&dsp->sendq, nskb); | ||
| 1540 | schedule_work(&dsp->workq); | ||
| 1541 | } | ||
| 1542 | |||
| 1543 | u32 samplecount; | ||
| 1544 | struct timer_list dsp_spl_tl; | ||
| 1545 | u32 dsp_spl_jiffies; /* calculate the next time to fire */ | ||
| 1546 | u32 dsp_start_jiffies; /* jiffies at the time, the calculation begins */ | ||
| 1547 | struct timeval dsp_start_tv; /* time at start of calculation */ | ||
| 1548 | |||
| 1549 | void | ||
| 1550 | dsp_cmx_send(void *arg) | ||
| 1551 | { | ||
| 1552 | struct dsp_conf *conf; | ||
| 1553 | struct dsp_conf_member *member; | ||
| 1554 | struct dsp *dsp; | ||
| 1555 | int mustmix, members; | ||
| 1556 | s32 mixbuffer[MAX_POLL+100], *c; | ||
| 1557 | u8 *p, *q; | ||
| 1558 | int r, rr; | ||
| 1559 | int jittercheck = 0, delay, i; | ||
| 1560 | u_long flags; | ||
| 1561 | struct timeval tv; | ||
| 1562 | u32 elapsed; | ||
| 1563 | s16 length; | ||
| 1564 | |||
| 1565 | /* lock */ | ||
| 1566 | spin_lock_irqsave(&dsp_lock, flags); | ||
| 1567 | |||
| 1568 | if (!dsp_start_tv.tv_sec) { | ||
| 1569 | do_gettimeofday(&dsp_start_tv); | ||
| 1570 | length = dsp_poll; | ||
| 1571 | } else { | ||
| 1572 | do_gettimeofday(&tv); | ||
| 1573 | elapsed = ((tv.tv_sec - dsp_start_tv.tv_sec) * 8000) | ||
| 1574 | + ((s32)(tv.tv_usec / 125) - (dsp_start_tv.tv_usec / 125)); | ||
| 1575 | dsp_start_tv.tv_sec = tv.tv_sec; | ||
| 1576 | dsp_start_tv.tv_usec = tv.tv_usec; | ||
| 1577 | length = elapsed; | ||
| 1578 | } | ||
| 1579 | if (length > MAX_POLL + 100) | ||
| 1580 | length = MAX_POLL + 100; | ||
| 1581 | /* printk(KERN_DEBUG "len=%d dsp_count=0x%x.%04x dsp_poll_diff=0x%x.%04x\n", | ||
| 1582 | length, dsp_count >> 16, dsp_count & 0xffff, dsp_poll_diff >> 16, | ||
| 1583 | dsp_poll_diff & 0xffff); | ||
| 1584 | */ | ||
| 1585 | |||
| 1586 | /* | ||
| 1587 | * check if jitter needs to be checked | ||
| 1588 | * (this is about every second = 8192 samples) | ||
| 1589 | */ | ||
| 1590 | samplecount += length; | ||
| 1591 | if ((samplecount & 8191) < length) | ||
| 1592 | jittercheck = 1; | ||
| 1593 | |||
| 1594 | /* loop all members that do not require conference mixing */ | ||
| 1595 | list_for_each_entry(dsp, &dsp_ilist, list) { | ||
| 1596 | if (dsp->hdlc) | ||
| 1597 | continue; | ||
| 1598 | conf = dsp->conf; | ||
| 1599 | mustmix = 0; | ||
| 1600 | members = 0; | ||
| 1601 | if (conf) { | ||
| 1602 | members = count_list_member(&conf->mlist); | ||
| 1603 | #ifdef CMX_CONF_DEBUG | ||
| 1604 | if (conf->software && members > 1) | ||
| 1605 | #else | ||
| 1606 | if (conf->software && members > 2) | ||
| 1607 | #endif | ||
| 1608 | mustmix = 1; | ||
| 1609 | } | ||
| 1610 | |||
| 1611 | /* transmission required */ | ||
| 1612 | if (!mustmix) { | ||
| 1613 | dsp_cmx_send_member(dsp, length, mixbuffer, members); | ||
| 1614 | |||
| 1615 | /* | ||
| 1616 | * unused mixbuffer is given to prevent a | ||
| 1617 | * potential null-pointer-bug | ||
| 1618 | */ | ||
| 1619 | } | ||
| 1620 | } | ||
| 1621 | |||
| 1622 | /* loop all members that require conference mixing */ | ||
| 1623 | list_for_each_entry(conf, &conf_ilist, list) { | ||
| 1624 | /* count members and check hardware */ | ||
| 1625 | members = count_list_member(&conf->mlist); | ||
| 1626 | #ifdef CMX_CONF_DEBUG | ||
| 1627 | if (conf->software && members > 1) { | ||
| 1628 | #else | ||
| 1629 | if (conf->software && members > 2) { | ||
| 1630 | #endif | ||
| 1631 | /* check for hdlc conf */ | ||
| 1632 | member = list_entry(conf->mlist.next, | ||
| 1633 | struct dsp_conf_member, list); | ||
| 1634 | if (member->dsp->hdlc) | ||
| 1635 | continue; | ||
| 1636 | /* mix all data */ | ||
| 1637 | memset(mixbuffer, 0, length*sizeof(s32)); | ||
| 1638 | list_for_each_entry(member, &conf->mlist, list) { | ||
| 1639 | dsp = member->dsp; | ||
| 1640 | /* get range of data to mix */ | ||
| 1641 | c = mixbuffer; | ||
| 1642 | q = dsp->rx_buff; | ||
| 1643 | r = dsp->rx_R; | ||
| 1644 | rr = (r + length) & CMX_BUFF_MASK; | ||
| 1645 | /* add member's data */ | ||
| 1646 | while (r != rr) { | ||
| 1647 | *c++ += dsp_audio_law_to_s32[q[r]]; | ||
| 1648 | r = (r+1) & CMX_BUFF_MASK; | ||
| 1649 | } | ||
| 1650 | } | ||
| 1651 | |||
| 1652 | /* process each member */ | ||
| 1653 | list_for_each_entry(member, &conf->mlist, list) { | ||
| 1654 | /* transmission */ | ||
| 1655 | dsp_cmx_send_member(member->dsp, length, | ||
| 1656 | mixbuffer, members); | ||
| 1657 | } | ||
| 1658 | } | ||
| 1659 | } | ||
| 1660 | |||
| 1661 | /* delete rx-data, increment buffers, change pointers */ | ||
| 1662 | list_for_each_entry(dsp, &dsp_ilist, list) { | ||
| 1663 | if (dsp->hdlc) | ||
| 1664 | continue; | ||
| 1665 | p = dsp->rx_buff; | ||
| 1666 | q = dsp->tx_buff; | ||
| 1667 | r = dsp->rx_R; | ||
| 1668 | /* move receive pointer when receiving */ | ||
| 1669 | if (!dsp->rx_is_off) { | ||
| 1670 | rr = (r + length) & CMX_BUFF_MASK; | ||
| 1671 | /* delete rx-data */ | ||
| 1672 | while (r != rr) { | ||
| 1673 | p[r] = dsp_silence; | ||
| 1674 | r = (r+1) & CMX_BUFF_MASK; | ||
| 1675 | } | ||
| 1676 | /* increment rx-buffer pointer */ | ||
| 1677 | dsp->rx_R = r; /* write incremented read pointer */ | ||
| 1678 | } | ||
| 1679 | |||
| 1680 | /* check current rx_delay */ | ||
| 1681 | delay = (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK; | ||
| 1682 | if (delay >= CMX_BUFF_HALF) | ||
| 1683 | delay = 0; /* will be the delay before next write */ | ||
| 1684 | /* check for lower delay */ | ||
| 1685 | if (delay < dsp->rx_delay[0]) | ||
| 1686 | dsp->rx_delay[0] = delay; | ||
| 1687 | /* check current tx_delay */ | ||
| 1688 | delay = (dsp->tx_W-dsp->tx_R) & CMX_BUFF_MASK; | ||
| 1689 | if (delay >= CMX_BUFF_HALF) | ||
| 1690 | delay = 0; /* will be the delay before next write */ | ||
| 1691 | /* check for lower delay */ | ||
| 1692 | if (delay < dsp->tx_delay[0]) | ||
| 1693 | dsp->tx_delay[0] = delay; | ||
| 1694 | if (jittercheck) { | ||
| 1695 | /* find the lowest of all rx_delays */ | ||
| 1696 | delay = dsp->rx_delay[0]; | ||
| 1697 | i = 1; | ||
| 1698 | while (i < MAX_SECONDS_JITTER_CHECK) { | ||
| 1699 | if (delay > dsp->rx_delay[i]) | ||
| 1700 | delay = dsp->rx_delay[i]; | ||
| 1701 | i++; | ||
| 1702 | } | ||
| 1703 | /* | ||
| 1704 | * remove rx_delay only if we have delay AND we | ||
| 1705 | * have not preset cmx_delay | ||
| 1706 | */ | ||
| 1707 | if (delay && !dsp->cmx_delay) { | ||
| 1708 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 1709 | printk(KERN_DEBUG | ||
| 1710 | "%s lowest rx_delay of %d bytes for" | ||
| 1711 | " dsp %s are now removed.\n", | ||
| 1712 | __func__, delay, | ||
| 1713 | dsp->name); | ||
| 1714 | r = dsp->rx_R; | ||
| 1715 | rr = (r + delay) & CMX_BUFF_MASK; | ||
| 1716 | /* delete rx-data */ | ||
| 1717 | while (r != rr) { | ||
| 1718 | p[r] = dsp_silence; | ||
| 1719 | r = (r+1) & CMX_BUFF_MASK; | ||
| 1720 | } | ||
| 1721 | /* increment rx-buffer pointer */ | ||
| 1722 | dsp->rx_R = r; | ||
| 1723 | /* write incremented read pointer */ | ||
| 1724 | } | ||
| 1725 | /* find the lowest of all tx_delays */ | ||
| 1726 | delay = dsp->tx_delay[0]; | ||
| 1727 | i = 1; | ||
| 1728 | while (i < MAX_SECONDS_JITTER_CHECK) { | ||
| 1729 | if (delay > dsp->tx_delay[i]) | ||
| 1730 | delay = dsp->tx_delay[i]; | ||
| 1731 | i++; | ||
| 1732 | } | ||
| 1733 | /* | ||
| 1734 | * remove delay only if we have delay AND we | ||
| 1735 | * have enabled tx_dejitter | ||
| 1736 | */ | ||
| 1737 | if (delay && dsp->tx_dejitter) { | ||
| 1738 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 1739 | printk(KERN_DEBUG | ||
| 1740 | "%s lowest tx_delay of %d bytes for" | ||
| 1741 | " dsp %s are now removed.\n", | ||
| 1742 | __func__, delay, | ||
| 1743 | dsp->name); | ||
| 1744 | r = dsp->tx_R; | ||
| 1745 | rr = (r + delay) & CMX_BUFF_MASK; | ||
| 1746 | /* delete tx-data */ | ||
| 1747 | while (r != rr) { | ||
| 1748 | q[r] = dsp_silence; | ||
| 1749 | r = (r+1) & CMX_BUFF_MASK; | ||
| 1750 | } | ||
| 1751 | /* increment rx-buffer pointer */ | ||
| 1752 | dsp->tx_R = r; | ||
| 1753 | /* write incremented read pointer */ | ||
| 1754 | } | ||
| 1755 | /* scroll up delays */ | ||
| 1756 | i = MAX_SECONDS_JITTER_CHECK - 1; | ||
| 1757 | while (i) { | ||
| 1758 | dsp->rx_delay[i] = dsp->rx_delay[i-1]; | ||
| 1759 | dsp->tx_delay[i] = dsp->tx_delay[i-1]; | ||
| 1760 | i--; | ||
| 1761 | } | ||
| 1762 | dsp->tx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */ | ||
| 1763 | dsp->rx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */ | ||
| 1764 | } | ||
| 1765 | } | ||
| 1766 | |||
| 1767 | /* if next event would be in the past ... */ | ||
| 1768 | if ((s32)(dsp_spl_jiffies+dsp_tics-jiffies) <= 0) | ||
| 1769 | dsp_spl_jiffies = jiffies + 1; | ||
| 1770 | else | ||
| 1771 | dsp_spl_jiffies += dsp_tics; | ||
| 1772 | |||
| 1773 | dsp_spl_tl.expires = dsp_spl_jiffies; | ||
| 1774 | add_timer(&dsp_spl_tl); | ||
| 1775 | |||
| 1776 | /* unlock */ | ||
| 1777 | spin_unlock_irqrestore(&dsp_lock, flags); | ||
| 1778 | } | ||
| 1779 | |||
| 1780 | /* | ||
| 1781 | * audio data is transmitted from upper layer to the dsp | ||
| 1782 | */ | ||
| 1783 | void | ||
| 1784 | dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb) | ||
| 1785 | { | ||
| 1786 | u_int w, ww; | ||
| 1787 | u8 *d, *p; | ||
| 1788 | int space; /* todo: , l = skb->len; */ | ||
| 1789 | #ifdef CMX_TX_DEBUG | ||
| 1790 | char debugbuf[256] = ""; | ||
| 1791 | #endif | ||
| 1792 | |||
| 1793 | /* check if there is enough space, and then copy */ | ||
| 1794 | w = dsp->tx_W; | ||
| 1795 | ww = dsp->tx_R; | ||
| 1796 | p = dsp->tx_buff; | ||
| 1797 | d = skb->data; | ||
| 1798 | space = ww-w; | ||
| 1799 | if (space <= 0) | ||
| 1800 | space += CMX_BUFF_SIZE; | ||
| 1801 | /* write-pointer should not overrun nor reach read pointer */ | ||
| 1802 | if (space-1 < skb->len) | ||
| 1803 | /* write to the space we have left */ | ||
| 1804 | ww = (ww - 1) & CMX_BUFF_MASK; | ||
| 1805 | else | ||
| 1806 | /* write until all byte are copied */ | ||
| 1807 | ww = (w + skb->len) & CMX_BUFF_MASK; | ||
| 1808 | dsp->tx_W = ww; | ||
| 1809 | |||
| 1810 | /* show current buffer */ | ||
| 1811 | #ifdef CMX_DEBUG | ||
| 1812 | printk(KERN_DEBUG | ||
| 1813 | "cmx_transmit(dsp=%lx) %d bytes to 0x%x-0x%x. %s\n", | ||
| 1814 | (u_long)dsp, (ww-w)&CMX_BUFF_MASK, w, ww, dsp->name); | ||
| 1815 | #endif | ||
| 1816 | |||
| 1817 | /* copy transmit data to tx-buffer */ | ||
| 1818 | #ifdef CMX_TX_DEBUG | ||
| 1819 | sprintf(debugbuf, "TX getting (%04x-%04x)%p: ", w, ww, p); | ||
| 1820 | #endif | ||
| 1821 | while (w != ww) { | ||
| 1822 | #ifdef CMX_TX_DEBUG | ||
| 1823 | if (strlen(debugbuf) < 48) | ||
| 1824 | sprintf(debugbuf+strlen(debugbuf), " %02x", *d); | ||
| 1825 | #endif | ||
| 1826 | p[w] = *d++; | ||
| 1827 | w = (w+1) & CMX_BUFF_MASK; | ||
| 1828 | } | ||
| 1829 | #ifdef CMX_TX_DEBUG | ||
| 1830 | printk(KERN_DEBUG "%s\n", debugbuf); | ||
| 1831 | #endif | ||
| 1832 | |||
| 1833 | } | ||
| 1834 | |||
| 1835 | /* | ||
| 1836 | * hdlc data is received from card and sent to all members. | ||
| 1837 | */ | ||
| 1838 | void | ||
| 1839 | dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb) | ||
| 1840 | { | ||
| 1841 | struct sk_buff *nskb = NULL; | ||
| 1842 | struct dsp_conf_member *member; | ||
| 1843 | struct mISDNhead *hh; | ||
| 1844 | |||
| 1845 | /* not if not active */ | ||
| 1846 | if (!dsp->b_active) | ||
| 1847 | return; | ||
| 1848 | |||
| 1849 | /* check if we have sompen */ | ||
| 1850 | if (skb->len < 1) | ||
| 1851 | return; | ||
| 1852 | |||
| 1853 | /* no conf */ | ||
| 1854 | if (!dsp->conf) { | ||
| 1855 | /* in case of hardware (echo) */ | ||
| 1856 | if (dsp->pcm_slot_tx >= 0) | ||
| 1857 | return; | ||
| 1858 | if (dsp->echo) | ||
| 1859 | nskb = skb_clone(skb, GFP_ATOMIC); | ||
| 1860 | if (nskb) { | ||
| 1861 | hh = mISDN_HEAD_P(nskb); | ||
| 1862 | hh->prim = PH_DATA_REQ; | ||
| 1863 | hh->id = 0; | ||
| 1864 | skb_queue_tail(&dsp->sendq, nskb); | ||
| 1865 | schedule_work(&dsp->workq); | ||
| 1866 | } | ||
| 1867 | return; | ||
| 1868 | } | ||
| 1869 | /* in case of hardware conference */ | ||
| 1870 | if (dsp->conf->hardware) | ||
| 1871 | return; | ||
| 1872 | list_for_each_entry(member, &dsp->conf->mlist, list) { | ||
| 1873 | if (dsp->echo || member->dsp != dsp) { | ||
| 1874 | nskb = skb_clone(skb, GFP_ATOMIC); | ||
| 1875 | if (nskb) { | ||
| 1876 | hh = mISDN_HEAD_P(nskb); | ||
| 1877 | hh->prim = PH_DATA_REQ; | ||
| 1878 | hh->id = 0; | ||
| 1879 | skb_queue_tail(&member->dsp->sendq, nskb); | ||
| 1880 | schedule_work(&member->dsp->workq); | ||
| 1881 | } | ||
| 1882 | } | ||
| 1883 | } | ||
| 1884 | } | ||
| 1885 | |||
| 1886 | |||
diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c new file mode 100644 index 000000000000..2f10ed82c0db --- /dev/null +++ b/drivers/isdn/mISDN/dsp_core.c | |||
| @@ -0,0 +1,1191 @@ | |||
| 1 | /* | ||
| 2 | * Author Andreas Eversberg (jolly@eversberg.eu) | ||
| 3 | * Based on source code structure by | ||
| 4 | * Karsten Keil (keil@isdn4linux.de) | ||
| 5 | * | ||
| 6 | * This file is (c) under GNU PUBLIC LICENSE | ||
| 7 | * For changes and modifications please read | ||
| 8 | * ../../../Documentation/isdn/mISDN.cert | ||
| 9 | * | ||
| 10 | * Thanks to Karsten Keil (great drivers) | ||
| 11 | * Cologne Chip (great chips) | ||
| 12 | * | ||
| 13 | * This module does: | ||
| 14 | * Real-time tone generation | ||
| 15 | * DTMF detection | ||
| 16 | * Real-time cross-connection and conferrence | ||
| 17 | * Compensate jitter due to system load and hardware fault. | ||
| 18 | * All features are done in kernel space and will be realized | ||
| 19 | * using hardware, if available and supported by chip set. | ||
| 20 | * Blowfish encryption/decryption | ||
| 21 | */ | ||
| 22 | |||
| 23 | /* STRUCTURE: | ||
| 24 | * | ||
| 25 | * The dsp module provides layer 2 for b-channels (64kbit). It provides | ||
| 26 | * transparent audio forwarding with special digital signal processing: | ||
| 27 | * | ||
| 28 | * - (1) generation of tones | ||
| 29 | * - (2) detection of dtmf tones | ||
| 30 | * - (3) crossconnecting and conferences (clocking) | ||
| 31 | * - (4) echo generation for delay test | ||
| 32 | * - (5) volume control | ||
| 33 | * - (6) disable receive data | ||
| 34 | * - (7) pipeline | ||
| 35 | * - (8) encryption/decryption | ||
| 36 | * | ||
| 37 | * Look: | ||
| 38 | * TX RX | ||
| 39 | * ------upper layer------ | ||
| 40 | * | ^ | ||
| 41 | * | |(6) | ||
| 42 | * v | | ||
| 43 | * +-----+-------------+-----+ | ||
| 44 | * |(3)(4) | | ||
| 45 | * | CMX | | ||
| 46 | * | | | ||
| 47 | * | +-------------+ | ||
| 48 | * | | ^ | ||
| 49 | * | | | | ||
| 50 | * |+---------+| +----+----+ | ||
| 51 | * ||(1) || |(2) | | ||
| 52 | * || || | | | ||
| 53 | * || Tones || | DTMF | | ||
| 54 | * || || | | | ||
| 55 | * || || | | | ||
| 56 | * |+----+----+| +----+----+ | ||
| 57 | * +-----+-----+ ^ | ||
| 58 | * | | | ||
| 59 | * v | | ||
| 60 | * +----+----+ +----+----+ | ||
| 61 | * |(5) | |(5) | | ||
| 62 | * | | | | | ||
| 63 | * |TX Volume| |RX Volume| | ||
| 64 | * | | | | | ||
| 65 | * | | | | | ||
| 66 | * +----+----+ +----+----+ | ||
| 67 | * | ^ | ||
| 68 | * | | | ||
| 69 | * v | | ||
| 70 | * +----+-------------+----+ | ||
| 71 | * |(7) | | ||
| 72 | * | | | ||
| 73 | * | Pipeline Processing | | ||
| 74 | * | | | ||
| 75 | * | | | ||
| 76 | * +----+-------------+----+ | ||
| 77 | * | ^ | ||
| 78 | * | | | ||
| 79 | * v | | ||
| 80 | * +----+----+ +----+----+ | ||
| 81 | * |(8) | |(8) | | ||
| 82 | * | | | | | ||
| 83 | * | Encrypt | | Decrypt | | ||
| 84 | * | | | | | ||
| 85 | * | | | | | ||
| 86 | * +----+----+ +----+----+ | ||
| 87 | * | ^ | ||
| 88 | * | | | ||
| 89 | * v | | ||
| 90 | * ------card layer------ | ||
| 91 | * TX RX | ||
| 92 | * | ||
| 93 | * Above you can see the logical data flow. If software is used to do the | ||
| 94 | * process, it is actually the real data flow. If hardware is used, data | ||
| 95 | * may not flow, but hardware commands to the card, to provide the data flow | ||
| 96 | * as shown. | ||
| 97 | * | ||
| 98 | * NOTE: The channel must be activated in order to make dsp work, even if | ||
| 99 | * no data flow to the upper layer is intended. Activation can be done | ||
| 100 | * after and before controlling the setting using PH_CONTROL requests. | ||
| 101 | * | ||
| 102 | * DTMF: Will be detected by hardware if possible. It is done before CMX | ||
| 103 | * processing. | ||
| 104 | * | ||
| 105 | * Tones: Will be generated via software if endless looped audio fifos are | ||
| 106 | * not supported by hardware. Tones will override all data from CMX. | ||
| 107 | * It is not required to join a conference to use tones at any time. | ||
| 108 | * | ||
| 109 | * CMX: Is transparent when not used. When it is used, it will do | ||
| 110 | * crossconnections and conferences via software if not possible through | ||
| 111 | * hardware. If hardware capability is available, hardware is used. | ||
| 112 | * | ||
| 113 | * Echo: Is generated by CMX and is used to check performane of hard and | ||
| 114 | * software CMX. | ||
| 115 | * | ||
| 116 | * The CMX has special functions for conferences with one, two and more | ||
| 117 | * members. It will allow different types of data flow. Receive and transmit | ||
| 118 | * data to/form upper layer may be swithed on/off individually without loosing | ||
| 119 | * features of CMX, Tones and DTMF. | ||
| 120 | * | ||
| 121 | * Echo Cancellation: Sometimes we like to cancel echo from the interface. | ||
| 122 | * Note that a VoIP call may not have echo caused by the IP phone. The echo | ||
| 123 | * is generated by the telephone line connected to it. Because the delay | ||
| 124 | * is high, it becomes an echo. RESULT: Echo Cachelation is required if | ||
| 125 | * both echo AND delay is applied to an interface. | ||
| 126 | * Remember that software CMX always generates a more or less delay. | ||
| 127 | * | ||
| 128 | * If all used features can be realized in hardware, and if transmit and/or | ||
| 129 | * receive data ist disabled, the card may not send/receive any data at all. | ||
| 130 | * Not receiving is usefull if only announcements are played. Not sending is | ||
| 131 | * usefull if an answering machine records audio. Not sending and receiving is | ||
| 132 | * usefull during most states of the call. If supported by hardware, tones | ||
| 133 | * will be played without cpu load. Small PBXs and NT-Mode applications will | ||
| 134 | * not need expensive hardware when processing calls. | ||
| 135 | * | ||
| 136 | * | ||
| 137 | * LOCKING: | ||
| 138 | * | ||
| 139 | * When data is received from upper or lower layer (card), the complete dsp | ||
| 140 | * module is locked by a global lock. This lock MUST lock irq, because it | ||
| 141 | * must lock timer events by DSP poll timer. | ||
| 142 | * When data is ready to be transmitted down, the data is queued and sent | ||
| 143 | * outside lock and timer event. | ||
| 144 | * PH_CONTROL must not change any settings, join or split conference members | ||
| 145 | * during process of data. | ||
| 146 | * | ||
| 147 | * HDLC: | ||
| 148 | * | ||
| 149 | * It works quite the same as transparent, except that HDLC data is forwarded | ||
| 150 | * to all other conference members if no hardware bridging is possible. | ||
| 151 | * Send data will be writte to sendq. Sendq will be sent if confirm is received. | ||
| 152 | * Conference cannot join, if one member is not hdlc. | ||
| 153 | * | ||
| 154 | */ | ||
| 155 | |||
| 156 | #include <linux/delay.h> | ||
| 157 | #include <linux/mISDNif.h> | ||
| 158 | #include <linux/mISDNdsp.h> | ||
| 159 | #include <linux/module.h> | ||
| 160 | #include <linux/vmalloc.h> | ||
| 161 | #include "core.h" | ||
| 162 | #include "dsp.h" | ||
| 163 | |||
| 164 | const char *mISDN_dsp_revision = "2.0"; | ||
| 165 | |||
| 166 | static int debug; | ||
| 167 | static int options; | ||
| 168 | static int poll; | ||
| 169 | static int dtmfthreshold = 100; | ||
| 170 | |||
| 171 | MODULE_AUTHOR("Andreas Eversberg"); | ||
| 172 | module_param(debug, uint, S_IRUGO | S_IWUSR); | ||
| 173 | module_param(options, uint, S_IRUGO | S_IWUSR); | ||
| 174 | module_param(poll, uint, S_IRUGO | S_IWUSR); | ||
| 175 | module_param(dtmfthreshold, uint, S_IRUGO | S_IWUSR); | ||
| 176 | MODULE_LICENSE("GPL"); | ||
| 177 | |||
| 178 | /*int spinnest = 0;*/ | ||
| 179 | |||
| 180 | spinlock_t dsp_lock; /* global dsp lock */ | ||
| 181 | struct list_head dsp_ilist; | ||
| 182 | struct list_head conf_ilist; | ||
| 183 | int dsp_debug; | ||
| 184 | int dsp_options; | ||
| 185 | int dsp_poll, dsp_tics; | ||
| 186 | |||
| 187 | /* check if rx may be turned off or must be turned on */ | ||
| 188 | static void | ||
| 189 | dsp_rx_off_member(struct dsp *dsp) | ||
| 190 | { | ||
| 191 | struct mISDN_ctrl_req cq; | ||
| 192 | int rx_off = 1; | ||
| 193 | |||
| 194 | if (!dsp->features_rx_off) | ||
| 195 | return; | ||
| 196 | |||
| 197 | /* not disabled */ | ||
| 198 | if (!dsp->rx_disabled) | ||
| 199 | rx_off = 0; | ||
| 200 | /* software dtmf */ | ||
| 201 | else if (dsp->dtmf.software) | ||
| 202 | rx_off = 0; | ||
| 203 | /* echo in software */ | ||
| 204 | else if (dsp->echo && dsp->pcm_slot_tx < 0) | ||
| 205 | rx_off = 0; | ||
| 206 | /* bridge in software */ | ||
| 207 | else if (dsp->conf) { | ||
| 208 | if (dsp->conf->software) | ||
| 209 | rx_off = 0; | ||
| 210 | } | ||
| 211 | |||
| 212 | if (rx_off == dsp->rx_is_off) | ||
| 213 | return; | ||
| 214 | |||
| 215 | if (!dsp->ch.peer) { | ||
| 216 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 217 | printk(KERN_DEBUG "%s: no peer, no rx_off\n", | ||
| 218 | __func__); | ||
| 219 | return; | ||
| 220 | } | ||
| 221 | cq.op = MISDN_CTRL_RX_OFF; | ||
| 222 | cq.p1 = rx_off; | ||
| 223 | if (dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq)) { | ||
| 224 | printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n", | ||
| 225 | __func__); | ||
| 226 | return; | ||
| 227 | } | ||
| 228 | dsp->rx_is_off = rx_off; | ||
| 229 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 230 | printk(KERN_DEBUG "%s: %s set rx_off = %d\n", | ||
| 231 | __func__, dsp->name, rx_off); | ||
| 232 | } | ||
| 233 | static void | ||
| 234 | dsp_rx_off(struct dsp *dsp) | ||
| 235 | { | ||
| 236 | struct dsp_conf_member *member; | ||
| 237 | |||
| 238 | if (dsp_options & DSP_OPT_NOHARDWARE) | ||
| 239 | return; | ||
| 240 | |||
| 241 | /* no conf */ | ||
| 242 | if (!dsp->conf) { | ||
| 243 | dsp_rx_off_member(dsp); | ||
| 244 | return; | ||
| 245 | } | ||
| 246 | /* check all members in conf */ | ||
| 247 | list_for_each_entry(member, &dsp->conf->mlist, list) { | ||
| 248 | dsp_rx_off_member(member->dsp); | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | static int | ||
| 253 | dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb) | ||
| 254 | { | ||
| 255 | struct sk_buff *nskb; | ||
| 256 | int ret = 0; | ||
| 257 | int cont; | ||
| 258 | u8 *data; | ||
| 259 | int len; | ||
| 260 | |||
| 261 | if (skb->len < sizeof(int)) | ||
| 262 | printk(KERN_ERR "%s: PH_CONTROL message too short\n", __func__); | ||
| 263 | cont = *((int *)skb->data); | ||
| 264 | len = skb->len - sizeof(int); | ||
| 265 | data = skb->data + sizeof(int); | ||
| 266 | |||
| 267 | switch (cont) { | ||
| 268 | case DTMF_TONE_START: /* turn on DTMF */ | ||
| 269 | if (dsp->hdlc) { | ||
| 270 | ret = -EINVAL; | ||
| 271 | break; | ||
| 272 | } | ||
| 273 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 274 | printk(KERN_DEBUG "%s: start dtmf\n", __func__); | ||
| 275 | if (len == sizeof(int)) { | ||
| 276 | printk(KERN_NOTICE "changing DTMF Threshold " | ||
| 277 | "to %d\n", *((int *)data)); | ||
| 278 | dsp->dtmf.treshold = (*(int *)data) * 10000; | ||
| 279 | } | ||
| 280 | /* init goertzel */ | ||
| 281 | dsp_dtmf_goertzel_init(dsp); | ||
| 282 | |||
| 283 | /* check dtmf hardware */ | ||
| 284 | dsp_dtmf_hardware(dsp); | ||
| 285 | break; | ||
| 286 | case DTMF_TONE_STOP: /* turn off DTMF */ | ||
| 287 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 288 | printk(KERN_DEBUG "%s: stop dtmf\n", __func__); | ||
| 289 | dsp->dtmf.hardware = 0; | ||
| 290 | dsp->dtmf.software = 0; | ||
| 291 | break; | ||
| 292 | case DSP_CONF_JOIN: /* join / update conference */ | ||
| 293 | if (len < sizeof(int)) { | ||
| 294 | ret = -EINVAL; | ||
| 295 | break; | ||
| 296 | } | ||
| 297 | if (*((u32 *)data) == 0) | ||
| 298 | goto conf_split; | ||
| 299 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 300 | printk(KERN_DEBUG "%s: join conference %d\n", | ||
| 301 | __func__, *((u32 *)data)); | ||
| 302 | ret = dsp_cmx_conf(dsp, *((u32 *)data)); | ||
| 303 | /* dsp_cmx_hardware will also be called here */ | ||
| 304 | dsp_rx_off(dsp); | ||
| 305 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 306 | dsp_cmx_debug(dsp); | ||
| 307 | break; | ||
| 308 | case DSP_CONF_SPLIT: /* remove from conference */ | ||
| 309 | conf_split: | ||
| 310 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 311 | printk(KERN_DEBUG "%s: release conference\n", __func__); | ||
| 312 | ret = dsp_cmx_conf(dsp, 0); | ||
| 313 | /* dsp_cmx_hardware will also be called here */ | ||
| 314 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 315 | dsp_cmx_debug(dsp); | ||
| 316 | dsp_rx_off(dsp); | ||
| 317 | break; | ||
| 318 | case DSP_TONE_PATT_ON: /* play tone */ | ||
| 319 | if (dsp->hdlc) { | ||
| 320 | ret = -EINVAL; | ||
| 321 | break; | ||
| 322 | } | ||
| 323 | if (len < sizeof(int)) { | ||
| 324 | ret = -EINVAL; | ||
| 325 | break; | ||
| 326 | } | ||
| 327 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 328 | printk(KERN_DEBUG "%s: turn tone 0x%x on\n", | ||
| 329 | __func__, *((int *)skb->data)); | ||
| 330 | ret = dsp_tone(dsp, *((int *)data)); | ||
| 331 | if (!ret) { | ||
| 332 | dsp_cmx_hardware(dsp->conf, dsp); | ||
| 333 | dsp_rx_off(dsp); | ||
| 334 | } | ||
| 335 | if (!dsp->tone.tone) | ||
| 336 | goto tone_off; | ||
| 337 | break; | ||
| 338 | case DSP_TONE_PATT_OFF: /* stop tone */ | ||
| 339 | if (dsp->hdlc) { | ||
| 340 | ret = -EINVAL; | ||
| 341 | break; | ||
| 342 | } | ||
| 343 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 344 | printk(KERN_DEBUG "%s: turn tone off\n", __func__); | ||
| 345 | dsp_tone(dsp, 0); | ||
| 346 | dsp_cmx_hardware(dsp->conf, dsp); | ||
| 347 | dsp_rx_off(dsp); | ||
| 348 | /* reset tx buffers (user space data) */ | ||
| 349 | tone_off: | ||
| 350 | dsp->rx_W = 0; | ||
| 351 | dsp->rx_R = 0; | ||
| 352 | break; | ||
| 353 | case DSP_VOL_CHANGE_TX: /* change volume */ | ||
| 354 | if (dsp->hdlc) { | ||
| 355 | ret = -EINVAL; | ||
| 356 | break; | ||
| 357 | } | ||
| 358 | if (len < sizeof(int)) { | ||
| 359 | ret = -EINVAL; | ||
| 360 | break; | ||
| 361 | } | ||
| 362 | dsp->tx_volume = *((int *)data); | ||
| 363 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 364 | printk(KERN_DEBUG "%s: change tx vol to %d\n", | ||
| 365 | __func__, dsp->tx_volume); | ||
| 366 | dsp_cmx_hardware(dsp->conf, dsp); | ||
| 367 | dsp_dtmf_hardware(dsp); | ||
| 368 | dsp_rx_off(dsp); | ||
| 369 | break; | ||
| 370 | case DSP_VOL_CHANGE_RX: /* change volume */ | ||
| 371 | if (dsp->hdlc) { | ||
| 372 | ret = -EINVAL; | ||
| 373 | break; | ||
| 374 | } | ||
| 375 | if (len < sizeof(int)) { | ||
| 376 | ret = -EINVAL; | ||
| 377 | break; | ||
| 378 | } | ||
| 379 | dsp->rx_volume = *((int *)data); | ||
| 380 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 381 | printk(KERN_DEBUG "%s: change rx vol to %d\n", | ||
| 382 | __func__, dsp->tx_volume); | ||
| 383 | dsp_cmx_hardware(dsp->conf, dsp); | ||
| 384 | dsp_dtmf_hardware(dsp); | ||
| 385 | dsp_rx_off(dsp); | ||
| 386 | break; | ||
| 387 | case DSP_ECHO_ON: /* enable echo */ | ||
| 388 | dsp->echo = 1; /* soft echo */ | ||
| 389 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 390 | printk(KERN_DEBUG "%s: enable cmx-echo\n", __func__); | ||
| 391 | dsp_cmx_hardware(dsp->conf, dsp); | ||
| 392 | dsp_rx_off(dsp); | ||
| 393 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 394 | dsp_cmx_debug(dsp); | ||
| 395 | break; | ||
| 396 | case DSP_ECHO_OFF: /* disable echo */ | ||
| 397 | dsp->echo = 0; | ||
| 398 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 399 | printk(KERN_DEBUG "%s: disable cmx-echo\n", __func__); | ||
| 400 | dsp_cmx_hardware(dsp->conf, dsp); | ||
| 401 | dsp_rx_off(dsp); | ||
| 402 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 403 | dsp_cmx_debug(dsp); | ||
| 404 | break; | ||
| 405 | case DSP_RECEIVE_ON: /* enable receive to user space */ | ||
| 406 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 407 | printk(KERN_DEBUG "%s: enable receive to user " | ||
| 408 | "space\n", __func__); | ||
| 409 | dsp->rx_disabled = 0; | ||
| 410 | dsp_rx_off(dsp); | ||
| 411 | break; | ||
| 412 | case DSP_RECEIVE_OFF: /* disable receive to user space */ | ||
| 413 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 414 | printk(KERN_DEBUG "%s: disable receive to " | ||
| 415 | "user space\n", __func__); | ||
| 416 | dsp->rx_disabled = 1; | ||
| 417 | dsp_rx_off(dsp); | ||
| 418 | break; | ||
| 419 | case DSP_MIX_ON: /* enable mixing of tx data */ | ||
| 420 | if (dsp->hdlc) { | ||
| 421 | ret = -EINVAL; | ||
| 422 | break; | ||
| 423 | } | ||
| 424 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 425 | printk(KERN_DEBUG "%s: enable mixing of " | ||
| 426 | "tx-data with conf mebers\n", __func__); | ||
| 427 | dsp->tx_mix = 1; | ||
| 428 | dsp_cmx_hardware(dsp->conf, dsp); | ||
| 429 | dsp_rx_off(dsp); | ||
| 430 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 431 | dsp_cmx_debug(dsp); | ||
| 432 | break; | ||
| 433 | case DSP_MIX_OFF: /* disable mixing of tx data */ | ||
| 434 | if (dsp->hdlc) { | ||
| 435 | ret = -EINVAL; | ||
| 436 | break; | ||
| 437 | } | ||
| 438 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 439 | printk(KERN_DEBUG "%s: disable mixing of " | ||
| 440 | "tx-data with conf mebers\n", __func__); | ||
| 441 | dsp->tx_mix = 0; | ||
| 442 | dsp_cmx_hardware(dsp->conf, dsp); | ||
| 443 | dsp_rx_off(dsp); | ||
| 444 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 445 | dsp_cmx_debug(dsp); | ||
| 446 | break; | ||
| 447 | case DSP_TXDATA_ON: /* enable txdata */ | ||
| 448 | dsp->tx_data = 1; | ||
| 449 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 450 | printk(KERN_DEBUG "%s: enable tx-data\n", __func__); | ||
| 451 | dsp_cmx_hardware(dsp->conf, dsp); | ||
| 452 | dsp_rx_off(dsp); | ||
| 453 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 454 | dsp_cmx_debug(dsp); | ||
| 455 | break; | ||
| 456 | case DSP_TXDATA_OFF: /* disable txdata */ | ||
| 457 | dsp->tx_data = 0; | ||
| 458 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 459 | printk(KERN_DEBUG "%s: disable tx-data\n", __func__); | ||
| 460 | dsp_cmx_hardware(dsp->conf, dsp); | ||
| 461 | dsp_rx_off(dsp); | ||
| 462 | if (dsp_debug & DEBUG_DSP_CMX) | ||
| 463 | dsp_cmx_debug(dsp); | ||
| 464 | break; | ||
| 465 | case DSP_DELAY: /* use delay algorithm instead of dynamic | ||
| 466 | jitter algorithm */ | ||
| 467 | if (dsp->hdlc) { | ||
| 468 | ret = -EINVAL; | ||
| 469 | break; | ||
| 470 | } | ||
| 471 | if (len < sizeof(int)) { | ||
| 472 | ret = -EINVAL; | ||
| 473 | break; | ||
| 474 | } | ||
| 475 | dsp->cmx_delay = (*((int *)data)) << 3; | ||
| 476 | /* miliseconds to samples */ | ||
| 477 | if (dsp->cmx_delay >= (CMX_BUFF_HALF>>1)) | ||
| 478 | /* clip to half of maximum usable buffer | ||
| 479 | (half of half buffer) */ | ||
| 480 | dsp->cmx_delay = (CMX_BUFF_HALF>>1) - 1; | ||
| 481 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 482 | printk(KERN_DEBUG "%s: use delay algorithm to " | ||
| 483 | "compensate jitter (%d samples)\n", | ||
| 484 | __func__, dsp->cmx_delay); | ||
| 485 | break; | ||
| 486 | case DSP_JITTER: /* use dynamic jitter algorithm instead of | ||
| 487 | delay algorithm */ | ||
| 488 | if (dsp->hdlc) { | ||
| 489 | ret = -EINVAL; | ||
| 490 | break; | ||
| 491 | } | ||
| 492 | dsp->cmx_delay = 0; | ||
| 493 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 494 | printk(KERN_DEBUG "%s: use jitter algorithm to " | ||
| 495 | "compensate jitter\n", __func__); | ||
| 496 | break; | ||
| 497 | case DSP_TX_DEJITTER: /* use dynamic jitter algorithm for tx-buffer */ | ||
| 498 | if (dsp->hdlc) { | ||
| 499 | ret = -EINVAL; | ||
| 500 | break; | ||
| 501 | } | ||
| 502 | dsp->tx_dejitter = 1; | ||
| 503 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 504 | printk(KERN_DEBUG "%s: use dejitter on TX " | ||
| 505 | "buffer\n", __func__); | ||
| 506 | break; | ||
| 507 | case DSP_TX_DEJ_OFF: /* use tx-buffer without dejittering*/ | ||
| 508 | if (dsp->hdlc) { | ||
| 509 | ret = -EINVAL; | ||
| 510 | break; | ||
| 511 | } | ||
| 512 | dsp->tx_dejitter = 0; | ||
| 513 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 514 | printk(KERN_DEBUG "%s: use TX buffer without " | ||
| 515 | "dejittering\n", __func__); | ||
| 516 | break; | ||
| 517 | case DSP_PIPELINE_CFG: | ||
| 518 | if (dsp->hdlc) { | ||
| 519 | ret = -EINVAL; | ||
| 520 | break; | ||
| 521 | } | ||
| 522 | if (len > 0 && ((char *)data)[len - 1]) { | ||
| 523 | printk(KERN_DEBUG "%s: pipeline config string " | ||
| 524 | "is not NULL terminated!\n", __func__); | ||
| 525 | ret = -EINVAL; | ||
| 526 | } else { | ||
| 527 | dsp->pipeline.inuse = 1; | ||
| 528 | dsp_cmx_hardware(dsp->conf, dsp); | ||
| 529 | ret = dsp_pipeline_build(&dsp->pipeline, | ||
| 530 | len > 0 ? (char *)data : NULL); | ||
| 531 | dsp_cmx_hardware(dsp->conf, dsp); | ||
| 532 | dsp_rx_off(dsp); | ||
| 533 | } | ||
| 534 | break; | ||
| 535 | case DSP_BF_ENABLE_KEY: /* turn blowfish on */ | ||
| 536 | if (dsp->hdlc) { | ||
| 537 | ret = -EINVAL; | ||
| 538 | break; | ||
| 539 | } | ||
| 540 | if (len < 4 || len > 56) { | ||
| 541 | ret = -EINVAL; | ||
| 542 | break; | ||
| 543 | } | ||
| 544 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 545 | printk(KERN_DEBUG "%s: turn blowfish on (key " | ||
| 546 | "not shown)\n", __func__); | ||
| 547 | ret = dsp_bf_init(dsp, (u8 *)data, len); | ||
| 548 | /* set new cont */ | ||
| 549 | if (!ret) | ||
| 550 | cont = DSP_BF_ACCEPT; | ||
| 551 | else | ||
| 552 | cont = DSP_BF_REJECT; | ||
| 553 | /* send indication if it worked to set it */ | ||
| 554 | nskb = _alloc_mISDN_skb(PH_CONTROL_IND, MISDN_ID_ANY, | ||
| 555 | sizeof(int), &cont, GFP_ATOMIC); | ||
| 556 | if (nskb) { | ||
| 557 | if (dsp->up) { | ||
| 558 | if (dsp->up->send(dsp->up, nskb)) | ||
| 559 | dev_kfree_skb(nskb); | ||
| 560 | } else | ||
| 561 | dev_kfree_skb(nskb); | ||
| 562 | } | ||
| 563 | if (!ret) { | ||
| 564 | dsp_cmx_hardware(dsp->conf, dsp); | ||
| 565 | dsp_dtmf_hardware(dsp); | ||
| 566 | dsp_rx_off(dsp); | ||
| 567 | } | ||
| 568 | break; | ||
| 569 | case DSP_BF_DISABLE: /* turn blowfish off */ | ||
| 570 | if (dsp->hdlc) { | ||
| 571 | ret = -EINVAL; | ||
| 572 | break; | ||
| 573 | } | ||
| 574 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 575 | printk(KERN_DEBUG "%s: turn blowfish off\n", __func__); | ||
| 576 | dsp_bf_cleanup(dsp); | ||
| 577 | dsp_cmx_hardware(dsp->conf, dsp); | ||
| 578 | dsp_dtmf_hardware(dsp); | ||
| 579 | dsp_rx_off(dsp); | ||
| 580 | break; | ||
| 581 | default: | ||
| 582 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 583 | printk(KERN_DEBUG "%s: ctrl req %x unhandled\n", | ||
| 584 | __func__, cont); | ||
| 585 | ret = -EINVAL; | ||
| 586 | } | ||
| 587 | return ret; | ||
| 588 | } | ||
| 589 | |||
| 590 | static void | ||
| 591 | get_features(struct mISDNchannel *ch) | ||
| 592 | { | ||
| 593 | struct dsp *dsp = container_of(ch, struct dsp, ch); | ||
| 594 | struct mISDN_ctrl_req cq; | ||
| 595 | |||
| 596 | if (dsp_options & DSP_OPT_NOHARDWARE) | ||
| 597 | return; | ||
| 598 | if (!ch->peer) { | ||
| 599 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 600 | printk(KERN_DEBUG "%s: no peer, no features\n", | ||
| 601 | __func__); | ||
| 602 | return; | ||
| 603 | } | ||
| 604 | memset(&cq, 0, sizeof(cq)); | ||
| 605 | cq.op = MISDN_CTRL_GETOP; | ||
| 606 | if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq) < 0) { | ||
| 607 | printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", | ||
| 608 | __func__); | ||
| 609 | return; | ||
| 610 | } | ||
| 611 | if (cq.op & MISDN_CTRL_RX_OFF) | ||
| 612 | dsp->features_rx_off = 1; | ||
| 613 | if ((cq.op & MISDN_CTRL_HW_FEATURES_OP)) { | ||
| 614 | cq.op = MISDN_CTRL_HW_FEATURES; | ||
| 615 | *((u_long *)&cq.p1) = (u_long)&dsp->features; | ||
| 616 | if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq)) { | ||
| 617 | printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n", | ||
| 618 | __func__); | ||
| 619 | } | ||
| 620 | } else | ||
| 621 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 622 | printk(KERN_DEBUG "%s: features not supported for %s\n", | ||
| 623 | __func__, dsp->name); | ||
| 624 | } | ||
| 625 | |||
| 626 | static int | ||
| 627 | dsp_function(struct mISDNchannel *ch, struct sk_buff *skb) | ||
| 628 | { | ||
| 629 | struct dsp *dsp = container_of(ch, struct dsp, ch); | ||
| 630 | struct mISDNhead *hh; | ||
| 631 | int ret = 0; | ||
| 632 | u8 *digits; | ||
| 633 | int cont; | ||
| 634 | struct sk_buff *nskb; | ||
| 635 | u_long flags; | ||
| 636 | |||
| 637 | hh = mISDN_HEAD_P(skb); | ||
| 638 | switch (hh->prim) { | ||
| 639 | /* FROM DOWN */ | ||
| 640 | case (PH_DATA_CNF): | ||
| 641 | dsp->data_pending = 0; | ||
| 642 | /* trigger next hdlc frame, if any */ | ||
| 643 | if (dsp->hdlc) { | ||
| 644 | spin_lock_irqsave(&dsp_lock, flags); | ||
| 645 | if (dsp->b_active) | ||
| 646 | schedule_work(&dsp->workq); | ||
| 647 | spin_unlock_irqrestore(&dsp_lock, flags); | ||
| 648 | } | ||
| 649 | break; | ||
| 650 | case (PH_DATA_IND): | ||
| 651 | case (DL_DATA_IND): | ||
| 652 | if (skb->len < 1) { | ||
| 653 | ret = -EINVAL; | ||
| 654 | break; | ||
| 655 | } | ||
| 656 | if (dsp->rx_is_off) { | ||
| 657 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 658 | printk(KERN_DEBUG "%s: rx-data during rx_off" | ||
| 659 | " for %s\n", | ||
| 660 | __func__, dsp->name); | ||
| 661 | } | ||
| 662 | if (dsp->hdlc) { | ||
| 663 | /* hdlc */ | ||
| 664 | spin_lock_irqsave(&dsp_lock, flags); | ||
| 665 | dsp_cmx_hdlc(dsp, skb); | ||
| 666 | spin_unlock_irqrestore(&dsp_lock, flags); | ||
| 667 | if (dsp->rx_disabled) { | ||
| 668 | /* if receive is not allowed */ | ||
| 669 | break; | ||
| 670 | } | ||
| 671 | hh->prim = DL_DATA_IND; | ||
| 672 | if (dsp->up) | ||
| 673 | return dsp->up->send(dsp->up, skb); | ||
| 674 | break; | ||
| 675 | } | ||
| 676 | |||
| 677 | /* decrypt if enabled */ | ||
| 678 | if (dsp->bf_enable) | ||
| 679 | dsp_bf_decrypt(dsp, skb->data, skb->len); | ||
| 680 | /* pipeline */ | ||
| 681 | if (dsp->pipeline.inuse) | ||
| 682 | dsp_pipeline_process_rx(&dsp->pipeline, skb->data, | ||
| 683 | skb->len); | ||
| 684 | /* change volume if requested */ | ||
| 685 | if (dsp->rx_volume) | ||
| 686 | dsp_change_volume(skb, dsp->rx_volume); | ||
| 687 | |||
| 688 | /* check if dtmf soft decoding is turned on */ | ||
| 689 | if (dsp->dtmf.software) { | ||
| 690 | digits = dsp_dtmf_goertzel_decode(dsp, skb->data, | ||
| 691 | skb->len, (dsp_options&DSP_OPT_ULAW)?1:0); | ||
| 692 | while (*digits) { | ||
| 693 | if (dsp_debug & DEBUG_DSP_DTMF) | ||
| 694 | printk(KERN_DEBUG "%s: digit" | ||
| 695 | "(%c) to layer %s\n", | ||
| 696 | __func__, *digits, dsp->name); | ||
| 697 | cont = DTMF_TONE_VAL | *digits; | ||
| 698 | nskb = _alloc_mISDN_skb(PH_CONTROL_IND, | ||
| 699 | MISDN_ID_ANY, sizeof(int), &cont, | ||
| 700 | GFP_ATOMIC); | ||
| 701 | if (nskb) { | ||
| 702 | if (dsp->up) { | ||
| 703 | if (dsp->up->send( | ||
| 704 | dsp->up, nskb)) | ||
| 705 | dev_kfree_skb(nskb); | ||
| 706 | } else | ||
| 707 | dev_kfree_skb(nskb); | ||
| 708 | } | ||
| 709 | digits++; | ||
| 710 | } | ||
| 711 | } | ||
| 712 | /* we need to process receive data if software */ | ||
| 713 | spin_lock_irqsave(&dsp_lock, flags); | ||
| 714 | if (dsp->pcm_slot_tx < 0 && dsp->pcm_slot_rx < 0) { | ||
| 715 | /* process data from card at cmx */ | ||
| 716 | dsp_cmx_receive(dsp, skb); | ||
| 717 | } | ||
| 718 | spin_unlock_irqrestore(&dsp_lock, flags); | ||
| 719 | |||
| 720 | if (dsp->rx_disabled) { | ||
| 721 | /* if receive is not allowed */ | ||
| 722 | break; | ||
| 723 | } | ||
| 724 | hh->prim = DL_DATA_IND; | ||
| 725 | if (dsp->up) | ||
| 726 | return dsp->up->send(dsp->up, skb); | ||
| 727 | break; | ||
| 728 | case (PH_CONTROL_IND): | ||
| 729 | if (dsp_debug & DEBUG_DSP_DTMFCOEFF) | ||
| 730 | printk(KERN_DEBUG "%s: PH_CONTROL INDICATION " | ||
| 731 | "received: %x (len %d) %s\n", __func__, | ||
| 732 | hh->id, skb->len, dsp->name); | ||
| 733 | switch (hh->id) { | ||
| 734 | case (DTMF_HFC_COEF): /* getting coefficients */ | ||
| 735 | if (!dsp->dtmf.hardware) { | ||
| 736 | if (dsp_debug & DEBUG_DSP_DTMFCOEFF) | ||
| 737 | printk(KERN_DEBUG "%s: ignoring DTMF " | ||
| 738 | "coefficients from HFC\n", | ||
| 739 | __func__); | ||
| 740 | break; | ||
| 741 | } | ||
| 742 | digits = dsp_dtmf_goertzel_decode(dsp, skb->data, | ||
| 743 | skb->len, 2); | ||
| 744 | while (*digits) { | ||
| 745 | int k; | ||
| 746 | struct sk_buff *nskb; | ||
| 747 | if (dsp_debug & DEBUG_DSP_DTMF) | ||
| 748 | printk(KERN_DEBUG "%s: digit" | ||
| 749 | "(%c) to layer %s\n", | ||
| 750 | __func__, *digits, dsp->name); | ||
| 751 | k = *digits | DTMF_TONE_VAL; | ||
| 752 | nskb = _alloc_mISDN_skb(PH_CONTROL_IND, | ||
| 753 | MISDN_ID_ANY, sizeof(int), &k, | ||
| 754 | GFP_ATOMIC); | ||
| 755 | if (nskb) { | ||
| 756 | if (dsp->up) { | ||
| 757 | if (dsp->up->send( | ||
| 758 | dsp->up, nskb)) | ||
| 759 | dev_kfree_skb(nskb); | ||
| 760 | } else | ||
| 761 | dev_kfree_skb(nskb); | ||
| 762 | } | ||
| 763 | digits++; | ||
| 764 | } | ||
| 765 | break; | ||
| 766 | case (HFC_VOL_CHANGE_TX): /* change volume */ | ||
| 767 | if (skb->len != sizeof(int)) { | ||
| 768 | ret = -EINVAL; | ||
| 769 | break; | ||
| 770 | } | ||
| 771 | spin_lock_irqsave(&dsp_lock, flags); | ||
| 772 | dsp->tx_volume = *((int *)skb->data); | ||
| 773 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 774 | printk(KERN_DEBUG "%s: change tx volume to " | ||
| 775 | "%d\n", __func__, dsp->tx_volume); | ||
| 776 | dsp_cmx_hardware(dsp->conf, dsp); | ||
| 777 | dsp_dtmf_hardware(dsp); | ||
| 778 | dsp_rx_off(dsp); | ||
| 779 | spin_unlock_irqrestore(&dsp_lock, flags); | ||
| 780 | break; | ||
| 781 | default: | ||
| 782 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 783 | printk(KERN_DEBUG "%s: ctrl ind %x unhandled " | ||
| 784 | "%s\n", __func__, hh->id, dsp->name); | ||
| 785 | ret = -EINVAL; | ||
| 786 | } | ||
| 787 | break; | ||
| 788 | case (PH_ACTIVATE_IND): | ||
| 789 | case (PH_ACTIVATE_CNF): | ||
| 790 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 791 | printk(KERN_DEBUG "%s: b_channel is now active %s\n", | ||
| 792 | __func__, dsp->name); | ||
| 793 | /* bchannel now active */ | ||
| 794 | spin_lock_irqsave(&dsp_lock, flags); | ||
| 795 | dsp->b_active = 1; | ||
| 796 | dsp->data_pending = 0; | ||
| 797 | dsp->rx_init = 1; | ||
| 798 | /* rx_W and rx_R will be adjusted on first frame */ | ||
| 799 | dsp->rx_W = 0; | ||
| 800 | dsp->rx_R = 0; | ||
| 801 | memset(dsp->rx_buff, 0, sizeof(dsp->rx_buff)); | ||
| 802 | dsp_cmx_hardware(dsp->conf, dsp); | ||
| 803 | dsp_dtmf_hardware(dsp); | ||
| 804 | dsp_rx_off(dsp); | ||
| 805 | spin_unlock_irqrestore(&dsp_lock, flags); | ||
| 806 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 807 | printk(KERN_DEBUG "%s: done with activation, sending " | ||
| 808 | "confirm to user space. %s\n", __func__, | ||
| 809 | dsp->name); | ||
| 810 | /* send activation to upper layer */ | ||
| 811 | hh->prim = DL_ESTABLISH_CNF; | ||
| 812 | if (dsp->up) | ||
| 813 | return dsp->up->send(dsp->up, skb); | ||
| 814 | break; | ||
| 815 | case (PH_DEACTIVATE_IND): | ||
| 816 | case (PH_DEACTIVATE_CNF): | ||
| 817 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 818 | printk(KERN_DEBUG "%s: b_channel is now inactive %s\n", | ||
| 819 | __func__, dsp->name); | ||
| 820 | /* bchannel now inactive */ | ||
| 821 | spin_lock_irqsave(&dsp_lock, flags); | ||
| 822 | dsp->b_active = 0; | ||
| 823 | dsp->data_pending = 0; | ||
| 824 | dsp_cmx_hardware(dsp->conf, dsp); | ||
| 825 | dsp_rx_off(dsp); | ||
| 826 | spin_unlock_irqrestore(&dsp_lock, flags); | ||
| 827 | hh->prim = DL_RELEASE_CNF; | ||
| 828 | if (dsp->up) | ||
| 829 | return dsp->up->send(dsp->up, skb); | ||
| 830 | break; | ||
| 831 | /* FROM UP */ | ||
| 832 | case (DL_DATA_REQ): | ||
| 833 | case (PH_DATA_REQ): | ||
| 834 | if (skb->len < 1) { | ||
| 835 | ret = -EINVAL; | ||
| 836 | break; | ||
| 837 | } | ||
| 838 | if (dsp->hdlc) { | ||
| 839 | /* hdlc */ | ||
| 840 | spin_lock_irqsave(&dsp_lock, flags); | ||
| 841 | if (dsp->b_active) { | ||
| 842 | skb_queue_tail(&dsp->sendq, skb); | ||
| 843 | schedule_work(&dsp->workq); | ||
| 844 | } | ||
| 845 | spin_unlock_irqrestore(&dsp_lock, flags); | ||
| 846 | return 0; | ||
| 847 | } | ||
| 848 | /* send data to tx-buffer (if no tone is played) */ | ||
| 849 | if (!dsp->tone.tone) { | ||
| 850 | spin_lock_irqsave(&dsp_lock, flags); | ||
| 851 | dsp_cmx_transmit(dsp, skb); | ||
| 852 | spin_unlock_irqrestore(&dsp_lock, flags); | ||
| 853 | } | ||
| 854 | break; | ||
| 855 | case (PH_CONTROL_REQ): | ||
| 856 | spin_lock_irqsave(&dsp_lock, flags); | ||
| 857 | ret = dsp_control_req(dsp, hh, skb); | ||
| 858 | spin_unlock_irqrestore(&dsp_lock, flags); | ||
| 859 | break; | ||
| 860 | case (DL_ESTABLISH_REQ): | ||
| 861 | case (PH_ACTIVATE_REQ): | ||
| 862 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 863 | printk(KERN_DEBUG "%s: activating b_channel %s\n", | ||
| 864 | __func__, dsp->name); | ||
| 865 | if (dsp->dtmf.hardware || dsp->dtmf.software) | ||
| 866 | dsp_dtmf_goertzel_init(dsp); | ||
| 867 | get_features(ch); | ||
| 868 | /* send ph_activate */ | ||
| 869 | hh->prim = PH_ACTIVATE_REQ; | ||
| 870 | if (ch->peer) | ||
| 871 | return ch->recv(ch->peer, skb); | ||
| 872 | break; | ||
| 873 | case (DL_RELEASE_REQ): | ||
| 874 | case (PH_DEACTIVATE_REQ): | ||
| 875 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 876 | printk(KERN_DEBUG "%s: releasing b_channel %s\n", | ||
| 877 | __func__, dsp->name); | ||
| 878 | spin_lock_irqsave(&dsp_lock, flags); | ||
| 879 | dsp->tone.tone = 0; | ||
| 880 | dsp->tone.hardware = 0; | ||
| 881 | dsp->tone.software = 0; | ||
| 882 | if (timer_pending(&dsp->tone.tl)) | ||
| 883 | del_timer(&dsp->tone.tl); | ||
| 884 | if (dsp->conf) | ||
| 885 | dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be | ||
| 886 | called here */ | ||
| 887 | skb_queue_purge(&dsp->sendq); | ||
| 888 | spin_unlock_irqrestore(&dsp_lock, flags); | ||
| 889 | hh->prim = PH_DEACTIVATE_REQ; | ||
| 890 | if (ch->peer) | ||
| 891 | return ch->recv(ch->peer, skb); | ||
| 892 | break; | ||
| 893 | default: | ||
| 894 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 895 | printk(KERN_DEBUG "%s: msg %x unhandled %s\n", | ||
| 896 | __func__, hh->prim, dsp->name); | ||
| 897 | ret = -EINVAL; | ||
| 898 | } | ||
| 899 | if (!ret) | ||
| 900 | dev_kfree_skb(skb); | ||
| 901 | return ret; | ||
| 902 | } | ||
| 903 | |||
| 904 | static int | ||
| 905 | dsp_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) | ||
| 906 | { | ||
| 907 | struct dsp *dsp = container_of(ch, struct dsp, ch); | ||
| 908 | u_long flags; | ||
| 909 | int err = 0; | ||
| 910 | |||
| 911 | if (debug & DEBUG_DSP_CTRL) | ||
| 912 | printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd); | ||
| 913 | |||
| 914 | switch (cmd) { | ||
| 915 | case OPEN_CHANNEL: | ||
| 916 | break; | ||
| 917 | case CLOSE_CHANNEL: | ||
| 918 | if (dsp->ch.peer) | ||
| 919 | dsp->ch.peer->ctrl(dsp->ch.peer, CLOSE_CHANNEL, NULL); | ||
| 920 | |||
| 921 | /* wait until workqueue has finished, | ||
| 922 | * must lock here, or we may hit send-process currently | ||
| 923 | * queueing. */ | ||
| 924 | spin_lock_irqsave(&dsp_lock, flags); | ||
| 925 | dsp->b_active = 0; | ||
| 926 | spin_unlock_irqrestore(&dsp_lock, flags); | ||
| 927 | /* MUST not be locked, because it waits until queue is done. */ | ||
| 928 | cancel_work_sync(&dsp->workq); | ||
| 929 | spin_lock_irqsave(&dsp_lock, flags); | ||
| 930 | if (timer_pending(&dsp->tone.tl)) | ||
| 931 | del_timer(&dsp->tone.tl); | ||
| 932 | skb_queue_purge(&dsp->sendq); | ||
| 933 | if (dsp_debug & DEBUG_DSP_CTRL) | ||
| 934 | printk(KERN_DEBUG "%s: releasing member %s\n", | ||
| 935 | __func__, dsp->name); | ||
| 936 | dsp->b_active = 0; | ||
| 937 | dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be called | ||
| 938 | here */ | ||
| 939 | dsp_pipeline_destroy(&dsp->pipeline); | ||
| 940 | |||
| 941 | if (dsp_debug & DEBUG_DSP_CTRL) | ||
| 942 | printk(KERN_DEBUG "%s: remove & destroy object %s\n", | ||
| 943 | __func__, dsp->name); | ||
| 944 | list_del(&dsp->list); | ||
| 945 | spin_unlock_irqrestore(&dsp_lock, flags); | ||
| 946 | |||
| 947 | if (dsp_debug & DEBUG_DSP_CTRL) | ||
| 948 | printk(KERN_DEBUG "%s: dsp instance released\n", | ||
| 949 | __func__); | ||
| 950 | vfree(dsp); | ||
| 951 | module_put(THIS_MODULE); | ||
| 952 | break; | ||
| 953 | } | ||
| 954 | return err; | ||
| 955 | } | ||
| 956 | |||
| 957 | static void | ||
| 958 | dsp_send_bh(struct work_struct *work) | ||
| 959 | { | ||
| 960 | struct dsp *dsp = container_of(work, struct dsp, workq); | ||
| 961 | struct sk_buff *skb; | ||
| 962 | struct mISDNhead *hh; | ||
| 963 | |||
| 964 | if (dsp->hdlc && dsp->data_pending) | ||
| 965 | return; /* wait until data has been acknowledged */ | ||
| 966 | |||
| 967 | /* send queued data */ | ||
| 968 | while ((skb = skb_dequeue(&dsp->sendq))) { | ||
| 969 | /* in locked date, we must have still data in queue */ | ||
| 970 | if (dsp->data_pending) { | ||
| 971 | if (dsp_debug & DEBUG_DSP_CORE) | ||
| 972 | printk(KERN_DEBUG "%s: fifo full %s, this is " | ||
| 973 | "no bug!\n", __func__, dsp->name); | ||
| 974 | /* flush transparent data, if not acked */ | ||
| 975 | dev_kfree_skb(skb); | ||
| 976 | continue; | ||
| 977 | } | ||
| 978 | hh = mISDN_HEAD_P(skb); | ||
| 979 | if (hh->prim == DL_DATA_REQ) { | ||
| 980 | /* send packet up */ | ||
| 981 | if (dsp->up) { | ||
| 982 | if (dsp->up->send(dsp->up, skb)) | ||
| 983 | dev_kfree_skb(skb); | ||
| 984 | } else | ||
| 985 | dev_kfree_skb(skb); | ||
| 986 | } else { | ||
| 987 | /* send packet down */ | ||
| 988 | if (dsp->ch.peer) { | ||
| 989 | dsp->data_pending = 1; | ||
| 990 | if (dsp->ch.recv(dsp->ch.peer, skb)) { | ||
| 991 | dev_kfree_skb(skb); | ||
| 992 | dsp->data_pending = 0; | ||
| 993 | } | ||
| 994 | } else | ||
| 995 | dev_kfree_skb(skb); | ||
| 996 | } | ||
| 997 | } | ||
| 998 | } | ||
| 999 | |||
| 1000 | static int | ||
| 1001 | dspcreate(struct channel_req *crq) | ||
| 1002 | { | ||
| 1003 | struct dsp *ndsp; | ||
| 1004 | u_long flags; | ||
| 1005 | |||
| 1006 | if (crq->protocol != ISDN_P_B_L2DSP | ||
| 1007 | && crq->protocol != ISDN_P_B_L2DSPHDLC) | ||
| 1008 | return -EPROTONOSUPPORT; | ||
| 1009 | ndsp = vmalloc(sizeof(struct dsp)); | ||
| 1010 | if (!ndsp) { | ||
| 1011 | printk(KERN_ERR "%s: vmalloc struct dsp failed\n", __func__); | ||
| 1012 | return -ENOMEM; | ||
| 1013 | } | ||
| 1014 | memset(ndsp, 0, sizeof(struct dsp)); | ||
| 1015 | if (dsp_debug & DEBUG_DSP_CTRL) | ||
| 1016 | printk(KERN_DEBUG "%s: creating new dsp instance\n", __func__); | ||
| 1017 | |||
| 1018 | /* default enabled */ | ||
| 1019 | INIT_WORK(&ndsp->workq, (void *)dsp_send_bh); | ||
| 1020 | skb_queue_head_init(&ndsp->sendq); | ||
| 1021 | ndsp->ch.send = dsp_function; | ||
| 1022 | ndsp->ch.ctrl = dsp_ctrl; | ||
| 1023 | ndsp->up = crq->ch; | ||
| 1024 | crq->ch = &ndsp->ch; | ||
| 1025 | if (crq->protocol == ISDN_P_B_L2DSP) { | ||
| 1026 | crq->protocol = ISDN_P_B_RAW; | ||
| 1027 | ndsp->hdlc = 0; | ||
| 1028 | } else { | ||
| 1029 | crq->protocol = ISDN_P_B_HDLC; | ||
| 1030 | ndsp->hdlc = 1; | ||
| 1031 | } | ||
| 1032 | if (!try_module_get(THIS_MODULE)) | ||
| 1033 | printk(KERN_WARNING "%s:cannot get module\n", | ||
| 1034 | __func__); | ||
| 1035 | |||
| 1036 | sprintf(ndsp->name, "DSP_C%x(0x%p)", | ||
| 1037 | ndsp->up->st->dev->id + 1, ndsp); | ||
| 1038 | /* set frame size to start */ | ||
| 1039 | ndsp->features.hfc_id = -1; /* current PCM id */ | ||
| 1040 | ndsp->features.pcm_id = -1; /* current PCM id */ | ||
| 1041 | ndsp->pcm_slot_rx = -1; /* current CPM slot */ | ||
| 1042 | ndsp->pcm_slot_tx = -1; | ||
| 1043 | ndsp->pcm_bank_rx = -1; | ||
| 1044 | ndsp->pcm_bank_tx = -1; | ||
| 1045 | ndsp->hfc_conf = -1; /* current conference number */ | ||
| 1046 | /* set tone timer */ | ||
| 1047 | ndsp->tone.tl.function = (void *)dsp_tone_timeout; | ||
| 1048 | ndsp->tone.tl.data = (long) ndsp; | ||
| 1049 | init_timer(&ndsp->tone.tl); | ||
| 1050 | |||
| 1051 | if (dtmfthreshold < 20 || dtmfthreshold > 500) | ||
| 1052 | dtmfthreshold = 200; | ||
| 1053 | ndsp->dtmf.treshold = dtmfthreshold*10000; | ||
| 1054 | |||
| 1055 | /* init pipeline append to list */ | ||
| 1056 | spin_lock_irqsave(&dsp_lock, flags); | ||
| 1057 | dsp_pipeline_init(&ndsp->pipeline); | ||
| 1058 | list_add_tail(&ndsp->list, &dsp_ilist); | ||
| 1059 | spin_unlock_irqrestore(&dsp_lock, flags); | ||
| 1060 | |||
| 1061 | return 0; | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | |||
| 1065 | static struct Bprotocol DSP = { | ||
| 1066 | .Bprotocols = (1 << (ISDN_P_B_L2DSP & ISDN_P_B_MASK)) | ||
| 1067 | | (1 << (ISDN_P_B_L2DSPHDLC & ISDN_P_B_MASK)), | ||
| 1068 | .name = "dsp", | ||
| 1069 | .create = dspcreate | ||
| 1070 | }; | ||
| 1071 | |||
| 1072 | static int dsp_init(void) | ||
| 1073 | { | ||
| 1074 | int err; | ||
| 1075 | int tics; | ||
| 1076 | |||
| 1077 | printk(KERN_INFO "DSP modul %s\n", mISDN_dsp_revision); | ||
| 1078 | |||
| 1079 | dsp_options = options; | ||
| 1080 | dsp_debug = debug; | ||
| 1081 | |||
| 1082 | /* set packet size */ | ||
| 1083 | dsp_poll = poll; | ||
| 1084 | if (dsp_poll) { | ||
| 1085 | if (dsp_poll > MAX_POLL) { | ||
| 1086 | printk(KERN_ERR "%s: Wrong poll value (%d), use %d " | ||
| 1087 | "maximum.\n", __func__, poll, MAX_POLL); | ||
| 1088 | err = -EINVAL; | ||
| 1089 | return err; | ||
| 1090 | } | ||
| 1091 | if (dsp_poll < 8) { | ||
| 1092 | printk(KERN_ERR "%s: Wrong poll value (%d), use 8 " | ||
| 1093 | "minimum.\n", __func__, dsp_poll); | ||
| 1094 | err = -EINVAL; | ||
| 1095 | return err; | ||
| 1096 | } | ||
| 1097 | dsp_tics = poll * HZ / 8000; | ||
| 1098 | if (dsp_tics * 8000 != poll * HZ) { | ||
| 1099 | printk(KERN_INFO "mISDN_dsp: Cannot clock every %d " | ||
| 1100 | "samples (0,125 ms). It is not a multiple of " | ||
| 1101 | "%d HZ.\n", poll, HZ); | ||
| 1102 | err = -EINVAL; | ||
| 1103 | return err; | ||
| 1104 | } | ||
| 1105 | } else { | ||
| 1106 | poll = 8; | ||
| 1107 | while (poll <= MAX_POLL) { | ||
| 1108 | tics = poll * HZ / 8000; | ||
| 1109 | if (tics * 8000 == poll * HZ) { | ||
| 1110 | dsp_tics = tics; | ||
| 1111 | dsp_poll = poll; | ||
| 1112 | if (poll >= 64) | ||
| 1113 | break; | ||
| 1114 | } | ||
| 1115 | poll++; | ||
| 1116 | } | ||
| 1117 | } | ||
| 1118 | if (dsp_poll == 0) { | ||
| 1119 | printk(KERN_INFO "mISDN_dsp: There is no multiple of kernel " | ||
| 1120 | "clock that equals exactly the duration of 8-256 " | ||
| 1121 | "samples. (Choose kernel clock speed like 100, 250, " | ||
| 1122 | "300, 1000)\n"); | ||
| 1123 | err = -EINVAL; | ||
| 1124 | return err; | ||
| 1125 | } | ||
| 1126 | printk(KERN_INFO "mISDN_dsp: DSP clocks every %d samples. This equals " | ||
| 1127 | "%d jiffies.\n", dsp_poll, dsp_tics); | ||
| 1128 | |||
| 1129 | spin_lock_init(&dsp_lock); | ||
| 1130 | INIT_LIST_HEAD(&dsp_ilist); | ||
| 1131 | INIT_LIST_HEAD(&conf_ilist); | ||
| 1132 | |||
| 1133 | /* init conversion tables */ | ||
| 1134 | dsp_audio_generate_law_tables(); | ||
| 1135 | dsp_silence = (dsp_options&DSP_OPT_ULAW)?0xff:0x2a; | ||
| 1136 | dsp_audio_law_to_s32 = (dsp_options&DSP_OPT_ULAW)?dsp_audio_ulaw_to_s32: | ||
| 1137 | dsp_audio_alaw_to_s32; | ||
| 1138 | dsp_audio_generate_s2law_table(); | ||
| 1139 | dsp_audio_generate_seven(); | ||
| 1140 | dsp_audio_generate_mix_table(); | ||
| 1141 | if (dsp_options & DSP_OPT_ULAW) | ||
| 1142 | dsp_audio_generate_ulaw_samples(); | ||
| 1143 | dsp_audio_generate_volume_changes(); | ||
| 1144 | |||
| 1145 | err = dsp_pipeline_module_init(); | ||
| 1146 | if (err) { | ||
| 1147 | printk(KERN_ERR "mISDN_dsp: Can't initialize pipeline, " | ||
| 1148 | "error(%d)\n", err); | ||
| 1149 | return err; | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | err = mISDN_register_Bprotocol(&DSP); | ||
| 1153 | if (err) { | ||
| 1154 | printk(KERN_ERR "Can't register %s error(%d)\n", DSP.name, err); | ||
| 1155 | return err; | ||
| 1156 | } | ||
| 1157 | |||
| 1158 | /* set sample timer */ | ||
| 1159 | dsp_spl_tl.function = (void *)dsp_cmx_send; | ||
| 1160 | dsp_spl_tl.data = 0; | ||
| 1161 | init_timer(&dsp_spl_tl); | ||
| 1162 | dsp_spl_tl.expires = jiffies + dsp_tics; | ||
| 1163 | dsp_spl_jiffies = dsp_spl_tl.expires; | ||
| 1164 | add_timer(&dsp_spl_tl); | ||
| 1165 | |||
| 1166 | return 0; | ||
| 1167 | } | ||
| 1168 | |||
| 1169 | |||
| 1170 | static void dsp_cleanup(void) | ||
| 1171 | { | ||
| 1172 | mISDN_unregister_Bprotocol(&DSP); | ||
| 1173 | |||
| 1174 | if (timer_pending(&dsp_spl_tl)) | ||
| 1175 | del_timer(&dsp_spl_tl); | ||
| 1176 | |||
| 1177 | if (!list_empty(&dsp_ilist)) { | ||
| 1178 | printk(KERN_ERR "mISDN_dsp: Audio DSP object inst list not " | ||
| 1179 | "empty.\n"); | ||
| 1180 | } | ||
| 1181 | if (!list_empty(&conf_ilist)) { | ||
| 1182 | printk(KERN_ERR "mISDN_dsp: Conference list not empty. Not " | ||
| 1183 | "all memory freed.\n"); | ||
| 1184 | } | ||
| 1185 | |||
| 1186 | dsp_pipeline_module_exit(); | ||
| 1187 | } | ||
| 1188 | |||
| 1189 | module_init(dsp_init); | ||
| 1190 | module_exit(dsp_cleanup); | ||
| 1191 | |||
diff --git a/drivers/isdn/mISDN/dsp_dtmf.c b/drivers/isdn/mISDN/dsp_dtmf.c new file mode 100644 index 000000000000..efc371c1f0dc --- /dev/null +++ b/drivers/isdn/mISDN/dsp_dtmf.c | |||
| @@ -0,0 +1,303 @@ | |||
| 1 | /* | ||
| 2 | * DTMF decoder. | ||
| 3 | * | ||
| 4 | * Copyright by Andreas Eversberg (jolly@eversberg.eu) | ||
| 5 | * based on different decoders such as ISDN4Linux | ||
| 6 | * | ||
| 7 | * This software may be used and distributed according to the terms | ||
| 8 | * of the GNU General Public License, incorporated herein by reference. | ||
| 9 | * | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/mISDNif.h> | ||
| 13 | #include <linux/mISDNdsp.h> | ||
| 14 | #include "core.h" | ||
| 15 | #include "dsp.h" | ||
| 16 | |||
| 17 | #define NCOEFF 8 /* number of frequencies to be analyzed */ | ||
| 18 | |||
| 19 | /* For DTMF recognition: | ||
| 20 | * 2 * cos(2 * PI * k / N) precalculated for all k | ||
| 21 | */ | ||
| 22 | static u64 cos2pik[NCOEFF] = | ||
| 23 | { | ||
| 24 | /* k << 15 (source: hfc-4s/8s documentation (www.colognechip.de)) */ | ||
| 25 | 55960, 53912, 51402, 48438, 38146, 32650, 26170, 18630 | ||
| 26 | }; | ||
| 27 | |||
| 28 | /* digit matrix */ | ||
| 29 | static char dtmf_matrix[4][4] = | ||
| 30 | { | ||
| 31 | {'1', '2', '3', 'A'}, | ||
| 32 | {'4', '5', '6', 'B'}, | ||
| 33 | {'7', '8', '9', 'C'}, | ||
| 34 | {'*', '0', '#', 'D'} | ||
| 35 | }; | ||
| 36 | |||
| 37 | /* dtmf detection using goertzel algorithm | ||
| 38 | * init function | ||
| 39 | */ | ||
| 40 | void dsp_dtmf_goertzel_init(struct dsp *dsp) | ||
| 41 | { | ||
| 42 | dsp->dtmf.size = 0; | ||
| 43 | dsp->dtmf.lastwhat = '\0'; | ||
| 44 | dsp->dtmf.lastdigit = '\0'; | ||
| 45 | dsp->dtmf.count = 0; | ||
| 46 | } | ||
| 47 | |||
| 48 | /* check for hardware or software features | ||
| 49 | */ | ||
| 50 | void dsp_dtmf_hardware(struct dsp *dsp) | ||
| 51 | { | ||
| 52 | int hardware = 1; | ||
| 53 | |||
| 54 | if (!dsp->features.hfc_dtmf) | ||
| 55 | hardware = 0; | ||
| 56 | |||
| 57 | /* check for volume change */ | ||
| 58 | if (dsp->tx_volume) { | ||
| 59 | if (dsp_debug & DEBUG_DSP_DTMF) | ||
| 60 | printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " | ||
| 61 | "because tx_volume is changed\n", | ||
| 62 | __func__, dsp->name); | ||
| 63 | hardware = 0; | ||
| 64 | } | ||
| 65 | if (dsp->rx_volume) { | ||
| 66 | if (dsp_debug & DEBUG_DSP_DTMF) | ||
| 67 | printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " | ||
| 68 | "because rx_volume is changed\n", | ||
| 69 | __func__, dsp->name); | ||
| 70 | hardware = 0; | ||
| 71 | } | ||
| 72 | /* check if encryption is enabled */ | ||
| 73 | if (dsp->bf_enable) { | ||
| 74 | if (dsp_debug & DEBUG_DSP_DTMF) | ||
| 75 | printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " | ||
| 76 | "because encryption is enabled\n", | ||
| 77 | __func__, dsp->name); | ||
| 78 | hardware = 0; | ||
| 79 | } | ||
| 80 | /* check if pipeline exists */ | ||
| 81 | if (dsp->pipeline.inuse) { | ||
| 82 | if (dsp_debug & DEBUG_DSP_DTMF) | ||
| 83 | printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " | ||
| 84 | "because pipeline exists.\n", | ||
| 85 | __func__, dsp->name); | ||
| 86 | hardware = 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | dsp->dtmf.hardware = hardware; | ||
| 90 | dsp->dtmf.software = !hardware; | ||
| 91 | } | ||
| 92 | |||
| 93 | |||
| 94 | /************************************************************* | ||
| 95 | * calculate the coefficients of the given sample and decode * | ||
| 96 | *************************************************************/ | ||
| 97 | |||
| 98 | /* the given sample is decoded. if the sample is not long enough for a | ||
| 99 | * complete frame, the decoding is finished and continued with the next | ||
| 100 | * call of this function. | ||
| 101 | * | ||
| 102 | * the algorithm is very good for detection with a minimum of errors. i | ||
| 103 | * tested it allot. it even works with very short tones (40ms). the only | ||
| 104 | * disadvantage is, that it doesn't work good with different volumes of both | ||
| 105 | * tones. this will happen, if accoustically coupled dialers are used. | ||
| 106 | * it sometimes detects tones during speach, which is normal for decoders. | ||
| 107 | * use sequences to given commands during calls. | ||
| 108 | * | ||
| 109 | * dtmf - points to a structure of the current dtmf state | ||
| 110 | * spl and len - the sample | ||
| 111 | * fmt - 0 = alaw, 1 = ulaw, 2 = coefficients from HFC DTMF hw-decoder | ||
| 112 | */ | ||
| 113 | |||
| 114 | u8 | ||
| 115 | *dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len, int fmt) | ||
| 116 | { | ||
| 117 | u8 what; | ||
| 118 | int size; | ||
| 119 | signed short *buf; | ||
| 120 | s32 sk, sk1, sk2; | ||
| 121 | int k, n, i; | ||
| 122 | s32 *hfccoeff; | ||
| 123 | s32 result[NCOEFF], tresh, treshl; | ||
| 124 | int lowgroup, highgroup; | ||
| 125 | s64 cos2pik_; | ||
| 126 | |||
| 127 | dsp->dtmf.digits[0] = '\0'; | ||
| 128 | |||
| 129 | /* Note: The function will loop until the buffer has not enough samples | ||
| 130 | * left to decode a full frame. | ||
| 131 | */ | ||
| 132 | again: | ||
| 133 | /* convert samples */ | ||
| 134 | size = dsp->dtmf.size; | ||
| 135 | buf = dsp->dtmf.buffer; | ||
| 136 | switch (fmt) { | ||
| 137 | case 0: /* alaw */ | ||
| 138 | case 1: /* ulaw */ | ||
| 139 | while (size < DSP_DTMF_NPOINTS && len) { | ||
| 140 | buf[size++] = dsp_audio_law_to_s32[*data++]; | ||
| 141 | len--; | ||
| 142 | } | ||
| 143 | break; | ||
| 144 | |||
| 145 | case 2: /* HFC coefficients */ | ||
| 146 | default: | ||
| 147 | if (len < 64) { | ||
| 148 | if (len > 0) | ||
| 149 | printk(KERN_ERR "%s: coefficients have invalid " | ||
| 150 | "size. (is=%d < must=%d)\n", | ||
| 151 | __func__, len, 64); | ||
| 152 | return dsp->dtmf.digits; | ||
| 153 | } | ||
| 154 | hfccoeff = (s32 *)data; | ||
| 155 | for (k = 0; k < NCOEFF; k++) { | ||
| 156 | sk2 = (*hfccoeff++)>>4; | ||
| 157 | sk = (*hfccoeff++)>>4; | ||
| 158 | if (sk > 32767 || sk < -32767 || sk2 > 32767 | ||
| 159 | || sk2 < -32767) | ||
| 160 | printk(KERN_WARNING | ||
| 161 | "DTMF-Detection overflow\n"); | ||
| 162 | /* compute |X(k)|**2 */ | ||
| 163 | result[k] = | ||
| 164 | (sk * sk) - | ||
| 165 | (((cos2pik[k] * sk) >> 15) * sk2) + | ||
| 166 | (sk2 * sk2); | ||
| 167 | } | ||
| 168 | data += 64; | ||
| 169 | len -= 64; | ||
| 170 | goto coefficients; | ||
| 171 | break; | ||
| 172 | } | ||
| 173 | dsp->dtmf.size = size; | ||
| 174 | |||
| 175 | if (size < DSP_DTMF_NPOINTS) | ||
| 176 | return dsp->dtmf.digits; | ||
| 177 | |||
| 178 | dsp->dtmf.size = 0; | ||
| 179 | |||
| 180 | /* now we have a full buffer of signed long samples - we do goertzel */ | ||
| 181 | for (k = 0; k < NCOEFF; k++) { | ||
| 182 | sk = 0; | ||
| 183 | sk1 = 0; | ||
| 184 | sk2 = 0; | ||
| 185 | buf = dsp->dtmf.buffer; | ||
| 186 | cos2pik_ = cos2pik[k]; | ||
| 187 | for (n = 0; n < DSP_DTMF_NPOINTS; n++) { | ||
| 188 | sk = ((cos2pik_*sk1)>>15) - sk2 + (*buf++); | ||
| 189 | sk2 = sk1; | ||
| 190 | sk1 = sk; | ||
| 191 | } | ||
| 192 | sk >>= 8; | ||
| 193 | sk2 >>= 8; | ||
| 194 | if (sk > 32767 || sk < -32767 || sk2 > 32767 || sk2 < -32767) | ||
| 195 | printk(KERN_WARNING "DTMF-Detection overflow\n"); | ||
| 196 | /* compute |X(k)|**2 */ | ||
| 197 | result[k] = | ||
| 198 | (sk * sk) - | ||
| 199 | (((cos2pik[k] * sk) >> 15) * sk2) + | ||
| 200 | (sk2 * sk2); | ||
| 201 | } | ||
| 202 | |||
| 203 | /* our (squared) coefficients have been calculated, we need to process | ||
| 204 | * them. | ||
| 205 | */ | ||
| 206 | coefficients: | ||
| 207 | tresh = 0; | ||
| 208 | for (i = 0; i < NCOEFF; i++) { | ||
| 209 | if (result[i] < 0) | ||
| 210 | result[i] = 0; | ||
| 211 | if (result[i] > dsp->dtmf.treshold) { | ||
| 212 | if (result[i] > tresh) | ||
| 213 | tresh = result[i]; | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | if (tresh == 0) { | ||
| 218 | what = 0; | ||
| 219 | goto storedigit; | ||
| 220 | } | ||
| 221 | |||
| 222 | if (dsp_debug & DEBUG_DSP_DTMFCOEFF) | ||
| 223 | printk(KERN_DEBUG "a %3d %3d %3d %3d %3d %3d %3d %3d" | ||
| 224 | " tr:%3d r %3d %3d %3d %3d %3d %3d %3d %3d\n", | ||
| 225 | result[0]/10000, result[1]/10000, result[2]/10000, | ||
| 226 | result[3]/10000, result[4]/10000, result[5]/10000, | ||
| 227 | result[6]/10000, result[7]/10000, tresh/10000, | ||
| 228 | result[0]/(tresh/100), result[1]/(tresh/100), | ||
| 229 | result[2]/(tresh/100), result[3]/(tresh/100), | ||
| 230 | result[4]/(tresh/100), result[5]/(tresh/100), | ||
| 231 | result[6]/(tresh/100), result[7]/(tresh/100)); | ||
| 232 | |||
| 233 | /* calc digit (lowgroup/highgroup) */ | ||
| 234 | lowgroup = -1; | ||
| 235 | highgroup = -1; | ||
| 236 | treshl = tresh >> 3; /* tones which are not on, must be below 9 dB */ | ||
| 237 | tresh = tresh >> 2; /* touchtones must match within 6 dB */ | ||
| 238 | for (i = 0; i < NCOEFF; i++) { | ||
| 239 | if (result[i] < treshl) | ||
| 240 | continue; /* ignore */ | ||
| 241 | if (result[i] < tresh) { | ||
| 242 | lowgroup = -1; | ||
| 243 | highgroup = -1; | ||
| 244 | break; /* noise inbetween */ | ||
| 245 | } | ||
| 246 | /* good level found. This is allowed only one time per group */ | ||
| 247 | if (i < NCOEFF/2) { | ||
| 248 | /* lowgroup */ | ||
| 249 | if (lowgroup >= 0) { | ||
| 250 | /* Bad. Another tone found. */ | ||
| 251 | lowgroup = -1; | ||
| 252 | break; | ||
| 253 | } else | ||
| 254 | lowgroup = i; | ||
| 255 | } else { | ||
| 256 | /* higroup */ | ||
| 257 | if (highgroup >= 0) { | ||
| 258 | /* Bad. Another tone found. */ | ||
| 259 | highgroup = -1; | ||
| 260 | break; | ||
| 261 | } else | ||
| 262 | highgroup = i-(NCOEFF/2); | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | /* get digit or null */ | ||
| 267 | what = 0; | ||
| 268 | if (lowgroup >= 0 && highgroup >= 0) | ||
| 269 | what = dtmf_matrix[lowgroup][highgroup]; | ||
| 270 | |||
| 271 | storedigit: | ||
| 272 | if (what && (dsp_debug & DEBUG_DSP_DTMF)) | ||
| 273 | printk(KERN_DEBUG "DTMF what: %c\n", what); | ||
| 274 | |||
| 275 | if (dsp->dtmf.lastwhat != what) | ||
| 276 | dsp->dtmf.count = 0; | ||
| 277 | |||
| 278 | /* the tone (or no tone) must remain 3 times without change */ | ||
| 279 | if (dsp->dtmf.count == 2) { | ||
| 280 | if (dsp->dtmf.lastdigit != what) { | ||
| 281 | dsp->dtmf.lastdigit = what; | ||
| 282 | if (what) { | ||
| 283 | if (dsp_debug & DEBUG_DSP_DTMF) | ||
| 284 | printk(KERN_DEBUG "DTMF digit: %c\n", | ||
| 285 | what); | ||
| 286 | if ((strlen(dsp->dtmf.digits)+1) | ||
| 287 | < sizeof(dsp->dtmf.digits)) { | ||
| 288 | dsp->dtmf.digits[strlen( | ||
| 289 | dsp->dtmf.digits)+1] = '\0'; | ||
| 290 | dsp->dtmf.digits[strlen( | ||
| 291 | dsp->dtmf.digits)] = what; | ||
| 292 | } | ||
| 293 | } | ||
| 294 | } | ||
| 295 | } else | ||
| 296 | dsp->dtmf.count++; | ||
| 297 | |||
| 298 | dsp->dtmf.lastwhat = what; | ||
| 299 | |||
| 300 | goto again; | ||
| 301 | } | ||
| 302 | |||
| 303 | |||
diff --git a/drivers/isdn/mISDN/dsp_ecdis.h b/drivers/isdn/mISDN/dsp_ecdis.h new file mode 100644 index 000000000000..8a20af43308b --- /dev/null +++ b/drivers/isdn/mISDN/dsp_ecdis.h | |||
| @@ -0,0 +1,110 @@ | |||
| 1 | /* | ||
| 2 | * SpanDSP - a series of DSP components for telephony | ||
| 3 | * | ||
| 4 | * ec_disable_detector.h - A detector which should eventually meet the | ||
| 5 | * G.164/G.165 requirements for detecting the | ||
| 6 | * 2100Hz echo cancellor disable tone. | ||
| 7 | * | ||
| 8 | * Written by Steve Underwood <steveu@coppice.org> | ||
| 9 | * | ||
| 10 | * Copyright (C) 2001 Steve Underwood | ||
| 11 | * | ||
| 12 | * All rights reserved. | ||
| 13 | * | ||
| 14 | * This program is free software; you can redistribute it and/or modify | ||
| 15 | * it under the terms of the GNU General Public License as published by | ||
| 16 | * the Free Software Foundation; either version 2 of the License, or | ||
| 17 | * (at your option) any later version. | ||
| 18 | * | ||
| 19 | * This program is distributed in the hope that it will be useful, | ||
| 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 22 | * GNU General Public License for more details. | ||
| 23 | * | ||
| 24 | * You should have received a copy of the GNU General Public License | ||
| 25 | * along with this program; if not, write to the Free Software | ||
| 26 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 27 | * | ||
| 28 | */ | ||
| 29 | |||
| 30 | #include "dsp_biquad.h" | ||
| 31 | |||
| 32 | struct ec_disable_detector_state { | ||
| 33 | struct biquad2_state notch; | ||
| 34 | int notch_level; | ||
| 35 | int channel_level; | ||
| 36 | int tone_present; | ||
| 37 | int tone_cycle_duration; | ||
| 38 | int good_cycles; | ||
| 39 | int hit; | ||
| 40 | }; | ||
| 41 | |||
| 42 | |||
| 43 | #define FALSE 0 | ||
| 44 | #define TRUE (!FALSE) | ||
| 45 | |||
| 46 | static inline void | ||
| 47 | echo_can_disable_detector_init(struct ec_disable_detector_state *det) | ||
| 48 | { | ||
| 49 | /* Elliptic notch */ | ||
| 50 | /* This is actually centred at 2095Hz, but gets the balance we want, due | ||
| 51 | to the asymmetric walls of the notch */ | ||
| 52 | biquad2_init(&det->notch, | ||
| 53 | (int32_t) (-0.7600000*32768.0), | ||
| 54 | (int32_t) (-0.1183852*32768.0), | ||
| 55 | (int32_t) (-0.5104039*32768.0), | ||
| 56 | (int32_t) (0.1567596*32768.0), | ||
| 57 | (int32_t) (1.0000000*32768.0)); | ||
| 58 | |||
| 59 | det->channel_level = 0; | ||
| 60 | det->notch_level = 0; | ||
| 61 | det->tone_present = FALSE; | ||
| 62 | det->tone_cycle_duration = 0; | ||
| 63 | det->good_cycles = 0; | ||
| 64 | det->hit = 0; | ||
| 65 | } | ||
| 66 | /*- End of function --------------------------------------------------------*/ | ||
| 67 | |||
| 68 | static inline int | ||
| 69 | echo_can_disable_detector_update(struct ec_disable_detector_state *det, | ||
| 70 | int16_t amp) | ||
| 71 | { | ||
| 72 | int16_t notched; | ||
| 73 | |||
| 74 | notched = biquad2(&det->notch, amp); | ||
| 75 | /* Estimate the overall energy in the channel, and the energy in | ||
| 76 | the notch (i.e. overall channel energy - tone energy => noise). | ||
| 77 | Use abs instead of multiply for speed (is it really faster?). | ||
| 78 | Damp the overall energy a little more for a stable result. | ||
| 79 | Damp the notch energy a little less, so we don't damp out the | ||
| 80 | blip every time the phase reverses */ | ||
| 81 | det->channel_level += ((abs(amp) - det->channel_level) >> 5); | ||
| 82 | det->notch_level += ((abs(notched) - det->notch_level) >> 4); | ||
| 83 | if (det->channel_level > 280) { | ||
| 84 | /* There is adequate energy in the channel. | ||
| 85 | Is it mostly at 2100Hz? */ | ||
| 86 | if (det->notch_level*6 < det->channel_level) { | ||
| 87 | /* The notch says yes, so we have the tone. */ | ||
| 88 | if (!det->tone_present) { | ||
| 89 | /* Do we get a kick every 450+-25ms? */ | ||
| 90 | if (det->tone_cycle_duration >= 425*8 | ||
| 91 | && det->tone_cycle_duration <= 475*8) { | ||
| 92 | det->good_cycles++; | ||
| 93 | if (det->good_cycles > 2) | ||
| 94 | det->hit = TRUE; | ||
| 95 | } | ||
| 96 | det->tone_cycle_duration = 0; | ||
| 97 | } | ||
| 98 | det->tone_present = TRUE; | ||
| 99 | } else | ||
| 100 | det->tone_present = FALSE; | ||
| 101 | det->tone_cycle_duration++; | ||
| 102 | } else { | ||
| 103 | det->tone_present = FALSE; | ||
| 104 | det->tone_cycle_duration = 0; | ||
| 105 | det->good_cycles = 0; | ||
| 106 | } | ||
| 107 | return det->hit; | ||
| 108 | } | ||
| 109 | /*- End of function --------------------------------------------------------*/ | ||
| 110 | /*- End of file ------------------------------------------------------------*/ | ||
diff --git a/drivers/isdn/mISDN/dsp_hwec.c b/drivers/isdn/mISDN/dsp_hwec.c new file mode 100644 index 000000000000..eb892d9dd5c6 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_hwec.c | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | /* | ||
| 2 | * dsp_hwec.c: | ||
| 3 | * builtin mISDN dsp pipeline element for enabling the hw echocanceller | ||
| 4 | * | ||
| 5 | * Copyright (C) 2007, Nadi Sarrar | ||
| 6 | * | ||
| 7 | * Nadi Sarrar <nadi@beronet.com> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify it | ||
| 10 | * under the terms of the GNU General Public License as published by the Free | ||
| 11 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 12 | * any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 17 | * more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License along with | ||
| 20 | * this program; if not, write to the Free Software Foundation, Inc., 59 | ||
| 21 | * Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 22 | * | ||
| 23 | * The full GNU General Public License is included in this distribution in the | ||
| 24 | * file called LICENSE. | ||
| 25 | * | ||
| 26 | */ | ||
| 27 | |||
| 28 | #include <linux/kernel.h> | ||
| 29 | #include <linux/string.h> | ||
| 30 | #include <linux/mISDNdsp.h> | ||
| 31 | #include <linux/mISDNif.h> | ||
| 32 | #include "core.h" | ||
| 33 | #include "dsp.h" | ||
| 34 | #include "dsp_hwec.h" | ||
| 35 | |||
| 36 | static struct mISDN_dsp_element_arg args[] = { | ||
| 37 | { "deftaps", "128", "Set the number of taps of cancellation." }, | ||
| 38 | }; | ||
| 39 | |||
| 40 | static struct mISDN_dsp_element dsp_hwec_p = { | ||
| 41 | .name = "hwec", | ||
| 42 | .new = NULL, | ||
| 43 | .free = NULL, | ||
| 44 | .process_tx = NULL, | ||
| 45 | .process_rx = NULL, | ||
| 46 | .num_args = sizeof(args) / sizeof(struct mISDN_dsp_element_arg), | ||
| 47 | .args = args, | ||
| 48 | }; | ||
| 49 | struct mISDN_dsp_element *dsp_hwec = &dsp_hwec_p; | ||
| 50 | |||
| 51 | void dsp_hwec_enable(struct dsp *dsp, const char *arg) | ||
| 52 | { | ||
| 53 | int deftaps = 128, | ||
| 54 | len; | ||
| 55 | struct mISDN_ctrl_req cq; | ||
| 56 | |||
| 57 | if (!dsp) { | ||
| 58 | printk(KERN_ERR "%s: failed to enable hwec: dsp is NULL\n", | ||
| 59 | __func__); | ||
| 60 | return; | ||
| 61 | } | ||
| 62 | |||
| 63 | if (!arg) | ||
| 64 | goto _do; | ||
| 65 | |||
| 66 | len = strlen(arg); | ||
| 67 | if (!len) | ||
| 68 | goto _do; | ||
| 69 | |||
| 70 | { | ||
| 71 | char _dup[len + 1]; | ||
| 72 | char *dup, *tok, *name, *val; | ||
| 73 | int tmp; | ||
| 74 | |||
| 75 | strcpy(_dup, arg); | ||
| 76 | dup = _dup; | ||
| 77 | |||
| 78 | while ((tok = strsep(&dup, ","))) { | ||
| 79 | if (!strlen(tok)) | ||
| 80 | continue; | ||
| 81 | name = strsep(&tok, "="); | ||
| 82 | val = tok; | ||
| 83 | |||
| 84 | if (!val) | ||
| 85 | continue; | ||
| 86 | |||
| 87 | if (!strcmp(name, "deftaps")) { | ||
| 88 | if (sscanf(val, "%d", &tmp) == 1) | ||
| 89 | deftaps = tmp; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | _do: | ||
| 95 | printk(KERN_DEBUG "%s: enabling hwec with deftaps=%d\n", | ||
| 96 | __func__, deftaps); | ||
| 97 | memset(&cq, 0, sizeof(cq)); | ||
| 98 | cq.op = MISDN_CTRL_HFC_ECHOCAN_ON; | ||
| 99 | cq.p1 = deftaps; | ||
| 100 | if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) { | ||
| 101 | printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", | ||
| 102 | __func__); | ||
| 103 | return; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | void dsp_hwec_disable(struct dsp *dsp) | ||
| 108 | { | ||
| 109 | struct mISDN_ctrl_req cq; | ||
| 110 | |||
| 111 | if (!dsp) { | ||
| 112 | printk(KERN_ERR "%s: failed to disable hwec: dsp is NULL\n", | ||
| 113 | __func__); | ||
| 114 | return; | ||
| 115 | } | ||
| 116 | |||
| 117 | printk(KERN_DEBUG "%s: disabling hwec\n", __func__); | ||
| 118 | memset(&cq, 0, sizeof(cq)); | ||
| 119 | cq.op = MISDN_CTRL_HFC_ECHOCAN_OFF; | ||
| 120 | if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) { | ||
| 121 | printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", | ||
| 122 | __func__); | ||
| 123 | return; | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | int dsp_hwec_init(void) | ||
| 128 | { | ||
| 129 | mISDN_dsp_element_register(dsp_hwec); | ||
| 130 | |||
| 131 | return 0; | ||
| 132 | } | ||
| 133 | |||
| 134 | void dsp_hwec_exit(void) | ||
| 135 | { | ||
| 136 | mISDN_dsp_element_unregister(dsp_hwec); | ||
| 137 | } | ||
| 138 | |||
diff --git a/drivers/isdn/mISDN/dsp_hwec.h b/drivers/isdn/mISDN/dsp_hwec.h new file mode 100644 index 000000000000..eebe80c3f713 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_hwec.h | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | /* | ||
| 2 | * dsp_hwec.h | ||
| 3 | */ | ||
| 4 | |||
| 5 | extern struct mISDN_dsp_element *dsp_hwec; | ||
| 6 | extern void dsp_hwec_enable(struct dsp *dsp, const char *arg); | ||
| 7 | extern void dsp_hwec_disable(struct dsp *dsp); | ||
| 8 | extern int dsp_hwec_init(void); | ||
| 9 | extern void dsp_hwec_exit(void); | ||
| 10 | |||
diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c new file mode 100644 index 000000000000..850260ab57d0 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_pipeline.c | |||
| @@ -0,0 +1,348 @@ | |||
| 1 | /* | ||
| 2 | * dsp_pipeline.c: pipelined audio processing | ||
| 3 | * | ||
| 4 | * Copyright (C) 2007, Nadi Sarrar | ||
| 5 | * | ||
| 6 | * Nadi Sarrar <nadi@beronet.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify it | ||
| 9 | * under the terms of the GNU General Public License as published by the Free | ||
| 10 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 11 | * any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 16 | * more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License along with | ||
| 19 | * this program; if not, write to the Free Software Foundation, Inc., 59 | ||
| 20 | * Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 21 | * | ||
| 22 | * The full GNU General Public License is included in this distribution in the | ||
| 23 | * file called LICENSE. | ||
| 24 | * | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include <linux/kernel.h> | ||
| 28 | #include <linux/list.h> | ||
| 29 | #include <linux/string.h> | ||
| 30 | #include <linux/mISDNif.h> | ||
| 31 | #include <linux/mISDNdsp.h> | ||
| 32 | #include "dsp.h" | ||
| 33 | #include "dsp_hwec.h" | ||
| 34 | |||
| 35 | /* uncomment for debugging */ | ||
| 36 | /*#define PIPELINE_DEBUG*/ | ||
| 37 | |||
| 38 | struct dsp_pipeline_entry { | ||
| 39 | struct mISDN_dsp_element *elem; | ||
| 40 | void *p; | ||
| 41 | struct list_head list; | ||
| 42 | }; | ||
| 43 | struct dsp_element_entry { | ||
| 44 | struct mISDN_dsp_element *elem; | ||
| 45 | struct device dev; | ||
| 46 | struct list_head list; | ||
| 47 | }; | ||
| 48 | |||
| 49 | static LIST_HEAD(dsp_elements); | ||
| 50 | |||
| 51 | /* sysfs */ | ||
| 52 | static struct class *elements_class; | ||
| 53 | |||
| 54 | static ssize_t | ||
| 55 | attr_show_args(struct device *dev, struct device_attribute *attr, char *buf) | ||
| 56 | { | ||
| 57 | struct mISDN_dsp_element *elem = dev_get_drvdata(dev); | ||
| 58 | ssize_t len = 0; | ||
| 59 | int i = 0; | ||
| 60 | |||
| 61 | *buf = 0; | ||
| 62 | for (; i < elem->num_args; ++i) | ||
| 63 | len = sprintf(buf, "%sName: %s\n%s%s%sDescription: %s\n" | ||
| 64 | "\n", buf, | ||
| 65 | elem->args[i].name, | ||
| 66 | elem->args[i].def ? "Default: " : "", | ||
| 67 | elem->args[i].def ? elem->args[i].def : "", | ||
| 68 | elem->args[i].def ? "\n" : "", | ||
| 69 | elem->args[i].desc); | ||
| 70 | |||
| 71 | return len; | ||
| 72 | } | ||
| 73 | |||
| 74 | static struct device_attribute element_attributes[] = { | ||
| 75 | __ATTR(args, 0444, attr_show_args, NULL), | ||
| 76 | }; | ||
| 77 | |||
| 78 | int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) | ||
| 79 | { | ||
| 80 | struct dsp_element_entry *entry; | ||
| 81 | int ret, i; | ||
| 82 | |||
| 83 | if (!elem) | ||
| 84 | return -EINVAL; | ||
| 85 | |||
| 86 | entry = kzalloc(sizeof(struct dsp_element_entry), GFP_KERNEL); | ||
| 87 | if (!entry) | ||
| 88 | return -ENOMEM; | ||
| 89 | |||
| 90 | entry->elem = elem; | ||
| 91 | |||
| 92 | entry->dev.class = elements_class; | ||
| 93 | dev_set_drvdata(&entry->dev, elem); | ||
| 94 | snprintf(entry->dev.bus_id, BUS_ID_SIZE, elem->name); | ||
| 95 | ret = device_register(&entry->dev); | ||
| 96 | if (ret) { | ||
| 97 | printk(KERN_ERR "%s: failed to register %s\n", | ||
| 98 | __func__, elem->name); | ||
| 99 | goto err1; | ||
| 100 | } | ||
| 101 | |||
| 102 | for (i = 0; i < (sizeof(element_attributes) | ||
| 103 | / sizeof(struct device_attribute)); ++i) | ||
| 104 | ret = device_create_file(&entry->dev, | ||
| 105 | &element_attributes[i]); | ||
| 106 | if (ret) { | ||
| 107 | printk(KERN_ERR "%s: failed to create device file\n", | ||
| 108 | __func__); | ||
| 109 | goto err2; | ||
| 110 | } | ||
| 111 | |||
| 112 | list_add_tail(&entry->list, &dsp_elements); | ||
| 113 | |||
| 114 | printk(KERN_DEBUG "%s: %s registered\n", __func__, elem->name); | ||
| 115 | |||
| 116 | return 0; | ||
| 117 | |||
| 118 | err2: | ||
| 119 | device_unregister(&entry->dev); | ||
| 120 | err1: | ||
| 121 | kfree(entry); | ||
| 122 | return ret; | ||
| 123 | } | ||
| 124 | EXPORT_SYMBOL(mISDN_dsp_element_register); | ||
| 125 | |||
| 126 | void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem) | ||
| 127 | { | ||
| 128 | struct dsp_element_entry *entry, *n; | ||
| 129 | |||
| 130 | if (!elem) | ||
| 131 | return; | ||
| 132 | |||
| 133 | list_for_each_entry_safe(entry, n, &dsp_elements, list) | ||
| 134 | if (entry->elem == elem) { | ||
| 135 | list_del(&entry->list); | ||
| 136 | device_unregister(&entry->dev); | ||
| 137 | kfree(entry); | ||
| 138 | printk(KERN_DEBUG "%s: %s unregistered\n", | ||
| 139 | __func__, elem->name); | ||
| 140 | return; | ||
| 141 | } | ||
| 142 | printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name); | ||
| 143 | } | ||
| 144 | EXPORT_SYMBOL(mISDN_dsp_element_unregister); | ||
| 145 | |||
| 146 | int dsp_pipeline_module_init(void) | ||
| 147 | { | ||
| 148 | elements_class = class_create(THIS_MODULE, "dsp_pipeline"); | ||
| 149 | if (IS_ERR(elements_class)) | ||
| 150 | return PTR_ERR(elements_class); | ||
| 151 | |||
| 152 | #ifdef PIPELINE_DEBUG | ||
| 153 | printk(KERN_DEBUG "%s: dsp pipeline module initialized\n", __func__); | ||
| 154 | #endif | ||
| 155 | |||
| 156 | dsp_hwec_init(); | ||
| 157 | |||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | void dsp_pipeline_module_exit(void) | ||
| 162 | { | ||
| 163 | struct dsp_element_entry *entry, *n; | ||
| 164 | |||
| 165 | dsp_hwec_exit(); | ||
| 166 | |||
| 167 | class_destroy(elements_class); | ||
| 168 | |||
| 169 | list_for_each_entry_safe(entry, n, &dsp_elements, list) { | ||
| 170 | list_del(&entry->list); | ||
| 171 | printk(KERN_WARNING "%s: element was still registered: %s\n", | ||
| 172 | __func__, entry->elem->name); | ||
| 173 | kfree(entry); | ||
| 174 | } | ||
| 175 | |||
| 176 | printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __func__); | ||
| 177 | } | ||
| 178 | |||
| 179 | int dsp_pipeline_init(struct dsp_pipeline *pipeline) | ||
| 180 | { | ||
| 181 | if (!pipeline) | ||
| 182 | return -EINVAL; | ||
| 183 | |||
| 184 | INIT_LIST_HEAD(&pipeline->list); | ||
| 185 | |||
| 186 | #ifdef PIPELINE_DEBUG | ||
| 187 | printk(KERN_DEBUG "%s: dsp pipeline ready\n", __func__); | ||
| 188 | #endif | ||
| 189 | |||
| 190 | return 0; | ||
| 191 | } | ||
| 192 | |||
| 193 | static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline) | ||
| 194 | { | ||
| 195 | struct dsp_pipeline_entry *entry, *n; | ||
| 196 | |||
| 197 | list_for_each_entry_safe(entry, n, &pipeline->list, list) { | ||
| 198 | list_del(&entry->list); | ||
| 199 | if (entry->elem == dsp_hwec) | ||
| 200 | dsp_hwec_disable(container_of(pipeline, struct dsp, | ||
| 201 | pipeline)); | ||
| 202 | else | ||
| 203 | entry->elem->free(entry->p); | ||
| 204 | kfree(entry); | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | void dsp_pipeline_destroy(struct dsp_pipeline *pipeline) | ||
| 209 | { | ||
| 210 | |||
| 211 | if (!pipeline) | ||
| 212 | return; | ||
| 213 | |||
| 214 | _dsp_pipeline_destroy(pipeline); | ||
| 215 | |||
| 216 | #ifdef PIPELINE_DEBUG | ||
| 217 | printk(KERN_DEBUG "%s: dsp pipeline destroyed\n", __func__); | ||
| 218 | #endif | ||
| 219 | } | ||
| 220 | |||
| 221 | int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) | ||
| 222 | { | ||
| 223 | int len, incomplete = 0, found = 0; | ||
| 224 | char *dup, *tok, *name, *args; | ||
| 225 | struct dsp_element_entry *entry, *n; | ||
| 226 | struct dsp_pipeline_entry *pipeline_entry; | ||
| 227 | struct mISDN_dsp_element *elem; | ||
| 228 | |||
| 229 | if (!pipeline) | ||
| 230 | return -EINVAL; | ||
| 231 | |||
| 232 | if (!list_empty(&pipeline->list)) | ||
| 233 | _dsp_pipeline_destroy(pipeline); | ||
| 234 | |||
| 235 | if (!cfg) | ||
| 236 | return 0; | ||
| 237 | |||
| 238 | len = strlen(cfg); | ||
| 239 | if (!len) | ||
| 240 | return 0; | ||
| 241 | |||
| 242 | dup = kmalloc(len + 1, GFP_KERNEL); | ||
| 243 | if (!dup) | ||
| 244 | return 0; | ||
| 245 | strcpy(dup, cfg); | ||
| 246 | while ((tok = strsep(&dup, "|"))) { | ||
| 247 | if (!strlen(tok)) | ||
| 248 | continue; | ||
| 249 | name = strsep(&tok, "("); | ||
| 250 | args = strsep(&tok, ")"); | ||
| 251 | if (args && !*args) | ||
| 252 | args = 0; | ||
| 253 | |||
| 254 | list_for_each_entry_safe(entry, n, &dsp_elements, list) | ||
| 255 | if (!strcmp(entry->elem->name, name)) { | ||
| 256 | elem = entry->elem; | ||
| 257 | |||
| 258 | pipeline_entry = kmalloc(sizeof(struct | ||
| 259 | dsp_pipeline_entry), GFP_KERNEL); | ||
| 260 | if (!pipeline_entry) { | ||
| 261 | printk(KERN_DEBUG "%s: failed to add " | ||
| 262 | "entry to pipeline: %s (out of " | ||
| 263 | "memory)\n", __func__, elem->name); | ||
| 264 | incomplete = 1; | ||
| 265 | goto _out; | ||
| 266 | } | ||
| 267 | pipeline_entry->elem = elem; | ||
| 268 | |||
| 269 | if (elem == dsp_hwec) { | ||
| 270 | /* This is a hack to make the hwec | ||
| 271 | available as a pipeline module */ | ||
| 272 | dsp_hwec_enable(container_of(pipeline, | ||
| 273 | struct dsp, pipeline), args); | ||
| 274 | list_add_tail(&pipeline_entry->list, | ||
| 275 | &pipeline->list); | ||
| 276 | } else { | ||
| 277 | pipeline_entry->p = elem->new(args); | ||
| 278 | if (pipeline_entry->p) { | ||
| 279 | list_add_tail(&pipeline_entry-> | ||
| 280 | list, &pipeline->list); | ||
| 281 | #ifdef PIPELINE_DEBUG | ||
| 282 | printk(KERN_DEBUG "%s: created " | ||
| 283 | "instance of %s%s%s\n", | ||
| 284 | __func__, name, args ? | ||
| 285 | " with args " : "", args ? | ||
| 286 | args : ""); | ||
| 287 | #endif | ||
| 288 | } else { | ||
| 289 | printk(KERN_DEBUG "%s: failed " | ||
| 290 | "to add entry to pipeline: " | ||
| 291 | "%s (new() returned NULL)\n", | ||
| 292 | __func__, elem->name); | ||
| 293 | kfree(pipeline_entry); | ||
| 294 | incomplete = 1; | ||
| 295 | } | ||
| 296 | } | ||
| 297 | found = 1; | ||
| 298 | break; | ||
| 299 | } | ||
| 300 | |||
| 301 | if (found) | ||
| 302 | found = 0; | ||
| 303 | else { | ||
| 304 | printk(KERN_DEBUG "%s: element not found, skipping: " | ||
| 305 | "%s\n", __func__, name); | ||
| 306 | incomplete = 1; | ||
| 307 | } | ||
| 308 | } | ||
| 309 | |||
| 310 | _out: | ||
| 311 | if (!list_empty(&pipeline->list)) | ||
| 312 | pipeline->inuse = 1; | ||
| 313 | else | ||
| 314 | pipeline->inuse = 0; | ||
| 315 | |||
| 316 | #ifdef PIPELINE_DEBUG | ||
| 317 | printk(KERN_DEBUG "%s: dsp pipeline built%s: %s\n", | ||
| 318 | __func__, incomplete ? " incomplete" : "", cfg); | ||
| 319 | #endif | ||
| 320 | kfree(dup); | ||
| 321 | return 0; | ||
| 322 | } | ||
| 323 | |||
| 324 | void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len) | ||
| 325 | { | ||
| 326 | struct dsp_pipeline_entry *entry; | ||
| 327 | |||
| 328 | if (!pipeline) | ||
| 329 | return; | ||
| 330 | |||
| 331 | list_for_each_entry(entry, &pipeline->list, list) | ||
| 332 | if (entry->elem->process_tx) | ||
| 333 | entry->elem->process_tx(entry->p, data, len); | ||
| 334 | } | ||
| 335 | |||
| 336 | void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len) | ||
| 337 | { | ||
| 338 | struct dsp_pipeline_entry *entry; | ||
| 339 | |||
| 340 | if (!pipeline) | ||
| 341 | return; | ||
| 342 | |||
| 343 | list_for_each_entry_reverse(entry, &pipeline->list, list) | ||
| 344 | if (entry->elem->process_rx) | ||
| 345 | entry->elem->process_rx(entry->p, data, len); | ||
| 346 | } | ||
| 347 | |||
| 348 | |||
diff --git a/drivers/isdn/mISDN/dsp_tones.c b/drivers/isdn/mISDN/dsp_tones.c new file mode 100644 index 000000000000..23dd0dd21524 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_tones.c | |||
| @@ -0,0 +1,551 @@ | |||
| 1 | /* | ||
| 2 | * Audio support data for ISDN4Linux. | ||
| 3 | * | ||
| 4 | * Copyright Andreas Eversberg (jolly@eversberg.eu) | ||
| 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 | #include <linux/mISDNif.h> | ||
| 12 | #include <linux/mISDNdsp.h> | ||
| 13 | #include "core.h" | ||
| 14 | #include "dsp.h" | ||
| 15 | |||
| 16 | |||
| 17 | #define DATA_S sample_silence | ||
| 18 | #define SIZE_S (&sizeof_silence) | ||
| 19 | #define DATA_GA sample_german_all | ||
| 20 | #define SIZE_GA (&sizeof_german_all) | ||
| 21 | #define DATA_GO sample_german_old | ||
| 22 | #define SIZE_GO (&sizeof_german_old) | ||
| 23 | #define DATA_DT sample_american_dialtone | ||
| 24 | #define SIZE_DT (&sizeof_american_dialtone) | ||
| 25 | #define DATA_RI sample_american_ringing | ||
| 26 | #define SIZE_RI (&sizeof_american_ringing) | ||
| 27 | #define DATA_BU sample_american_busy | ||
| 28 | #define SIZE_BU (&sizeof_american_busy) | ||
| 29 | #define DATA_S1 sample_special1 | ||
| 30 | #define SIZE_S1 (&sizeof_special1) | ||
| 31 | #define DATA_S2 sample_special2 | ||
| 32 | #define SIZE_S2 (&sizeof_special2) | ||
| 33 | #define DATA_S3 sample_special3 | ||
| 34 | #define SIZE_S3 (&sizeof_special3) | ||
| 35 | |||
| 36 | /***************/ | ||
| 37 | /* tones loops */ | ||
| 38 | /***************/ | ||
| 39 | |||
| 40 | /* all tones are alaw encoded */ | ||
| 41 | /* the last sample+1 is in phase with the first sample. the error is low */ | ||
| 42 | |||
| 43 | static u8 sample_german_all[] = { | ||
| 44 | 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, | ||
| 45 | 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, | ||
| 46 | 0xdc, 0xfc, 0x6c, | ||
| 47 | 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, | ||
| 48 | 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, | ||
| 49 | 0xdc, 0xfc, 0x6c, | ||
| 50 | 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, | ||
| 51 | 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, | ||
| 52 | 0xdc, 0xfc, 0x6c, | ||
| 53 | 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, | ||
| 54 | 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, | ||
| 55 | 0xdc, 0xfc, 0x6c, | ||
| 56 | }; | ||
| 57 | static u32 sizeof_german_all = sizeof(sample_german_all); | ||
| 58 | |||
| 59 | static u8 sample_german_old[] = { | ||
| 60 | 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, | ||
| 61 | 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, | ||
| 62 | 0x8c, | ||
| 63 | 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, | ||
| 64 | 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, | ||
| 65 | 0x8c, | ||
| 66 | 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, | ||
| 67 | 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, | ||
| 68 | 0x8c, | ||
| 69 | 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, | ||
| 70 | 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, | ||
| 71 | 0x8c, | ||
| 72 | }; | ||
| 73 | static u32 sizeof_german_old = sizeof(sample_german_old); | ||
| 74 | |||
| 75 | static u8 sample_american_dialtone[] = { | ||
| 76 | 0x2a, 0x18, 0x90, 0x6c, 0x4c, 0xbc, 0x4c, 0x6c, | ||
| 77 | 0x10, 0x58, 0x32, 0xb9, 0x31, 0x2d, 0x8d, 0x0d, | ||
| 78 | 0x8d, 0x2d, 0x31, 0x99, 0x0f, 0x28, 0x60, 0xf0, | ||
| 79 | 0xd0, 0x50, 0xd0, 0x30, 0x60, 0x08, 0x8e, 0x67, | ||
| 80 | 0x09, 0x19, 0x21, 0xe1, 0xd9, 0xb9, 0x29, 0x67, | ||
| 81 | 0x83, 0x02, 0xce, 0xbe, 0xee, 0x1a, 0x1b, 0xef, | ||
| 82 | 0xbf, 0xcf, 0x03, 0x82, 0x66, 0x28, 0xb8, 0xd8, | ||
| 83 | 0xe0, 0x20, 0x18, 0x08, 0x66, 0x8f, 0x09, 0x61, | ||
| 84 | 0x31, 0xd1, 0x51, 0xd1, 0xf1, 0x61, 0x29, 0x0e, | ||
| 85 | 0x98, 0x30, 0x2c, 0x8c, 0x0c, 0x8c, 0x2c, 0x30, | ||
| 86 | 0xb8, 0x33, 0x59, 0x11, 0x6d, 0x4d, 0xbd, 0x4d, | ||
| 87 | 0x6d, 0x91, 0x19, | ||
| 88 | }; | ||
| 89 | static u32 sizeof_american_dialtone = sizeof(sample_american_dialtone); | ||
| 90 | |||
| 91 | static u8 sample_american_ringing[] = { | ||
| 92 | 0x2a, 0xe0, 0xac, 0x0c, 0xbc, 0x4c, 0x8c, 0x90, | ||
| 93 | 0x48, 0xc7, 0xc1, 0xed, 0xcd, 0x4d, 0xcd, 0xed, | ||
| 94 | 0xc1, 0xb7, 0x08, 0x30, 0xec, 0xcc, 0xcc, 0x8c, | ||
| 95 | 0x10, 0x58, 0x1a, 0x99, 0x71, 0xed, 0x8d, 0x8d, | ||
| 96 | 0x2d, 0x41, 0x89, 0x9e, 0x20, 0x70, 0x2c, 0xec, | ||
| 97 | 0x2c, 0x70, 0x20, 0x86, 0x77, 0xe1, 0x31, 0x11, | ||
| 98 | 0xd1, 0xf1, 0x81, 0x09, 0xa3, 0x56, 0x58, 0x00, | ||
| 99 | 0x40, 0xc0, 0x60, 0x38, 0x46, 0x43, 0x57, 0x39, | ||
| 100 | 0xd9, 0x59, 0x99, 0xc9, 0x77, 0x2f, 0x2e, 0xc6, | ||
| 101 | 0xd6, 0x28, 0xd6, 0x36, 0x26, 0x2e, 0x8a, 0xa3, | ||
| 102 | 0x43, 0x63, 0x4b, 0x4a, 0x62, 0x42, 0xa2, 0x8b, | ||
| 103 | 0x2f, 0x27, 0x37, 0xd7, 0x29, 0xd7, 0xc7, 0x2f, | ||
| 104 | 0x2e, 0x76, 0xc8, 0x98, 0x58, 0xd8, 0x38, 0x56, | ||
| 105 | 0x42, 0x47, 0x39, 0x61, 0xc1, 0x41, 0x01, 0x59, | ||
| 106 | 0x57, 0xa2, 0x08, 0x80, 0xf0, 0xd0, 0x10, 0x30, | ||
| 107 | 0xe0, 0x76, 0x87, 0x21, 0x71, 0x2d, 0xed, 0x2d, | ||
| 108 | 0x71, 0x21, 0x9f, 0x88, 0x40, 0x2c, 0x8c, 0x8c, | ||
| 109 | 0xec, 0x70, 0x98, 0x1b, 0x59, 0x11, 0x8d, 0xcd, | ||
| 110 | 0xcd, 0xed, 0x31, 0x09, 0xb6, 0xc0, 0xec, 0xcc, | ||
| 111 | 0x4c, 0xcc, 0xec, 0xc0, 0xc6, 0x49, 0x91, 0x8d, | ||
| 112 | 0x4d, 0xbd, 0x0d, 0xad, 0xe1, | ||
| 113 | }; | ||
| 114 | static u32 sizeof_american_ringing = sizeof(sample_american_ringing); | ||
| 115 | |||
| 116 | static u8 sample_american_busy[] = { | ||
| 117 | 0x2a, 0x00, 0x6c, 0x4c, 0x4c, 0x6c, 0xb0, 0x66, | ||
| 118 | 0x99, 0x11, 0x6d, 0x8d, 0x2d, 0x41, 0xd7, 0x96, | ||
| 119 | 0x60, 0xf0, 0x70, 0x40, 0x58, 0xf6, 0x53, 0x57, | ||
| 120 | 0x09, 0x89, 0xd7, 0x5f, 0xe3, 0x2a, 0xe3, 0x5f, | ||
| 121 | 0xd7, 0x89, 0x09, 0x57, 0x53, 0xf6, 0x58, 0x40, | ||
| 122 | 0x70, 0xf0, 0x60, 0x96, 0xd7, 0x41, 0x2d, 0x8d, | ||
| 123 | 0x6d, 0x11, 0x99, 0x66, 0xb0, 0x6c, 0x4c, 0x4c, | ||
| 124 | 0x6c, 0x00, 0x2a, 0x01, 0x6d, 0x4d, 0x4d, 0x6d, | ||
| 125 | 0xb1, 0x67, 0x98, 0x10, 0x6c, 0x8c, 0x2c, 0x40, | ||
| 126 | 0xd6, 0x97, 0x61, 0xf1, 0x71, 0x41, 0x59, 0xf7, | ||
| 127 | 0x52, 0x56, 0x08, 0x88, 0xd6, 0x5e, 0xe2, 0x2a, | ||
| 128 | 0xe2, 0x5e, 0xd6, 0x88, 0x08, 0x56, 0x52, 0xf7, | ||
| 129 | 0x59, 0x41, 0x71, 0xf1, 0x61, 0x97, 0xd6, 0x40, | ||
| 130 | 0x2c, 0x8c, 0x6c, 0x10, 0x98, 0x67, 0xb1, 0x6d, | ||
| 131 | 0x4d, 0x4d, 0x6d, 0x01, | ||
| 132 | }; | ||
| 133 | static u32 sizeof_american_busy = sizeof(sample_american_busy); | ||
| 134 | |||
| 135 | static u8 sample_special1[] = { | ||
| 136 | 0x2a, 0x2c, 0xbc, 0x6c, 0xd6, 0x71, 0xbd, 0x0d, | ||
| 137 | 0xd9, 0x80, 0xcc, 0x4c, 0x40, 0x39, 0x0d, 0xbd, | ||
| 138 | 0x11, 0x86, 0xec, 0xbc, 0xec, 0x0e, 0x51, 0xbd, | ||
| 139 | 0x8d, 0x89, 0x30, 0x4c, 0xcc, 0xe0, 0xe1, 0xcd, | ||
| 140 | 0x4d, 0x31, 0x88, 0x8c, 0xbc, 0x50, 0x0f, 0xed, | ||
| 141 | 0xbd, 0xed, 0x87, 0x10, 0xbc, 0x0c, 0x38, 0x41, | ||
| 142 | 0x4d, 0xcd, 0x81, 0xd8, 0x0c, 0xbc, 0x70, 0xd7, | ||
| 143 | 0x6d, 0xbd, 0x2d, | ||
| 144 | }; | ||
| 145 | static u32 sizeof_special1 = sizeof(sample_special1); | ||
| 146 | |||
| 147 | static u8 sample_special2[] = { | ||
| 148 | 0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc, | ||
| 149 | 0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d, | ||
| 150 | 0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6, | ||
| 151 | 0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0, | ||
| 152 | 0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd, | ||
| 153 | 0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc, | ||
| 154 | 0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d, | ||
| 155 | 0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6, | ||
| 156 | 0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0, | ||
| 157 | 0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd, | ||
| 158 | }; | ||
| 159 | static u32 sizeof_special2 = sizeof(sample_special2); | ||
| 160 | |||
| 161 | static u8 sample_special3[] = { | ||
| 162 | 0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1, | ||
| 163 | 0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c, | ||
| 164 | 0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc, | ||
| 165 | 0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7, | ||
| 166 | 0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd, | ||
| 167 | 0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1, | ||
| 168 | 0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c, | ||
| 169 | 0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc, | ||
| 170 | 0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7, | ||
| 171 | 0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd, | ||
| 172 | }; | ||
| 173 | static u32 sizeof_special3 = sizeof(sample_special3); | ||
| 174 | |||
| 175 | static u8 sample_silence[] = { | ||
| 176 | 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | ||
| 177 | 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | ||
| 178 | 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | ||
| 179 | 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | ||
| 180 | 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | ||
| 181 | 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | ||
| 182 | 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | ||
| 183 | 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | ||
| 184 | 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | ||
| 185 | 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | ||
| 186 | 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | ||
| 187 | 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | ||
| 188 | }; | ||
| 189 | static u32 sizeof_silence = sizeof(sample_silence); | ||
| 190 | |||
| 191 | struct tones_samples { | ||
| 192 | u32 *len; | ||
| 193 | u8 *data; | ||
| 194 | }; | ||
| 195 | static struct | ||
| 196 | tones_samples samples[] = { | ||
| 197 | {&sizeof_german_all, sample_german_all}, | ||
| 198 | {&sizeof_german_old, sample_german_old}, | ||
| 199 | {&sizeof_american_dialtone, sample_american_dialtone}, | ||
| 200 | {&sizeof_american_ringing, sample_american_ringing}, | ||
| 201 | {&sizeof_american_busy, sample_american_busy}, | ||
| 202 | {&sizeof_special1, sample_special1}, | ||
| 203 | {&sizeof_special2, sample_special2}, | ||
| 204 | {&sizeof_special3, sample_special3}, | ||
| 205 | {NULL, NULL}, | ||
| 206 | }; | ||
| 207 | |||
| 208 | /*********************************** | ||
| 209 | * generate ulaw from alaw samples * | ||
| 210 | ***********************************/ | ||
| 211 | |||
| 212 | void | ||
| 213 | dsp_audio_generate_ulaw_samples(void) | ||
| 214 | { | ||
| 215 | int i, j; | ||
| 216 | |||
| 217 | i = 0; | ||
| 218 | while (samples[i].len) { | ||
| 219 | j = 0; | ||
| 220 | while (j < (*samples[i].len)) { | ||
| 221 | samples[i].data[j] = | ||
| 222 | dsp_audio_alaw_to_ulaw[samples[i].data[j]]; | ||
| 223 | j++; | ||
| 224 | } | ||
| 225 | i++; | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | |||
| 230 | /**************************** | ||
| 231 | * tone sequence definition * | ||
| 232 | ****************************/ | ||
| 233 | |||
| 234 | struct pattern { | ||
| 235 | int tone; | ||
| 236 | u8 *data[10]; | ||
| 237 | u32 *siz[10]; | ||
| 238 | u32 seq[10]; | ||
| 239 | } pattern[] = { | ||
| 240 | {TONE_GERMAN_DIALTONE, | ||
| 241 | {DATA_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 242 | {SIZE_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 243 | {1900, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, | ||
| 244 | |||
| 245 | {TONE_GERMAN_OLDDIALTONE, | ||
| 246 | {DATA_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 247 | {SIZE_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 248 | {1998, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, | ||
| 249 | |||
| 250 | {TONE_AMERICAN_DIALTONE, | ||
| 251 | {DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 252 | {SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 253 | {8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, | ||
| 254 | |||
| 255 | {TONE_GERMAN_DIALPBX, | ||
| 256 | {DATA_GA, DATA_S, DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0}, | ||
| 257 | {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0}, | ||
| 258 | {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, | ||
| 259 | |||
| 260 | {TONE_GERMAN_OLDDIALPBX, | ||
| 261 | {DATA_GO, DATA_S, DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0}, | ||
| 262 | {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0}, | ||
| 263 | {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, | ||
| 264 | |||
| 265 | {TONE_AMERICAN_DIALPBX, | ||
| 266 | {DATA_DT, DATA_S, DATA_DT, DATA_S, DATA_DT, DATA_S, 0, 0, 0, 0}, | ||
| 267 | {SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, 0, 0, 0, 0}, | ||
| 268 | {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, | ||
| 269 | |||
| 270 | {TONE_GERMAN_RINGING, | ||
| 271 | {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 272 | {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 273 | {8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} }, | ||
| 274 | |||
| 275 | {TONE_GERMAN_OLDRINGING, | ||
| 276 | {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 277 | {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 278 | {8000, 40000, 0, 0, 0, 0, 0, 0, 0, 0} }, | ||
| 279 | |||
| 280 | {TONE_AMERICAN_RINGING, | ||
| 281 | {DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 282 | {SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 283 | {8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} }, | ||
| 284 | |||
| 285 | {TONE_GERMAN_RINGPBX, | ||
| 286 | {DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0}, | ||
| 287 | {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0}, | ||
| 288 | {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, | ||
| 289 | |||
| 290 | {TONE_GERMAN_OLDRINGPBX, | ||
| 291 | {DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0}, | ||
| 292 | {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0}, | ||
| 293 | {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, | ||
| 294 | |||
| 295 | {TONE_AMERICAN_RINGPBX, | ||
| 296 | {DATA_RI, DATA_S, DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0}, | ||
| 297 | {SIZE_RI, SIZE_S, SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0}, | ||
| 298 | {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, | ||
| 299 | |||
| 300 | {TONE_GERMAN_BUSY, | ||
| 301 | {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 302 | {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 303 | {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, | ||
| 304 | |||
| 305 | {TONE_GERMAN_OLDBUSY, | ||
| 306 | {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 307 | {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 308 | {1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} }, | ||
| 309 | |||
| 310 | {TONE_AMERICAN_BUSY, | ||
| 311 | {DATA_BU, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 312 | {SIZE_BU, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 313 | {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, | ||
| 314 | |||
| 315 | {TONE_GERMAN_HANGUP, | ||
| 316 | {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 317 | {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 318 | {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, | ||
| 319 | |||
| 320 | {TONE_GERMAN_OLDHANGUP, | ||
| 321 | {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 322 | {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 323 | {1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} }, | ||
| 324 | |||
| 325 | {TONE_AMERICAN_HANGUP, | ||
| 326 | {DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 327 | {SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 328 | {8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, | ||
| 329 | |||
| 330 | {TONE_SPECIAL_INFO, | ||
| 331 | {DATA_S1, DATA_S2, DATA_S3, DATA_S, 0, 0, 0, 0, 0, 0}, | ||
| 332 | {SIZE_S1, SIZE_S2, SIZE_S3, SIZE_S, 0, 0, 0, 0, 0, 0}, | ||
| 333 | {2666, 2666, 2666, 8002, 0, 0, 0, 0, 0, 0} }, | ||
| 334 | |||
| 335 | {TONE_GERMAN_GASSENBESETZT, | ||
| 336 | {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 337 | {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 338 | {2000, 2000, 0, 0, 0, 0, 0, 0, 0, 0} }, | ||
| 339 | |||
| 340 | {TONE_GERMAN_AUFSCHALTTON, | ||
| 341 | {DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0}, | ||
| 342 | {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0}, | ||
| 343 | {1000, 5000, 1000, 17000, 0, 0, 0, 0, 0, 0} }, | ||
| 344 | |||
| 345 | {0, | ||
| 346 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 347 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
| 348 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, | ||
| 349 | }; | ||
| 350 | |||
| 351 | /****************** | ||
| 352 | * copy tone data * | ||
| 353 | ******************/ | ||
| 354 | |||
| 355 | /* an sk_buff is generated from the number of samples needed. | ||
| 356 | * the count will be changed and may begin from 0 each pattern period. | ||
| 357 | * the clue is to precalculate the pointers and legths to use only one | ||
| 358 | * memcpy per function call, or two memcpy if the tone sequence changes. | ||
| 359 | * | ||
| 360 | * pattern - the type of the pattern | ||
| 361 | * count - the sample from the beginning of the pattern (phase) | ||
| 362 | * len - the number of bytes | ||
| 363 | * | ||
| 364 | * return - the sk_buff with the sample | ||
| 365 | * | ||
| 366 | * if tones has finished (e.g. knocking tone), dsp->tones is turned off | ||
| 367 | */ | ||
| 368 | void dsp_tone_copy(struct dsp *dsp, u8 *data, int len) | ||
| 369 | { | ||
| 370 | int index, count, start, num; | ||
| 371 | struct pattern *pat; | ||
| 372 | struct dsp_tone *tone = &dsp->tone; | ||
| 373 | |||
| 374 | /* if we have no tone, we copy silence */ | ||
| 375 | if (!tone->tone) { | ||
| 376 | memset(data, dsp_silence, len); | ||
| 377 | return; | ||
| 378 | } | ||
| 379 | |||
| 380 | /* process pattern */ | ||
| 381 | pat = (struct pattern *)tone->pattern; | ||
| 382 | /* points to the current pattern */ | ||
| 383 | index = tone->index; /* gives current sequence index */ | ||
| 384 | count = tone->count; /* gives current sample */ | ||
| 385 | |||
| 386 | /* copy sample */ | ||
| 387 | while (len) { | ||
| 388 | /* find sample to start with */ | ||
| 389 | while (42) { | ||
| 390 | /* warp arround */ | ||
| 391 | if (!pat->seq[index]) { | ||
| 392 | count = 0; | ||
| 393 | index = 0; | ||
| 394 | } | ||
| 395 | /* check if we are currently playing this tone */ | ||
| 396 | if (count < pat->seq[index]) | ||
| 397 | break; | ||
| 398 | if (dsp_debug & DEBUG_DSP_TONE) | ||
| 399 | printk(KERN_DEBUG "%s: reaching next sequence " | ||
| 400 | "(index=%d)\n", __func__, index); | ||
| 401 | count -= pat->seq[index]; | ||
| 402 | index++; | ||
| 403 | } | ||
| 404 | /* calculate start and number of samples */ | ||
| 405 | start = count % (*(pat->siz[index])); | ||
| 406 | num = len; | ||
| 407 | if (num+count > pat->seq[index]) | ||
| 408 | num = pat->seq[index] - count; | ||
| 409 | if (num+start > (*(pat->siz[index]))) | ||
| 410 | num = (*(pat->siz[index])) - start; | ||
| 411 | /* copy memory */ | ||
| 412 | memcpy(data, pat->data[index]+start, num); | ||
| 413 | /* reduce length */ | ||
| 414 | data += num; | ||
| 415 | count += num; | ||
| 416 | len -= num; | ||
| 417 | } | ||
| 418 | tone->index = index; | ||
| 419 | tone->count = count; | ||
| 420 | |||
| 421 | /* return sk_buff */ | ||
| 422 | return; | ||
| 423 | } | ||
| 424 | |||
| 425 | |||
| 426 | /******************************* | ||
| 427 | * send HW message to hfc card * | ||
| 428 | *******************************/ | ||
| 429 | |||
| 430 | static void | ||
| 431 | dsp_tone_hw_message(struct dsp *dsp, u8 *sample, int len) | ||
| 432 | { | ||
| 433 | struct sk_buff *nskb; | ||
| 434 | |||
| 435 | /* unlocking is not required, because we don't expect a response */ | ||
| 436 | nskb = _alloc_mISDN_skb(PH_CONTROL_REQ, | ||
| 437 | (len)?HFC_SPL_LOOP_ON:HFC_SPL_LOOP_OFF, len, sample, | ||
| 438 | GFP_ATOMIC); | ||
| 439 | if (nskb) { | ||
| 440 | if (dsp->ch.peer) { | ||
| 441 | if (dsp->ch.recv(dsp->ch.peer, nskb)) | ||
| 442 | dev_kfree_skb(nskb); | ||
| 443 | } else | ||
| 444 | dev_kfree_skb(nskb); | ||
| 445 | } | ||
| 446 | } | ||
| 447 | |||
| 448 | |||
| 449 | /***************** | ||
| 450 | * timer expires * | ||
| 451 | *****************/ | ||
| 452 | void | ||
| 453 | dsp_tone_timeout(void *arg) | ||
| 454 | { | ||
| 455 | struct dsp *dsp = arg; | ||
| 456 | struct dsp_tone *tone = &dsp->tone; | ||
| 457 | struct pattern *pat = (struct pattern *)tone->pattern; | ||
| 458 | int index = tone->index; | ||
| 459 | |||
| 460 | if (!tone->tone) | ||
| 461 | return; | ||
| 462 | |||
| 463 | index++; | ||
| 464 | if (!pat->seq[index]) | ||
| 465 | index = 0; | ||
| 466 | tone->index = index; | ||
| 467 | |||
| 468 | /* set next tone */ | ||
| 469 | if (pat->data[index] == DATA_S) | ||
| 470 | dsp_tone_hw_message(dsp, 0, 0); | ||
| 471 | else | ||
| 472 | dsp_tone_hw_message(dsp, pat->data[index], *(pat->siz[index])); | ||
| 473 | /* set timer */ | ||
| 474 | init_timer(&tone->tl); | ||
| 475 | tone->tl.expires = jiffies + (pat->seq[index] * HZ) / 8000; | ||
| 476 | add_timer(&tone->tl); | ||
| 477 | } | ||
| 478 | |||
| 479 | |||
| 480 | /******************** | ||
| 481 | * set/release tone * | ||
| 482 | ********************/ | ||
| 483 | |||
| 484 | /* | ||
| 485 | * tones are relaized by streaming or by special loop commands if supported | ||
| 486 | * by hardware. when hardware is used, the patterns will be controlled by | ||
| 487 | * timers. | ||
| 488 | */ | ||
| 489 | int | ||
| 490 | dsp_tone(struct dsp *dsp, int tone) | ||
| 491 | { | ||
| 492 | struct pattern *pat; | ||
| 493 | int i; | ||
| 494 | struct dsp_tone *tonet = &dsp->tone; | ||
| 495 | |||
| 496 | tonet->software = 0; | ||
| 497 | tonet->hardware = 0; | ||
| 498 | |||
| 499 | /* we turn off the tone */ | ||
| 500 | if (!tone) { | ||
| 501 | if (dsp->features.hfc_loops) | ||
| 502 | if (timer_pending(&tonet->tl)) | ||
| 503 | del_timer(&tonet->tl); | ||
| 504 | if (dsp->features.hfc_loops) | ||
| 505 | dsp_tone_hw_message(dsp, NULL, 0); | ||
| 506 | tonet->tone = 0; | ||
| 507 | return 0; | ||
| 508 | } | ||
| 509 | |||
| 510 | pat = NULL; | ||
| 511 | i = 0; | ||
| 512 | while (pattern[i].tone) { | ||
| 513 | if (pattern[i].tone == tone) { | ||
| 514 | pat = &pattern[i]; | ||
| 515 | break; | ||
| 516 | } | ||
| 517 | i++; | ||
| 518 | } | ||
| 519 | if (!pat) { | ||
| 520 | printk(KERN_WARNING "dsp: given tone 0x%x is invalid\n", tone); | ||
| 521 | return -EINVAL; | ||
| 522 | } | ||
| 523 | if (dsp_debug & DEBUG_DSP_TONE) | ||
| 524 | printk(KERN_DEBUG "%s: now starting tone %d (index=%d)\n", | ||
| 525 | __func__, tone, 0); | ||
| 526 | tonet->tone = tone; | ||
| 527 | tonet->pattern = pat; | ||
| 528 | tonet->index = 0; | ||
| 529 | tonet->count = 0; | ||
| 530 | |||
| 531 | if (dsp->features.hfc_loops) { | ||
| 532 | tonet->hardware = 1; | ||
| 533 | /* set first tone */ | ||
| 534 | dsp_tone_hw_message(dsp, pat->data[0], *(pat->siz[0])); | ||
| 535 | /* set timer */ | ||
| 536 | if (timer_pending(&tonet->tl)) | ||
| 537 | del_timer(&tonet->tl); | ||
| 538 | init_timer(&tonet->tl); | ||
| 539 | tonet->tl.expires = jiffies + (pat->seq[0] * HZ) / 8000; | ||
| 540 | add_timer(&tonet->tl); | ||
| 541 | } else { | ||
| 542 | tonet->software = 1; | ||
| 543 | } | ||
| 544 | |||
| 545 | return 0; | ||
| 546 | } | ||
| 547 | |||
| 548 | |||
| 549 | |||
| 550 | |||
| 551 | |||
diff --git a/include/linux/mISDNdsp.h b/include/linux/mISDNdsp.h new file mode 100644 index 000000000000..6b71d2dce508 --- /dev/null +++ b/include/linux/mISDNdsp.h | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | #ifndef __mISDNdsp_H__ | ||
| 2 | #define __mISDNdsp_H__ | ||
| 3 | |||
| 4 | struct mISDN_dsp_element_arg { | ||
| 5 | char *name; | ||
| 6 | char *def; | ||
| 7 | char *desc; | ||
| 8 | }; | ||
| 9 | |||
| 10 | struct mISDN_dsp_element { | ||
| 11 | char *name; | ||
| 12 | void *(*new)(const char *arg); | ||
| 13 | void (*free)(void *p); | ||
| 14 | void (*process_tx)(void *p, unsigned char *data, int len); | ||
| 15 | void (*process_rx)(void *p, unsigned char *data, int len); | ||
| 16 | int num_args; | ||
| 17 | struct mISDN_dsp_element_arg | ||
| 18 | *args; | ||
| 19 | }; | ||
| 20 | |||
| 21 | extern int mISDN_dsp_element_register(struct mISDN_dsp_element *elem); | ||
| 22 | extern void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem); | ||
| 23 | |||
| 24 | struct dsp_features { | ||
| 25 | int hfc_id; /* unique id to identify the chip (or -1) */ | ||
| 26 | int hfc_dtmf; /* set if HFCmulti card supports dtmf */ | ||
| 27 | int hfc_loops; /* set if card supports tone loops */ | ||
| 28 | int hfc_echocanhw; /* set if card supports echocancelation*/ | ||
| 29 | int pcm_id; /* unique id to identify the pcm bus (or -1) */ | ||
| 30 | int pcm_slots; /* number of slots on the pcm bus */ | ||
| 31 | int pcm_banks; /* number of IO banks of pcm bus */ | ||
| 32 | int unclocked; /* data is not clocked (has jitter/loss) */ | ||
| 33 | int unordered; /* data is unordered (packets have index) */ | ||
| 34 | }; | ||
| 35 | |||
| 36 | #endif | ||
| 37 | |||
