dahdi-linux/drivers/dahdi/pciradio.c

1892 lines
51 KiB
C
Raw Normal View History

/*
* PCI RADIO Card DAHDI Telephony PCI Quad Radio Interface driver
*
* Written by Jim Dixon <jim@lambdatel.com>
* Based on previous work by Mark Spencer <markster@digium.com>
* Based on previous works, designs, and archetectures conceived and
* written by Jim Dixon <jim@lambdatel.com>.
*
* Copyright (C) 2001-2007 Jim Dixon / Zapata Telephony.
*
* All rights reserved.
*
*
*/
/*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2 as published by the
* Free Software Foundation. See the LICENSE file included with
* this program for more details.
*/
/*
The PCI Radio Interface card interfaces up to 4 two-way radios (either
a base/mobile radio or repeater system) to DAHDI 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 DAHDI radio interface.
*/
/* Latency tests:
Without driver: 308496
With driver: 303826 (1.5 %)
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/moduleparam.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/delay.h>
#include <dahdi/kernel.h>
#define RAD_MAX_IFACES 128
#define NUM_CODES 15
#define SERIAL_BUFLEN 128
#define SRX_TIMEOUT 300
#define RAD_CNTL 0x00
#define RAD_OPER 0x01
#define RAD_AUXC 0x02
#define RAD_AUXD 0x03
#define XPGM 4
#define XCS 2
#define RAD_MASK0 0x04
#define RAD_MASK1 0x05
#define RAD_INTSTAT 0x06
#define RAD_AUXR 0x07
#define XINIT 8
#define XDONE 0x10
#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 RAD_CTCSSMASK 0xf
#define RAD_CTCSSOTHER 0xf
#define RAD_CTCSSVALID 0x10
#define NUM_CHANS 4
#define RAD_GOTRX_DEBOUNCE_TIME 75
#define RAD_CTCSS_ACQUIRE_TIME 10
#define RAD_CTCSS_TALKOFF_TIME 1000
#define DAHDI_RADPAR_CTCSSACQUIRETIME 18 /* DEBUG only, this belongs in dahdi.h */
#define DAHDI_RADPAR_CTCSSTALKOFFTIME 19 /* DEBUG only, this belongs in dahdi.h */
/*
* 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 encdec
{
unsigned char state; /* 0 = idle */
int chan;
unsigned char req[NUM_CHANS];
unsigned char dcsrx[NUM_CHANS];
unsigned char ctrx[NUM_CHANS];
unsigned char dcstx[NUM_CHANS];
unsigned char cttx[NUM_CHANS];
unsigned char saudio_ctrl[NUM_CHANS];
unsigned char saudio_setup[NUM_CHANS];
unsigned char txcode[NUM_CHANS];
unsigned long lastcmd;
int myindex[NUM_CHANS];
unsigned long waittime;
unsigned char retstate;
} ;
struct pciradio {
struct pci_dev *dev;
struct dahdi_device *ddev;
struct dahdi_span span;
unsigned char ios;
int usecount;
unsigned int intcount;
int dead;
int pos;
int freeregion;
int nchans;
spinlock_t lock;
int remote_locked;
unsigned char rxbuf[SERIAL_BUFLEN];
unsigned short rxindex;
unsigned long srxtimer;
unsigned char txbuf[SERIAL_BUFLEN];
unsigned short txindex;
unsigned short txlen;
unsigned char pasave;
unsigned char pfsave;
volatile unsigned long ioaddr;
dma_addr_t readdma;
dma_addr_t writedma;
volatile unsigned int *writechunk; /* Double-word aligned write memory */
volatile unsigned int *readchunk; /* Double-word aligned read memory */
unsigned char saudio_status[NUM_CHANS];
char gotcor[NUM_CHANS];
char gotct[NUM_CHANS];
char newctcssstate[NUM_CHANS];
char ctcssstate[NUM_CHANS];
char gotrx[NUM_CHANS];
char gotrx1[NUM_CHANS];
char gottx[NUM_CHANS];
char lasttx[NUM_CHANS];
int gotrxtimer[NUM_CHANS];
int ctcsstimer[NUM_CHANS];
int debouncetime[NUM_CHANS];
int ctcssacquiretime[NUM_CHANS];
int ctcsstalkofftime[NUM_CHANS];
int bursttime[NUM_CHANS];
int bursttimer[NUM_CHANS];
unsigned char remmode[NUM_CHANS];
unsigned short present_code[NUM_CHANS];
unsigned short last_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 dahdi_chan _chans[NUM_CHANS];
struct dahdi_chan *chans;
unsigned char mx828_addr;
struct encdec encdec;
unsigned long lastremcmd;
};
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;
} ;
#include "radfw.h"
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 rbi_out(struct pciradio *rad, int n, unsigned char *rbicmd)
{
unsigned long flags;
int x;
for(;;)
{
spin_lock_irqsave(&rad->lock,flags);
x = rad->remote_locked || (__pciradio_getcreg(rad,0xc) & 2);
if (!x) rad->remote_locked = 1;
spin_unlock_irqrestore(&rad->lock,flags);
if (x)
msleep_interruptible(20);
else break;
}
spin_lock_irqsave(&rad->lock,flags);
/* enable and address RBI serializer */
__pciradio_setcreg(rad,0xf,rad->pfsave | (n << 4) | 0x40);
/* output commands */
for(x = 0; x < 5; x++) __pciradio_setcreg(rad,0xc,rbicmd[x]);
/* output it */
__pciradio_setcreg(rad,0xb,1);
rad->remote_locked = 0;
spin_unlock_irqrestore(&rad->lock,flags);
return;
}
/*
* Output a command to the MX828 over the serial bus
*/
static void mx828_command(struct pciradio *rad,int channel, unsigned char command, unsigned char *byte1, unsigned char *byte2)
{
if(channel > 3)
return;
rad->mx828_addr = channel;
__pciradio_setcreg(rad,0,channel);
if (byte1) __pciradio_setcreg(rad,1,*byte1);
if (byte2) __pciradio_setcreg(rad,2,*byte2);
__pciradio_setcreg(rad,3,command);
}
static void mx828_command_wait(struct pciradio *rad,int channel, unsigned char command, unsigned char *byte1, unsigned char *byte2)
{
unsigned long flags;
spin_lock_irqsave(&rad->lock,flags);
while(rad->encdec.state)
{
spin_unlock_irqrestore(&rad->lock,flags);
msleep_interruptible(20);
spin_lock_irqsave(&rad->lock,flags);
}
rad->encdec.lastcmd = jiffies + 1000;
spin_unlock_irqrestore(&rad->lock,flags);
while(__pciradio_getcreg(rad,0xc) & 1);
rad->encdec.lastcmd = jiffies + 1000;
spin_lock_irqsave(&rad->lock,flags);
rad->encdec.lastcmd = jiffies + 1000;
mx828_command(rad,channel,command,byte1,byte2);
spin_unlock_irqrestore(&rad->lock,flags);
rad->encdec.lastcmd = jiffies + 1000;
while(__pciradio_getcreg(rad,0xc) & 1);
rad->encdec.lastcmd = jiffies;
}
static void _do_encdec(struct pciradio *rad)
{
int i,n;
unsigned char byte1 = 0,byte2 = 0;
/* return doing nothing if busy */
if ((rad->encdec.lastcmd + 2) > jiffies) return;
if (__pciradio_getcreg(rad,0xc) & 1) return;
n = 0;
byte2 = 0;
switch(rad->encdec.state)
{
case 0:
for(i = 0; i < rad->nchans; i++)
{
n = (unsigned)(i - rad->intcount) % rad->nchans;
if (rad->encdec.req[n]) break;
}
if (i >= rad->nchans) return;
rad->encdec.req[n] = 0;
rad->encdec.dcsrx[n] = 0;
rad->encdec.ctrx[n] = 0;
rad->encdec.dcstx[n] = 0;
rad->encdec.cttx[n] = 0;
rad->encdec.myindex[n] = 0;
rad->encdec.req[n] = 0;
rad->encdec.chan = n;
/* if something in code 0 for rx, is DCS */
if (rad->rxcode[n][0]) rad->encdec.dcsrx[n] = 1;
else { /* otherwise, if something in other codes, is CT rx */
for(i = 1; i <= NUM_CODES; i++)
{
if (rad->rxcode[n][1]) rad->encdec.ctrx[n] = 1;
}
}
/* get index for tx code. Will be 0 if not receiving a CT */
rad->encdec.myindex[n] = 0;
if (rad->gotrx[n] && rad->encdec.ctrx[n] && (rad->present_code[n]))
rad->encdec.myindex[n] = rad->present_code[n];
/* get actual tx code from array */
rad->encdec.txcode[n] = rad->txcode[n][rad->encdec.myindex[n]];
if (rad->encdec.txcode[n] & 0x8000) rad->encdec.dcstx[n] = 1;
else if (rad->encdec.txcode[n]) rad->encdec.cttx[n] = 1;
if (rad->radmode[n] & RADMODE_NOENCODE)
rad->encdec.dcstx[n] = rad->encdec.cttx[n] = 0;
if ((!rad->gottx[n]) || rad->bursttimer[n])
rad->encdec.dcstx[n] = rad->encdec.cttx[n] = 0;
rad->encdec.saudio_ctrl[n] = 0;
rad->encdec.saudio_setup[n] = 0;
rad->encdec.state = 1;
break;
case 1:
if (rad->encdec.dcstx[rad->encdec.chan] && (!rad->encdec.dcsrx[rad->encdec.chan])) /* if to transmit DCS */
{
rad->encdec.saudio_setup[rad->encdec.chan] |= 3;
rad->encdec.saudio_ctrl[rad->encdec.chan] |= 0x80;
byte1 = dcstable[rad->encdec.txcode[rad->encdec.chan] & 0x7fff].b1;
mx828_command(rad,rad->encdec.chan, MX828_DCS1, &byte1, &byte2 );
rad->encdec.state = 2;
break;
}
rad->encdec.state = 4;
break;
case 2:
byte1 = dcstable[rad->encdec.txcode[rad->encdec.chan] & 0x7fff].b2;
mx828_command(rad,rad->encdec.chan, MX828_DCS2, &byte1, &byte2 );
rad->encdec.state = 3;
break;
case 3:
byte1 = dcstable[rad->encdec.txcode[rad->encdec.chan] & 0x7fff].b3;
mx828_command(rad,rad->encdec.chan, MX828_DCS3, &byte1, &byte2 );
rad->encdec.state = 4;
break;
case 4:
if (rad->encdec.cttx[rad->encdec.chan])
{
rad->encdec.saudio_ctrl[rad->encdec.chan] |= 0x80;
byte1 = cttable_tx[rad->encdec.txcode[rad->encdec.chan]].b1;
byte2 = cttable_tx[rad->encdec.txcode[rad->encdec.chan]].b2;
mx828_command(rad,rad->encdec.chan, MX828_TX_TONE, &byte1, &byte2 );
}
rad->encdec.state = 5;
break;
case 5:
if (rad->encdec.dcsrx[rad->encdec.chan])
{
rad->encdec.saudio_setup[rad->encdec.chan] |= 1;
rad->encdec.saudio_ctrl[rad->encdec.chan] |= 0x41;
byte1 = dcstable[rad->rxcode[rad->encdec.chan][0]].b1;
mx828_command(rad,rad->encdec.chan, MX828_DCS1, &byte1, &byte2 );
rad->encdec.state = 6;
break;
}
rad->encdec.state = 8;
break;
case 6:
byte1 = dcstable[rad->rxcode[rad->encdec.chan][0]].b2;
mx828_command(rad,rad->encdec.chan, MX828_DCS2, &byte1, &byte2 );
rad->encdec.state = 7;
break;
case 7:
byte1 = dcstable[rad->rxcode[rad->encdec.chan][0]].b3;
mx828_command(rad,rad->encdec.chan, MX828_DCS3, &byte1, &byte2 );
rad->encdec.state = 8;
break;
case 8:
if (rad->encdec.ctrx[rad->encdec.chan])
{
rad->encdec.saudio_setup[rad->encdec.chan] |= 0x80;
rad->encdec.saudio_ctrl[rad->encdec.chan] |= 0x60;
}
byte1 = rad->encdec.saudio_setup[rad->encdec.chan];
mx828_command(rad,rad->encdec.chan, MX828_SAUDIO_SETUP, &byte1, &byte2 );
rad->encdec.state = 9;
break;
case 9:
byte1 = rad->encdec.saudio_ctrl[rad->encdec.chan];
mx828_command(rad,rad->encdec.chan, MX828_SAUDIO_CTRL, &byte1, &byte2 );
rad->encdec.state = 10;
break;
case 10:
rad->encdec.chan = 0;
rad->encdec.state = 0;
break;
}
}
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 + DAHDI_CHUNKSIZE;
/* Calculate Transmission */
dahdi_transmit(&rad->span);
for (x=0;x<DAHDI_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 + DAHDI_CHUNKSIZE;
else
/* Read is at interrupt address. Valid data is available at normal offset */
readchunk = rad->readchunk;
for (x=0;x<DAHDI_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++) {
dahdi_ec_chunk(&rad->chans[x], rad->chans[x].readchunk, rad->chans[x].writechunk);
}
dahdi_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 LEAVE_THIS_COMMENTED_OUT
static irqreturn_t pciradio_interrupt(int irq, void *dev_id, struct pt_regs *regs)
#endif
DAHDI_IRQ_HANDLER(pciradio_interrupt)
{
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)
return IRQ_NONE;
if (ints & 0x10) {
/* Stop DMA, wait for watchdog */
printk(KERN_INFO "RADIO PCI Master abort\n");
pciradio_stop_dma(rad);
return IRQ_RETVAL(1);
}
if (ints & 0x20) {
printk(KERN_INFO "RADIO PCI Target abort\n");
return IRQ_RETVAL(1);
}
if (ints & 0x0f) {
rad->intcount++;
x = rad->intcount % rad->nchans;
/* freeze */
__pciradio_setcreg(rad,0,rad->mx828_addr | 4);
/* read SAUDIO_STATUS for the proper channel */
byte1 = rad->saudio_status[x] = __pciradio_getcreg(rad,x);
/* thaw */
__pciradio_setcreg(rad,0,rad->mx828_addr);
/* get COR input */
byte2 = __pciradio_getcreg(rad,9);
/* 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); /* they're on the UIOB's */
unsigned char byteuio;
/* set UIOB as input */
byteuio = __pciradio_getcreg(rad,0xe);
byteuio |= mask;
__pciradio_setcreg(rad,0xe,byteuio);
/* get UIO input */
byteuio = __pciradio_getcreg(rad,8);
if (rad->radmode[x] & RADMODE_EXTINVERT)
gotctcss = gotslowctcss = ((byteuio & mask) == 0);
else
gotctcss = gotslowctcss = ((byteuio & mask) != 0);
}
rad->gotct[x] = gotslowctcss;
if ((rad->radmode[x] & RADMODE_IGNORECT) ||
((!(rad->radmode[x] & RADMODE_EXTTONE)) && (!ctcss)))
{
gotctcss = 1;
gotslowctcss = 1;
rad->present_code[x] = 0;
}
if(rad->newctcssstate[x] != gotctcss){
rad->newctcssstate[x] = gotctcss;
if(rad->newctcssstate[x])
rad->ctcsstimer[x]=rad->ctcssacquiretime[x];
else
rad->ctcsstimer[x]=rad->ctcsstalkofftime[x];
}
else{
if(!rad->ctcsstimer[x])
rad->ctcssstate[x] = rad->newctcssstate[x];
else
rad->ctcsstimer[x]--;
}
gotrx = gotcor && rad->ctcssstate[x];
if (gotrx != rad->gotrx[x])
{
rad->gotrxtimer[x] = rad->debouncetime[x];
}
rad->gotrx[x] = gotrx;
if (rad->present_code[x] != rad->last_code[x])
{
rad->encdec.req[x] = 1;
rad->last_code[x] = rad->present_code[x];
}
_do_encdec(rad);
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->pasave |= mask;
__pciradio_setcreg(rad, 0xa, rad->pasave);
}
else
{
if (!rad->bursttime[x])
{
rad->pasave &= ~mask;
__pciradio_setcreg(rad, 0xa, rad->pasave);
}
else
{
rad->bursttimer[x] = rad->bursttime[x];
}
}
rad->encdec.req[x] = 1;
rad->lasttx[x] = rad->gottx[x];
}
if (rad->bursttimer[x])
{
/* if just getting to zero */
if (!(--rad->bursttimer[x]))
{
rad->pasave &= ~mask;
__pciradio_setcreg(rad, 0xa, rad->pasave);
}
}
/* if timer active */
if (rad->gotrxtimer[x])
{
/* if just getting to zero */
if (!(--rad->gotrxtimer[x]))
{
mask = 1 << (x + 4);
rad->pasave &= ~mask;
if (gotctcss) rad->pasave |= mask;
__pciradio_setcreg(rad, 0xa, rad->pasave);
if (rad->gotrx[x] != rad->gotrx1[x])
{
if (rad->gotrx[x]) {
if (debug)
{
if (rad->present_code[x])
printk(KERN_DEBUG "Chan %d got rx (ctcss code %d)\n",x + 1,
cttable_rx[rad->rxcode[x][rad->present_code[x]]].code);
else
printk(KERN_DEBUG "Chan %d got rx\n",x + 1);
}
dahdi_hooksig(&rad->chans[x],DAHDI_RXSIG_OFFHOOK);
} else {
if (debug) printk(KERN_DEBUG "Chan %d lost rx\n",x + 1);
dahdi_hooksig(&rad->chans[x],DAHDI_RXSIG_ONHOOK);
}
rad->encdec.req[x] = 1;
}
rad->gotrx1[x] = rad->gotrx[x];
}
}
}
/* process serial if any */
/* send byte if there is one in buffer to send */
if (rad->txlen && (rad->txlen != rad->txindex))
{
/* if tx not busy */
if (!(__pciradio_getcreg(rad,9) & 0x80))
{
__pciradio_setcreg(rad, 4, rad->txbuf[rad->txindex++]);
}
}
rad->srxtimer++;
/* if something in rx to read */
while(__pciradio_getcreg(rad,9) & 0x10)
{
unsigned char c = __pciradio_getcreg(rad,4);
rad->srxtimer = 0;
if (rad->rxindex < RAD_SERIAL_BUFLEN)
{
rad->rxbuf[rad->rxindex++] = c;
}
udelay(1);
}
pciradio_receiveprep(rad, ints);
pciradio_transmitprep(rad, ints);
i = 0;
for(x = 0; x < 4; x++)
{
if (rad->gottx[x]) i |= (1 << (x * 2));
if (rad->gotrx[x]) i |= (2 << (x * 2));
}
/* output LED's */
__pciradio_setcreg(rad, 9, i);
}
return IRQ_RETVAL(1);
}
static int pciradio_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data)
{
int i,mycode;
unsigned long flags;
unsigned char byte1,byte2,mask;
union {
struct dahdi_radio_stat s;
struct dahdi_radio_param p;
} stack;
struct pciradio *rad = chan->pvt;
switch (cmd) {
case DAHDI_RADIO_GETPARAM:
if (copy_from_user(&stack.p, (__user void *) data, sizeof(stack.p))) return -EFAULT;
spin_lock_irqsave(&rad->lock,flags);
stack.p.data = 0; /* start with 0 value in output */
switch(stack.p.radpar) {
case DAHDI_RADPAR_INVERTCOR:
if (rad->radmode[chan->chanpos - 1] & RADMODE_INVERTCOR)
stack.p.data = 1;
break;
case DAHDI_RADPAR_IGNORECOR:
if (rad->radmode[chan->chanpos - 1] & RADMODE_IGNORECOR)
stack.p.data = 1;
break;
case DAHDI_RADPAR_IGNORECT:
if (rad->radmode[chan->chanpos - 1] & RADMODE_IGNORECT)
stack.p.data = 1;
break;
case DAHDI_RADPAR_NOENCODE:
if (rad->radmode[chan->chanpos - 1] & RADMODE_NOENCODE)
stack.p.data = 1;
break;
case DAHDI_RADPAR_CORTHRESH:
stack.p.data = rad->corthresh[chan->chanpos - 1] & 7;
break;
case DAHDI_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 DAHDI_RADPAR_NUMTONES:
stack.p.data = NUM_CODES;
break;
case DAHDI_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 DAHDI_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 DAHDI_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 DAHDI_RADPAR_DEBOUNCETIME:
stack.p.data = rad->debouncetime[chan->chanpos - 1];
break;
case DAHDI_RADPAR_CTCSSACQUIRETIME:
stack.p.data = rad->ctcssacquiretime[chan->chanpos - 1];
break;
case DAHDI_RADPAR_CTCSSTALKOFFTIME:
stack.p.data = rad->ctcsstalkofftime[chan->chanpos - 1];
break;
case DAHDI_RADPAR_BURSTTIME:
stack.p.data = rad->bursttime[chan->chanpos - 1];
break;
case DAHDI_RADPAR_UIODATA:
stack.p.data = 0;
byte1 = __pciradio_getcreg(rad,8);
if (byte1 & (1 << (chan->chanpos - 1))) stack.p.data |= 1;
if (byte1 & (1 << (chan->chanpos + 3))) stack.p.data |= 2;
break;
case DAHDI_RADPAR_UIOMODE:
stack.p.data = 0;
byte1 = __pciradio_getcreg(rad,0xe);
if (byte1 & (1 << (chan->chanpos - 1))) stack.p.data |= 1;
if (byte1 & (1 << (chan->chanpos + 3))) stack.p.data |= 2;
break;
case DAHDI_RADPAR_REMMODE:
stack.p.data = rad->remmode[chan->chanpos - 1];
break;
default:
spin_unlock_irqrestore(&rad->lock,flags);
return -EINVAL;
}
spin_unlock_irqrestore(&rad->lock,flags);
if (copy_to_user((__user void *) data, &stack.p, sizeof(stack.p))) return -EFAULT;
break;
case DAHDI_RADIO_SETPARAM:
if (copy_from_user(&stack.p, (__user void *) data, sizeof(stack.p))) return -EFAULT;
spin_lock_irqsave(&rad->lock,flags);
switch(stack.p.radpar) {
case DAHDI_RADPAR_INVERTCOR:
if (stack.p.data)
rad->radmode[chan->chanpos - 1] |= RADMODE_INVERTCOR;
else
rad->radmode[chan->chanpos - 1] &= ~RADMODE_INVERTCOR;
break;
case DAHDI_RADPAR_IGNORECOR:
if (stack.p.data)
rad->radmode[chan->chanpos - 1] |= RADMODE_IGNORECOR;
else
rad->radmode[chan->chanpos - 1] &= ~RADMODE_IGNORECOR;
break;
case DAHDI_RADPAR_IGNORECT:
if (stack.p.data)
rad->radmode[chan->chanpos - 1] |= RADMODE_IGNORECT;
else
rad->radmode[chan->chanpos - 1] &= ~RADMODE_IGNORECT;
break;
case DAHDI_RADPAR_NOENCODE:
if (stack.p.data)
rad->radmode[chan->chanpos - 1] |= RADMODE_NOENCODE;
else
rad->radmode[chan->chanpos - 1] &= ~RADMODE_NOENCODE;
break;
case DAHDI_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;
byte1 = 0xc0 | (rad->corthresh[chan->chanpos - 1] << 2);
spin_unlock_irqrestore(&rad->lock,flags);
mx828_command_wait(rad,chan->chanpos - 1, MX828_GEN_CTRL, &byte1, &byte2);
spin_lock_irqsave(&rad->lock,flags);
break;
case DAHDI_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 DAHDI_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;
}
spin_unlock_irqrestore(&rad->lock,flags);
for(i = 0; i < NUM_CODES; i++)
{
/* set to no encode/decode */
byte1 = 0;
mx828_command_wait(rad,chan->chanpos - 1, MX828_SAUDIO_CTRL, &byte1, &byte2 );
/* set rx tone to none */
byte1 = i << 4;
byte2 = 0;
mx828_command_wait(rad,chan->chanpos - 1, MX828_RX_TONE, &byte1, &byte2 );
}
spin_lock_irqsave(&rad->lock,flags);
break;
case DAHDI_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;
rad->encdec.req[chan->chanpos - 1] = 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;
spin_unlock_irqrestore(&rad->lock,flags);
mx828_command_wait(rad,chan->chanpos - 1, MX828_RX_TONE, &byte1, &byte2 );
spin_lock_irqsave(&rad->lock,flags);
/* zot out DCS one if there */
rad->rxcode[chan->chanpos - 1][0] = 0;
rad->encdec.req[chan->chanpos - 1] = 1;
break;
case DAHDI_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 DAHDI_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;
rad->encdec.req[chan->chanpos - 1] = 1;
break;
case DAHDI_RADPAR_DEBOUNCETIME:
rad->debouncetime[chan->chanpos - 1] = stack.p.data;
break;
case DAHDI_RADPAR_CTCSSACQUIRETIME:
rad->ctcssacquiretime[chan->chanpos - 1] = stack.p.data;
break;
case DAHDI_RADPAR_CTCSSTALKOFFTIME:
rad->ctcsstalkofftime[chan->chanpos - 1] = stack.p.data;
break;
case DAHDI_RADPAR_BURSTTIME:
rad->bursttime[chan->chanpos - 1] = stack.p.data;
break;
case DAHDI_RADPAR_UIODATA:
byte1 = __pciradio_getcreg(rad,8);
byte1 &= ~(1 << (chan->chanpos - 1));
byte1 &= ~(1 << (chan->chanpos + 3));
if (stack.p.data & 1) byte1 |= (1 << (chan->chanpos - 1));
if (stack.p.data & 2) byte1 |= (1 << (chan->chanpos + 3));
__pciradio_setcreg(rad,8,byte1);
break;
case DAHDI_RADPAR_UIOMODE:
byte1 = __pciradio_getcreg(rad,0xe);
byte1 &= ~(1 << (chan->chanpos - 1));
byte1 &= ~(1 << (chan->chanpos + 3));
if (stack.p.data & 1) byte1 |= (1 << (chan->chanpos - 1));
if (stack.p.data & 2) byte1 |= (1 << (chan->chanpos + 3));
__pciradio_setcreg(rad,0xe,byte1);
break;
case DAHDI_RADPAR_REMMODE:
rad->remmode[chan->chanpos - 1] = stack.p.data;
break;
case DAHDI_RADPAR_REMCOMMAND:
/* if no remote mode, return an error */
if (rad->remmode[chan->chanpos - 1] == DAHDI_RADPAR_REM_NONE)
{
spin_unlock_irqrestore(&rad->lock,flags);
return -EINVAL;
}
i = 0;
if (rad->remmode[chan->chanpos - 1] == DAHDI_RADPAR_REM_RBI1)
{
/* set UIOA and UIOB for output */
byte1 = __pciradio_getcreg(rad,0xe);
mask = (1 << (chan->chanpos - 1)) |
(1 << (chan->chanpos + 3));
byte2 = byte1 & (~mask);
i = (byte2 != byte1);
__pciradio_setcreg(rad,0xe,byte2);
byte1 = __pciradio_getcreg(rad,8);
mask = 1 << (chan->chanpos - 1);
byte2 = byte1 | mask;
i = (byte2 != byte1);
__pciradio_setcreg(rad,8,byte2);
spin_unlock_irqrestore(&rad->lock,flags);
if (i || (jiffies < rad->lastremcmd + 10))
msleep_interruptible(100);
rad->lastremcmd = jiffies;
rbi_out(rad,chan->chanpos - 1,(unsigned char *)&stack.p.data);
spin_lock_irqsave(&rad->lock,flags);
break;
}
spin_unlock_irqrestore(&rad->lock,flags);
for(;;)
{
int x;
spin_lock_irqsave(&rad->lock,flags);
x = rad->remote_locked || (__pciradio_getcreg(rad,0xc) & 2);
if (!x) rad->remote_locked = 1;
spin_unlock_irqrestore(&rad->lock,flags);
if (x)
msleep_interruptible(20);
else break;
}
spin_lock_irqsave(&rad->lock,flags);
/* set UIOA for input and UIOB for output */
byte1 = __pciradio_getcreg(rad,0xe);
mask = 1 << (chan->chanpos + 3); /* B an output */
byte2 = byte1 & (~mask);
byte2 |= 1 << (chan->chanpos - 1); /* A in input */
__pciradio_setcreg(rad,0xe,byte2);
byte1 = __pciradio_getcreg(rad,8);
byte2 = byte1 | mask;
byte2 |= 1 << (chan->chanpos - 1);
byte2 |= 1 << (chan->chanpos + 3);
__pciradio_setcreg(rad,8,byte2);
spin_unlock_irqrestore(&rad->lock,flags);
if (byte1 != byte2)
msleep_interruptible(30);
while (jiffies < rad->lastremcmd + 10)
msleep_interruptible(100);
rad->lastremcmd = jiffies;
for(;;)
{
if (!(__pciradio_getcreg(rad,0xc) & 2)) break;
msleep_interruptible(20);
}
spin_lock_irqsave(&rad->lock,flags);
/* enable and address async serializer */
__pciradio_setcreg(rad,0xf,rad->pfsave | ((chan->chanpos - 1) << 4) | 0x80);
/* copy tx buffer */
memcpy(rad->txbuf,stack.p.buf,stack.p.index);
rad->txlen = stack.p.index;
rad->txindex = 0;
rad->rxindex = 0;
rad->srxtimer = 0;
memset(stack.p.buf,0,SERIAL_BUFLEN);
stack.p.index = 0;
if (stack.p.data) for(;;)
{
rad->rxbuf[rad->rxindex] = 0;
if ((rad->rxindex < stack.p.data) &&
(rad->srxtimer < SRX_TIMEOUT) &&
((rad->remmode[chan->chanpos - 1] == DAHDI_RADPAR_REM_SERIAL) ||
(!strchr((char *)rad->rxbuf,'\r'))))
{
spin_unlock_irqrestore(&rad->lock,flags);
msleep_interruptible(20);
spin_lock_irqsave(&rad->lock,flags);
continue;
}
memset(stack.p.buf,0,SERIAL_BUFLEN);
if (stack.p.data && (rad->rxindex > stack.p.data))
rad->rxindex = stack.p.data;
if (rad->rxindex)
memcpy(stack.p.buf,rad->rxbuf,rad->rxindex);
stack.p.index = rad->rxindex;
break;
}
/* wait for done if in SERIAL_ASCII mode, or if no Rx aftwards */
if ((rad->remmode[chan->chanpos - 1] == DAHDI_RADPAR_REM_SERIAL_ASCII) ||
(!stack.p.data))
{
/* wait for TX to be done if not already */
while(rad->txlen && (rad->txindex < rad->txlen))
{
spin_unlock_irqrestore(&rad->lock,flags);
msleep_interruptible(20);
spin_lock_irqsave(&rad->lock,flags);
}
/* disable and un-address async serializer */
__pciradio_setcreg(rad,0xf,rad->pfsave);
}
rad->remote_locked = 0;
spin_unlock_irqrestore(&rad->lock,flags);
if (rad->remmode[chan->chanpos - 1] == DAHDI_RADPAR_REM_SERIAL_ASCII)
msleep_interruptible(1000);
if (copy_to_user((__user void *) data, &stack.p, sizeof(stack.p))) return -EFAULT;
return 0;
default:
spin_unlock_irqrestore(&rad->lock,flags);
return -EINVAL;
}
spin_unlock_irqrestore(&rad->lock,flags);
break;
case DAHDI_RADIO_GETSTAT:
spin_lock_irqsave(&rad->lock,flags);
/* start with clean object */
memset(&stack.s, 0, sizeof(stack.s));
/* if we have rx */
if (rad->gotrx[chan->chanpos - 1])
{
stack.s.radstat |= DAHDI_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 |= DAHDI_RADSTAT_TX;
stack.s.radstat |= DAHDI_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 |= DAHDI_RADSTAT_IGNCOR;
if (rad->radmode[chan->chanpos - 1] & RADMODE_IGNORECT)
stack.s.radstat |= DAHDI_RADSTAT_IGNCT;
if (rad->radmode[chan->chanpos - 1] & RADMODE_NOENCODE)
stack.s.radstat |= DAHDI_RADSTAT_NOENCODE;
if (rad->gotcor[chan->chanpos - 1])
stack.s.radstat |= DAHDI_RADSTAT_RXCOR;
if (rad->gotct[chan->chanpos - 1])
stack.s.radstat |= DAHDI_RADSTAT_RXCT;
spin_unlock_irqrestore(&rad->lock,flags);
if (copy_to_user((__user void *) data, &stack.s, sizeof(stack.s))) return -EFAULT;
break;
default:
return -ENOTTY;
}
return 0;
}
static int pciradio_open(struct dahdi_chan *chan)
{
struct pciradio *rad = chan->pvt;
if (rad->dead)
return -ENODEV;
rad->usecount++;
return 0;
}
static int pciradio_watchdog(struct dahdi_span *span, int event)
{
printk(KERN_INFO "PCI RADIO: Restarting DMA\n");
pciradio_restart_dma(container_of(span, struct pciradio, span));
return 0;
}
static int pciradio_close(struct dahdi_chan *chan)
{
struct pciradio *rad = chan->pvt;
rad->usecount--;
/* If we're dead, release us now */
if (!rad->usecount && rad->dead)
pciradio_release(rad);
return 0;
}
static int pciradio_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig)
{
struct pciradio *rad = chan->pvt;
switch(txsig) {
case DAHDI_TXSIG_START:
case DAHDI_TXSIG_OFFHOOK:
rad->gottx[chan->chanpos - 1] = 1;
break;
case DAHDI_TXSIG_ONHOOK:
rad->gottx[chan->chanpos - 1] = 0;
break;
default:
printk(KERN_DEBUG "pciradio: Can't set tx state to %d\n", txsig);
break;
}
if (debug)
printk(KERN_DEBUG "pciradio: Setting Radio hook state to %d on chan %d\n", txsig, chan->chanpos);
return 0;
}
static const struct dahdi_span_ops pciradio_span_ops = {
.owner = THIS_MODULE,
.hooksig = pciradio_hooksig,
.open = pciradio_open,
.close = pciradio_close,
.ioctl = pciradio_ioctl,
.watchdog = pciradio_watchdog,
};
static int pciradio_initialize(struct pciradio *rad)
{
int x;
/* DAHDI stuff */
sprintf(rad->span.name, "PCIRADIO/%d", rad->pos);
sprintf(rad->span.desc, "Board %d", rad->pos + 1);
rad->span.deflaw = DAHDI_LAW_MULAW;
for (x=0;x<rad->nchans;x++) {
sprintf(rad->chans[x].name, "PCIRADIO/%d/%d", rad->pos, x);
rad->chans[x].sigcap = DAHDI_SIG_SF | DAHDI_SIG_EM;
rad->chans[x].chanpos = x+1;
rad->chans[x].pvt = rad;
rad->debouncetime[x] = RAD_GOTRX_DEBOUNCE_TIME;
rad->ctcssacquiretime[x] = RAD_CTCSS_ACQUIRE_TIME;
rad->ctcsstalkofftime[x] = RAD_CTCSS_TALKOFF_TIME;
}
rad->span.chans = &rad->chans;
rad->span.channels = rad->nchans;
rad->span.flags = DAHDI_FLAG_RBS;
rad->span.ops = &pciradio_span_ops;
if (dahdi_register_device(rad->ddev, &rad->dev->dev)) {
printk(KERN_NOTICE "Unable to register span with DAHDI\n");
return -1;
}
return 0;
}
static void wait_just_a_bit(int foo)
{
long newjiffies;
newjiffies = jiffies + foo;
while(jiffies < newjiffies);
}
static int pciradio_hardware_init(struct pciradio *rad)
{
unsigned char byte1,byte2;
int x;
unsigned long endjif;
/* 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 */
rad->ios = 0xfe;
outb(rad->ios, rad->ioaddr + RAD_AUXD);
/* Set all to outputs except AUX 3 & 4, which are inputs */
outb(0x67, 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);
/* attempt to load the Xilinx Chip */
/* De-assert CS+Write */
rad->ios |= XCS;
outb(rad->ios, rad->ioaddr + RAD_AUXD);
/* Assert PGM */
rad->ios &= ~XPGM;
outb(rad->ios, rad->ioaddr + RAD_AUXD);
/* wait for INIT and DONE to go low */
endjif = jiffies + 10;
while (inb(rad->ioaddr + RAD_AUXR) & (XINIT | XDONE) && (jiffies <= endjif));
if (endjif < jiffies) {
printk(KERN_DEBUG "Timeout waiting for INIT and DONE to go low\n");
return -1;
}
if (debug) printk(KERN_DEBUG "fwload: Init and done gone to low\n");
/* De-assert PGM */
rad->ios |= XPGM;
outb(rad->ios, rad->ioaddr + RAD_AUXD);
/* wait for INIT to go high (clearing done */
endjif = jiffies + 10;
while (!(inb(rad->ioaddr + RAD_AUXR) & XINIT) && (jiffies <= endjif));
if (endjif < jiffies) {
printk(KERN_DEBUG "Timeout waiting for INIT to go high\n");
return -1;
}
if (debug) printk(KERN_DEBUG "fwload: Init went high (clearing done)\nNow loading...\n");
/* Assert CS+Write */
rad->ios &= ~XCS;
outb(rad->ios, rad->ioaddr + RAD_AUXD);
for (x = 0; x < sizeof(radfw); x++)
{
/* write the byte */
outb(radfw[x],rad->ioaddr + RAD_REGBASE);
/* if DONE signal, we're done, exit */
if (inb(rad->ioaddr + RAD_AUXR) & XDONE) break;
/* if INIT drops, we're screwed, exit */
if (!(inb(rad->ioaddr + RAD_AUXR) & XINIT)) break;
}
if (debug) printk(KERN_DEBUG "fwload: Transferred %d bytes into chip\n",x);
/* Wait for FIFO to clear */
endjif = jiffies + 2;
while (jiffies < endjif); /* wait */
printk(KERN_DEBUG "Transfered %d bytes into chip\n",x);
/* De-assert CS+Write */
rad->ios |= XCS;
outb(rad->ios, rad->ioaddr + RAD_AUXD);
if (debug) printk(KERN_INFO "fwload: Loading done!\n");
/* Wait for FIFO to clear */
endjif = jiffies + 2;
while (jiffies < endjif); /* wait */
if (!(inb(rad->ioaddr + RAD_AUXR) & XINIT))
{
printk(KERN_NOTICE "Drove Init low!! CRC Error!!!\n");
return -1;
}
if (!(inb(rad->ioaddr + RAD_AUXR) & XDONE))
{
printk(KERN_INFO "Did not get DONE signal. Short file maybe??\n");
return -1;
}
wait_just_a_bit(2);
/* get the thingy started */
outb(0,rad->ioaddr + RAD_REGBASE);
outb(0,rad->ioaddr + RAD_REGBASE);
printk(KERN_INFO "Xilinx Chip successfully loaded, configured and started!!\n");
wait_just_a_bit(HZ/4);
/* Back to normal, with automatic DMA wrap around */
outb(0x30 | 0x01, rad->ioaddr + RAD_CNTL);
/* Configure serial port for MSB->LSB operation */
outb(0xc1, rad->ioaddr + RAD_SERCTL); /* DEBUG set double dlck to 0 SR */
rad->pasave = 0;
__pciradio_setcreg(rad,0xa,rad->pasave);
__pciradio_setcreg(rad,0xf,rad->pfsave);
__pciradio_setcreg(rad,8,0xff);
__pciradio_setcreg(rad,0xe,0xff);
__pciradio_setcreg(rad,9,0);
rad->pfsave = 0;
/* 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 + DAHDI_CHUNKSIZE * 4 - 4, rad->ioaddr + RAD_DMAWI); /* Middle (interrupt) */
outl(rad->writedma + DAHDI_CHUNKSIZE * 8 - 4, rad->ioaddr + RAD_DMAWE); /* End */
outl(rad->readdma, rad->ioaddr + RAD_DMARS); /* Read start */
outl(rad->readdma + DAHDI_CHUNKSIZE * 4 - 4, rad->ioaddr + RAD_DMARI); /* Middle (interrupt) */
outl(rad->readdma + DAHDI_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_wait(rad,x, MX828_GEN_RESET, &byte1, &byte2 );
byte1 = 0x3f;
byte2 = 0x3f;
mx828_command_wait(rad,x, MX828_AUD_CTRL, &byte1, &byte2 );
byte1 = 0;
mx828_command_wait(rad,x, MX828_SAUDIO_SETUP, &byte1, &byte2 );
byte1 = 0;
mx828_command_wait(rad,x, MX828_SAUDIO_CTRL, &byte1, &byte2 );
byte1 = 0xc8; /* default COR thresh is 2 */
mx828_command_wait(rad,x, MX828_GEN_CTRL, &byte1, &byte2);
rad->corthresh[x] = 2;
}
/* Wait 1/4 of a sec */
wait_just_a_bit(HZ/4);
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(0x31, rad->ioaddr + RAD_CNTL);
outb(0x01, rad->ioaddr + RAD_OPER);
}
static void pciradio_start_dma(struct pciradio *rad)
{
/* Reset Master and serial */
outb(0x3f, rad->ioaddr + RAD_CNTL);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(1);
outb(0x31, 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(0x3f, 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(KERN_NOTICE "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;
rad->chans = rad->_chans;
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 = pci_alloc_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, &rad->writedma);
if (!rad->writechunk) {
printk(KERN_NOTICE "pciradio: Unable to allocate DMA-able memory\n");
if (rad->freeregion)
release_region(rad->ioaddr, 0xff);
return -ENOMEM;
}
rad->readchunk = rad->writechunk + DAHDI_MAX_CHUNKSIZE * 2; /* in doublewords */
rad->readdma = rad->writedma + DAHDI_MAX_CHUNKSIZE * 8; /* in bytes */
if (pciradio_initialize(rad)) {
printk(KERN_INFO "pciradio: Unable to intialize\n");
/* Set Reset Low */
x=inb(rad->ioaddr + RAD_CNTL);
outb((~0x1)&x, rad->ioaddr + RAD_CNTL);
outb(x, rad->ioaddr + RAD_CNTL);
__pciradio_setcreg(rad,8,0xff);
__pciradio_setcreg(rad,0xe,0xff);
/* Free Resources */
free_irq(pdev->irq, rad);
if (rad->freeregion)
release_region(rad->ioaddr, 0xff);
pci_free_consistent(pdev, DAHDI_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 (pciradio_hardware_init(rad)) {
/* Set Reset Low */
x=inb(rad->ioaddr + RAD_CNTL);
outb((~0x1)&x, rad->ioaddr + RAD_CNTL);
outb(x, rad->ioaddr + RAD_CNTL);
__pciradio_setcreg(rad,8,0xff);
__pciradio_setcreg(rad,0xe,0xff);
/* Free Resources */
free_irq(pdev->irq, rad);
if (rad->freeregion)
release_region(rad->ioaddr, 0xff);
pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)rad->writechunk, rad->writedma);
pci_set_drvdata(pdev, NULL);
dahdi_free_device(rad->ddev);
kfree(rad);
return -EIO;
}
if (request_irq(pdev->irq, pciradio_interrupt,
IRQF_SHARED, "pciradio", rad)) {
printk(KERN_NOTICE "pciradio: Unable to request IRQ %d\n", pdev->irq);
if (rad->freeregion)
release_region(rad->ioaddr, 0xff);
pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)rad->writechunk, rad->writedma);
pci_set_drvdata(pdev, NULL);
kfree(rad);
return -EIO;
}
/* Enable interrupts */
pciradio_enable_interrupts(rad);
/* Initialize Write/Buffers to all blank data */
memset((void *)rad->writechunk,0,DAHDI_MAX_CHUNKSIZE * 2 * 2 * 4);
/* Start DMA */
pciradio_start_dma(rad);
printk(KERN_INFO "Found a PCI Radio Card\n");
res = 0;
} else
res = -ENOMEM;
}
return res;
}
static void pciradio_release(struct pciradio *rad)
{
dahdi_unregister_device(rad->ddev);
if (rad->freeregion)
release_region(rad->ioaddr, 0xff);
kfree(rad);
printk(KERN_INFO "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, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)rad->writechunk, rad->writedma);
free_irq(pdev->irq, rad);
/* Reset PCI chip and registers */
outb(0x3e, rad->ioaddr + RAD_CNTL);
/* Clear Reset Line */
outb(0x3f, rad->ioaddr + RAD_CNTL);
__pciradio_setcreg(rad,8,0xff);
__pciradio_setcreg(rad,0xe,0xff);
/* Release span, possibly delayed */
if (!rad->usecount)
pciradio_release(rad);
else
rad->dead = 1;
}
}
static DEFINE_PCI_DEVICE_TABLE(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,
.remove = __devexit_p(pciradio_remove_one),
.id_table = pciradio_pci_tbl,
};
static int __init pciradio_init(void)
{
int res;
res = dahdi_pci_module(&pciradio_driver);
if (res)
return -ENODEV;
return 0;
}
static void __exit pciradio_cleanup(void)
{
pci_unregister_driver(&pciradio_driver);
}
module_param(debug, int, 0600);
MODULE_DESCRIPTION("DAHDI Telephony PCI Radio Card Driver");
MODULE_AUTHOR("Jim Dixon <jim@lambdatel.com>");
MODULE_LICENSE("GPL v2");
module_init(pciradio_init);
module_exit(pciradio_cleanup);