1892 lines
51 KiB
C
1892 lines
51 KiB
C
|
/*
|
||
|
* 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/errno.h>
|
||
|
#include <linux/pci.h>
|
||
|
#include <linux/interrupt.h>
|
||
|
#include <linux/moduleparam.h>
|
||
|
#include <linux/sched.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_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;
|
||
|
DECLARE_WAIT_QUEUE_HEAD(mywait);
|
||
|
|
||
|
|
||
|
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) interruptible_sleep_on_timeout(&mywait,2);
|
||
|
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)
|
||
|
{
|
||
|
DECLARE_WAIT_QUEUE_HEAD(mywait);
|
||
|
unsigned long flags;
|
||
|
|
||
|
|
||
|
spin_lock_irqsave(&rad->lock,flags);
|
||
|
while(rad->encdec.state)
|
||
|
{
|
||
|
spin_unlock_irqrestore(&rad->lock,flags);
|
||
|
interruptible_sleep_on_timeout(&mywait,2);
|
||
|
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;
|
||
|
DECLARE_WAIT_QUEUE_HEAD(mywait);
|
||
|
|
||
|
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))
|
||
|
interruptible_sleep_on_timeout(&mywait,10);
|
||
|
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) interruptible_sleep_on_timeout(&mywait,2);
|
||
|
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)
|
||
|
interruptible_sleep_on_timeout(&mywait,3);
|
||
|
while (jiffies < rad->lastremcmd + 10)
|
||
|
interruptible_sleep_on_timeout(&mywait,10);
|
||
|
rad->lastremcmd = jiffies;
|
||
|
for(;;)
|
||
|
{
|
||
|
if (!(__pciradio_getcreg(rad,0xc) & 2)) break;
|
||
|
interruptible_sleep_on_timeout(&mywait,2);
|
||
|
}
|
||
|
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);
|
||
|
interruptible_sleep_on_timeout(&mywait,2);
|
||
|
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);
|
||
|
interruptible_sleep_on_timeout(&mywait,2);
|
||
|
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)
|
||
|
interruptible_sleep_on_timeout(&mywait,100);
|
||
|
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;
|
||
|
init_waitqueue_head(&rad->span.maintq);
|
||
|
|
||
|
if (dahdi_register(&rad->span, 0)) {
|
||
|
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_unregister(&rad->span);
|
||
|
kfree(rad);
|
||
|
return -EIO;
|
||
|
|
||
|
}
|
||
|
|
||
|
if (request_irq(pdev->irq, pciradio_interrupt, DAHDI_IRQ_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(&rad->span);
|
||
|
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 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,
|
||
|
.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);
|