diff options
author | kpfleming <kpfleming@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2007-12-14 23:47:31 +0000 |
---|---|---|
committer | kpfleming <kpfleming@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2007-12-14 23:47:31 +0000 |
commit | e82cf68ec969ba027f671b27261442b54b5f8737 (patch) | |
tree | 4a8a03d12bd400e69f53b7fce7f3075a41a407c2 /zaptel-base.c | |
parent | ef2eddaeb8ac4e25b283081911a36a642ce5f157 (diff) |
(merging dtmf-twister branch plus a few fixes)
move DTMF/MF generation into tonezone.c (libtonezone) so that it can happen at runtime instead of compile time; this allows for DTMF/MF to be different on a zone-by-zone basis without requiring a recompile of Zaptel
set DTMF 'twist' for Brazil (zone 'br') to 2dB
git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.4@3490 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'zaptel-base.c')
-rw-r--r-- | zaptel-base.c | 383 |
1 files changed, 214 insertions, 169 deletions
diff --git a/zaptel-base.c b/zaptel-base.c index 278ae29..acd4743 100644 --- a/zaptel-base.c +++ b/zaptel-base.c @@ -45,6 +45,7 @@ #include <linux/pci.h> #include <linux/init.h> #include <linux/version.h> +#include <linux/ctype.h> #include <linux/kmod.h> #ifdef CONFIG_DEVFS_FS #include <linux/devfs_fs_kernel.h> @@ -302,8 +303,10 @@ of the next sample chunk's data (next time around the world). #include "digits.h" -static struct zt_tone *dtmf_tones_continuous = NULL; -static struct zt_tone *mfv1_tones_continuous = NULL; +static struct zt_dialparams global_dialparams = { + .dtmf_tonelen = DEFAULT_DTMF_LENGTH, + .mfv1_tonelen = DEFAULT_MFV1_LENGTH, +}; static int zt_chan_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data, int unit); @@ -363,6 +366,10 @@ struct zt_zone { of zt_tones to generate what we want. Use NULL if the tone is unavailable */ + struct zt_tone dtmf[16]; /* DTMF tones for this zone, with desired length */ + struct zt_tone dtmf_continuous[16]; /* DTMF tones for this zone, continuous play */ + struct zt_tone mf[15]; /* MF tones for this zone, with desired length */ + struct zt_tone mf_continuous[15]; /* MF tones for this zone, continuous play */ }; static struct zt_span *spans[ZT_MAX_SPANS]; @@ -1074,13 +1081,11 @@ static void close_channel(struct zt_chan *chan) static int free_tone_zone(int num) { struct zt_zone *z; - if ((num < 0) || (num >= ZT_TONE_ZONE_MAX)) - return -EINVAL; - write_lock(&zone_lock); + z = tone_zones[num]; tone_zones[num] = NULL; - write_unlock(&zone_lock); kfree(z); + return 0; } @@ -1130,10 +1135,14 @@ static int start_tone(struct zt_chan *chan, int tone) /* ZT_SENDTONE should never be used on a channel configured for pulse dialing */ chan->dialing = 1; res = 0; - if (chan->digitmode == DIGIT_MODE_DTMF) - chan->curtone = dtmf_tones_continuous + (tone - ZT_TONE_DTMF_BASE); - else if (chan->digitmode == DIGIT_MODE_MFV1 && tone != ZT_TONE_DTMF_MAX) /* No 'D' */ - chan->curtone = mfv1_tones_continuous + (tone - ZT_TONE_DTMF_BASE); + if ((chan->digitmode == DIGIT_MODE_DTMF) && + (tone >= ZT_TONE_DTMF_BASE) && + (tone <= ZT_TONE_DTMF_MAX)) + chan->curtone = &chan->curzone->dtmf_continuous[tone - ZT_TONE_DTMF_BASE]; + else if ((chan->digitmode == DIGIT_MODE_MFV1) && + (tone >= ZT_TONE_MF_BASE) && + (tone <= ZT_TONE_MF_MAX)) + chan->curtone = &chan->curzone->mf_continuous[tone - ZT_TONE_MF_BASE]; else { chan->dialing = 0; res = -EINVAL; @@ -2563,84 +2572,112 @@ static ssize_t zt_write(struct file *file, const char *usrbuf, size_t count, lof /* No bigger than 32k for everything per tone zone */ #define MAX_SIZE 32768 -/* No more than 64 subtones */ -#define MAX_TONES 64 - -static int -ioctl_load_zone(unsigned long data) +/* No more than 128 subtones */ +#define MAX_TONES 128 + +/* The tones to be loaded can (will) be a mix of regular tones, + DTMF tones and MF tones. We need to load DTMF and MF tones + a bit differently than regular tones because their storage + format is much simpler (an array structure field of the zone + structure, rather an array of pointers). +*/ +static int ioctl_load_zone(unsigned long data) { - struct zt_tone *samples[MAX_TONES]; - short next[MAX_TONES]; + struct zt_tone *samples[MAX_TONES] = { NULL, }; + short next[MAX_TONES] = { 0, }; struct zt_tone_def_header th; - void *slab, *ptr; - long size; - struct zt_zone *z; struct zt_tone_def td; + struct zt_zone *z; struct zt_tone *t; + void *slab, *ptr; int x; - int space; + size_t space; + size_t size; int res; - /* XXX Unnecessary XXX */ - memset(samples, 0, sizeof(samples)); - /* XXX Unnecessary XXX */ - memset(next, 0, sizeof(next)); - if (copy_from_user(&th, (struct zt_tone_def_header *)data, sizeof(th))) + if (copy_from_user(&th, (struct zt_tone_def_header *) data, sizeof(th))) return -EFAULT; + + data += sizeof(th); + if ((th.count < 0) || (th.count > MAX_TONES)) { printk("Too many tones included\n"); return -EINVAL; } - space = size = sizeof(struct zt_zone) + - th.count * sizeof(struct zt_tone); - if ((size > MAX_SIZE) || (size < 0)) + + space = size = sizeof(*z) + th.count * sizeof(*t); + + if (size > MAX_SIZE) return -E2BIG; - ptr = slab = (char *)kmalloc(size, GFP_KERNEL); - if (!slab) + + if (!(z = ptr = slab = kmalloc(size, GFP_KERNEL))) return -ENOMEM; - /* Zero it out for simplicity */ + memset(slab, 0, size); - /* Grab the zone */ - z = (struct zt_zone *)slab; + + ptr += sizeof(*z); + space -= sizeof(*z); + strncpy(z->name, th.name, sizeof(z->name) - 1); - for (x=0;x<ZT_MAX_CADENCE;x++) + + for (x = 0; x < ZT_MAX_CADENCE; x++) z->ringcadence[x] = th.ringcadence[x]; - data += sizeof(struct zt_tone_def_header); - ptr += sizeof(struct zt_zone); - space -= sizeof(struct zt_zone); - for (x=0;x<th.count;x++) { - if (space < sizeof(struct zt_tone)) { - /* Check space for zt_tone struct */ + + for (x = 0; x < th.count; x++) { + enum { + REGULAR_TONE, + DTMF_TONE, + MF_TONE, + } tone_type; + + if (space < sizeof(*t)) { kfree(slab); printk("Insufficient tone zone space\n"); return -EINVAL; } - if (copy_from_user(&td, (struct zt_tone_def *)data, sizeof(struct zt_tone_def))) { + + if (copy_from_user(&td, (struct zt_tone_def *) data, sizeof(td))) { kfree(slab); return -EFAULT; } - /* Index the current sample */ - samples[x] = t = (struct zt_tone *)ptr; - /* Remember which sample is next */ - next[x] = td.next; - /* Make sure the "next" one is sane */ - if ((next[x] >= th.count) || (next[x] < 0)) { - printk("Invalid 'next' pointer: %d\n", next[x]); - kfree(slab); - return -EINVAL; - } - if (td.tone >= ZT_TONE_MAX) { - printk("Too many tones defined\n"); - /* Make sure it's sane */ + + data += sizeof(td); + + if ((td.tone >= 0) && (td.tone < ZT_TONE_MAX)) { + tone_type = REGULAR_TONE; + + t = samples[x] = ptr; + + space -= sizeof(*t); + ptr += sizeof(*t); + + /* Remember which sample is next */ + next[x] = td.next; + + /* Make sure the "next" one is sane */ + if ((next[x] >= th.count) || (next[x] < 0)) { + printk("Invalid 'next' pointer: %d\n", next[x]); + kfree(slab); + return -EINVAL; + } + } else if ((td.tone >= ZT_TONE_DTMF_BASE) && + (td.tone <= ZT_TONE_DTMF_MAX)) { + tone_type = DTMF_TONE; + + td.tone -= ZT_TONE_DTMF_BASE; + t = &z->dtmf[td.tone]; + } else if ((td.tone >= ZT_TONE_MF_BASE) && + (td.tone <= ZT_TONE_MF_MAX)) { + tone_type = MF_TONE; + + td.tone -= ZT_TONE_MF_BASE; + t = &z->mf[td.tone]; + } else { + printk("Invalid tone (%d) defined\n", td.tone); kfree(slab); return -EINVAL; } - /* Update pointers to account for zt_tone header */ - space -= sizeof(struct zt_tone); - ptr += sizeof(struct zt_tone); - data += sizeof(struct zt_tone_def); - /* Fill in tonedata, datalen, and tonesamples fields */ - t->tonesamples = td.samples; + t->fac1 = td.fac1; t->init_v2_1 = td.init_v2_1; t->init_v3_1 = td.init_v3_1; @@ -2648,18 +2685,39 @@ ioctl_load_zone(unsigned long data) t->init_v2_2 = td.init_v2_2; t->init_v3_2 = td.init_v3_2; t->modulate = td.modulate; - t->next = NULL; /* XXX Unnecessary XXX */ - if (!z->tones[td.tone]) - z->tones[td.tone] = t; + + switch (tone_type) { + case REGULAR_TONE: + t->tonesamples = td.samples; + if (!z->tones[td.tone]) + z->tones[td.tone] = t; + break; + case DTMF_TONE: + t->tonesamples = global_dialparams.dtmf_tonelen; + t->next = &dtmf_silence; + z->dtmf_continuous[td.tone] = *t; + z->dtmf_continuous[td.tone].next = &z->dtmf_continuous[td.tone]; + break; + case MF_TONE: + t->tonesamples = global_dialparams.mfv1_tonelen; + t->next = &mfv1_silence; + /* Special case for K/P tone */ + if (td.tone == 10) + t->tonesamples *= 5 / 3; + z->mf_continuous[td.tone] = *t; + z->mf_continuous[td.tone].next = &z->mf_continuous[td.tone]; + break; + } } - for (x=0;x<th.count;x++) - /* Set "next" pointers */ - samples[x]->next = samples[next[x]]; - /* Actually register zone */ - res = zt_register_tone_zone(th.zone, z); - if (res) + for (x = 0; x < th.count; x++) { + if (samples[x] && next[x]) + samples[x]->next = samples[next[x]]; + } + + if ((res = zt_register_tone_zone(th.zone, z))) kfree(slab); + return res; } @@ -2674,15 +2732,22 @@ void zt_init_tone_state(struct zt_tone_state *ts, struct zt_tone *zt) ts->modulate = zt->modulate; } -struct zt_tone *zt_dtmf_tone(char digit, int mf) +struct zt_tone *zt_dtmf_tone(const struct zt_chan *chan, char digit) { struct zt_tone *z; - if (!mf) - z = dtmf_tones; - else - z = mfv1_tones; - switch(digit) { + switch (chan->digitmode) { + case DIGIT_MODE_DTMF: + z = &chan->curzone->dtmf[0]; + break; + case DIGIT_MODE_MFV1: + z = &chan->curzone->mf[0]; + break; + default: + z = NULL; + } + + switch (digit) { case '0': case '1': case '2': @@ -2693,7 +2758,7 @@ struct zt_tone *zt_dtmf_tone(char digit, int mf) case '7': case '8': case '9': - return z + (int)(digit - '0'); + return z + (digit - '0'); case '*': return z + 10; case '#': @@ -2703,66 +2768,48 @@ struct zt_tone *zt_dtmf_tone(char digit, int mf) case 'C': return z + (digit + 12 - 'A'); case 'D': - if (!mf) - return z + ( digit + 12 - 'A'); - return NULL; - case 'a': - case 'b': - case 'c': - return z + (digit + 12 - 'a'); - case 'd': - if (!mf) - return z + ( digit + 12 - 'a'); - return NULL; + if (chan->digitmode == DIGIT_MODE_MFV1) + return NULL; + else + return z + (digit + 12 - 'A'); case 'W': - case 'w': return &tone_pause; } + return NULL; } static void __do_dtmf(struct zt_chan *chan) { char c; + /* Called with chan->lock held */ - while (strlen(chan->txdialbuf)) { - c = chan->txdialbuf[0]; - /* Skooch */ + while ((c = chan->txdialbuf[0])) { memmove(chan->txdialbuf, chan->txdialbuf + 1, sizeof(chan->txdialbuf) - 1); - switch(c) { + switch (c) { case 'T': - case 't': chan->digitmode = DIGIT_MODE_DTMF; chan->tonep = 0; break; case 'M': - case 'm': chan->digitmode = DIGIT_MODE_MFV1; chan->tonep = 0; break; case 'P': - case 'p': chan->digitmode = DIGIT_MODE_PULSE; chan->tonep = 0; break; default: - if (chan->digitmode == DIGIT_MODE_PULSE) - { - if ((c >= '0') && (c <= '9') && (chan->txhooksig == ZT_TXSIG_OFFHOOK)) - { - chan->pdialcount = c - '0'; - /* a '0' is ten pulses */ - if (!chan->pdialcount) chan->pdialcount = 10; - zt_rbs_sethook(chan, ZT_TXSIG_ONHOOK, - ZT_TXSTATE_PULSEBREAK, chan->pulsebreaktime); + if ((c != 'W') && (chan->digitmode == DIGIT_MODE_PULSE)) { + if ((c >= '0') && (c <= '9') && (chan->txhooksig == ZT_TXSIG_OFFHOOK)) { + chan->pdialcount = (c == '0') ? 10 : c - '0'; + zt_rbs_sethook(chan, ZT_TXSIG_ONHOOK, ZT_TXSTATE_PULSEBREAK, + chan->pulsebreaktime); return; } } else { - case 'w': - case 'W': - chan->curtone = zt_dtmf_tone(c, (chan->digitmode == DIGIT_MODE_MFV1)); + chan->curtone = zt_dtmf_tone(chan, c); chan->tonep = 0; - /* All done */ if (chan->curtone) { zt_init_tone_state(&chan->ts, chan->curtone); return; @@ -2770,6 +2817,7 @@ static void __do_dtmf(struct zt_chan *chan) } } } + /* Notify userspace process if there is nothing left */ chan->dialing = 0; __qevent(chan, ZT_EVENT_DIALCOMPLETE); @@ -3567,43 +3615,70 @@ static int zt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd return res; case ZT_DEFAULTZONE: if (get_user(j,(int *)data)) - return -EFAULT; /* get conf # */ - if ((j < 0) || (j >= ZT_TONE_ZONE_MAX)) return (-EINVAL); + return -EFAULT; + if ((j < 0) || (j >= ZT_TONE_ZONE_MAX)) + return -EINVAL; write_lock(&zone_lock); + if (!tone_zones[j]) { + write_unlock(&zone_lock); + return -EINVAL; + } default_zone = j; write_unlock(&zone_lock); - return 0; + break; case ZT_LOADZONE: return ioctl_load_zone(data); case ZT_FREEZONE: - get_user(j,(int *)data); /* get conf # */ - if ((j < 0) || (j >= ZT_TONE_ZONE_MAX)) return (-EINVAL); + get_user(j, (int *) data); + if ((j < 0) || (j >= ZT_TONE_ZONE_MAX)) + return -EINVAL; + write_lock(&zone_lock); +#if 0 + if (j == default_zone) { + write_unlock(&zone_lock); + /* XXX: possibly a better return code here */ + return -EINVAL; + } +#endif free_tone_zone(j); - return 0; + write_unlock(&zone_lock); + break; case ZT_SET_DIALPARAMS: - if (copy_from_user(&tdp, (struct zt_dialparams *)data, sizeof(tdp))) + if (copy_from_user(&tdp, (struct zt_dialparams *) data, sizeof(tdp))) return -EFAULT; if ((tdp.dtmf_tonelen > 4000) || (tdp.dtmf_tonelen < 10)) return -EINVAL; if ((tdp.mfv1_tonelen > 4000) || (tdp.mfv1_tonelen < 10)) return -EINVAL; - for (i=0;i<16;i++) - dtmf_tones[i].tonesamples = tdp.dtmf_tonelen * ZT_CHUNKSIZE; + + global_dialparams = tdp; + + /* update the lengths in all currently loaded zones */ + write_lock(&zone_lock); + for (j = 0; j < sizeof(tone_zones) / sizeof(tone_zones[0]); j++) { + struct zt_zone *z = tone_zones[j]; + + if (!z) + continue; + + for (i = 0; i < sizeof(z->dtmf) / sizeof(z->dtmf[0]); i++) + z->dtmf[i].tonesamples = tdp.dtmf_tonelen * ZT_CHUNKSIZE; + + for (i = 0; i < sizeof(z->mf) / sizeof(z->mf[0]); i++) + z->mf[i].tonesamples = tdp.mfv1_tonelen * ZT_CHUNKSIZE; + + /* Special case for K/P tone */ + z->mf[10].tonesamples *= 5 / 3; + } + write_unlock(&zone_lock); + dtmf_silence.tonesamples = tdp.dtmf_tonelen * ZT_CHUNKSIZE; - for (i=0;i<15;i++) - mfv1_tones[i].tonesamples = tdp.mfv1_tonelen * ZT_CHUNKSIZE; mfv1_silence.tonesamples = tdp.mfv1_tonelen * ZT_CHUNKSIZE; - /* Special case for K/P tone */ - mfv1_tones[10].tonesamples = tdp.mfv1_tonelen * ZT_CHUNKSIZE * 5 / 3; + break; case ZT_GET_DIALPARAMS: - tdp.dtmf_tonelen = dtmf_tones[0].tonesamples / ZT_CHUNKSIZE; - tdp.mfv1_tonelen = mfv1_tones[0].tonesamples / ZT_CHUNKSIZE; - tdp.reserved[0] = 0; - tdp.reserved[1] = 0; - tdp.reserved[2] = 0; - tdp.reserved[3] = 0; - if (copy_to_user((struct zt_dialparams *)data, &tdp, sizeof(tdp))) + tdp = global_dialparams; + if (copy_to_user((struct zt_dialparams *) data, &tdp, sizeof(tdp))) return -EFAULT; break; case ZT_GETVERSION: @@ -3687,6 +3762,7 @@ static int zt_chanandpseudo_ioctl(struct inode *inode, struct file *file, unsign unsigned long flags, flagso; int i, j, k, rv; int ret, c; + char *s; if (!chan) return -EINVAL; @@ -3702,10 +3778,12 @@ static int zt_chanandpseudo_ioctl(struct inode *inode, struct file *file, unsign if (copy_from_user(&stack.tdo, (struct zt_dialoperation *)data, sizeof(stack.tdo))) return -EFAULT; rv = 0; - /* Force proper NULL termination */ + /* Force proper NULL termination and uppercase entry */ stack.tdo.dialstr[ZT_MAX_DTMF_BUF - 1] = '\0'; + for (s = stack.tdo.dialstr; *s; s++) + *s = toupper(*s); spin_lock_irqsave(&chan->lock, flags); - switch(stack.tdo.op) { + switch (stack.tdo.op) { case ZT_DIAL_OP_CANCEL: chan->curtone = NULL; chan->dialing = 0; @@ -3719,17 +3797,15 @@ static int zt_chanandpseudo_ioctl(struct inode *inode, struct file *file, unsign __do_dtmf(chan); break; case ZT_DIAL_OP_APPEND: - if (strlen(stack.tdo.dialstr) + strlen(chan->txdialbuf) >= ZT_MAX_DTMF_BUF) - { + if (strlen(stack.tdo.dialstr) + strlen(chan->txdialbuf) >= (ZT_MAX_DTMF_BUF - 1)) { rv = -EBUSY; break; - } - strncpy(chan->txdialbuf + strlen(chan->txdialbuf), stack.tdo.dialstr, ZT_MAX_DTMF_BUF - strlen(chan->txdialbuf)); - if (!chan->dialing) - { + } + strncpy(chan->txdialbuf + strlen(chan->txdialbuf), stack.tdo.dialstr, ZT_MAX_DTMF_BUF - strlen(chan->txdialbuf) - 1); + if (!chan->dialing) { chan->dialing = 1; __do_dtmf(chan); - } + } break; default: rv = -EINVAL; @@ -7144,7 +7220,6 @@ static void __exit watchdog_cleanup(void) static int __init zt_init(void) { int res = 0; - int i = 0; #ifdef CONFIG_PROC_FS proc_entries[0] = proc_mkdir("zaptel", NULL); @@ -7178,26 +7253,6 @@ static int __init zt_init(void) { } #endif /* CONFIG_DEVFS_FS */ - if (!(dtmf_tones_continuous = kmalloc(sizeof(dtmf_tones), GFP_KERNEL))) { - printk(KERN_ERR "Zaptel: THERE IS A CRISIS IN THE BATCAVE!" - " Unable to allocate memory for continuous DTMF tones list!\n"); - return -ENOMEM; - } - - if (!(mfv1_tones_continuous = kmalloc(sizeof(mfv1_tones), GFP_KERNEL))) { - printk(KERN_ERR "Zaptel: THERE IS A CRISIS IN THE BATCAVE!" - " Unable to allocate memory for continuous MFV1 tones list!\n"); - return -ENOMEM; - } - - memcpy(dtmf_tones_continuous, dtmf_tones, sizeof(dtmf_tones)); - for (i = 0; i < (sizeof(dtmf_tones) / sizeof(dtmf_tones[0])); i++) - dtmf_tones_continuous[i].next = dtmf_tones_continuous + i; - - memcpy(mfv1_tones_continuous, mfv1_tones, sizeof(mfv1_tones)); - for (i = 0; i < (sizeof(mfv1_tones) / sizeof(mfv1_tones[0])); i++) - mfv1_tones_continuous[i].next = mfv1_tones_continuous + i; - printk(KERN_INFO "Zapata Telephony Interface Registered on major %d\n", ZT_MAJOR); printk(KERN_INFO "Zaptel Version: %s\n", ZAPTEL_VERSION); echo_can_init(); @@ -7224,16 +7279,6 @@ static void __exit zt_cleanup(void) { kfree(tone_zones[x]); } - if (dtmf_tones_continuous) { - kfree(dtmf_tones_continuous); - dtmf_tones_continuous = NULL; - } - - if (mfv1_tones_continuous) { - kfree(mfv1_tones_continuous); - mfv1_tones_continuous = NULL; - } - #ifdef CONFIG_DEVFS_FS devfs_unregister(timer); devfs_unregister(transcode); |