diff options
Diffstat (limited to 'kernel/torisa.c')
-rw-r--r-- | kernel/torisa.c | 1172 |
1 files changed, 1172 insertions, 0 deletions
diff --git a/kernel/torisa.c b/kernel/torisa.c new file mode 100644 index 0000000..a8fb9d3 --- /dev/null +++ b/kernel/torisa.c @@ -0,0 +1,1172 @@ +/* + * Zapata Telephony "Tormenta" ISA card LINUX driver, version 2.2 11/29/01 + * + * 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. + * + * Modified from original tor.c by Mark Spencer <markster@digium.com> + * original by Jim Dixon <jim@lambdatel.com> + */ + +#include <linux/version.h> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) +#include <linux/config.h> +#endif +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <asm/io.h> +#ifdef STANDALONE_ZAPATA +#include "zaptel.h" +#else +#include <zaptel/zaptel.h> +#endif +#ifdef LINUX26 +#include <linux/moduleparam.h> +#endif + +/* Board address offsets (specified in word (not byte) offsets) */ +#define DDATA 0 /* Data I/O Register */ +#define DADDR 0x100 /* Dallas Card Address Reg., 0x200 in byte offset higher */ +#define CTLREG 0x100 /* Control/Status Reg., 0x200 in byte offset */ + +/* Control register bits */ +#define OUTBIT 8 /* Status output bit (for external measurements) */ +#define INTENA 4 /* Interrupt enable bit */ +#define MASTERVAL 0x41 /* Enable E1 master clock on Rev. B board */ +#define ENA16 0x80 /* 16 bit bus cycle enable bit */ + +#define TYPE_T1 1 /* is a T1 card */ +#define TYPE_E1 2 /* is an E1 card */ + +#define E1SYNCSTABLETHRESH 15000 /* amount of samples needed for E1 Sync stability */ + +static int syncsrc; + +static int syncs[2]; + +static int debug; + +#define MASTERCLOCK (*clockvals) /* value for master clock */ + +/* clock values */ +static u_char clockvals_t1[] = {MASTERVAL,0x12,0x22,MASTERVAL}; +static u_char clockvals_e1[] = {MASTERVAL,0x13,0x23,MASTERVAL}; + +static u_char *clockvals; + +/* translations of data channels for 24 channels in a 32 bit PCM highway */ +unsigned datxlt_t1[] = { 0, + 1 ,2 ,3 ,5 ,6 ,7 ,9 ,10,11,13,14,15,17,18,19,21,22,23,25,26,27,29,30,31 }; + +/* translations of data channels for 30/31 channels in a 32 bit PCM highway */ +unsigned datxlt_e1[] = { 0, + 1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, + 25,26,27,28,29,30,31 }; + +unsigned int *datxlt; + +/* This is the order that the data (audio) channels get +scanned in. This was done in this rather poopy manner because when outputting +(and inputting) a sine wave, such as in the case of TDD, any repeated samples +(because of PCM bus contention) will result in nasty-sounding distortion. The +Mitel STPA chips (MT8920) have a contention mechanism, which results in a +situation where, if the processor accesses a timeslot that is currently +being transmitted or received, it will HOLD the bus until it is done with +the timeslot. This means that there can be cases where we are trying +to write to a timeslot, and its already outputting the same value +as the last one (since we didnt get there in time), and in a sine-wave +output, distortion will occur. In any other output, it will be utterly +un-noticeable. So, what we do is use a pattern that gives us the most +flexibility in how long our interrupt latency is (note: Even with this, +our interrupt latency must be between 4 and 28 microseconds!!!) Essentially +we receive the interrupt just after the 24th channel is read. It will +take us AT LEAST 30 microseconds to read it, but could take as much as +35 microseconds to read all the channels. In any case it's the very +first thing we do in the interrupt handler. Worst case (30 microseconds) +is that the MT8920 has only moved 7 channels. That's where the 6 comes from. +*/ + +static int chseq_t1[] = + { 6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,1,2,3,4,5 } ; + +static int chseq_e1[] = + { 6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,1,2,3,4,5 } ; + +static int *chseq; + +struct torisa_pvt { + int span; +}; + +static struct zt_span spans[2]; +static struct zt_chan chans[64]; +static struct torisa_pvt pvts[64]; +static u_char txsigs[2][16]; +static int loopupcnt[2]; +static int loopdowncnt[2]; +static int alarmtimer[2]; + +static int channels_per_span = 24; + +static int card_type = TYPE_T1; + +static int prefmaster = 0; + +static int spansstarted = 0; + +#ifdef DEFINE_RWLOCK +static DEFINE_RWLOCK(torisa); +#else +static rwlock_t torisa = RW_LOCK_UNLOCKED; +#endif + +static u_char readdata[2][64][ZT_MAX_CHUNKSIZE]; +static u_char writedata[2][64][ZT_MAX_CHUNKSIZE]; +static u_char last_ecwrite[2][32]; +static int curread; + +static unsigned long base; +volatile static unsigned short *maddr; + +static int irq; +static unsigned int irqcount = 0; +static unsigned int taskletsched = 0; +static unsigned int taskletrun = 0; +static unsigned int taskletexec = 0; + +/* set the control register */ +static void setctlreg(unsigned char val) +{ +volatile register char *cp; + + cp = (char *) &maddr[CTLREG]; + *cp = val; +} + +/* output a byte to one of the registers in one of the Dallas T-1 chips */ +static void t1out(int spanno, int loc, unsigned char val) +{ +register int n; +volatile register char *cp; + + /* get the memory offset */ + n = spanno << 9; + /* point a char * at the address location */ + cp = (char *) &maddr[DADDR + n]; + *cp = loc; /* set address in T1 chip */ + /* point a char * at the data location */ + cp = (char *) &maddr[DDATA + n]; + *cp = val; /* out the value */ +} + +/* get a byte from one of the registers in one of the Dallas T-1 chips */ +static unsigned char t1in(int spanno, int loc) +{ +register int n; +volatile register char *cp; + + /* get the memory offset */ + n = spanno << 9; + /* point a char * at the address location */ + cp = (char *) &maddr[DADDR + n]; + *cp = loc; /* set address in T1 chip */ + cp = (char *) &maddr[DDATA + n]; + /* point a char * at the data location */ + return(*cp); +} + +/* get input from the status register */ +static unsigned char getctlreg(void) +{ +register char *cp; + + cp = (char *) &maddr[CTLREG]; + return(*cp); +} + +static void set_clear(void) +{ + int i,j,s; + unsigned short val=0; + for (s=0;s<2;s++) { + for (i=0;i<channels_per_span;i++) { + j = (i/8); + if (spans[s].chans[i].flags & ZT_FLAG_CLEAR) + val |= 1 << (i % 8); + + if ((i % 8)==7) { +#if 0 + printk("Putting %d in register %02x on span %d\n", + val, 0x39 + j, 1 + s); +#endif + t1out(1 + s, 0x39 + j, val); + val = 0; + } + } + } + +} + +/* device probe routine .. determines if the Tormenta device is present in + the system */ +static int +tor_probe(void) +{ + int i,status; + u_char c1,c2; + maddr = phys_to_virt(base); + + status = -1; /* default status return is 'not present' */ + + clockvals = clockvals_t1; + datxlt = datxlt_t1; + chseq = chseq_t1; + + /* initialize control register */ + setctlreg(MASTERCLOCK); + + /* init all the registers in first T-1 chip to 0 */ + for(i = 0; i <= 0xff; i++) t1out(1,i,0); /* set register to 0 */ + /* simple test that will fail if tried in an array of standard memory */ + /* put an 0x55 here */ + t1out(1,0x2b,0x55); + /* put an 0xaa here */ + t1out(1,0x2c,0xaa); + /* get input from first location */ + c1 = t1in(1,0x2b); + /* get input from second location */ + c2 = t1in(1,0x2c); + /* see if we read back what we put in */ + if ((c1 == 0x55) && (c2 == 0xaa)) { + /* We now need to determine card type */ + /* This test is documented in Dallas app note 341 */ + t1out(1, 0x7D, 0); + t1out(1, 0x36, 0); + t1out(1, 0x15, 0); + t1out(1, 0x19, 0); + t1out(1, 0x23, 0x55); + c1 = t1in(1, 0x23); + if (c1 == 0x55) { /* if this is an E-1 card */ + + clockvals = clockvals_e1; + chseq = chseq_e1; + channels_per_span = 31; + datxlt = datxlt_e1; + card_type = TYPE_E1; + + /* initialize control register */ + setctlreg(MASTERCLOCK); + } + /* Try to get the irq if the user didn't specify one */ + if (irq < 1) { +#ifdef LINUX26 + unsigned long irqs; + unsigned long delay = jiffies + 5; + irqs = probe_irq_on(); + setctlreg(MASTERCLOCK|INTENA); + while((long)(jiffies - delay) < 0); + irq = probe_irq_off(irqs); +#else + autoirq_setup(0); + setctlreg(MASTERCLOCK|INTENA); + /* Wait a jiffie -- that's plenty of time */ + irq = autoirq_report(5); +#endif + } + /* disable interrupts having gotten one */ + setctlreg(MASTERCLOCK); + if (irq == 2) + irq = 9; + if (irq) { + /* init both STPA's to all silence */ + for(i = 0; i < 32; i++) maddr[i] = 0x7f7f; + + status = 0; /* found */ + if (debug) + printk("ISA Tormenta %s Card found at base addr 0x%lx, irq %d\n", + ((card_type == TYPE_E1) ? "E1" : "T1"), + base,irq); + } else + printk("ISA Tormenta %s Card found at base addr 0x%lx, but unable to determine IRQ. Try using irq= option\n", + ((card_type == TYPE_E1) ? "E1" : "T1"), base ); + } + return status; +} + +static void make_chans(void) +{ + int x,y; + int c; + for (x=0;x<2;x++) + for (y=0;y<channels_per_span;y++) { + c = x * channels_per_span + y; + sprintf(chans[c].name, "TorISA/%d/%d", x + 1, y + 1); + chans[c].sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | ZT_SIG_FXSLS | ZT_SIG_FXSGS | ZT_SIG_FXSKS | + ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_FXOKS | ZT_SIG_CAS | ZT_SIG_SF; + chans[c].pvt = &pvts[c]; + pvts[c].span = x; + chans[c].chanpos = y + 1; + } + +} + +static int torisa_rbsbits(struct zt_chan *chan, int bits) +{ + u_char m,c; + int k,n,b; + struct torisa_pvt *p = chan->pvt; + unsigned long flags; +#if 0 + printk("Setting bits to %x hex on channel %s\n", bits, chan->name); +#endif + if (card_type == TYPE_E1) { /* do it E1 way */ + if (chan->chanpos > 30) return 0; /* cant do this for chan 31 */ + n = chan->chanpos - 1; + k = p->span; + b = (n % 15) + 1; + c = txsigs[k][b]; + m = (n / 15) * 4; /* nibble selector */ + c &= (15 << m); /* keep the other nibble */ + c |= (bits & 15) << (4 - m); /* put our new nibble here */ + txsigs[k][b] = c; + /* output them into the chip */ + t1out(k + 1,0x40 + b,c); + return 0; + } + n = chan->chanpos - 1; + k = p->span; + b = (n / 8); /* get byte number */ + m = 1 << (n & 7); /* get mask */ + c = txsigs[k][b]; + c &= ~m; /* clear mask bit */ + /* set mask bit, if bit is to be set */ + if (bits & ZT_ABIT) c |= m; + txsigs[k][b] = c; + write_lock_irqsave(&torisa, flags); + t1out(k + 1,0x70 + b,c); + b += 3; /* now points to b bit stuff */ + /* get current signalling values */ + c = txsigs[k][b]; + c &= ~m; /* clear mask bit */ + /* set mask bit, if bit is to be set */ + if (bits & ZT_BBIT) c |= m; + /* save new signalling values */ + txsigs[k][b] = c; + /* output them into the chip */ + t1out(k + 1,0x70 + b,c); + b += 3; /* now points to c bit stuff */ + /* get current signalling values */ + c = txsigs[k][b]; + c &= ~m; /* clear mask bit */ + /* set mask bit, if bit is to be set */ + if (bits & ZT_CBIT) c |= m; + /* save new signalling values */ + txsigs[k][b] = c; + /* output them into the chip */ + t1out(k + 1,0x70 + b,c); + b += 3; /* now points to d bit stuff */ + /* get current signalling values */ + c = txsigs[k][b]; + c &= ~m; /* clear mask bit */ + /* set mask bit, if bit is to be set */ + if (bits & ZT_DBIT) c |= m; + /* save new signalling values */ + txsigs[k][b] = c; + /* output them into the chip */ + t1out(k + 1,0x70 + b,c); + write_unlock_irqrestore(&torisa, flags); + return 0; +} + +static inline int getspan(struct zt_span *span) +{ + if (span == spans) + return 1; + if (span == spans + 1) + return 2; + return -1; +} + +static int torisa_shutdown(struct zt_span *span) +{ + int i; + int tspan; + int wasrunning; + unsigned long flags; + + tspan = getspan(span); + if (tspan < 0) { + printk("TorISA: Span '%d' isn't us?\n", span->spanno); + return -1; + } + + write_lock_irqsave(&torisa, flags); + wasrunning = span->flags & ZT_FLAG_RUNNING; + + span->flags &= ~ZT_FLAG_RUNNING; + /* Zero out all registers */ + for (i = 0; i< 0xff; i++) t1out(tspan, i, 0); + if (wasrunning) + spansstarted--; + write_unlock_irqrestore(&torisa, flags); + if (!spans[0].flags & ZT_FLAG_RUNNING && + !spans[1].flags & ZT_FLAG_RUNNING) + /* No longer in use, disable interrupts */ + setctlreg(clockvals[syncsrc]); + + if (debug) + printk("Span %d (%s) shutdown\n", span->spanno, span->name); + return 0; +} + +static int torisa_startup(struct zt_span *span) +{ + unsigned long endjif; + int i; + int tspan; + unsigned long flags; + char *coding; + char *framing; + char *crcing; + int alreadyrunning; + + tspan = getspan(span); + if (tspan < 0) { + printk("TorISA: Span '%d' isn't us?\n", span->spanno); + return -1; + } + + + write_lock_irqsave(&torisa, flags); + + alreadyrunning = span->flags & ZT_FLAG_RUNNING; + + /* initialize the start value for the last ec buffer */ + for(i = 0; i < span->channels; i++) + { + last_ecwrite[tspan - 1][i] = ZT_LIN2X(0,&span->chans[i]); + } + crcing = ""; + if (card_type == TYPE_T1) { /* if its a T1 card */ + if (!alreadyrunning) { + + setctlreg(MASTERCLOCK); + /* Zero out all registers */ + for (i = 0x20; i< 0x40; i++) + t1out(tspan, i, 0); + for (i = 0x60; i< 0x80; i++) + t1out(tspan, i, 0); + + /* Full-on Sync required (RCR1) */ + t1out(tspan, 0x2b, 8); + /* RSYNC is an input (RCR2) */ + t1out(tspan, 0x2c, 8); + /* RBS enable (TCR1) */ + t1out(tspan, 0x35, 0x10); + /* TSYNC to be output (TCR2) */ + t1out(tspan, 0x36, 4); + /* Tx & Rx Elastic store, sysclk = 2.048 mhz, loopback controls (CCR1) */ + t1out(tspan, 0x37, 0x8c); + } + /* Enable F bits pattern */ + i = 0x20; + if (span->lineconfig & ZT_CONFIG_ESF) + i = 0x88; + if (span->lineconfig & ZT_CONFIG_B8ZS) + i |= 0x44; + t1out(tspan, 0x38, i); + if (i & 0x80) + coding = "ESF"; + else + coding = "SF"; + if (i & 0x40) + framing = "B8ZS"; + else { + framing = "AMI"; + t1out(tspan,0x7e,0x1c); /* F bits pattern (0x1c) into FDL register */ + } + t1out(tspan, 0x7c, span->txlevel << 5); + + if (!alreadyrunning) { + /* LIRST to 1 in CCR3 */ + t1out(tspan, 0x30, 1); + + /* Wait 100 ms */ + endjif = jiffies + 10; + write_unlock_irqrestore(&torisa, flags); + + while(jiffies < endjif); /* wait 100 ms */ + + write_lock_irqsave(&torisa, flags); + t1out(tspan,0x30,0x40); /* set CCR3 to 0x40, resetting Elastic Store */ + + span->flags |= ZT_FLAG_RUNNING; + spansstarted++; + +#if 0 + printk("Enabling interrupts: %d\n", clockvals[syncsrc] | INTENA); +#endif + + /* output the clock info and enable interrupts */ + setctlreg(clockvals[syncsrc] | INTENA); + } + set_clear(); /* this only applies to a T1 */ + } else { /* if its an E1 card */ + u_char ccr1 = 0, tcr1 = 0; + + if (!alreadyrunning) { + t1out(tspan,0x1a,4); /* CCR2: set LOTCMC */ + for(i = 0; i <= 8; i++) t1out(tspan,i,0); + for(i = 0x10; i <= 0x4f; i++) if (i != 0x1a) t1out(tspan,i,0); + t1out(tspan,0x10,0x20); /* RCR1: Rsync as input */ + t1out(tspan,0x11,6); /* RCR2: Sysclk=2.048 Mhz */ + t1out(tspan,0x12,8); /* TCR1: TSiS mode */ + } + tcr1 = 8; /* base TCR1 value: TSis mode */ + if (span->lineconfig & ZT_CONFIG_CCS) { + ccr1 |= 8; /* CCR1: Rx Sig mode: CCS */ + coding = "CCS"; + } else { + tcr1 |= 0x20; + coding = "CAS"; + } + if (span->lineconfig & ZT_CONFIG_HDB3) { + ccr1 |= 0x44; /* CCR1: TX and RX HDB3 */ + framing = "HDB3"; + } else framing = "AMI"; + if (span->lineconfig & ZT_CONFIG_CRC4) { + ccr1 |= 0x11; /* CCR1: TX and TX CRC4 */ + crcing = "/CRC4"; + } + t1out(tspan,0x12,tcr1); + t1out(tspan,0x14,ccr1); + t1out(tspan, 0x18, 0x80); + + if (!alreadyrunning) { + t1out(tspan,0x1b,0x8a); /* CCR3: LIRST & TSCLKM */ + t1out(tspan,0x20,0x1b); /* TAFR */ + t1out(tspan,0x21,0x5f); /* TNAFR */ + t1out(tspan,0x40,0xb); /* TSR1 */ + for(i = 0x41; i <= 0x4f; i++) t1out(tspan,i,0x55); + for(i = 0x22; i <= 0x25; i++) t1out(tspan,i,0xff); + /* Wait 100 ms */ + endjif = jiffies + 10; + write_unlock_irqrestore(&torisa, flags); + while(jiffies < endjif); /* wait 100 ms */ + write_lock_irqsave(&torisa, flags); + t1out(tspan,0x1b,0x9a); /* CCR3: set also ESR */ + t1out(tspan,0x1b,0x82); /* CCR3: TSCLKM only now */ + + /* output the clock info and enable interrupts */ + setctlreg(clockvals[syncsrc] | INTENA); + } + + } + + write_unlock_irqrestore(&torisa, flags); + + if (debug) { + if (card_type == TYPE_T1) { + if (alreadyrunning) + printk("TorISA: Reconfigured span %d (%s/%s) LBO: %s\n", span->spanno, coding, framing, zt_lboname(span->txlevel)); + else + printk("TorISA: Startup span %d (%s/%s) LBO: %s\n", span->spanno, coding, framing, zt_lboname(span->txlevel)); + } else { + if (alreadyrunning) + printk("TorISA: Reconfigured span %d (%s/%s%s) 120 ohms\n", span->spanno, coding, framing, crcing); + else + printk("TorISA: Startup span %d (%s/%s%s) 120 ohms\n", span->spanno, coding, framing, crcing); + } + } + if (syncs[0] == span->spanno) printk("SPAN %d: Primary Sync Source\n",span->spanno); + if (syncs[1] == span->spanno) printk("SPAN %d: Secondary Sync Source\n",span->spanno); + return 0; +} + +static int torisa_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) +{ + if (debug) + printk("TorISA: Configuring span %d\n", span->spanno); + + span->syncsrc = syncsrc; + + /* remove this span number from the current sync sources, if there */ + if (syncs[0] == span->spanno) syncs[0] = 0; + if (syncs[1] == span->spanno) syncs[1] = 0; + /* if a sync src, put it in proper place */ + if (lc->sync) syncs[lc->sync - 1] = span->spanno; + + /* If we're already running, then go ahead and apply the changes */ + if (span->flags & ZT_FLAG_RUNNING) + return torisa_startup(span); + + return 0; +} + +static int torisa_chanconfig(struct zt_chan *chan, int sigtype) +{ + int alreadyrunning; + unsigned long flags; + alreadyrunning = chan->span->flags & ZT_FLAG_RUNNING; + if (debug) { + if (alreadyrunning) + printk("TorISA: Reconfigured channel %d (%s) sigtype %d\n", chan->channo, chan->name, sigtype); + else + printk("TorISA: Configured channel %d (%s) sigtype %d\n", chan->channo, chan->name, sigtype); + } + write_lock_irqsave(&torisa, flags); + if (alreadyrunning && (card_type == TYPE_T1)) + set_clear(); + write_unlock_irqrestore(&torisa, flags); + return 0; +} + +static int torisa_open(struct zt_chan *chan) +{ +#ifndef LINUX26 + MOD_INC_USE_COUNT; +#endif + return 0; +} + +static int torisa_close(struct zt_chan *chan) +{ +#ifndef LINUX26 + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +static int torisa_maint(struct zt_span *span, int cmd) +{ + int tspan = getspan(span); + + switch(cmd) { + case ZT_MAINT_NONE: + t1out(tspan,0x1a,4); /* clear system */ + break; + case ZT_MAINT_LOCALLOOP: + t1out(tspan,0x1a,5); /* local loopback */ + break; + case ZT_MAINT_REMOTELOOP: + t1out(tspan,0x37,6); /* remote loopback */ + break; + case ZT_MAINT_LOOPUP: + if (card_type == TYPE_E1) return -ENOSYS; + t1out(tspan,0x30,2); /* send loopup code */ + break; + case ZT_MAINT_LOOPDOWN: + if (card_type == TYPE_E1) return -ENOSYS; + t1out(tspan,0x30,4); /* send loopdown code */ + break; + case ZT_MAINT_LOOPSTOP: + if (card_type == TYPE_T1) + t1out(tspan,0x30,0); /* stop sending loopup code */ + break; + default: + printk("torisa: Unknown maint command: %d\n", cmd); + break; + } + return 0; +} + +static int taskletpending; + +static struct tasklet_struct torisa_tlet; + +static void torisa_tasklet(unsigned long data) +{ + int x,y; + u_char mychunk[2][ZT_CHUNKSIZE]; + taskletrun++; + if (taskletpending) { + taskletexec++; + /* Perform receive data calculations. Reverse to run most + likely master last */ + if (spans[1].flags & ZT_FLAG_RUNNING) { + /* Perform echo cancellation */ + for (x=0;x<channels_per_span;x++) + { + for(y = 0; y < ZT_CHUNKSIZE; y++) + { + mychunk[1][y] = last_ecwrite[1][x]; + last_ecwrite[1][x] = + writedata[1-curread][x + channels_per_span][y]; + } + zt_ec_chunk(&spans[1].chans[x], + spans[1].chans[x].readchunk,mychunk[1]); + } + zt_receive(&spans[1]); + } + if (spans[0].flags & ZT_FLAG_RUNNING) { + /* Perform echo cancellation */ + for (x=0;x<channels_per_span;x++) + { + for(y = 0; y < ZT_CHUNKSIZE; y++) + { + mychunk[0][y] = last_ecwrite[0][x]; + last_ecwrite[0][x] = writedata[1-curread][x][y]; + } + zt_ec_chunk(&spans[0].chans[x], + spans[0].chans[x].readchunk,mychunk[0]); + } + zt_receive(&spans[0]); + } + + /* Prepare next set for transmission */ + if (spans[1].flags & ZT_FLAG_RUNNING) + zt_transmit(&spans[1]); + if (spans[0].flags & ZT_FLAG_RUNNING) + zt_transmit(&spans[0]); + } + taskletpending = 0; +} + +static int txerrors; + +ZAP_IRQ_HANDLER(torisa_intr) +{ + static unsigned int passno = 0, mysynccnt = 0, lastsyncsrc = -1; + int n, n1, i, j, k, x, mysyncsrc, oldn; + static unsigned short rxword[33],txword[33]; + unsigned char txc, rxc, c; + unsigned char abits, bbits, cbits, dbits; + + + irqcount++; + + /* 1. Do all I/O Immediately -- Normally we would ask for + the transmission first, but because of the incredibly + tight timing we're lucky to be able to do the I/O + at this point */ + + /* make sure its a real interrupt for us */ + if (!(getctlreg() & 1)) /* if not, just return */ + { +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + } + + /* set outbit and put int 16 bit bus mode, reset interrupt enable */ + setctlreg(clockvals[syncsrc] | OUTBIT | ENA16); + +#if 0 + if (!passno) + printk("Interrupt handler\n"); +#endif + + /* Do the actual transmit and receive in poopy order */ + for(n1 = 0; n1 < channels_per_span; n1++) + { + n = chseq[n1]; + maddr[DDATA + datxlt[n]] = txword[n]; + rxword[n] = maddr[DDATA + datxlt[n]]; /* get rx word */ + } + + + setctlreg(clockvals[syncsrc] | OUTBIT); /* clear 16 bit mode */ + + /* Calculate the transmit, and receive go thru all the chans */ + oldn = -1; + for(n1 = 0; n1 < channels_per_span; n1++) { + n = chseq[n1]; + txword[n] = 0; + if (n < oldn) { + /* We've circled around. + Now we increment the passno and stuff */ + if ((passno % ZT_CHUNKSIZE) == (ZT_CHUNKSIZE - 1)) { + /* Swap buffers */ + for (x = 0;x < (channels_per_span * 2);x++) { + chans[x].readchunk = readdata[curread][x]; + chans[x].writechunk = writedata[curread][x]; + } + /* Lets work with the others now which presumably have been filled */ + curread = 1 - curread; + if (!taskletpending) { + taskletpending = 1; + taskletsched++; + tasklet_hi_schedule(&torisa_tlet); + } else { + txerrors++; + } + } + passno++; + } + oldn = n; + /* go thru both spans */ + for(j = 0; j < 2; j++) + { + /* enter the transmit stuff with i being channel number, + leaving with txc being character to transmit */ + txc = writedata[curread][j * channels_per_span + n-1][passno % ZT_CHUNKSIZE]; + txword[n] |= txc << (j * 8); + + /* receive side */ + i = n + (j * channels_per_span); /* calc chan number */ + rxc = (rxword[n] >> (j * 8)) & 0xff; + readdata[curread][j * channels_per_span + n - 1][passno % ZT_CHUNKSIZE] = rxc; + } + } + + i = passno & 127; + /* if an E1 card, do rx signalling for it */ + if (i < 3 && (card_type == TYPE_E1)) { /* if an E1 card */ + for(j = (i * 3); j < (i * 3) + 5; j++) + { + for(k = 1,x = j; k <= 2; k++,x += channels_per_span) { + c = t1in(k,0x31 + j); + rxc = c & 15; + if (rxc != chans[x + 15].rxsig) { + /* Check for changes in received bits */ + if (!(chans[x + 15].sig & ZT_SIG_CLEAR)) + zt_rbsbits(&chans[x + 15], rxc); + } + rxc = c >> 4; + if (rxc != chans[x].rxsig) { + /* Check for changes in received bits */ + if (!(chans[x].sig & ZT_SIG_CLEAR)) + zt_rbsbits(&chans[x], rxc); + } + } + } + } + /* if a t1 card, do rx signalling for it */ + if ((i < 6) && (card_type == TYPE_T1)) { + k = (i / 3); /* get span */ + n = (i % 3); /* get base */ + abits = t1in(k + 1, 0x60 + n); + bbits = t1in(k + 1, 0x63 + n); + cbits = t1in(k + 1, 0x66 + n); + dbits = t1in(k + 1, 0x69 + n); + for (j=0; j< 8; j++) { + /* Get channel number */ + i = (k * 24) + (n * 8) + j; + rxc = 0; + if (abits & (1 << j)) rxc |= ZT_ABIT; + if (bbits & (1 << j)) rxc |= ZT_BBIT; + if (cbits & (1 << j)) rxc |= ZT_CBIT; + if (dbits & (1 << j)) rxc |= ZT_DBIT; + if (chans[i].rxsig != rxc) { + /* Check for changes in received bits */ + if (!(chans[i].sig & ZT_SIG_CLEAR)) + zt_rbsbits(&chans[i], rxc); + } + } + } + + if (!(passno & 0x7)) { + for(i = 0; i < 2; i++) + { + /* if alarm timer, and it's timed out */ + if (alarmtimer[i]) { + if (!--alarmtimer[i]) + { + /* clear recover status */ + spans[i].alarms &= ~ZT_ALARM_RECOVER; + if (card_type == TYPE_T1) + t1out(i + 1,0x35,0x10); /* turn off yel */ + else + t1out(i + 1,0x21,0x5f); /* turn off remote alarm */ + zt_alarm_notify(&spans[i]); /* let them know */ + } + } + } + } + + i = passno & 511; + if ((i == 100) || (i == 101)) + { + j = 0; /* clear this alarm status */ + i -= 100; + if (card_type == TYPE_T1) { + c = t1in(i + 1,0x31); /* get RIR2 */ + spans[i].rxlevel = c >> 6; /* get rx level */ + t1out(i + 1,0x20,0xff); + c = t1in(i + 1,0x20); /* get the status */ + /* detect the code, only if we are not sending one */ + if ((!spans[i].mainttimer) && (c & 0x80)) /* if loop-up code detected */ + { + /* set into remote loop, if not there already */ + if ((loopupcnt[i]++ > 80) && + (spans[i].maintstat != ZT_MAINT_REMOTELOOP)) + { + t1out(i + 1,0x37,0x9c); /* remote loopback */ + spans[i].maintstat = ZT_MAINT_REMOTELOOP; + } + } else loopupcnt[i] = 0; + /* detect the code, only if we are not sending one */ + if ((!spans[i].mainttimer) && (c & 0x40)) /* if loop-down code detected */ + { + /* if in remote loop, get out of it */ + if ((loopdowncnt[i]++ > 80) && + (spans[i].maintstat == ZT_MAINT_REMOTELOOP)) + { + t1out(i + 1,0x37,0x8c); /* normal */ + spans[i].maintstat = ZT_MAINT_NONE; + } + } else loopdowncnt[i] = 0; + if (c & 3) /* if red alarm */ + { + j |= ZT_ALARM_RED; + } + if (c & 8) /* if blue alarm */ + { + j |= ZT_ALARM_BLUE; + } + } else { /* its an E1 card */ + t1out(i + 1,6,0xff); + c = t1in(i + 1,6); /* get the status */ + if (c & 9) /* if red alarm */ + { + j |= ZT_ALARM_RED; + } + if (c & 2) /* if blue alarm */ + { + j |= ZT_ALARM_BLUE; + } + } + /* only consider previous carrier alarm state */ + spans[i].alarms &= (ZT_ALARM_RED | ZT_ALARM_BLUE | ZT_ALARM_NOTOPEN); + n = 1; /* set to 1 so will not be in yellow alarm if we dont + care about open channels */ + /* if to have yellow alarm if nothing open */ + if (spans[i].lineconfig & ZT_CONFIG_NOTOPEN) + { + /* go thru all chans, and count # open */ + for(n = 0,k = (i * channels_per_span); k < (i * channels_per_span) + channels_per_span; k++) + { + if ((chans[k].flags & ZT_FLAG_OPEN) || + (chans[k].flags & ZT_FLAG_NETDEV)) n++; + } + /* if none open, set alarm condition */ + if (!n) j |= ZT_ALARM_NOTOPEN; + } + /* if no more alarms, and we had some */ + if ((!j) && spans[i].alarms) + { + alarmtimer[i] = ZT_ALARMSETTLE_TIME; + } + if (alarmtimer[i]) j |= ZT_ALARM_RECOVER; + /* if going into alarm state, set yellow (remote) alarm */ + if ((j) && (!spans[i].alarms)) { + if (card_type == TYPE_T1) t1out(i + 1,0x35,0x11); + else t1out(i + 1,0x21,0x7f); + } + if (c & 4) /* if yellow alarm */ + j |= ZT_ALARM_YELLOW; + if (spans[i].maintstat || spans[i].mainttimer) j |= ZT_ALARM_LOOPBACK; + spans[i].alarms = j; + zt_alarm_notify(&spans[i]); + } + if (!(passno % 8000)) /* even second boundary */ + { + /* do both spans */ + for(i = 1; i <= 2; i++) + { + if (card_type == TYPE_T1) { + /* add this second's BPV count to total one */ + spans[i - 1].bpvcount += t1in(i,0x24) + (t1in(i,0x23) << 8); + } else { + /* add this second's BPV count to total one */ + spans[i - 1].bpvcount += t1in(i,1) + (t1in(i,0) << 8); + } + } + } + /* re-evaluate active sync src */ + mysyncsrc = 0; + /* if primary sync specified, see if we can use it */ + if (syncs[0]) + { + /* if no alarms, use it */ + if (!(spans[syncs[0] - 1].alarms & (ZT_ALARM_RED | ZT_ALARM_BLUE | + ZT_ALARM_LOOPBACK))) mysyncsrc = syncs[0]; + } + /* if we dont have one yet, and there is a secondary, see if we can use it */ + if ((!mysyncsrc) && (syncs[1])) + { + /* if no alarms, use it */ + if (!(spans[syncs[1] - 1].alarms & (ZT_ALARM_RED | ZT_ALARM_BLUE | + ZT_ALARM_LOOPBACK))) mysyncsrc = syncs[1]; + } + /* on the E1 card, the PLL takes a bit of time to lock going + between internal and external clocking. There needs to be some + settle time before actually changing the source, otherwise it will + oscillate between in and out of sync */ + if (card_type == TYPE_E1) + { + /* if stable, add to count */ + if (lastsyncsrc == mysyncsrc) mysynccnt++; else mysynccnt = 0; + lastsyncsrc = mysyncsrc; + /* if stable sufficiently long, change it */ + if (mysynccnt >= E1SYNCSTABLETHRESH) + { + mysynccnt = 0; + syncsrc = mysyncsrc; + } + } + else syncsrc = mysyncsrc; /* otherwise on a T1 card, just use current value */ + /* update sync src info */ + spans[0].syncsrc = spans[1].syncsrc = syncsrc; + /* If this is the last pass, then prepare the next set */ + /* clear outbit, restore interrupt enable */ + setctlreg(clockvals[syncsrc] | INTENA); +#ifdef LINUX26 + return IRQ_RETVAL(1); +#endif +} + + +static int torisa_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) +{ + struct torisa_debug td; + switch(cmd) { + case TORISA_GETDEBUG: + td.txerrors = txerrors; + td.irqcount = irqcount; + td.taskletsched = taskletsched; + td.taskletrun = taskletrun; + td.taskletexec = taskletexec; + td.span1flags = spans[0].flags; + td.span2flags = spans[1].flags; + if (copy_to_user((struct torisa_debug *)data, &td, sizeof(td))) + return -EFAULT; + return 0; + default: + return -ENOTTY; + } + return 0; +} + +static int __init tor_init(void) +{ + if (!base) { + printk("Specify address with base=0xNNNNN\n"); + return -EIO; + } + if (tor_probe()) { + printk(KERN_ERR "No ISA tormenta card found at %05lx\n", base); + return -EIO; + } + if (request_irq(irq, torisa_intr, ZAP_IRQ_DISABLED, "torisa", NULL)) { + printk(KERN_ERR "Unable to request tormenta IRQ %d\n", irq); + return -EIO; + } + if (!request_mem_region(base, 4096, "Tormenta ISA")) { + printk(KERN_ERR "Unable to request 4k memory window at %lx\n", base); + free_irq(irq, NULL); + return -EIO; + } + + strcpy(spans[0].name, "TorISA/1"); + strncpy(spans[0].desc, "ISA Tormenta Span 1", sizeof(spans[0].desc) - 1); + spans[0].manufacturer = "Digium"; + strncpy(spans[0].devicetype, "Tormenta ISA", sizeof(spans[0].devicetype) - 1); + spans[0].spanconfig = torisa_spanconfig; + spans[0].chanconfig = torisa_chanconfig; + spans[0].startup = torisa_startup; + spans[0].shutdown = torisa_shutdown; + spans[0].rbsbits = torisa_rbsbits; + spans[0].maint = torisa_maint; + spans[0].open = torisa_open; + spans[0].close = torisa_close; + spans[0].channels = channels_per_span; + spans[0].chans = &chans[0]; + spans[0].flags = ZT_FLAG_RBS; + spans[0].ioctl = torisa_ioctl; + spans[0].irq = irq; + + if (card_type == TYPE_E1) { + spans[0].spantype = "T1"; + spans[0].linecompat = ZT_CONFIG_AMI | ZT_CONFIG_B8ZS | ZT_CONFIG_D4 | ZT_CONFIG_ESF; + spans[0].deflaw = ZT_LAW_ALAW; + } else { + spans[0].spantype = "E1"; + spans[0].linecompat = ZT_CONFIG_HDB3 | ZT_CONFIG_CCS | ZT_CONFIG_CRC4; + spans[0].deflaw = ZT_LAW_MULAW; + } + + spans[1] = spans[0]; + strcpy(spans[1].name, "TorISA/2"); + strcpy(spans[1].desc, "ISA Tormenta Span 2"); + spans[1].chans = &chans[channels_per_span]; + + init_waitqueue_head(&spans[0].maintq); + init_waitqueue_head(&spans[1].maintq); + + make_chans(); + if (zt_register(&spans[0], prefmaster)) { + printk(KERN_ERR "Unable to register span %s\n", spans[0].name); + return -EIO; + } + if (zt_register(&spans[1], 0)) { + printk(KERN_ERR "Unable to register span %s\n", spans[1].name); + zt_unregister(&spans[0]); + return -EIO; + } + tasklet_init(&torisa_tlet, torisa_tasklet, (long)0); + printk("TORISA Loaded\n"); + return 0; +} + + +#if !defined(LINUX26) +static int __init set_tor_base(char *str) +{ + base = simple_strtol(str, NULL, 0); + return 1; +} + +__setup("tor=", set_tor_base); +#endif + +static void __exit tor_exit(void) +{ + free_irq(irq, NULL); + release_mem_region(base, 4096); + if (spans[0].flags & ZT_FLAG_REGISTERED) + zt_unregister(&spans[0]); + if (spans[1].flags & ZT_FLAG_REGISTERED) + zt_unregister(&spans[1]); +} + +MODULE_AUTHOR("Mark Spencer <markster@digium.com>"); +MODULE_DESCRIPTION("Tormenta ISA Zapata Telephony Driver"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifdef LINUX26 +module_param(prefmaster, int, 0600); +module_param(base, long, 0600); +module_param(irq, int, 0600); +module_param(syncsrc, int, 0600); +module_param(debug, int, 0600); +#else +MODULE_PARM(prefmaster, "i"); +MODULE_PARM(base, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(syncsrc, "i"); +MODULE_PARM(debug, "i"); +#endif + +module_init(tor_init); +module_exit(tor_exit); |