diff options
-rwxr-xr-x | gendigits.c | 172 | ||||
-rwxr-xr-x | tonezone.c | 412 | ||||
-rwxr-xr-x | tonezone.h | 79 | ||||
-rwxr-xr-x | zonedata.c | 40 |
4 files changed, 703 insertions, 0 deletions
diff --git a/gendigits.c b/gendigits.c new file mode 100755 index 0000000..d1847cb --- /dev/null +++ b/gendigits.c @@ -0,0 +1,172 @@ +/* Generate a header file for a particular + single or double frequency */ + +#include <stdio.h> +#include <math.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#define CLIP 32635 +#define BIAS 0x84 + +/* Dial frequency tables */ +typedef struct +{ +char chr; /* character representation */ +float f1; /* first freq */ +float f2; /* second freq */ +} ZAP_DIAL; + +ZAP_DIAL dtmf_dial[] = { +{ '1',697.0,1209.0 }, +{ '4',770.0,1209.0 }, +{ '7',852.0,1209.0 }, +{ '*',941.0,1209.0 }, +{ '2',697.0,1336.0 }, +{ '5',770.0,1336.0 }, +{ '8',852.0,1336.0 }, +{ '0',941.0,1336.0 }, +{ '3',697.0,1477.0 }, +{ '6',770.0,1477.0 }, +{ '9',852.0,1477.0 }, +{ '#',941.0,1477.0 }, +{ 'A',697.0,1633.0 }, +{ 'B',770.0,1633.0 }, +{ 'C',852.0,1633.0 }, +{ 'D',941.0,1633.0 }, +{ 0,0,0 } +} ; + +ZAP_DIAL mf_dial[] = { +{ '1',700.0,900.0 }, +{ '2',700.0,1100.0 }, +{ '3',900.0,1100.0 }, +{ '4',700.0,1300.0 }, +{ '5',900.0,1300.0 }, +{ '6',1100.0,1300.0 }, +{ '7',700.0,1500.0 }, +{ '8',900.0,1500.0 }, +{ '9',1100.0,1500.0 }, +{ '0',1300.0,1500.0 }, +{ '*',1100.0,1700.0 }, /* KP */ +{ '#',1500.0,1700.0 }, /* ST */ +{ 'A',900.0,1700.0 }, /* ST' */ +{ 'B',1300.0,1700.0}, /* ST'' */ +{ 'C',700.0,1700.0}, /* ST''' */ +{ 0,0,0 } +} ; + +static float loudness=8192.0; + +unsigned char +linear2ulaw(sample) +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 */ + if (sample > CLIP) sample = CLIP; /* clip the 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); +#ifdef ZEROTRAP + if (ulawbyte == 0) ulawbyte = 0x02; /* optional CCITT trap */ +#endif + + return(ulawbyte); +} + +static int calc_samples(int freq) +{ + int x, samples; + /* Calculate the number of samples at 8000hz sampling + we need to have this wave form */ + samples = 8000; + /* Take out common 2's up to six times */ + for (x=0;x<6;x++) + if (!(freq % 2)) { + freq /= 2; + samples /= 2; + } + /* Take out common 5's (up to three times */ + for (x=0;x<3;x++) + if (!(freq % 5)) { + freq /= 5; + samples /=5; + } + /* No more common factors. */ + return samples; +} + +int process(FILE *f, char *label, ZAP_DIAL z[]) +{ + char c; + int x, samples, samples1, samples2; + float val; + while(z->chr) { + c = z->chr; + if (c == '*') + c = 's'; + if (c == '#') + c = 'p'; + samples1 = calc_samples((int)z->f1); + samples2 = calc_samples((int)z->f2); + samples = samples1; + while(samples % samples2) + samples += samples1; + printf("Need %d samples for %s_%c\n", samples, label, c); + fprintf(f, "static unsigned char %s_%c[%d] = {\n\t", label, c, samples); + for (x=0;x<samples;x++) { + val = loudness * sin((z->f1 * 2.0 * M_PI * x)/8000.0); + val += loudness * sin((z->f2 * 2.0 * M_PI * x)/8000.0); + fprintf(f, "%3d, ", linear2ulaw((int)val)); + if (!((x+1) % 15)) + fprintf(f, "\n\t"); + } + if (x % 15) + fprintf(f, "\n"); + fprintf(f, "};\n\n"); + z++; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + FILE *f; + + if ((f = fopen("tones.h", "w"))) { + fprintf(f, "/* DTMF and MF tones used by the Tormenta Driver, in static tables.\n" + " Generated automatically from gendigits. Do not edit by hand. */\n"); + process(f, "dtmf", dtmf_dial); + process(f, "mfv1", mf_dial); + fprintf(f, "/* END tones.h */\n"); + fclose(f); + } else { + fprintf(stderr, "Unable to open tones.h for writing\n"); + return 1; + } + + return 0; +} diff --git a/tonezone.c b/tonezone.c new file mode 100755 index 0000000..e601de9 --- /dev/null +++ b/tonezone.c @@ -0,0 +1,412 @@ +/* + * BSD Telephony Of Mexico "Tormenta" Tone Zone Support 2/22/01 + * + * Working with the "Tormenta ISA" Card + * + * This program is free software; you can redistribute it and/or modify + * it under thet erms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Lesser 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. + * + * Primary Author: Mark Spencer <markster@linux-support.net> + * + */ + +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include "tonezone.h" + +#define DEFAULT_ZT_DEV "/dev/zap/ctl" + +#define MAX_SIZE 16384 +#define CLIP 32635 +#define BIAS 0x84 + +static float loudness=8192.0; + +unsigned char +linear2ulaw(sample) +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 */ + if (sample > CLIP) sample = CLIP; /* clip the 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); +#ifdef ZEROTRAP + if (ulawbyte == 0) ulawbyte = 0x02; /* optional CCITT trap */ +#endif + + return(ulawbyte); +} + + + +static int calc_samples(int freq) +{ + int x, samples; + /* Calculate the number of samples at 8000hz sampling + we need to have this wave form */ + samples = 8000; + /* Take out common 2's up to six times */ + for (x=0;x<6;x++) + if (!(freq % 2)) { + freq /= 2; + samples /= 2; + } + /* Take out common 5's (up to three times */ + for (x=0;x<3;x++) + if (!(freq % 5)) { + freq /= 5; + samples /=5; + } + /* No more common factors. */ + return samples; +} + + + +struct tone_zone *tone_zone_find(char *country) +{ + struct tone_zone *z; + z = builtin_zones; + while(z->zone > -1) { + if (!strcasecmp(country, z->country)) + return z; + z++; + } + return NULL; +} + +struct tone_zone *tone_zone_find_by_num(int id) +{ + struct tone_zone *z; + z = builtin_zones; + while(z->zone > -1) { + if (z->zone == id) + return z; + z++; + } + return NULL; +} + +static int build_tone(char *data, int size, struct tone_zone_sound *t, int *count) +{ + char *dup, *s; + struct zt_tone_def *td=NULL; + int firstnobang = -1; + int freq1, freq2, time; + int samples, samples1, samples2; + int x; + int used = 0; + float val; + dup = strdup(t->data); + s = strtok(dup, ","); + while(s && strlen(s)) { + /* Handle optional ! which signifies don't start here*/ + if (s[0] == '!') + s++; + else if (firstnobang < 0) { +#if 0 + printf("First no bang: %s\n", s); +#endif + firstnobang = *count; + } + if (sscanf(s, "%d+%d/%d", &freq1, &freq2, &time) == 3) { + /* f1+f2/time format */ +#if 0 + printf("f1+f2/time format: %d, %d, %d\n", freq1, freq2, time); +#endif + } else if (sscanf(s, "%d+%d", &freq1, &freq2) == 2) { +#if 0 + printf("f1+f2 format: %d, %d\n", freq1, freq2); +#endif + time = 0; + } else if (sscanf(s, "%d/%d", &freq1, &time) == 2) { +#if 0 + printf("f1/time format: %d, %d\n", freq1, time); +#endif + freq2 = 0; + } else if (sscanf(s, "%d", &freq1) == 1) { +#if 0 + printf("f1 format: %d\n", freq1); +#endif + firstnobang = *count; + freq2 = 0; + time = 0; + } else { + fprintf(stderr, "tone component '%s' of '%s' is a syntax error\n", s,t->data); + return -1; + } + if (freq1) + samples1 = calc_samples(freq1); + else + samples1 = 40; + if (freq2) + samples2 = calc_samples(freq2); + else + samples2 = 0; + samples = samples1; + if (freq2) { + while(samples % samples2) + samples += samples1; + } +#if 0 + printf("Using %d samples for %d and %d\n", samples, freq1, freq2); +#endif + if (size < samples + sizeof(struct zt_tone_def)) { + fprintf(stderr, "Not enough space for samples\n"); + return -1; + } + td = (struct zt_tone_def *)data; + data += (sizeof(struct zt_tone_def) + samples); + used += (sizeof(struct zt_tone_def) + samples); + size -= (sizeof(struct zt_tone_def) + samples); + td->size = samples; + td->tone = t->toneid; + if (time) { + /* We should move to the next tone */ + td->next = *count + 1; + td->samples = time * 8; + } else { + /* Stay with us */ + td->next = *count; + td->samples = samples; + } + for (x=0;x<samples;x++) { + val = 0.0; + if (freq1) + val = loudness * sin((freq1 * 2.0 * M_PI * x)/8000.0); + if (freq2) + val += loudness * sin((freq2 * 2.0 * M_PI * x)/8000.0); + td->data[x] = linear2ulaw((int)val); + } + (*count)++; + s = strtok(NULL, ","); + } + if (td && time) { + /* If we don't end on a solid tone, return */ + td->next = firstnobang; + } + return used; +} + +char *tone_zone_tone_name(int id) +{ + static char tmp[80]; + switch(id) { + case ZT_TONE_DIALTONE: + return "Dialtone"; + case ZT_TONE_BUSY: + return "Busy"; + case ZT_TONE_RINGTONE: + return "Ringtone"; + case ZT_TONE_CONGESTION: + return "Congestion"; + case ZT_TONE_CALLWAIT: + return "Call Waiting"; + case ZT_TONE_DIALRECALL: + return "Dial Recall"; + case ZT_TONE_RECORDTONE: + return "Record Tone"; + case ZT_TONE_CUST1: + return "Custom 1"; + case ZT_TONE_CUST2: + return "Custom 2"; + case ZT_TONE_INFO: + return "Special Information"; + default: + snprintf(tmp, sizeof(tmp), "Unknown tone %d", id); + return tmp; + } +} + +#ifdef TONEZONE_DRIVER +static void dump_tone_zone(void *data) +{ + struct zt_tone_def_header *z; + struct zt_tone_def *td; + int x; + int len=0; + z = data; + data += sizeof(*z); + printf("Header: %d tones, %d bytes of data, zone %d (%s)\n", + z->count, z->size, z->zone, z->name); + for (x=0;x < z->count; x++) { + td = data; + printf("Tone Fragment %d: %d bytes, %s tone, next is %d, %d samples total\n", + x, td->size, tone_name(td->tone), td->next, td->samples); + data += sizeof(*td); + data += td->size; + len += td->size; + } + printf("Total measured bytes of data: %d\n", len); +} +#endif + +int tone_zone_register_zone(int fd, struct tone_zone *z) +{ + char buf[MAX_SIZE]; + int res; + int count=0; + int x; + int used = 0; + int iopenedit = 0; + int space = MAX_SIZE; + char *ptr = buf; + struct zt_tone_def_header *h; + if (fd < 0) { + fd = open(DEFAULT_ZT_DEV, O_RDWR); + iopenedit=1; + if (fd < 0) { + fprintf(stderr, "Unable to open %s and fd not provided\n", DEFAULT_ZT_DEV); + return -1; + } + } + h = (struct zt_tone_def_header *)ptr; + ptr += sizeof(struct zt_tone_def_header); + space -= sizeof(struct zt_tone_def_header); + used += sizeof(struct zt_tone_def_header); + /* + * Fill in ring cadence + */ + for (x=0;x<ZT_MAX_CADENCE;x++) + h->ringcadence[x] = z->ringcadence[x]; + /* Put in an appropriate method for a kernel ioctl */ + for (x=0;x<ZT_TONE_MAX;x++) { + if (strlen(z->tones[x].data)) { + /* It's a real tone */ +#if 0 + printf("Tone: %d, string: %s\n", z->tones[x].toneid, z->tones[x].data); +#endif + res = build_tone(ptr, space, &z->tones[x], &count); + if (res < 0) { + fprintf(stderr, "Tone not built.\n"); + if (iopenedit) + close(fd); + return -1; + } + ptr += res; + used += res; + space -= res; + } + } + h->count = count; + h->size = used - sizeof(struct zt_tone_def_header) - count * sizeof(struct zt_tone_def); + h->zone = z->zone; + strncpy(h->name, z->description, sizeof(h->name)); + x = z->zone; + ioctl(fd, ZT_FREEZONE, &x); + res = ioctl(fd, ZT_LOADZONE, h); + if (res) + fprintf(stderr, "ioctl(ZT_LOADZONE) failed: %s\n", strerror(errno)); + if (iopenedit) + close(fd); + return res; +} + +int tone_zone_register(int fd, char *country) +{ + struct tone_zone *z; + z = tone_zone_find(country); + if (z) { + return tone_zone_register_zone(-1, z); + } else { + return -1; + } +} + + + +int tone_zone_set_zone(int fd, char *country) +{ + int res=-1; + struct tone_zone *z; + if (fd > -1) { + z = tone_zone_find(country); + if (z) + res = ioctl(fd, ZT_SETTONEZONE, &z->zone); + if ((res < 0) && (errno == ENODATA)) { + tone_zone_register_zone(fd, z); + res = ioctl(fd, ZT_SETTONEZONE, &z->zone); + } + } + return res; +} + +int tone_zone_get_zone(int fd) +{ + int x=-1; + if (fd > -1) { + ioctl(fd, ZT_GETTONEZONE, &x); + return x; + } + return -1; +} + +int tone_zone_play_tone(int fd, int tone) +{ + struct tone_zone *z; + int res = -1; + int zone; +#if 0 + fprintf(stderr, "Playing tone %d (%s) on %d\n", tone, tone_zone_tone_name(tone), fd); +#endif + if (fd > -1) { + res = ioctl(fd, ZT_SENDTONE, &tone); + if ((res < 0) && (errno == ENODATA)) { + ioctl(fd, ZT_GETTONEZONE, &zone); + z = tone_zone_find_by_num(zone); + if (z) { + res = tone_zone_register_zone(fd, z); + /* Recall the zone */ + ioctl(fd, ZT_SETTONEZONE, &zone); + if (res < 0) { + fprintf(stderr, "Failed to register zone '%s': %s\n", z->description, strerror(errno)); + } else { + res = ioctl(fd, ZT_SENDTONE, &tone); + } + } else + fprintf(stderr, "Don't know anything about zone %d\n", zone); + } + } + return res; +} diff --git a/tonezone.h b/tonezone.h new file mode 100755 index 0000000..cc3a248 --- /dev/null +++ b/tonezone.h @@ -0,0 +1,79 @@ +/* + * BSD Telephony Of Mexico "Tormenta" Tone Zone Support 2/22/01 + * + * Working with the "Tormenta ISA" Card + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Lesser 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. + * + * Primary Author: Mark Spencer <markster@linux-support.net> + * + */ + +#ifndef _TONEZONE_H +#define _TONEZONE_H +#ifdef BUILDING_TONEZONE +#include "zaptel.h" +#else +#include <linux/zaptel.h> +#endif + +struct tone_zone_sound { + int toneid; + char data[256]; /* Actual zone description */ + /* Description is a series of tones of the format: + [!]freq1[+freq2][/time] separated by commas. There + are no spaces. The sequence is repeated back to the + first tone description not preceeded by !. time is + specified in milliseconds */ +}; + +struct tone_zone { + int zone; /* Zone number */ + char country[10]; /* Country code */ + char description[40]; /* Description */ + int ringcadence[ZT_MAX_CADENCE]; /* Ring cadence */ + struct tone_zone_sound tones[ZT_TONE_MAX]; +}; + +extern struct tone_zone builtin_zones[]; + +/* Register a given two-letter tone zone if we can */ +extern int tone_zone_register(int fd, char *country); + +/* Register a given two-letter tone zone if we can */ +extern int tone_zone_register_zone(int fd, struct tone_zone *z); + +/* Retrieve a raw tone zone structure */ +extern struct tone_zone *tone_zone_find(char *country); + +/* Retrieve a raw tone zone structure by id instead of country*/ +extern struct tone_zone *tone_zone_find_by_num(int id); + +/* Retrieve a string name for a given tone id */ +extern char *tone_zone_tone_name(int id); + +/* Set a given file descriptor into a given country -- USE THIS + INTERFACE INSTEAD OF THE IOCTL ITSELF. Auto-loads tone + zone if necessary */ +extern int tone_zone_set_zone(int fd, char *country); + +/* Get the current tone zone */ +extern int tone_zone_get_zone(int fd); + +/* Play a given tone, loading tone zone automatically + if necessary */ +extern int tone_zone_play_tone(int fd, int toneid); + +#endif diff --git a/zonedata.c b/zonedata.c new file mode 100755 index 0000000..84104f0 --- /dev/null +++ b/zonedata.c @@ -0,0 +1,40 @@ +/* + * BSD Telephony Of Mexico "Tormenta" Tone Zone Support 2/22/01 + * + * Working with the "Tormenta ISA" Card + * + * This program is free software; you can redistribute it and/or modify + * it under thet erms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Lesser 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. + * + * Primary Author: Mark Spencer <markster@linux-support.net> + * + * This information from ITU E.180 Supplement 2. + */ +#include "tonezone.h" + +struct tone_zone builtin_zones[] = +{ + { 0, "us", "United States / North America", { 2000, 4000 }, + { + { ZT_TONE_DIALTONE, "350+440" }, + { ZT_TONE_BUSY, "480+620/500,0/500" }, + { ZT_TONE_RINGTONE, "440+480/2000,0/4000" }, + { ZT_TONE_CONGESTION, "480+620/250,0/250" }, + { ZT_TONE_CALLWAIT, "440/300,0/10000" }, + { ZT_TONE_DIALRECALL, "!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440" }, + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "!950/330,!1400/330,!1800/330,0" } } + }, + { -1 } +}; |