| /* | 
 |  * Audio support data for ISDN4Linux. | 
 |  * | 
 |  * Copyright 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. | 
 |  * | 
 |  */ | 
 |  | 
 | #include <linux/gfp.h> | 
 | #include <linux/mISDNif.h> | 
 | #include <linux/mISDNdsp.h> | 
 | #include "core.h" | 
 | #include "dsp.h" | 
 |  | 
 |  | 
 | #define DATA_S sample_silence | 
 | #define SIZE_S (&sizeof_silence) | 
 | #define DATA_GA sample_german_all | 
 | #define SIZE_GA (&sizeof_german_all) | 
 | #define DATA_GO sample_german_old | 
 | #define SIZE_GO (&sizeof_german_old) | 
 | #define DATA_DT sample_american_dialtone | 
 | #define SIZE_DT (&sizeof_american_dialtone) | 
 | #define DATA_RI sample_american_ringing | 
 | #define SIZE_RI (&sizeof_american_ringing) | 
 | #define DATA_BU sample_american_busy | 
 | #define SIZE_BU (&sizeof_american_busy) | 
 | #define DATA_S1 sample_special1 | 
 | #define SIZE_S1 (&sizeof_special1) | 
 | #define DATA_S2 sample_special2 | 
 | #define SIZE_S2 (&sizeof_special2) | 
 | #define DATA_S3 sample_special3 | 
 | #define SIZE_S3 (&sizeof_special3) | 
 |  | 
 | /***************/ | 
 | /* tones loops */ | 
 | /***************/ | 
 |  | 
 | /* all tones are alaw encoded */ | 
 | /* the last sample+1 is in phase with the first sample. the error is low */ | 
 |  | 
 | static u8 sample_german_all[] = { | 
 | 	0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, | 
 | 	0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, | 
 | 	0xdc, 0xfc, 0x6c, | 
 | 	0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, | 
 | 	0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, | 
 | 	0xdc, 0xfc, 0x6c, | 
 | 	0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, | 
 | 	0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, | 
 | 	0xdc, 0xfc, 0x6c, | 
 | 	0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, | 
 | 	0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, | 
 | 	0xdc, 0xfc, 0x6c, | 
 | }; | 
 | static u32 sizeof_german_all = sizeof(sample_german_all); | 
 |  | 
 | static u8 sample_german_old[] = { | 
 | 	0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, | 
 | 	0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, | 
 | 	0x8c, | 
 | 	0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, | 
 | 	0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, | 
 | 	0x8c, | 
 | 	0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, | 
 | 	0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, | 
 | 	0x8c, | 
 | 	0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, | 
 | 	0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, | 
 | 	0x8c, | 
 | }; | 
 | static u32 sizeof_german_old = sizeof(sample_german_old); | 
 |  | 
 | static u8 sample_american_dialtone[] = { | 
 | 	0x2a, 0x18, 0x90, 0x6c, 0x4c, 0xbc, 0x4c, 0x6c, | 
 | 	0x10, 0x58, 0x32, 0xb9, 0x31, 0x2d, 0x8d, 0x0d, | 
 | 	0x8d, 0x2d, 0x31, 0x99, 0x0f, 0x28, 0x60, 0xf0, | 
 | 	0xd0, 0x50, 0xd0, 0x30, 0x60, 0x08, 0x8e, 0x67, | 
 | 	0x09, 0x19, 0x21, 0xe1, 0xd9, 0xb9, 0x29, 0x67, | 
 | 	0x83, 0x02, 0xce, 0xbe, 0xee, 0x1a, 0x1b, 0xef, | 
 | 	0xbf, 0xcf, 0x03, 0x82, 0x66, 0x28, 0xb8, 0xd8, | 
 | 	0xe0, 0x20, 0x18, 0x08, 0x66, 0x8f, 0x09, 0x61, | 
 | 	0x31, 0xd1, 0x51, 0xd1, 0xf1, 0x61, 0x29, 0x0e, | 
 | 	0x98, 0x30, 0x2c, 0x8c, 0x0c, 0x8c, 0x2c, 0x30, | 
 | 	0xb8, 0x33, 0x59, 0x11, 0x6d, 0x4d, 0xbd, 0x4d, | 
 | 	0x6d, 0x91, 0x19, | 
 | }; | 
 | static u32 sizeof_american_dialtone = sizeof(sample_american_dialtone); | 
 |  | 
 | static u8 sample_american_ringing[] = { | 
 | 	0x2a, 0xe0, 0xac, 0x0c, 0xbc, 0x4c, 0x8c, 0x90, | 
 | 	0x48, 0xc7, 0xc1, 0xed, 0xcd, 0x4d, 0xcd, 0xed, | 
 | 	0xc1, 0xb7, 0x08, 0x30, 0xec, 0xcc, 0xcc, 0x8c, | 
 | 	0x10, 0x58, 0x1a, 0x99, 0x71, 0xed, 0x8d, 0x8d, | 
 | 	0x2d, 0x41, 0x89, 0x9e, 0x20, 0x70, 0x2c, 0xec, | 
 | 	0x2c, 0x70, 0x20, 0x86, 0x77, 0xe1, 0x31, 0x11, | 
 | 	0xd1, 0xf1, 0x81, 0x09, 0xa3, 0x56, 0x58, 0x00, | 
 | 	0x40, 0xc0, 0x60, 0x38, 0x46, 0x43, 0x57, 0x39, | 
 | 	0xd9, 0x59, 0x99, 0xc9, 0x77, 0x2f, 0x2e, 0xc6, | 
 | 	0xd6, 0x28, 0xd6, 0x36, 0x26, 0x2e, 0x8a, 0xa3, | 
 | 	0x43, 0x63, 0x4b, 0x4a, 0x62, 0x42, 0xa2, 0x8b, | 
 | 	0x2f, 0x27, 0x37, 0xd7, 0x29, 0xd7, 0xc7, 0x2f, | 
 | 	0x2e, 0x76, 0xc8, 0x98, 0x58, 0xd8, 0x38, 0x56, | 
 | 	0x42, 0x47, 0x39, 0x61, 0xc1, 0x41, 0x01, 0x59, | 
 | 	0x57, 0xa2, 0x08, 0x80, 0xf0, 0xd0, 0x10, 0x30, | 
 | 	0xe0, 0x76, 0x87, 0x21, 0x71, 0x2d, 0xed, 0x2d, | 
 | 	0x71, 0x21, 0x9f, 0x88, 0x40, 0x2c, 0x8c, 0x8c, | 
 | 	0xec, 0x70, 0x98, 0x1b, 0x59, 0x11, 0x8d, 0xcd, | 
 | 	0xcd, 0xed, 0x31, 0x09, 0xb6, 0xc0, 0xec, 0xcc, | 
 | 	0x4c, 0xcc, 0xec, 0xc0, 0xc6, 0x49, 0x91, 0x8d, | 
 | 	0x4d, 0xbd, 0x0d, 0xad, 0xe1, | 
 | }; | 
 | static u32 sizeof_american_ringing = sizeof(sample_american_ringing); | 
 |  | 
 | static u8 sample_american_busy[] = { | 
 | 	0x2a, 0x00, 0x6c, 0x4c, 0x4c, 0x6c, 0xb0, 0x66, | 
 | 	0x99, 0x11, 0x6d, 0x8d, 0x2d, 0x41, 0xd7, 0x96, | 
 | 	0x60, 0xf0, 0x70, 0x40, 0x58, 0xf6, 0x53, 0x57, | 
 | 	0x09, 0x89, 0xd7, 0x5f, 0xe3, 0x2a, 0xe3, 0x5f, | 
 | 	0xd7, 0x89, 0x09, 0x57, 0x53, 0xf6, 0x58, 0x40, | 
 | 	0x70, 0xf0, 0x60, 0x96, 0xd7, 0x41, 0x2d, 0x8d, | 
 | 	0x6d, 0x11, 0x99, 0x66, 0xb0, 0x6c, 0x4c, 0x4c, | 
 | 	0x6c, 0x00, 0x2a, 0x01, 0x6d, 0x4d, 0x4d, 0x6d, | 
 | 	0xb1, 0x67, 0x98, 0x10, 0x6c, 0x8c, 0x2c, 0x40, | 
 | 	0xd6, 0x97, 0x61, 0xf1, 0x71, 0x41, 0x59, 0xf7, | 
 | 	0x52, 0x56, 0x08, 0x88, 0xd6, 0x5e, 0xe2, 0x2a, | 
 | 	0xe2, 0x5e, 0xd6, 0x88, 0x08, 0x56, 0x52, 0xf7, | 
 | 	0x59, 0x41, 0x71, 0xf1, 0x61, 0x97, 0xd6, 0x40, | 
 | 	0x2c, 0x8c, 0x6c, 0x10, 0x98, 0x67, 0xb1, 0x6d, | 
 | 	0x4d, 0x4d, 0x6d, 0x01, | 
 | }; | 
 | static u32 sizeof_american_busy = sizeof(sample_american_busy); | 
 |  | 
 | static u8 sample_special1[] = { | 
 | 	0x2a, 0x2c, 0xbc, 0x6c, 0xd6, 0x71, 0xbd, 0x0d, | 
 | 	0xd9, 0x80, 0xcc, 0x4c, 0x40, 0x39, 0x0d, 0xbd, | 
 | 	0x11, 0x86, 0xec, 0xbc, 0xec, 0x0e, 0x51, 0xbd, | 
 | 	0x8d, 0x89, 0x30, 0x4c, 0xcc, 0xe0, 0xe1, 0xcd, | 
 | 	0x4d, 0x31, 0x88, 0x8c, 0xbc, 0x50, 0x0f, 0xed, | 
 | 	0xbd, 0xed, 0x87, 0x10, 0xbc, 0x0c, 0x38, 0x41, | 
 | 	0x4d, 0xcd, 0x81, 0xd8, 0x0c, 0xbc, 0x70, 0xd7, | 
 | 	0x6d, 0xbd, 0x2d, | 
 | }; | 
 | static u32 sizeof_special1 = sizeof(sample_special1); | 
 |  | 
 | static u8 sample_special2[] = { | 
 | 	0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc, | 
 | 	0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d, | 
 | 	0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6, | 
 | 	0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0, | 
 | 	0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd, | 
 | 	0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc, | 
 | 	0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d, | 
 | 	0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6, | 
 | 	0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0, | 
 | 	0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd, | 
 | }; | 
 | static u32 sizeof_special2 = sizeof(sample_special2); | 
 |  | 
 | static u8 sample_special3[] = { | 
 | 	0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1, | 
 | 	0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c, | 
 | 	0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc, | 
 | 	0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7, | 
 | 	0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd, | 
 | 	0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1, | 
 | 	0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c, | 
 | 	0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc, | 
 | 	0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7, | 
 | 	0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd, | 
 | }; | 
 | static u32 sizeof_special3 = sizeof(sample_special3); | 
 |  | 
 | static u8 sample_silence[] = { | 
 | 	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | 
 | 	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | 
 | 	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | 
 | 	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | 
 | 	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | 
 | 	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | 
 | 	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | 
 | 	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | 
 | 	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | 
 | 	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | 
 | 	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | 
 | 	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, | 
 | }; | 
 | static u32 sizeof_silence = sizeof(sample_silence); | 
 |  | 
 | struct tones_samples { | 
 | 	u32 *len; | 
 | 	u8 *data; | 
 | }; | 
 | static struct | 
 | tones_samples samples[] = { | 
 | 	{&sizeof_german_all, sample_german_all}, | 
 | 	{&sizeof_german_old, sample_german_old}, | 
 | 	{&sizeof_american_dialtone, sample_american_dialtone}, | 
 | 	{&sizeof_american_ringing, sample_american_ringing}, | 
 | 	{&sizeof_american_busy, sample_american_busy}, | 
 | 	{&sizeof_special1, sample_special1}, | 
 | 	{&sizeof_special2, sample_special2}, | 
 | 	{&sizeof_special3, sample_special3}, | 
 | 	{NULL, NULL}, | 
 | }; | 
 |  | 
 | /*********************************** | 
 |  * generate ulaw from alaw samples * | 
 |  ***********************************/ | 
 |  | 
 | void | 
 | dsp_audio_generate_ulaw_samples(void) | 
 | { | 
 | 	int i, j; | 
 |  | 
 | 	i = 0; | 
 | 	while (samples[i].len) { | 
 | 		j = 0; | 
 | 		while (j < (*samples[i].len)) { | 
 | 			samples[i].data[j] = | 
 | 				dsp_audio_alaw_to_ulaw[samples[i].data[j]]; | 
 | 			j++; | 
 | 		} | 
 | 		i++; | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | /**************************** | 
 |  * tone sequence definition * | 
 |  ****************************/ | 
 |  | 
 | static struct pattern { | 
 | 	int tone; | 
 | 	u8 *data[10]; | 
 | 	u32 *siz[10]; | 
 | 	u32 seq[10]; | 
 | } pattern[] = { | 
 | 	{TONE_GERMAN_DIALTONE, | 
 | 	{DATA_GA, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{SIZE_GA, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{1900, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_GERMAN_OLDDIALTONE, | 
 | 	{DATA_GO, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{SIZE_GO, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{1998, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_AMERICAN_DIALTONE, | 
 | 	{DATA_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{SIZE_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_GERMAN_DIALPBX, | 
 | 	{DATA_GA, DATA_S, DATA_GA, DATA_S, DATA_GA, DATA_S, NULL, NULL, NULL, | 
 | 		NULL}, | 
 | 	{SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, NULL, NULL, NULL, | 
 | 		NULL}, | 
 | 	{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_GERMAN_OLDDIALPBX, | 
 | 	{DATA_GO, DATA_S, DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, | 
 | 		NULL}, | 
 | 	{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, | 
 | 		NULL}, | 
 | 	{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_AMERICAN_DIALPBX, | 
 | 	{DATA_DT, DATA_S, DATA_DT, DATA_S, DATA_DT, DATA_S, NULL, NULL, NULL, | 
 | 		NULL}, | 
 | 	{SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, NULL, NULL, NULL, | 
 | 		NULL}, | 
 | 	{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_GERMAN_RINGING, | 
 | 	{DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_GERMAN_OLDRINGING, | 
 | 	{DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{8000, 40000, 0, 0, 0, 0, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_AMERICAN_RINGING, | 
 | 	{DATA_RI, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{SIZE_RI, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_GERMAN_RINGPBX, | 
 | 	{DATA_GA, DATA_S, DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_GERMAN_OLDRINGPBX, | 
 | 	{DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_AMERICAN_RINGPBX, | 
 | 	{DATA_RI, DATA_S, DATA_RI, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{SIZE_RI, SIZE_S, SIZE_RI, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_GERMAN_BUSY, | 
 | 	{DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_GERMAN_OLDBUSY, | 
 | 	{DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_AMERICAN_BUSY, | 
 | 	{DATA_BU, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{SIZE_BU, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_GERMAN_HANGUP, | 
 | 	{DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_GERMAN_OLDHANGUP, | 
 | 	{DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_AMERICAN_HANGUP, | 
 | 	{DATA_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{SIZE_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_SPECIAL_INFO, | 
 | 	{DATA_S1, DATA_S2, DATA_S3, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{SIZE_S1, SIZE_S2, SIZE_S3, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{2666, 2666, 2666, 8002, 0, 0, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_GERMAN_GASSENBESETZT, | 
 | 	{DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{2000, 2000, 0, 0, 0, 0, 0, 0, 0, 0} }, | 
 |  | 
 | 	{TONE_GERMAN_AUFSCHALTTON, | 
 | 	{DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{1000, 5000, 1000, 17000, 0, 0, 0, 0, 0, 0} }, | 
 |  | 
 | 	{0, | 
 | 	{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | 
 | 	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, | 
 | }; | 
 |  | 
 | /****************** | 
 |  * copy tone data * | 
 |  ******************/ | 
 |  | 
 | /* an sk_buff is generated from the number of samples needed. | 
 |  * the count will be changed and may begin from 0 each pattern period. | 
 |  * the clue is to precalculate the pointers and legths to use only one | 
 |  * memcpy per function call, or two memcpy if the tone sequence changes. | 
 |  * | 
 |  * pattern - the type of the pattern | 
 |  * count - the sample from the beginning of the pattern (phase) | 
 |  * len - the number of bytes | 
 |  * | 
 |  * return - the sk_buff with the sample | 
 |  * | 
 |  * if tones has finished (e.g. knocking tone), dsp->tones is turned off | 
 |  */ | 
 | void dsp_tone_copy(struct dsp *dsp, u8 *data, int len) | 
 | { | 
 | 	int index, count, start, num; | 
 | 	struct pattern *pat; | 
 | 	struct dsp_tone *tone = &dsp->tone; | 
 |  | 
 | 	/* if we have no tone, we copy silence */ | 
 | 	if (!tone->tone) { | 
 | 		memset(data, dsp_silence, len); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* process pattern */ | 
 | 	pat = (struct pattern *)tone->pattern; | 
 | 		/* points to the current pattern */ | 
 | 	index = tone->index; /* gives current sequence index */ | 
 | 	count = tone->count; /* gives current sample */ | 
 |  | 
 | 	/* copy sample */ | 
 | 	while (len) { | 
 | 		/* find sample to start with */ | 
 | 		while (42) { | 
 | 			/* wrap around */ | 
 | 			if (!pat->seq[index]) { | 
 | 				count = 0; | 
 | 				index = 0; | 
 | 			} | 
 | 			/* check if we are currently playing this tone */ | 
 | 			if (count < pat->seq[index]) | 
 | 				break; | 
 | 			if (dsp_debug & DEBUG_DSP_TONE) | 
 | 				printk(KERN_DEBUG "%s: reaching next sequence " | 
 | 					"(index=%d)\n", __func__, index); | 
 | 			count -= pat->seq[index]; | 
 | 			index++; | 
 | 		} | 
 | 		/* calculate start and number of samples */ | 
 | 		start = count % (*(pat->siz[index])); | 
 | 		num = len; | 
 | 		if (num+count > pat->seq[index]) | 
 | 			num = pat->seq[index] - count; | 
 | 		if (num+start > (*(pat->siz[index]))) | 
 | 			num = (*(pat->siz[index])) - start; | 
 | 		/* copy memory */ | 
 | 		memcpy(data, pat->data[index]+start, num); | 
 | 		/* reduce length */ | 
 | 		data += num; | 
 | 		count += num; | 
 | 		len -= num; | 
 | 	} | 
 | 	tone->index = index; | 
 | 	tone->count = count; | 
 |  | 
 | 	/* return sk_buff */ | 
 | 	return; | 
 | } | 
 |  | 
 |  | 
 | /******************************* | 
 |  * send HW message to hfc card * | 
 |  *******************************/ | 
 |  | 
 | static void | 
 | dsp_tone_hw_message(struct dsp *dsp, u8 *sample, int len) | 
 | { | 
 | 	struct sk_buff *nskb; | 
 |  | 
 | 	/* unlocking is not required, because we don't expect a response */ | 
 | 	nskb = _alloc_mISDN_skb(PH_CONTROL_REQ, | 
 | 		(len) ? HFC_SPL_LOOP_ON : HFC_SPL_LOOP_OFF, len, sample, | 
 | 		GFP_ATOMIC); | 
 | 	if (nskb) { | 
 | 		if (dsp->ch.peer) { | 
 | 			if (dsp->ch.recv(dsp->ch.peer, nskb)) | 
 | 				dev_kfree_skb(nskb); | 
 | 		} else | 
 | 			dev_kfree_skb(nskb); | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | /***************** | 
 |  * timer expires * | 
 |  *****************/ | 
 | void | 
 | dsp_tone_timeout(void *arg) | 
 | { | 
 | 	struct dsp *dsp = arg; | 
 | 	struct dsp_tone *tone = &dsp->tone; | 
 | 	struct pattern *pat = (struct pattern *)tone->pattern; | 
 | 	int index = tone->index; | 
 |  | 
 | 	if (!tone->tone) | 
 | 		return; | 
 |  | 
 | 	index++; | 
 | 	if (!pat->seq[index]) | 
 | 		index = 0; | 
 | 	tone->index = index; | 
 |  | 
 | 	/* set next tone */ | 
 | 	if (pat->data[index] == DATA_S) | 
 | 		dsp_tone_hw_message(dsp, NULL, 0); | 
 | 	else | 
 | 		dsp_tone_hw_message(dsp, pat->data[index], *(pat->siz[index])); | 
 | 	/* set timer */ | 
 | 	init_timer(&tone->tl); | 
 | 	tone->tl.expires = jiffies + (pat->seq[index] * HZ) / 8000; | 
 | 	add_timer(&tone->tl); | 
 | } | 
 |  | 
 |  | 
 | /******************** | 
 |  * set/release tone * | 
 |  ********************/ | 
 |  | 
 | /* | 
 |  * tones are relaized by streaming or by special loop commands if supported | 
 |  * by hardware. when hardware is used, the patterns will be controlled by | 
 |  * timers. | 
 |  */ | 
 | int | 
 | dsp_tone(struct dsp *dsp, int tone) | 
 | { | 
 | 	struct pattern *pat; | 
 | 	int i; | 
 | 	struct dsp_tone *tonet = &dsp->tone; | 
 |  | 
 | 	tonet->software = 0; | 
 | 	tonet->hardware = 0; | 
 |  | 
 | 	/* we turn off the tone */ | 
 | 	if (!tone) { | 
 | 		if (dsp->features.hfc_loops && timer_pending(&tonet->tl)) | 
 | 			del_timer(&tonet->tl); | 
 | 		if (dsp->features.hfc_loops) | 
 | 			dsp_tone_hw_message(dsp, NULL, 0); | 
 | 		tonet->tone = 0; | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	pat = NULL; | 
 | 	i = 0; | 
 | 	while (pattern[i].tone) { | 
 | 		if (pattern[i].tone == tone) { | 
 | 			pat = &pattern[i]; | 
 | 			break; | 
 | 		} | 
 | 		i++; | 
 | 	} | 
 | 	if (!pat) { | 
 | 		printk(KERN_WARNING "dsp: given tone 0x%x is invalid\n", tone); | 
 | 		return -EINVAL; | 
 | 	} | 
 | 	if (dsp_debug & DEBUG_DSP_TONE) | 
 | 		printk(KERN_DEBUG "%s: now starting tone %d (index=%d)\n", | 
 | 			__func__, tone, 0); | 
 | 	tonet->tone = tone; | 
 | 	tonet->pattern = pat; | 
 | 	tonet->index = 0; | 
 | 	tonet->count = 0; | 
 |  | 
 | 	if (dsp->features.hfc_loops) { | 
 | 		tonet->hardware = 1; | 
 | 		/* set first tone */ | 
 | 		dsp_tone_hw_message(dsp, pat->data[0], *(pat->siz[0])); | 
 | 		/* set timer */ | 
 | 		if (timer_pending(&tonet->tl)) | 
 | 			del_timer(&tonet->tl); | 
 | 		init_timer(&tonet->tl); | 
 | 		tonet->tl.expires = jiffies + (pat->seq[0] * HZ) / 8000; | 
 | 		add_timer(&tonet->tl); | 
 | 	} else { | 
 | 		tonet->software = 1; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 |  | 
 |  | 
 |  |