diff options
-rwxr-xr-x | Makefile | 5 | ||||
-rwxr-xr-x | pciradio.c | 1599 | ||||
-rwxr-xr-x | zaptel.c | 9 | ||||
-rwxr-xr-x | zaptel.conf.sample | 61 | ||||
-rwxr-xr-x | zaptel.h | 102 | ||||
-rwxr-xr-x | ztcfg.c | 429 |
6 files changed, 2200 insertions, 5 deletions
@@ -52,7 +52,7 @@ endif TZOBJS=zonedata.lo tonezone.lo LIBTONEZONE=libtonezone.so.1.0 MODULES=zaptel tor2 torisa wcusb wcfxo wcfxs \ - ztdynamic ztd-eth wct1xxp wct4xxp wcte11xp # ztdummy + ztdynamic ztd-eth wct1xxp wct4xxp wcte11xp pciradio # ztdummy #MODULES+=wcfxsusb MODULESO=$(shell for x in $(MODULES); do echo "$$x.o "; done ) @@ -117,6 +117,9 @@ wct4xxp.o:wct4xxp.c zaptel.h wcfxs.o:wcfxs.c zaptel.h $(HOSTCC) $(KFLAGS) -c wcfxs.c +pciradio.o:pciradio.c zaptel.h + $(HOSTCC) $(KFLAGS) -c pciradio.c + wcs3200p.o:wcs3200p.c zaptel.h $(HOSTCC) $(KFLAGS) -c wcs3200p.c diff --git a/pciradio.c b/pciradio.c new file mode 100755 index 0000000..2ba8913 --- /dev/null +++ b/pciradio.c @@ -0,0 +1,1599 @@ +/* + * PCI RADIO Card Zapata Telephony PCI Quad Radio Interface driver + * + * Written by Jim Dixon <jim@lambdatel.com> + * Based on previous work by Mark Spencer <markster@linux-support.net> + * Based on previous works, designs, and archetectures conceived and + * written by Jim Dixon <jim@lambdatel.com>. + * + * Copyright (C) 2001-2004 Jim Dixon / Zapata Telephony. + * + * 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 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 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. + * + */ + +/* + The PCI Radio Interface card interfaces up to 4 two-way radios (either + a base/mobile radio or repeater system) to Zaptel channels. The driver + may work either independent of an application, or with it, through + the driver;s ioctl() interface. This file gives you access to specify + load-time parameters for Radio channels, so that the driver may run + by itself, and just act like a generic Zaptel radio interface. +*/ + +/* Latency tests: + +Without driver: 308208 +Without int: 304096 (1.3 %) +With PL check: 267722 (13.2 % -- will be much improved with new Xilinx) + +*/ + + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <asm/delay.h> + +#ifdef STANDALONE_ZAPATA +#include "zaptel.h" +#else +#include <linux/zaptel.h> +#endif + +#define RAD_MAX_IFACES 128 + +#define NUM_CODES 15 + +#define RAD_CNTL 0x00 +#define RAD_OPER 0x01 +#define RAD_AUXC 0x02 +#define RAD_AUXD 0x03 + #define MX828_DOUT 0x02 /* Data from MX828 */ + #define MX828_DIN 0x04 /* Data to MX828 */ + #define MX828_CS0 0x08 /* MX828 CS Channel 0 */ + #define MX828_CS1 0x10 /* MX828 CS Channel 1 */ + #define MX828_CS2 0x20 /* MX828 CS Channel 2 */ + #define MX828_CS3 0x40 /* MX828 CS Channel 3 */ + #define MX828_SCLK 0x80 /* MX828 Serial Clock */ +#define RAD_MASK0 0x04 +#define RAD_MASK1 0x05 +#define RAD_INTSTAT 0x06 +#define RAD_AUXR 0x07 + +#define RAD_DMAWS 0x08 +#define RAD_DMAWI 0x0c +#define RAD_DMAWE 0x10 +#define RAD_DMARS 0x18 +#define RAD_DMARI 0x1c +#define RAD_DMARE 0x20 + +#define RAD_AUXFUNC 0x2b +#define RAD_SERCTL 0x2d +#define RAD_FSCDELAY 0x2f + +#define RAD_REGBASE 0xc0 + +#define BIT_CS (1 << 2) +#define BIT_SCLK (1 << 3) +#define BIT_SDI (1 << 4) +#define BIT_SDO (1 << 5) + +#define RAD_CTCSSMASK 0xf +#define RAD_CTCSSOTHER 0xf +#define RAD_CTCSSVALID 0x10 + +#define NUM_CHANS 4 + +#define RAD_GOTRX_DEBOUNCE_TIME 75 + +/* +* MX828 Commands +*/ + +#define MX828_GEN_RESET 0x01 /* W */ +#define MX828_SAUDIO_CTRL 0x80 /* W */ +#define MX828_SAUDIO_STATUS 0x81 /* R */ +#define MX828_SAUDIO_SETUP 0x82 /* W */ +#define MX828_TX_TONE 0x83 /* W16 */ +#define MX828_RX_TONE 0x84 /* W16 */ +#define MX828_DCS3 0x85 /* W */ +#define MX828_DCS2 0x86 /* W */ +#define MX828_DCS1 0x87 /* W */ +#define MX828_GEN_CTRL 0x88 /* W */ +#define MX828_GPT 0x8B /* W */ +#define MX828_IRQ_MASK 0x8E /* W */ +#define MX828_SELCALL 0x8D /* W16 */ +#define MX828_AUD_CTRL 0x8A /* W16 */ +#define MX828_IRQ_FLAG 0x8F /* R */ + + +struct pciradio { + struct pci_dev *dev; + struct zt_span span; + unsigned char ios; + int usecount; + int intcount; + int dead; + int pos; + int freeregion; + int nchans; + spinlock_t lock; + unsigned char p0save; + unsigned char p1save; + unsigned char p2save; + unsigned long ioaddr; + dma_addr_t readdma; + dma_addr_t writedma; + volatile int *writechunk; /* Double-word aligned write memory */ + volatile int *readchunk; /* Double-word aligned read memory */ + unsigned char saudio_status[NUM_CHANS]; + char gotcor[NUM_CHANS]; + char gotct[NUM_CHANS]; + char gotrx[NUM_CHANS]; + char gotrx1[NUM_CHANS]; + char gottx[NUM_CHANS]; + char lasttx[NUM_CHANS]; + int gotrxtimer[NUM_CHANS]; + int debouncetime[NUM_CHANS]; + int bursttime[NUM_CHANS]; + int bursttimer[NUM_CHANS]; + unsigned short present_code[NUM_CHANS]; + unsigned short rxcode[NUM_CHANS][NUM_CODES + 1]; + unsigned short rxclass[NUM_CHANS][NUM_CODES + 1]; + unsigned short txcode[NUM_CHANS][NUM_CODES + 1];; + unsigned char radmode[NUM_CHANS]; +#define RADMODE_INVERTCOR 1 +#define RADMODE_IGNORECOR 2 +#define RADMODE_EXTTONE 4 +#define RADMODE_EXTINVERT 8 +#define RADMODE_IGNORECT 16 +#define RADMODE_NOENCODE 32 + unsigned char corthresh[NUM_CHANS]; + struct zt_chan chans[NUM_CHANS]; +}; + + +static struct pciradio *ifaces[RAD_MAX_IFACES]; + +static void pciradio_release(struct pciradio *rad); + +static int debug = 0; + +struct tonedef { + int code; + unsigned char b1; + unsigned char b2; +} ; + + +static struct tonedef cttable_tx [] = { +{0,0,0}, +{670,0xE,0xB1}, +{693,0xE,0x34}, +{719,0xD,0xB1}, +{744,0xD,0x3B}, +{770,0xC,0xC9}, +{797,0xC,0x5A}, +{825,0xB,0xEF}, +{854,0xB,0x87}, +{885,0xB,0x1F}, +{915,0xA,0xC2}, +{948,0xA,0x62}, +{974,0xA,0x1B}, +{1000,0x9,0xD8}, +{1035,0x9,0x83}, +{1072,0x9,0x2F}, +{1109,0x8,0xE0}, +{1148,0x8,0x93}, +{1188,0x8,0x49}, +{1230,0x8,0x1}, +{1273,0x7,0xBC}, +{1318,0x7,0x78}, +{1365,0x7,0x36}, +{1413,0x6,0xF7}, +{1462,0x6,0xBC}, +{1514,0x6,0x80}, +{1567,0x6,0x48}, +{1598,0x6,0x29}, +{1622,0x6,0x12}, +{1679,0x5,0xDD}, +{1738,0x5,0xAA}, +{1799,0x5,0x79}, +{1835,0x5,0x5D}, +{1862,0x5,0x49}, +{1899,0x5,0x2F}, +{1928,0x5,0x1B}, +{1966,0x5,0x2}, +{1995,0x4,0xEF}, +{2035,0x4,0xD6}, +{2065,0x4,0xC4}, +{2107,0x4,0xAC}, +{2181,0x4,0x83}, +{2257,0x4,0x5D}, +{2291,0x4,0x4C}, +{2336,0x4,0x37}, +{2418,0x4,0x12}, +{2503,0x3,0xEF}, +{2541,0x3,0xE0}, +{0,0,0} +} ; + +static struct tonedef cttable_rx [] = { +{0,0,0}, +{670,0x3,0xD8}, +{693,0x4,0x9}, +{719,0x4,0x1B}, +{744,0x4,0x4E}, +{770,0x4,0x83}, +{797,0x4,0x94}, +{825,0x4,0xCB}, +{854,0x5,0x2}, +{885,0x5,0x14}, +{915,0x5,0x4C}, +{948,0x5,0x87}, +{974,0x5,0x94}, +{1000,0x5,0xCB}, +{1035,0x6,0x7}, +{1072,0x6,0x45}, +{1109,0x6,0x82}, +{1148,0x6,0xC0}, +{1188,0x6,0xD1}, +{1230,0x7,0x10}, +{1273,0x7,0x50}, +{1318,0x7,0xC0}, +{1365,0x8,0x2}, +{1413,0x8,0x44}, +{1462,0x8,0x86}, +{1514,0x8,0xC9}, +{1567,0x9,0xC}, +{1598,0x9,0x48}, +{1622,0x9,0x82}, +{1679,0x9,0xC6}, +{1738,0xA,0xB}, +{1799,0xA,0x84}, +{1835,0xA,0xC2}, +{1862,0xA,0xC9}, +{1899,0xB,0x8}, +{1928,0xB,0x44}, +{1966,0xB,0x83}, +{1995,0xB,0x8A}, +{2035,0xB,0xC9}, +{2065,0xC,0x6}, +{2107,0xC,0x46}, +{2181,0xC,0xC3}, +{2257,0xD,0x41}, +{2291,0xD,0x48}, +{2336,0xD,0x89}, +{2418,0xE,0x8}, +{2503,0xE,0x88}, +{2541,0xE,0xC7}, +{0,0,0} +}; + +static struct { + int code; + char b3; + char b2; + char b1; +} dcstable[] = { +{0,0,0,0}, +{23,0x76,0x38,0x13}, +{25,0x6B,0x78,0x15}, +{26,0x65,0xD8,0x16}, +{31,0x51,0xF8,0x19}, +{32,0x5F,0x58,0x1A}, +{43,0x5B,0x68,0x23}, +{47,0x0F,0xD8,0x27}, +{51,0x7C,0xA8,0x29}, +{54,0x6F,0x48,0x2C}, +{65,0x5D,0x18,0x35}, +{71,0x67,0x98,0x39}, +{72,0x69,0x38,0x3A}, +{73,0x2E,0x68,0x3B}, +{74,0x74,0x78,0x3C}, +{114,0x35,0xE8,0x4C}, +{115,0x72,0xB8,0x4D}, +{116,0x7C,0x18,0x4E}, +{125,0x07,0xB8,0x55}, +{131,0x3D,0x38,0x59}, +{132,0x33,0x98,0x5A}, +{134,0x2E,0xD8,0x5C}, +{143,0x37,0xA8,0x63}, +{152,0x1E,0xC8,0x6A}, +{155,0x44,0xD8,0x6D}, +{156,0x4A,0x78,0x6E}, +{162,0x6B,0xC8,0x72}, +{165,0x31,0xD8,0x75}, +{172,0x05,0xF8,0x7A}, +{174,0x18,0xB8,0x7C}, +{205,0x6E,0x98,0x85}, +{223,0x68,0xE8,0x93}, +{226,0x7B,0x08,0x96}, +{243,0x45,0xB8,0xA3}, +{244,0x1F,0xA8,0xA4}, +{245,0x58,0xF8,0xA5}, +{251,0x62,0x78,0xA9}, +{261,0x17,0x78,0xB1}, +{263,0x5E,0x88,0xB3}, +{265,0x43,0xC8,0xB5}, +{271,0x79,0x48,0xB9}, +{306,0x0C,0xF8,0xC6}, +{311,0x38,0xD8,0xC9}, +{315,0x6C,0x68,0xCD}, +{331,0x23,0xE8,0xD9}, +{343,0x29,0x78,0xE3}, +{346,0x3A,0x98,0xE6}, +{351,0x0E,0xB8,0xE9}, +{364,0x68,0x58,0xF4}, +{365,0x2F,0x08,0xF5}, +{371,0x15,0x88,0xF9}, +{411,0x77,0x69,0x09}, +{412,0x79,0xC9,0x0A}, +{413,0x3E,0x99,0x0B}, +{423,0x4B,0x99,0x13}, +{431,0x6C,0x59,0x19}, +{432,0x62,0xF9,0x1A}, +{445,0x7B,0x89,0x25}, +{464,0x27,0xE9,0x34}, +{465,0x60,0xB9,0x35}, +{466,0x6E,0x19,0x36}, +{503,0x3C,0x69,0x43}, +{506,0x2F,0x89,0x46}, +{516,0x41,0xB9,0x4E}, +{532,0x0E,0x39,0x5A}, +{546,0x19,0xE9,0x66}, +{565,0x0C,0x79,0x75}, +{606,0x5D,0x99,0x86}, +{612,0x67,0x19,0x8A}, +{624,0x0F,0x59,0x94}, +{627,0x01,0xF9,0x97}, +{631,0x72,0x89,0x99}, +{632,0x7C,0x29,0x9A}, +{654,0x4C,0x39,0xAC}, +{662,0x24,0x79,0xB2}, +{664,0x39,0x39,0xB4}, +{703,0x22,0xB9,0xC3}, +{712,0x0B,0xD9,0xCA}, +{723,0x39,0x89,0xD3}, +{731,0x1E,0x49,0xD9}, +{732,0x10,0xE9,0xDA}, +{734,0x0D,0xA9,0xDC}, +{743,0x14,0xD9,0xE3}, +{754,0x20,0xF9,0xEC}, +{0,0,0,0} +}; + +static int gettxtone(int code) +{ +int i; + + if (!code) return(0); + for(i = 0; cttable_tx[i].code || (!i); i++) + { + if (cttable_tx[i].code == code) + { + return (i); + } + } + return(-1); +} + +static int getrxtone(int code) +{ +int i; + + if (!code) return(0); + for(i = 0; cttable_rx[i].code || (!i); i++) + { + if (cttable_rx[i].code == code) + { + return (i); + } + } + return(-1); +} + + +static int getdcstone(int code) +{ +int i; + + if (!code) return(0); + for(i = 0; dcstable[i].code || (!i); i++) + { + if (dcstable[i].code == code) + { + return (i); + } + } + return(-1); +} + + +static void __pciradio_setcreg(struct pciradio *rad, unsigned char reg, unsigned char val) +{ + outb(val, rad->ioaddr + RAD_REGBASE + ((reg & 0xf) << 2)); +} + +static unsigned char __pciradio_getcreg(struct pciradio *rad, unsigned char reg) +{ + return inb(rad->ioaddr + RAD_REGBASE + ((reg & 0xf) << 2)); +} + +static void wait_just_a_bit(int foo) +{ + long newjiffies; + newjiffies = jiffies + foo; + while(jiffies < newjiffies); +} + +/* +* Output a byte to the MX828 ctcss encoder/decoder chip +*/ + +void mx828_set_serdata(struct pciradio *rad, char bit) +{ + unsigned char regsave; + + regsave = inb(rad->ioaddr + RAD_AUXR); + regsave &= ~MX828_DIN; + if(bit) + regsave |= MX828_DIN; + outb(regsave, rad->ioaddr + RAD_AUXD); +} + +void mx828_wiggle_sclk(struct pciradio *rad) +{ + unsigned char regsave; + + regsave = inb(rad->ioaddr + RAD_AUXR); + regsave &= ~MX828_SCLK; /* SCLK 1 -> 0 */ + outb(regsave, rad->ioaddr + RAD_AUXD); + udelay(1); + + regsave = inb(rad->ioaddr + RAD_AUXR); + regsave |= MX828_SCLK; /* SCLK 0 -> 1 */ + outb(regsave, rad->ioaddr + RAD_AUXD); + udelay(1); +} + + +/* +* Output a command to the MX828 over the serial bus +*/ + + +void mx828_command(struct pciradio *rad,int channel, unsigned char command, unsigned char *byte1, unsigned char *byte2) +{ + + int i, param = 1, wr = 1, word = 0; + unsigned char byte, regsave; + + if(channel > 3) + return; + + /* Pull the transfer info from the command code */ + + switch(command){ + case MX828_GEN_RESET: /* Commands with no param */ + param = 0; + break; + + case MX828_SAUDIO_CTRL: /* 8 bit write commands */ + case MX828_SAUDIO_SETUP: + case MX828_DCS1: + case MX828_DCS2: + case MX828_DCS3: + case MX828_IRQ_MASK: + case MX828_GEN_CTRL: + case MX828_GPT: + break; + + case MX828_SAUDIO_STATUS: /* 8 bit read commands */ + case MX828_IRQ_FLAG: + case 0: + wr = 0; + break; + + case MX828_TX_TONE: /* 16 bit write commands */ + case MX828_RX_TONE: + case MX828_AUD_CTRL: + case MX828_SELCALL: + word = 1; + break; + + default: + return; + } + + + mx828_set_serdata(rad,1); /* Start with data = 1 */ + + udelay(2); + + /* Set the proper CS */ + + byte = (unsigned char ) 1 << (channel + 3); + + regsave = inb(rad->ioaddr + RAD_AUXR); + regsave |= (MX828_CS0 | MX828_CS1 | MX828_CS2 | MX828_CS3); + regsave &= ~byte; + outb(regsave, rad->ioaddr + RAD_AUXD); + + + udelay(2); + + /* Output the command byte */ + + byte = command; + + for( i = 0 ; i < 8 ; i++){ + udelay(2); + mx828_set_serdata(rad,0x80 & byte); /* MSB first */ + byte <<= 1; + mx828_wiggle_sclk(rad); + } + if(param){ + udelay(4); + if(wr){ + byte = *byte1; + for( i = 0 ; i < 8 ; i++){ + udelay(2); + mx828_set_serdata(rad,0x80 & byte); + byte <<= 1; + mx828_wiggle_sclk(rad); + } + if(word){ + udelay(4); + byte = *byte2; + for( i = 0 ; i < 8 ; i++){ + udelay(2); + mx828_set_serdata(rad,0x80 & byte); + byte <<= 1; + mx828_wiggle_sclk(rad); + } + } + } + else { /* rd */ + byte = 0; + for( i = 0 ; i < 8 ; i++){ + mx828_wiggle_sclk(rad); + byte <<= 1; + udelay(2); + if(inb(rad->ioaddr + RAD_AUXR) & MX828_DOUT) + byte |= 0x01; + } + *byte1 = byte; + if(word){ + byte = 0; + udelay(4); + for( i = 0 ; i < 8 ; i++){ + mx828_wiggle_sclk(rad); + byte <<= 1; + udelay(2); + if(inb(rad->ioaddr + RAD_AUXR) & MX828_DOUT) + byte |= 0x01; + } + *byte2 = byte; + } + + + } + } + + udelay(4); + + /* Release chip selects */ + + regsave = inb(rad->ioaddr + RAD_AUXR); + regsave |= (MX828_CS0 | MX828_CS1 | MX828_CS2 | MX828_CS3); + outb(regsave, rad->ioaddr + RAD_AUXD); + + +} + +static void _set_encdec(struct pciradio *rad, int n) +{ +int i,myindex; +char dcsrx = 0, ctrx = 0, dcstx = 0, cttx = 0; +unsigned char byte1,byte2,saudio_ctrl,saudio_setup; +unsigned short txcode; + + + /* if something in code 0 for rx, is DCS */ + if (rad->rxcode[n][0]) dcsrx = 1; + else { /* otherwise, if something in other codes, is CT rx */ + for(i = 1; i <= NUM_CODES; i++) + { + if (rad->rxcode[n][1]) ctrx = 1; + } + } + /* get index for tx code. Will be 0 if not receiving a CT */ + myindex = 0; + if (ctrx && (rad->present_code[n])) myindex = rad->present_code[n]; + /* get actual tx code from array */ + txcode = rad->txcode[n][myindex]; + if (txcode & 0x8000) dcstx = 1; else if (txcode) cttx = 1; + if (rad->radmode[n] & RADMODE_NOENCODE) dcstx = cttx = 0; + if (rad->bursttimer[n]) dcstx = cttx = 0; + saudio_ctrl = 0; + saudio_setup = 0; + if (dcstx && (!dcsrx)) /* if to transmit DCS */ + { + saudio_setup |= 3; + saudio_ctrl |= 0x80; + byte1 = dcstable[txcode & 0x7fff].b1; + mx828_command(rad,n, MX828_DCS1, &byte1, &byte2 ); + byte1 = dcstable[txcode & 0x7fff].b2; + mx828_command(rad,n, MX828_DCS2, &byte1, &byte2 ); + byte1 = dcstable[txcode & 0x7fff].b3; + mx828_command(rad,n, MX828_DCS3, &byte1, &byte2 ); + } + if (cttx) + { + saudio_ctrl |= 0x80; + byte1 = cttable_tx[txcode].b1; + byte2 = cttable_tx[txcode].b2; + mx828_command(rad,n, MX828_TX_TONE, &byte1, &byte2 ); + } + if (dcsrx) + { + saudio_setup |= 1; + saudio_ctrl |= 0x41; + byte1 = dcstable[rad->rxcode[n][0]].b1; + mx828_command(rad,n, MX828_DCS1, &byte1, &byte2 ); + byte1 = dcstable[rad->rxcode[n][0]].b2; + mx828_command(rad,n, MX828_DCS2, &byte1, &byte2 ); + byte1 = dcstable[rad->rxcode[n][0]].b3; + mx828_command(rad,n, MX828_DCS3, &byte1, &byte2 ); + } + if (ctrx) + { + saudio_setup |= 0x80; + saudio_ctrl |= 0x60; + } + byte1 = saudio_setup; + mx828_command(rad,n, MX828_SAUDIO_SETUP, &byte1, &byte2 ); + byte1 = saudio_ctrl; + mx828_command(rad,n, MX828_SAUDIO_CTRL, &byte1, &byte2 ); + return; +} + +static inline void pciradio_transmitprep(struct pciradio *rad, unsigned char ints) +{ + volatile unsigned int *writechunk; + int x; + if (ints & 0x01) + /* Write is at interrupt address. Start writing from normal offset */ + writechunk = rad->writechunk; + else + writechunk = rad->writechunk + ZT_CHUNKSIZE; + + /* Calculate Transmission */ + zt_transmit(&rad->span); + + for (x=0;x<ZT_CHUNKSIZE;x++) { + /* Send a sample, as a 32-bit word */ + writechunk[x] = 0; + writechunk[x] |= (rad->chans[0].writechunk[x] << 24); + writechunk[x] |= (rad->chans[1].writechunk[x] << 16); + writechunk[x] |= (rad->chans[2].writechunk[x] << 8); + writechunk[x] |= (rad->chans[3].writechunk[x]); + } +} + +static inline void pciradio_receiveprep(struct pciradio *rad, unsigned char ints) +{ + volatile unsigned int *readchunk; + int x; + + if (ints & 0x08) + readchunk = rad->readchunk + ZT_CHUNKSIZE; + else + /* Read is at interrupt address. Valid data is available at normal offset */ + readchunk = rad->readchunk; + for (x=0;x<ZT_CHUNKSIZE;x++) { + rad->chans[0].readchunk[x] = (readchunk[x] >> 24) & 0xff; + rad->chans[1].readchunk[x] = (readchunk[x] >> 16) & 0xff; + rad->chans[2].readchunk[x] = (readchunk[x] >> 8) & 0xff; + rad->chans[3].readchunk[x] = (readchunk[x]) & 0xff; + } + for (x=0;x<rad->nchans;x++) { + zt_ec_chunk(&rad->chans[x], rad->chans[x].readchunk, rad->chans[x].writechunk); + } + zt_receive(&rad->span); +} + +static void pciradio_stop_dma(struct pciradio *rad); +static void pciradio_reset_serial(struct pciradio *rad); +static void pciradio_restart_dma(struct pciradio *rad); + +#ifdef LINUX26 +static irqreturn_t pciradio_interrupt(int irq, void *dev_id, struct pt_regs *regs) +#else +static void pciradio_interrupt(int irq, void *dev_id, struct pt_regs *regs) +#endif +{ + struct pciradio *rad = dev_id; + unsigned char ints,byte1,byte2,gotcor,gotctcss,gotslowctcss,ctcss; + int i,x,gotrx; + + ints = inb(rad->ioaddr + RAD_INTSTAT); + outb(ints, rad->ioaddr + RAD_INTSTAT); + + if (!ints) +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + + if (ints & 0x10) { + /* Stop DMA, wait for watchdog */ + printk("RADIO PCI Master abort\n"); + pciradio_stop_dma(rad); +#ifdef LINUX26 + return IRQ_RETVAL(1); +#else + return; +#endif + } + + if (ints & 0x20) { + printk("RADIO PCI Target abort\n"); +#ifdef LINUX26 + return IRQ_RETVAL(1); +#else + return; +#endif + } + + if (ints & 0x0f) { + rad->intcount++; + x = rad->intcount % rad->nchans; + mx828_command(rad, x, MX828_SAUDIO_STATUS, &byte1, &byte2); + rad->saudio_status[x] = byte1; + /* get COR input */ + byte2 = __pciradio_getcreg(rad,0); + /* get bit for this channel */ + gotcor = byte2 & (1 << x); + if (rad->radmode[x] & RADMODE_INVERTCOR) gotcor = !gotcor; + rad->gotcor[x] = gotcor; + if (rad->radmode[x] & RADMODE_IGNORECOR) gotcor = 1; + gotslowctcss = 0; + if ((byte1 & RAD_CTCSSVALID) && + ((byte1 & RAD_CTCSSMASK) != RAD_CTCSSOTHER)) gotslowctcss = 1; + gotctcss = 1; + ctcss = 0; + /* set ctcss to 1 if decoding ctcss */ + if (!rad->rxcode[x][0]) + { + for(i = 1; i <= NUM_CODES; i++) + { + if (rad->rxcode[x][i]) + { + ctcss = 1; + break; + } + } + } + if (ctcss) + { + if ((!(byte1 & 0x40)) || + ((!rad->gotrx[x]) && (!gotslowctcss))) gotctcss = 0; + } + rad->present_code[x] = 0; + if (rad->rxcode[x][0]) + { + if (byte1 & 0x80) gotctcss = gotslowctcss = 1; else gotctcss = 0; + } else if (gotslowctcss) rad->present_code[x] = (byte1 & RAD_CTCSSMASK) + 1; + if (rad->radmode[x] & RADMODE_EXTTONE) + { + unsigned mask = 1 << (x + 4); + + if (rad->radmode[x] & RADMODE_EXTINVERT) + gotctcss = gotslowctcss = ((byte2 & mask) == 0); + else + gotctcss = gotslowctcss = ((byte2 & mask) != 0); + } + rad->gotct[x] = gotslowctcss; + if (rad->radmode[x] & RADMODE_IGNORECT) + { + gotctcss = 1; + gotslowctcss = 1; + rad->present_code[x] = 0; + } + gotrx = gotcor && gotctcss; + if (gotrx != rad->gotrx[x]) + { + rad->gotrxtimer[x] = rad->debouncetime[x]; + } + rad->gotrx[x] = gotrx; + for(x = 0; x < rad->nchans; x++) + { + unsigned char mask = 1 << x; + + if (rad->gottx[x] != rad->lasttx[x]) + { + if (rad->gottx[x]) + { + rad->bursttimer[x] = 0; + rad->p1save |= mask; + __pciradio_setcreg(rad, 1, rad->p1save); + } + else + { + if (!rad->bursttime[x]) + { + rad->p1save &= ~mask; + __pciradio_setcreg(rad, 1, rad->p1save); + } + else + { + rad->bursttimer[x] = rad->bursttime[x]; + } + } + _set_encdec(rad,x); + rad->lasttx[x] = rad->gottx[x]; + } + if (rad->bursttimer[x]) + { + /* if just getting to zero */ + if (!(--rad->bursttimer[x])) + { + unsigned char mask = 1 << x; + + rad->p1save &= ~mask; + __pciradio_setcreg(rad, 1, rad->p1save); + } + } + + /* if timer active */ + if (rad->gotrxtimer[x]) + { + /* if just getting to zero */ + if (!(--rad->gotrxtimer[x])) + { + unsigned char mask; + + mask = 1 << (x + 4); + rad->p1save &= ~mask; + if (gotctcss) rad->p1save |= mask; + __pciradio_setcreg(rad, 1, rad->p1save); + + if (rad->gotrx[x] != rad->gotrx1[x]) + { + if (rad->gotrx[x]) { + if (debug) printk("Chan %d got rx (ctcss code %d)\n",x + 1,rad->present_code[x]); + zt_qevent_lock(&rad->chans[x], ZT_EVENT_RINGOFFHOOK); + } else { + if (debug) printk("Chan %d lost rx\n",x + 1); + zt_qevent_lock(&rad->chans[x], ZT_EVENT_ONHOOK); + } + _set_encdec(rad,x); + } + rad->gotrx1[x] = rad->gotrx[x]; + } + } + } + pciradio_receiveprep(rad, ints); + pciradio_transmitprep(rad, ints); + } +#ifdef LINUX26 + return IRQ_RETVAL(1); +#endif + +} + +static int pciradio_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) +{ + int i,mycode; + unsigned long flags; + unsigned char byte1,byte2; + union { + struct zt_radio_stat s; + struct zt_radio_param p; + } stack; + + struct pciradio *rad = chan->pvt; + + switch (cmd) { + case ZT_RADIO_GETPARAM: + if (copy_from_user(&stack.p,(struct zt_radio_param *)data,sizeof(struct zt_radio_param))) return -EFAULT; + spin_lock_irqsave(&rad->lock,flags); + stack.p.data = 0; /* start with 0 value in output */ + switch(stack.p.radpar) { + case ZT_RADPAR_INVERTCOR: + if (rad->radmode[chan->chanpos - 1] & RADMODE_INVERTCOR) + stack.p.data = 1; + break; + case ZT_RADPAR_IGNORECOR: + if (rad->radmode[chan->chanpos - 1] & RADMODE_IGNORECOR) + stack.p.data = 1; + break; + case ZT_RADPAR_IGNORECT: + if (rad->radmode[chan->chanpos - 1] & RADMODE_IGNORECT) + stack.p.data = 1; + break; + case ZT_RADPAR_NOENCODE: + if (rad->radmode[chan->chanpos - 1] & RADMODE_NOENCODE) + stack.p.data = 1; + break; + case ZT_RADPAR_CORTHRESH: + stack.p.data = rad->corthresh[chan->chanpos - 1] & 7; + break; + case ZT_RADPAR_EXTRXTONE: + if (rad->radmode[chan->chanpos - 1] & RADMODE_EXTTONE) + { + stack.p.data = 1; + if (rad->radmode[chan->chanpos - 1] & RADMODE_EXTINVERT) + { + stack.p.data = 2; + } + } + break; + case ZT_RADPAR_NUMTONES: + stack.p.data = NUM_CODES; + break; + case ZT_RADPAR_RXTONE: + if ((stack.p.index < 1) || (stack.p.index > NUM_CODES)) { + spin_unlock_irqrestore(&rad->lock,flags); + return -EINVAL; + } + stack.p.data = + cttable_rx[rad->rxcode[chan->chanpos - 1][stack.p.index] & 0x7fff].code; + break; + case ZT_RADPAR_RXTONECLASS: + if ((stack.p.index < 1) || (stack.p.index > NUM_CODES)) { + spin_unlock_irqrestore(&rad->lock,flags); + return -EINVAL; + } + stack.p.data = rad->rxclass[chan->chanpos - 1][stack.p.index] & 0xffff; + break; + case ZT_RADPAR_TXTONE: + if (stack.p.index > NUM_CODES) { + spin_unlock_irqrestore(&rad->lock,flags); + return -EINVAL; + } + stack.p.data = cttable_tx[rad->txcode[chan->chanpos - 1][stack.p.index] & 0x7fff].code; + /* if a DCS tone, return as such */ + if (rad->txcode[chan->chanpos - 1][stack.p.index] & 0x8000) + stack.p.data |= 0x8000; + break; + case ZT_RADPAR_DEBOUNCETIME: + stack.p.data = rad->debouncetime[chan->chanpos - 1]; + break; + case ZT_RADPAR_BURSTTIME: + stack.p.data = rad->bursttime[chan->chanpos - 1]; + break; + default: + spin_unlock_irqrestore(&rad->lock,flags); + return -EINVAL; + } + spin_unlock_irqrestore(&rad->lock,flags); + if (copy_to_user((struct zt_radio_param *)data,&stack.p,sizeof(struct zt_radio_param))) return -EFAULT; + break; + case ZT_RADIO_SETPARAM: + if (copy_from_user(&stack.p,(struct zt_radio_param *)data,sizeof(struct zt_radio_param))) return -EFAULT; + spin_lock_irqsave(&rad->lock,flags); + switch(stack.p.radpar) { + case ZT_RADPAR_INVERTCOR: + if (stack.p.data) + rad->radmode[chan->chanpos - 1] |= RADMODE_INVERTCOR; + else + rad->radmode[chan->chanpos - 1] &= ~RADMODE_INVERTCOR; + break; + case ZT_RADPAR_IGNORECOR: + if (stack.p.data) + rad->radmode[chan->chanpos - 1] |= RADMODE_IGNORECOR; + else + rad->radmode[chan->chanpos - 1] &= ~RADMODE_IGNORECOR; + break; + case ZT_RADPAR_IGNORECT: + if (stack.p.data) + rad->radmode[chan->chanpos - 1] |= RADMODE_IGNORECT; + else + rad->radmode[chan->chanpos - 1] &= ~RADMODE_IGNORECT; + break; + case ZT_RADPAR_NOENCODE: + if (stack.p.data) + rad->radmode[chan->chanpos - 1] |= RADMODE_NOENCODE; + else + rad->radmode[chan->chanpos - 1] &= ~RADMODE_NOENCODE; + break; + case ZT_RADPAR_CORTHRESH: + if ((stack.p.data < 0) || (stack.p.data > 7)) { + spin_unlock_irqrestore(&rad->lock,flags); + return -EINVAL; + } + rad->corthresh[chan->chanpos - 1] = stack.p.data; + break; + case ZT_RADPAR_EXTRXTONE: + if (stack.p.data) + rad->radmode[chan->chanpos - 1] |= RADMODE_EXTTONE; + else + rad->radmode[chan->chanpos - 1] &= ~RADMODE_EXTTONE; + if (stack.p.data > 1) + rad->radmode[chan->chanpos - 1] |= RADMODE_EXTINVERT; + else + rad->radmode[chan->chanpos - 1] &= ~RADMODE_EXTINVERT; + break; + case ZT_RADPAR_INITTONE: + for(i = 0; i <= NUM_CODES; i++) + { + rad->rxcode[chan->chanpos - 1][i] = 0; + rad->rxclass[chan->chanpos - 1][i] = 0; + rad->txcode[chan->chanpos - 1][i] = 0; + } + break; + for(i = 0; i < NUM_CODES; i++) + { + /* set to no encode/decode */ + byte1 = 0; + mx828_command(rad,chan->chanpos - 1, MX828_SAUDIO_CTRL, &byte1, &byte2 ); + /* set rx tone to none */ + byte1 = i << 4; + byte2 = 0; + mx828_command(rad,chan->chanpos - 1, MX828_RX_TONE, &byte1, &byte2 ); + } + case ZT_RADPAR_RXTONE: + if (!stack.p.index) /* if RX DCS mode */ + { + if ((stack.p.data < 0) || (stack.p.data > 777)) { + spin_unlock_irqrestore(&rad->lock,flags); + return -EINVAL; + } + mycode = getdcstone(stack.p.data); + if (mycode < 0) { + spin_unlock_irqrestore(&rad->lock,flags); + return -EINVAL; + } + rad->rxcode[chan->chanpos - 1][0] = mycode; + _set_encdec(rad,chan->chanpos - 1); + break; + } + if ((stack.p.index < 1) || (stack.p.index > NUM_CODES)) { + spin_unlock_irqrestore(&rad->lock,flags); + return -EINVAL; + } + mycode = getrxtone(stack.p.data); + if (mycode < 0) { + spin_unlock_irqrestore(&rad->lock,flags); + return -EINVAL; + } + rad->rxcode[chan->chanpos - 1][stack.p.index] = mycode; + byte1 = cttable_rx[mycode].b1 | ((stack.p.index - 1) << 4); + byte2 = cttable_rx[mycode].b2; + mx828_command(rad,chan->chanpos - 1, MX828_RX_TONE, &byte1, &byte2 ); + /* zot out DCS one if there */ + rad->rxcode[chan->chanpos - 1][0] = 0; + _set_encdec(rad,chan->chanpos - 1); + break; + case ZT_RADPAR_RXTONECLASS: + if ((stack.p.index < 1) || (stack.p.index > NUM_CODES)) { + spin_unlock_irqrestore(&rad->lock,flags); + return -EINVAL; + } + rad->rxclass[chan->chanpos - 1][stack.p.index] = stack.p.data & 0xffff; + break; + case ZT_RADPAR_TXTONE: + if (stack.p.index > NUM_CODES) { + spin_unlock_irqrestore(&rad->lock,flags); + return -EINVAL; + } + if (stack.p.data & 0x8000) /* if dcs */ + mycode = getdcstone(stack.p.data & 0x7fff); + else + mycode = gettxtone(stack.p.data); + if (mycode < 0) { + spin_unlock_irqrestore(&rad->lock,flags); + return -EINVAL; + } + if (stack.p.data & 0x8000) mycode |= 0x8000; + rad->txcode[chan->chanpos - 1][stack.p.index] = mycode; + _set_encdec(rad,chan->chanpos - 1); + break; + case ZT_RADPAR_DEBOUNCETIME: + rad->debouncetime[chan->chanpos - 1] = stack.p.data; + break; + case ZT_RADPAR_BURSTTIME: + rad->bursttime[chan->chanpos - 1] = stack.p.data; + break; + default: + spin_unlock_irqrestore(&rad->lock,flags); + return -EINVAL; + } + spin_unlock_irqrestore(&rad->lock,flags); + break; + case ZT_RADIO_GETSTAT: + spin_lock_irqsave(&rad->lock,flags); + /* start with clean object */ + memset(&stack.s,0,sizeof(struct zt_radio_stat)); + /* if we have rx */ + if (rad->gotrx[chan->chanpos - 1]) + { + stack.s.radstat |= ZT_RADSTAT_RX; + if (rad->rxcode[chan->chanpos - 1][0]) + stack.s.ctcode_rx = + dcstable[rad->rxcode[chan->chanpos - 1][0]].code | 0x8000; + else { + stack.s.ctcode_rx = + cttable_rx[rad->rxcode[chan->chanpos - 1][rad->present_code[chan->chanpos - 1]]].code; + stack.s.ctclass = + rad->rxclass[chan->chanpos - 1][rad->present_code[chan->chanpos - 1]]; + } + } + /* if we have tx */ + if (rad->gottx[chan->chanpos - 1]) + { + unsigned short x,myindex; + + stack.s.radstat |= ZT_RADSTAT_TX; + stack.s.radstat |= ZT_RADSTAT_TX; + + myindex = 0; + if ((!rad->rxcode[chan->chanpos - 1][0]) + && (rad->present_code[chan->chanpos - 1])) + myindex = rad->present_code[chan->chanpos - 1]; + x = rad->txcode[chan->chanpos - 1][myindex]; + if (x & 0x8000) + stack.s.ctcode_tx = dcstable[x & 0x7fff].code | 0x8000; + else + stack.s.ctcode_tx = cttable_tx[x].code; + + } + + if (rad->radmode[chan->chanpos - 1] & RADMODE_IGNORECOR) + stack.s.radstat |= ZT_RADSTAT_IGNCOR; + if (rad->radmode[chan->chanpos - 1] & RADMODE_IGNORECT) + stack.s.radstat |= ZT_RADSTAT_IGNCT; + if (rad->radmode[chan->chanpos - 1] & RADMODE_NOENCODE) + stack.s.radstat |= ZT_RADSTAT_NOENCODE; + if (rad->gotcor[chan->chanpos - 1]) + stack.s.radstat |= ZT_RADSTAT_RXCOR; + if (rad->gotct[chan->chanpos - 1]) + stack.s.radstat |= ZT_RADSTAT_RXCT; + spin_unlock_irqrestore(&rad->lock,flags); + if (copy_to_user((struct zt_radio_stat *)data,&stack.s,sizeof(struct zt_radio_stat))) return -EFAULT; + break; + default: + return -ENOTTY; + } + return 0; + +} + +static int pciradio_open(struct zt_chan *chan) +{ + struct pciradio *rad = chan->pvt; + if (rad->dead) + return -ENODEV; + rad->usecount++; +#ifndef LINUX26 + MOD_INC_USE_COUNT; +#endif + return 0; +} + +static int pciradio_watchdog(struct zt_span *span, int event) +{ + printk("PCI RADIO: Restarting DMA\n"); + pciradio_restart_dma(span->pvt); + return 0; +} + +static int pciradio_close(struct zt_chan *chan) +{ + struct pciradio *rad = chan->pvt; + rad->usecount--; +#ifndef LINUX26 + MOD_DEC_USE_COUNT; +#endif + /* If we're dead, release us now */ + if (!rad->usecount && rad->dead) + pciradio_release(rad); + return 0; +} + +static int pciradio_hooksig(struct zt_chan *chan, zt_txsig_t txsig) +{ + struct pciradio *rad = chan->pvt; + + switch(txsig) { + case ZT_TXSIG_START: + case ZT_TXSIG_OFFHOOK: + rad->gottx[chan->chanpos - 1] = 1; + break; + case ZT_TXSIG_ONHOOK: + rad->gottx[chan->chanpos - 1] = 0; + break; + default: + printk("pciradio: Can't set tx state to %d\n", txsig); + break; + } + if (debug) + printk("pciradio: Setting Radio hook state to %d\n", txsig); + return 0; +} + +static int pciradio_initialize(struct pciradio *rad) +{ + int x; + + /* Zapata stuff */ + sprintf(rad->span.name, "PCIRADIO/%d", rad->pos); + sprintf(rad->span.desc, "Board %d", rad->pos + 1); + rad->span.deflaw = ZT_LAW_MULAW; + for (x=0;x<rad->nchans;x++) { + sprintf(rad->chans[x].name, "PCIRADIO/%d/%d", rad->pos, x); + rad->chans[x].sigcap = ZT_SIG_SF | ZT_SIG_EM; + rad->chans[x].chanpos = x+1; + rad->chans[x].pvt = rad; + rad->debouncetime[x] = RAD_GOTRX_DEBOUNCE_TIME; + } + rad->span.chans = rad->chans; + rad->span.channels = rad->nchans; + rad->span.hooksig = pciradio_hooksig; + rad->span.open = pciradio_open; + rad->span.close = pciradio_close; + rad->span.flags = ZT_FLAG_RBS; + rad->span.ioctl = pciradio_ioctl; + rad->span.watchdog = pciradio_watchdog; + init_waitqueue_head(&rad->span.maintq); + + rad->span.pvt = rad; + if (zt_register(&rad->span, 0)) { + printk("Unable to register span with zaptel\n"); + return -1; + } + return 0; +} + +static int pciradio_hardware_init(struct pciradio *rad) +{ +unsigned char byte1,byte2; +int x; + + /* Signal Reset */ + outb(0x01, rad->ioaddr + RAD_CNTL); + + /* Reset PCI Interface chip and registers (and serial) */ + outb(0x06, rad->ioaddr + RAD_CNTL); + /* Setup our proper outputs for when we switch for our "serial" port */ + rad->ios = BIT_CS | BIT_SCLK | BIT_SDI; + + outb(rad->ios, rad->ioaddr + RAD_AUXD); + + /* Set all to outputs except AUX 2, which is an input */ + outb(0xfd, rad->ioaddr + RAD_AUXC); + + /* Select alternate function for AUX0 */ + outb(0x4, rad->ioaddr + RAD_AUXFUNC); + + /* Wait 1/4 of a sec */ + wait_just_a_bit(HZ/4); + + /* Back to normal, with automatic DMA wrap around */ + outb(0x30 | 0x01, rad->ioaddr + RAD_CNTL); + + /* Make sure serial port and DMA are out of reset */ + outb(inb(rad->ioaddr + RAD_CNTL) & 0xf9, RAD_CNTL); + + /* Configure serial port for MSB->LSB operation */ + outb(0xc1, rad->ioaddr + RAD_SERCTL); /* DEBUG set double dlck to 0 SR */ + + /* Delay FSC by 0 so it's properly aligned */ + outb(/* 1 */ 0, rad->ioaddr + RAD_FSCDELAY); + + /* Setup DMA Addresses */ + outl(rad->writedma, rad->ioaddr + RAD_DMAWS); /* Write start */ + outl(rad->writedma + ZT_CHUNKSIZE * 4 - 4, rad->ioaddr + RAD_DMAWI); /* Middle (interrupt) */ + outl(rad->writedma + ZT_CHUNKSIZE * 8 - 4, rad->ioaddr + RAD_DMAWE); /* End */ + + outl(rad->readdma, rad->ioaddr + RAD_DMARS); /* Read start */ + outl(rad->readdma + ZT_CHUNKSIZE * 4 - 4, rad->ioaddr + RAD_DMARI); /* Middle (interrupt) */ + outl(rad->readdma + ZT_CHUNKSIZE * 8 - 4, rad->ioaddr + RAD_DMARE); /* End */ + + /* Clear interrupts */ + outb(0xff, rad->ioaddr + RAD_INTSTAT); + + /* Wait 1/4 of a second more */ + wait_just_a_bit(HZ/4); + + for(x = 0; x < rad->nchans; x++) + { + mx828_command(rad,x, MX828_GEN_RESET, &byte1, &byte2 ); + byte1 = 0x3f; + byte2 = 0x3f; + mx828_command(rad,x, MX828_AUD_CTRL, &byte1, &byte2 ); + byte1 = 0x09; + byte2 = 0xD8; + mx828_command(rad,x, MX828_TX_TONE, &byte1, &byte2 ); + byte1 = 0x35; + byte2 = 0xcb; + mx828_command(rad,x, MX828_RX_TONE, &byte1, &byte2 ); + byte1 = 0x47; + byte2 = 0xc0; + mx828_command(rad,x, MX828_RX_TONE, &byte1, &byte2 ); +#if 0 + byte1 = 0x1e; + byte2 = 0x88; + mx828_command(rad,x, MX828_RX_TONE, &byte1, &byte2 ); + byte1 = 0x23; + byte2 = 0xd8; + mx828_command(rad,x, MX828_RX_TONE, &byte1, &byte2 ); + byte1 = 0x34; + byte2 = 0xcb; + mx828_command(rad,x, MX828_RX_TONE, &byte1, &byte2 ); +#endif + byte1 = 0x80; + mx828_command(rad,x, MX828_SAUDIO_SETUP, &byte1, &byte2 ); + byte1 = 0xe0; + mx828_command(rad,x, MX828_SAUDIO_CTRL, &byte1, &byte2 ); + byte1 = 0xc0; + mx828_command(rad,x, MX828_GEN_CTRL, &byte1, &byte2); + } + + udelay(1000); + + return 0; +} + +static void pciradio_enable_interrupts(struct pciradio *rad) +{ + /* Enable interrupts (we care about all of them) */ + outb(0x3f, rad->ioaddr + RAD_MASK0); + /* No external interrupts */ + outb(0x00, rad->ioaddr + RAD_MASK1); +} + +static void pciradio_restart_dma(struct pciradio *rad) +{ + /* Reset Master and serial */ + outb(0x01, rad->ioaddr + RAD_CNTL); + outb(0x01, rad->ioaddr + RAD_OPER); +} + +static void pciradio_start_dma(struct pciradio *rad) +{ + /* Reset Master and serial */ + outb(0x0f, rad->ioaddr + RAD_CNTL); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + outb(0x01, rad->ioaddr + RAD_CNTL); + outb(0x01, rad->ioaddr + RAD_OPER); +} + +static void pciradio_stop_dma(struct pciradio *rad) +{ + outb(0x00, rad->ioaddr + RAD_OPER); +} + +static void pciradio_reset_serial(struct pciradio *rad) +{ + /* Reset serial */ + outb(0x0f, rad->ioaddr + RAD_CNTL); +} + +static void pciradio_disable_interrupts(struct pciradio *rad) +{ + outb(0x00, rad->ioaddr + RAD_MASK0); + outb(0x00, rad->ioaddr + RAD_MASK1); +} + +static int __devinit pciradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int res; + struct pciradio *rad; + int x; + static int initd_ifaces=0; + + if(initd_ifaces){ + memset((void *)ifaces,0,(sizeof(struct pciradio *))*RAD_MAX_IFACES); + initd_ifaces=1; + } + for (x=0;x<RAD_MAX_IFACES;x++) + if (!ifaces[x]) break; + if (x >= RAD_MAX_IFACES) { + printk("Too many interfaces\n"); + return -EIO; + } + + if (pci_enable_device(pdev)) { + res = -EIO; + } else { + rad = kmalloc(sizeof(struct pciradio), GFP_KERNEL); + if (rad) { + int i; + + ifaces[x] = rad; + memset(rad, 0, sizeof(struct pciradio)); + spin_lock_init(&rad->lock); + rad->nchans = 4; + rad->ioaddr = pci_resource_start(pdev, 0); + rad->dev = pdev; + rad->pos = x; + for(i = 0; i < rad->nchans; i++) rad->lasttx[x] = rad->gotrx1[i] = -1; + /* Keep track of whether we need to free the region */ + if (request_region(rad->ioaddr, 0xff, "pciradio")) + rad->freeregion = 1; + + /* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses + 32 bits. Allocate an extra set just for control too */ + rad->writechunk = (int *)pci_alloc_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, &rad->writedma); + if (!rad->writechunk) { + printk("pciradio: Unable to allocate DMA-able memory\n"); + if (rad->freeregion) + release_region(rad->ioaddr, 0xff); + return -ENOMEM; + } + + rad->readchunk = rad->writechunk + ZT_MAX_CHUNKSIZE * 2; /* in doublewords */ + rad->readdma = rad->writedma + ZT_MAX_CHUNKSIZE * 8; /* in bytes */ + + if (pciradio_initialize(rad)) { + printk("pciradio: Unable to intialize FXS\n"); + /* Set Reset Low */ + x=inb(rad->ioaddr + RAD_CNTL); + outb((~0x1)&x, rad->ioaddr + RAD_CNTL); + /* Free Resources */ + free_irq(pdev->irq, rad); + if (rad->freeregion) + release_region(rad->ioaddr, 0xff); + pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)rad->writechunk, rad->writedma); + kfree(rad); + return -EIO; + } + + /* Enable bus mastering */ + pci_set_master(pdev); + + /* Keep track of which device we are */ + pci_set_drvdata(pdev, rad); + + if (request_irq(pdev->irq, pciradio_interrupt, SA_SHIRQ, "pciradio", rad)) { + printk("pciradio: Unable to request IRQ %d\n", pdev->irq); + if (rad->freeregion) + release_region(rad->ioaddr, 0xff); + pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)rad->writechunk, rad->writedma); + pci_set_drvdata(pdev, NULL); + kfree(rad); + return -EIO; + } + + + if (pciradio_hardware_init(rad)) { + unsigned char x; + /* Set Reset Low */ + x=inb(rad->ioaddr + RAD_CNTL); + outb((~0x1)&x, rad->ioaddr + RAD_CNTL); + /* Free Resources */ + free_irq(pdev->irq, rad); + if (rad->freeregion) + release_region(rad->ioaddr, 0xff); + pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)rad->writechunk, rad->writedma); + pci_set_drvdata(pdev, NULL); + zt_unregister(&rad->span); + kfree(rad); + return -EIO; + + } + + /* Enable interrupts */ + pciradio_enable_interrupts(rad); + /* Initialize Write/Buffers to all blank data */ + memset((void *)rad->writechunk,0,ZT_MAX_CHUNKSIZE * 2 * 2 * 4); + + /* Start DMA */ + pciradio_start_dma(rad); + + printk("Found a PCI Radio Card\n"); + res = 0; + } else + res = -ENOMEM; + } + return res; +} + +static void pciradio_release(struct pciradio *rad) +{ + zt_unregister(&rad->span); + if (rad->freeregion) + release_region(rad->ioaddr, 0xff); + kfree(rad); + printk("Freed a PCI RADIO card\n"); +} + +static void __devexit pciradio_remove_one(struct pci_dev *pdev) +{ + struct pciradio *rad = pci_get_drvdata(pdev); + if (rad) { + + /* Stop any DMA */ + pciradio_stop_dma(rad); + pciradio_reset_serial(rad); + + /* In case hardware is still there */ + pciradio_disable_interrupts(rad); + + /* Immediately free resources */ + pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)rad->writechunk, rad->writedma); + free_irq(pdev->irq, rad); + + /* Reset PCI chip and registers */ + outb(0x0e, rad->ioaddr + RAD_CNTL); + + /* Release span, possibly delayed */ + if (!rad->usecount) + pciradio_release(rad); + else + rad->dead = 1; + } +} + +static struct pci_device_id pciradio_pci_tbl[] = { + { 0xe159, 0x0001, 0xe16b, PCI_ANY_ID, 0, 0, (unsigned long)"PCIRADIO" }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, pciradio_pci_tbl); + +static struct pci_driver pciradio_driver = { + name: "pciradio", + probe: pciradio_init_one, +#ifdef LINUX26 + remove: __devexit_p(pciradio_remove_one), +#else + remove: pciradio_remove_one, +#endif + suspend: NULL, + resume: NULL, + id_table: pciradio_pci_tbl, +}; + +static int __init pciradio_init(void) +{ + int res; + + res = pci_module_init(&pciradio_driver); + if (res) + return -ENODEV; + return 0; +} + +static void __exit pciradio_cleanup(void) +{ + pci_unregister_driver(&pciradio_driver); +} + +MODULE_PARM(debug, "i"); +MODULE_DESCRIPTION("Zapate Telephony PCI Radio Card Zaptel Driver"); +MODULE_AUTHOR("Jim Dixon <jim@lambdatel.com>"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +module_init(pciradio_init); +module_exit(pciradio_cleanup); + + + @@ -274,6 +274,8 @@ of the next sample chunk's data (next time around the world). #include "digits.h" +static int zt_chan_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data, int unit); + #if defined(CONFIG_ZAPTEL_MMX) || defined(ECHO_CAN_FP) /* XXX kernel_fpu_begin() is NOT exported properly (in 2.4), so we have to make a local version. Somebody fix this! XXX */ @@ -2687,6 +2689,7 @@ static int zt_timer_ioctl(struct inode *node, struct file *file, unsigned int cm } return 0; } + static int zt_common_ioctl(struct inode *node, struct file *file, unsigned int cmd, unsigned long data, int unit) { union { @@ -2989,9 +2992,15 @@ static int zt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd struct zt_chan *newmaster; struct zt_dialparams tdp; struct zt_maintinfo maint; + struct zt_indirect_data ind; unsigned long flags; int rv; switch(cmd) { + case ZT_INDIRECT: + if (copy_from_user(&ind, (struct zt_indirect_data *)data, sizeof(ind))) + return -EFAULT; + VALID_CHANNEL(ind.chan); + return zt_chan_ioctl(inode, file, ind.op, (unsigned long) ind.data, ind.chan); case ZT_SPANCONFIG: if (copy_from_user(&lc, (struct zt_lineconfig *)data, sizeof(lc))) return -EFAULT; diff --git a/zaptel.conf.sample b/zaptel.conf.sample index 0d4558c..6c670b0 100755 --- a/zaptel.conf.sample +++ b/zaptel.conf.sample @@ -140,3 +140,64 @@ loadzone = us #loadzone=sp #loadzone=no defaultzone=us +# +# Section for PCI Radio Interface +# (see http://www.zapatatelephony.org/app_rpt.html) +# +# The PCI Radio Interface card interfaces up to 4 two-way radios (either +# a base/mobile radio or repeater system) to Zaptel channels. The driver +# may work either independent of an application, or with it, through +# the driver;s ioctl() interface. This file gives you access to specify +# load-time parameters for Radio channels, so that the driver may run +# by itself, and just act like a generic Zaptel radio interface. +# +# Unlike the rest of this file, you specify a block of parameters, and +# then the channel(s) to which they apply. CTCSS is specified as a frequency +# in tenths of hertz, for example 131.8 HZ is specified as 1318. DCS +# for receive is specified as the code directly, for example 223. DCS for +# transmit is specified as D and then the code, for example D223. +# +# The hardware supports a "community" CTCSS decoder system that has +# arbitrary transmit CTCSS or DCS codes associated with them, unlike +# traditional "community" systems that encode the same tone they decode. +# +# this example is a single tone DCS transmit and receive +# +# # specify the transmit tone (in DCS mode this stays constant) +# tx=D371 +# # specify the receive DCS code +# dcsrx=223 +# +# this example is a "community" CTCSS (if you only want a single tone, then +# only specify 1 in the ctcss list) +# +# # specify the default transmit tone (when not receiving) +# tx=1000 +# # Specify the receive freq, the tag (use 0 if none), and the transmit code. +# # The tag may be used by applications to determine classification of tones. +# # The tones are to be specified in order of presedence, most important first. +# # Currently, 15 tones may be specified.. +# ctcss=1318,1,1318 +# ctcss=1862,1,1862 +# +# The following parameters may be omitted if their default value is acceptible +# +# # set the receive debounce time in milliseconds +# debouncetime=123 +# # set the transmit quiet dropoff burst time in milliseconds +# bursttime=234 +# # set the COR level threshold (specified in tenths of millivolts) +# # valid values are {3125,6250,9375,12500,15625,18750,21875,25000} +# corthresh=12500 +# # Invert COR signal {y,n} +# invertcor=y +# # set the external tone mode; yes, no, internal {y,n,i} +# exttone=y +# +# Now apply the configuration to the specified channels: +# +# # We are all done with our channel parameters, so now we specify what +# # channels they apply to +# channels=1-4 + + @@ -304,6 +304,14 @@ char dialstr[ZT_MAX_DTMF_BUF]; } ZT_DIAL_OPERATION; +typedef struct zt_indirect_data +{ +int chan; +int op; +void *data; +} ZT_INDIRECT_DATA; + + /* ioctl definitions */ #define ZT_CODE 'J' @@ -590,6 +598,12 @@ char dialstr[ZT_MAX_DTMF_BUF]; #define ZT_GETSIGFREEZE _IOR (ZT_CODE, 55, int) /* + * Do a channel IOCTL from the /dev/zap/ctl interface + */ +#define ZT_INDIRECT _IOWR (ZT_CODE, 56, struct zt_indirect_data) + + +/* * 60-80 are reserved for private drivers * 80-85 are reserved for dynamic span stuff */ @@ -1441,4 +1455,92 @@ static inline short zt_txtone_nextsample(struct zt_chan *ss) #endif /* __KERNEL__ */ +/* The following is for the PCI RADIO interface only. This is specified in +this file because external processes need to interact with the device. +Some devices have private functions used for test/diagnostic only, but +this is not the case here. */ + +struct zt_radio_stat { + unsigned short ctcode_rx; /* code of currently received CTCSS + or DCS, 0 for none */ + unsigned short ctclass; /* class of currently received CTCSS or + DCS code */ + unsigned short ctcode_tx; /* code of currently encoded CTCSS or + DCS, 0 for none */ + unsigned char radstat; /* status bits of radio */ +}; + +struct zt_radio_param { + unsigned short radpar; /* param identifier */ + unsigned short index; /* tone number */ + int data; /* pointer to param */ +}; + + +/* Get current status IOCTL */ +#define ZT_RADIO_GETSTAT _IOR (ZT_CODE, 57, struct zt_radio_stat) +/* Set a channel parameter IOCTL */ +#define ZT_RADIO_SETPARAM _IOW (ZT_CODE, 58, struct zt_radio_param) +/* Get a channel parameter IOCTL */ +#define ZT_RADIO_GETPARAM _IOR (ZT_CODE, 59, struct zt_radio_param) + + +/* Defines for Radio Status (zt_radio_stat.radstat) bits */ + +#define ZT_RADSTAT_RX 1 /* currently "receiving " */ +#define ZT_RADSTAT_TX 2 /* currently "transmitting" */ +#define ZT_RADSTAT_RXCT 4 /* currently receiving continuous tone with + current settings */ +#define ZT_RADSTAT_RXCOR 8 /* currently receiving COR (irrelevant of COR + ignore) */ +#define ZT_RADSTAT_IGNCOR 16 /* currently ignoring COR */ +#define ZT_RADSTAT_IGNCT 32 /* currently ignoring CTCSS/DCS decode */ +#define ZT_RADSTAT_NOENCODE 64 /* currently blocking CTCSS/DCS encode */ + +/* Defines for Radio Parameters (zt_radio_param.radpar) */ + +#define ZT_RADPAR_INVERTCOR 1 /* invert the COR signal (0/1) */ +#define ZT_RADPAR_IGNORECOR 2 /* ignore the COR signal (0/1) */ +#define ZT_RADPAR_IGNORECT 3 /* ignore the CTCSS/DCS decode (0/1) */ +#define ZT_RADPAR_NOENCODE 4 /* block the CTCSS/DCS encode (0/1) */ +#define ZT_RADPAR_CORTHRESH 5 /* COR trigger threshold (0-7) */ + +#define ZT_RADPAR_EXTRXTONE 6 /* 0 means use internal decoder, 1 means UIOA + logic true is CT decode, 2 means UIOA logic + false is CT decode */ +#define ZT_RADPAR_NUMTONES 7 /* returns maximum tone index (curently 15) */ +#define ZT_RADPAR_INITTONE 8 /* init all tone indexes to 0 (no tones) */ +#define ZT_RADPAR_RXTONE 9 /* CTCSS tone, (1-32) or DCS tone (1-777), + or 0 meaning no tone, set index also (1-15) */ +#define ZT_RADPAR_RXTONECLASS 10 /* Tone class (0-65535), set index also (1-15) */ +#define ZT_RADPAR_TXTONE 11 /* CTCSS tone (1-32) or DCS tone (1-777) or 0 + to indicate no tone, to transmit + for this tone index (0-32, 0 disables + transmit CTCSS), set index also (0-15) */ +#define ZT_RADPAR_DEBOUNCETIME 12 /* receive indication debounce time, + milliseconds (1-999) */ +#define ZT_RADPAR_BURSTTIME 13 /* end of transmit with no CT tone in + milliseconds (0-999) */ + +#if 0 +The following are not implemented in the prototype version of the card: +(UIO will always be an input for external CTCSS decode input) + +#define ZT_RADPAR_UIODATA 14 /* read/write UIOA and UIOB data. Bit 0 is + UIOA, bit 1 is UIOB */ +#define ZT_RADPAR_UIOMODE 15 /* 0 means UIOA and UIOB are both outputs, 1 + means UIOA is input, UIOB is output, 2 + means UIOB is input and UIOA is output, + 3 means both UIOA and UIOB are inputs. Note + mode for UIOA is overridden when in + EXTRXTONE mode. */ + +#define ZT_RADPAR_EXTDATA 16 /* read/write external byte, set index also + (0-2) */ +#define ZT_RADPAR_EXTMODE 17 /* set mode for external byte, bitwise a 1 + means write, and 0 means read, set index + also (0-2) */ + +#endif + #endif /* _LINUX_ZAPTEL_H */ @@ -25,7 +25,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Primary Author: Mark Spencer <markster@linux-support.net> - * + * Radio Support by Jim Dixon <jim@lambdatel.com> */ #include <stdio.h> @@ -43,6 +43,8 @@ #define NUM_SPANS ZT_MAX_SPANS +#define NUM_TONES 15 + /* Assume no more than 1024 dynamics */ #define NUM_DYNAMIC 1024 @@ -52,6 +54,13 @@ static FILE *cf; static char *filename=CONFIG_FILENAME; +int rxtones[NUM_TONES + 1],rxtags[NUM_TONES + 1],txtones[NUM_TONES + 1]; +int bursttime = 0, debouncetime = 0, invertcor = 0, exttone = 0, corthresh = 0; + +int corthreshes[] = {3125,6250,9375,12500,15625,18750,21875,25000,0} ; + +static int toneindex = 1; + #define DEBUG_READER (1 << 0) #define DEBUG_PARSER (1 << 1) #define DEBUG_APPLY (1 << 2) @@ -85,6 +94,8 @@ static char zonestoload[ZT_TONE_ZONE_MAX][10]; static int numzones = 0; +static int fd = -1; + static char *lbostr[] = { "0 db (CSU)/0-133 feet (DSX-1)", "133-266 feet (DSX-1)", @@ -102,6 +113,28 @@ static char *laws[] = { "A-law" }; +int ind_ioctl(int channo, int fd, int op, void *data) +{ +ZT_INDIRECT_DATA ind; + + ind.chan = channo; + ind.op = op; + ind.data = data; + return ioctl(fd,ZT_INDIRECT,&ind); +} + +static void clear_fields() +{ + + memset(rxtones,0,sizeof(rxtones)); + memset(rxtags,0,sizeof(rxtags)); + memset(txtones,0,sizeof(txtones)); + bursttime = 0; + debouncetime = 0; + invertcor = 0; + exttone = 0; +} + static int error(char *fmt, ...) { int res; @@ -558,6 +591,382 @@ static int unimplemented(char *keyword, char *args) } #endif + +/* Radio functions */ + +int ctcss(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int rxtone; + int rxtag; + int txtone; + int isdcs = 0; + argc = res = parseargs(args, realargs, 3, ','); + if (res != 3) { + error("Incorrect number of arguments to 'ctcss' (should be <rxtone>,<rxtag>,<txtone>)\n"); + } + res = sscanf(realargs[0], "%d", &rxtone); + if ((res == 1) && (rxtone < 1)) + res = -1; + if (res != 1) { + error("Invalid rxtone '%s', should be a number > 0.\n", realargs[0]); + } + res = sscanf(realargs[1], "%i", &rxtag); + if ((res == 1) && (rxtag < 0)) + res = -1; + if (res != 1) { + error("Invalid rxtag '%s', should be a number > 0.\n", realargs[1]); + } + if ((*realargs[2] == 'D') || (*realargs[2] == 'd')) + { + realargs[2]++; + isdcs = 0x8000; + } + res = sscanf(realargs[2], "%d", &txtone); + if ((res == 1) && (rxtag < 0)) + res = -1; + if (res != 1) { + error("Invalid txtone '%s', should be a number > 0.\n", realargs[2]); + } + + if (toneindex >= NUM_TONES) + { + error("Cannot specify more then %d CTCSS tones\n",NUM_TONES); + } + rxtones[toneindex] = rxtone; + rxtags[toneindex] = rxtag; + txtones[toneindex] = txtone | isdcs; + toneindex++; + return 0; +} + +int dcsrx(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int rxtone; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'dcsrx' (should be <rxtone>)\n"); + } + res = sscanf(realargs[0], "%d", &rxtone); + if ((res == 1) && (rxtone < 1)) + res = -1; + if (res != 1) { + error("Invalid rxtone '%s', should be a number > 0.\n", realargs[0]); + } + + rxtones[0] = rxtone; + return 0; +} + +int tx(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int txtone; + int isdcs = 0; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'tx' (should be <txtone>)\n"); + } + if ((*realargs[0] == 'D') || (*realargs[0] == 'd')) + { + realargs[0]++; + isdcs = 0x8000; + } + res = sscanf(realargs[0], "%d", &txtone); + if ((res == 1) && (txtone < 1)) + res = -1; + if (res != 1) { + error("Invalid tx (tone) '%s', should be a number > 0.\n", realargs[0]); + } + + txtones[0] = txtone | isdcs; + return 0; +} + +int debounce_time(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int val; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'debouncetime' (should be <value>)\n"); + } + res = sscanf(realargs[0], "%d", &val); + if ((res == 1) && (val < 1)) + res = -1; + if (res != 1) { + error("Invalid value '%s', should be a number > 0.\n", realargs[0]); + } + + debouncetime = val; + return 0; +} + +int burst_time(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int val; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'bursttime' (should be <value>)\n"); + } + res = sscanf(realargs[0], "%d", &val); + if ((res == 1) && (val < 1)) + res = -1; + if (res != 1) { + error("Invalid value '%s', should be a number > 0.\n", realargs[0]); + } + + bursttime = val; + return 0; +} + +int invert_cor(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int val; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'invertcor' (should be <value>)\n"); + } + if ((*realargs[0] == 'y') || (*realargs[0] == 'Y')) val = 1; + else if ((*realargs[0] == 'n') || (*realargs[0] == 'N')) val = 0; + else + { + res = sscanf(realargs[0], "%d", &val); + if ((res == 1) && (val < 0)) + res = -1; + if (res != 1) { + error("Invalid value '%s', should be a number > 0.\n", realargs[0]); + } + } + invertcor = (val > 0); + return 0; +} + +int ext_tone(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int val; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'exttone' (should be <value>)\n"); + } + if ((*realargs[0] == 'y') || (*realargs[0] == 'Y')) val = 1; + else if ((*realargs[0] == 'n') || (*realargs[0] == 'N')) val = 0; + else if ((*realargs[0] == 'i') || (*realargs[0] == 'I')) val = 2; + else + { + res = sscanf(realargs[0], "%d", &val); + if ((res == 1) && (val < 0)) + res = -1; + if (val > 2) res = -1; + if (res != 1) { + error("Invalid value '%s', should be a number > 0.\n", realargs[0]); + } + } + exttone = val; + return 0; +} + +int cor_thresh(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int val; + int x = 0; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'corthresh' (should be <value>)\n"); + } + res = sscanf(realargs[0], "%d", &val); + if ((res == 1) && (val < 1)) + res = -1; + for(x = 0; corthreshes[x]; x++) + { + if (corthreshes[x] == val) break; + } + if (!corthreshes[x]) res = -1; + if (res != 1) { + error("Invalid value '%s', should be a number > 0.\n", realargs[0]); + } + corthresh = x; + return 0; +} + +int rad_apply_channels(int chans[], char *argstr) +{ + char *args[ZT_MAX_CHANNELS+1]; + char *range[3]; + int res,x, res2,y; + int chan; + int start, finish; + char argcopy[256]; + res = parseargs(argstr, args, ZT_MAX_CHANNELS, ','); + if (res < 0) + error("Too many arguments... Max is %d\n", ZT_MAX_CHANNELS); + for (x=0;x<res;x++) { + if (strchr(args[x], '-')) { + /* It's a range */ + strncpy(argcopy, args[x], sizeof(argcopy)); + res2 = parseargs(argcopy, range, 2, '-'); + if (res2 != 2) { + error("Syntax error in range '%s'. Should be <val1>-<val2>.\n", args[x]); + return -1; + } + res2 =sscanf(range[0], "%i", &start); + if (res2 != 1) { + error("Syntax error. Start of range '%s' should be a number from 1 to %d\n", args[x], ZT_MAX_CHANNELS - 1); + return -1; + } else if ((start < 1) || (start >= ZT_MAX_CHANNELS)) { + error("Start of range '%s' must be between 1 and %d (not '%d')\n", args[x], ZT_MAX_CHANNELS - 1, start); + return -1; + } + res2 =sscanf(range[1], "%i", &finish); + if (res2 != 1) { + error("Syntax error. End of range '%s' should be a number from 1 to %d\n", args[x], ZT_MAX_CHANNELS - 1); + return -1; + } else if ((finish < 1) || (finish >= ZT_MAX_CHANNELS)) { + error("end of range '%s' must be between 1 and %d (not '%d')\n", args[x], ZT_MAX_CHANNELS - 1, finish); + return -1; + } + if (start > finish) { + error("Range '%s' should start before it ends\n", args[x]); + return -1; + } + for (y=start;y<=finish;y++) + chans[y]=1; + } else { + /* It's a single channel */ + res2 =sscanf(args[x], "%i", &chan); + if (res2 != 1) { + error("Syntax error. Channel should be a number from 1 to %d, not '%s'\n", ZT_MAX_CHANNELS - 1, args[x]); + return -1; + } else if ((chan < 1) || (chan >= ZT_MAX_CHANNELS)) { + error("Channel must be between 1 and %d (not '%d')\n", ZT_MAX_CHANNELS - 1, chan); + return -1; + } + chans[chan]=1; + } + } + return res; +} + +static int rad_chanconfig(char *keyword, char *args) +{ + int chans[ZT_MAX_CHANNELS]; + int res = 0; + int x,i,n; + struct zt_radio_param p; + + bzero(chans, sizeof(chans)); + res = rad_apply_channels(chans, args); + if (res <= 0) + return -1; + for (x=1;x<ZT_MAX_CHANNELS;x++) { + if (chans[x]) { + + p.radpar = ZT_RADPAR_NUMTONES; + if (ind_ioctl(x,fd,ZT_RADIO_GETPARAM,&p) == -1) + error("Cannot get number of tones chanel %d\n",x); + n = p.data; + if (!rxtones[0]) for(i = 1; i <= n; i++) + { + if (rxtones[i]) + { + p.radpar = ZT_RADPAR_RXTONE; + p.index = i; + p.data = rxtones[i]; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set rxtone on channel %d\n",x); + } + if (rxtags[i]) + { + p.radpar = ZT_RADPAR_RXTONECLASS; + p.index = i; + p.data = rxtags[i]; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set rxtag on channel %d\n",x); + } + if (txtones[i]) + { + p.radpar = ZT_RADPAR_RXTONE; + p.index = i; + p.data = txtones[i]; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set txtone on channel %d\n",x); + } + } else { /* if we have DCS receive */ + p.radpar = ZT_RADPAR_RXTONE; + p.index = 0; + p.data = rxtones[0]; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set DCS rxtone on channel %d\n",x); + } + if (txtones[0]) + { + p.radpar = ZT_RADPAR_TXTONE; + p.index = 0; + p.data = txtones[0]; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set default txtone on channel %d\n",x); + } + if (debouncetime) + { + p.radpar = ZT_RADPAR_DEBOUNCETIME; + p.data = debouncetime; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set debouncetime on channel %d\n",x); + } + if (bursttime) + { + p.radpar = ZT_RADPAR_BURSTTIME; + p.data = bursttime; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set bursttime on channel %d\n",x); + } + if (invertcor) + { + p.radpar = ZT_RADPAR_INVERTCOR; + p.data = invertcor; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set invertcor on channel %d\n",x); + } + if (exttone) + { + p.radpar = ZT_RADPAR_EXTRXTONE; + p.data = exttone; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set exttone on channel %d\n",x); + } + p.radpar = ZT_RADPAR_CORTHRESH; + p.data = corthresh; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set corthresh on channel %d\n",x); + } + } + clear_fields(); + return 0; +} + +/* End Radio functions */ + static void printconfig() { int x,y; @@ -635,6 +1044,17 @@ static struct handler { { "alaw", setlaw }, { "mulaw", setlaw }, { "deflaw", setlaw }, + { "ctcss", ctcss }, + { "dcsrx", dcsrx }, + { "rxdcs", dcsrx }, + { "tx", tx }, + { "debouncetime", debounce_time }, + { "bursttime", burst_time }, + { "exttone", ext_tone }, + { "invertcor", invert_cor }, + { "corthresh", cor_thresh }, + { "channel", rad_chanconfig }, + { "channels", rad_chanconfig }, }; static char *readline() @@ -680,7 +1100,6 @@ int main(int argc, char *argv[]) char *buf; char *key, *value; int x,found; - int fd; while((c = getopt(argc, argv, "thc:vs")) != -1) { switch(c) { case 'c': @@ -703,6 +1122,9 @@ int main(int argc, char *argv[]) break; } } + if (fd == -1) fd = open(MASTER_DEVICE, O_RDWR); + if (fd < 0) + error("Unable to open master device '%s'\n", MASTER_DEVICE); cf = fopen(filename, "r"); if (cf) { while((buf = readline())) { @@ -748,7 +1170,7 @@ int main(int argc, char *argv[]) printf("About to open Master device\n"); fflush(stdout); } - fd = open(MASTER_DEVICE, O_RDWR); + if (fd == -1) fd = open(MASTER_DEVICE, O_RDWR); if (fd < 0) error("Unable to open master device '%s'\n", MASTER_DEVICE); else { @@ -821,7 +1243,6 @@ int main(int argc, char *argv[]) } } } - close(fd); } } } else { |