summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xMakefile5
-rwxr-xr-xpciradio.c1599
-rwxr-xr-xzaptel.c9
-rwxr-xr-xzaptel.conf.sample61
-rwxr-xr-xzaptel.h102
-rwxr-xr-xztcfg.c429
6 files changed, 2200 insertions, 5 deletions
diff --git a/Makefile b/Makefile
index 6e888e6..a2a58cf 100755
--- a/Makefile
+++ b/Makefile
@@ -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);
+
+
+
diff --git a/zaptel.c b/zaptel.c
index 4f88f6f..d28b40b 100755
--- a/zaptel.c
+++ b/zaptel.c
@@ -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
+
+
diff --git a/zaptel.h b/zaptel.h
index 14dd69f..8616169 100755
--- a/zaptel.h
+++ b/zaptel.h
@@ -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 */
diff --git a/ztcfg.c b/ztcfg.c
index b85cd56..0bc6cf3 100755
--- a/ztcfg.c
+++ b/ztcfg.c
@@ -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 {