diff -ruN mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/dsp_audio.c mISDN-1_1_5/drivers/isdn/hardware/mISDN/dsp_audio.c --- mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/dsp_audio.c 2007-07-02 19:01:34.000000000 +0930 +++ mISDN-1_1_5/drivers/isdn/hardware/mISDN/dsp_audio.c 2007-09-15 19:54:02.000000000 +0930 @@ -15,331 +15,173 @@ #include "dsp.h" /* ulaw[unsigned char] -> signed 16-bit */ -s32 dsp_audio_ulaw_to_s32[256] = -{ - 0xffff8284, 0xffff8684, 0xffff8a84, 0xffff8e84, - 0xffff9284, 0xffff9684, 0xffff9a84, 0xffff9e84, - 0xffffa284, 0xffffa684, 0xffffaa84, 0xffffae84, - 0xffffb284, 0xffffb684, 0xffffba84, 0xffffbe84, - 0xffffc184, 0xffffc384, 0xffffc584, 0xffffc784, - 0xffffc984, 0xffffcb84, 0xffffcd84, 0xffffcf84, - 0xffffd184, 0xffffd384, 0xffffd584, 0xffffd784, - 0xffffd984, 0xffffdb84, 0xffffdd84, 0xffffdf84, - 0xffffe104, 0xffffe204, 0xffffe304, 0xffffe404, - 0xffffe504, 0xffffe604, 0xffffe704, 0xffffe804, - 0xffffe904, 0xffffea04, 0xffffeb04, 0xffffec04, - 0xffffed04, 0xffffee04, 0xffffef04, 0xfffff004, - 0xfffff0c4, 0xfffff144, 0xfffff1c4, 0xfffff244, - 0xfffff2c4, 0xfffff344, 0xfffff3c4, 0xfffff444, - 0xfffff4c4, 0xfffff544, 0xfffff5c4, 0xfffff644, - 0xfffff6c4, 0xfffff744, 0xfffff7c4, 0xfffff844, - 0xfffff8a4, 0xfffff8e4, 0xfffff924, 0xfffff964, - 0xfffff9a4, 0xfffff9e4, 0xfffffa24, 0xfffffa64, - 0xfffffaa4, 0xfffffae4, 0xfffffb24, 0xfffffb64, - 0xfffffba4, 0xfffffbe4, 0xfffffc24, 0xfffffc64, - 0xfffffc94, 0xfffffcb4, 0xfffffcd4, 0xfffffcf4, - 0xfffffd14, 0xfffffd34, 0xfffffd54, 0xfffffd74, - 0xfffffd94, 0xfffffdb4, 0xfffffdd4, 0xfffffdf4, - 0xfffffe14, 0xfffffe34, 0xfffffe54, 0xfffffe74, - 0xfffffe8c, 0xfffffe9c, 0xfffffeac, 0xfffffebc, - 0xfffffecc, 0xfffffedc, 0xfffffeec, 0xfffffefc, - 0xffffff0c, 0xffffff1c, 0xffffff2c, 0xffffff3c, - 0xffffff4c, 0xffffff5c, 0xffffff6c, 0xffffff7c, - 0xffffff88, 0xffffff90, 0xffffff98, 0xffffffa0, - 0xffffffa8, 0xffffffb0, 0xffffffb8, 0xffffffc0, - 0xffffffc8, 0xffffffd0, 0xffffffd8, 0xffffffe0, - 0xffffffe8, 0xfffffff0, 0xfffffff8, 0xffffffff, - 0x00007d7c, 0x0000797c, 0x0000757c, 0x0000717c, - 0x00006d7c, 0x0000697c, 0x0000657c, 0x0000617c, - 0x00005d7c, 0x0000597c, 0x0000557c, 0x0000517c, - 0x00004d7c, 0x0000497c, 0x0000457c, 0x0000417c, - 0x00003e7c, 0x00003c7c, 0x00003a7c, 0x0000387c, - 0x0000367c, 0x0000347c, 0x0000327c, 0x0000307c, - 0x00002e7c, 0x00002c7c, 0x00002a7c, 0x0000287c, - 0x0000267c, 0x0000247c, 0x0000227c, 0x0000207c, - 0x00001efc, 0x00001dfc, 0x00001cfc, 0x00001bfc, - 0x00001afc, 0x000019fc, 0x000018fc, 0x000017fc, - 0x000016fc, 0x000015fc, 0x000014fc, 0x000013fc, - 0x000012fc, 0x000011fc, 0x000010fc, 0x00000ffc, - 0x00000f3c, 0x00000ebc, 0x00000e3c, 0x00000dbc, - 0x00000d3c, 0x00000cbc, 0x00000c3c, 0x00000bbc, - 0x00000b3c, 0x00000abc, 0x00000a3c, 0x000009bc, - 0x0000093c, 0x000008bc, 0x0000083c, 0x000007bc, - 0x0000075c, 0x0000071c, 0x000006dc, 0x0000069c, - 0x0000065c, 0x0000061c, 0x000005dc, 0x0000059c, - 0x0000055c, 0x0000051c, 0x000004dc, 0x0000049c, - 0x0000045c, 0x0000041c, 0x000003dc, 0x0000039c, - 0x0000036c, 0x0000034c, 0x0000032c, 0x0000030c, - 0x000002ec, 0x000002cc, 0x000002ac, 0x0000028c, - 0x0000026c, 0x0000024c, 0x0000022c, 0x0000020c, - 0x000001ec, 0x000001cc, 0x000001ac, 0x0000018c, - 0x00000174, 0x00000164, 0x00000154, 0x00000144, - 0x00000134, 0x00000124, 0x00000114, 0x00000104, - 0x000000f4, 0x000000e4, 0x000000d4, 0x000000c4, - 0x000000b4, 0x000000a4, 0x00000094, 0x00000084, - 0x00000078, 0x00000070, 0x00000068, 0x00000060, - 0x00000058, 0x00000050, 0x00000048, 0x00000040, - 0x00000038, 0x00000030, 0x00000028, 0x00000020, - 0x00000018, 0x00000010, 0x00000008, 0x00000000 -}; - +s32 dsp_audio_ulaw_to_s32[256]; /* alaw[unsigned char] -> signed 16-bit */ -s32 dsp_audio_alaw_to_s32[256] = -{ - 0x000013fc, 0xffffec04, 0x00000144, 0xfffffebc, - 0x0000517c, 0xffffae84, 0x0000051c, 0xfffffae4, - 0x00000a3c, 0xfffff5c4, 0x00000048, 0xffffffb8, - 0x0000287c, 0xffffd784, 0x0000028c, 0xfffffd74, - 0x00001bfc, 0xffffe404, 0x000001cc, 0xfffffe34, - 0x0000717c, 0xffff8e84, 0x0000071c, 0xfffff8e4, - 0x00000e3c, 0xfffff1c4, 0x000000c4, 0xffffff3c, - 0x0000387c, 0xffffc784, 0x0000039c, 0xfffffc64, - 0x00000ffc, 0xfffff004, 0x00000104, 0xfffffefc, - 0x0000417c, 0xffffbe84, 0x0000041c, 0xfffffbe4, - 0x0000083c, 0xfffff7c4, 0x00000008, 0xfffffff8, - 0x0000207c, 0xffffdf84, 0x0000020c, 0xfffffdf4, - 0x000017fc, 0xffffe804, 0x0000018c, 0xfffffe74, - 0x0000617c, 0xffff9e84, 0x0000061c, 0xfffff9e4, - 0x00000c3c, 0xfffff3c4, 0x00000084, 0xffffff7c, - 0x0000307c, 0xffffcf84, 0x0000030c, 0xfffffcf4, - 0x000015fc, 0xffffea04, 0x00000164, 0xfffffe9c, - 0x0000597c, 0xffffa684, 0x0000059c, 0xfffffa64, - 0x00000b3c, 0xfffff4c4, 0x00000068, 0xffffff98, - 0x00002c7c, 0xffffd384, 0x000002cc, 0xfffffd34, - 0x00001dfc, 0xffffe204, 0x000001ec, 0xfffffe14, - 0x0000797c, 0xffff8684, 0x000007bc, 0xfffff844, - 0x00000f3c, 0xfffff0c4, 0x000000e4, 0xffffff1c, - 0x00003c7c, 0xffffc384, 0x000003dc, 0xfffffc24, - 0x000011fc, 0xffffee04, 0x00000124, 0xfffffedc, - 0x0000497c, 0xffffb684, 0x0000049c, 0xfffffb64, - 0x0000093c, 0xfffff6c4, 0x00000028, 0xffffffd8, - 0x0000247c, 0xffffdb84, 0x0000024c, 0xfffffdb4, - 0x000019fc, 0xffffe604, 0x000001ac, 0xfffffe54, - 0x0000697c, 0xffff9684, 0x0000069c, 0xfffff964, - 0x00000d3c, 0xfffff2c4, 0x000000a4, 0xffffff5c, - 0x0000347c, 0xffffcb84, 0x0000034c, 0xfffffcb4, - 0x000012fc, 0xffffed04, 0x00000134, 0xfffffecc, - 0x00004d7c, 0xffffb284, 0x000004dc, 0xfffffb24, - 0x000009bc, 0xfffff644, 0x00000038, 0xffffffc8, - 0x0000267c, 0xffffd984, 0x0000026c, 0xfffffd94, - 0x00001afc, 0xffffe504, 0x000001ac, 0xfffffe54, - 0x00006d7c, 0xffff9284, 0x000006dc, 0xfffff924, - 0x00000dbc, 0xfffff244, 0x000000b4, 0xffffff4c, - 0x0000367c, 0xffffc984, 0x0000036c, 0xfffffc94, - 0x00000f3c, 0xfffff0c4, 0x000000f4, 0xffffff0c, - 0x00003e7c, 0xffffc184, 0x000003dc, 0xfffffc24, - 0x000007bc, 0xfffff844, 0x00000008, 0xfffffff8, - 0x00001efc, 0xffffe104, 0x000001ec, 0xfffffe14, - 0x000016fc, 0xffffe904, 0x00000174, 0xfffffe8c, - 0x00005d7c, 0xffffa284, 0x000005dc, 0xfffffa24, - 0x00000bbc, 0xfffff444, 0x00000078, 0xffffff88, - 0x00002e7c, 0xffffd184, 0x000002ec, 0xfffffd14, - 0x000014fc, 0xffffeb04, 0x00000154, 0xfffffeac, - 0x0000557c, 0xffffaa84, 0x0000055c, 0xfffffaa4, - 0x00000abc, 0xfffff544, 0x00000058, 0xffffffa8, - 0x00002a7c, 0xffffd584, 0x000002ac, 0xfffffd54, - 0x00001cfc, 0xffffe304, 0x000001cc, 0xfffffe34, - 0x0000757c, 0xffff8a84, 0x0000075c, 0xfffff8a4, - 0x00000ebc, 0xfffff144, 0x000000d4, 0xffffff2c, - 0x00003a7c, 0xffffc584, 0x0000039c, 0xfffffc64, - 0x000010fc, 0xffffef04, 0x00000114, 0xfffffeec, - 0x0000457c, 0xffffba84, 0x0000045c, 0xfffffba4, - 0x000008bc, 0xfffff744, 0x00000018, 0xffffffe8, - 0x0000227c, 0xffffdd84, 0x0000022c, 0xfffffdd4, - 0x000018fc, 0xffffe704, 0x0000018c, 0xfffffe74, - 0x0000657c, 0xffff9a84, 0x0000065c, 0xfffff9a4, - 0x00000cbc, 0xfffff344, 0x00000094, 0xffffff6c, - 0x0000327c, 0xffffcd84, 0x0000032c, 0xfffffcd4 -}; +s32 dsp_audio_alaw_to_s32[256]; s32 *dsp_audio_law_to_s32; /* signed 16-bit -> law */ u8 dsp_audio_s16_to_law[65536]; -/* table is used to generate s16_to_alaw */ -static short dsp_audio_alaw_relations[512] = +/* alaw -> ulaw */ +u8 dsp_audio_alaw_to_ulaw[256]; +/* ulaw -> alaw */ +u8 dsp_audio_ulaw_to_alaw[256]; +u8 dsp_silence; + + +/***************************************************** + * generate table for conversion of s16 to alaw/ulaw * + *****************************************************/ + +#define AMI_MASK 0x55 + +static inline unsigned char linear2alaw (short int linear) { - 0x8684, 0x55, 0x8a84, 0xd5, 0x8e84, 0x15, 0x9284, 0x95, - 0x9684, 0x75, 0x9a84, 0xf5, 0x9e84, 0x35, 0xa284, 0xb5, - 0xa684, 0x45, 0xaa84, 0xc5, 0xae84, 0x05, 0xb284, 0x85, - 0xb684, 0x65, 0xba84, 0xe5, 0xbe84, 0x25, 0xc184, 0xa5, - 0xc384, 0x5d, 0xc584, 0xdd, 0xc784, 0x1d, 0xc984, 0x9d, - 0xcb84, 0x7d, 0xcd84, 0xfd, 0xcf84, 0x3d, 0xd184, 0xbd, - 0xd384, 0x4d, 0xd584, 0xcd, 0xd784, 0x0d, 0xd984, 0x8d, - 0xdb84, 0x6d, 0xdd84, 0xed, 0xdf84, 0x2d, 0xe104, 0xad, - 0xe204, 0x51, 0xe304, 0xd1, 0xe404, 0x11, 0xe504, 0x91, - 0xe604, 0x71, 0xe704, 0xf1, 0xe804, 0x31, 0xe904, 0xb1, - 0xea04, 0x41, 0xeb04, 0xc1, 0xec04, 0x01, 0xed04, 0x81, - 0xee04, 0x61, 0xef04, 0xe1, 0xf004, 0x21, 0xf0c4, 0x59, - 0xf0c4, 0xa1, 0xf144, 0xd9, 0xf1c4, 0x19, 0xf244, 0x99, - 0xf2c4, 0x79, 0xf344, 0xf9, 0xf3c4, 0x39, 0xf444, 0xb9, - 0xf4c4, 0x49, 0xf544, 0xc9, 0xf5c4, 0x09, 0xf644, 0x89, - 0xf6c4, 0x69, 0xf744, 0xe9, 0xf7c4, 0x29, 0xf844, 0x57, - 0xf844, 0xa9, 0xf8a4, 0xd7, 0xf8e4, 0x17, 0xf924, 0x97, - 0xf964, 0x77, 0xf9a4, 0xf7, 0xf9e4, 0x37, 0xfa24, 0xb7, - 0xfa64, 0x47, 0xfaa4, 0xc7, 0xfae4, 0x07, 0xfb24, 0x87, - 0xfb64, 0x67, 0xfba4, 0xe7, 0xfbe4, 0x27, 0xfc24, 0x5f, - 0xfc24, 0xa7, 0xfc64, 0x1f, 0xfc64, 0xdf, 0xfc94, 0x9f, - 0xfcb4, 0x7f, 0xfcd4, 0xff, 0xfcf4, 0x3f, 0xfd14, 0xbf, - 0xfd34, 0x4f, 0xfd54, 0xcf, 0xfd74, 0x0f, 0xfd94, 0x8f, - 0xfdb4, 0x6f, 0xfdd4, 0xef, 0xfdf4, 0x2f, 0xfe14, 0x53, - 0xfe14, 0xaf, 0xfe34, 0x13, 0xfe34, 0xd3, 0xfe54, 0x73, - 0xfe54, 0x93, 0xfe74, 0x33, 0xfe74, 0xf3, 0xfe8c, 0xb3, - 0xfe9c, 0x43, 0xfeac, 0xc3, 0xfebc, 0x03, 0xfecc, 0x83, - 0xfedc, 0x63, 0xfeec, 0xe3, 0xfefc, 0x23, 0xff0c, 0xa3, - 0xff1c, 0x5b, 0xff2c, 0xdb, 0xff3c, 0x1b, 0xff4c, 0x9b, - 0xff5c, 0x7b, 0xff6c, 0xfb, 0xff7c, 0x3b, 0xff88, 0xbb, - 0xff98, 0x4b, 0xffa8, 0xcb, 0xffb8, 0x0b, 0xffc8, 0x8b, - 0xffd8, 0x6b, 0xffe8, 0xeb, 0xfff8, 0x2b, 0xfff8, 0xab, - 0x0008, 0x2a, 0x0008, 0xaa, 0x0018, 0xea, 0x0028, 0x6a, - 0x0038, 0x8a, 0x0048, 0x0a, 0x0058, 0xca, 0x0068, 0x4a, - 0x0078, 0xba, 0x0084, 0x3a, 0x0094, 0xfa, 0x00a4, 0x7a, - 0x00b4, 0x9a, 0x00c4, 0x1a, 0x00d4, 0xda, 0x00e4, 0x5a, - 0x00f4, 0xa2, 0x0104, 0x22, 0x0114, 0xe2, 0x0124, 0x62, - 0x0134, 0x82, 0x0144, 0x02, 0x0154, 0xc2, 0x0164, 0x42, - 0x0174, 0xb2, 0x018c, 0x32, 0x018c, 0xf2, 0x01ac, 0x72, - 0x01ac, 0x92, 0x01cc, 0x12, 0x01cc, 0xd2, 0x01ec, 0x52, - 0x01ec, 0xae, 0x020c, 0x2e, 0x022c, 0xee, 0x024c, 0x6e, - 0x026c, 0x8e, 0x028c, 0x0e, 0x02ac, 0xce, 0x02cc, 0x4e, - 0x02ec, 0xbe, 0x030c, 0x3e, 0x032c, 0xfe, 0x034c, 0x7e, - 0x036c, 0x9e, 0x039c, 0x1e, 0x039c, 0xde, 0x03dc, 0x5e, - 0x03dc, 0xa6, 0x041c, 0x26, 0x045c, 0xe6, 0x049c, 0x66, - 0x04dc, 0x86, 0x051c, 0x06, 0x055c, 0xc6, 0x059c, 0x46, - 0x05dc, 0xb6, 0x061c, 0x36, 0x065c, 0xf6, 0x069c, 0x76, - 0x06dc, 0x96, 0x071c, 0x16, 0x075c, 0xd6, 0x07bc, 0x56, - 0x07bc, 0xa8, 0x083c, 0x28, 0x08bc, 0xe8, 0x093c, 0x68, - 0x09bc, 0x88, 0x0a3c, 0x08, 0x0abc, 0xc8, 0x0b3c, 0x48, - 0x0bbc, 0xb8, 0x0c3c, 0x38, 0x0cbc, 0xf8, 0x0d3c, 0x78, - 0x0dbc, 0x98, 0x0e3c, 0x18, 0x0ebc, 0xd8, 0x0f3c, 0x58, - 0x0f3c, 0xa0, 0x0ffc, 0x20, 0x10fc, 0xe0, 0x11fc, 0x60, - 0x12fc, 0x80, 0x13fc, 0x00, 0x14fc, 0xc0, 0x15fc, 0x40, - 0x16fc, 0xb0, 0x17fc, 0x30, 0x18fc, 0xf0, 0x19fc, 0x70, - 0x1afc, 0x90, 0x1bfc, 0x10, 0x1cfc, 0xd0, 0x1dfc, 0x50, - 0x1efc, 0xac, 0x207c, 0x2c, 0x227c, 0xec, 0x247c, 0x6c, - 0x267c, 0x8c, 0x287c, 0x0c, 0x2a7c, 0xcc, 0x2c7c, 0x4c, - 0x2e7c, 0xbc, 0x307c, 0x3c, 0x327c, 0xfc, 0x347c, 0x7c, - 0x367c, 0x9c, 0x387c, 0x1c, 0x3a7c, 0xdc, 0x3c7c, 0x5c, - 0x3e7c, 0xa4, 0x417c, 0x24, 0x457c, 0xe4, 0x497c, 0x64, - 0x4d7c, 0x84, 0x517c, 0x04, 0x557c, 0xc4, 0x597c, 0x44, - 0x5d7c, 0xb4, 0x617c, 0x34, 0x657c, 0xf4, 0x697c, 0x74, - 0x6d7c, 0x94, 0x717c, 0x14, 0x757c, 0xd4, 0x797c, 0x54 -}; + int mask; + int seg; + int pcm_val; + static int seg_end[8] = { + 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF + }; + + pcm_val = linear; + if (pcm_val >= 0) { + /* Sign (7th) bit = 1 */ + mask = AMI_MASK | 0x80; + } else { + /* Sign bit = 0 */ + mask = AMI_MASK; + pcm_val = -pcm_val; + } + /* Convert the scaled magnitude to segment number. */ + for (seg = 0; seg < 8; seg++) { + if (pcm_val <= seg_end[seg]) + break; + } + /* Combine the sign, segment, and quantization bits. */ + return ((seg << 4) | + ((pcm_val >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask; +} -/* alaw -> ulaw */ -u8 dsp_audio_alaw_to_ulaw[256] = + +static inline short int alaw2linear (unsigned char alaw) { - 0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49, - 0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57, - 0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41, - 0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f, - 0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d, - 0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b, - 0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45, - 0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53, - 0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47, - 0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55, - 0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f, - 0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e, - 0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b, - 0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59, - 0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43, - 0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51, - 0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a, - 0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58, - 0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42, - 0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50, - 0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e, - 0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c, - 0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46, - 0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54, - 0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48, - 0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56, - 0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40, - 0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f, - 0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c, - 0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a, - 0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44, - 0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52 -}; + int i; + int seg; -/* ulaw -> alaw */ -u8 dsp_audio_ulaw_to_alaw[256] = + alaw ^= AMI_MASK; + i = ((alaw & 0x0F) << 4) + 8 /* rounding error */; + seg = (((int) alaw & 0x70) >> 4); + if (seg) + i = (i + 0x100) << (seg - 1); + return (short int) ((alaw & 0x80) ? i : -i); +} + +static inline short int ulaw2linear(unsigned char ulaw) { - 0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35, - 0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25, - 0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d, - 0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d, - 0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31, - 0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21, - 0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9, - 0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9, - 0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47, - 0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf, - 0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f, - 0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33, - 0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23, - 0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b, - 0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b, - 0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b, - 0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34, - 0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24, - 0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c, - 0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c, - 0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30, - 0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20, - 0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8, - 0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8, - 0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46, - 0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde, - 0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e, - 0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32, - 0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22, - 0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a, - 0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a, - 0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a -}; + short mu, e, f, y; + static short etab[] = {0,132,396,924,1980,4092,8316,16764}; -u8 dsp_silence; + mu = 255 - ulaw; + e = (mu & 0x70) / 16; + f = mu & 0x0f; + y = f * (1 << (e + 3)); + y += etab[e]; + if (mu & 0x80) + y = -y; + return y; +} +#define BIAS 0x84 /*!< define the add-in bias for 16 bit samples */ -/***************************************************** - * generate table for conversion of s16 to alaw/ulaw * - *****************************************************/ +static unsigned char linear2ulaw(short sample) +{ + static int exp_lut[256] = { + 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 }; + int sign, exponent, mantissa; + unsigned char ulawbyte; + + /* Get the sample into sign-magnitude. */ + sign = (sample >> 8) & 0x80; /* set aside the sign */ + if (sign != 0) + sample = -sample; /* get magnitude */ + + /* Convert from 16 bit linear to ulaw. */ + sample = sample + BIAS; + exponent = exp_lut[(sample >> 7) & 0xFF]; + mantissa = (sample >> (exponent + 3)) & 0x0F; + ulawbyte = ~(sign | (exponent << 4) | mantissa); + + return ulawbyte; +} + +static int reverse_bits(int i) +{ + int z,j; + z = 0; + + for (j = 0; j < 8; j++) { + if ((i & (1 << j)) != 0) { + z |= 1 << (7 - j); + } + } + return z; +} + + +void dsp_audio_generate_law_tables(void) +{ + int i; + for (i = 0; i < 256; i++) { + dsp_audio_alaw_to_s32[i] = alaw2linear(reverse_bits(i)); + } + + for (i = 0; i < 256; i++) { + dsp_audio_ulaw_to_s32[i] = ulaw2linear(reverse_bits(i)); + } + + for (i = 0; i < 256; i++) { + dsp_audio_alaw_to_ulaw[i] = + linear2ulaw(dsp_audio_alaw_to_s32[i]); + dsp_audio_ulaw_to_alaw[i] = + linear2alaw(dsp_audio_ulaw_to_s32[i]); + } +} void dsp_audio_generate_s2law_table(void) { - int i, j; + int i; if (dsp_options & DSP_OPT_ULAW) { /* generating ulaw-table */ - i = j = 0; - while(i < 32768) { - if (i-32768 > dsp_audio_law_to_s32[j]) - j++; - dsp_audio_s16_to_law[(i-32768) & 0xffff] = j; - i++; - } - j = 255; - while(i < 65536) { - if (i-32768 > dsp_audio_law_to_s32[j]) - j--; - dsp_audio_s16_to_law[(i-32768) & 0xffff] = j; - i++; + for (i = -32768; i < 32768; i++) { + dsp_audio_s16_to_law[i & 0xffff] = + reverse_bits(linear2ulaw(i)); } } else { /* generating alaw-table */ - i = j = 0; - while(i < 65536) { - if (i-32768 > dsp_audio_alaw_relations[j<<1]) - j++; - if (j>255) - j=255; - dsp_audio_s16_to_law[(i-32768) & 0xffff] - = dsp_audio_alaw_relations[(j<<1)|1]; - i++; + for (i = -32768; i < 32768; i++) { + dsp_audio_s16_to_law[i & 0xffff] = + reverse_bits(linear2alaw(i)); } } } @@ -359,7 +201,7 @@ void dsp_audio_generate_seven(void) { - int i, j; + int i, j, z; u8 spl; /* conversion from law to seven bit audio */ @@ -370,30 +212,16 @@ if (dsp_options & DSP_OPT_ULAW) spl = dsp_audio_ulaw_to_alaw[i]; /* find the 7-bit-sample */ - j = 0; - while(j < 256) { - if (dsp_audio_alaw_relations[(j<<1)|1] == spl) - break; - j++; - } - if (j == 256) { - printk(KERN_WARNING "fatal error in %s: alaw-sample '0x%2x' not found in relations-table.\n", __FUNCTION__, spl); + j = 0; + for (z = 0; z < 256; z++) { + if (dsp_audio_alaw_to_s32[z] < dsp_audio_alaw_to_s32[spl]) { + j++; + } } + /* write 7-bit audio value */ dsp_audio_law2seven[i] = j >> 1; - i++; - } - - /* conversion from seven bit audio to law */ - i = 0; - while(i < 128) { - /* find alaw-spl */ - spl = dsp_audio_alaw_relations[(i<<2)|1]; - /* convert to ulaw, if required */ - if (dsp_options & DSP_OPT_ULAW) - spl = dsp_audio_alaw_to_ulaw[spl]; - /* write 8-bit law sample */ - dsp_audio_seven2law[i] = spl; + if ((j & 1) == 0) dsp_audio_seven2law[j >> 1] = i; i++; } } @@ -475,60 +303,62 @@ { register s32 sample; int i; + int num[] = { 110, 125, 150, 175, 200, 300, 400, 500 }; + int denum[] = { 100, 100, 100, 100, 100, 100, 100, 100 }; i = 0; while(i < 256) { - dsp_audio_reduce8[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>8) & 0xffff]; - dsp_audio_reduce7[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>7) & 0xffff]; - dsp_audio_reduce6[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>6) & 0xffff]; - dsp_audio_reduce5[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>5) & 0xffff]; - dsp_audio_reduce4[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>4) & 0xffff]; - dsp_audio_reduce3[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>3) & 0xffff]; - dsp_audio_reduce2[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>2) & 0xffff]; - dsp_audio_reduce1[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>1) & 0xffff]; - sample = dsp_audio_law_to_s32[i] << 1; + dsp_audio_reduce8[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i] * denum[7] / num[7] ) & 0xffff]; + dsp_audio_reduce7[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i] * denum[6] / num[6]) & 0xffff]; + dsp_audio_reduce6[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i] * denum[5] / num[5]) & 0xffff]; + dsp_audio_reduce5[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i] * denum[4] / num[4]) & 0xffff]; + dsp_audio_reduce4[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i] * denum[3] / num[3]) & 0xffff]; + dsp_audio_reduce3[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i] * denum[2] / num[2]) & 0xffff]; + dsp_audio_reduce2[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i] * denum[1] / num[1]) & 0xffff]; + dsp_audio_reduce1[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i] * denum[0] / num[0]) & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[0] / denum[0]; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; dsp_audio_increase1[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] << 2; + sample = dsp_audio_law_to_s32[i] * num[1] / denum[1]; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; dsp_audio_increase2[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] << 3; + sample = dsp_audio_law_to_s32[i] * num[2] / denum[2]; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; dsp_audio_increase3[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] << 4; + sample = dsp_audio_law_to_s32[i] * num[3] / denum[3]; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; dsp_audio_increase4[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] << 5; + sample = dsp_audio_law_to_s32[i] * num[4] / denum[4]; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; dsp_audio_increase5[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] << 6; + sample = dsp_audio_law_to_s32[i] * num[5] / denum[5]; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; dsp_audio_increase6[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] << 7; + sample = dsp_audio_law_to_s32[i] * num[6] / denum[6]; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; dsp_audio_increase7[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] << 8; + sample = dsp_audio_law_to_s32[i] * num[7] / denum[7]; if (sample < -32768) sample = -32768; else if (sample > 32767) diff -ruN mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/dsp_cancel.c mISDN-1_1_5/drivers/isdn/hardware/mISDN/dsp_cancel.c --- mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/dsp_cancel.c 2007-07-02 19:01:34.000000000 +0930 +++ mISDN-1_1_5/drivers/isdn/hardware/mISDN/dsp_cancel.c 2007-09-16 01:31:20.000000000 +0930 @@ -52,6 +52,8 @@ int bchdev_echocancel_activate(dsp_t* dev, int deftaps, int train); void bchdev_echocancel_deactivate(dsp_t* dev); +static int samples; +extern int sample_impulse; void dsp_cancel_tx(dsp_t *dsp, u8 *data, int len) @@ -60,6 +62,23 @@ if (!data) return; if (dsp->txbuflen + len < ECHOCAN_BUFLEN) { + + /* Start Zaptap code -----------------------------------------*/ + + if (sample_impulse) { + int x; + for (x=0;xtxbuf[dsp->txbuflen],data,len); dsp->txbuflen+=len; } else { @@ -152,6 +171,7 @@ //printk("Enabling EC\n"); dsp->cancel_enable = 1; + samples = 0; return(0); } @@ -301,6 +321,10 @@ dev->ecdis_wr = NULL; } +extern int ec_debug; +void sample_echo_before(int channo, short rxlin, short txlin); +void sample_echo_after(int channo, short rxlin); + /** Processes one TX- and one RX-packet with echocancellation */ void bchdev_echocancel_chunk(dsp_t* ss, uint8_t *rxchunk, uint8_t *txchunk, uint16_t size) { @@ -338,10 +362,16 @@ } } else { for (x=0;xec, txlin, rxlin); + if (ec_debug & 0x4) { + sample_echo_before(1, rxlin, txlin); + rxlin = echo_can_update(ss->ec, txlin, rxlin); + sample_echo_after(1, rxlin); + } rxchunk[x] = dsp_audio_s16_to_law[rxlin &0xffff]; + } } } } diff -ruN mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/dsp_core.c mISDN-1_1_5/drivers/isdn/hardware/mISDN/dsp_core.c --- mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/dsp_core.c 2007-07-02 19:01:34.000000000 +0930 +++ mISDN-1_1_5/drivers/isdn/hardware/mISDN/dsp_core.c 2007-09-16 01:06:14.000000000 +0930 @@ -174,6 +174,7 @@ #include #include #include +#include #include "core.h" #include "layer1.h" #include "helper.h" @@ -183,6 +184,8 @@ static char DSPName[] = "DSP"; mISDNobject_t dsp_obj; +int ec_debug; + static int debug = 0; int dsp_debug; static int options = 0; @@ -210,6 +213,169 @@ #endif #endif +/* Zaptap code -----------------------------------------------------------*/ + +#define SAMPLE_BUF_SZ 1000 +#define SAMPLE_IDLE 0 +#define SAMPLE_PING 1 +#define SAMPLE_PONG 2 + +DECLARE_WAIT_QUEUE_HEAD(sample_wait); +static int sample_state = 0; +int samples = 0; +static short *psample; +static short ping[3*SAMPLE_BUF_SZ]; +static short pong[3*SAMPLE_BUF_SZ]; +static int sample_ch = 1; +int sample_impulse = 0; +static int tmp1,tmp2; + +void sample_echo_before(int channo, short rxlin, short txlin) { + + // Sample echo canceller signals + // Notes: + // 1. Samples are multiplexed in buffer: + // tx sample + // rx sample + // ec sample + // 2. We needs to sample rx here before echo can as it is + // overwritten. + + tmp1++; + tmp2 = channo; + if ((sample_state != SAMPLE_IDLE) && (channo == sample_ch)) { + *psample++ = txlin; + *psample++ = rxlin; + } +} + +void sample_echo_after(int channo, short rxlin) { + + if ((sample_state != SAMPLE_IDLE) && (channo == sample_ch)) { + + *psample++ = rxlin; + + // sample collection ping-pong buffer logic + + samples++; + if (samples >= SAMPLE_BUF_SZ) { + // time to swap buffers + samples = 0; + + if (sample_state == SAMPLE_PING) { + sample_state = SAMPLE_PONG; + psample = pong; + } + else { + sample_state = SAMPLE_PING; + psample = ping; + } + wake_up_interruptible(&sample_wait); + } + } +} + +/* end Zaptap code -----------------------------------------------------*/ + +/* Zaptap code -----------------------------------------------------*/ + +static int sample_open (struct inode *inode, struct file *file) { + printk("sample_open:\n"); + tmp1 = tmp2 = -1; + + psample = ping; + samples = 0; + sample_state = SAMPLE_PING; + + return 0; +} + +static int sample_release (struct inode *inode, struct file *file) { + printk("sample_release: tmp1 = %d tmp2 = %d\n", tmp1, tmp2); + + sample_state = SAMPLE_IDLE; + sample_impulse = 0; + samples = 0; + + return 0; +} + +static ssize_t sample_read(struct file *file, char *buf, + size_t count, loff_t *ppos) { + int err, len; + short *pread; + + /* wait for next buffer to be prepared by ISR, we read + alternate buffer just after transition. + */ + interruptible_sleep_on(&sample_wait); + + if (sample_state == SAMPLE_PING) { + pread = pong; + } + else { + pread = ping; + } + + len = 3*sizeof(short)*SAMPLE_BUF_SZ; + err = copy_to_user(buf, pread, len); + + if (err != 0) + return -EFAULT; + + return len; +} + +/* ioctls for sample */ + +#define SAMPLE_SET_CHANNEL 0 +#define SAMPLE_TX_IMPULSE 1 + +static int sample_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) { + int retval = 0; + + switch ( cmd ) { + case SAMPLE_SET_CHANNEL: + if (copy_from_user(&sample_ch, (int *)arg, sizeof(int))) + return -EFAULT; + printk("sample_ioctl: sample_ch = %d\n", sample_ch); + break; + case SAMPLE_TX_IMPULSE: + sample_impulse = 1; + printk("sample_ioctl: under impulse power\n"); + break; + default: + retval = -EINVAL; + } + + return retval; +} + +// define which file operations are supported +struct file_operations sample_fops = { + .owner = THIS_MODULE, + .llseek = NULL, + .read = sample_read, + .write = NULL, + .readdir= NULL, + .poll = NULL, + .ioctl = sample_ioctl, + .mmap = NULL, + .open = sample_open, + .flush = NULL, + .release= sample_release, + .fsync = NULL, + .fasync = NULL, + .lock = NULL, + + +}; + +#define SAMPLE_NAME "sample" +#define SAMPLE_MAJOR 34 + +/* end Zaptap code -----------------------------------------------------*/ /* * special message process for DL_CONTROL | REQUEST @@ -462,7 +628,7 @@ if (dsp->tx_volume) dsp_change_volume(skb, dsp->tx_volume); /* cancel echo */ - if (dsp->cancel_enable) + if (dsp->cancel_enable && ec_debug) dsp_cancel_tx(dsp, skb->data, skb->len); /* crypt */ if (dsp->bf_enable) @@ -570,7 +736,7 @@ if (dsp->bf_enable) dsp_bf_decrypt(dsp, skb->data, skb->len); /* if echo cancellation is enabled */ - if (dsp->cancel_enable) + if (dsp->cancel_enable && ec_debug) dsp_cancel_rx(dsp, skb->data, skb->len); /* check if dtmf soft decoding is turned on */ if (dsp->dtmf.software) { @@ -960,6 +1126,27 @@ return(ret); } +static int proc_read_debug(char *buf, char **start, off_t offset, + int count, int *eof, void *data) +{ + int len; + + *eof = 1; + + len = sprintf(buf, "%d\n", ec_debug); + return len; +} + + +static int proc_write_debug(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + + char *endbuffer; + + ec_debug = simple_strtol (buffer, &endbuffer, 10); + return count; +} /* * initialize DSP object @@ -1013,6 +1200,7 @@ INIT_LIST_HEAD(&dsp_obj.ilist); /* initialize audio tables */ + dsp_audio_generate_law_tables(); dsp_silence = (dsp_options&DSP_OPT_ULAW)?0xff:0x2a; dsp_audio_law_to_s32 = (dsp_options&DSP_OPT_ULAW)?dsp_audio_ulaw_to_s32:dsp_audio_alaw_to_s32; dsp_audio_generate_s2law_table(); @@ -1037,7 +1225,34 @@ add_timer(&dsp_spl_tl); mISDN_module_register(THIS_MODULE); + + /* proc interface for debugging echo can noise */ + + { + struct proc_dir_entry *proc_misdn, *proc_debug; + + printk("dsp_core.c: Creating /proc/misdn debug interface\n"); + + proc_misdn = proc_mkdir("misdn", 0); + proc_debug = create_proc_read_entry("misdn/debug", 0, NULL, proc_read_debug, NULL); + proc_debug->write_proc = proc_write_debug; + } + /* start Zaptap code ----------------------------------------*/ + + { + int res; + sample_state = SAMPLE_IDLE; + sample_impulse = 0; + if ((res = register_chrdev (SAMPLE_MAJOR, SAMPLE_NAME, &sample_fops))) { + printk(KERN_ERR "Zaptap unable to register %s char driver on %d\n", SAMPLE_NAME, SAMPLE_MAJOR); + return res; + } + } + printk("Registered %s char driver on major %d\n", SAMPLE_NAME, SAMPLE_MAJOR); + + /* end Zaptap code ------------------------------------------*/ + return(0); } @@ -1050,6 +1265,13 @@ dsp_t *dspl, *nd; int err; + unregister_chrdev (SAMPLE_MAJOR, SAMPLE_NAME); /* Zaptap code */ + + { + remove_proc_entry("misdn/debug", NULL); + remove_proc_entry("misdn", NULL); + } + mISDN_module_unregister(THIS_MODULE); if (timer_pending(&dsp_spl_tl)) diff -ruN mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/dsp_core.c.orig mISDN-1_1_5/drivers/isdn/hardware/mISDN/dsp_core.c.orig --- mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/dsp_core.c.orig 1970-01-01 09:30:00.000000000 +0930 +++ mISDN-1_1_5/drivers/isdn/hardware/mISDN/dsp_core.c.orig 2007-09-15 00:22:16.000000000 +0930 @@ -0,0 +1,1120 @@ +/* $Id: dsp_core.c,v 1.29 2007/03/27 15:06:29 jolly Exp $ + * + * Author Andreas Eversberg (jolly@eversberg.eu) + * Based on source code structure by + * Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/mISDN.cert + * + * Thanks to Karsten Keil (great drivers) + * Cologne Chip (great chips) + * + * This module does: + * Real-time tone generation + * DTMF detection + * Real-time cross-connection and conferrence + * Compensate jitter due to system load and hardware fault. + * All features are done in kernel space and will be realized + * using hardware, if available and supported by chip set. + * Blowfish encryption/decryption + */ + +/* STRUCTURE: + * + * The dsp module provides layer 2 for b-channels (64kbit). It provides + * transparent audio forwarding with special digital signal processing: + * + * - (1) generation of tones + * - (2) detection of dtmf tones + * - (3) crossconnecting and conferences + * - (4) echo generation for delay test + * - (5) volume control + * - (6) disable receive data + * - (7) echo cancelation + * - (8) encryption/decryption + * + * Look: + * TX RX + * ------upper layer------ + * | ^ + * | |(6) + * v | + * +-----+-------------+-----+ + * |(3)(4) | + * | CMX | + * | | + * | +-------------+ + * | | ^ + * | | | + * |+---------+| +----+----+ + * ||(1) || |(5) | + * || || | | + * || Tones || |RX Volume| + * || || | | + * || || | | + * |+----+----+| +----+----+ + * +-----+-----+ ^ + * | | + * v | + * +----+----+ +----+----+ + * |(5) | |(2) | + * | | | | + * |TX Volume| | DTMF | + * | | | | + * | | | | + * +----+----+ +----+----+ + * | ^ + * | | + * v | + * +----+-------------+----+ + * |(7) | + * | | + * | Echo Cancellation | + * | | + * | | + * +----+-------------+----+ + * | ^ + * | | + * v | + * +----+----+ +----+----+ + * |(8) | |(8) | + * | | | | + * | Encrypt | | Decrypt | + * | | | | + * | | | | + * +----+----+ +----+----+ + * | ^ + * | | + * v | + * ------card layer------ + * TX RX + * + * Above you can see the logical data flow. If software is used to do the + * process, it is actually the real data flow. If hardware is used, data + * may not flow, but hardware commands to the card, to provide the data flow + * as shown. + * + * NOTE: The channel must be activated in order to make dsp work, even if + * no data flow to the upper layer is intended. Activation can be done + * after and before controlling the setting using PH_CONTROL requests. + * + * DTMF: Will be detected by hardware if possible. It is done before CMX + * processing. + * + * Tones: Will be generated via software if endless looped audio fifos are + * not supported by hardware. Tones will override all data from CMX. + * It is not required to join a conference to use tones at any time. + * + * CMX: Is transparent when not used. When it is used, it will do + * crossconnections and conferences via software if not possible through + * hardware. If hardware capability is available, hardware is used. + * + * Echo: Is generated by CMX and is used to check performane of hard and + * software CMX. + * + * The CMX has special functions for conferences with one, two and more + * members. It will allow different types of data flow. Receive and transmit + * data to/form upper layer may be swithed on/off individually without loosing + * features of CMX, Tones and DTMF. + * + * Echo Cancellation: Sometimes we like to cancel echo from the interface. + * Note that a VoIP call may not have echo caused by the IP phone. The echo + * is generated by the telephone line connected to it. Because the delay + * is high, it becomes an echo. RESULT: Echo Cachelation is required if + * both echo AND delay is applied to an interface. + * Remember that software CMX always generates a more or less delay. + * + * If all used features can be realized in hardware, and if transmit and/or + * receive data ist disabled, the card may not send/receive any data at all. + * Not receiving is usefull if only announcements are played. Not sending is + * usefull if an answering machine records audio. Not sending and receiving is + * usefull during most states of the call. If supported by hardware, tones + * will be played without cpu load. Small PBXs and NT-Mode applications will + * not need expensive hardware when processing calls. + * + * + * LOCKING: + * + * When data is received from upper or lower layer (card), the complete dsp + * module is locked by a global lock. When data is ready to be transmitted + * to a different layer, the module is unlocked. It is not allowed to hold a + * lock outside own layer. + * Reasons: Multiple threads must not process cmx at the same time, if threads + * serve instances, that are connected in same conference. + * PH_CONTROL must not change any settings, join or split conference members + * during process of data. + * + * + * TRANSMISSION: + * + +TBD + +There are three things that need to receive data from card: + - software DTMF decoder + - software cmx (if conference exists) + - upper layer, if rx-data not disabled + +Whenever dtmf decoder is turned on or off, software cmx changes, rx-data is disabled or enabled, or card becomes activated, then rx-data is disabled or enabled using a special command to the card. + +There are three things that need to transmit data to card: + - software tone generation (part of cmx) + - software cmx + - upper layer, if tx-data is written to tx-buffer + + + + + */ + +const char *dsp_revision = "$Revision: 1.29 $"; + +#include +#include +#include +#include +#include "core.h" +#include "layer1.h" +#include "helper.h" +#include "debug.h" +#include "dsp.h" + +static char DSPName[] = "DSP"; +mISDNobject_t dsp_obj; + +int ec_debug; + +static int debug = 0; +int dsp_debug; +static int options = 0; +int dsp_options; +static int poll = 0; +int dsp_poll, dsp_tics; + +int dtmfthreshold=100L; + +#ifdef MODULE +MODULE_AUTHOR("Andreas Eversberg"); +#ifdef OLD_MODULE_PARAM +MODULE_PARM(debug, "1i"); +MODULE_PARM(options, "1i"); +MODULE_PARM(poll, "1i"); +MODULE_PARM(dtmfthreshold, "1i"); +#else +module_param(debug, uint, S_IRUGO | S_IWUSR); +module_param(options, uint, S_IRUGO | S_IWUSR); +module_param(poll, uint, S_IRUGO | S_IWUSR); +module_param(dtmfthreshold, uint, S_IRUGO | S_IWUSR); +#endif +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +#endif + + +/* + * special message process for DL_CONTROL | REQUEST + */ +static int +dsp_control_req(dsp_t *dsp, mISDN_head_t *hh, struct sk_buff *skb) +{ + struct sk_buff *nskb; + int ret = 0; + int cont; + u8 *data; + int len; + + if (skb->len < sizeof(int)) { + printk(KERN_ERR "%s: PH_CONTROL message too short\n", __FUNCTION__); + } + cont = *((int *)skb->data); + len = skb->len - sizeof(int); + data = skb->data + sizeof(int); + + switch (cont) { + case DTMF_TONE_START: /* turn on DTMF */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: start dtmf\n", __FUNCTION__); +#if 0 + if (len == sizeof(int)) { + printk(KERN_NOTICE "changing DTMF Threshold to %d\n",*((int*)data)); + dsp->dtmf.treshold=(*(int*)data)*10000; + } +#endif + + dsp_dtmf_goertzel_init(dsp); + /* checking for hardware capability */ + if (dsp->features.hfc_dtmf) { + dsp->dtmf.hardware = 1; + dsp->dtmf.software = 0; + } else { + dsp->dtmf.hardware = 0; + dsp->dtmf.software = 1; + } + break; + case DTMF_TONE_STOP: /* turn off DTMF */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: stop dtmf\n", __FUNCTION__); + dsp->dtmf.hardware = 0; + dsp->dtmf.software = 0; + break; + case CMX_CONF_JOIN: /* join / update conference */ + if (len != sizeof(int)) { + ret = -EINVAL; + break; + } + if (*((u32 *)data) == 0) + goto conf_split; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: join conference %d\n", __FUNCTION__, *((u32 *)data)); + ret = dsp_cmx_conf(dsp, *((u32 *)data)); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case CMX_CONF_SPLIT: /* remove from conference */ + conf_split: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: release conference\n", __FUNCTION__); + ret = dsp_cmx_conf(dsp, 0); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case TONE_PATT_ON: /* play tone */ + if (len != sizeof(int)) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn tone 0x%x on\n", __FUNCTION__, *((int *)skb->data)); + ret = dsp_tone(dsp, *((int *)data)); + if (!ret) + dsp_cmx_hardware(dsp->conf, dsp); + if (!dsp->tone.tone) + goto tone_off; + break; + case TONE_PATT_OFF: /* stop tone */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn tone off\n", __FUNCTION__); + dsp_tone(dsp, 0); + dsp_cmx_hardware(dsp->conf, dsp); + /* reset tx buffers (user space data) */ + tone_off: + dsp->tx_R = dsp->tx_W = 0; + break; + case VOL_CHANGE_TX: /* change volume */ + if (len != sizeof(int)) { + ret = -EINVAL; + break; + } + dsp->tx_volume = *((int *)data); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: change tx volume to %d\n", __FUNCTION__, dsp->tx_volume); + dsp_cmx_hardware(dsp->conf, dsp); + break; + case VOL_CHANGE_RX: /* change volume */ + if (len != sizeof(int)) { + ret = -EINVAL; + break; + } + dsp->rx_volume = *((int *)data); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: change rx volume to %d\n", __FUNCTION__, dsp->tx_volume); + dsp_cmx_hardware(dsp->conf, dsp); + break; + case CMX_ECHO_ON: /* enable echo */ + dsp->echo = 1; /* soft echo */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: enable cmx-echo\n", __FUNCTION__); + dsp_cmx_hardware(dsp->conf, dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case CMX_ECHO_OFF: /* disable echo */ + dsp->echo = 0; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: disable cmx-echo\n", __FUNCTION__); + dsp_cmx_hardware(dsp->conf, dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case CMX_RECEIVE_ON: /* enable receive to user space */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: enable receive to user space\n", __FUNCTION__); + dsp->rx_disabled = 0; + dsp_cmx_hardware(dsp->conf, dsp); + break; + case CMX_RECEIVE_OFF: /* disable receive to user space */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: disable receive to user space\n", __FUNCTION__); + dsp->rx_disabled = 1; + dsp_cmx_hardware(dsp->conf, dsp); + break; + case CMX_MIX_ON: /* enable mixing of transmit data with conference members */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: enable mixing of tx-data with conf mebers\n", __FUNCTION__); + dsp->tx_mix = 1; + dsp_cmx_hardware(dsp->conf, dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case CMX_MIX_OFF: /* disable mixing of transmit data with conference members */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: disable mixing of tx-data with conf mebers\n", __FUNCTION__); + dsp->tx_mix = 0; + dsp_cmx_hardware(dsp->conf, dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case ECHOCAN_ON: /* turn echo calcellation on */ + if (len<4) { + ret = -EINVAL; + } else { + int ec_arr[2]; + memcpy(&ec_arr,data,sizeof(ec_arr)); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn echo cancelation on (delay=%d attenuation-shift=%d\n", + __FUNCTION__, ec_arr[0], ec_arr[1]); + + ret = dsp_cancel_init(dsp, ec_arr[0], ec_arr[1] ,1); + dsp_cmx_hardware(dsp->conf, dsp); + } + break; + case ECHOCAN_OFF: /* turn echo calcellation off */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn echo cancelation off\n", __FUNCTION__); + + ret = dsp_cancel_init(dsp, 0,0,-1); + dsp_cmx_hardware(dsp->conf, dsp); + break; + case BF_ENABLE_KEY: /* turn blowfish on */ + if (len<4 || len>56) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn blowfish on (key not shown)\n", __FUNCTION__); + ret = dsp_bf_init(dsp, (u8*)data, len); + /* set new cont */ + if (!ret) + cont = BF_ACCEPT; + else + cont = BF_REJECT; + /* send indication if it worked to set it */ + nskb = create_link_skb(PH_CONTROL | INDICATION, 0, sizeof(int), &cont, 0); + if (mISDN_queue_up(&dsp->inst, 0, nskb)) + dev_kfree_skb(nskb); + if (!ret) + dsp_cmx_hardware(dsp->conf, dsp); + break; + case BF_DISABLE: /* turn blowfish off */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn blowfish off\n", __FUNCTION__); + dsp_bf_cleanup(dsp); + dsp_cmx_hardware(dsp->conf, dsp); + break; + default: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: ctrl req %x unhandled\n", __FUNCTION__, cont); + ret = -EINVAL; + } + if (!ret) + dev_kfree_skb(skb); + return(ret); +} + + +/* + * messages from upper layers + */ +static int +dsp_from_up(mISDNinstance_t *inst, struct sk_buff *skb) +{ + dsp_t *dsp; + mISDN_head_t *hh; + int ret = 0; + u_long flags; + + if (!skb) + return(-EINVAL); + dsp = inst->privat; + if (!dsp) { + return(-EIO); + } + + hh = mISDN_HEAD_P(skb); + switch(hh->prim) { + case DL_DATA | RESPONSE: + case PH_DATA | RESPONSE: + /* ignore response */ + dev_kfree_skb(skb); + break; + case DL_DATA | REQUEST: + case PH_DATA | REQUEST: + if (skb->len < 1) + return(-EINVAL); + + if (!dsp->conf_id) { + /* PROCESS TONES/TX-DATA ONLY */ + if (dsp->tone.tone) { + /* -> copy tone */ + dsp_tone_copy(dsp, skb->data, skb->len); + } + + if (dsp->tx_volume) + dsp_change_volume(skb, dsp->tx_volume); + /* cancel echo */ + if (dsp->cancel_enable && ec_debug) + dsp_cancel_tx(dsp, skb->data, skb->len); + /* crypt */ + if (dsp->bf_enable) + dsp_bf_encrypt(dsp, skb->data, skb->len); + /* send packet */ + if (mISDN_queue_down(&dsp->inst, 0, skb)) { + dev_kfree_skb(skb); + printk(KERN_ERR "%s: failed to send tx-packet\n", __FUNCTION__); + + return (-EIO); + } + + } else { + if (dsp->features.pcm_id>=0) { + printk("Not sending Data to CMX -- > returning because of HW bridge\n"); + dev_kfree_skb(skb); + break; + } + /* send data to tx-buffer (if no tone is played) */ + spin_lock_irqsave(&dsp_obj.lock, flags); + if (!dsp->tone.tone) { + dsp_cmx_transmit(dsp, skb); + } + spin_unlock_irqrestore(&dsp_obj.lock, flags); + + dev_kfree_skb(skb); + } + break; + case PH_CONTROL | REQUEST: + + spin_lock_irqsave(&dsp_obj.lock, flags); + ret = dsp_control_req(dsp, hh, skb); + spin_unlock_irqrestore(&dsp_obj.lock, flags); + + break; + case DL_ESTABLISH | REQUEST: + case PH_ACTIVATE | REQUEST: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: activating b_channel %s\n", __FUNCTION__, dsp->inst.name); + + if (dsp->dtmf.hardware || dsp->dtmf.software) + dsp_dtmf_goertzel_init(dsp); + hh->prim = PH_ACTIVATE | REQUEST; + ret = mISDN_queue_down(&dsp->inst, 0, skb); + + break; + case DL_RELEASE | REQUEST: + case PH_DEACTIVATE | REQUEST: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: releasing b_channel %s\n", __FUNCTION__, dsp->inst.name); + + dsp->tone.tone = dsp->tone.hardware = dsp->tone.software = 0; + if (timer_pending(&dsp->tone.tl)) + del_timer(&dsp->tone.tl); + hh->prim = PH_DEACTIVATE | REQUEST; + ret = mISDN_queue_down(&dsp->inst, 0, skb); + + break; + default: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: msg %x unhandled %s\n", __FUNCTION__, hh->prim, dsp->inst.name); + ret = -EINVAL; + break; + } + return(ret); +} + + +/* + * messages from lower layers + */ +static int +dsp_from_down(mISDNinstance_t *inst, struct sk_buff *skb) +{ + dsp_t *dsp; + mISDN_head_t *hh; + int ret = 0; + u8 *digits; + int cont; + struct sk_buff *nskb; + u_long flags; + + if (!skb) + return(-EINVAL); + dsp = inst->privat; + if (!dsp) + return(-EIO); + + hh = mISDN_HEAD_P(skb); + switch(hh->prim) + { + case PH_DATA | CONFIRM: + case DL_DATA | CONFIRM: + /* flush response, because no relation to upper layer */ + dev_kfree_skb(skb); + break; + case PH_DATA | INDICATION: + case DL_DATA | INDICATION: + if (skb->len < 1) + return(-EINVAL); + + + + /* decrypt if enabled */ + if (dsp->bf_enable) + dsp_bf_decrypt(dsp, skb->data, skb->len); + /* if echo cancellation is enabled */ + if (dsp->cancel_enable && ec_debug) + dsp_cancel_rx(dsp, skb->data, skb->len); + /* check if dtmf soft decoding is turned on */ + if (dsp->dtmf.software) { + digits = dsp_dtmf_goertzel_decode(dsp, skb->data, skb->len, (dsp_options&DSP_OPT_ULAW)?1:0); + if (digits) while(*digits) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s: sending software decoded digit(%c) to upper layer %s\n", __FUNCTION__, *digits, dsp->inst.name); + cont = DTMF_TONE_VAL | *digits; + nskb = create_link_skb(PH_CONTROL | INDICATION, 0, sizeof(int), &cont, 0); + if (mISDN_queue_up(&dsp->inst, 0, nskb)) + dev_kfree_skb(nskb); + digits++; + } + } + /* change volume if requested */ + if (dsp->rx_volume) + dsp_change_volume(skb, dsp->rx_volume); + + if (dsp->conf_id) { + /* we need to process receive data if software */ + spin_lock_irqsave(&dsp_obj.lock, flags); + if (dsp->pcm_slot_tx<0 && dsp->pcm_slot_rx<0) { + /* process data from card at cmx */ + dsp_cmx_receive(dsp, skb); + } + spin_unlock_irqrestore(&dsp_obj.lock, flags); + } + + if (dsp->rx_disabled) { + /* if receive is not allowed */ + dev_kfree_skb(skb); + + break; + } + hh->prim = DL_DATA | INDICATION; + ret = mISDN_queue_up(&dsp->inst, 0, skb); + + break; + case PH_CONTROL | INDICATION: + + if (dsp_debug & DEBUG_DSP_DTMFCOEFF) + printk(KERN_DEBUG "%s: PH_CONTROL received: %x (len %d) %s\n", __FUNCTION__, hh->dinfo, skb->len, dsp->inst.name); + switch (hh->dinfo) { + case HW_HFC_COEFF: /* getting coefficients */ + if (!dsp->dtmf.hardware) { + if (dsp_debug & DEBUG_DSP_DTMFCOEFF) + printk(KERN_DEBUG "%s: ignoring DTMF coefficients from HFC\n", __FUNCTION__); + dev_kfree_skb(skb); + break; + } + digits = dsp_dtmf_goertzel_decode(dsp, skb->data, skb->len, 2); + if (digits) while(*digits) { + int k; + struct sk_buff *nskb; + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s: now sending software decoded digit(%c) to upper layer %s\n", __FUNCTION__, *digits, dsp->inst.name); + k = *digits | DTMF_TONE_VAL; + nskb = create_link_skb(PH_CONTROL | INDICATION, 0, sizeof(int), &k, 0); + if (mISDN_queue_up(&dsp->inst, 0, nskb)) + dev_kfree_skb(nskb); + digits++; + } + dev_kfree_skb(skb); + break; + + case VOL_CHANGE_TX: /* change volume */ + if (skb->len != sizeof(int)) { + ret = -EINVAL; + break; + } + dsp->tx_volume = *((int *)skb->data); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: change tx volume to %d\n", __FUNCTION__, dsp->tx_volume); + printk(KERN_DEBUG "%s: change tx volume to %d\n", __FUNCTION__, dsp->tx_volume); + dsp_cmx_hardware(dsp->conf, dsp); + break; + default: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: ctrl ind %x unhandled %s\n", __FUNCTION__, hh->dinfo, dsp->inst.name); + ret = -EINVAL; + } + + break; + case PH_ACTIVATE | CONFIRM: + + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: b_channel is now active %s\n", __FUNCTION__, dsp->inst.name); + /* bchannel now active */ + spin_lock_irqsave(&dsp_obj.lock, flags); + dsp->b_active = 1; + dsp->tx_W = dsp->tx_R = 0; /* clear TX buffer */ + dsp->rx_W = dsp->rx_R = -1; /* reset RX buffer */ + memset(dsp->rx_buff, 0, sizeof(dsp->rx_buff)); + dsp_cmx_hardware(dsp->conf, dsp); + spin_unlock_irqrestore(&dsp_obj.lock, flags); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: done with activation, sending confirm to user space. %s\n", __FUNCTION__, dsp->inst.name); + /* send activation to upper layer */ + hh->prim = DL_ESTABLISH | CONFIRM; + ret = mISDN_queue_up(&dsp->inst, 0, skb); + + break; + case PH_DEACTIVATE | CONFIRM: + + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: b_channel is now inactive %s\n", __FUNCTION__, dsp->inst.name); + /* bchannel now inactive */ + spin_lock_irqsave(&dsp_obj.lock, flags); + dsp->b_active = 0; + dsp_cmx_hardware(dsp->conf, dsp); + spin_unlock_irqrestore(&dsp_obj.lock, flags); + hh->prim = DL_RELEASE | CONFIRM; + ret = mISDN_queue_up(&dsp->inst, 0, skb); + + break; + default: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: msg %x unhandled %s\n", __FUNCTION__, hh->prim, dsp->inst.name); + ret = -EINVAL; + } + return(ret); +} + + +/* + * messages from queue + */ +static int +dsp_function(mISDNinstance_t *inst, struct sk_buff *skb) +{ + mISDN_head_t *hh; + int ret = -EINVAL; + + hh = mISDN_HEAD_P(skb); + switch (hh->addr & MSG_DIR_MASK) { + case FLG_MSG_DOWN: + ret = dsp_from_up(inst, skb); + break; + case FLG_MSG_UP: + ret = dsp_from_down(inst, skb); + break; + } + + return(ret); +} + + +/* + * desroy DSP instances + */ +static void +release_dsp(dsp_t *dsp) +{ + mISDNinstance_t *inst = &dsp->inst; + conference_t *conf; + u_long flags; + + spin_lock_irqsave(&dsp_obj.lock, flags); + if (timer_pending(&dsp->feature_tl)) + del_timer(&dsp->feature_tl); + if (timer_pending(&dsp->tone.tl)) + del_timer(&dsp->tone.tl); + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: removing conferences %s\n", __FUNCTION__, dsp->inst.name); + conf = dsp->conf; + if (conf) { + dsp_cmx_del_conf_member(dsp); + if (!list_empty(&conf->mlist)) { + dsp_cmx_del_conf(conf); + } + } + + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: remove & destroy object %s\n", __FUNCTION__, dsp->inst.name); + list_del(&dsp->list); + spin_unlock_irqrestore(&dsp_obj.lock, flags); + mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + vfree(dsp); + + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: dsp instance released\n", __FUNCTION__); +} + + +/* + * ask for hardware features + */ +static void +dsp_feat(void *arg) +{ + dsp_t *dsp = arg; + struct sk_buff *nskb; + void *feat; + + + switch (dsp->feature_state) { + case FEAT_STATE_INIT: + feat = &dsp->features; + nskb = create_link_skb(PH_CONTROL | REQUEST, HW_FEATURES, sizeof(feat), &feat, 0); + if (!nskb) + break; + if (mISDN_queue_down(&dsp->inst, 0, nskb)) { + dev_kfree_skb(nskb); + break; + } + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: features will be quered now for instance %s\n", __FUNCTION__, dsp->inst.name); + spin_lock(&dsp->feature_lock); + dsp->feature_state = FEAT_STATE_WAIT; + spin_unlock(&dsp->feature_lock); + init_timer(&dsp->feature_tl); + dsp->feature_tl.expires = jiffies + (HZ / 100); + add_timer(&dsp->feature_tl); + break; + case FEAT_STATE_WAIT: + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: features of %s are: hfc_id=%d hfc_dtmf=%d hfc_loops=%d hfc_echocanhw:%d pcm_id=%d pcm_slots=%d pcm_banks=%d\n", + __FUNCTION__, dsp->inst.name, + dsp->features.hfc_id, + dsp->features.hfc_dtmf, + dsp->features.hfc_loops, + dsp->features.hfc_echocanhw, + dsp->features.pcm_id, + dsp->features.pcm_slots, + dsp->features.pcm_banks); + + spin_lock(&dsp->feature_lock); + dsp->feature_state = FEAT_STATE_RECEIVED; + spin_unlock(&dsp->feature_lock); + + if (dsp->queue_conf_id) { + /*work on queued conf id*/ + dsp_cmx_conf(dsp, dsp->queue_conf_id ); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + } + + if (dsp->queue_cancel[2]) { + dsp_cancel_init(dsp, + dsp->queue_cancel[0], + dsp->queue_cancel[1], + dsp->queue_cancel[2] + ); + + } + break; + } + +} + + +/* + * create new DSP instances + */ +static int +new_dsp(mISDNstack_t *st, mISDN_pid_t *pid) +{ + int err = 0; + dsp_t *ndsp; + u_long flags; + + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: creating new dsp instance\n", __FUNCTION__); + + if (!st || !pid) + return(-EINVAL); + if (!(ndsp = vmalloc(sizeof(dsp_t)))) { + printk(KERN_ERR "%s: vmalloc dsp_t failed\n", __FUNCTION__); + return(-ENOMEM); + } + memset(ndsp, 0, sizeof(dsp_t)); + memcpy(&ndsp->inst.pid, pid, sizeof(mISDN_pid_t)); + mISDN_init_instance(&ndsp->inst, &dsp_obj, ndsp, dsp_function); + if (!mISDN_SetHandledPID(&dsp_obj, &ndsp->inst.pid)) { + int_error(); + err = -ENOPROTOOPT; + free_mem: + vfree(ndsp); + return(err); + } + sprintf(ndsp->inst.name, "DSP_S%x/C%x", + (st->id&0xff00)>>8, (st->id&0xff0000)>>16); + /* set frame size to start */ + ndsp->features.hfc_id = -1; /* current PCM id */ + ndsp->features.pcm_id = -1; /* current PCM id */ + ndsp->pcm_slot_rx = -1; /* current CPM slot */ + ndsp->pcm_slot_tx = -1; + ndsp->pcm_bank_rx = -1; + ndsp->pcm_bank_tx = -1; + ndsp->hfc_conf = -1; /* current conference number */ + /* set tone timer */ + ndsp->tone.tl.function = (void *)dsp_tone_timeout; + ndsp->tone.tl.data = (long) ndsp; + init_timer(&ndsp->tone.tl); + /* set dsp feture timer */ + ndsp->feature_tl.function = (void *)dsp_feat; + ndsp->feature_tl.data = (long) ndsp; + ndsp->feature_state = FEAT_STATE_INIT; + + if (dtmfthreshold < 20 || dtmfthreshold> 500) { + dtmfthreshold=200; + } + ndsp->dtmf.treshold=dtmfthreshold*10000; + + spin_lock_init(&ndsp->feature_lock); + init_timer(&ndsp->feature_tl); + if (!(dsp_options & DSP_OPT_NOHARDWARE)) { + ndsp->feature_tl.expires = jiffies + (HZ / 100); + add_timer(&ndsp->feature_tl); + } + spin_lock_irqsave(&dsp_obj.lock, flags); + /* append and register */ + list_add_tail(&ndsp->list, &dsp_obj.ilist); + spin_unlock_irqrestore(&dsp_obj.lock, flags); + err = mISDN_ctrl(st, MGR_REGLAYER | INDICATION, &ndsp->inst); + if (err) { + printk(KERN_ERR "%s: failed to register layer %s\n", __FUNCTION__, ndsp->inst.name); + spin_lock_irqsave(&dsp_obj.lock, flags); + list_del(&ndsp->list); + spin_unlock_irqrestore(&dsp_obj.lock, flags); + goto free_mem; + } + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: dsp instance created %s\n", __FUNCTION__, ndsp->inst.name); + return(err); +} + + +/* + * manager for DSP instances + */ +static int +dsp_manager(void *data, u_int prim, void *arg) { + mISDNinstance_t *inst = data; + dsp_t *dspl; + int ret = -EINVAL; + u_long flags; + + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: data:%p prim:%x arg:%p\n", __FUNCTION__, data, prim, arg); + if (!data) + return(ret); + spin_lock_irqsave(&dsp_obj.lock, flags); + list_for_each_entry(dspl, &dsp_obj.ilist, list) { + if (&dspl->inst == inst) { + ret = 0; + break; + } + } + spin_unlock_irqrestore(&dsp_obj.lock, flags); + if (ret && (prim != (MGR_NEWLAYER | REQUEST))) { + printk(KERN_WARNING "%s: given instance(%p) not in ilist.\n", __FUNCTION__, data); + return(ret); + } + + switch(prim) { + case MGR_NEWLAYER | REQUEST: + ret = new_dsp(data, arg); + break; + case MGR_SETSTACK | INDICATION: + break; +#ifdef OBSOLETE + case MGR_CONNECT | REQUEST: + ret = mISDN_ConnectIF(inst, arg); + break; + case MGR_SETIF | REQUEST: + case MGR_SETIF | INDICATION: + ret = mISDN_SetIF(inst, arg, prim, dsp_from_up, dsp_from_down, dspl); + break; + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + ret = mISDN_DisConnectIF(inst, arg); + break; +#endif + case MGR_UNREGLAYER | REQUEST: + case MGR_RELEASE | INDICATION: + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: release_dsp id %x\n", __FUNCTION__, dspl->inst.st->id); + + release_dsp(dspl); + break; + default: + printk(KERN_WARNING "%s: prim %x not handled\n", __FUNCTION__, prim); + ret = -EINVAL; + break; + } + return(ret); +} + +static int proc_read_debug(char *buf, char **start, off_t offset, + int count, int *eof, void *data) +{ + int len; + + *eof = 1; + + len = sprintf(buf, "%d\n", ec_debug); + return len; +} + + +static int proc_write_debug(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + + char *endbuffer; + + ec_debug = simple_strtol (buffer, &endbuffer, 10); + return count; +} + +/* + * initialize DSP object + */ +static int dsp_init(void) +{ + int err; + + /* copy variables */ + dsp_options = options; + dsp_debug = debug; + + /* display revision */ + printk(KERN_INFO "mISDN_dsp: Audio DSP Rev. %s (debug=0x%x) EchoCancellor %s dtmfthreshold(%d)\n", mISDN_getrev(dsp_revision), debug, EC_TYPE, dtmfthreshold); + + /* set packet size */ + if (poll == 0) { + if (HZ == 100) + poll = 80; + else + poll = 64; + } + + if (poll > MAX_POLL) { + printk(KERN_ERR "%s: Wrong poll value (%d), using %d.\n", __FUNCTION__, poll, MAX_POLL); + poll = MAX_POLL; + } + if (poll < 8) { + printk(KERN_ERR "%s: Wrong poll value (%d), using 8.\n", __FUNCTION__, poll); + poll = 8; + } + dsp_poll = poll; + dsp_tics = poll * HZ / 8000; + if (dsp_tics * 8000 == poll * HZ) + printk(KERN_INFO "mISDN_dsp: DSP clocks every %d samples. This equals %d jiffies.\n", poll, dsp_tics); + else { + printk(KERN_INFO "mISDN_dsp: Cannot clock ever %d samples. Use a multiple of %d (samples)\n", poll, 8000 / HZ); + err = -EINVAL; + return(err); + } + + /* fill mISDN object (dsp_obj) */ + memset(&dsp_obj, 0, sizeof(dsp_obj)); +#ifdef MODULE + SET_MODULE_OWNER(&dsp_obj); +#endif + spin_lock_init(&dsp_obj.lock); + dsp_obj.name = DSPName; + dsp_obj.BPROTO.protocol[3] = ISDN_PID_L3_B_DSP; + dsp_obj.own_ctrl = dsp_manager; + INIT_LIST_HEAD(&dsp_obj.ilist); + + /* initialize audio tables */ + dsp_silence = (dsp_options&DSP_OPT_ULAW)?0xff:0x2a; + dsp_audio_law_to_s32 = (dsp_options&DSP_OPT_ULAW)?dsp_audio_ulaw_to_s32:dsp_audio_alaw_to_s32; + dsp_audio_generate_s2law_table(); + dsp_audio_generate_seven(); + dsp_audio_generate_mix_table(); + if (dsp_options & DSP_OPT_ULAW) + dsp_audio_generate_ulaw_samples(); + dsp_audio_generate_volume_changes(); + + /* register object */ + if ((err = mISDN_register(&dsp_obj))) { + printk(KERN_ERR "mISDN_dsp: Can't register %s error(%d)\n", DSPName, err); + return(err); + } + + /* set sample timer */ + dsp_spl_tl.function = (void *)dsp_cmx_send; + dsp_spl_tl.data = 0; + init_timer(&dsp_spl_tl); + dsp_spl_tl.expires = jiffies + dsp_tics + 1; /* safer */ + dsp_spl_jiffies = dsp_spl_tl.expires; + add_timer(&dsp_spl_tl); + + mISDN_module_register(THIS_MODULE); + + /* proc interface for debugiing echo can noise */ + + { + struct proc_dir_entry *proc_misdn, *proc_debug; + + printk("dsp_core.c: Creating /proc/misdn debug interface\n"); + + proc_misdn = proc_mkdir("misdn", 0); + proc_debug = create_proc_read_entry("misdn/debug", 0, NULL, proc_read_debug, NULL); + proc_debug->write_proc = proc_write_debug; + } + + return(0); +} + + +/* + * cleanup DSP object during module removal + */ +static void dsp_cleanup(void) +{ + dsp_t *dspl, *nd; + int err; + + { + remove_proc_entry("misdn/debug", NULL); + remove_proc_entry("misdn", NULL); + } + + mISDN_module_unregister(THIS_MODULE); + + if (timer_pending(&dsp_spl_tl)) + del_timer(&dsp_spl_tl); + + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: removing module\n", __FUNCTION__); + + if ((err = mISDN_unregister(&dsp_obj))) { + printk(KERN_ERR "mISDN_dsp: Can't unregister Audio DSP error(%d)\n", + err); + } + if (!list_empty(&dsp_obj.ilist)) { + printk(KERN_WARNING "mISDN_dsp: Audio DSP object inst list not empty.\n"); + list_for_each_entry_safe(dspl, nd, &dsp_obj.ilist, list) + release_dsp(dspl); + } + if (!list_empty(&Conf_list)) { + printk(KERN_ERR "mISDN_dsp: Conference list not empty. Not all memory freed.\n"); + } +} + +#ifdef MODULE +module_init(dsp_init); +module_exit(dsp_cleanup); +#endif + diff -ruN mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/dsp.h mISDN-1_1_5/drivers/isdn/hardware/mISDN/dsp.h --- mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/dsp.h 2007-07-02 19:01:34.000000000 +0930 +++ mISDN-1_1_5/drivers/isdn/hardware/mISDN/dsp.h 2007-09-15 19:54:02.000000000 +0930 @@ -50,8 +50,12 @@ //#include "dsp_mec2.h" //#include "dsp_kb1ec.h" -#include "dsp_mg2ec.h" - +// #include "dsp_mg2ec.h" +#include "oslec.h" +#define echo_can_create oslec_echo_can_create +#define echo_can_free oslec_echo_can_free +#define echo_can_update oslec_echo_can_update +#define echo_can_traintap oslec_echo_can_traintap /* @@ -76,6 +80,7 @@ extern u8 dsp_audio_mix_law[65536]; extern u8 dsp_audio_seven2law[128]; extern u8 dsp_audio_law2seven[256]; +extern void dsp_audio_generate_law_tables(void); extern void dsp_audio_generate_s2law_table(void); extern void dsp_audio_generate_seven(void); extern void dsp_audio_generate_mix_table(void); diff -ruN mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/dsp.h.orig mISDN-1_1_5/drivers/isdn/hardware/mISDN/dsp.h.orig --- mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/dsp.h.orig 1970-01-01 09:30:00.000000000 +0930 +++ mISDN-1_1_5/drivers/isdn/hardware/mISDN/dsp.h.orig 2007-09-13 08:46:20.000000000 +0930 @@ -0,0 +1,301 @@ +/* $Id: dsp.h,v 1.13 2007/03/27 15:06:29 jolly Exp $ + * + * Audio support data for ISDN4Linux. + * + * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#define DEBUG_DSP_MGR 0x0001 +#define DEBUG_DSP_CORE 0x0002 +#define DEBUG_DSP_DTMF 0x0004 +#define DEBUG_DSP_DTMFCOEFF 0x0008 +#define DEBUG_DSP_CMX 0x0010 +#define DEBUG_DSP_TONE 0x0020 +#define DEBUG_DSP_BLOWFISH 0x0040 +#define DEBUG_DSP_DELAY 0x0080 + +/* options may be: + * + * bit 0 = use ulaw instead of alaw + * bit 1 = enable hfc hardware accelleration for all channels + * + */ +#define DSP_OPT_ULAW (1<<0) +#define DSP_OPT_NOHARDWARE (1<<1) + +#define FEAT_STATE_INIT 1 +#define FEAT_STATE_WAIT 2 +#define FEAT_STATE_RECEIVED 3 + +#include + +#ifdef MISDN_MEMDEBUG +#include "memdbg.h" +#endif + +#include "dsp_ecdis.h" + +/* + * You are now able to choose between the Mark2 and the + * kb1 Echo cancellor. Just comment the one and comment + * out the other. + */ + + +//#define AGGRESSIVE_SUPPRESSOR + +//#include "dsp_mec2.h" +//#include "dsp_kb1ec.h" +// #include "dsp_mg2ec.h" +#include "oslec.h" +#define echo_can_create oslec_echo_can_create +#define echo_can_free oslec_echo_can_free +#define echo_can_update oslec_echo_can_update +#define echo_can_traintap oslec_echo_can_traintap + + +/* +* uncomment this one to cancel echo more aggressive +*/ +//#define AGGRESSIVE_SUPPRESSOR + +extern int dsp_options; +extern int dsp_debug; +extern int dsp_poll; +extern int dsp_tics; + +/*************** + * audio stuff * + ***************/ + +extern s32 dsp_audio_alaw_to_s32[256]; +extern s32 dsp_audio_ulaw_to_s32[256]; +extern s32 *dsp_audio_law_to_s32; +extern u8 dsp_audio_s16_to_law[65536]; +extern u8 dsp_audio_alaw_to_ulaw[256]; +extern u8 dsp_audio_mix_law[65536]; +extern u8 dsp_audio_seven2law[128]; +extern u8 dsp_audio_law2seven[256]; +extern void dsp_audio_generate_s2law_table(void); +extern void dsp_audio_generate_seven(void); +extern void dsp_audio_generate_mix_table(void); +extern void dsp_audio_generate_ulaw_samples(void); +extern void dsp_audio_generate_volume_changes(void); +extern u8 dsp_silence; + + +/************* + * cmx stuff * + *************/ + +#define MAX_POLL 256 /* maximum number of send-chunks */ + +#define CMX_BUFF_SIZE 0x8000 /* must be 2**n (0x1000 about 1/2 second) */ +#define CMX_BUFF_HALF 0x4000 /* CMX_BUFF_SIZE / 2 */ +#define CMX_BUFF_MASK 0x7fff /* CMX_BUFF_SIZE - 1 */ + +/* how many seconds will we check the lowest delay until the jitter buffer + is reduced by that delay */ +#define MAX_SECONDS_JITTER_CHECK 5 + +extern struct timer_list dsp_spl_tl; +extern u64 dsp_spl_jiffies; + +/* the structure of conferences: + * + * each conference has a unique number, given by user space. + * the conferences are linked in a chain. + * each conference has members linked in a chain. + * each dsplayer points to a member, each member points to a dsplayer. + */ + +/* all members within a conference (this is linked 1:1 with the dsp) */ +struct _dsp; +typedef struct _conf_member { + struct list_head list; + struct _dsp *dsp; +} conf_member_t; + +/* the list of all conferences */ +typedef struct _conference { + struct list_head list; + u32 id; /* all cmx stacks with the same ID are connected */ + struct list_head mlist; + int software; /* conf is processed by software */ + int hardware; /* conf is processed by hardware */ +} conference_t; + +extern mISDNobject_t dsp_obj; + + +/************** + * DTMF stuff * + **************/ + +#define DSP_DTMF_NPOINTS 102 + +#define ECHOCAN_BUFLEN 4*128 + +typedef struct _dtmf_t { + int treshold; /* above this is dtmf (square of) */ + int software; /* dtmf uses software decoding */ + int hardware; /* dtmf uses hardware decoding */ + int size; /* number of bytes in buffer */ + signed short buffer[DSP_DTMF_NPOINTS]; /* buffers one full dtmf frame */ + u8 lastwhat, lastdigit; + int count; + u8 digits[16]; /* just the dtmf result */ +} dtmf_t; + + +/**************** + * cancel stuff * + ****************/ + + + +/*************** + * tones stuff * + ***************/ + +typedef struct _tone_t { + int software; /* tones are generated by software */ + int hardware; /* tones are generated by hardware */ + int tone; + void *pattern; + int count; + int index; + struct timer_list tl; +} tone_t; + +/***************** + * general stuff * + *****************/ + +#define DELAY_CHECK 8000 + +struct dsp_features { + int hfc_id; /* unique id to identify the chip (or -1) */ + int hfc_dtmf; /* set if HFCmulti card supports dtmf */ + int hfc_loops; /* set if card supports tone loops */ + int hfc_echocanhw; /* set if card supports echocancelation*/ + int pcm_id; /* unique id to identify the pcm bus (or -1) */ + int pcm_slots; /* number of slots on the pcm bus */ + int pcm_banks; /* number of IO banks of pcm bus */ + int has_jitter; /* data is jittered and unsorted */ +}; + +typedef struct _dsp { + struct list_head list; + mISDNinstance_t inst; + int b_active; + int echo; /* echo is done by software */ + int rx_disabled; + int tx_mix; + tone_t tone; + dtmf_t dtmf; + int tx_volume, rx_volume; + + /* conference stuff */ + u32 conf_id; + conference_t *conf; + conf_member_t *member; + + /* while we're waiting for the hw */ + u32 queue_conf_id; + + /* buffer stuff */ + int rx_W; /* current write pos for data without timestamp */ + int rx_R; /* current read pos for transmit clock */ + int tx_W; /* current write pos for transmit data */ + int tx_R; /* current read pos for transmit clock */ + int delay[MAX_SECONDS_JITTER_CHECK]; + u8 tx_buff[CMX_BUFF_SIZE]; + u8 rx_buff[CMX_BUFF_SIZE]; + + /* hardware stuff */ + struct dsp_features features; /* features */ + struct timer_list feature_tl; + + spinlock_t feature_lock; + int feature_state; + int pcm_slot_rx; /* current PCM slot (or -1) */ + int pcm_bank_rx; + int pcm_slot_tx; + int pcm_bank_tx; + int hfc_conf; /* unique id of current conference (or -1) */ + + /* encryption stuff */ + int bf_enable; + u32 bf_p[18]; + u32 bf_s[1024]; + int bf_crypt_pos; + u8 bf_data_in[9]; + u8 bf_crypt_out[9]; + int bf_decrypt_in_pos; + int bf_decrypt_out_pos; + u8 bf_crypt_inring[16]; + u8 bf_data_out[9]; + int bf_sync; + + /* echo cancellation stuff */ + int queue_cancel[3]; + int cancel_enable; + int cancel_hardware; /*we are using hw echo canc*/ + struct echo_can_state * ec; /**< == NULL: echo cancellation disabled; + != NULL: echo cancellation enabled */ + + echo_can_disable_detector_state_t* ecdis_rd; + echo_can_disable_detector_state_t* ecdis_wr; + + uint16_t echotimer; + uint16_t echostate; + uint16_t echolastupdate; + + char txbuf[ECHOCAN_BUFLEN]; + int txbuflen; + + char rxbuf[ECHOCAN_BUFLEN]; + int rxbuflen; + +} dsp_t; + +/* functions */ + +extern void dsp_change_volume(struct sk_buff *skb, int volume); + +extern struct list_head Conf_list; +extern void dsp_cmx_debug(dsp_t *dsp); +extern void dsp_cmx_hardware(conference_t *conf, dsp_t *dsp); +extern int dsp_cmx_conf(dsp_t *dsp, u32 conf_id); +extern void dsp_cmx_receive(dsp_t *dsp, struct sk_buff *skb); +#ifdef OLDCMX +extern struct sk_buff *dsp_cmx_send(dsp_t *dsp, int len, int dinfo); +#else +extern void dsp_cmx_send(void *data); +#endif +extern void dsp_cmx_transmit(dsp_t *dsp, struct sk_buff *skb); +extern int dsp_cmx_del_conf_member(dsp_t *dsp); +extern int dsp_cmx_del_conf(conference_t *conf); + +extern void dsp_dtmf_goertzel_init(dsp_t *dsp); +extern u8 *dsp_dtmf_goertzel_decode(dsp_t *dsp, u8 *data, int len, int fmt); + +extern int dsp_tone(dsp_t *dsp, int tone); +extern void dsp_tone_copy(dsp_t *dsp, u8 *data, int len); +extern void dsp_tone_timeout(void *arg); + +extern void dsp_bf_encrypt(dsp_t *dsp, u8 *data, int len); +extern void dsp_bf_decrypt(dsp_t *dsp, u8 *data, int len); +extern int dsp_bf_init(dsp_t *dsp, const u8 *key, unsigned int keylen); +extern void dsp_bf_cleanup(dsp_t *dsp); + +extern void dsp_cancel_tx(dsp_t *dsp, u8 *data, int len); +extern void dsp_cancel_rx(dsp_t *dsp, u8 *data, int len); +extern int dsp_cancel_init(dsp_t *dsp, int taps, int training, int delay); + + diff -ruN mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/Makefile mISDN-1_1_5/drivers/isdn/hardware/mISDN/Makefile --- mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/Makefile 2007-07-02 19:01:34.000000000 +0930 +++ mISDN-1_1_5/drivers/isdn/hardware/mISDN/Makefile 2007-09-16 01:31:48.000000000 +0930 @@ -110,7 +110,7 @@ asn1_basic_service.o asn1_address.o asn1_enc.o capi_enc.o \ supp_serv.o mISDN_dtmf-objs := dtmf.o -mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_cancel.o +mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_cancel.o oslec_wrap.o oslec_echo.o mISDN_loop-objs := loop.o mISDN_x25dte-objs := x25_dte.o x25_l3.o I4LmISDN-objs := i4l_mISDN.o diff -ruN mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/Makefile.v2.6 mISDN-1_1_5/drivers/isdn/hardware/mISDN/Makefile.v2.6 --- mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/Makefile.v2.6 2007-07-02 19:01:34.000000000 +0930 +++ mISDN-1_1_5/drivers/isdn/hardware/mISDN/Makefile.v2.6 2007-09-13 08:46:20.000000000 +0930 @@ -110,7 +110,7 @@ asn1_basic_service.o asn1_address.o asn1_enc.o capi_enc.o \ supp_serv.o mISDN_dtmf-objs := dtmf.o -mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_cancel.o +mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_cancel.o oslec_wrap.o oslec_echo.o mISDN_loop-objs := loop.o mISDN_x25dte-objs := x25_dte.o x25_l3.o I4LmISDN-objs := i4l_mISDN.o diff -ruN mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/oslec_bit_operations.h mISDN-1_1_5/drivers/isdn/hardware/mISDN/oslec_bit_operations.h --- mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/oslec_bit_operations.h 1970-01-01 09:30:00.000000000 +0930 +++ mISDN-1_1_5/drivers/isdn/hardware/mISDN/oslec_bit_operations.h 2007-09-13 08:46:20.000000000 +0930 @@ -0,0 +1,253 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * bit_operations.h - Various bit level operations, such as bit reversal + * + * Written by Steve Underwood + * + * Copyright (C) 2006 Steve Underwood + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: bit_operations.h,v 1.11 2006/11/28 15:37:03 steveu Exp $ + */ + +/*! \file */ + +#if !defined(_BIT_OPERATIONS_H_) +#define _BIT_OPERATIONS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__i386__) || defined(__x86_64__) +/*! \brief Find the bit position of the highest set bit in a word + \param bits The word to be searched + \return The bit number of the highest set bit, or -1 if the word is zero. */ +static __inline__ int top_bit(unsigned int bits) +{ + int res; + + __asm__ (" xorl %[res],%[res];\n" + " decl %[res];\n" + " bsrl %[bits],%[res]\n" + : [res] "=&r" (res) + : [bits] "rm" (bits)); + return res; +} +/*- End of function --------------------------------------------------------*/ + +/*! \brief Find the bit position of the lowest set bit in a word + \param bits The word to be searched + \return The bit number of the lowest set bit, or -1 if the word is zero. */ +static __inline__ int bottom_bit(unsigned int bits) +{ + int res; + + __asm__ (" xorl %[res],%[res];\n" + " decl %[res];\n" + " bsfl %[bits],%[res]\n" + : [res] "=&r" (res) + : [bits] "rm" (bits)); + return res; +} +/*- End of function --------------------------------------------------------*/ +#else +static __inline__ int top_bit(unsigned int bits) +{ + int i; + + if (bits == 0) + return -1; + i = 0; + if (bits & 0xFFFF0000) + { + bits &= 0xFFFF0000; + i += 16; + } + if (bits & 0xFF00FF00) + { + bits &= 0xFF00FF00; + i += 8; + } + if (bits & 0xF0F0F0F0) + { + bits &= 0xF0F0F0F0; + i += 4; + } + if (bits & 0xCCCCCCCC) + { + bits &= 0xCCCCCCCC; + i += 2; + } + if (bits & 0xAAAAAAAA) + { + bits &= 0xAAAAAAAA; + i += 1; + } + return i; +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ int bottom_bit(unsigned int bits) +{ + int i; + + if (bits == 0) + return -1; + i = 32; + if (bits & 0x0000FFFF) + { + bits &= 0x0000FFFF; + i -= 16; + } + if (bits & 0x00FF00FF) + { + bits &= 0x00FF00FF; + i -= 8; + } + if (bits & 0x0F0F0F0F) + { + bits &= 0x0F0F0F0F; + i -= 4; + } + if (bits & 0x33333333) + { + bits &= 0x33333333; + i -= 2; + } + if (bits & 0x55555555) + { + bits &= 0x55555555; + i -= 1; + } + return i; +} +/*- End of function --------------------------------------------------------*/ +#endif + +/*! \brief Bit reverse a byte. + \param data The byte to be reversed. + \return The bit reversed version of data. */ +static __inline__ uint8_t bit_reverse8(uint8_t x) +{ +#if defined(__i386__) || defined(__x86_64__) + /* If multiply is fast */ + return ((x*0x0802U & 0x22110U) | (x*0x8020U & 0x88440U))*0x10101U >> 16; +#else + /* If multiply is slow, but we have a barrel shifter */ + x = (x >> 4) | (x << 4); + x = ((x & 0xCC) >> 2) | ((x & 0x33) << 2); + return ((x & 0xAA) >> 1) | ((x & 0x55) << 1); +#endif +} +/*- End of function --------------------------------------------------------*/ + +/*! \brief Bit reverse a 16 bit word. + \param data The word to be reversed. + \return The bit reversed version of data. */ +uint16_t bit_reverse16(uint16_t data); + +/*! \brief Bit reverse a 32 bit word. + \param data The word to be reversed. + \return The bit reversed version of data. */ +uint32_t bit_reverse32(uint32_t data); + +/*! \brief Bit reverse each of the four bytes in a 32 bit word. + \param data The word to be reversed. + \return The bit reversed version of data. */ +uint32_t bit_reverse_4bytes(uint32_t data); + +/*! \brief Find the number of set bits in a 32 bit word. + \param x The word to be searched. + \return The number of set bits. */ +int one_bits32(uint32_t x); + +/*! \brief Create a mask as wide as the number in a 32 bit word. + \param x The word to be searched. + \return The mask. */ +uint32_t make_mask32(uint32_t x); + +/*! \brief Create a mask as wide as the number in a 16 bit word. + \param x The word to be searched. + \return The mask. */ +uint16_t make_mask16(uint16_t x); + +/*! \brief Find the least significant one in a word, and return a word + with just that bit set. + \param x The word to be searched. + \return The word with the single set bit. */ +static __inline__ uint32_t least_significant_one32(uint32_t x) +{ + return (x & (-(int32_t) x)); +} +/*- End of function --------------------------------------------------------*/ + +/*! \brief Find the most significant one in a word, and return a word + with just that bit set. + \param x The word to be searched. + \return The word with the single set bit. */ +static __inline__ uint32_t most_significant_one32(uint32_t x) +{ +#if defined(__i386__) || defined(__x86_64__) + return 1 << top_bit(x); +#else + x = make_mask32(x); + return (x ^ (x >> 1)); +#endif +} +/*- End of function --------------------------------------------------------*/ + +/*! \brief Find the parity of a byte. + \param x The byte to be checked. + \return 1 for odd, or 0 for even. */ +static __inline__ int parity8(uint8_t x) +{ + x = (x ^ (x >> 4)) & 0x0F; + return (0x6996 >> x) & 1; +} +/*- End of function --------------------------------------------------------*/ + +/*! \brief Find the parity of a 16 bit word. + \param x The word to be checked. + \return 1 for odd, or 0 for even. */ +static __inline__ int parity16(uint16_t x) +{ + x ^= (x >> 8); + x = (x ^ (x >> 4)) & 0x0F; + return (0x6996 >> x) & 1; +} +/*- End of function --------------------------------------------------------*/ + +/*! \brief Find the parity of a 32 bit word. + \param x The word to be checked. + \return 1 for odd, or 0 for even. */ +static __inline__ int parity32(uint32_t x) +{ + x ^= (x >> 16); + x ^= (x >> 8); + x = (x ^ (x >> 4)) & 0x0F; + return (0x6996 >> x) & 1; +} +/*- End of function --------------------------------------------------------*/ + +#ifdef __cplusplus +} +#endif + +#endif +/*- End of file ------------------------------------------------------------*/ diff -ruN mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/oslec_echo.c mISDN-1_1_5/drivers/isdn/hardware/mISDN/oslec_echo.c --- mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/oslec_echo.c 1970-01-01 09:30:00.000000000 +0930 +++ mISDN-1_1_5/drivers/isdn/hardware/mISDN/oslec_echo.c 2007-09-13 08:46:20.000000000 +0930 @@ -0,0 +1,628 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * echo.c - A line echo canceller. This code is being developed + * against and partially complies with G168. + * + * Written by Steve Underwood + * and David Rowe + * + * Copyright (C) 2001, 2003 Steve Underwood, 2007 David Rowe + * + * Based on a bit from here, a bit from there, eye of toad, ear of + * bat, 15 years of failed attempts by David and a few fried brain + * cells. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: echo.c,v 1.20 2006/12/01 18:00:48 steveu Exp $ + */ + +/*! \file */ + +/* Implementation Notes + David Rowe + April 2007 + + This code started life as Steve's NLMS algorithm with a tap + rotation algorithm to handle divergence during double talk. I + added a Geigel Double Talk Detector (DTD) [2] and performed some + G168 tests. However I had trouble meeting the G168 requirements, + especially for double talk - there were always cases where my DTD + failed, for example where near end speech was under the 6dB + threshold required for declaring double talk. + + So I tried a two path algorithm [1], which has so far given better + results. The original tap rotation/Geigel algorithm is available + in SVN http://svn.rowetel.com/software/oslec/tags/before_16bit. + It's probably possible to make it work if some one wants to put some + serious work into it. + + At present no special treatment is provided for tones, which + generally cause NLMS algorithms to diverge. Initial runs of a + subset of the G168 tests for tones (e.g ./echo_test 6) show the + current algorithm is passing OK, which is kind of surprising. The + full set of tests needs to be performed to confirm this result. + + One other interesting change is that I have managed to get the NLMS + code to work with 16 bit coefficients, rather than the original 32 + bit coefficents. This reduces the MIPs and storage required. + I evaulated the 16 bit port using g168_tests.sh and listening tests + on 4 real-world samples. + + I also attempted the implementation of a block based NLMS update + [2] but although this passes g168_tests.sh it didn't converge well + on the real-world samples. I have no idea why, perhaps a scaling + problem. The block based code is also available in SVN + http://svn.rowetel.com/software/oslec/tags/before_16bit. If this + code can be debugged, it will lead to further reduction in MIPS, as + the block update code maps nicely onto DSP instruction sets (it's a + dot product) compared to the current sample-by-sample update. + + Steve also has some nice notes on echo cancellers in echo.h + + + References: + + [1] Ochiai, Areseki, and Ogihara, "Echo Canceller with Two Echo + Path Models", IEEE Transactions on communications, COM-25, + No. 6, June + 1977. + http://www.rowetel.com/images/echo/dual_path_paper.pdf + + [2] The classic, very useful paper that tells you how to + actually build a real world echo canceller: + Messerschmitt, Hedberg, Cole, Haoui, Winship, "Digital Voice + Echo Canceller with a TMS320020, + http://www.rowetel.com/images/echo/spra129.pdf + + [3] I have written a series of blog posts on this work, here is + Part 1: http://www.rowetel.com/blog/?p=18 + + [4] The source code http://svn.rowetel.com/software/oslec/ + + [5] A nice reference on LMS filters: + http://en.wikipedia.org/wiki/Least_mean_squares_filter + + Credits: + + Thanks to Steve Underwood, Jean-Marc Valin, and Ramakrishnan + Muthukrishnan for their suggestions and email discussions. Thanks + also to those people who collected echo samples for me such as + Mark, Pawel, and Pavel. +*/ + +#ifdef __KERNEL__ +#include /* We're doing kernel work */ +#include +#include +#include +#define malloc(a) kmalloc((a), GFP_KERNEL) +#define free(a) kfree(a) +#else +#include +#include +#include +#include + +#endif + +#include "oslec_bit_operations.h" +#include "oslec_echo.h" + +#if !defined(NULL) +#define NULL (void *) 0 +#endif +#if !defined(FALSE) +#define FALSE 0 +#endif +#if !defined(TRUE) +#define TRUE (!FALSE) +#endif + +#define MIN_TX_POWER_FOR_ADAPTION 64 +#define MIN_RX_POWER_FOR_ADAPTION 64 +#define DTD_HANGOVER 600 /* 600 samples, or 75ms */ +#define DC_LOG2BETA 3 /* log2() of DC filter Beta */ + +/*-----------------------------------------------------------------------*\ + + FUNCTIONS + +\*-----------------------------------------------------------------------*/ + +/* adapting coeffs using the traditional stochastic descent (N)LMS algorithm */ + + +#ifdef __BLACKFIN_ASM__ +static void __inline__ lms_adapt_bg(echo_can_state_t *ec, int clean, int shift) +{ + int i, j; + int offset1; + int offset2; + int factor; + int exp; + int16_t *phist; + int n; + + if (shift > 0) + factor = clean << shift; + else + factor = clean >> -shift; + + /* Update the FIR taps */ + + offset2 = ec->curr_pos; + offset1 = ec->taps - offset2; + phist = &ec->fir_state_bg.history[offset2]; + + /* st: and en: help us locate the assembler in echo.s */ + + //asm("st:"); + n = ec->taps; + for (i = 0, j = offset2; i < n; i++, j++) + { + exp = *phist++ * factor; + ec->fir_taps16[1][i] += (int16_t) ((exp+(1<<14)) >> 15); + } + //asm("en:"); + + /* Note the asm for the inner loop above generated by Blackfin gcc + 4.1.1 is pretty good (note even parallel instructions used): + + R0 = W [P0++] (X); + R0 *= R2; + R0 = R0 + R3 (NS) || + R1 = W [P1] (X) || + nop; + R0 >>>= 15; + R0 = R0 + R1; + W [P1++] = R0; + + A block based update algorithm would be much faster but the + above can't be improved on much. Every instruction saved in + the loop above is 2 MIPs/ch! The for loop above is where the + Blackfin spends most of it's time - about 17 MIPs/ch measured + with speedtest.c with 256 taps (32ms). Write-back and + Write-through cache gave about the same performance. + */ +} + +/* + IDEAS for further optimisation of lms_adapt_bg(): + + 1/ The rounding is quite costly. Could we keep as 32 bit coeffs + then make filter pluck the MS 16-bits of the coeffs when filtering? + However this would lower potential optimisation of filter, as I + think the dual-MAC architecture requires packed 16 bit coeffs. + + 2/ Block based update would be more efficient, as per comments above, + could use dual MAC architecture. + + 3/ Look for same sample Blackfin LMS code, see if we can get dual-MAC + packing. + + 4/ Execute the whole e/c in a block of say 20ms rather than sample + by sample. Processing a few samples every ms is inefficient. +*/ + +#else +static __inline__ void lms_adapt_bg(echo_can_state_t *ec, int clean, int shift) +{ + int i; + + int offset1; + int offset2; + int factor; + int exp; + + if (shift > 0) + factor = clean << shift; + else + factor = clean >> -shift; + + /* Update the FIR taps */ + + offset2 = ec->curr_pos; + offset1 = ec->taps - offset2; + + for (i = ec->taps - 1; i >= offset1; i--) + { + exp = (ec->fir_state_bg.history[i - offset1]*factor); + ec->fir_taps16[1][i] += (int16_t) ((exp+(1<<14)) >> 15); + } + for ( ; i >= 0; i--) + { + exp = (ec->fir_state_bg.history[i + offset2]*factor); + ec->fir_taps16[1][i] += (int16_t) ((exp+(1<<14)) >> 15); + } +} +#endif + +/*- End of function --------------------------------------------------------*/ + +echo_can_state_t *echo_can_create(int len, int adaption_mode) +{ + echo_can_state_t *ec; + int i; + int j; + + ec = (echo_can_state_t *) malloc(sizeof(*ec)); + if (ec == NULL) + return NULL; + memset(ec, 0, sizeof(*ec)); + + ec->taps = len; + ec->log2taps = top_bit(len); + ec->curr_pos = ec->taps - 1; + + for (i = 0; i < 2; i++) + { + if ((ec->fir_taps16[i] = (int16_t *) malloc((ec->taps)*sizeof(int16_t))) == NULL) + { + for (j = 0; j < i; j++) + free(ec->fir_taps16[j]); + free(ec); + return NULL; + } + memset(ec->fir_taps16[i], 0, (ec->taps)*sizeof(int16_t)); + } + + fir16_create(&ec->fir_state, + ec->fir_taps16[0], + ec->taps); + fir16_create(&ec->fir_state_bg, + ec->fir_taps16[1], + ec->taps); + + for(i=0; i<5; i++) { + ec->xvtx[i] = ec->yvtx[i] = ec->xvrx[i] = ec->yvrx[i] = 0; + } + + ec->cng_level = 1000; + echo_can_adaption_mode(ec, adaption_mode); + + ec->snapshot = (int16_t*)malloc(ec->taps*sizeof(int16_t)); + memset(ec->snapshot, 0, sizeof(int16_t)*ec->taps); + + ec->cond_met = 0; + ec->Pstates = 0; + ec->Ltxacc = ec->Lrxacc = ec->Lcleanacc = ec->Lclean_bgacc = 0; + ec->Ltx = ec->Lrx = ec->Lclean = ec->Lclean_bg = 0; + ec->tx_1 = ec->tx_2 = ec->rx_1 = ec->rx_2 = 0; + ec->Lbgn = ec->Lbgn_acc = 0; + ec->Lbgn_upper = 200; + ec->Lbgn_upper_acc = ec->Lbgn_upper << 13; + + return ec; +} +/*- End of function --------------------------------------------------------*/ + +void echo_can_free(echo_can_state_t *ec) +{ + int i; + + fir16_free(&ec->fir_state); + fir16_free(&ec->fir_state_bg); + for (i = 0; i < 2; i++) + free(ec->fir_taps16[i]); + free(ec->snapshot); + free(ec); +} +/*- End of function --------------------------------------------------------*/ + +void echo_can_adaption_mode(echo_can_state_t *ec, int adaption_mode) +{ + ec->adaption_mode = adaption_mode; +} +/*- End of function --------------------------------------------------------*/ + +void echo_can_flush(echo_can_state_t *ec) +{ + int i; + + ec->Ltxacc = ec->Lrxacc = ec->Lcleanacc = ec->Lclean_bgacc = 0; + ec->Ltx = ec->Lrx = ec->Lclean = ec->Lclean_bg = 0; + ec->tx_1 = ec->tx_2 = ec->rx_1 = ec->rx_2 = 0; + + ec->Lbgn = ec->Lbgn_acc = 0; + ec->Lbgn_upper = 200; + ec->Lbgn_upper_acc = ec->Lbgn_upper << 13; + + ec->nonupdate_dwell = 0; + + fir16_flush(&ec->fir_state); + fir16_flush(&ec->fir_state_bg); + ec->fir_state.curr_pos = ec->taps - 1; + ec->fir_state_bg.curr_pos = ec->taps - 1; + for (i = 0; i < 2; i++) + memset(ec->fir_taps16[i], 0, ec->taps*sizeof(int16_t)); + + ec->curr_pos = ec->taps - 1; + ec->Pstates = 0; +} +/*- End of function --------------------------------------------------------*/ + +void echo_can_snapshot(echo_can_state_t *ec) { + memcpy(ec->snapshot, ec->fir_taps16[0], ec->taps*sizeof(int16_t)); +} +/*- End of function --------------------------------------------------------*/ + +/* Dual Path Echo Canceller ------------------------------------------------*/ + +int16_t echo_can_update(echo_can_state_t *ec, int16_t tx, int16_t rx) +{ + int32_t echo_value; + int clean_bg; + int tmp; + + /* Input scaling was found be required to prevent problems when tx + starts clipping. Another possible way to handle this would be the + filter coefficent scaling. */ + + ec->tx = tx; ec->rx = rx; + tx >>=1; + rx >>=1; + + /* + Filter DC, 3dB point is 160Hz (I think), note 32 bit precision required + otherwise values do not track down to 0. Zero at DC, Pole at (1-Beta) + only real axis. Some chip sets (like Si labs) don't need + this, but something like a $10 X100P card does. Any DC really slows + down convergence. + + Note: removes some low frequency from the signal, this reduces + the speech quality when listening to samples through headphones + but may not be obvious through a telephone handset. + + Note that the 3dB frequency in radians is approx Beta, e.g. for + Beta = 2^(-3) = 0.125, 3dB freq is 0.125 rads = 159Hz. + */ + + if (ec->adaption_mode & ECHO_CAN_USE_RX_HPF) { + tmp = rx << 15; + ec->rx_1 += -(ec->rx_1>>DC_LOG2BETA) + tmp - ec->rx_2; + rx = ec->rx_1 >> 15; + ec->rx_2 = tmp; + } + + /* Block average of power in the filter states. Used for + adaption power calculation. */ + + { + int new, old; + + /* efficient "out with the old and in with the new" algorithm so + we don't have to recalculate over the whole block of + samples. */ + new = (int)tx * (int)tx; + old = (int)ec->fir_state.history[ec->fir_state.curr_pos] * + (int)ec->fir_state.history[ec->fir_state.curr_pos]; + ec->Pstates += ((new - old) + (1<log2taps)) >> ec->log2taps; + if (ec->Pstates < 0) ec->Pstates = 0; + } + + /* Calculate short term average levels using simple single pole IIRs */ + + ec->Ltxacc += abs(tx) - ec->Ltx; + ec->Ltx = (ec->Ltxacc + (1<<4)) >> 5; + ec->Lrxacc += abs(rx) - ec->Lrx; + ec->Lrx = (ec->Lrxacc + (1<<4)) >> 5; + + /* Foreground filter ---------------------------------------------------*/ + + ec->fir_state.coeffs = ec->fir_taps16[0]; + echo_value = fir16(&ec->fir_state, tx); + ec->clean = rx - echo_value; + ec->Lcleanacc += abs(ec->clean) - ec->Lclean; + ec->Lclean = (ec->Lcleanacc + (1<<4)) >> 5; + + /* Background filter ---------------------------------------------------*/ + + echo_value = fir16(&ec->fir_state_bg, tx); + clean_bg = rx - echo_value; + ec->Lclean_bgacc += abs(clean_bg) - ec->Lclean_bg; + ec->Lclean_bg = (ec->Lclean_bgacc + (1<<4)) >> 5; + + /* Background Filter adaption -----------------------------------------*/ + + /* Almost always adap bg filter, just simple DT and energy + detection to minimise adaption in cases of strong double talk. + However this is not critical for the dual path algorithm. + */ + ec->factor = 0; + ec->shift = 0; + if ((ec->nonupdate_dwell == 0)) { + int P, logP, shift; + + /* Determine: + + f = Beta * clean_bg_rx/P ------ (1) + + where P is the total power in the filter states. + + The Boffins have shown that if we obey (1) we converge + quickly and avoid instability. + + The correct factor f must be in Q30, as this is the fixed + point format required by the lms_adapt_bg() function, + therefore the scaled version of (1) is: + + (2^30) * f = (2^30) * Beta * clean_bg_rx/P + factor = (2^30) * Beta * clean_bg_rx/P ----- (2) + + We have chosen Beta = 0.25 by experiment, so: + + factor = (2^30) * (2^-2) * clean_bg_rx/P + + (30 - 2 - log2(P)) + factor = clean_bg_rx 2 ----- (3) + + To avoid a divide we approximate log2(P) as top_bit(P), + which returns the position of the highest non-zero bit in + P. This approximation introduces an error as large as a + factor of 2, but the algorithm seems to handle it OK. + + Come to think of it a divide may not be a big deal on a + modern DSP, so its probably worth checking out the cycles + for a divide versus a top_bit() implementation. + */ + + P = MIN_TX_POWER_FOR_ADAPTION + ec->Pstates; + logP = top_bit(P) + ec->log2taps; + shift = 30 - 2 - logP; + ec->shift = shift; + + lms_adapt_bg(ec, clean_bg, shift); + } + + /* very simple DTD to make sure we dont try and adapt with strong + near end speech */ + + ec->adapt = 0; + if ((ec->Lrx > MIN_RX_POWER_FOR_ADAPTION) && (ec->Lrx > ec->Ltx)) + ec->nonupdate_dwell = DTD_HANGOVER; + if (ec->nonupdate_dwell) + ec->nonupdate_dwell--; + + /* Transfer logic ------------------------------------------------------*/ + + /* These conditions are from the dual path paper [1], I messed with + them a bit to improve performance. */ + + if ((ec->adaption_mode & ECHO_CAN_USE_ADAPTION) && + (ec->nonupdate_dwell == 0) && + (8*ec->Lclean_bg < 7*ec->Lclean) /* (ec->Lclean_bg < 0.875*ec->Lclean) */ && + (8*ec->Lclean_bg < ec->Ltx) /* (ec->Lclean_bg < 0.125*ec->Ltx) */ ) + { + if (ec->cond_met == 6) { + /* BG filter has had better results for 6 consecutive samples */ + ec->adapt = 1; + memcpy(ec->fir_taps16[0], ec->fir_taps16[1], ec->taps*sizeof(int16_t)); + } + else + ec->cond_met++; + } + else + ec->cond_met = 0; + + /* Non-Linear Processing ---------------------------------------------------*/ + + ec->clean_nlp = ec->clean; + if (ec->adaption_mode & ECHO_CAN_USE_NLP) + { + /* Non-linear processor - a fancy way to say "zap small signals, to avoid + residual echo due to (uLaw/ALaw) non-linearity in the channel.". */ + + if ((16*ec->Lclean < ec->Ltx)) + { + /* Our e/c has improved echo by at least 24 dB (each factor of 2 is 6dB, + so 2*2*2*2=16 is the same as 6+6+6+6=24dB) */ + if (ec->adaption_mode & ECHO_CAN_USE_CNG) + { + ec->cng_level = ec->Lbgn; + + /* Very elementary comfort noise generation. Just random + numbers rolled off very vaguely Hoth-like. DR: This + noise doesn't sound quite right to me - I suspect there + are some overlfow issues in the filtering as it's too + "crackly". TODO: debug this, maybe just play noise at + high level or look at spectrum. + */ + + ec->cng_rndnum = 1664525U*ec->cng_rndnum + 1013904223U; + ec->cng_filter = ((ec->cng_rndnum & 0xFFFF) - 32768 + 5*ec->cng_filter) >> 3; + ec->clean_nlp = (ec->cng_filter*ec->cng_level*8) >> 14; + + } + else if (ec->adaption_mode & ECHO_CAN_USE_CLIP) + { + /* This sounds much better than CNG */ + if (ec->clean_nlp > ec->Lbgn) + ec->clean_nlp = ec->Lbgn; + if (ec->clean_nlp < -ec->Lbgn) + ec->clean_nlp = -ec->Lbgn; + } + else + { + /* just mute the residual, doesn't sound very good, used mainly + in G168 tests */ + ec->clean_nlp = 0; + } + } + else { + /* Background noise estimator. I tried a few algorithms + here without much luck. This very simple one seems to + work best, we just average the level using a slow (1 sec + time const) filter if the current level is less than a + (experimentally derived) constant. This means we dont + include high level signals like near end speech. When + combined with CNG or especially CLIP seems to work OK. + */ + if (ec->Lclean < 40) { + ec->Lbgn_acc += abs(ec->clean) - ec->Lbgn; + ec->Lbgn = (ec->Lbgn_acc + (1<<11)) >> 12; + } + } + } + + /* Roll around the taps buffer */ + if (ec->curr_pos <= 0) + ec->curr_pos = ec->taps; + ec->curr_pos--; + + if (ec->adaption_mode & ECHO_CAN_DISABLE) + ec->clean_nlp = rx; + + /* Output scaled back up again to match input scaling */ + + return (int16_t) ec->clean_nlp << 1; +} + +/*- End of function --------------------------------------------------------*/ + +/* This function is seperated from the echo canceller is it is usually called + as part of the tx process. See rx HP (DC blocking) filter above, it's + the same design. + + Some soft phones send speech signals with a lot of low frequency + energy, e.g. down to 20Hz. This can make the hybrid non-linear + which causes the echo canceller to fall over. This filter can help + by removing any low frequency before it gets to the tx port of the + hybrid. + + It can also help by removing and DC in the tx signal. DC is bad + for LMS algorithms. +*/ + +int16_t echo_can_hpf_tx(echo_can_state_t *ec, int16_t tx) { + int tmp, tmp1; + + if (ec->adaption_mode & ECHO_CAN_USE_TX_HPF) { + tmp = tx << 15; + ec->tx_1 += -(ec->tx_1>>DC_LOG2BETA) + tmp - ec->tx_2; + tmp1 = ec->tx_1 >> 15; + if (tmp1 > 32767) tmp1 = 32767; + if (tmp1 < -32767) tmp1 = -32767; + tx = tmp1; + ec->tx_2 = tmp; + } + + return tx; +} + +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ diff -ruN mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/oslec_echo.h mISDN-1_1_5/drivers/isdn/hardware/mISDN/oslec_echo.h --- mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/oslec_echo.h 1970-01-01 09:30:00.000000000 +0930 +++ mISDN-1_1_5/drivers/isdn/hardware/mISDN/oslec_echo.h 2007-09-13 08:46:20.000000000 +0930 @@ -0,0 +1,220 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * echo.c - A line echo canceller. This code is being developed + * against and partially complies with G168. + * + * Written by Steve Underwood + * and David Rowe + * + * Copyright (C) 2001 Steve Underwood and 2007 David Rowe + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: echo.h,v 1.9 2006/10/24 13:45:28 steveu Exp $ + */ + +/*! \file */ + +#if !defined(_ECHO_H_) +#define _ECHO_H_ + +/*! \page echo_can_page Line echo cancellation for voice + +\section echo_can_page_sec_1 What does it do? +This module aims to provide G.168-2002 compliant echo cancellation, to remove +electrical echoes (e.g. from 2-4 wire hybrids) from voice calls. + +\section echo_can_page_sec_2 How does it work? +The heart of the echo cancellor is FIR filter. This is adapted to match the echo +impulse response of the telephone line. It must be long enough to adequately cover +the duration of that impulse response. The signal transmitted to the telephone line +is passed through the FIR filter. Once the FIR is properly adapted, the resulting +output is an estimate of the echo signal received from the line. This is subtracted +from the received signal. The result is an estimate of the signal which originated +at the far end of the line, free from echos of our own transmitted signal. + +The least mean squares (LMS) algorithm is attributed to Widrow and Hoff, and was +introduced in 1960. It is the commonest form of filter adaption used in things +like modem line equalisers and line echo cancellers. There it works very well. +However, it only works well for signals of constant amplitude. It works very poorly +for things like speech echo cancellation, where the signal level varies widely. +This is quite easy to fix. If the signal level is normalised - similar to applying +AGC - LMS can work as well for a signal of varying amplitude as it does for a modem +signal. This normalised least mean squares (NLMS) algorithm is the commonest one used +for speech echo cancellation. Many other algorithms exist - e.g. RLS (essentially +the same as Kalman filtering), FAP, etc. Some perform significantly better than NLMS. +However, factors such as computational complexity and patents favour the use of NLMS. + +A simple refinement to NLMS can improve its performance with speech. NLMS tends +to adapt best to the strongest parts of a signal. If the signal is white noise, +the NLMS algorithm works very well. However, speech has more low frequency than +high frequency content. Pre-whitening (i.e. filtering the signal to flatten +its spectrum) the echo signal improves the adapt rate for speech, and ensures the +final residual signal is not heavily biased towards high frequencies. A very low +complexity filter is adequate for this, so pre-whitening adds little to the +compute requirements of the echo canceller. + +An FIR filter adapted using pre-whitened NLMS performs well, provided certain +conditions are met: + + - The transmitted signal has poor self-correlation. + - There is no signal being generated within the environment being cancelled. + +The difficulty is that neither of these can be guaranteed. + +If the adaption is performed while transmitting noise (or something fairly noise +like, such as voice) the adaption works very well. If the adaption is performed +while transmitting something highly correlative (typically narrow band energy +such as signalling tones or DTMF), the adaption can go seriously wrong. The reason +is there is only one solution for the adaption on a near random signal - the impulse +response of the line. For a repetitive signal, there are any number of solutions +which converge the adaption, and nothing guides the adaption to choose the generalised +one. Allowing an untrained canceller to converge on this kind of narrowband +energy probably a good thing, since at least it cancels the tones. Allowing a well +converged canceller to continue converging on such energy is just a way to ruin +its generalised adaption. A narrowband detector is needed, so adapation can be +suspended at appropriate times. + +The adaption process is based on trying to eliminate the received signal. When +there is any signal from within the environment being cancelled it may upset the +adaption process. Similarly, if the signal we are transmitting is small, noise +may dominate and disturb the adaption process. If we can ensure that the +adaption is only performed when we are transmitting a significant signal level, +and the environment is not, things will be OK. Clearly, it is easy to tell when +we are sending a significant signal. Telling, if the environment is generating a +significant signal, and doing it with sufficient speed that the adaption will +not have diverged too much more we stop it, is a little harder. + +The key problem in detecting when the environment is sourcing significant energy +is that we must do this very quickly. Given a reasonably long sample of the +received signal, there are a number of strategies which may be used to assess +whether that signal contains a strong far end component. However, by the time +that assessment is complete the far end signal will have already caused major +mis-convergence in the adaption process. An assessment algorithm is needed which +produces a fairly accurate result from a very short burst of far end energy. + +\section echo_can_page_sec_3 How do I use it? +The echo cancellor processes both the transmit and receive streams sample by +sample. The processing function is not declared inline. Unfortunately, +cancellation requires many operations per sample, so the call overhead is only a +minor burden. +*/ + +#include "oslec_fir.h" + +/* Mask bits for the adaption mode */ + +#define ECHO_CAN_USE_ADAPTION 0x01 +#define ECHO_CAN_USE_NLP 0x02 +#define ECHO_CAN_USE_CNG 0x04 +#define ECHO_CAN_USE_CLIP 0x08 +#define ECHO_CAN_USE_TX_HPF 0x10 +#define ECHO_CAN_USE_RX_HPF 0x20 +#define ECHO_CAN_DISABLE 0x40 + +/*! + G.168 echo canceller descriptor. This defines the working state for a line + echo canceller. +*/ +typedef struct +{ + int16_t tx,rx; + int16_t clean; + int16_t clean_nlp; + + int nonupdate_dwell; + int curr_pos; + int taps; + int log2taps; + int adaption_mode; + + int cond_met; + int32_t Pstates; + int16_t adapt; + int32_t factor; + int16_t shift; + + /* Average levels and averaging filter states */ + int Ltxacc, Lrxacc, Lcleanacc, Lclean_bgacc; + int Ltx, Lrx; + int Lclean; + int Lclean_bg; + int Lbgn, Lbgn_acc, Lbgn_upper, Lbgn_upper_acc; + + /* foreground and background filter states */ + fir16_state_t fir_state; + fir16_state_t fir_state_bg; + int16_t *fir_taps16[2]; + + /* DC blocking filter states */ + int tx_1, tx_2, rx_1, rx_2; + + /* optional High Pass Filter states */ + int32_t xvtx[5], yvtx[5]; + int32_t xvrx[5], yvrx[5]; + + /* Parameters for the optional Hoth noise generator */ + int cng_level; + int cng_rndnum; + int cng_filter; + + /* snapshot sample of coeffs used for development */ + int16_t *snapshot; + +} echo_can_state_t; + +/*! Create a voice echo canceller context. + \param len The length of the canceller, in samples. + \return The new canceller context, or NULL if the canceller could not be created. +*/ +echo_can_state_t *echo_can_create(int len, int adaption_mode); + +/*! Free a voice echo canceller context. + \param ec The echo canceller context. +*/ +void echo_can_free(echo_can_state_t *ec); + +/*! Flush (reinitialise) a voice echo canceller context. + \param ec The echo canceller context. +*/ +void echo_can_flush(echo_can_state_t *ec); + +/*! Set the adaption mode of a voice echo canceller context. + \param ec The echo canceller context. + \param adapt The mode. +*/ +void echo_can_adaption_mode(echo_can_state_t *ec, int adaption_mode); + +void echo_can_snapshot(echo_can_state_t *ec); + +/*! Process a sample through a voice echo canceller. + \param ec The echo canceller context. + \param tx The transmitted audio sample. + \param rx The received audio sample. + \return The clean (echo cancelled) received sample. +*/ +int16_t echo_can_update(echo_can_state_t *ec, int16_t tx, int16_t rx); + +/*! Process to high pass filter the tx signal. + \param ec The echo canceller context. + \param tx The transmitted auio sample. + \return The HP filtered transmit sample, send this to your D/A. +*/ +int16_t echo_can_hpf_tx(echo_can_state_t *ec, int16_t tx); + +#endif +/*- End of file ------------------------------------------------------------*/ diff -ruN mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/oslec_fir.h mISDN-1_1_5/drivers/isdn/hardware/mISDN/oslec_fir.h --- mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/oslec_fir.h 1970-01-01 09:30:00.000000000 +0930 +++ mISDN-1_1_5/drivers/isdn/hardware/mISDN/oslec_fir.h 2007-09-13 08:46:20.000000000 +0930 @@ -0,0 +1,369 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * fir.h - General telephony FIR routines + * + * Written by Steve Underwood + * + * Copyright (C) 2002 Steve Underwood + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: fir.h,v 1.8 2006/10/24 13:45:28 steveu Exp $ + */ + +/*! \page fir_page FIR filtering +\section fir_page_sec_1 What does it do? +???. + +\section fir_page_sec_2 How does it work? +???. +*/ + +#if !defined(_FIR_H_) +#define _FIR_H_ + +/* + Blackfin NOTES & IDEAS: + + A simple dot product function is used to implement the filter. This performs + just one MAC/cycle which is inefficient but was easy to implement as a first + pass. The current Blackfin code also uses an unrolled form of the filter + history to avoid 0 length hardware loop issues. This is wasteful of + memory. + + Ideas for improvement: + + 1/ Rewrite filter for dual MAC inner loop. The issue here is handling + history sample offsets that are 16 bit aligned - the dual MAC needs + 32 bit aligmnent. There are some good examples in libbfdsp. + + 2/ Use the hardware circular buffer facility tohalve memory usage. + + 3/ Consider using internal memory. + + Using less memory might also improve speed as cache misses will be + reduced. A drop in MIPs and memory approaching 50% should be + possible. + + The foreground and background filters currenlty use a total of + about 10 MIPs/ch as measured with speedtest.c on a 256 TAP echo + can. +*/ + +#if defined(USE_MMX) || defined(USE_SSE2) +#include "mmx.h" +#endif + +/*! + 16 bit integer FIR descriptor. This defines the working state for a single + instance of an FIR filter using 16 bit integer coefficients. +*/ +typedef struct +{ + int taps; + int curr_pos; + const int16_t *coeffs; + int16_t *history; +} fir16_state_t; + +/*! + 32 bit integer FIR descriptor. This defines the working state for a single + instance of an FIR filter using 32 bit integer coefficients, and filtering + 16 bit integer data. +*/ +typedef struct +{ + int taps; + int curr_pos; + const int32_t *coeffs; + int16_t *history; +} fir32_state_t; + +/*! + Floating point FIR descriptor. This defines the working state for a single + instance of an FIR filter using floating point coefficients and data. +*/ +typedef struct +{ + int taps; + int curr_pos; + const float *coeffs; + float *history; +} fir_float_state_t; + +#ifdef __cplusplus +extern "C" { +#endif + +static __inline__ const int16_t *fir16_create(fir16_state_t *fir, + const int16_t *coeffs, + int taps) +{ + fir->taps = taps; + fir->curr_pos = taps - 1; + fir->coeffs = coeffs; +#if defined(USE_MMX) || defined(USE_SSE2) || defined(__BLACKFIN_ASM__) + if ((fir->history = malloc(2*taps*sizeof(int16_t)))) + memset(fir->history, 0, 2*taps*sizeof(int16_t)); +#else + if ((fir->history = (int16_t *) malloc(taps*sizeof(int16_t)))) + memset(fir->history, 0, taps*sizeof(int16_t)); +#endif + return fir->history; +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ void fir16_flush(fir16_state_t *fir) +{ +#if defined(USE_MMX) || defined(USE_SSE2) || defined(__BLACKFIN_ASM__) + memset(fir->history, 0, 2*fir->taps*sizeof(int16_t)); +#else + memset(fir->history, 0, fir->taps*sizeof(int16_t)); +#endif +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ void fir16_free(fir16_state_t *fir) +{ + free(fir->history); +} +/*- End of function --------------------------------------------------------*/ + +#ifdef __BLACKFIN_ASM__ +static inline int32_t dot_asm(short *x, short *y, int len) +{ + int dot; + + len--; + + __asm__ + ( + "I0 = %1;\n\t" + "I1 = %2;\n\t" + "A0 = 0;\n\t" + "R0.L = W[I0++] || R1.L = W[I1++];\n\t" + "LOOP dot%= LC0 = %3;\n\t" + "LOOP_BEGIN dot%=;\n\t" + "A0 += R0.L * R1.L (IS) || R0.L = W[I0++] || R1.L = W[I1++];\n\t" + "LOOP_END dot%=;\n\t" + "A0 += R0.L*R1.L (IS);\n\t" + "R0 = A0;\n\t" + "%0 = R0;\n\t" + : "=&d" (dot) + : "a" (x), "a" (y), "a" (len) + : "I0", "I1", "A1", "A0", "R0", "R1" + ); + + return dot; +} +#endif +/*- End of function --------------------------------------------------------*/ + +static __inline__ int16_t fir16(fir16_state_t *fir, int16_t sample) +{ + int32_t y; +#if defined(USE_MMX) + int i; + mmx_t *mmx_coeffs; + mmx_t *mmx_hist; + + fir->history[fir->curr_pos] = sample; + fir->history[fir->curr_pos + fir->taps] = sample; + + mmx_coeffs = (mmx_t *) fir->coeffs; + mmx_hist = (mmx_t *) &fir->history[fir->curr_pos]; + i = fir->taps; + pxor_r2r(mm4, mm4); + /* 8 samples per iteration, so the filter must be a multiple of 8 long. */ + while (i > 0) + { + movq_m2r(mmx_coeffs[0], mm0); + movq_m2r(mmx_coeffs[1], mm2); + movq_m2r(mmx_hist[0], mm1); + movq_m2r(mmx_hist[1], mm3); + mmx_coeffs += 2; + mmx_hist += 2; + pmaddwd_r2r(mm1, mm0); + pmaddwd_r2r(mm3, mm2); + paddd_r2r(mm0, mm4); + paddd_r2r(mm2, mm4); + i -= 8; + } + movq_r2r(mm4, mm0); + psrlq_i2r(32, mm0); + paddd_r2r(mm0, mm4); + movd_r2m(mm4, y); + emms(); +#elif defined(USE_SSE2) + int i; + xmm_t *xmm_coeffs; + xmm_t *xmm_hist; + + fir->history[fir->curr_pos] = sample; + fir->history[fir->curr_pos + fir->taps] = sample; + + xmm_coeffs = (xmm_t *) fir->coeffs; + xmm_hist = (xmm_t *) &fir->history[fir->curr_pos]; + i = fir->taps; + pxor_r2r(xmm4, xmm4); + /* 16 samples per iteration, so the filter must be a multiple of 16 long. */ + while (i > 0) + { + movdqu_m2r(xmm_coeffs[0], xmm0); + movdqu_m2r(xmm_coeffs[1], xmm2); + movdqu_m2r(xmm_hist[0], xmm1); + movdqu_m2r(xmm_hist[1], xmm3); + xmm_coeffs += 2; + xmm_hist += 2; + pmaddwd_r2r(xmm1, xmm0); + pmaddwd_r2r(xmm3, xmm2); + paddd_r2r(xmm0, xmm4); + paddd_r2r(xmm2, xmm4); + i -= 16; + } + movdqa_r2r(xmm4, xmm0); + psrldq_i2r(8, xmm0); + paddd_r2r(xmm0, xmm4); + movdqa_r2r(xmm4, xmm0); + psrldq_i2r(4, xmm0); + paddd_r2r(xmm0, xmm4); + movd_r2m(xmm4, y); +#elif defined(__BLACKFIN_ASM__) + fir->history[fir->curr_pos] = sample; + fir->history[fir->curr_pos + fir->taps] = sample; + y = dot_asm((int16_t*)fir->coeffs, &fir->history[fir->curr_pos], fir->taps); +#else + int i; + int offset1; + int offset2; + + fir->history[fir->curr_pos] = sample; + + offset2 = fir->curr_pos; + offset1 = fir->taps - offset2; + y = 0; + for (i = fir->taps - 1; i >= offset1; i--) + y += fir->coeffs[i]*fir->history[i - offset1]; + for ( ; i >= 0; i--) + y += fir->coeffs[i]*fir->history[i + offset2]; +#endif + if (fir->curr_pos <= 0) + fir->curr_pos = fir->taps; + fir->curr_pos--; + return (int16_t) (y >> 15); +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ const int16_t *fir32_create(fir32_state_t *fir, + const int32_t *coeffs, + int taps) +{ + fir->taps = taps; + fir->curr_pos = taps - 1; + fir->coeffs = coeffs; + fir->history = (int16_t *) malloc(taps*sizeof(int16_t)); + if (fir->history) + memset(fir->history, '\0', taps*sizeof(int16_t)); + return fir->history; +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ void fir32_flush(fir32_state_t *fir) +{ + memset(fir->history, 0, fir->taps*sizeof(int16_t)); +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ void fir32_free(fir32_state_t *fir) +{ + free(fir->history); +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ int16_t fir32(fir32_state_t *fir, int16_t sample) +{ + int i; + int32_t y; + int offset1; + int offset2; + + fir->history[fir->curr_pos] = sample; + offset2 = fir->curr_pos; + offset1 = fir->taps - offset2; + y = 0; + for (i = fir->taps - 1; i >= offset1; i--) + y += fir->coeffs[i]*fir->history[i - offset1]; + for ( ; i >= 0; i--) + y += fir->coeffs[i]*fir->history[i + offset2]; + if (fir->curr_pos <= 0) + fir->curr_pos = fir->taps; + fir->curr_pos--; + return (int16_t) (y >> 15); +} +/*- End of function --------------------------------------------------------*/ + +#ifndef __KERNEL__ +static __inline__ const float *fir_float_create(fir_float_state_t *fir, + const float *coeffs, + int taps) +{ + fir->taps = taps; + fir->curr_pos = taps - 1; + fir->coeffs = coeffs; + fir->history = (float *) malloc(taps*sizeof(float)); + if (fir->history) + memset(fir->history, '\0', taps*sizeof(float)); + return fir->history; +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ void fir_float_free(fir_float_state_t *fir) +{ + free(fir->history); +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ int16_t fir_float(fir_float_state_t *fir, int16_t sample) +{ + int i; + float y; + int offset1; + int offset2; + + fir->history[fir->curr_pos] = sample; + + offset2 = fir->curr_pos; + offset1 = fir->taps - offset2; + y = 0; + for (i = fir->taps - 1; i >= offset1; i--) + y += fir->coeffs[i]*fir->history[i - offset1]; + for ( ; i >= 0; i--) + y += fir->coeffs[i]*fir->history[i + offset2]; + if (fir->curr_pos <= 0) + fir->curr_pos = fir->taps; + fir->curr_pos--; + return (int16_t) y; +} +/*- End of function --------------------------------------------------------*/ +#endif + +#ifdef __cplusplus +} +#endif + +#endif +/*- End of file ------------------------------------------------------------*/ diff -ruN mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/oslec.h mISDN-1_1_5/drivers/isdn/hardware/mISDN/oslec.h --- mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/oslec.h 1970-01-01 09:30:00.000000000 +0930 +++ mISDN-1_1_5/drivers/isdn/hardware/mISDN/oslec.h 2007-09-13 08:46:20.000000000 +0930 @@ -0,0 +1,47 @@ +/* + oslec.h + David Rowe + 7 Feb 2007 + + Interface for OSLEC module. +*/ + +/* + Copyright (C) 2007 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2, as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __OSLEC__ +#define __OSLEC__ + + +#define EC_TYPE "OSLEC" + +struct echo_can_state { + void *ec; +}; + +struct echo_can_state *oslec_echo_can_create(int len, int adaption_mode); +void oslec_echo_can_free(struct echo_can_state *ec); +short oslec_echo_can_update(struct echo_can_state *ec, short iref, short isig); +int oslec_echo_can_traintap(struct echo_can_state *ec, int pos, short val); +static inline void echo_can_init(void) {} +static inline void echo_can_shutdown(void) {} +short oslec_hpf_tx(struct echo_can_state *ec, short txlin); + +#endif + diff -ruN mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/oslec_wrap.c mISDN-1_1_5/drivers/isdn/hardware/mISDN/oslec_wrap.c --- mISDN-1_1_5-orig/drivers/isdn/hardware/mISDN/oslec_wrap.c 1970-01-01 09:30:00.000000000 +0930 +++ mISDN-1_1_5/drivers/isdn/hardware/mISDN/oslec_wrap.c 2007-09-13 08:46:20.000000000 +0930 @@ -0,0 +1,115 @@ +/* + oslec_wrap.c + David Rowe + 7 Feb 2007 + + Wrapper for OSLEC to turn it into a kernel module compatable with Zaptel. + + patched to make it work with mISDN + Peter Schlaile + 23 Aug 2007 + + The /proc/oslec interface points to the first echo canceller + instance created. Zaptel appears to create/destroy e/c on a call by + call basis, and with the current echo can function interface it is + difficult to tell which channel is assigned to which e/c. So to + simply the /proc interface (at least in this first implementation) + we limit it to the first echo canceller created. + + So if you only have one call up on a system, /proc/oslec will refer + to that. That should be sufficient for debugging the echo canceller + algorithm, we can extend it to handle multiple simultaneous channels + later. +*/ + +/* + Copyright (C) 2007 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2, as + published by the Free Software Foundation. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include + + +#define malloc(a) kmalloc((a), GFP_KERNEL) +#define free(a) kfree(a) + +#include "oslec.h" +#include "oslec_echo.h" + +/* constants for isr cycle averaging */ + +#define LTC 5 /* base 2 log of TC */ + +/* number of cycles we are using per call */ + +/* We need this lock as multiple threads may try to manipulate + the globals used for diagnostics at the same time */ + +DECLARE_MUTEX(oslec_lock); + +struct echo_can_state *oslec_echo_can_create(int len, int adaption_mode) { + struct echo_can_state *ec; + + down(&oslec_lock); + + ec = (struct echo_can_state *)malloc(sizeof(struct echo_can_state)); + ec->ec = (void*)echo_can_create(len, ECHO_CAN_USE_ADAPTION + | ECHO_CAN_USE_NLP + | ECHO_CAN_USE_CLIP + | ECHO_CAN_USE_TX_HPF + | ECHO_CAN_USE_RX_HPF); + + up(&oslec_lock); + + return ec; +} + +void oslec_echo_can_free(struct echo_can_state *ec) { + down(&oslec_lock); + + echo_can_free((echo_can_state_t*)(ec->ec)); + free(ec); + + up(&oslec_lock); +} + +short oslec_echo_can_update(struct echo_can_state *ec, short iref, short isig) { + short clean; + + clean = echo_can_update((echo_can_state_t*)(ec->ec), iref, isig); + + /* + Simple IIR averager: + + -LTC -LTC + y(n) = (1 - 2 )y(n-1) + 2 x(n) + + */ + + return clean; +} + +int oslec_echo_can_traintap(struct echo_can_state *ec, int pos, short val) +{ + return 0; +} + Binary files mISDN-1_1_5-orig/misdn_impulse_ec.raw and mISDN-1_1_5/misdn_impulse_ec.raw differ Binary files mISDN-1_1_5-orig/misdn_impulse_rx.raw and mISDN-1_1_5/misdn_impulse_rx.raw differ Binary files mISDN-1_1_5-orig/misdn_impulse_tx.raw and mISDN-1_1_5/misdn_impulse_tx.raw differ